-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Invalid String Length When Saving PDF #1724
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Below is the code that I'm running when this error is generated:
|
I had this issue as well our reports generate 3000+ pages with images attached. we were running out of memory but as it stands now. We managed to fix the issue. but we are not using html2canvas. |
Actually the problem is that javascript can only handle a specific length of a string. And this is the problem, because the mentioned line is content.join('\n'); We can not modify the string length of the javascript engine. Probably we have to reduce the amount of memory/string length by using compression. |
Can you please update which API you are using, please share the link for new API to generate PDF. Thanks |
I got same error when im making a pdf of about 600 pages with a image about 1.5M on every page. |
Hello there, |
@steve231293 I know this is late but it might still be useful to others. CauseAs we've found out the library is able to generate very large PDFs but crashes when it tries to save them, as it tries to concatenate an array of lines into a single string. Node uses V8 which has a maximum string size cap of 2^29 = 512M on 64-bit platforms, which is the maximum it can support given that any heap object's size in bytes must fit into a Smi (which are now 31-bit on all 64-bit platforms, with or without pointer compression).
FixTo fix this we patched the library to return a string array instead of a single string. To do the patching you can simply add I've attached the patch file for jspdf version Or if you want to apply the patch manually here is the diff diff --git a/src/jspdf.js b/src/jspdf.js
index fa3dd923..39418ae3 100644
--- a/src/jspdf.js
+++ b/src/jspdf.js
@@ -2975,7 +2975,7 @@ function jsPDF(options) {
}
});
- var buildDocument = (API.__private__.buildDocument = function() {
+ var buildDocument = (API.__private__.buildDocument = function(raw = false) {
resetDocument();
setOutputDestination(content);
@@ -2998,6 +2998,14 @@ function jsPDF(options) {
setOutputDestination(pages[currentPage]);
+ if (raw) {
+ for (let index = 0; index < content.length - 1; index++) {
+ content[index] += "\n";
+ }
+
+ return content;
+ }
+
return content.join("\n");
});
@@ -3048,6 +3056,8 @@ function jsPDF(options) {
switch (type) {
case undefined:
return buildDocument();
+ case "stringArray":
+ return buildDocument(true);
case "save":
API.save(options.filename);
break;
(END) Using the fixWe then turn this string array into a readable stream and stream its results directly to S3 therefore bypassing the string size limitation. Note that javascript strings are unicode, but jspdf is ASCII so streaming unicode strings would result in corrupted data, instead we convert the ASCII strings to a byte array to fix the problem. const pdfOutput = doc.output('stringArray');
const readableStream = new Readable({
read() {
const chunk = pdfOutput.shift();
if (chunk) {
// convert string to bytes to keep PDF ascii encoding
// if we don't do this the PDF will break and show blank
this.push(Uint8Array.from(chunk, (x) => x.charCodeAt(0)));
} else {
this.push(null);
}
}
});
const passThrough = new PassThrough();
const uploadToS3 = new Upload({
client: new S3({
...
}),
params: {
...
Body: passThrough
},
});
readableStream.on('data', (chunk) => {
// S3 streaming also breaks unless we force ascii encoding here as well
passThrough.write(chunk, 'ascii');
});
readableStream.on('end', () => {
passThrough.end();
});
await uploadToS3.done(); Testing the fix
This script begins generating a PDF with 400 pages, encoding a 4k image on each page. We also add some text over the image to ensure that the images cannot be re-referenced. Since each image is embedded as base64 we will quickly hit the JavaScript string size limit and it'll crash on the save step. You might need to tweak the number of pages (400) to align with the image size etc. Here is the messy script const fs = require("fs");
const process = require("process");
const sharp = require("sharp");
const sizeOf = require("image-size");
const { Readable } = require("stream");
const file = fs.readFileSync("test.jpg");
const dimensions = sizeOf(file);
function* gen(start, end) {
for (let i = start; i <= end; i++) yield i;
}
function logIt(num) {
console.log(
`${num} ${Object.entries(process.memoryUsage()).reduce(
(carry, [key, value]) => {
return `${carry}${key}:${
Math.round((value / 1024 / 1024) * 100) / 100
}MB `;
},
""
)}`
);
}
async function runTest() {
try {
let doc = new jsPDF();
for await (const num of gen(1, 400)) {
// render a number over the image to increase file size
// prevents JSPDF reusing the same image reference
await sharp(file)
.composite([
{
input: Buffer.from(` <svg width="${dimensions.width}" height="${dimensions.height}"> <style> .title { fill: #fff; font-size: 1000px; font-weight: bold;} </style> <text x="50%" y="50%" text-anchor="middle" class="title">#${num}</text> </svg> `),
top: 0,
left: 0,
},
])
.toBuffer({
resolveWithObject: true,
})
.then(({ data, info }) => {
doc.setFontSize(40);
doc.text(`test page ${num}`, 35, 25);
doc.addImage(
`data:image/${info.format};base64,${data.toString("base64")}`,
"JPEG",
15,
40,
180,
180
);
doc.addPage();
});
logIt(num);
}
// broken version - should throw an error
const buffer = Buffer.from(doc.output("arraybuffer"));
fs.writeFileSync("./x.pdf", buffer, {encoding: "ascii"});
// patched version - should be working
const readableStream = Readable.from(doc.output("stringArray"));
const writableStream = fs.createWriteStream("./x.pdf", {flags: 'a', encoding: "ascii"});
readableStream.on("data", (row) => {
writableStream.write(row);
});
} catch (e) {
console.log(e);
}
}
runTest(); |
@leviznull You are aware that there are 2 pending pull requests for this bug (#3625, #3646). There are already multiple solutions for this. For now, you can just use my fork:
And do:
|
Thank you @leviznull and @laucheukhim |
@laucheukhim @leviznull Hey guys, I’m facing the same "string too long" issue when trying to add multiple different images into a PDF document using jsPDF. I followed @laucheukhim's suggestion by downloading the zip folder and importing the jspdf.umd.min.js file into my Laravel web project, but unfortunately, it hasn't resolved the issue. Is there something I might be overlooking? |
Thank you for submitting an issue to jsPDF. Please read carefully.
Are you using the latest version of jsPDF?
Yes (1.3.5)
Have you tried using jspdf.debug.js?
Yes
Steps to reproduce
What I saw
The save function silently fails, and a console log error displays with an Invalid string length error:
What I expected
I expected the pdf file to save.
The text was updated successfully, but these errors were encountered: