Search code examples
javascripthtml5-canvasjspdf

Uncaught Error: Incomplete or corrupt PNG file


I am trying to add a couple of images to a pdf. I am using the jspdf plugin for this. My code is as following:

document.getElementById("help").addEventListener("click",function(){
    var base1="";
    var base2="";
    var base3="";
    var doc = new jsPDF();
    var img=new Image();
    var img1=new Image();
    var img2=new Image();
    img.onload = function() {
        var canvas = document.createElement("canvas");
        ctx = canvas.getContext('2d');
        ctx.drawImage(img, 0, 0);
        base1 = canvas.toDataURL();
    }
    img.src='/Screenshot (1).png';
    img1.onload = function() {
        var canvas = document.createElement("canvas");
        ctx = canvas.getContext('2d');
        ctx.drawImage(img1, 0, 0);
        base2 = canvas.toDataURL();
    }
    img1.src='/Screenshot (2).png';
    img2.onload = function() {
        var canvas = document.createElement("canvas");
        ctx = canvas.getContext('2d');
        ctx.drawImage(img2, 0, 0);
        base3 = canvas.toDataURL();
    }
    img2.src='/Screenshot (3).png';

    doc.addImage(base1,'PNG',20,36,100,120);
    
    doc.addImage(base2,'PNG',20,158,100,120);
   
    doc.addImage(base3,'PNG',20,281,100,120);
    doc.save("example.pdf");
})

But when I execute the code, I get the following error in addImage():

Uncaught Error: Incomplete or corrupt PNG file

How do I fix this?

EDIT: After implementing @AKX's solution, the code works in the local machine. But when tested live, it throws this error:

Uncaught (in promise) Event {isTrusted: true, type: 'error', target: null, currentTarget: null, eventPhase: 0, …}


Solution

  • You're not waiting for all the images to load. You can refactor things to use a promisified function to load an image and turn it into a data URI (though according to the docs for jspdf, a HTMLImageElement would also do, so you could skip the canvas bit), and then turn your handler into an async function to make it easy to just await for all the loads.

    function loadImageAsDataURL(url) {
      return new Promise((resolve, reject) => {
        var img = new Image();
        img.onload = function () {
          var canvas = document.createElement('canvas');
          var ctx = canvas.getContext('2d');
          ctx.drawImage(img, 0, 0);
          resolve(canvas.toDataURL());
        }
        img.onerror = reject;
        img.src = url;
      });
    }
    
    document.getElementById('help').addEventListener('click', async function () {
      // Load all images first...
      var base1 = await loadImageAsDataURL("/Screenshot (1).png");
      var base2 = await loadImageAsDataURL("/Screenshot (2).png");
      var base3 = await loadImageAsDataURL("/Screenshot (3).png");
      // ... and then generate the PDF.
      var doc = new jsPDF();
      doc.addImage(base1, 'PNG', 20, 36, 100, 120);
      doc.addImage(base2, 'PNG', 20, 158, 100, 120);
      doc.addImage(base3, 'PNG', 20, 281, 100, 120);
      doc.save('example.pdf');
    })