From 9a6b786c8372027ae48384bf4c62f358abf2a7e0 Mon Sep 17 00:00:00 2001 From: Pratik Dulal Date: Wed, 29 Jul 2026 16:05:49 +0545 Subject: [PATCH] fix: stop bare addPage() from re-forcing the document's default font PDFPage's constructor applies options.font/options.fontSize whenever they're set, but addPage() reuses the exact same options object passed to the PDFDocument constructor when called with no arguments. So if a default font was set via `new PDFDocument({ font: ... })`, every subsequent bare addPage() call silently re-applied it, clobbering any doc.font()/doc.fontSize() call made since the previous page (#1739). initFonts() already applies the constructor's default font/fontSize once, before the first page is even created, so PDFPage re-deriving them from options was only ever necessary for two cases: the very first page (harmless no-op re-application) and an explicit addPage({ font, fontSize }) call, which is a documented way to override for a specific page. Only apply them in those two cases now, identified by checking whether the options object is the exact one reused from document.options and whether a page already exists. --- lib/page.js | 17 +++++++++++++++-- tests/unit/page.spec.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/lib/page.js b/lib/page.js index 9a8b0be39..87d93db98 100644 --- a/lib/page.js +++ b/lib/page.js @@ -85,8 +85,21 @@ class PDFPage { this.content = this.document.ref(); - if (options.font) document.font(options.font, options.fontFamily); - if (options.fontSize) document.fontSize(options.fontSize); + // `options` defaults to `document.options` (the PDFDocument constructor options) when + // addPage() is called without arguments, so it's the same object for every page. Applying + // font/fontSize from it unconditionally would silently override any doc.font()/doc.fontSize() + // call made since the previous page. Only (re-)apply them here when they were explicitly + // passed to this addPage() call, or for the very first page (document.page is still null), + // where they're the intended initial default - already applied by initFonts(), so this is + // just keeping fontSize/margin calculation below consistent. + const isInheritedDefaultOptions = options === document.options; + const isFirstPage = document.page == null; + if (options.font && (!isInheritedDefaultOptions || isFirstPage)) { + document.font(options.font, options.fontFamily); + } + if (options.fontSize && (!isInheritedDefaultOptions || isFirstPage)) { + document.fontSize(options.fontSize); + } // process margins // Margin calculation must occur after font assignment to ensure any dynamic sizes are calculated correctly diff --git a/tests/unit/page.spec.js b/tests/unit/page.spec.js index 8164517f4..1aff920e4 100644 --- a/tests/unit/page.spec.js +++ b/tests/unit/page.spec.js @@ -28,4 +28,34 @@ describe('page', function () { expect(page.margins).toEqual({ top: 0, right: 0, bottom: 0, left: 0 }); }); }); + + // https://github.com/foliojs/pdfkit/issues/1739 + describe('font persistence across addPage()', function () { + test('a font set with doc.font() persists onto a bare addPage() call', function () { + const doc = new PDFDocument({ font: 'Helvetica' }); + doc.font('Helvetica-Bold'); + doc.addPage(); + expect(doc._font.name).toBe('Helvetica-Bold'); + }); + + test('a font size set with doc.fontSize() persists onto a bare addPage() call', function () { + const doc = new PDFDocument({ font: 'Helvetica' }); + doc.fontSize(30); + doc.addPage(); + expect(doc._fontSize).toBe(30); + }); + + test('an explicit font passed to addPage() still overrides the current font', function () { + const doc = new PDFDocument({ font: 'Helvetica' }); + doc.font('Helvetica-Bold'); + doc.addPage({ font: 'Times-Roman' }); + expect(doc._font.name).toBe('Times-Roman'); + }); + + test('an explicit fontSize passed to addPage() still applies', function () { + const doc = new PDFDocument({ font: 'Helvetica' }); + doc.addPage({ fontSize: 20 }); + expect(doc._fontSize).toBe(20); + }); + }); });