0

I'm coding a pdf generator with html2canvas + jsPDF. I use a recursive method for this. When I launch it on chrome, it make the 'Aw snap!' exception about the 25th iteration. On Mozilla, I can go over 30 and it won't crash. I don't understand why. Here's my code:

function pdfMe(){
        var pdf=new jsPDF('l');
        var slides=document.getElementById("diapoStock").children; //return some divs
        var firstSlide=slides[0].id;
        pdfSlide(firstSlide,firstSlide,pdf);
    }
    //PDF recursive
    function pdfSlide(id,idFirst,pdf){
        document.getElementById("pdfOutput").innerHTML="";   //empty the output
        var next=nextElementOf(id.substr(0,id.length-3)+"Div");    //next div to PDF
        showMe(id.substr(0,id.length-3));                //Show the div to PDF in 'central'
        html2canvas(document.getElementById("central",{async:true})).then(function (canvas) {
            document.getElementById("pdfOutput").appendChild(canvas);   //div to PDF shown
            var img=canvas.toDataURL("image/png");
            pdf.addImage(img, 'PNG', 0, 0, 297, 210);    //div added to PDF
            if(next===idFirst){                          //if end of divs
                endPDF(pdf);
            }
            else{
                pdf.addPage();
                pdfSlide(next,idFirst,pdf);             //to next div
            }
        });
    }
    //Print
    function endPDF(pdf){
        pdf.save('{{ propale.titre }}.pdf');
    }

I don't know if it is due to a difference of cache size. I printed it with console.log(window.performance.memory); but it gave me the same for 10 or 30 iterations.

Is it due to my recursive method? Does it use too much memory?

Hope it's understandable

Bibimission
  • 317
  • 5
  • 17
  • Hello, I have recently experienced html2canvas and canvas2pdf and the first thing tha comes to my mind is that Chrome eats a ton of memory, for sure, especially when it comes to recursive functions and also canvases. However, your code can be improved a bit, let me try in an answer. – Andrew Adam Jun 04 '18 at 13:42
  • Yes, I opened Task Manager and I see, when I launch the process, the memory usage of chrome goes from 70Mo to 1.2Go – Bibimission Jun 04 '18 at 13:55

1 Answers1

0

A few things I noticed: Your 'firstSlide' variable was always overwritten by the pdfMe() function every time it was called. This caused never to go to the if(next === idFirst) branch. This also caused the browser to try to asynchronously go again and again to the pdfSlide() which broke the browser.

You have passed the same firstSlide variable twice to the pdfSlide() function, but this was nothing breaking just unnecessary.

I believe that the comparison mentioned above is the key to enhance your performance. I simply added a 'first' flag just to show you how I would approach saving the very first item and then starting the iteration. When you arrive to the last one (I could not determine it based on your code) then try to come up with a different if() statement for quitting the recursive function.

I know this will not be a copy-paste answer but hopefully guides you to the right direction.

var first = true;
var firstID = "";

function pdfMe(){
    var pdf = new jsPDF('l');
    var slides = document.getElementById("diapoStock").children;
    if(first) firstID = slides[0].id; // creating this 'first' flag not to overwrite with every iteration
    first = false;
    pdfSlide(slides[0].id,firstID,pdf); // you were passing the same argument twice to the pdfSlide function. Removed one of those
};

// Added these options from the html2canvas documentation
var options = {
  async: true,
  allowTaint: false,
  imageTimeout: 0
}
function pdfSlide(id,firstID,pdf){
    document.getElementById("pdfOutput").innerHTML="";
    var next = nextElementOf(id.substr(0,id.length-3)+"div"); // this needs to be set differently
    showMe(id.substr(0,id.length-3));
    
    html2canvas(document.getElementById("central",options)).then(function(canvas){
        document.getElementById("pdfOutput").appendChild(canvas);
        var img = canvas.toDataURL("image/png");
        pdf.addImage(img, 'PNG', 0, 0, 297, 210);
        if(next.id === firstID){                          // <-- this condition never happened and because of asynchronous behaviour browser tries to load the else->recursive again and again
            endPDF(pdf);
        }
        else{
            pdf.addPage();
            pdfSlide(next,idFirst,pdf);
        }
    });
}
//Print
function endPDF(pdf){
    pdf.save('{{ propale.titre }}.pdf');
}
Andrew Adam
  • 1,572
  • 9
  • 22
  • Is a return statement necessary to my recursive function to clear memory? – Bibimission Jun 04 '18 at 14:02
  • not necessarily if your if-else condition is foolproof and will eventually run to the endPDF() function. However please note that async behaviour could mix things up a lot: e.g. 3rd page and 4th page are running at the same time, 4th page finishes, no new "pdfSlide()" is called since 4th is last but then when the 3rd page finishes it calls the 4th again. Okay, this is a badly formatted example but you get the gist! – Andrew Adam Jun 04 '18 at 14:05