jsPDF icon indicating copy to clipboard operation
jsPDF copied to clipboard

addImage PNG fails >= 2.4.0

Open cobbrg opened this issue 4 years ago • 10 comments

The following code exports a good PNG image to PDF in jsPDF version 2.3.1 and below. In version 2.4.0 and 2.5.0, the image is not displayed and Acrobat Reader reports "An error exists on this page". The image is displayed with a black background when viewed in the Edge PDF viewer (previously transparent).

where imageCanvas is an HTMLCanvasElement and pdf is a jsPDF

let img = imageCanvas.toDataURL("image/png", 1.0);
pdf.addImage(img, 'PNG', 20, 20, 560, 300);
pdf.save('testPNG.pdf');

JPEG and WEBP show an image when using 2.4.0 and 2.5.0, but the JPEG background is black and the WEBP image looks like an 8-bit conversion.

cobbrg avatar Jan 12 '22 23:01 cobbrg

Cannot reproduce. This works:

const canvas = document.createElement("canvas");
canvas.width = 100;
canvas.height = 100;
const ctx = canvas.getContext("2d");
ctx.fillStyle = "red";
ctx.fillRect(0, 0, 100, 100);

const pdf = new jsPDF({ unit: "pt", format: [200, 200] });
const img = canvas.toDataURL("image/png", 1.0);
pdf.addImage(img, "PNG", 20, 20, 100, 100);
pdf.save("testPNG.pdf");

HackbrettXXX avatar Jan 18 '22 13:01 HackbrettXXX

Reproduced using the test code to display a red square. Works with 2.3.1, but not 2.4.0. PDF reader used is Adobe Acrobat DC 2021.011.20039. This is an Angular 7 project. The only package.json difference between the working and non-working projects is "jspdf": "^2.3.1" vs. "jspdf": "2.4.0". The only yarn.lock difference is the additional dependency on "@babel/runtime" "^7.14.0".

package.json and yarn.lock files attached. Thanks for investigating. package.json.txt yarn.lock.txt

cobbrg avatar Jan 18 '22 15:01 cobbrg

I've tested it with 2.4.0 and 2.5.0 and Adobe Acrobat DC 2021.011.20039. I can't reproduce it. I didn't test with Angular, although I think that shouldn't make a difference. Please provide a complete project where the bug is reproducible.

HackbrettXXX avatar Jan 21 '22 09:01 HackbrettXXX

Sample project that reproduces the issue is attached. One project is configured to use jsPDF 2.4.0 in package.json. A PDF created by this project and opened in Adobe Acrobat displays "An error exists on this page. Acrobat may not display the page correctly. Please contact the person who created the PDF document to correct the problem." The other project changes the jsPDF version in package.json to 2.3.1 and the red square is displayed with no error in Adobe Acrobat. Thanks.

Edited to include separate builds for each version of jsPDF: jsPDFTest_2.3.1.zip jsPDFTest_2.4.0.zip

cobbrg avatar Jan 21 '22 21:01 cobbrg

I also reproduced this issue using v2.5.1. I used a png url to verify that the issue was not with how I was using canvas.

const pngImg = new Image();
pngImg.src =
  'https://raw.githubusercontent.com/YourUserAccount/YourProject/master/DirectoryPath/Example.png?raw=true';
pdf.addImage(
  pngImg,
  'png',
  0,
  0,
  pngImg.width,
  pngImg.height,
);
pdf.save('pngImage.pdf')

makeitraina avatar Mar 15 '22 15:03 makeitraina

I am facing this same issue with jspdf 2.4.0 and a canvas generated from html2canvas 1.3.3. It is only failing when opening the resulting pdf with Adobe Acrobat in both windows and mac os. If I open the pdf with any other viewer (like the chrome browser) it works fine

Not sure it is related but it seems jsPDF.addImage does not wait for image to be loaded. There is a related issue: https://github.com/parallax/jsPDF/issues/3376

I was able to workaround this by waiting Image.onload() before calling jsPDF.addImage()

Hope it'll help.

TiBeN avatar May 06 '22 11:05 TiBeN

For me, it works when I call the save method, but if I try to output the document to a datauristring it fails when I make the canvas larger.

skiano avatar Aug 02 '22 23:08 skiano

I'm not sure if this will help anyone else... but I was able to work around my version of this issue by:

  1. converting the canvas to a blob instead of a dataUrl using
  2. converting the blob to a url
  3. using that url in the pdf

It looks something like this

const doc = new jsPDF({});

canvas.toBlob((blob) => {
  const url = URL.createObjectURL(blob);
  doc.addImage(url, "PNG", 20, 20, 100, 100);
}, 'image/png');

skiano avatar Aug 02 '22 23:08 skiano

I'm not sure if this will help anyone else... but I was able to work around my version of this issue by:

  1. converting the canvas to a blob instead of a dataUrl using
  2. converting the blob to a url
  3. using that url in the pdf

It looks something like this

const doc = new jsPDF({});

canvas.toBlob((blob) => {
  const url = URL.createObjectURL(blob);
  doc.addImage(url, "PNG", 20, 20, 100, 100);
}, 'image/png');

This helped me, thanks! My implementation (using html2canvas and your code snippet) is below:

    html2canvas(element, {
      width: element.clientWidth,
      height: element.clientHeight,
      useCORS: true,
      allowTaint: false
    }).then((canvas: HTMLCanvasElement) => {
      let outputCanvas: HTMLCanvasElement = document.createElement('canvas');
      let outputContext: CanvasRenderingContext2D = outputCanvas.getContext('2d');
      outputCanvas.width = this.width;
      outputCanvas.height = this.height;

      canvas.toBlob((blob) => {
        let imageObj = new Image();
        imageObj.width = this.width;
        imageObj.height = this.height;
        imageObj.style.objectFit = 'cover';
        imageObj.src = URL.createObjectURL(blob);
        imageObj.onload = () => {
          outputContext.drawImage(imageObj, 0, 0, this.width, this.height);
          if (callback) {
            callback(null, outputCanvas.toDataURL().replace('data:image/png;base64,', ''));
          }
        };
        imageObj.onerror = (error) => {
          callback(error);
        };
      });

I'm using:

Finally, I observed the black background issue even with this approach if I called outputContext.scale() prior to outputContext.drawImage().

felipecmonteiro avatar Oct 11 '23 14:10 felipecmonteiro