From 416646c8adb13f704d952c5bba493c964b8fffab Mon Sep 17 00:00:00 2001 From: HawkForge <75696486+HawkForge@users.noreply.github.com> Date: Fri, 16 Oct 2026 00:31:52 +0600 Subject: [PATCH] Write the embedded file CheckSum as a hex string of the raw MD5 bytes Fixes #1531 --- CHANGELOG.md | 1 + lib/mixins/attachments.js | 11 +++++++---- tests/unit/attachments.spec.js | 6 +++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad6b9729..03a9f630 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### Unreleased +- Fix the `/CheckSum` of an embedded file being written as a 32-character literal string of hex digits, instead of the 16 raw bytes of the MD5 digest the PDF specification requires, which validators such as veraPDF and pdfcpu reject. It is now written as a hex string of those bytes. Fixes #1531 - Fix `doc.table()` parsing a font passed as binary data (e.g. registered with `registerFont(name, uint8Array)`) again for every cell, when it restores the font after measuring and drawing the cell, which made tables with such fonts slower. A font loaded from the same `Uint8Array` is now reused without parsing it again - Add `lineBreak: 'whitespace'` to `doc.text()`, which breaks a line only at whitespace, a soft hyphen or a zero-width space. The Unicode line breaking rules the wrapper follows also allow a break after a hyphen or a slash, so `design-system` or `https://github.com/foliojs/pdfkit` could be cut at a line end, and a text extractor that joins the lines, such as a résumé parser, reads two words - Add an `objectStreams` option, which packs the objects that are not streams into compressed object streams and writes a cross-reference stream instead of the xref table, making documents with many such objects, e.g. tagged ones, much smaller. It requires a `pdfVersion` of `1.5` or higher, and cannot be used with encryption or PDF/A-1. Fixes #1810 diff --git a/lib/mixins/attachments.js b/lib/mixins/attachments.js index e9f7e6eb..360969f6 100644 --- a/lib/mixins/attachments.js +++ b/lib/mixins/attachments.js @@ -1,5 +1,5 @@ import fs from '#fs'; -import { md5Hex } from '../crypto/md5'; +import { md5Hash } from '../crypto/md5'; import { escapeName } from '../object.js'; import { fromBase64 } from '../binary'; @@ -67,8 +67,7 @@ export default { } // add checksum and size information - const checksum = md5Hex(new Uint8Array(data)); - refBody.Params.CheckSum = new String(checksum); + refBody.Params.CheckSum = md5Hash(new Uint8Array(data)); refBody.Params.Size = data.byteLength; // save some space when embedding the same file again @@ -113,11 +112,15 @@ export default { }, }; +function bytesEqual(a, b) { + return a.length === b.length && a.every((byte, i) => byte === b[i]); +} + /** check two embedded file metadata objects for equality */ function isEqual(a, b) { return ( a.Subtype === b.Subtype && - a.Params.CheckSum.toString() === b.Params.CheckSum.toString() && + bytesEqual(a.Params.CheckSum, b.Params.CheckSum) && a.Params.Size === b.Params.Size && isSameDate(a.Params.CreationDate, b.Params.CreationDate) && isSameDate(a.Params.ModDate, b.Params.ModDate) diff --git a/tests/unit/attachments.spec.js b/tests/unit/attachments.spec.js index d911526a..1efb2466 100644 --- a/tests/unit/attachments.spec.js +++ b/tests/unit/attachments.spec.js @@ -39,7 +39,7 @@ describe('file', () => { /Params << /CreationDate (D:20180201000000Z) /ModDate (D:20180201000000Z) -/CheckSum (${md5}) +/CheckSum <${md5}> /Size 12 >> /Subtype /text#2Fplain @@ -97,7 +97,7 @@ describe('file', () => { /Params << /CreationDate (D:20180201000000Z) /ModDate (D:20180201000000Z) -/CheckSum (${md5}) +/CheckSum <${md5}> /Size 12 >> /Length 20 @@ -155,7 +155,7 @@ describe('file', () => { /Params << /CreationDate (D:20180201000000Z) /ModDate (D:20180201000000Z) -/CheckSum (${md5}) +/CheckSum <${md5}> /Size 12 >> /Length 20