From 8d811cae8d2cc0e5c629c4755266b20069f65b64 Mon Sep 17 00:00:00 2001 From: JamBalaya56562 Date: Sun, 28 Jun 2026 09:34:29 +0900 Subject: [PATCH] Accept Uint8Array (and other ArrayBuffer views) as image source doc.image() handled Buffer, ArrayBuffer, base64 data URIs and file paths, but a plain Uint8Array (e.g. bytes from S3 or fetch) fell through to fs.readFileSync and threw ERR_INVALID_ARG_VALUE. Add an ArrayBuffer.isView branch after the Buffer check so typed arrays and DataView are wrapped via Buffer.from(buffer, byteOffset, byteLength), preserving byteOffset/byteLength for subarray views. Fixes #1446 Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 1 + lib/image.js | 4 ++++ tests/unit/image.spec.js | 17 +++++++++++++++++ 3 files changed, 22 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8155b4e35..45fcf9be9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - [BREAKING CHANGE] Restrict AcroForm options to documented mappings and explicit escape hatches. - [BREAKING CHANGE] Stop automatically uppercasing annotation option keys. - Do not mutate options passed to `doc.annotate()` and its convenience methods (link, note, strike, lineAnnotation, rectAnnotation, ellipseAnnotation, textAnnotation, fileAnnotation) +- Accept `Uint8Array` (and other `ArrayBuffer` views) as an image source in `doc.image()` ([#1446](https://github.com/foliojs/pdfkit/issues/1446)) ### [v0.19.1] - 2026-06-10 diff --git a/lib/image.js b/lib/image.js index 5b7535c7f..1017e9fb9 100644 --- a/lib/image.js +++ b/lib/image.js @@ -14,6 +14,10 @@ class PDFImage { data = src; } else if (src instanceof ArrayBuffer) { data = Buffer.from(new Uint8Array(src)); + } else if (ArrayBuffer.isView(src)) { + // Typed arrays (e.g. Uint8Array) and DataView. Buffer is handled above, + // so this covers views that are not Node Buffers, preserving byteOffset/length. + data = Buffer.from(src.buffer, src.byteOffset, src.byteLength); } else { const match = /^data:.+?;base64,(.*)$/.exec(src); if (match) { diff --git a/tests/unit/image.spec.js b/tests/unit/image.spec.js index d76ad077e..171d666a7 100644 --- a/tests/unit/image.spec.js +++ b/tests/unit/image.spec.js @@ -21,6 +21,23 @@ describe('Image', function () { expect(document.y).toBe(originalY + imageHeight); }); + test('accepts a Uint8Array as image source (issue #1446)', () => { + const bytes = new Uint8Array(fs.readFileSync('./tests/images/bee.png')); + expect(bytes).toBeInstanceOf(Uint8Array); + expect(Buffer.isBuffer(bytes)).toBe(false); + expect(() => document.image(bytes, 0, 0, { width: 50 })).not.toThrow(); + }); + + test('accepts a Uint8Array view with a byteOffset as image source (issue #1446)', () => { + const file = fs.readFileSync('./tests/images/bee.png'); + // Place the PNG bytes inside a larger buffer so the view has a non-zero offset. + const backing = new Uint8Array(file.length + 8); + backing.set(file, 8); + const view = backing.subarray(8); + expect(view.byteOffset).toBe(8); + expect(() => document.image(view, 0, 0, { width: 50 })).not.toThrow(); + }); + test('parse JPEG with null byte padding in EXIF (issue #1175)', () => { const data = fs.readFileSync('./tests/images/issue-1175.jpeg'); const jpeg = new JPEG(data, 'test');