pdf-codec 5.5.1 → 5.6.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.
@@ -57,27 +57,51 @@ function resolveColorSpace(csObj, resolver, sink) {
57
57
  name: family ?? "(unrecognised array colour space)"
58
58
  };
59
59
  }
60
- function unpackSamples(data, width, height, componentsPerPixel, bitsPerComponent) {
61
- if (bitsPerComponent === BITS_PER_BYTE) return Array.from(data.subarray(0, width * height * componentsPerPixel));
60
+ /**
61
+ * Allocates the buffers one `decodeImageSamples` call needs and counts their bytes, so a test can bound the decode's memory in bytes rather than time.
62
+ */
63
+ var SampleDecodeWork = class {
64
+ #bytesAllocated = 0;
65
+ /** Bytes of every buffer the decode allocated: its raster, its one reusable row and its lookup table. Each lives until the decode returns, so this is also the decode's peak; a view onto the stream it was given costs nothing. */
66
+ get bytesAllocated() {
67
+ return this.#bytesAllocated;
68
+ }
69
+ /**
70
+ * A zero-filled buffer of `length` bytes, counted.
71
+ * @param length - Its length in bytes.
72
+ * @returns The buffer.
73
+ */
74
+ allocate(length) {
75
+ this.#bytesAllocated += length;
76
+ return new Uint8Array(length);
77
+ }
78
+ };
79
+ function readSampleRow(data, row, bitsPerComponent, target) {
80
+ const samplesPerRow = target.length;
81
+ if (bitsPerComponent === BITS_PER_BYTE) {
82
+ const rowStart = row * samplesPerRow;
83
+ for (let s = 0; s < samplesPerRow; s++) target[s] = data[rowStart + s] ?? 0;
84
+ return;
85
+ }
86
+ const rowStart = row * Math.ceil(samplesPerRow * bitsPerComponent / BITS_PER_BYTE);
87
+ const sampleMask = (1 << bitsPerComponent) - 1;
88
+ for (let s = 0; s < samplesPerRow; s++) {
89
+ const bitPos = s * bitsPerComponent;
90
+ const byte = data[rowStart + Math.floor(bitPos / BITS_PER_BYTE)] ?? 0;
91
+ const shift = BITS_PER_BYTE - bitsPerComponent - bitPos % BITS_PER_BYTE;
92
+ target[s] = byte >> shift & sampleMask;
93
+ }
94
+ }
95
+ function forEachSampleRow(data, width, height, componentsPerPixel, bitsPerComponent, work, visit) {
62
96
  const samplesPerRow = width * componentsPerPixel;
63
- const bytesPerRow = Math.ceil(samplesPerRow * bitsPerComponent / BITS_PER_BYTE);
64
- const out = [];
97
+ const rowSamples = work.allocate(samplesPerRow);
65
98
  for (let row = 0; row < height; row++) {
66
- const rowStart = row * bytesPerRow;
67
- let bitPos = 0;
68
- for (let s = 0; s < samplesPerRow; s++) {
69
- let value = 0;
70
- for (let b = 0; b < bitsPerComponent; b++) {
71
- const byteIndex = rowStart + Math.floor(bitPos / BITS_PER_BYTE);
72
- const bitIndex = 7 - bitPos % BITS_PER_BYTE;
73
- const bit = (data[byteIndex] ?? 0) >> bitIndex & 1;
74
- value = value << 1 | bit;
75
- bitPos++;
76
- }
77
- out.push(value);
78
- }
99
+ readSampleRow(data, row, bitsPerComponent, rowSamples);
100
+ visit(rowSamples, row * samplesPerRow);
79
101
  }
80
- return out;
102
+ }
103
+ function availableSampleCount(data, sampleCount, bitsPerComponent) {
104
+ return bitsPerComponent === BITS_PER_BYTE ? Math.min(data.length, sampleCount) : sampleCount;
81
105
  }
82
106
  function scaleToByte(value, maxValue, inverted) {
83
107
  const v = inverted ? maxValue - value : value;
@@ -106,56 +130,89 @@ function sampleFromPalette(base, lookup, offset) {
106
130
  b: lookup[offset + 2] ?? 0
107
131
  };
108
132
  }
109
- function buildRawImage(data, width, height, bitsPerComponent, colorSpace, inverted) {
133
+ function scaleTable(maxValue, inverted, work) {
134
+ const table = work.allocate(maxValue + 1);
135
+ for (let value = 0; value <= maxValue; value++) table[value] = scaleToByte(value, maxValue, inverted);
136
+ return table;
137
+ }
138
+ /**
139
+ * Decodes an image's samples into the 8-bit raster `encodePng` takes, holding at most the raster itself, one row of samples and a lookup table of at most 256 entries beside the stream; nothing is allocated per sample.
140
+ * @param data - The image's decoded stream: rows of `width` pixels padded to a whole byte, sub-byte samples packed most significant bit first.
141
+ * @param width - Pixels per row.
142
+ * @param height - Rows.
143
+ * @param bitsPerComponent - 1, 2, 4 or 8.
144
+ * @param colorSpace - The resolved colour space; never `unsupported`.
145
+ * @param inverted - Whether `/Decode` is `[1 0]`, which a gray, RGB or CMYK sample is inverted by and an indexed one ignores.
146
+ * @param work - Counts the bytes allocated; see SampleDecodeWork.
147
+ * @returns A gray or RGB raster. An 8-bit gray or RGB image with no `/Decode` inversion is already that raster, so its data is a view onto `data` and costs no allocation. An 8-bit stream shorter than the image yields a gray, RGB or indexed raster holding only the pixels the stream covers, and a CMYK one in which each missing sample reads as 0.
148
+ */
149
+ function decodeImageSamples(data, width, height, bitsPerComponent, colorSpace, inverted, work = new SampleDecodeWork()) {
110
150
  const maxValue = (1 << bitsPerComponent) - 1;
151
+ const pixelCount = width * height;
111
152
  if (colorSpace.kind === "indexed") {
112
- const indices = unpackSamples(data, width, height, 1, bitsPerComponent);
153
+ const channels = colorSpace.base.kind === "gray" ? 1 : RGB_CHANNEL_COUNT;
113
154
  const baseComponents = componentsOf(colorSpace.base);
114
- const out = new Uint8Array(width * height * (colorSpace.base.kind === "gray" ? 1 : RGB_CHANNEL_COUNT));
115
- let outIdx = 0;
116
- for (const index of indices) {
155
+ const palette = work.allocate((maxValue + 1) * channels);
156
+ for (let index = 0; index <= maxValue; index++) {
117
157
  const rgb = sampleFromPalette(colorSpace.base, colorSpace.lookup, index * baseComponents);
118
- if (colorSpace.base.kind === "gray") out[outIdx++] = rgb.r;
119
- else {
120
- out[outIdx++] = rgb.r;
121
- out[outIdx++] = rgb.g;
122
- out[outIdx++] = rgb.b;
158
+ palette[index * channels] = rgb.r;
159
+ if (channels === RGB_CHANNEL_COUNT) {
160
+ palette[index * channels + 1] = rgb.g;
161
+ palette[index * channels + 2] = rgb.b;
123
162
  }
124
163
  }
164
+ const out = work.allocate(pixelCount * channels);
165
+ const indexed = availableSampleCount(data, pixelCount, bitsPerComponent);
166
+ forEachSampleRow(data, width, height, 1, bitsPerComponent, work, (indices, firstPixel) => {
167
+ const count = Math.min(indices.length, indexed - firstPixel);
168
+ for (let i = 0; i < count; i++) {
169
+ const entry = indices[i] * channels;
170
+ const at = (firstPixel + i) * channels;
171
+ for (let channel = 0; channel < channels; channel++) out[at + channel] = palette[entry + channel];
172
+ }
173
+ });
125
174
  return {
126
175
  width,
127
176
  height,
128
- channels: colorSpace.base.kind === "gray" ? 1 : RGB_CHANNEL_COUNT,
177
+ channels,
129
178
  data: out
130
179
  };
131
180
  }
132
- if (colorSpace.kind === "gray") {
133
- const samples = unpackSamples(data, width, height, 1, bitsPerComponent);
134
- return {
181
+ if (colorSpace.kind === "gray" || colorSpace.kind === "rgb") {
182
+ const channels = colorSpace.kind === "gray" ? 1 : RGB_CHANNEL_COUNT;
183
+ const available = availableSampleCount(data, pixelCount * channels, bitsPerComponent);
184
+ if (bitsPerComponent === BITS_PER_BYTE && !inverted) return {
135
185
  width,
136
186
  height,
137
- channels: 1,
138
- data: Uint8Array.from(samples, (v) => scaleToByte(v, maxValue, inverted))
187
+ channels,
188
+ data: data.subarray(0, available)
139
189
  };
140
- }
141
- if (colorSpace.kind === "rgb") {
142
- const samples = unpackSamples(data, width, height, RGB_CHANNEL_COUNT, bitsPerComponent);
143
- const out = Uint8Array.from(samples, (v) => scaleToByte(v, maxValue, inverted));
190
+ const scale = scaleTable(maxValue, inverted, work);
191
+ const out = work.allocate(available);
192
+ forEachSampleRow(data, width, height, channels, bitsPerComponent, work, (samples, firstSample) => {
193
+ const count = Math.min(samples.length, available - firstSample);
194
+ for (let i = 0; i < count; i++) out[firstSample + i] = scale[samples[i]];
195
+ });
144
196
  return {
145
197
  width,
146
198
  height,
147
- channels: RGB_CHANNEL_COUNT,
199
+ channels,
148
200
  data: out
149
201
  };
150
202
  }
151
- const samples = unpackSamples(data, width, height, CMYK_COMPONENT_COUNT, bitsPerComponent);
152
- const out = new Uint8Array(width * height * RGB_CHANNEL_COUNT);
153
- for (let px = 0; px < width * height; px++) {
154
- const rgb = cmykToRgbByte(scaleToByte(samples[px * CMYK_COMPONENT_COUNT] ?? 0, maxValue, inverted) / MAX_BYTE_VALUE, scaleToByte(samples[px * CMYK_COMPONENT_COUNT + 1] ?? 0, maxValue, inverted) / MAX_BYTE_VALUE, scaleToByte(samples[px * CMYK_COMPONENT_COUNT + 2] ?? 0, maxValue, inverted) / MAX_BYTE_VALUE, scaleToByte(samples[px * CMYK_COMPONENT_COUNT + 3] ?? 0, maxValue, inverted) / MAX_BYTE_VALUE);
155
- out[px * RGB_CHANNEL_COUNT] = rgb.r;
156
- out[px * RGB_CHANNEL_COUNT + 1] = rgb.g;
157
- out[px * RGB_CHANNEL_COUNT + 2] = rgb.b;
158
- }
203
+ const scale = scaleTable(maxValue, inverted, work);
204
+ const out = work.allocate(pixelCount * RGB_CHANNEL_COUNT);
205
+ forEachSampleRow(data, width, height, CMYK_COMPONENT_COUNT, bitsPerComponent, work, (samples, firstSample) => {
206
+ const firstPixel = firstSample / CMYK_COMPONENT_COUNT;
207
+ for (let px = 0; px < width; px++) {
208
+ const base = px * CMYK_COMPONENT_COUNT;
209
+ const rgb = cmykToRgbByte(scale[samples[base]] / MAX_BYTE_VALUE, scale[samples[base + 1]] / MAX_BYTE_VALUE, scale[samples[base + 2]] / MAX_BYTE_VALUE, scale[samples[base + 3]] / MAX_BYTE_VALUE);
210
+ const outBase = (firstPixel + px) * RGB_CHANNEL_COUNT;
211
+ out[outBase] = rgb.r;
212
+ out[outBase + 1] = rgb.g;
213
+ out[outBase + 2] = rgb.b;
214
+ }
215
+ });
159
216
  return {
160
217
  width,
161
218
  height,
@@ -326,7 +383,7 @@ function readImageXObject(dict, raw, resolver, sink) {
326
383
  }
327
384
  const decodeArr = asArray(dictGet(dict, "Decode"));
328
385
  const inverted = decodeArr !== void 0 && asNumber(decodeArr[0]) === 1 && asNumber(decodeArr[1]) === 0;
329
- const rawImage = buildRawImage(decoded.bytes, width, height, bitsPerComponent, colorSpace, inverted);
386
+ const rawImage = decodeImageSamples(decoded.bytes, width, height, bitsPerComponent, colorSpace, inverted);
330
387
  const alpha = readSoftMaskAlpha(dict, width, height, resolver, sink);
331
388
  const withAlpha = alpha !== void 0 ? {
332
389
  ...rawImage,
@@ -341,4 +398,4 @@ function readImageXObject(dict, raw, resolver, sink) {
341
398
  };
342
399
  }
343
400
  //#endregion
344
- export { cmykToRgbByte, readImageXObject, sampleFromPalette, scaleToByte };
401
+ export { SampleDecodeWork, cmykToRgbByte, decodeImageSamples, readImageXObject, sampleFromPalette, scaleToByte };
package/dist/index.cjs CHANGED
@@ -21,7 +21,7 @@ const require_write_skipped_surfaces = require("./write-skipped-surfaces.cjs");
21
21
  const require_write = require("./write.cjs");
22
22
  const require_codec = require("./codec.cjs");
23
23
  const require_font_face = require("./font-face.cjs");
24
- const require_raster = require("./raster-HUE5bw5s.cjs");
24
+ const require_raster = require("./raster-B8i3Tgjj.cjs");
25
25
  exports.DEFAULT_BASELINE_TOLERANCE_EM = require_text_group.DEFAULT_BASELINE_TOLERANCE_EM;
26
26
  exports.DEFAULT_COLUMN_GAP_EM = require_text_group.DEFAULT_COLUMN_GAP_EM;
27
27
  exports.DEFAULT_VERTICAL_METRIC_POLICY = require_measure.DEFAULT_VERTICAL_METRIC_POLICY;
package/dist/index.js CHANGED
@@ -20,6 +20,6 @@ import { PdfSkippedSurfacesError } from "./write-skipped-surfaces.js";
20
20
  import { PdfPartialDocumentError, writePdf } from "./write.js";
21
21
  import { PdfBytesSchema, pdfCodec } from "./codec.js";
22
22
  import { FontFaceParseError, readFontFace } from "./font-face.js";
23
- import { r as renderPdfPage } from "./raster-DUmwfi3F.js";
23
+ import { r as renderPdfPage } from "./raster-CIc6ot36.js";
24
24
  export * from "byte-codec";
25
25
  export { DEFAULT_BASELINE_TOLERANCE_EM, DEFAULT_COLUMN_GAP_EM, DEFAULT_VERTICAL_METRIC_POLICY, DEFAULT_WORD_GAP_EM, FontFaceParseError, Jbig2ParseError, Jbig2UnsupportedError, Jpeg2000ParseError, Jpeg2000UnsupportedError, LAYOUT_FORMAT_VERSION, LayoutAnnotationQuadSchema, LayoutAnnotationSchema, LayoutAttachmentSchema, LayoutDestinationSchema, LayoutDestinationTargetSchema, LayoutDocumentSchema, LayoutDocumentSurfaceSchema, LayoutEllipseSchema, LayoutFormFieldSchema, LayoutFormWidgetSchema, LayoutImageAssetSchema, LayoutImageSchema, LayoutInternalLinkSchema, LayoutItemSchema, LayoutLayerSchema, LayoutLineSchema, LayoutLinkSchema, LayoutOutlineItemSchema, LayoutPageSchema, LayoutPathSchema, LayoutPathSegmentSchema, LayoutRectSchema, LayoutStructureElementSchema, LayoutSubpathSchema, LayoutTextSchema, NOOP_DIAGNOSTIC_SINK, PdfBytesSchema, PdfEncryptedError, PdfEncryptionError, PdfPageSelectionError, PdfParseError, PdfPartialDocumentError, PdfPasswordRequiredError, PdfSkippedSurfacesError, STANDARD_METRICS, assembleStretchyGlyph, createFontMeasurer, createFontRegistry, createStandardFontMeasurer, decodeCcittFax, decodeJbig2Embedded, decodeJpeg2000, filterScanlines, groupPdfTextRuns, loadMathFont, looksLikeBareCodestream, parseJp2Container, pdfCodec, readFontFace, readJpeg2000Metadata, readPdf, renderPdfPage, resolveFaceWithRegistry, resolveStandardFont, runGapPt, runsShareBaseline, scaleMathStretchConstruction, sourcePageIndex, unfilterScanlines, writePdf };
@@ -306,10 +306,9 @@ function runContentStream(bytes, resources, initialState, context, depth, marked
306
306
  advanceThroughString(el.bytes);
307
307
  } else if (el.kind === "number") {
308
308
  const adjustment = -(el.value / GLYPH_SPACE_PER_TEXT_SPACE_UNIT) * gs.fontSizePt;
309
- const endsRun = !vertical && totalLength > 0 && adjustment >= .125 * gs.fontSizePt;
310
- if (endsRun) flushRun();
309
+ if (!vertical && totalLength > 0 && adjustment >= .125 * gs.fontSizePt) flushRun();
311
310
  text.tm = require_matrix.multiplyMatrices(vertical ? require_matrix.translationMatrix(0, adjustment) : require_matrix.translationMatrix(adjustment * gs.horizScale, 0), text.tm);
312
- if (endsRun) startMatrix = computeTrm(gs, text);
311
+ if (totalLength === 0) startMatrix = computeTrm(gs, text);
313
312
  }
314
313
  flushRun();
315
314
  };
@@ -306,10 +306,9 @@ function runContentStream(bytes, resources, initialState, context, depth, marked
306
306
  advanceThroughString(el.bytes);
307
307
  } else if (el.kind === "number") {
308
308
  const adjustment = -(el.value / GLYPH_SPACE_PER_TEXT_SPACE_UNIT) * gs.fontSizePt;
309
- const endsRun = !vertical && totalLength > 0 && adjustment >= .125 * gs.fontSizePt;
310
- if (endsRun) flushRun();
309
+ if (!vertical && totalLength > 0 && adjustment >= .125 * gs.fontSizePt) flushRun();
311
310
  text.tm = multiplyMatrices(vertical ? translationMatrix(0, adjustment) : translationMatrix(adjustment * gs.horizScale, 0), text.tm);
312
- if (endsRun) startMatrix = computeTrm(gs, text);
311
+ if (totalLength === 0) startMatrix = computeTrm(gs, text);
313
312
  }
314
313
  flushRun();
315
314
  };
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_interpret = require("./interpret-BDVrFWhd.cjs");
2
+ const require_interpret = require("./interpret-2_N_lh0h.cjs");
3
3
  exports.cmykColor = require_interpret.cmykColor;
4
4
  exports.genericColor = require_interpret.genericColor;
5
5
  exports.grayColor = require_interpret.grayColor;
@@ -1,2 +1,2 @@
1
- import { a as grayColor, i as genericColor, o as rgbColor, r as cmykColor } from "./interpret---0lZPlv.js";
1
+ import { a as grayColor, i as genericColor, o as rgbColor, r as cmykColor } from "./interpret-BeMKZGBm.js";
2
2
  export { cmykColor, genericColor, grayColor, rgbColor };
@@ -1,4 +1,4 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_interpret = require("./interpret-BDVrFWhd.cjs");
2
+ const require_interpret = require("./interpret-2_N_lh0h.cjs");
3
3
  exports.interpretContentStream = require_interpret.interpretContentStream;
4
4
  exports.numAt = require_interpret.numAt;
package/dist/interpret.js CHANGED
@@ -1,2 +1,2 @@
1
- import { n as numAt, t as interpretContentStream } from "./interpret---0lZPlv.js";
1
+ import { n as numAt, t as interpretContentStream } from "./interpret-BeMKZGBm.js";
2
2
  export { interpretContentStream, numAt };
package/dist/layout.cjs CHANGED
@@ -270,7 +270,7 @@ const LayoutStructureElementSchema = zod.z.lazy(() => zod.z.object({
270
270
  children: zod.z.array(LayoutStructureElementSchema)
271
271
  }));
272
272
  /**
273
- * The document-level surfaces a read can leave out (`ReadPdfOptions.surfaces`), each named by the `LayoutDocument` field it fills: `outline`, `destinations`, `form`, `layers` and `structure` are those fields, and `residue` is the package-level `source` channel. A `LayoutDocument` lists the ones its read skipped in `skippedSurfaces`.
273
+ * The document-level surfaces a read can leave out (`ReadPdfOptions.surfaces`), each named by the `LayoutDocument` field it fills: `outline`, `destinations`, `form`, `layers`, `structure` and `images` are those fields, and `residue` is the package-level `source` channel. A `LayoutDocument` lists the ones its read skipped in `skippedSurfaces`.
274
274
  */
275
275
  const LayoutDocumentSurfaceSchema = zod.z.enum([
276
276
  "outline",
@@ -278,7 +278,8 @@ const LayoutDocumentSurfaceSchema = zod.z.enum([
278
278
  "form",
279
279
  "layers",
280
280
  "structure",
281
- "residue"
281
+ "residue",
282
+ "images"
282
283
  ]);
283
284
  const LayoutDocumentSchema = zod.z.object({
284
285
  formatVersion: zod.z.literal(1),
package/dist/layout.d.cts CHANGED
@@ -785,10 +785,11 @@ interface LayoutStructureElement {
785
785
  readonly children: readonly LayoutStructureElement[];
786
786
  }
787
787
  /**
788
- * The document-level surfaces a read can leave out (`ReadPdfOptions.surfaces`), each named by the `LayoutDocument` field it fills: `outline`, `destinations`, `form`, `layers` and `structure` are those fields, and `residue` is the package-level `source` channel. A `LayoutDocument` lists the ones its read skipped in `skippedSurfaces`.
788
+ * The document-level surfaces a read can leave out (`ReadPdfOptions.surfaces`), each named by the `LayoutDocument` field it fills: `outline`, `destinations`, `form`, `layers`, `structure` and `images` are those fields, and `residue` is the package-level `source` channel. A `LayoutDocument` lists the ones its read skipped in `skippedSurfaces`.
789
789
  */
790
790
  declare const LayoutDocumentSurfaceSchema: z.ZodEnum<{
791
791
  structure: "structure";
792
+ images: "images";
792
793
  destinations: "destinations";
793
794
  outline: "outline";
794
795
  layers: "layers";
@@ -1115,6 +1116,7 @@ declare const LayoutDocumentSchema: z.ZodObject<{
1115
1116
  }, z.core.$strict>>>;
1116
1117
  skippedSurfaces: z.ZodOptional<z.ZodArray<z.ZodEnum<{
1117
1118
  structure: "structure";
1119
+ images: "images";
1118
1120
  destinations: "destinations";
1119
1121
  outline: "outline";
1120
1122
  layers: "layers";
package/dist/layout.d.ts CHANGED
@@ -785,10 +785,11 @@ interface LayoutStructureElement {
785
785
  readonly children: readonly LayoutStructureElement[];
786
786
  }
787
787
  /**
788
- * The document-level surfaces a read can leave out (`ReadPdfOptions.surfaces`), each named by the `LayoutDocument` field it fills: `outline`, `destinations`, `form`, `layers` and `structure` are those fields, and `residue` is the package-level `source` channel. A `LayoutDocument` lists the ones its read skipped in `skippedSurfaces`.
788
+ * The document-level surfaces a read can leave out (`ReadPdfOptions.surfaces`), each named by the `LayoutDocument` field it fills: `outline`, `destinations`, `form`, `layers`, `structure` and `images` are those fields, and `residue` is the package-level `source` channel. A `LayoutDocument` lists the ones its read skipped in `skippedSurfaces`.
789
789
  */
790
790
  declare const LayoutDocumentSurfaceSchema: z.ZodEnum<{
791
791
  structure: "structure";
792
+ images: "images";
792
793
  destinations: "destinations";
793
794
  outline: "outline";
794
795
  layers: "layers";
@@ -1115,6 +1116,7 @@ declare const LayoutDocumentSchema: z.ZodObject<{
1115
1116
  }, z.core.$strict>>>;
1116
1117
  skippedSurfaces: z.ZodOptional<z.ZodArray<z.ZodEnum<{
1117
1118
  structure: "structure";
1119
+ images: "images";
1118
1120
  destinations: "destinations";
1119
1121
  outline: "outline";
1120
1122
  layers: "layers";
package/dist/layout.js CHANGED
@@ -269,7 +269,7 @@ const LayoutStructureElementSchema = z.lazy(() => z.object({
269
269
  children: z.array(LayoutStructureElementSchema)
270
270
  }));
271
271
  /**
272
- * The document-level surfaces a read can leave out (`ReadPdfOptions.surfaces`), each named by the `LayoutDocument` field it fills: `outline`, `destinations`, `form`, `layers` and `structure` are those fields, and `residue` is the package-level `source` channel. A `LayoutDocument` lists the ones its read skipped in `skippedSurfaces`.
272
+ * The document-level surfaces a read can leave out (`ReadPdfOptions.surfaces`), each named by the `LayoutDocument` field it fills: `outline`, `destinations`, `form`, `layers`, `structure` and `images` are those fields, and `residue` is the package-level `source` channel. A `LayoutDocument` lists the ones its read skipped in `skippedSurfaces`.
273
273
  */
274
274
  const LayoutDocumentSurfaceSchema = z.enum([
275
275
  "outline",
@@ -277,7 +277,8 @@ const LayoutDocumentSurfaceSchema = z.enum([
277
277
  "form",
278
278
  "layers",
279
279
  "structure",
280
- "residue"
280
+ "residue",
281
+ "images"
281
282
  ]);
282
283
  const LayoutDocumentSchema = z.object({
283
284
  formatVersion: z.literal(1),
@@ -11,7 +11,7 @@ const require_diagnostics = require("./diagnostics.cjs");
11
11
  const require_document = require("./document.cjs");
12
12
  const require_util_abort = require("./util/abort.cjs");
13
13
  const require_font_read = require("./font-read.cjs");
14
- const require_interpret = require("./interpret-BDVrFWhd.cjs");
14
+ const require_interpret = require("./interpret-2_N_lh0h.cjs");
15
15
  const require_read = require("./read.cjs");
16
16
  const require_glyf = require("./glyf.cjs");
17
17
  const require_glyf_contours = require("./glyf-contours.cjs");
@@ -11,7 +11,7 @@ import { NOOP_DIAGNOSTIC_SINK, PdfParseError } from "./diagnostics.js";
11
11
  import { openPdfDocument } from "./document.js";
12
12
  import { throwIfAborted } from "./util/abort.js";
13
13
  import { createFontResolver } from "./font-read.js";
14
- import { t as interpretContentStream } from "./interpret---0lZPlv.js";
14
+ import { t as interpretContentStream } from "./interpret-BeMKZGBm.js";
15
15
  import { normalizeRotation, pageRotationTransform } from "./read.js";
16
16
  import { parseGlyf } from "./glyf.js";
17
17
  import { decodeGlyphOutline } from "./glyf-contours.js";
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_raster = require("./raster-HUE5bw5s.cjs");
2
+ const require_raster = require("./raster-B8i3Tgjj.cjs");
3
3
  exports.drawGlyphOutline = require_raster.drawGlyphOutline;
4
4
  exports.drawTextRun = require_raster.drawTextRun;
5
5
  exports.glyphOutlineSubpaths = require_raster.glyphOutlineSubpaths;
@@ -1,2 +1,2 @@
1
- import { a as drawTextRun, i as drawGlyphOutline, o as glyphOutlineSubpaths } from "./raster-DUmwfi3F.js";
1
+ import { a as drawTextRun, i as drawGlyphOutline, o as glyphOutlineSubpaths } from "./raster-CIc6ot36.js";
2
2
  export { drawGlyphOutline, drawTextRun, glyphOutlineSubpaths };
package/dist/raster.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_raster = require("./raster-HUE5bw5s.cjs");
2
+ const require_raster = require("./raster-B8i3Tgjj.cjs");
3
3
  exports.EXTENT_ZERO_EPSILON = require_raster.EXTENT_ZERO_EPSILON;
4
4
  exports.flattenCubic = require_raster.flattenCubic;
5
5
  exports.renderPdfPage = require_raster.renderPdfPage;
package/dist/raster.js CHANGED
@@ -1,2 +1,2 @@
1
- import { n as flattenCubic, r as renderPdfPage, t as EXTENT_ZERO_EPSILON } from "./raster-DUmwfi3F.js";
1
+ import { n as flattenCubic, r as renderPdfPage, t as EXTENT_ZERO_EPSILON } from "./raster-CIc6ot36.js";
2
2
  export { EXTENT_ZERO_EPSILON, flattenCubic, renderPdfPage };
@@ -7,14 +7,25 @@ const require_bytes_crc32 = require("./bytes/crc32.cjs");
7
7
  const require_images_read = require("./images-read.cjs");
8
8
  let byte_codec = require("byte-codec");
9
9
  //#region src/read-convert.ts
10
- function convertExtractedItem(item, pageMatrix, fontResolver, images, imageIdCache, resolver, sink) {
10
+ /**
11
+ * Converts one interpreted item to a layout item in page space.
12
+ * @param item - The item the content-stream interpreter extracted.
13
+ * @param pageMatrix - Maps the interpreter's user space into the page's output space.
14
+ * @param fontResolver - Resolves a text item's font.
15
+ * @param imageRegistry - Where images are decoded into, or undefined for a read that skips images, which converts no image item and decodes no image stream.
16
+ * @param resolver - Resolves indirect objects an image names.
17
+ * @param sink - Receives diagnostics.
18
+ * @returns The layout item, or undefined for an item that converts to none: an image that could not be decoded, or any image when `imageRegistry` is undefined.
19
+ */
20
+ function convertExtractedItem(item, pageMatrix, fontResolver, imageRegistry, resolver, sink) {
11
21
  if (item.kind === "text") return convertText(item, pageMatrix, fontResolver);
12
22
  if (item.kind === "rect") return convertRect(item, pageMatrix);
13
23
  if (item.kind === "ellipse") return convertEllipse(item, pageMatrix);
14
24
  if (item.kind === "line") return convertLine(item, pageMatrix);
15
25
  if (item.kind === "path") return convertPath(item, pageMatrix);
16
- if (item.kind === "image") return convertImage(item, pageMatrix, images, imageIdCache, resolver, sink);
17
- return convertInlineImage(item, pageMatrix, images, resolver, sink);
26
+ if (imageRegistry === void 0) return;
27
+ if (item.kind === "image") return convertImage(item, pageMatrix, imageRegistry.images, imageRegistry.imageIdCache, resolver, sink);
28
+ return convertInlineImage(item, pageMatrix, imageRegistry.images, resolver, sink);
18
29
  }
19
30
  function convertText(item, pageMatrix, fontResolver) {
20
31
  const font = fontResolver.resolve(item.fontResourceName, item.resources);
@@ -8,7 +8,24 @@ import { DestinationRegistry } from "./navigation.cjs";
8
8
  import { ExtractedPdfImage } from "./images-read.cjs";
9
9
  import { Color } from "document-schema.js";
10
10
  //#region src/read-convert.d.ts
11
- declare function convertExtractedItem(item: ExtractedItem, pageMatrix: Matrix, fontResolver: FontResolverService, images: Record<string, LayoutImageAsset>, imageIdCache: Map<PdfDict, string | null>, resolver: Readonly<PdfObjectResolver>, sink: PdfDiagnosticSink): LayoutItem | undefined;
11
+ /**
12
+ * Where a read's images go: the registry each decoded image is stored in once, and the id each image XObject dictionary already decoded to (null for one that could not be).
13
+ */
14
+ interface ImageRegistry {
15
+ readonly images: Record<string, LayoutImageAsset>;
16
+ readonly imageIdCache: Map<PdfDict, string | null>;
17
+ }
18
+ /**
19
+ * Converts one interpreted item to a layout item in page space.
20
+ * @param item - The item the content-stream interpreter extracted.
21
+ * @param pageMatrix - Maps the interpreter's user space into the page's output space.
22
+ * @param fontResolver - Resolves a text item's font.
23
+ * @param imageRegistry - Where images are decoded into, or undefined for a read that skips images, which converts no image item and decodes no image stream.
24
+ * @param resolver - Resolves indirect objects an image names.
25
+ * @param sink - Receives diagnostics.
26
+ * @returns The layout item, or undefined for an item that converts to none: an image that could not be decoded, or any image when `imageRegistry` is undefined.
27
+ */
28
+ declare function convertExtractedItem(item: ExtractedItem, pageMatrix: Matrix, fontResolver: FontResolverService, imageRegistry: ImageRegistry | undefined, resolver: Readonly<PdfObjectResolver>, sink: PdfDiagnosticSink): LayoutItem | undefined;
12
29
  declare function convertText(item: ExtractedTextRun, pageMatrix: Matrix, fontResolver: FontResolverService): LayoutText | undefined;
13
30
  declare function paintFields(paint: ExtractedPaint): {
14
31
  fill?: Color;
@@ -55,4 +72,4 @@ declare function convertImage(item: ExtractedImage, pageMatrix: Matrix, images:
55
72
  declare function readLinkAnnotations(page: PdfDict, pageMatrix: Matrix, resolver: Readonly<PdfObjectResolver>, destinationRegistry: DestinationRegistry): (LayoutLink | LayoutInternalLink)[];
56
73
  declare function readPageNotes(page: PdfDict, resolver: Readonly<PdfObjectResolver>): string | undefined;
57
74
  //#endregion
58
- export { convertEllipse, convertExtractedItem, convertImage, convertLine, convertPath, convertRect, convertText, imagePlacementFrom, paintFields, readLinkAnnotations, readPageNotes, registerExtractedImage, resolveCachedImageId, transformBox, transformSubpath };
75
+ export { ImageRegistry, convertEllipse, convertExtractedItem, convertImage, convertLine, convertPath, convertRect, convertText, imagePlacementFrom, paintFields, readLinkAnnotations, readPageNotes, registerExtractedImage, resolveCachedImageId, transformBox, transformSubpath };
@@ -8,7 +8,24 @@ import { DestinationRegistry } from "./navigation.js";
8
8
  import { ExtractedPdfImage } from "./images-read.js";
9
9
  import { Color } from "document-schema.js";
10
10
  //#region src/read-convert.d.ts
11
- declare function convertExtractedItem(item: ExtractedItem, pageMatrix: Matrix, fontResolver: FontResolverService, images: Record<string, LayoutImageAsset>, imageIdCache: Map<PdfDict, string | null>, resolver: Readonly<PdfObjectResolver>, sink: PdfDiagnosticSink): LayoutItem | undefined;
11
+ /**
12
+ * Where a read's images go: the registry each decoded image is stored in once, and the id each image XObject dictionary already decoded to (null for one that could not be).
13
+ */
14
+ interface ImageRegistry {
15
+ readonly images: Record<string, LayoutImageAsset>;
16
+ readonly imageIdCache: Map<PdfDict, string | null>;
17
+ }
18
+ /**
19
+ * Converts one interpreted item to a layout item in page space.
20
+ * @param item - The item the content-stream interpreter extracted.
21
+ * @param pageMatrix - Maps the interpreter's user space into the page's output space.
22
+ * @param fontResolver - Resolves a text item's font.
23
+ * @param imageRegistry - Where images are decoded into, or undefined for a read that skips images, which converts no image item and decodes no image stream.
24
+ * @param resolver - Resolves indirect objects an image names.
25
+ * @param sink - Receives diagnostics.
26
+ * @returns The layout item, or undefined for an item that converts to none: an image that could not be decoded, or any image when `imageRegistry` is undefined.
27
+ */
28
+ declare function convertExtractedItem(item: ExtractedItem, pageMatrix: Matrix, fontResolver: FontResolverService, imageRegistry: ImageRegistry | undefined, resolver: Readonly<PdfObjectResolver>, sink: PdfDiagnosticSink): LayoutItem | undefined;
12
29
  declare function convertText(item: ExtractedTextRun, pageMatrix: Matrix, fontResolver: FontResolverService): LayoutText | undefined;
13
30
  declare function paintFields(paint: ExtractedPaint): {
14
31
  fill?: Color;
@@ -55,4 +72,4 @@ declare function convertImage(item: ExtractedImage, pageMatrix: Matrix, images:
55
72
  declare function readLinkAnnotations(page: PdfDict, pageMatrix: Matrix, resolver: Readonly<PdfObjectResolver>, destinationRegistry: DestinationRegistry): (LayoutLink | LayoutInternalLink)[];
56
73
  declare function readPageNotes(page: PdfDict, resolver: Readonly<PdfObjectResolver>): string | undefined;
57
74
  //#endregion
58
- export { convertEllipse, convertExtractedItem, convertImage, convertLine, convertPath, convertRect, convertText, imagePlacementFrom, paintFields, readLinkAnnotations, readPageNotes, registerExtractedImage, resolveCachedImageId, transformBox, transformSubpath };
75
+ export { ImageRegistry, convertEllipse, convertExtractedItem, convertImage, convertLine, convertPath, convertRect, convertText, imagePlacementFrom, paintFields, readLinkAnnotations, readPageNotes, registerExtractedImage, resolveCachedImageId, transformBox, transformSubpath };
@@ -6,14 +6,25 @@ import { crc32 } from "./bytes/crc32.js";
6
6
  import { readImageXObject } from "./images-read.js";
7
7
  import { bytesToBase64 } from "byte-codec";
8
8
  //#region src/read-convert.ts
9
- function convertExtractedItem(item, pageMatrix, fontResolver, images, imageIdCache, resolver, sink) {
9
+ /**
10
+ * Converts one interpreted item to a layout item in page space.
11
+ * @param item - The item the content-stream interpreter extracted.
12
+ * @param pageMatrix - Maps the interpreter's user space into the page's output space.
13
+ * @param fontResolver - Resolves a text item's font.
14
+ * @param imageRegistry - Where images are decoded into, or undefined for a read that skips images, which converts no image item and decodes no image stream.
15
+ * @param resolver - Resolves indirect objects an image names.
16
+ * @param sink - Receives diagnostics.
17
+ * @returns The layout item, or undefined for an item that converts to none: an image that could not be decoded, or any image when `imageRegistry` is undefined.
18
+ */
19
+ function convertExtractedItem(item, pageMatrix, fontResolver, imageRegistry, resolver, sink) {
10
20
  if (item.kind === "text") return convertText(item, pageMatrix, fontResolver);
11
21
  if (item.kind === "rect") return convertRect(item, pageMatrix);
12
22
  if (item.kind === "ellipse") return convertEllipse(item, pageMatrix);
13
23
  if (item.kind === "line") return convertLine(item, pageMatrix);
14
24
  if (item.kind === "path") return convertPath(item, pageMatrix);
15
- if (item.kind === "image") return convertImage(item, pageMatrix, images, imageIdCache, resolver, sink);
16
- return convertInlineImage(item, pageMatrix, images, resolver, sink);
25
+ if (imageRegistry === void 0) return;
26
+ if (item.kind === "image") return convertImage(item, pageMatrix, imageRegistry.images, imageRegistry.imageIdCache, resolver, sink);
27
+ return convertInlineImage(item, pageMatrix, imageRegistry.images, resolver, sink);
17
28
  }
18
29
  function convertText(item, pageMatrix, fontResolver) {
19
30
  const font = fontResolver.resolve(item.fontResourceName, item.resources);
package/dist/read.cjs CHANGED
@@ -17,7 +17,7 @@ const require_diagnostics = require("./diagnostics.cjs");
17
17
  const require_document = require("./document.cjs");
18
18
  const require_util_abort = require("./util/abort.cjs");
19
19
  const require_font_read = require("./font-read.cjs");
20
- const require_interpret = require("./interpret-BDVrFWhd.cjs");
20
+ const require_interpret = require("./interpret-2_N_lh0h.cjs");
21
21
  const require_navigation = require("./navigation.cjs");
22
22
  //#region src/read.ts
23
23
  function skippedSurfacesOf(surfaces) {
@@ -59,11 +59,14 @@ function readPdf(bytes, options) {
59
59
  sink
60
60
  });
61
61
  const images = {};
62
- const imageIdCache = /* @__PURE__ */ new Map();
63
62
  const pageDicts = doc.pages();
64
63
  const selection = options?.pages === void 0 ? void 0 : resolvePageSelection(options.pages, pageDicts.length);
65
64
  const skippedSurfaces = skippedSurfacesOf(options?.surfaces);
66
65
  const reads = (surface) => !skippedSurfaces.includes(surface);
66
+ const imageRegistry = reads("images") ? {
67
+ images,
68
+ imageIdCache: /* @__PURE__ */ new Map()
69
+ } : void 0;
67
70
  const destinationRegistry = reads("destinations") ? require_navigation.createDestinationRegistry(doc.catalog, doc, (obj) => doc.pageIndex(obj), sink) : UNREAD_DESTINATION_REGISTRY;
68
71
  const outline = reads("outline") ? require_navigation.readOutline(doc.catalog, destinationRegistry, doc, sink) : void 0;
69
72
  const attachments = selection === void 0 ? require_attachments.readAttachments(doc.catalog, pageDicts, doc, sink) : [];
@@ -75,7 +78,7 @@ function readPdf(bytes, options) {
75
78
  const pages = pageDicts.flatMap((pageDict, index) => {
76
79
  if (selection !== void 0 && !selection.has(index)) return [];
77
80
  require_util_abort.throwIfAborted(signal);
78
- const read = readPage(index, pageDict, doc, fontResolver, images, imageIdCache, destinationRegistry, optionalContent?.layerNameOf ?? noLayerName, structure?.ownerOf ?? noStructureOwner, sink);
81
+ const read = readPage(index, pageDict, doc, fontResolver, imageRegistry, destinationRegistry, optionalContent?.layerNameOf ?? noLayerName, structure?.ownerOf ?? noStructureOwner, sink);
79
82
  if (read.boxRow !== void 0) pageBoxRows.push(read.boxRow);
80
83
  return [selection === void 0 ? read.page : {
81
84
  ...read.page,
@@ -309,7 +312,7 @@ function pageBoxResidueEntry(pageIndex, page, mediaBox, cropBox) {
309
312
  for (const [key, value] of declaredBoxes) if (value !== void 0) entries[key] = value;
310
313
  return require_objects.pdfDict(entries);
311
314
  }
312
- function readPage(pageIndex, page, resolver, fontResolver, images, imageIdCache, destinationRegistry, layerNameOf, structureOwnerOf, sink) {
315
+ function readPage(pageIndex, page, resolver, fontResolver, imageRegistry, destinationRegistry, layerNameOf, structureOwnerOf, sink) {
313
316
  const resources = resolver.resolveDict(require_objects.dictGet(page, "Resources"));
314
317
  const mediaBox = readMediaBox(page);
315
318
  let cropBox = readDeclaredPageBox(page, "CropBox") ?? mediaBox;
@@ -338,7 +341,7 @@ function readPage(pageIndex, page, resolver, fontResolver, images, imageIdCache,
338
341
  layerNameOf
339
342
  });
340
343
  for (const item of extracted) {
341
- const converted = require_read_convert.convertExtractedItem(item, pageMatrix, fontResolver, images, imageIdCache, resolver, sink);
344
+ const converted = require_read_convert.convertExtractedItem(item, pageMatrix, fontResolver, imageRegistry, resolver, sink);
342
345
  if (converted === void 0 || !itemIntersectsVisibleRegion(converted, widthPt, heightPt)) continue;
343
346
  const owner = item.mcid === void 0 ? void 0 : structureOwnerOf(pageIndex, item.mcid);
344
347
  items.push(owner === void 0 ? converted : withStructure(converted, owner));
package/dist/read.d.cts CHANGED
@@ -18,7 +18,7 @@ interface ReadPdfOptions {
18
18
  /**
19
19
  * Which document-level surfaces to read. Each is read unless its entry is `false`, so omitting the setting, or an entry, reads it as before. Independent of `pages`: it applies to a full read and a page-selected read alike.
20
20
  *
21
- * A surface turned off is never parsed (its part of the catalog is not walked, so a malformed outline, form, layer list or structure tree reports no diagnostic) and its field is absent from the result whatever the file holds; the result lists it in `skippedSurfaces`, which is how a caller tells "not read" from "none". What each one covers, and what page items lose without it:
21
+ * A surface turned off is never parsed (its part of the catalog is not walked, so a malformed outline, form, layer list or structure tree reports no diagnostic, and no image a page draws is decoded or diagnosed) and its field is absent from the result whatever the file holds (`images` is left empty instead); the result lists it in `skippedSurfaces`, which is how a caller tells "not read" from "none". What each one covers, and what page items lose without it:
22
22
  *
23
23
  * - `outline`: the `/Outlines` bookmark tree (`outline`). Page items do not depend on it, except through minted names: a destination stated as a direct array is named with the first unused `destN` in the order the reader meets it, and the outline is read before any page, so an outline entry's direct destination takes a lower number than any link's. Without the outline, the names minted for page links' direct destinations are renumbered, and `destinations` loses the entries only the outline minted; each link still resolves to the same target.
24
24
  * - `destinations`: the named destinations of the catalog `/Dests` dictionary and the `/Names /Dests` name tree, and the registry links and outline entries name their targets through (`destinations`). Without it nothing can name an internal target, so a link whose target is internal (a `/Dest`, or a `/GoTo` action, named or direct) is left out of its page's items, and a read outline entry carries no `destination`; a link to a URI is kept as usual.
@@ -26,6 +26,7 @@ interface ReadPdfOptions {
26
26
  * - `layers`: optional content, the `/OCProperties` groups and their default visibility (`layers`). Without it no item carries a `layer`, since an `/OC` membership resolves to no name; the items themselves are all still read, so content the default view hides is indistinguishable from visible content.
27
27
  * - `structure`: the `/StructTreeRoot` element tree and its `/ParentTree` ownership map (`structure`). Without it no item carries a `structure` owner, and the parent-tree diagnostics are not reported.
28
28
  * - `residue`: the package-level residue rows (`source`), including the per-page `page-boxes` row. Page items do not depend on it; `metadata` still reads the XMP packet it falls back to.
29
+ * - `images`: the image registry (`images`), which holds every image XObject and inline image a read page draws, decoded to PNG or passed through as JPEG. Without it no image's stream is decoded, `images` is an empty record (every `LayoutDocument` carries one), and each page's `image` items are left out, since they would name assets that were never read. Decoding an image costs memory in proportion to its pixels, so a read that wants only a page's text leaves this out.
29
30
  *
30
31
  * Apart from those stated losses and the renumbering a skipped outline causes, every page item is exactly what a read with the surface on produces.
31
32
  */