From 88f1d74a2ea022dc8e4c84f0f43c02f9d4895a9a Mon Sep 17 00:00:00 2001 From: Daniel Bartholomae Date: Tue, 22 Sep 2026 11:36:02 +0200 Subject: [PATCH 1/2] Let the Node build be bundled A bundler that inlines pdfkit leaves the bundle in a package without the `#standard-fonts/*` mapping and without `data/`, and a CommonJS output has no `import.meta.url` at all. The Node build bound `createRequire(import.meta.url)` and the ICC profile URL at import time, so an ESM bundle threw `Cannot find module '#standard-fonts/Helvetica'` on the first document and a CommonJS bundle threw `Invalid URL` while being imported, with no way for the application to supply the font data. Resolve both on first use, name the font and the fix when a standard font cannot be loaded, and export `registerStdFonts` from the Node build as the browser build already does. The `require('#standard-fonts/')` calls stay literal so file tracers still pick the font files up. Fixes #1801 Co-Authored-By: Claude Fable 5.1 --- CHANGELOG.md | 1 + README.md | 31 ++++++++ lib/document.browser.js | 4 +- lib/document.node.js | 45 +++++++++-- lib/mixins/pdfa.js | 28 +++++-- rollup.config.mjs | 2 +- tests/package-resolution.cjs | 5 +- tests/package-resolution.mjs | 8 +- tests/unit/bundling.spec.js | 148 +++++++++++++++++++++++++++++++++++ 9 files changed, 256 insertions(+), 16 deletions(-) create mode 100644 tests/unit/bundling.spec.js diff --git a/CHANGELOG.md b/CHANGELOG.md index d2c1a97c..bb17f058 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### Unreleased +- Fix the Node build throwing once a bundler inlines it into an application bundle, `Cannot find module '#standard-fonts/Helvetica'` on the first document from an ESM bundle and `Invalid URL` while importing a CommonJS one, because the standard font metrics and the PDF/A ICC profile were resolved relative to pdfkit's own package directory at import time. Both are now resolved on first use, a standard font that cannot be loaded names itself and the fix in its error, and `registerStdFonts` is exported from the Node build as it already was from the browser build, so a bundle can register the fonts it uses from `pdfkit/standard-fonts/`. Fixes #1801 - Fix `doc.list()` drawing the label of a `numbered` or `lettered` item with the line wrapper's own options object, which threw `unsupported number: NaN` for `align` `center` and `right`, applied a word spacing derived from the previous line to the label for `align` `justify`, and sized the underline, strike, link and goTo of every label after the first from the previous item's text - Add a `hidden` option to form annotation methods, for a field that should start hidden (e.g. one an interactive action reveals later) instead of the usual default of visible and printable - Fix annotations placed under `doc.rotate()` marking the wrong area, because `_convertRect` derived each corner's y from the already transformed x and mapped only two of the four corners, so the rectangle a viewer makes interactive did not follow the rotated content. Fixes #1153 diff --git a/README.md b/README.md index b1d10a09..ed7398ef 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,37 @@ doc.end(); complex documents with a very small amount of code. For more, see the `demo` folder and the [PDFKit programming guide](http://pdfkit.org/docs/getting_started.html). +## Bundling for Node + +The Node build loads the metrics of the 14 standard fonts on first use from +`pdfkit/standard-fonts/`, and PDF/A output reads the sRGB ICC profile +from `data/` next to the build. Both are resolved relative to pdfkit's own +package directory, which a bundle that inlines pdfkit (esbuild, rollup, +webpack, the AWS CDK's `NodejsFunction`, ...) does not have. Pick one of: + +- Mark `pdfkit` as external in the bundler and ship `node_modules/pdfkit` next + to the bundle. File tracers such as `@vercel/nft` pick up every file pdfkit + needs. +- Register the standard fonts the document uses before creating it, the same + way as in the browser: + + ```javascript + import { PDFDocument, registerStdFonts } from 'pdfkit'; + import Helvetica from 'pdfkit/standard-fonts/Helvetica'; + + registerStdFonts(Helvetica); + const doc = new PDFDocument(); + ``` + + Using a standard font that is neither registered nor loadable throws an + error naming the font. Fonts read from the file system or passed as data are + not affected. + +PDF/A output additionally reads `data/sRGB_IEC61966_2_1.icc` relative to the +bundle's `import.meta.url`: copy pdfkit's `js/data` directory next to an ESM +bundle, or register the profile under that URL with `registerFile`. A CommonJS +bundle has no `import.meta.url` and says so when PDF/A output is requested. + ## Browser Usage There are three ways to use PDFKit in the browser: diff --git a/lib/document.browser.js b/lib/document.browser.js index 6d0e2cfb..0811c13c 100644 --- a/lib/document.browser.js +++ b/lib/document.browser.js @@ -3,10 +3,10 @@ import LineWrapper from './line_wrapper'; import { registerStdFonts } from './font/standard_fonts'; import { registerFile } from '#fs'; import { fromBase64 } from './binary'; -import { ICC_PROFILE_PATH } from './mixins/pdfa'; +import { getIccProfilePath } from './mixins/pdfa'; import iccProfileBase64 from './mixins/data/sRGB_IEC61966_2_1.icc'; -registerFile(ICC_PROFILE_PATH, fromBase64(iccProfileBase64)); +registerFile(getIccProfilePath(), fromBase64(iccProfileBase64)); export { PDFDocument, LineWrapper, registerStdFonts, registerFile }; export default PDFDocument; diff --git a/lib/document.node.js b/lib/document.node.js index 213ae1e7..dc976c40 100644 --- a/lib/document.node.js +++ b/lib/document.node.js @@ -1,12 +1,20 @@ import { createRequire } from 'module'; import PDFDocument from './document'; import LineWrapper from './line_wrapper'; -import { registerStdFontLoaders } from './font/standard_fonts'; +import { + registerStdFontLoaders, + registerStdFonts, +} from './font/standard_fonts'; import { registerFile } from '#fs'; -const require = createRequire(import.meta.url); +// A bundler that folds this module into its own output leaves `import.meta.url` +// undefined (CommonJS output) or pointing at the bundle (ESM output), and the +// bundle's package has no `#standard-fonts/*` mapping either way. Neither may +// fail before a document actually asks for a standard font, so the lookup falls +// back to a base createRequire accepts and a failed lookup explains itself. +const require = createRequire(import.meta.url ?? 'file:///'); -registerStdFontLoaders({ +const loaders = { Courier: () => require('#standard-fonts/Courier'), 'Courier-Bold': () => require('#standard-fonts/CourierBold'), 'Courier-BoldOblique': () => require('#standard-fonts/CourierBoldOblique'), @@ -22,7 +30,34 @@ registerStdFontLoaders({ 'Times-Italic': () => require('#standard-fonts/TimesItalic'), 'Times-Roman': () => require('#standard-fonts/TimesRoman'), ZapfDingbats: () => require('#standard-fonts/ZapfDingbats'), -}); +}; -export { PDFDocument, LineWrapper, registerFile }; +registerStdFontLoaders( + Object.fromEntries( + Object.entries(loaders).map(([name, load]) => [ + name, + () => { + try { + return load(); + } catch (error) { + if (error?.code !== 'MODULE_NOT_FOUND') { + throw error; + } + + throw new Error( + `Cannot load the standard font "${name}" from pdfkit's package ` + + 'directory, which is not available once pdfkit is bundled ' + + 'into another file. Either mark pdfkit as external in the ' + + 'bundler, or import the fonts the document uses from ' + + '"pdfkit/standard-fonts/" and pass them to ' + + 'registerStdFonts() before creating the document.', + { cause: error }, + ); + } + }, + ]), + ), +); + +export { PDFDocument, LineWrapper, registerStdFonts, registerFile }; export default PDFDocument; diff --git a/lib/mixins/pdfa.js b/lib/mixins/pdfa.js index ff7632d2..4c106d9a 100644 --- a/lib/mixins/pdfa.js +++ b/lib/mixins/pdfa.js @@ -1,12 +1,28 @@ import fs from '#fs'; -export const ICC_PROFILE_PATH = new URL( - './data/sRGB_IEC61966_2_1.icc', - import.meta.url, -).href; - +let iccProfilePath; let iccProfile; +// Resolved on first use rather than at import time: a bundler that folds this +// module into a CommonJS output leaves `import.meta.url` undefined, and a +// document that never produces PDF/A output has no reason to fail over it. +export const getIccProfilePath = () => { + if (iccProfilePath === undefined) { + if (import.meta.url == null) { + throw new Error( + 'pdfkit cannot locate its sRGB ICC profile because import.meta.url ' + + 'is not available in this build. Bundle pdfkit as ESM or mark it ' + + 'as external in the bundler.', + ); + } + + iccProfilePath = new URL('./data/sRGB_IEC61966_2_1.icc', import.meta.url) + .href; + } + + return iccProfilePath; +}; + export default { initPDFA(pSubset) { if (pSubset.charAt(pSubset.length - 3) === '-') { @@ -28,7 +44,7 @@ export default { _addColorOutputIntent() { if (!iccProfile) { - iccProfile = fs.readFileSync(ICC_PROFILE_PATH); + iccProfile = fs.readFileSync(getIccProfilePath()); } const colorProfileRef = this.ref({ diff --git a/rollup.config.mjs b/rollup.config.mjs index aa843aed..be3811b2 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -86,7 +86,7 @@ export default [ interop: 'default', exports: 'named', footer: - 'module.exports = exports.default;\nmodule.exports.PDFDocument = exports.PDFDocument;\nmodule.exports.LineWrapper = exports.LineWrapper;\nmodule.exports.registerFile = exports.registerFile;', + 'module.exports = exports.default;\nmodule.exports.PDFDocument = exports.PDFDocument;\nmodule.exports.LineWrapper = exports.LineWrapper;\nmodule.exports.registerStdFonts = exports.registerStdFonts;\nmodule.exports.registerFile = exports.registerFile;', }, { file: 'js/pdfkit.node.mjs', diff --git a/tests/package-resolution.cjs b/tests/package-resolution.cjs index ac2ba29d..c2fe71f1 100644 --- a/tests/package-resolution.cjs +++ b/tests/package-resolution.cjs @@ -64,9 +64,11 @@ assert.equal(typeof PDFDocument, 'function'); assert.equal(PDFDocument.name, 'PDFDocument'); assert.equal(PDFDocument.PDFDocument, PDFDocument); assert.equal(typeof PDFDocument.LineWrapper, 'function'); +assert.equal(typeof PDFDocument.registerStdFonts, 'function'); assert.equal(typeof PDFDocument.registerFile, 'function'); -const { LineWrapper, registerFile } = require('pdfkit'); +const { LineWrapper, registerStdFonts, registerFile } = require('pdfkit'); assert.equal(LineWrapper, PDFDocument.LineWrapper); +assert.equal(registerStdFonts, PDFDocument.registerStdFonts); assert.equal(registerFile, PDFDocument.registerFile); assert.equal(typeof outputHelpers.toBlob, 'function'); assert.equal(typeof outputHelpers.toBytes, 'function'); @@ -87,6 +89,7 @@ assert.equal(BrowserPDFDocument.registerFile, undefined); assert.equal(typeof nodeEsModule.default, 'function'); assert.equal(nodeEsModule.PDFDocument, nodeEsModule.default); assert.equal(typeof nodeEsModule.LineWrapper, 'function'); + assert.equal(typeof nodeEsModule.registerStdFonts, 'function'); assert.equal(typeof nodeEsModule.registerFile, 'function'); const browserModule = await import(browserEsBundleUrl); diff --git a/tests/package-resolution.mjs b/tests/package-resolution.mjs index 8887976a..5ad4d13a 100644 --- a/tests/package-resolution.mjs +++ b/tests/package-resolution.mjs @@ -1,7 +1,11 @@ import assert from 'node:assert/strict'; import { createRequire } from 'node:module'; import { fileURLToPath } from 'node:url'; -import PDFDocument, { LineWrapper, registerFile } from 'pdfkit'; +import PDFDocument, { + LineWrapper, + registerStdFonts, + registerFile, +} from 'pdfkit'; import { toBytes } from 'pdfkit/output'; const require = createRequire(import.meta.url); @@ -15,8 +19,10 @@ assert.equal( assert.equal(typeof PDFDocument, 'function'); assert.equal(PDFDocument.name, 'PDFDocument'); assert.equal(typeof LineWrapper, 'function'); +assert.equal(typeof registerStdFonts, 'function'); assert.equal(typeof registerFile, 'function'); assert.equal(PDFDocument.LineWrapper, undefined); +assert.equal(PDFDocument.registerStdFonts, undefined); assert.equal(PDFDocument.registerFile, undefined); const loadedStandardFontModules = () => diff --git a/tests/unit/bundling.spec.js b/tests/unit/bundling.spec.js new file mode 100644 index 00000000..354d53d6 --- /dev/null +++ b/tests/unit/bundling.spec.js @@ -0,0 +1,148 @@ +import { + mkdirSync, + mkdtempSync, + readFileSync, + rmSync, + writeFileSync, +} from 'fs'; +import { createRequire } from 'module'; +import path from 'path'; +import { fileURLToPath, pathToFileURL } from 'url'; +import { nodeResolve } from '@rollup/plugin-node-resolve'; +import { rollup } from 'rollup'; +import Helvetica from '../../lib/font/generated/Helvetica'; + +const rootDir = fileURLToPath(new URL('../..', import.meta.url)); +const iccProfile = readFileSync( + path.join(rootDir, 'lib/mixins/data/sRGB_IEC61966_2_1.icc'), +); + +const render = (document) => + new Promise((resolve, reject) => { + const chunks = []; + document.on('data', (chunk) => chunks.push(chunk)); + document.on('end', () => resolve(Buffer.concat(chunks))); + document.on('error', reject); + document.end(); + }); + +// Bundles the Node build the way an application bundler does: every module of +// pdfkit folded into one file that lives in a package of its own, so neither +// the `#standard-fonts/*` mapping nor the `data/` directory of the pdfkit +// package are reachable from it. The bundle sits under node_modules so bare +// imports of pdfkit's dependencies still resolve and Vitest leaves it to Node. +describe('bundled node build', () => { + let bundleDir; + let esmBundle; + let cjsBundle; + + beforeAll(async () => { + const cacheDir = path.join(rootDir, 'node_modules/.cache'); + mkdirSync(cacheDir, { recursive: true }); + bundleDir = mkdtempSync(path.join(cacheDir, 'pdfkit-bundle-')); + writeFileSync(path.join(bundleDir, 'package.json'), '{}\n'); + + const bundle = await rollup({ + input: path.join(rootDir, 'lib/document.node.js'), + external: (id) => + !id.startsWith('.') && !id.startsWith('#') && !path.isAbsolute(id), + plugins: [nodeResolve({ exportConditions: ['node'] })], + onwarn: () => {}, + }); + + esmBundle = path.join(bundleDir, 'pdfkit.mjs'); + await bundle.write({ file: esmBundle, format: 'es' }); + + // A CommonJS output has no `import.meta`; esbuild substitutes an empty + // object, so every `import.meta.url` reads as undefined. + cjsBundle = path.join(bundleDir, 'pdfkit.cjs'); + await bundle.write({ + file: cjsBundle, + format: 'cjs', + exports: 'named', + plugins: [ + { name: 'no-import-meta', resolveImportMeta: () => 'undefined' }, + ], + }); + + await bundle.close(); + }); + + afterAll(() => { + rmSync(bundleDir, { recursive: true, force: true }); + }); + + describe.each([ + ['ESM', () => import(pathToFileURL(esmBundle).href)], + ['CommonJS', () => createRequire(import.meta.url)(cjsBundle)], + ])('as %s', (format, load) => { + let pdfkit; + + beforeAll(async () => { + pdfkit = await load(); + }); + + test('loads without touching the standard fonts', () => { + expect(typeof pdfkit.PDFDocument).toBe('function'); + expect(typeof pdfkit.registerStdFonts).toBe('function'); + expect(typeof pdfkit.registerFile).toBe('function'); + }); + + test('explains a standard font it cannot find', () => { + let error; + try { + new pdfkit.PDFDocument(); + } catch (caught) { + error = caught; + } + + expect(error.message).toMatch( + /^Cannot load the standard font "Helvetica" .*registerStdFonts\(\)/s, + ); + expect(error.cause.code).toBe('MODULE_NOT_FOUND'); + }); + + test('renders with a registered standard font', async () => { + pdfkit.registerStdFonts(Helvetica); + + const document = new pdfkit.PDFDocument(); + document.text('Standard fonts can be registered in a bundle'); + const output = await render(document); + + expect(output.subarray(0, 5).toString()).toBe('%PDF-'); + }); + + test('rejects a font that is still unregistered', () => { + const document = new pdfkit.PDFDocument(); + + expect(() => document.font('Courier')).toThrow( + /^Cannot load the standard font "Courier" /, + ); + }); + + if (format === 'ESM') { + test('reads the ICC profile relative to the bundle', async () => { + const document = new pdfkit.PDFDocument({ subset: 'PDF/A-1' }); + await expect(render(document)).rejects.toThrow(/ENOENT/); + + pdfkit.registerFile( + new URL('./data/sRGB_IEC61966_2_1.icc', pathToFileURL(esmBundle)) + .href, + new Uint8Array(iccProfile), + ); + const registered = new pdfkit.PDFDocument({ subset: 'PDF/A-1' }); + const output = await render(registered); + + expect(output.subarray(0, 5).toString()).toBe('%PDF-'); + }); + } else { + test('explains a missing import.meta.url when PDF/A needs the profile', async () => { + const document = new pdfkit.PDFDocument({ subset: 'PDF/A-1' }); + + await expect(render(document)).rejects.toThrow( + /import\.meta\.url is not available/, + ); + }); + } + }); +}); From fff40ec1ef89c4857954ecf491e9ff2d82361b1b Mon Sep 17 00:00:00 2001 From: Daniel Bartholomae Date: Tue, 22 Sep 2026 13:29:47 +0200 Subject: [PATCH 2/2] Require the standard fonts by a direct path instead of the import mapping The `#standard-fonts/*` entry in package.json was only ever referenced from the Node entry, so the fourteen loaders now require `./standard-fonts/.cjs` relative to the built file. That drops the mapping, and with it the check that kept its import and require conditions pointing at the same file, and lets an ESM bundle load the fonts from a copy of `js/standard-fonts` placed next to it. A CommonJS bundle still has no `import.meta.url`, so the createRequire fallback, the named error and the `registerStdFonts` export stay as they are. Co-Authored-By: Claude Fable 5.1 --- CHANGELOG.md | 2 +- README.md | 12 +++++++---- lib/document.node.js | 41 +++++++++++++++++++----------------- package.json | 3 +-- tests/package-resolution.mjs | 24 --------------------- tests/unit/bundling.spec.js | 6 +++--- 6 files changed, 35 insertions(+), 53 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb17f058..dfa5638a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ### Unreleased -- Fix the Node build throwing once a bundler inlines it into an application bundle, `Cannot find module '#standard-fonts/Helvetica'` on the first document from an ESM bundle and `Invalid URL` while importing a CommonJS one, because the standard font metrics and the PDF/A ICC profile were resolved relative to pdfkit's own package directory at import time. Both are now resolved on first use, a standard font that cannot be loaded names itself and the fix in its error, and `registerStdFonts` is exported from the Node build as it already was from the browser build, so a bundle can register the fonts it uses from `pdfkit/standard-fonts/`. Fixes #1801 +- Fix the Node build throwing once a bundler inlines it into an application bundle, `Cannot find module '#standard-fonts/Helvetica'` on the first document from an ESM bundle and `Invalid URL` while importing a CommonJS one, because the standard font metrics and the PDF/A ICC profile were resolved relative to pdfkit's own package directory at import time. Both are now resolved on first use, a standard font that cannot be loaded names itself and the fix in its error, and `registerStdFonts` is exported from the Node build as it already was from the browser build, so a bundle can register the fonts it uses from `pdfkit/standard-fonts/`. The Node build now requires the standard fonts by a path relative to the built file, which replaces the `#standard-fonts/*` import mapping and lets an ESM bundle load them from a copy of `js/standard-fonts` next to it. Fixes #1801 - Fix `doc.list()` drawing the label of a `numbered` or `lettered` item with the line wrapper's own options object, which threw `unsupported number: NaN` for `align` `center` and `right`, applied a word spacing derived from the previous line to the label for `align` `justify`, and sized the underline, strike, link and goTo of every label after the first from the previous item's text - Add a `hidden` option to form annotation methods, for a field that should start hidden (e.g. one an interactive action reveals later) instead of the usual default of visible and printable - Fix annotations placed under `doc.rotate()` marking the wrong area, because `_convertRect` derived each corner's y from the already transformed x and mapped only two of the four corners, so the rectangle a viewer makes interactive did not follow the rotated content. Fixes #1153 diff --git a/README.md b/README.md index ed7398ef..df2c518e 100644 --- a/README.md +++ b/README.md @@ -139,14 +139,18 @@ complex documents with a very small amount of code. For more, see the `demo` fol ## Bundling for Node The Node build loads the metrics of the 14 standard fonts on first use from -`pdfkit/standard-fonts/`, and PDF/A output reads the sRGB ICC profile -from `data/` next to the build. Both are resolved relative to pdfkit's own -package directory, which a bundle that inlines pdfkit (esbuild, rollup, -webpack, the AWS CDK's `NodejsFunction`, ...) does not have. Pick one of: +`standard-fonts/` next to the built file, and PDF/A output reads the sRGB ICC +profile from `data/` next to it. Both are resolved relative to the built file +inside pdfkit's package directory, which a bundle that inlines pdfkit (esbuild, +rollup, webpack, the AWS CDK's `NodejsFunction`, ...) does not sit in. Pick one +of: - Mark `pdfkit` as external in the bundler and ship `node_modules/pdfkit` next to the bundle. File tracers such as `@vercel/nft` pick up every file pdfkit needs. +- Copy pdfkit's `js/standard-fonts` directory next to an ESM bundle, which then + loads the fonts from there. A CommonJS bundle has no `import.meta.url` to + resolve against, so this route is not open to it. - Register the standard fonts the document uses before creating it, the same way as in the browser: diff --git a/lib/document.node.js b/lib/document.node.js index dc976c40..a212e732 100644 --- a/lib/document.node.js +++ b/lib/document.node.js @@ -7,29 +7,32 @@ import { } from './font/standard_fonts'; import { registerFile } from '#fs'; -// A bundler that folds this module into its own output leaves `import.meta.url` -// undefined (CommonJS output) or pointing at the bundle (ESM output), and the -// bundle's package has no `#standard-fonts/*` mapping either way. Neither may -// fail before a document actually asks for a standard font, so the lookup falls -// back to a base createRequire accepts and a failed lookup explains itself. +// The standard fonts are required by a path relative to the built file, so they +// resolve next to `js/pdfkit.js` and `js/pdfkit.node.mjs`. A bundler that +// folds this module into its own output leaves `import.meta.url` undefined +// (CommonJS output) or pointing at a bundle with no `standard-fonts/` directory +// next to it (ESM output). Neither may fail before a document actually asks for +// a standard font, so the lookup falls back to a base createRequire accepts and +// a failed lookup explains itself. const require = createRequire(import.meta.url ?? 'file:///'); const loaders = { - Courier: () => require('#standard-fonts/Courier'), - 'Courier-Bold': () => require('#standard-fonts/CourierBold'), - 'Courier-BoldOblique': () => require('#standard-fonts/CourierBoldOblique'), - 'Courier-Oblique': () => require('#standard-fonts/CourierOblique'), - Helvetica: () => require('#standard-fonts/Helvetica'), - 'Helvetica-Bold': () => require('#standard-fonts/HelveticaBold'), + Courier: () => require('./standard-fonts/Courier.cjs'), + 'Courier-Bold': () => require('./standard-fonts/CourierBold.cjs'), + 'Courier-BoldOblique': () => + require('./standard-fonts/CourierBoldOblique.cjs'), + 'Courier-Oblique': () => require('./standard-fonts/CourierOblique.cjs'), + Helvetica: () => require('./standard-fonts/Helvetica.cjs'), + 'Helvetica-Bold': () => require('./standard-fonts/HelveticaBold.cjs'), 'Helvetica-BoldOblique': () => - require('#standard-fonts/HelveticaBoldOblique'), - 'Helvetica-Oblique': () => require('#standard-fonts/HelveticaOblique'), - Symbol: () => require('#standard-fonts/Symbol'), - 'Times-Bold': () => require('#standard-fonts/TimesBold'), - 'Times-BoldItalic': () => require('#standard-fonts/TimesBoldItalic'), - 'Times-Italic': () => require('#standard-fonts/TimesItalic'), - 'Times-Roman': () => require('#standard-fonts/TimesRoman'), - ZapfDingbats: () => require('#standard-fonts/ZapfDingbats'), + require('./standard-fonts/HelveticaBoldOblique.cjs'), + 'Helvetica-Oblique': () => require('./standard-fonts/HelveticaOblique.cjs'), + Symbol: () => require('./standard-fonts/Symbol.cjs'), + 'Times-Bold': () => require('./standard-fonts/TimesBold.cjs'), + 'Times-BoldItalic': () => require('./standard-fonts/TimesBoldItalic.cjs'), + 'Times-Italic': () => require('./standard-fonts/TimesItalic.cjs'), + 'Times-Roman': () => require('./standard-fonts/TimesRoman.cjs'), + ZapfDingbats: () => require('./standard-fonts/ZapfDingbats.cjs'), }; registerStdFontLoaders( diff --git a/package.json b/package.json index dd170a6e..ceae4b28 100644 --- a/package.json +++ b/package.json @@ -153,8 +153,7 @@ "#stream": { "node": "./lib/stream/node.js", "default": "./lib/stream/browser.js" - }, - "#standard-fonts/*": "./js/standard-fonts/*.cjs" + } }, "engine": [ "node >= v20.0.0" diff --git a/tests/package-resolution.mjs b/tests/package-resolution.mjs index 5ad4d13a..0a47c64c 100644 --- a/tests/package-resolution.mjs +++ b/tests/package-resolution.mjs @@ -9,7 +9,6 @@ import PDFDocument, { import { toBytes } from 'pdfkit/output'; const require = createRequire(import.meta.url); -const packageJson = require('../package.json'); assert.equal( import.meta.resolve('pdfkit'), @@ -47,29 +46,6 @@ assert.equal( false, ); -// The node ESM build reaches the standard fonts through createRequire, so the -// require condition is the only one ever taken at runtime. Bundlers and file -// tracers walk this same file as ESM and resolve `#standard-fonts/*` under the -// import condition instead: if the two point at different files, the tracer packs -// the modules that are never loaded, omits the ones that are, and the bundle -// throws `Cannot find module` on the first document. Keep every standard font -// resolving to the one file the runtime uses, under both conditions. -const standardFonts = Object.keys(packageJson.exports) - .filter((entry) => entry.startsWith('./standard-fonts/')) - .map((entry) => entry.slice('./standard-fonts/'.length)); - -assert.equal(standardFonts.length, 14); - -for (const font of standardFonts) { - const expected = new URL(`../js/standard-fonts/${font}.cjs`, import.meta.url) - .href; - assert.equal(import.meta.resolve(`#standard-fonts/${font}`), expected); - assert.equal( - require.resolve(`#standard-fonts/${font}`), - fileURLToPath(expected), - ); -} - const fileDocument = new PDFDocument(); const fileOutput = toBytes(fileDocument); fileDocument.font( diff --git a/tests/unit/bundling.spec.js b/tests/unit/bundling.spec.js index 354d53d6..3fb481aa 100644 --- a/tests/unit/bundling.spec.js +++ b/tests/unit/bundling.spec.js @@ -27,9 +27,9 @@ const render = (document) => }); // Bundles the Node build the way an application bundler does: every module of -// pdfkit folded into one file that lives in a package of its own, so neither -// the `#standard-fonts/*` mapping nor the `data/` directory of the pdfkit -// package are reachable from it. The bundle sits under node_modules so bare +// pdfkit folded into one file that lives in a directory of its own, so neither +// the `standard-fonts/` nor the `data/` directory of the pdfkit package sits +// next to it. The bundle sits under node_modules so bare // imports of pdfkit's dependencies still resolve and Vitest leaves it to Node. describe('bundled node build', () => { let bundleDir;