addImage PNG fails >= 2.4.0
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.
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");
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
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.
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
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')
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.
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.
I'm not sure if this will help anyone else... but I was able to work around my version of this issue by:
- converting the canvas to a blob instead of a dataUrl using
- converting the blob to a url
- 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');
I'm not sure if this will help anyone else... but I was able to work around my version of this issue by:
- converting the canvas to a blob instead of a dataUrl using
- converting the blob to a url
- 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:
- Angular 7
- [email protected]
- [email protected]
Finally, I observed the black background issue even with this approach if I called outputContext.scale() prior to outputContext.drawImage().