wpovernight / woocommerce-pdf-invoices-packing-slips

Create, print & automatically email PDF invoices & packing slips for WooCommerce orders.
https://wordpress.org/plugins/woocommerce-pdf-invoices-packing-slips/
Other
100 stars 46 forks source link

Dynamic PDF height using DOMPDF engine #157

Open alexmigf opened 3 years ago

alexmigf commented 3 years ago

Currently in our setup is not very easy to get the DOMPDF body height easily. Normally we provide a custom code snippet, like in here, but it's not very efficient. This is crucial for situations when customers want to print documents in a roll instead of a fixed paper size.

Slack discussion: https://wpovernight.slack.com/archives/C0121KYKGN6/p1615436120016500

DOMPDF issue suggested by @YordanSoares : https://github.com/dompdf/dompdf/issues/1524

alexmigf commented 3 years ago

@Spreeuw maybe we should create a setting for this?

Spreeuw commented 3 years ago

Not sure, it's a bit of an exotic setting. I would first check wether it would be possible to do this with a code snippet (and if not, what hooks would be required to be able to do that). We could always move it to a setting later.

alexmigf commented 3 years ago

Seems to work:

Captura de ecrã de 2021-03-16 14-32-01

Captura de ecrã de 2021-03-16 14-44-42

Spreeuw commented 3 years ago

Can you share your code snippet?

alexmigf commented 3 years ago

See here https://github.com/wpovernight/woocommerce-pdf-invoices-packing-slips/commit/897cc051c71229ef6df2e89af12014e81cb49efd

Spreeuw commented 3 years ago

Apologies if my previous message was unclear: I'd like to see a separate code snippet first before we implement this into the plugin itself (for example using wpo_wcpdf_before_dompdf_render, though I'm not sure if that's possible?

alexmigf commented 3 years ago

Sorry, it's an hook, I will need to test.

alexmigf commented 3 years ago

I believe is not possible because I need to access the $options to instantiate Dompdf again, but we could add a filter before that passing the variable and it should work. Let me know your opinion.

Spreeuw commented 3 years ago

You can do:

$options = $dompdf->getOptions();

you may even be able to do:

$_dompdf = clone $dompdf;
// do your calculations using $_dompdf
unset($_dompdf);
$dompdf->setPaper( /* your height */ );

But overwriting the object is more efficient in terms of memory.

I think you should pick a less generic name for the global.

alexmigf commented 3 years ago

Working with this:

add_filter( 'wpo_wcpdf_before_dompdf_render', function( $dompdf, $html )
{
    $_dompdf                   = clone $dompdf;
    unset( $dompdf );
    $_dompdf_options           = $_dompdf->getOptions();
    $_dompdf_paper_size        = $_dompdf->getPaperSize();
    $_dompdf_paper_orientation = $_dompdf->getPaperOrientation();

    $GLOBALS['wpo_wcpdf_dompdf_body_height'] = 0;
    $_dompdf->setCallbacks(
        array(
            'myCallbacks' => array(
                'event' => 'end_frame',
                'f'     => function ( $infos ) {
                    $frame = $infos["frame"];
                    if ( strtolower( $frame->get_node()->nodeName ) === "body" ) {
                        $padding_box                              = $frame->get_padding_box();
                        $GLOBALS['wpo_wcpdf_dompdf_body_height'] += $padding_box['h'];
                    }
                },
            )
        )
    );

    $_dompdf->loadHtml( $html );
    $_dompdf->render();
    unset( $_dompdf );

    if( ! empty( $_dompdf_paper_size  ) ) {
        $_dompdf_paper_size[3] = $GLOBALS['wpo_wcpdf_dompdf_body_height'] + 150;
    }

    $dompdf = new \Dompdf\Dompdf( $_dompdf_options  );
    $dompdf->loadHtml( $html );
    $dompdf->setPaper( $_dompdf_paper_size, $_dompdf_paper_orientation );

    return $dompdf;
}, 10, 2 );
YordanSoares commented 3 years ago

@alexmigf I tested the code with a long list of products and works like a charm!

alexmigf commented 3 years ago

This works too:

add_filter( 'wpo_wcpdf_before_dompdf_render', function( $_dompdf, $html )
{
    $_dompdf_options           = $_dompdf->getOptions();
    $_dompdf_paper_size        = $_dompdf->getPaperSize();
    $_dompdf_paper_orientation = $_dompdf->getPaperOrientation();

    $GLOBALS['wpo_wcpdf_dompdf_body_height'] = 0;
    $_dompdf->setCallbacks(
        array(
            'myCallbacks' => array(
                'event' => 'end_frame',
                'f'     => function ( $infos ) {
                    $frame = $infos["frame"];
                    if ( strtolower( $frame->get_node()->nodeName ) === "body" ) {
                        $padding_box                              = $frame->get_padding_box();
                        $GLOBALS['wpo_wcpdf_dompdf_body_height'] += $padding_box['h'];
                    }
                },
            )
        )
    );

    $_dompdf->loadHtml( $html );
    $_dompdf->render();
    unset( $_dompdf );

    if( ! empty( $_dompdf_paper_size  ) ) {
        $_dompdf_paper_size[3] = $GLOBALS['wpo_wcpdf_dompdf_body_height'] + 150;
    }

    $dompdf = new \Dompdf\Dompdf( $_dompdf_options );
    $dompdf->loadHtml( $html );
    $dompdf->setPaper( $_dompdf_paper_size, $_dompdf_paper_orientation );

    return $dompdf;
}, 10, 2 );
YordanSoares commented 3 years ago

@alexmigf I tested your previous code and also works 👍

alexmigf commented 3 years ago

This should do the trick for 80mm thermal printer rolls:

add_filter( 'wpo_wcpdf_paper_format', function( $paper_format, $document_type) {
    // change the values below
    $width = 80; //mm!
    $height = 115; //mm!

    //convert mm to points
    $paper_format = array( 0, 0, ($width/25.4) * 72, ($height/25.4) * 72 );

    return $paper_format;
}, 10, 2 );

add_filter( 'wpo_wcpdf_before_dompdf_render', function( $_dompdf, $html )
{
    $_dompdf_options           = $_dompdf->getOptions();
    $_dompdf_paper_size        = $_dompdf->getPaperSize();
    $_dompdf_paper_orientation = $_dompdf->getPaperOrientation();

    $GLOBALS['wpo_wcpdf_dompdf_body_height'] = 0;
    $_dompdf->setCallbacks(
        array(
            'myCallbacks' => array(
                'event' => 'end_frame',
                'f'     => function ( $infos ) {
                    $frame = $infos["frame"];
                    if ( strtolower( $frame->get_node()->nodeName ) === "body" ) {
                        $padding_box                              = $frame->get_padding_box();
                        $GLOBALS['wpo_wcpdf_dompdf_body_height'] += $padding_box['h'];
                    }
                },
            )
        )
    );

    $_dompdf->loadHtml( $html );
    $_dompdf->render();
    unset( $_dompdf );

    if( ! empty( $_dompdf_paper_size  ) ) {
        $_dompdf_paper_size[3] = $GLOBALS['wpo_wcpdf_dompdf_body_height'] + 150; // if too many space at the bottom, replace this 150 value with a lower one
    }

    $dompdf = new \Dompdf\Dompdf( $_dompdf_options );
    $dompdf->loadHtml( $html );
    $dompdf->setPaper( $_dompdf_paper_size, $_dompdf_paper_orientation );

    return $dompdf;
}, 10, 2 );

Could require additional custom CSS styles.

alexmigf commented 1 year ago

The code above is now triggering this error:

Fatal error: Cannot use object of type Dompdf\FrameDecorator\Text as array

Needs to be updated to this:

add_filter( 'wpo_wcpdf_paper_format', function( $paper_format, $document_type) {
    // change the values below
    $width = 80; //mm!
    $height = 115; //mm!

    //convert mm to points
    $paper_format = array( 0, 0, ($width/25.4) * 72, ($height/25.4) * 72 );

    return $paper_format;
}, 10, 2 );

add_filter( 'wpo_wcpdf_before_dompdf_render', function( $_dompdf, $html )
{
    $_dompdf_options           = $_dompdf->getOptions();
    $_dompdf_paper_size        = $_dompdf->getPaperSize();
    $_dompdf_paper_orientation = $_dompdf->getPaperOrientation();

    $GLOBALS['wpo_wcpdf_dompdf_body_height'] = 0;
    $_dompdf->setCallbacks(
        array(
            'myCallbacks' => array(
                'event' => 'end_frame',
                'f'     => function ( \Dompdf\Frame $frame ) {
                    if ( strtolower( $frame->get_node()->nodeName ) === "body" ) {
                        $padding_box                              = $frame->get_padding_box();
                        $GLOBALS['wpo_wcpdf_dompdf_body_height'] += $padding_box['h'];
                    }
                },
            )
        )
    );

    $_dompdf->loadHtml( $html );
    $_dompdf->render();
    unset( $_dompdf );

    if( ! empty( $_dompdf_paper_size  ) ) {
        $_dompdf_paper_size[3] = $GLOBALS['wpo_wcpdf_dompdf_body_height'] + 150; // if too many space at the bottom, replace this 150 value with a lower one
    }

    $dompdf = new \Dompdf\Dompdf( $_dompdf_options );
    $dompdf->loadHtml( $html );
    $dompdf->setPaper( $_dompdf_paper_size, $_dompdf_paper_orientation );

    return $dompdf;
}, 10, 2 );
TRIXServer commented 1 year ago

The code above is now triggering this error:

Fatal error: Cannot use object of type Dompdf\FrameDecorator\Text as array

Needs to be updated to this: [Code snippet]

This solution work for me. I have 80mm thermal printers. Thank you.

Pratik67796 commented 1 year ago

@alexmigf How can we use this add_filter function in Laravel controller ?

DJKAMY0 commented 1 year ago

image

image

How do I get rid of the margins on the size so that the text appears clean? please help, Thanks in advance

alexmigf commented 1 year ago

@Pratik67796

How can we use this add_filter function in Laravel controller ?

I don't think you can, it's a WordPress thing, unless you use something like this. Also, this is a filter from our own plugin, which runs only in WordPress with WooCommerce.

alexmigf commented 1 year ago

@DJKAMY0

You need to apply custom CSS. See this documentation page: https://docs.wpovernight.com/woocommerce-pdf-invoices-packing-slips/using-custom-styles/