From a41792981e6b90f048855870f600adef538b4dbe Mon Sep 17 00:00:00 2001 From: douglasmatheus Date: Fri, 22 May 2026 17:02:57 -0300 Subject: [PATCH] Fix putImage mutating the global filters array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The internal getFilters() returns the live reference to the document's filters array. putImage was calling splice() on that reference to strip "FlateEncode" before writing image data — but this side-effect persists between save()/output() calls. After the first putImage during a save, the document's filters array becomes empty, so all subsequent page content streams are emitted uncompressed. Result: the 1st PDF output is normal size, but every subsequent output() balloons (e.g. 5 KB compressed page stream becomes 95 KB raw, only on documents that contain images). Fix: copy the array via .slice() before mutating, so putImage's local view is independent of the document state. Repro: const doc = new jsPDF({ compress: true }); doc.addImage(jpg, "JPEG", 10, 10, 100, 75); const a = doc.output(); const b = doc.output(); console.assert(a.length === b.length); // failed before this fix Regression covered by new test in test/specs/addimage.spec.js. --- src/modules/addimage.js | 6 +++++- test/specs/addimage.spec.js | 18 +++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/modules/addimage.js b/src/modules/addimage.js index abaf2fb43..aee148fd6 100644 --- a/src/modules/addimage.js +++ b/src/modules/addimage.js @@ -203,7 +203,11 @@ import { atob } from "../libs/AtobBtoa.js"; var putStream = this.internal.putStream; var getFilters = this.internal.getFilters; - var filter = getFilters(); + // getFilters() returns the live reference to the document-wide filters + // array. Copy it before mutating so we don't strip FlateEncode from + // subsequent output() calls (which would emit page content streams + // uncompressed after the first call to putImage). + var filter = getFilters().slice(); while (filter.indexOf("FlateEncode") !== -1) { filter.splice(filter.indexOf("FlateEncode"), 1); } diff --git a/test/specs/addimage.spec.js b/test/specs/addimage.spec.js index 94eac858c..956eb073f 100644 --- a/test/specs/addimage.spec.js +++ b/test/specs/addimage.spec.js @@ -64,7 +64,9 @@ describe("Module: addimage", () => { canvas.width = 0; canvas.height = 100; - var expectedError = new Error("Given canvas must have data. Canvas width: 0, height: 100"); + var expectedError = new Error( + "Given canvas must have data. Canvas width: 0, height: 100" + ); expect(function() { doc.addImage(canvas, 10, 10); @@ -92,4 +94,18 @@ describe("Module: addimage", () => { }).not.toThrow(); }); } + + it("repeated output() after addImage produces identical PDFs (regression: putImage was mutating the global filters array)", () => { + const doc = new jsPDF({ compress: true }); + doc.addImage(jpg, "JPEG", 10, 10, 100, 75); + + const out1 = doc.output(); + const out2 = doc.output(); + const out3 = doc.output(); + + expect(out2.length).toEqual(out1.length); + expect(out3.length).toEqual(out1.length); + expect(out2).toEqual(out1); + expect(out3).toEqual(out1); + }); });