pdf-codec 3.7.0 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -260,6 +260,7 @@ function decodeJpeg2000(data, options = {}) {
260
260
  const siz = codestream.siz;
261
261
  const width = siz.xsiz - siz.xosiz;
262
262
  const height = siz.ysiz - siz.yosiz;
263
+ if (options.maxWidth !== void 0 && width > options.maxWidth || options.maxHeight !== void 0 && height > options.maxHeight || options.maxPixels !== void 0 && width * height > options.maxPixels) throw new Error(`cannot decode a ${String(width)}x${String(height)} JPEG 2000 canvas; this exceeds the caller's configured size bound`);
263
264
  const componentCount = siz.components.length;
264
265
  const first = siz.components[0];
265
266
  if (first === void 0) throw new require_image_jpeg2000_errors.Jpeg2000ParseError("the codestream declares no components");
@@ -38,6 +38,9 @@ interface Jpeg2000Metadata {
38
38
  }
39
39
  interface Jpeg2000DecodeOptions {
40
40
  readonly onWarning?: (message: string) => void;
41
+ readonly maxWidth?: number;
42
+ readonly maxHeight?: number;
43
+ readonly maxPixels?: number;
41
44
  }
42
45
  interface Jpeg2000Image {
43
46
  readonly width: number;
@@ -38,6 +38,9 @@ interface Jpeg2000Metadata {
38
38
  }
39
39
  interface Jpeg2000DecodeOptions {
40
40
  readonly onWarning?: (message: string) => void;
41
+ readonly maxWidth?: number;
42
+ readonly maxHeight?: number;
43
+ readonly maxPixels?: number;
41
44
  }
42
45
  interface Jpeg2000Image {
43
46
  readonly width: number;
@@ -259,6 +259,7 @@ function decodeJpeg2000(data, options = {}) {
259
259
  const siz = codestream.siz;
260
260
  const width = siz.xsiz - siz.xosiz;
261
261
  const height = siz.ysiz - siz.yosiz;
262
+ if (options.maxWidth !== void 0 && width > options.maxWidth || options.maxHeight !== void 0 && height > options.maxHeight || options.maxPixels !== void 0 && width * height > options.maxPixels) throw new Error(`cannot decode a ${String(width)}x${String(height)} JPEG 2000 canvas; this exceeds the caller's configured size bound`);
262
263
  const componentCount = siz.components.length;
263
264
  const first = siz.components[0];
264
265
  if (first === void 0) throw new Jpeg2000ParseError("the codestream declares no components");
@@ -1,9 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_objects = require("./objects.cjs");
3
3
  const require_filters = require("./filters.cjs");
4
- const require_image_png_encode = require("./image/png-encode.cjs");
5
- const require_image_jpeg_info = require("./image/jpeg-info.cjs");
6
4
  const require_image_jpeg2000 = require("./image/jpeg2000.cjs");
5
+ let byte_codec = require("byte-codec");
7
6
  //#region src/images-read.ts
8
7
  function componentsOf(cs) {
9
8
  if (cs.kind === "gray") return 1;
@@ -185,13 +184,18 @@ function jpeg2000ChannelKind(image, dict, resolver, sink) {
185
184
  function readJpeg2000Image(dict, raw, resolver, sink) {
186
185
  let image;
187
186
  try {
188
- image = require_image_jpeg2000.decodeJpeg2000(raw, { onWarning: (message) => {
189
- sink({
190
- code: "image/jpx-degraded",
191
- severity: "warning",
192
- message
193
- });
194
- } });
187
+ image = require_image_jpeg2000.decodeJpeg2000(raw, {
188
+ onWarning: (message) => {
189
+ sink({
190
+ code: "image/jpx-degraded",
191
+ severity: "warning",
192
+ message
193
+ });
194
+ },
195
+ maxWidth: byte_codec.PNG_MAX_DIMENSION,
196
+ maxHeight: byte_codec.PNG_MAX_DIMENSION,
197
+ maxPixels: byte_codec.PNG_MAX_PIXELS
198
+ });
195
199
  } catch (error) {
196
200
  sink({
197
201
  code: "image/jpx-undecodable",
@@ -243,7 +247,7 @@ function readJpeg2000Image(dict, raw, resolver, sink) {
243
247
  } : rawImage;
244
248
  return {
245
249
  format: "png",
246
- bytes: require_image_png_encode.encodePng(withAlpha),
250
+ bytes: (0, byte_codec.encodePng)(withAlpha),
247
251
  widthPx: image.width,
248
252
  heightPx: image.height
249
253
  };
@@ -261,7 +265,7 @@ function readImageXObject(dict, raw, resolver, sink) {
261
265
  if (decoded.remainingFilter === "DCTDecode") {
262
266
  let info;
263
267
  try {
264
- info = require_image_jpeg_info.readJpegInfo(decoded.bytes);
268
+ info = (0, byte_codec.readJpegInfo)(decoded.bytes);
265
269
  } catch {
266
270
  sink({
267
271
  code: "image/undecodable",
@@ -281,11 +285,11 @@ function readImageXObject(dict, raw, resolver, sink) {
281
285
  if (decoded.remainingFilter !== void 0) return;
282
286
  const width = require_objects.asNumber(require_objects.dictGet(dict, "Width") ?? require_objects.dictGet(dict, "W"));
283
287
  const height = require_objects.asNumber(require_objects.dictGet(dict, "Height") ?? require_objects.dictGet(dict, "H"));
284
- if (width === void 0 || height === void 0 || width <= 0 || height <= 0) {
288
+ if (width === void 0 || height === void 0 || !Number.isInteger(width) || !Number.isInteger(height) || width <= 0 || height <= 0 || width > byte_codec.PNG_MAX_DIMENSION || height > byte_codec.PNG_MAX_DIMENSION || width * height > byte_codec.PNG_MAX_PIXELS) {
285
289
  sink({
286
290
  code: "image/undecodable",
287
291
  severity: "warning",
288
- message: "image XObject is missing a valid /Width or /Height; skipping"
292
+ message: "image XObject has an invalid, out-of-range, or unusably large /Width or /Height; skipping"
289
293
  });
290
294
  return;
291
295
  }
@@ -317,7 +321,7 @@ function readImageXObject(dict, raw, resolver, sink) {
317
321
  } : rawImage;
318
322
  return {
319
323
  format: "png",
320
- bytes: require_image_png_encode.encodePng(withAlpha),
324
+ bytes: (0, byte_codec.encodePng)(withAlpha),
321
325
  widthPx: width,
322
326
  heightPx: height
323
327
  };
@@ -1,8 +1,7 @@
1
1
  import { asArray, asBool, asName, asNumber, dictGet } from "./objects.js";
2
2
  import { decodeStream } from "./filters.js";
3
- import { encodePng } from "./image/png-encode.js";
4
- import { readJpegInfo } from "./image/jpeg-info.js";
5
3
  import { decodeJpeg2000 } from "./image/jpeg2000.js";
4
+ import { PNG_MAX_DIMENSION, PNG_MAX_PIXELS, encodePng, readJpegInfo } from "byte-codec";
6
5
  //#region src/images-read.ts
7
6
  function componentsOf(cs) {
8
7
  if (cs.kind === "gray") return 1;
@@ -184,13 +183,18 @@ function jpeg2000ChannelKind(image, dict, resolver, sink) {
184
183
  function readJpeg2000Image(dict, raw, resolver, sink) {
185
184
  let image;
186
185
  try {
187
- image = decodeJpeg2000(raw, { onWarning: (message) => {
188
- sink({
189
- code: "image/jpx-degraded",
190
- severity: "warning",
191
- message
192
- });
193
- } });
186
+ image = decodeJpeg2000(raw, {
187
+ onWarning: (message) => {
188
+ sink({
189
+ code: "image/jpx-degraded",
190
+ severity: "warning",
191
+ message
192
+ });
193
+ },
194
+ maxWidth: PNG_MAX_DIMENSION,
195
+ maxHeight: PNG_MAX_DIMENSION,
196
+ maxPixels: PNG_MAX_PIXELS
197
+ });
194
198
  } catch (error) {
195
199
  sink({
196
200
  code: "image/jpx-undecodable",
@@ -280,11 +284,11 @@ function readImageXObject(dict, raw, resolver, sink) {
280
284
  if (decoded.remainingFilter !== void 0) return;
281
285
  const width = asNumber(dictGet(dict, "Width") ?? dictGet(dict, "W"));
282
286
  const height = asNumber(dictGet(dict, "Height") ?? dictGet(dict, "H"));
283
- if (width === void 0 || height === void 0 || width <= 0 || height <= 0) {
287
+ if (width === void 0 || height === void 0 || !Number.isInteger(width) || !Number.isInteger(height) || width <= 0 || height <= 0 || width > PNG_MAX_DIMENSION || height > PNG_MAX_DIMENSION || width * height > PNG_MAX_PIXELS) {
284
288
  sink({
285
289
  code: "image/undecodable",
286
290
  severity: "warning",
287
- message: "image XObject is missing a valid /Width or /Height; skipping"
291
+ message: "image XObject has an invalid, out-of-range, or unusably large /Width or /Height; skipping"
288
292
  });
289
293
  return;
290
294
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pdf-codec",
3
- "version": "3.7.0",
3
+ "version": "4.0.1",
4
4
  "description": "Hand-written, dependency-minimal PDF codec: parses arbitrary real-world PDFs and generates new ones, built on its own codec-owned LayoutDocument item model and Zod 4 codecs.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -99,8 +99,8 @@
99
99
  "license": "MIT",
100
100
  "packageManager": "pnpm@11.6.0",
101
101
  "dependencies": {
102
- "byte-codec": "^1.3.0",
103
- "document-schema.js": "^6.1.0",
102
+ "byte-codec": "^1.4.0",
103
+ "document-schema.js": "^6.2.0",
104
104
  "fflate": "^0.8.3",
105
105
  "zod": "^4.4.3"
106
106
  },
@@ -1,64 +0,0 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_bytes_writer = require("../bytes/writer.cjs");
3
- const require_bytes_flate = require("../bytes/flate.cjs");
4
- const require_image_png_filter = require("./png-filter.cjs");
5
- const require_bytes_crc32 = require("../bytes/crc32.cjs");
6
- //#region src/image/png-encode.ts
7
- const PNG_SIGNATURE = new Uint8Array([
8
- 137,
9
- 80,
10
- 78,
11
- 71,
12
- 13,
13
- 10,
14
- 26,
15
- 10
16
- ]);
17
- function u32be(value) {
18
- const bytes = /* @__PURE__ */ new Uint8Array(4);
19
- new DataView(bytes.buffer).setUint32(0, value);
20
- return bytes;
21
- }
22
- function writeChunk(writer, type, data) {
23
- const typeBytes = new TextEncoder().encode(type);
24
- writer.writeBytes(u32be(data.length));
25
- writer.writeBytes(typeBytes);
26
- writer.writeBytes(data);
27
- writer.writeBytes(u32be(require_bytes_crc32.crc32(require_bytes_writer.concatBytes([typeBytes, data]))));
28
- }
29
- function colorTypeFor(image) {
30
- if (image.channels === 1) return image.alpha === void 0 ? 0 : 4;
31
- return image.alpha === void 0 ? 2 : 6;
32
- }
33
- function encodePng(image, options = {}) {
34
- const { width, height, channels, data, alpha } = image;
35
- const outChannels = alpha === void 0 ? channels : channels + 1;
36
- const bytesPerRow = width * outChannels;
37
- const pixelCount = width * height;
38
- const interleaved = new Uint8Array(pixelCount * outChannels);
39
- for (let i = 0; i < pixelCount; i++) {
40
- const srcBase = i * channels;
41
- const dstBase = i * outChannels;
42
- for (let c = 0; c < channels; c++) interleaved[dstBase + c] = data[srcBase + c];
43
- if (alpha !== void 0) interleaved[dstBase + channels] = alpha[i];
44
- }
45
- const filtered = require_image_png_filter.filterScanlines(interleaved, height, bytesPerRow, outChannels, options.filter ?? "adaptive");
46
- const compressed = require_bytes_flate.deflate(filtered);
47
- const ihdr = /* @__PURE__ */ new Uint8Array(13);
48
- const ihdrView = new DataView(ihdr.buffer);
49
- ihdrView.setUint32(0, width);
50
- ihdrView.setUint32(4, height);
51
- ihdr[8] = 8;
52
- ihdr[9] = colorTypeFor(image);
53
- ihdr[10] = 0;
54
- ihdr[11] = 0;
55
- ihdr[12] = 0;
56
- const writer = new require_bytes_writer.ByteWriter();
57
- writer.writeBytes(PNG_SIGNATURE);
58
- writeChunk(writer, "IHDR", ihdr);
59
- writeChunk(writer, "IDAT", compressed);
60
- writeChunk(writer, "IEND", /* @__PURE__ */ new Uint8Array(0));
61
- return writer.toBytes();
62
- }
63
- //#endregion
64
- exports.encodePng = encodePng;
@@ -1,8 +0,0 @@
1
- import { RawImage } from "./png-decode.cjs";
2
- //#region src/image/png-encode.d.ts
3
- interface PngEncodeOptions {
4
- readonly filter?: "none" | "adaptive";
5
- }
6
- declare function encodePng(image: RawImage, options?: PngEncodeOptions): Uint8Array<ArrayBuffer>;
7
- //#endregion
8
- export { PngEncodeOptions, encodePng };
@@ -1,8 +0,0 @@
1
- import { RawImage } from "./png-decode.js";
2
- //#region src/image/png-encode.d.ts
3
- interface PngEncodeOptions {
4
- readonly filter?: "none" | "adaptive";
5
- }
6
- declare function encodePng(image: RawImage, options?: PngEncodeOptions): Uint8Array<ArrayBuffer>;
7
- //#endregion
8
- export { PngEncodeOptions, encodePng };
@@ -1,63 +0,0 @@
1
- import { ByteWriter, concatBytes } from "../bytes/writer.js";
2
- import { deflate } from "../bytes/flate.js";
3
- import { filterScanlines } from "./png-filter.js";
4
- import { crc32 } from "../bytes/crc32.js";
5
- //#region src/image/png-encode.ts
6
- const PNG_SIGNATURE = new Uint8Array([
7
- 137,
8
- 80,
9
- 78,
10
- 71,
11
- 13,
12
- 10,
13
- 26,
14
- 10
15
- ]);
16
- function u32be(value) {
17
- const bytes = /* @__PURE__ */ new Uint8Array(4);
18
- new DataView(bytes.buffer).setUint32(0, value);
19
- return bytes;
20
- }
21
- function writeChunk(writer, type, data) {
22
- const typeBytes = new TextEncoder().encode(type);
23
- writer.writeBytes(u32be(data.length));
24
- writer.writeBytes(typeBytes);
25
- writer.writeBytes(data);
26
- writer.writeBytes(u32be(crc32(concatBytes([typeBytes, data]))));
27
- }
28
- function colorTypeFor(image) {
29
- if (image.channels === 1) return image.alpha === void 0 ? 0 : 4;
30
- return image.alpha === void 0 ? 2 : 6;
31
- }
32
- function encodePng(image, options = {}) {
33
- const { width, height, channels, data, alpha } = image;
34
- const outChannels = alpha === void 0 ? channels : channels + 1;
35
- const bytesPerRow = width * outChannels;
36
- const pixelCount = width * height;
37
- const interleaved = new Uint8Array(pixelCount * outChannels);
38
- for (let i = 0; i < pixelCount; i++) {
39
- const srcBase = i * channels;
40
- const dstBase = i * outChannels;
41
- for (let c = 0; c < channels; c++) interleaved[dstBase + c] = data[srcBase + c];
42
- if (alpha !== void 0) interleaved[dstBase + channels] = alpha[i];
43
- }
44
- const filtered = filterScanlines(interleaved, height, bytesPerRow, outChannels, options.filter ?? "adaptive");
45
- const compressed = deflate(filtered);
46
- const ihdr = /* @__PURE__ */ new Uint8Array(13);
47
- const ihdrView = new DataView(ihdr.buffer);
48
- ihdrView.setUint32(0, width);
49
- ihdrView.setUint32(4, height);
50
- ihdr[8] = 8;
51
- ihdr[9] = colorTypeFor(image);
52
- ihdr[10] = 0;
53
- ihdr[11] = 0;
54
- ihdr[12] = 0;
55
- const writer = new ByteWriter();
56
- writer.writeBytes(PNG_SIGNATURE);
57
- writeChunk(writer, "IHDR", ihdr);
58
- writeChunk(writer, "IDAT", compressed);
59
- writeChunk(writer, "IEND", /* @__PURE__ */ new Uint8Array(0));
60
- return writer.toBytes();
61
- }
62
- //#endregion
63
- export { encodePng };