diff --git a/bun.lock b/bun.lock index ab0a685139..7227a29c2c 100644 --- a/bun.lock +++ b/bun.lock @@ -184,7 +184,7 @@ "mitt": "^3.0.1", "motion": "^12.34.3", "nanoid": "^5.1.6", - "pdfkit": "^0.19.1", + "pdfkit": "^0.20.2", "tailwind-merge": "^3.5.0", "three-bvh-csg": "^0.0.18", "three-mesh-bvh": "~0.9.8", @@ -1170,8 +1170,6 @@ "brotli": ["brotli@1.3.3", "", { "dependencies": { "base64-js": "^1.1.2" } }, "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg=="], - "browserify-zlib": ["browserify-zlib@0.2.0", "", { "dependencies": { "pako": "~1.0.5" } }, "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA=="], - "browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="], "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], @@ -1600,8 +1598,6 @@ "jose": ["jose@6.2.3", "", {}, "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw=="], - "js-md5": ["js-md5@0.8.3", "", {}, "sha512-qR0HB5uP6wCuRMrWPTrkMaev7MJZwJuuw4fnwAzRgP4J4/F8RwtodOKpGp4XpqsLBFzzgqIO42efFAyz2Et6KQ=="], - "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], "js-yaml": ["js-yaml@4.2.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw=="], @@ -1810,7 +1806,7 @@ "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - "pdfkit": ["pdfkit@0.19.1", "", { "dependencies": { "@noble/ciphers": "^1.0.0", "@noble/hashes": "^1.6.0", "fontkit": "^2.0.4", "js-md5": "^0.8.3", "linebreak": "^1.1.0", "png-js": "^1.1.0" } }, "sha512-6Gzk+wDwTs4VSxsR5rCMTnIl5nlmkye1oWB0l2hDB1EX6ZNSIBroKQEv+2+fPPn+stVjyqzmsqRJVDfB9fo5DA=="], + "pdfkit": ["pdfkit@0.20.2", "", { "dependencies": { "@noble/ciphers": "^1.3.0", "@noble/hashes": "^1.8.0", "fflate": "^0.8.3", "fontkit": "^2.0.4", "linebreak": "^1.1.0", "png-js": "^2.0.0" } }, "sha512-Q/w03ICAQyXfHNfTsg1udp0ADerdBN0s7a6XSPL7J7Ro6ABnafoBOCDBstIO9U02mvzHEV8HrvFIw5iV5ejIRA=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], @@ -1818,7 +1814,7 @@ "pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="], - "png-js": ["png-js@1.1.0", "", { "dependencies": { "browserify-zlib": "^0.2.0" } }, "sha512-PM/uYGzGdNSzqeOgly68+6wKQDL1SY0a/N+OEa/+br6LnHWOAJB0Npiamnodfq3jd2LS/i2fMeOKSAILjA+m5Q=="], + "png-js": ["png-js@2.0.0", "", { "dependencies": { "fflate": "^0.8.2" } }, "sha512-GdzJuUMc6ZSpxFJWVxtOH1bzYHym+TOnveqUjb+VJIbZWbZzyiRGFiKhbiielfpYbgMlhHVhsJ0FTazfuRFkMA=="], "polygon-clipping": ["polygon-clipping@0.15.7", "", { "dependencies": { "robust-predicates": "^3.0.2", "splaytree": "^3.1.0" } }, "sha512-nhfdr83ECBg6xtqOAJab1tbksbBAOMUltN60bU+llHVOL0e5Onm1WpAXXWXVB39L8AJFssoIhEVuy/S90MmotA=="], @@ -2204,8 +2200,6 @@ "agent-install/commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], - "browserify-zlib/pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="], - "conf/semver": ["semver@7.8.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-wnilbGyMxzbY7dNOl7jpKbLSjcfeweJWU5j4+u5qW+6/wuGD9KzIGOyZnQVSBM9E7DtWaaH3CyHkppYrKYoxwg=="], "deslop-js/fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], diff --git a/packages/editor/package.json b/packages/editor/package.json index 1cd4fa24f1..b13cdbb386 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -57,7 +57,7 @@ "mitt": "^3.0.1", "motion": "^12.34.3", "nanoid": "^5.1.6", - "pdfkit": "^0.19.1", + "pdfkit": "^0.20.2", "tailwind-merge": "^3.5.0", "three-bvh-csg": "^0.0.18", "three-mesh-bvh": "~0.9.8", diff --git a/packages/editor/src/lib/floorplan/floorplan-pdfkit-document.ts b/packages/editor/src/lib/floorplan/floorplan-pdfkit-document.ts index 6788bc63eb..f1653b0d29 100644 --- a/packages/editor/src/lib/floorplan/floorplan-pdfkit-document.ts +++ b/packages/editor/src/lib/floorplan/floorplan-pdfkit-document.ts @@ -1,7 +1,11 @@ +/// import type PdfKitDocument from 'pdfkit' type PdfKitDocumentInstance = InstanceType +/** pdfkit 0.20's browser-build export, which @types/pdfkit does not describe. */ +type PdfKitStandardFontRegistry = { registerStdFonts?: (...fonts: unknown[]) => void } + type PdfTextOptions = { align?: 'left' | 'center' | 'right' maxWidth?: number @@ -461,16 +465,29 @@ export async function createFloorplanPdfDocument( defaultPageSize: readonly [number, number], options: { title?: string } = {}, ) { - const [{ default: PDFDocument }, { default: blobStream }, fonts] = await Promise.all([ - import('pdfkit/js/pdfkit.standalone'), - import('blob-stream'), - // an export in the standard fonts beats no export when the font files are unreachable - loadFloorplanPdfFonts().catch((error: unknown) => { - console.warn('[floorplan-export] Falling back to the standard PDF fonts', error) - return null - }), - ]) - const raw = new PDFDocument({ + const [pdfkit, helvetica, helveticaBold, courier, courierBold, { default: blobStream }, fonts] = + await Promise.all([ + import('pdfkit'), + import('pdfkit/standard-fonts/Helvetica'), + import('pdfkit/standard-fonts/HelveticaBold'), + import('pdfkit/standard-fonts/Courier'), + import('pdfkit/standard-fonts/CourierBold'), + import('blob-stream'), + // an export in the standard fonts beats no export when the font files are unreachable + loadFloorplanPdfFonts().catch((error: unknown) => { + console.warn('[floorplan-export] Falling back to the standard PDF fonts', error) + return null + }), + ]) + // The browser build has no standard-font metrics until they are registered, and + // the constructor already sets Helvetica; the Node build loads its own and has no registry. + ;(pdfkit as PdfKitStandardFontRegistry).registerStdFonts?.( + helvetica.default, + helveticaBold.default, + courier.default, + courierBold.default, + ) + const raw = new pdfkit.default({ autoFirstPage: false, compress: true, margin: 0, diff --git a/packages/editor/src/lib/floorplan/pdfkit-standard-fonts.d.ts b/packages/editor/src/lib/floorplan/pdfkit-standard-fonts.d.ts new file mode 100644 index 0000000000..0b8adff53b --- /dev/null +++ b/packages/editor/src/lib/floorplan/pdfkit-standard-fonts.d.ts @@ -0,0 +1,5 @@ +// pdfkit 0.20 ships these entry points without types, and @types/pdfkit predates them. +declare module 'pdfkit/standard-fonts/*' { + const font: unknown + export default font +}