eKoopmans / html2pdf.js

Client-side HTML-to-PDF rendering using pure JS.
MIT License
4.05k stars 1.37k forks source link

Saving/email PDF to WordPress, it keeps saving/emailing a blank corrupt PDF (jsPDF & html2pdf) #589

Open Chantellelander opened 2 years ago

Chantellelander commented 2 years ago

I have based from my code from https://github.com/eKoopmans/html2pdf.js/issues/181 (odedta) comment and also tried the second users comment below theirs.

I have a script using html2pdf bundle. Which works well. It create a multi page PDF with images. The content of the PDF is data which is pulled from a WordPress category (using a wp Query) and results in 3-6 Posts being put into a PDF.

HTML for example:

<script type="text/javascript" charset="utf8" src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>

<div id="PDFcontent">
<h1> PDF tile </h1>
<img src="https://styles.redditmedia.com/t5_2r5i1/styles/communityIcon_x4lqmqzu1hi81.jpg?width=256&s=fc15e67e2b431bbd2e93e980be3090306b78be55"> 
<div class="html2pdf__page-break"></div>
<h2 class="exhibitied-title"> Page 2 title<h2>
Some text
<div class="html2pdf__page-break"></div>
<h2> Page 3 title<h2>
Some text
</div>

I have my Jquery at the bottom of the page

jQuery(function($) {
            window.onload = function() {
                let el = document.getElementById('PDFcontent');
                var title = $('.exhibitied-title').text();
                let opt = {
                    margin: 0.2,
                    filename: title + '.pdf',
                    image: {
                        type: 'jpeg',
                        quality: 0.99
                    },
                    html2canvas: {
                        scale: 2,
                        logging: true,
                        dpi: 192,
                        letterRendering: true,
                        /* useCORS: true*/
                    },
                    jsPDF: {
                        unit: 'mm',
                        format: 'a4',
                        orientation: 'portrait'
                    }
                };
                html2pdf().set(opt).from(el).toPdf().output('datauristring').save().then(function(pdfAsString) {
                    title = $('.exhibitied-title').text();
                    filetitle = title + '.pdf';
                    let data = {
                        'action': 'send_email_with_pdf',
                        'fileDataURI': pdfAsString,
                        'filetitle': filetitle,
                    }
                    $.post(myAjax.ajaxurl, data, function(response) {
                        console.log(response);
                    });
                });
            }
        });

.save works perfectly, the PDF is formatted exactly how I want it.

But when it comes to saving or emailing the PDF I am having problems. The email is does send and a PDF is created. However the pdf in the email is 115bytes and just says "Failed to load PDF document." when you try opening it, and the pdf which is saved in my WP uploads folder is 0 bytes and also "Failed to load PDF document." shows.

Inside my functions.php I have (for those who do not use Wordpress, this would be the equivalent to a upload.php page)

function send_email_with_pdf() {
    $pdfdoc     = $_POST['fileDataURI'];
    $b64file        = trim( str_replace( 'data:application/pdf;base64,', '', $pdfdoc ) );
    $b64file        = str_replace( ' ', '+', $b64file );
    $decoded_pdf    = base64_decode( $b64file );
    //file_put_contents( $attachment, $decodPdf );

     $upload_dir = wp_upload_dir();
     $image_data = file_get_contents( $decoded_pdf );
    $filename = $_POST['filetitle'];
    if ( wp_mkdir_p( $upload_dir['path'] ) ) {
                  $file = $upload_dir['path'] . '/' . $filename;
                }
                else {
                  $file = $upload_dir['basedir'] . '/' . $filename;
                }
    file_put_contents( $file, $image_data );
    $mail = new PHPMailer;
    $mail->setFrom( 'me@myemail.com', 'My name' );
    $mail->addAddress( 'you@youremail.com', 'your name' );
    $mail->Subject  = 'Subject';

    $mail->Body     = $filename;

    $mail->addStringAttachment($decoded_pdf, $filename);
    $mail->isHTML( false );
    if( !$mail->send() ) {
        $response = 'Message was not sent.';
        $response .= 'Mailer error: ' . $mail->ErrorInfo;
    }
    else {
        $response = 'Message has been sent.';
    }

    wp_send_json( $response );
}
add_action( 'wp_ajax_send_email_with_pdf', 'send_email_with_pdf' );
add_action( 'wp_ajax_nopriv_send_email_with_pdf', 'send_email_with_pdf' );

Is anyone able to point me in the right direction please, or has anyone come across this issue before?

I have posted as much information as I can, but please let me know if you need more information.

I also put html2pdf().output().length; above html2pdf().set(opt).from(el).save().toPdf().output('datauristring').then(function(pdfAsString) { and then console had a look to see if it had a length, but came back as 0.

Chantellelander commented 2 years ago

I got this working, for anyone else needing it.

I have added notes in the code for explanation

First add this to the top of your page <script type="text/javascript" charset="utf8" src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>

HTML / Wordpress content loop part

<div id="PDFcontent">
<h1 class="mypdftitle"> PDF tile </h1>
<img src="https://styles.redditmedia.com/t5_2r5i1/styles/communityIcon_x4lqmqzu1hi81.jpg?width=256&s=fc15e67e2b431bbd2e93e980be3090306b78be55"> 
<div class="html2pdf__page-break"></div>
<h2> Page 2 title<h2>
Some text
<div class="html2pdf__page-break"></div>
<h2> Page 3 title<h2>
Some text
</div>

JQuery - goes at the bottom on the page above the footer

jQuery(function($) {
    window.onload = function() {
        let el = document.getElementById('PDFcontent');
        var title = $('.exhibitied-title').text();
        let opt = { //Create/design the pdf
            margin: 0.2,
            filename: title + '.pdf',
            image: {
                type: 'jpeg',
                quality: 0.99
            },
            html2canvas: {
                scale: 2,
                logging: true,
                dpi: 192,
                letterRendering: true,
            },
            jsPDF: {
                unit: 'mm',
                format: 'a4',
                orientation: 'portrait'
            }
        };
        html2pdf().from(el).set(opt).toPdf() /*add .save() if you need it to download locally too*/.output('datauristring').then(function(res) {
            this.blobString = res;
            title = $('mypdftitle').text(); //Get the title from h1 html
            filetitle = title; //set the title to be send in the ajax
            let data = {
                'action': 'send_email_with_pdf', //Name of function in functions.php
                'fileDataURI': this.blobString,
                'filetitle': filetitle, //name of pdf - you set this above
            }
            $.post(myAjax.ajaxurl, data, function(response) { //myAjax.ajaxurl see the bottom code comment
                console.log(response);
                console.log(filetitle);
            });
            console.log(this.blobString);
        });
    }
});

Now in functions.php

function send_email_with_pdf() {
    if (isset($_POST['fileDataURI'])) {

        /*** Prepare and decode the uri string ***/
            $pdfdoc     = $_POST['fileDataURI']; // This looks like data:application/pdf;filename=generated.pdf;base64,JVBERi0xLjMKJbrfrOAKMyAwIG9iago8PC9UeXB....
                   $b64file        = str_replace( 'data:application/pdf;filename=generated.pdf;base64,', '', $pdfdoc ) ; //remove data:application/pdf;filename=generated.pdf;base64,
                 $decoded_pdf    = base64_decode(  $b64file  ); //decode it

        /**** Unique naming ****/   
           $date = date("d-m-y"); // Today date day month year
               $time = date("h:i"); // The time 
         //Adding date and time to the pdf, will make sure each pdf generated is unique 
         $ext = ".pdf"; //file extension
             $thename = $_POST['filetitle']; //The title sent by ajax from the extension page
             $name        = str_replace( ' ', '-', $thename ) ; //remove spaces - makes urls better
        $filename =  $name . '-' . $date . '-' . $time . $ext; // combine the file name
        //If you dont want date and time, then do the following $filename = $thename . $ext;

        /**** Upload directory *****/
        $upload_dir = wp_upload_dir();
        if (wp_mkdir_p($upload_dir['path'])) { // Puts the file in wp-content/uploads/year/month
            $file = $upload_dir['path'].
            '/'.$filename;
        } else {
            $file = $upload_dir['basedir'].
            '/'.$filename;
        }

        /*** Upload it ****/
        file_put_contents($file, $decoded_pdf);

        /*** Send the email ****/
        $mail = new PHPMailer;
                $mail->setFrom( 'me@myemail.com', 'My name' );
                $mail->addAddress( 'you@youremail.com', 'your name' );
        $mail - > Subject = $filename;
        $mail - > Body = $filename; //email text goes here, I have made it the file name for example
        $mail - > addStringAttachment($decoded_pdf, $filename);
        $mail - > isHTML(false);
        if (!$mail - > send()) {
            $response = 'Message was not sent.';
            $response. = 'Mailer error: '.$mail - > ErrorInfo;
        } else {
            $response = 'Message has been sent.';
            $response = $image_data;
        }
        wp_send_json($response);
    }
}
add_action('wp_ajax_send_email_with_pdf', 'send_email_with_pdf');
add_action('wp_ajax_nopriv_send_email_with_pdf', 'send_email_with_pdf');

For those who are unsure about the myAjax.ajaxurl in the JQuery part, Add this to the top of your functions.php in your WordPress theme, and then when ever you want to call Ajax on your site you can use the myAjax.ajaxurl

add_action( 'init', 'script_enqueuer' );

function script_enqueuer() {
   wp_register_script( "liker_script", get_stylesheet_directory_uri().'/js/functionality.js', array('jquery') );
   wp_localize_script( 'liker_script', 'myAjax', array( 'ajaxurl' => admin_url( 'admin-ajax.php' )));        
    wp_enqueue_script( 'jquery' );
   wp_enqueue_script( 'liker_script' );
}
abdo20898 commented 1 year ago

Dude, you saved my life. This was perfect! Thank you very much.

Esteban-Codificador commented 12 months ago

[Chantellelander] you are one of the good ones