pdf-codec 5.3.32 → 5.4.0

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.
Files changed (72) hide show
  1. package/README.md +3 -1
  2. package/dist/annotations.d.cts +1 -1
  3. package/dist/annotations.d.ts +1 -1
  4. package/dist/attachments.d.cts +1 -1
  5. package/dist/attachments.d.ts +1 -1
  6. package/dist/cmap.d.cts +1 -1
  7. package/dist/cmap.d.ts +1 -1
  8. package/dist/codec.d.cts +2 -0
  9. package/dist/codec.d.ts +2 -0
  10. package/dist/content-read.d.cts +1 -1
  11. package/dist/content-read.d.ts +1 -1
  12. package/dist/diagnostics-BkG_Ej2x.d.cts +32 -0
  13. package/dist/diagnostics-BkG_Ej2x.d.ts +32 -0
  14. package/dist/diagnostics.cjs +17 -0
  15. package/dist/diagnostics.d.cts +2 -2
  16. package/dist/diagnostics.d.ts +2 -2
  17. package/dist/diagnostics.js +17 -1
  18. package/dist/document.d.cts +1 -1
  19. package/dist/document.d.ts +1 -1
  20. package/dist/encrypt.d.cts +1 -1
  21. package/dist/encrypt.d.ts +1 -1
  22. package/dist/filters.d.cts +1 -1
  23. package/dist/filters.d.ts +1 -1
  24. package/dist/font-read.d.cts +1 -1
  25. package/dist/font-read.d.ts +1 -1
  26. package/dist/form.d.cts +1 -1
  27. package/dist/form.d.ts +1 -1
  28. package/dist/images-read.d.cts +1 -1
  29. package/dist/images-read.d.ts +1 -1
  30. package/dist/index.cjs +3 -0
  31. package/dist/index.d.cts +4 -4
  32. package/dist/index.d.ts +4 -4
  33. package/dist/index.js +4 -4
  34. package/dist/interpret-types.d.cts +1 -1
  35. package/dist/interpret-types.d.ts +1 -1
  36. package/dist/layout.cjs +13 -1
  37. package/dist/layout.d.cts +11 -1
  38. package/dist/layout.d.ts +11 -1
  39. package/dist/layout.js +13 -2
  40. package/dist/names.d.cts +1 -1
  41. package/dist/names.d.ts +1 -1
  42. package/dist/navigation.d.cts +1 -1
  43. package/dist/navigation.d.ts +1 -1
  44. package/dist/optional-content.d.cts +1 -1
  45. package/dist/optional-content.d.ts +1 -1
  46. package/dist/parse.d.cts +1 -1
  47. package/dist/parse.d.ts +1 -1
  48. package/dist/predictors.d.cts +1 -1
  49. package/dist/predictors.d.ts +1 -1
  50. package/dist/raster-text.d.cts +1 -1
  51. package/dist/raster-text.d.ts +1 -1
  52. package/dist/raster.d.cts +1 -1
  53. package/dist/raster.d.ts +1 -1
  54. package/dist/read-convert.d.cts +1 -1
  55. package/dist/read-convert.d.ts +1 -1
  56. package/dist/read.cjs +19 -4
  57. package/dist/read.d.cts +11 -1
  58. package/dist/read.d.ts +11 -1
  59. package/dist/read.js +20 -5
  60. package/dist/structure.cjs +13 -3
  61. package/dist/structure.d.cts +11 -2
  62. package/dist/structure.d.ts +11 -2
  63. package/dist/structure.js +13 -3
  64. package/dist/write.cjs +15 -0
  65. package/dist/write.d.cts +9 -1
  66. package/dist/write.d.ts +9 -1
  67. package/dist/write.js +15 -1
  68. package/dist/xref.d.cts +1 -1
  69. package/dist/xref.d.ts +1 -1
  70. package/package.json +1 -1
  71. package/dist/diagnostics-C9FxfZ6H.d.cts +0 -22
  72. package/dist/diagnostics-C9FxfZ6H.d.ts +0 -22
package/dist/layout.js CHANGED
@@ -172,8 +172,18 @@ const LayoutPageSchema = z.object({
172
172
  heightPt: z.number().positive(),
173
173
  items: z.array(LayoutItemSchema),
174
174
  annotations: z.array(LayoutAnnotationSchema).optional(),
175
- notes: z.string().optional()
175
+ notes: z.string().optional(),
176
+ index: z.number().int().nonnegative().optional()
176
177
  });
178
+ /**
179
+ * The zero-based index in the source PDF's page tree of the page at `position` in a `LayoutDocument`'s `pages`: the page's own `index` when a page-selected read set one, otherwise `position` itself, which is the source index whenever `pages` holds every page in document order. The one place that rule is spelled, so every consumer resolving a page against a document-level reference (a destination, a form widget, a frame) agrees on it.
180
+ * @param page - the page as it appears in `pages`
181
+ * @param position - the page's position in `pages`
182
+ * @returns the page's index in the source document's page tree
183
+ */
184
+ function sourcePageIndex(page, position) {
185
+ return page.index ?? position;
186
+ }
177
187
  const LayoutImageAssetSchema = z.object({
178
188
  format: z.enum(["png", "jpeg"]),
179
189
  base64: z.string(),
@@ -262,6 +272,7 @@ const LayoutDocumentSchema = z.object({
262
272
  formatVersion: z.literal(1),
263
273
  metadata: LayoutMetadataSchema,
264
274
  pages: z.array(LayoutPageSchema),
275
+ pageCount: z.number().int().nonnegative().optional(),
265
276
  images: z.record(z.string(), LayoutImageAssetSchema),
266
277
  destinations: z.array(LayoutDestinationSchema).optional(),
267
278
  outline: z.array(LayoutOutlineItemSchema).optional(),
@@ -272,4 +283,4 @@ const LayoutDocumentSchema = z.object({
272
283
  source: z.record(z.string(), SourceResidueSchema).optional()
273
284
  });
274
285
  //#endregion
275
- export { LAYOUT_FORMAT_VERSION, LayoutAnnotationQuadSchema, LayoutAnnotationSchema, LayoutAttachmentSchema, LayoutDestinationSchema, LayoutDestinationTargetSchema, LayoutDocumentSchema, LayoutEllipseSchema, LayoutFormFieldSchema, LayoutFormWidgetSchema, LayoutImageAssetSchema, LayoutImageSchema, LayoutInternalLinkSchema, LayoutItemSchema, LayoutLayerSchema, LayoutLineSchema, LayoutLinkSchema, LayoutOutlineItemSchema, LayoutPageSchema, LayoutPathSchema, LayoutPathSegmentSchema, LayoutRectSchema, LayoutStructureElementSchema, LayoutSubpathSchema, LayoutTextSchema };
286
+ export { LAYOUT_FORMAT_VERSION, LayoutAnnotationQuadSchema, LayoutAnnotationSchema, LayoutAttachmentSchema, LayoutDestinationSchema, LayoutDestinationTargetSchema, LayoutDocumentSchema, LayoutEllipseSchema, LayoutFormFieldSchema, LayoutFormWidgetSchema, LayoutImageAssetSchema, LayoutImageSchema, LayoutInternalLinkSchema, LayoutItemSchema, LayoutLayerSchema, LayoutLineSchema, LayoutLinkSchema, LayoutOutlineItemSchema, LayoutPageSchema, LayoutPathSchema, LayoutPathSegmentSchema, LayoutRectSchema, LayoutStructureElementSchema, LayoutSubpathSchema, LayoutTextSchema, sourcePageIndex };
package/dist/names.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { i as PdfDiagnosticSink } from "./diagnostics-C9FxfZ6H.cjs";
1
+ import { i as PdfDiagnosticSink } from "./diagnostics-BkG_Ej2x.cjs";
2
2
  import { r as PdfObject } from "./objects-e2RS3-yT.cjs";
3
3
  import { PdfObjectResolver } from "./interpret-types.cjs";
4
4
  //#region src/names.d.ts
package/dist/names.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { i as PdfDiagnosticSink } from "./diagnostics-C9FxfZ6H.js";
1
+ import { i as PdfDiagnosticSink } from "./diagnostics-BkG_Ej2x.js";
2
2
  import { r as PdfObject } from "./objects-e2RS3-yT.js";
3
3
  import { PdfObjectResolver } from "./interpret-types.js";
4
4
  //#region src/names.d.ts
@@ -1,4 +1,4 @@
1
- import { i as PdfDiagnosticSink } from "./diagnostics-C9FxfZ6H.cjs";
1
+ import { i as PdfDiagnosticSink } from "./diagnostics-BkG_Ej2x.cjs";
2
2
  import { n as PdfDict, r as PdfObject } from "./objects-e2RS3-yT.cjs";
3
3
  import { PdfObjectResolver } from "./interpret-types.cjs";
4
4
  import { LayoutDestination, LayoutDestinationTarget, LayoutOutlineItem } from "./layout.cjs";
@@ -1,4 +1,4 @@
1
- import { i as PdfDiagnosticSink } from "./diagnostics-C9FxfZ6H.js";
1
+ import { i as PdfDiagnosticSink } from "./diagnostics-BkG_Ej2x.js";
2
2
  import { n as PdfDict, r as PdfObject } from "./objects-e2RS3-yT.js";
3
3
  import { PdfObjectResolver } from "./interpret-types.js";
4
4
  import { LayoutDestination, LayoutDestinationTarget, LayoutOutlineItem } from "./layout.js";
@@ -1,4 +1,4 @@
1
- import { i as PdfDiagnosticSink } from "./diagnostics-C9FxfZ6H.cjs";
1
+ import { i as PdfDiagnosticSink } from "./diagnostics-BkG_Ej2x.cjs";
2
2
  import { n as PdfDict, r as PdfObject } from "./objects-e2RS3-yT.cjs";
3
3
  import { PdfObjectResolver } from "./interpret-types.cjs";
4
4
  import { LayoutLayer } from "./layout.cjs";
@@ -1,4 +1,4 @@
1
- import { i as PdfDiagnosticSink } from "./diagnostics-C9FxfZ6H.js";
1
+ import { i as PdfDiagnosticSink } from "./diagnostics-BkG_Ej2x.js";
2
2
  import { n as PdfDict, r as PdfObject } from "./objects-e2RS3-yT.js";
3
3
  import { PdfObjectResolver } from "./interpret-types.js";
4
4
  import { LayoutLayer } from "./layout.js";
package/dist/parse.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { i as PdfDiagnosticSink } from "./diagnostics-C9FxfZ6H.cjs";
1
+ import { i as PdfDiagnosticSink } from "./diagnostics-BkG_Ej2x.cjs";
2
2
  import { r as PdfObject } from "./objects-e2RS3-yT.cjs";
3
3
  import { ByteReader } from "./bytes/reader.cjs";
4
4
  //#region src/parse.d.ts
package/dist/parse.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { i as PdfDiagnosticSink } from "./diagnostics-C9FxfZ6H.js";
1
+ import { i as PdfDiagnosticSink } from "./diagnostics-BkG_Ej2x.js";
2
2
  import { r as PdfObject } from "./objects-e2RS3-yT.js";
3
3
  import { ByteReader } from "./bytes/reader.js";
4
4
  //#region src/parse.d.ts
@@ -1,4 +1,4 @@
1
- import { i as PdfDiagnosticSink } from "./diagnostics-C9FxfZ6H.cjs";
1
+ import { i as PdfDiagnosticSink } from "./diagnostics-BkG_Ej2x.cjs";
2
2
  import { n as PdfDict } from "./objects-e2RS3-yT.cjs";
3
3
  //#region src/predictors.d.ts
4
4
  interface PredictorParams {
@@ -1,4 +1,4 @@
1
- import { i as PdfDiagnosticSink } from "./diagnostics-C9FxfZ6H.js";
1
+ import { i as PdfDiagnosticSink } from "./diagnostics-BkG_Ej2x.js";
2
2
  import { n as PdfDict } from "./objects-e2RS3-yT.js";
3
3
  //#region src/predictors.d.ts
4
4
  interface PredictorParams {
@@ -1,4 +1,4 @@
1
- import { i as PdfDiagnosticSink } from "./diagnostics-C9FxfZ6H.cjs";
1
+ import { i as PdfDiagnosticSink } from "./diagnostics-BkG_Ej2x.cjs";
2
2
  import { n as PdfDict } from "./objects-e2RS3-yT.cjs";
3
3
  import { r as Matrix } from "./matrix-B7_SBKQ5.cjs";
4
4
  import { ExtractedTextRun, PdfObjectResolver } from "./interpret-types.cjs";
@@ -1,4 +1,4 @@
1
- import { i as PdfDiagnosticSink } from "./diagnostics-C9FxfZ6H.js";
1
+ import { i as PdfDiagnosticSink } from "./diagnostics-BkG_Ej2x.js";
2
2
  import { n as PdfDict } from "./objects-e2RS3-yT.js";
3
3
  import { r as Matrix } from "./matrix-B7_SBKQ5.js";
4
4
  import { ExtractedTextRun, PdfObjectResolver } from "./interpret-types.js";
package/dist/raster.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { i as PdfDiagnosticSink } from "./diagnostics-C9FxfZ6H.cjs";
1
+ import { i as PdfDiagnosticSink } from "./diagnostics-BkG_Ej2x.cjs";
2
2
  import { t as SfntFont } from "./sfnt-V08bb5ut.cjs";
3
3
  import { r as GlyfTable } from "./glyf-DFyknMrW.cjs";
4
4
  import { Color } from "document-schema.js";
package/dist/raster.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { i as PdfDiagnosticSink } from "./diagnostics-C9FxfZ6H.js";
1
+ import { i as PdfDiagnosticSink } from "./diagnostics-BkG_Ej2x.js";
2
2
  import { t as SfntFont } from "./sfnt-V08bb5ut.js";
3
3
  import { r as GlyfTable } from "./glyf-Bv1katwK.js";
4
4
  import { Color } from "document-schema.js";
@@ -1,4 +1,4 @@
1
- import { i as PdfDiagnosticSink } from "./diagnostics-C9FxfZ6H.cjs";
1
+ import { i as PdfDiagnosticSink } from "./diagnostics-BkG_Ej2x.cjs";
2
2
  import { n as PdfDict } from "./objects-e2RS3-yT.cjs";
3
3
  import { r as Matrix } from "./matrix-B7_SBKQ5.cjs";
4
4
  import { ExtractedEllipse, ExtractedImage, ExtractedItem, ExtractedLine, ExtractedPaint, ExtractedPath, ExtractedRect, ExtractedSubpath, ExtractedTextRun, PdfObjectResolver } from "./interpret-types.cjs";
@@ -1,4 +1,4 @@
1
- import { i as PdfDiagnosticSink } from "./diagnostics-C9FxfZ6H.js";
1
+ import { i as PdfDiagnosticSink } from "./diagnostics-BkG_Ej2x.js";
2
2
  import { n as PdfDict } from "./objects-e2RS3-yT.js";
3
3
  import { r as Matrix } from "./matrix-B7_SBKQ5.js";
4
4
  import { ExtractedEllipse, ExtractedImage, ExtractedItem, ExtractedLine, ExtractedPaint, ExtractedPath, ExtractedRect, ExtractedSubpath, ExtractedTextRun, PdfObjectResolver } from "./interpret-types.js";
package/dist/read.cjs CHANGED
@@ -20,6 +20,15 @@ const require_font_read = require("./font-read.cjs");
20
20
  const require_interpret = require("./interpret-BDVrFWhd.cjs");
21
21
  const require_navigation = require("./navigation.cjs");
22
22
  //#region src/read.ts
23
+ function resolvePageSelection(selection, pageCount) {
24
+ const resolved = /* @__PURE__ */ new Set();
25
+ for (const requested of selection) {
26
+ const index = requested < 0 ? pageCount + requested : requested;
27
+ if (!Number.isInteger(requested) || index < 0 || index >= pageCount) throw new require_diagnostics.PdfPageSelectionError(requested, pageCount);
28
+ resolved.add(index);
29
+ }
30
+ return resolved;
31
+ }
23
32
  const PDF_HEADER_BYTES = new TextEncoder().encode("%PDF-");
24
33
  const HEADER_SEARCH_WINDOW = 1024;
25
34
  const DEFAULT_PAGE_WIDTH_PT = 612;
@@ -45,19 +54,24 @@ function readPdf(bytes, options) {
45
54
  const images = {};
46
55
  const imageIdCache = /* @__PURE__ */ new Map();
47
56
  const pageDicts = doc.pages();
57
+ const selection = options?.pages === void 0 ? void 0 : resolvePageSelection(options.pages, pageDicts.length);
48
58
  const destinationRegistry = require_navigation.createDestinationRegistry(doc.catalog, doc, (obj) => doc.pageIndex(obj), sink);
49
59
  const outline = require_navigation.readOutline(doc.catalog, destinationRegistry, doc, sink);
50
- const attachments = require_attachments.readAttachments(doc.catalog, pageDicts, doc, sink);
60
+ const attachments = selection === void 0 ? require_attachments.readAttachments(doc.catalog, pageDicts, doc, sink) : [];
51
61
  const optionalContent = require_optional_content.readOptionalContent(doc.catalog, doc, sink);
52
62
  const form = require_form.readAcroForm(doc.catalog, doc, (obj) => doc.pageIndex(obj), sink);
53
- const structure = require_structure.readStructure(doc.catalog, pageDicts, doc, sink);
63
+ const structure = require_structure.readStructure(doc.catalog, pageDicts, (index) => selection === void 0 || selection.has(index), doc, sink);
54
64
  const source = readDocumentResidue(doc, sink);
55
65
  const pageBoxRows = [];
56
- const pages = pageDicts.map((pageDict, index) => {
66
+ const pages = pageDicts.flatMap((pageDict, index) => {
67
+ if (selection !== void 0 && !selection.has(index)) return [];
57
68
  require_util_abort.throwIfAborted(signal);
58
69
  const read = readPage(index, pageDict, doc, fontResolver, images, imageIdCache, destinationRegistry, optionalContent.layerNameOf, structure.ownerOf, sink);
59
70
  if (read.boxRow !== void 0) pageBoxRows.push(read.boxRow);
60
- return read.page;
71
+ return [selection === void 0 ? read.page : {
72
+ ...read.page,
73
+ index
74
+ }];
61
75
  });
62
76
  if (pageBoxRows.length > 0) source["page-boxes"] = {
63
77
  format: "pdf",
@@ -67,6 +81,7 @@ function readPdf(bytes, options) {
67
81
  formatVersion: 1,
68
82
  metadata: readMetadata(doc.trailer, doc, doc.catalog, sink),
69
83
  pages,
84
+ ...selection !== void 0 ? { pageCount: pageDicts.length } : {},
70
85
  images,
71
86
  ...destinationRegistry.entries.length > 0 ? { destinations: [...destinationRegistry.entries] } : {},
72
87
  ...outline.length > 0 ? { outline } : {},
package/dist/read.d.cts CHANGED
@@ -1,10 +1,20 @@
1
- import { i as PdfDiagnosticSink } from "./diagnostics-C9FxfZ6H.cjs";
1
+ import { i as PdfDiagnosticSink } from "./diagnostics-BkG_Ej2x.cjs";
2
2
  import { r as Matrix } from "./matrix-B7_SBKQ5.cjs";
3
3
  import { LayoutDocument } from "./layout.cjs";
4
4
  //#region src/read.d.ts
5
5
  interface ReadPdfOptions {
6
6
  readonly sink?: PdfDiagnosticSink;
7
7
  readonly signal?: AbortSignal;
8
+ /**
9
+ * The pages to read, as zero-based indices into the document's page tree; a negative index counts from the end (`-1` is the last page). Omitted, every page is read.
10
+ *
11
+ * What a selected read skips: an unselected page's content stream is never fetched, decoded or interpreted, so the fonts and images only unselected pages use are never resolved; embedded-file payloads are never decoded, so `attachments` is absent from a page-selected result (an attachment is document-level payload whose size is independent of the pages asked for, and decoding it would cost the same whichever pages were selected).
12
+ *
13
+ * What it still reads whole, because a negative index and every document-level page reference resolve against it or because it is document-level and holds no page content stream: the page tree, the outline, named destinations, optional-content layers, the AcroForm, the structure element tree and the /ParentTree number tree (both of which scale with the document), metadata and package residue. Two surfaces are built from page content and so cover selected pages only: the structure-ownership association an item's `structure` field names (and its parent-tree diagnostics), and the `page-boxes` residue row. Destinations a link states as a direct array are minted into `destinations` (as `dest1`, `dest2`, ...) only when their page is interpreted, so an unselected page's direct link destinations are absent and a minted name depends on which pages were read; named destinations are unaffected.
14
+ *
15
+ * Duplicates collapse, and the result's `pages` holds the selected pages in ascending document order whatever order they were asked for in, each carrying its source position as `index`, with the document's total as `pageCount`; every document-level reference keeps naming source indices. An empty array is a valid selection: it reads no page and returns the document-level surfaces with `pageCount`, the cheapest way to learn a file's length and metadata. An entry naming no page throws `PdfPageSelectionError` rather than being skipped.
16
+ */
17
+ readonly pages?: readonly number[];
8
18
  }
9
19
  declare function readPdf(bytes: Uint8Array<ArrayBuffer>, options?: ReadPdfOptions): LayoutDocument;
10
20
  type PageRotation = 0 | 90 | 180 | 270;
package/dist/read.d.ts CHANGED
@@ -1,10 +1,20 @@
1
- import { i as PdfDiagnosticSink } from "./diagnostics-C9FxfZ6H.js";
1
+ import { i as PdfDiagnosticSink } from "./diagnostics-BkG_Ej2x.js";
2
2
  import { r as Matrix } from "./matrix-B7_SBKQ5.js";
3
3
  import { LayoutDocument } from "./layout.js";
4
4
  //#region src/read.d.ts
5
5
  interface ReadPdfOptions {
6
6
  readonly sink?: PdfDiagnosticSink;
7
7
  readonly signal?: AbortSignal;
8
+ /**
9
+ * The pages to read, as zero-based indices into the document's page tree; a negative index counts from the end (`-1` is the last page). Omitted, every page is read.
10
+ *
11
+ * What a selected read skips: an unselected page's content stream is never fetched, decoded or interpreted, so the fonts and images only unselected pages use are never resolved; embedded-file payloads are never decoded, so `attachments` is absent from a page-selected result (an attachment is document-level payload whose size is independent of the pages asked for, and decoding it would cost the same whichever pages were selected).
12
+ *
13
+ * What it still reads whole, because a negative index and every document-level page reference resolve against it or because it is document-level and holds no page content stream: the page tree, the outline, named destinations, optional-content layers, the AcroForm, the structure element tree and the /ParentTree number tree (both of which scale with the document), metadata and package residue. Two surfaces are built from page content and so cover selected pages only: the structure-ownership association an item's `structure` field names (and its parent-tree diagnostics), and the `page-boxes` residue row. Destinations a link states as a direct array are minted into `destinations` (as `dest1`, `dest2`, ...) only when their page is interpreted, so an unselected page's direct link destinations are absent and a minted name depends on which pages were read; named destinations are unaffected.
14
+ *
15
+ * Duplicates collapse, and the result's `pages` holds the selected pages in ascending document order whatever order they were asked for in, each carrying its source position as `index`, with the document's total as `pageCount`; every document-level reference keeps naming source indices. An empty array is a valid selection: it reads no page and returns the document-level surfaces with `pageCount`, the cheapest way to learn a file's length and metadata. An entry naming no page throws `PdfPageSelectionError` rather than being skipped.
16
+ */
17
+ readonly pages?: readonly number[];
8
18
  }
9
19
  declare function readPdf(bytes: Uint8Array<ArrayBuffer>, options?: ReadPdfOptions): LayoutDocument;
10
20
  type PageRotation = 0 | 90 | 180 | 270;
package/dist/read.js CHANGED
@@ -12,13 +12,22 @@ import { readXmpMetadata } from "./xmp.js";
12
12
  import { readOptionalContent } from "./optional-content.js";
13
13
  import { readStructure } from "./structure.js";
14
14
  import "./layout.js";
15
- import { NOOP_DIAGNOSTIC_SINK, PdfParseError } from "./diagnostics.js";
15
+ import { NOOP_DIAGNOSTIC_SINK, PdfPageSelectionError, PdfParseError } from "./diagnostics.js";
16
16
  import { openPdfDocument } from "./document.js";
17
17
  import { throwIfAborted } from "./util/abort.js";
18
18
  import { createFontResolver } from "./font-read.js";
19
19
  import { t as interpretContentStream } from "./interpret---0lZPlv.js";
20
20
  import { createDestinationRegistry, readOutline } from "./navigation.js";
21
21
  //#region src/read.ts
22
+ function resolvePageSelection(selection, pageCount) {
23
+ const resolved = /* @__PURE__ */ new Set();
24
+ for (const requested of selection) {
25
+ const index = requested < 0 ? pageCount + requested : requested;
26
+ if (!Number.isInteger(requested) || index < 0 || index >= pageCount) throw new PdfPageSelectionError(requested, pageCount);
27
+ resolved.add(index);
28
+ }
29
+ return resolved;
30
+ }
22
31
  const PDF_HEADER_BYTES = new TextEncoder().encode("%PDF-");
23
32
  const HEADER_SEARCH_WINDOW = 1024;
24
33
  const DEFAULT_PAGE_WIDTH_PT = 612;
@@ -44,19 +53,24 @@ function readPdf(bytes, options) {
44
53
  const images = {};
45
54
  const imageIdCache = /* @__PURE__ */ new Map();
46
55
  const pageDicts = doc.pages();
56
+ const selection = options?.pages === void 0 ? void 0 : resolvePageSelection(options.pages, pageDicts.length);
47
57
  const destinationRegistry = createDestinationRegistry(doc.catalog, doc, (obj) => doc.pageIndex(obj), sink);
48
58
  const outline = readOutline(doc.catalog, destinationRegistry, doc, sink);
49
- const attachments = readAttachments(doc.catalog, pageDicts, doc, sink);
59
+ const attachments = selection === void 0 ? readAttachments(doc.catalog, pageDicts, doc, sink) : [];
50
60
  const optionalContent = readOptionalContent(doc.catalog, doc, sink);
51
61
  const form = readAcroForm(doc.catalog, doc, (obj) => doc.pageIndex(obj), sink);
52
- const structure = readStructure(doc.catalog, pageDicts, doc, sink);
62
+ const structure = readStructure(doc.catalog, pageDicts, (index) => selection === void 0 || selection.has(index), doc, sink);
53
63
  const source = readDocumentResidue(doc, sink);
54
64
  const pageBoxRows = [];
55
- const pages = pageDicts.map((pageDict, index) => {
65
+ const pages = pageDicts.flatMap((pageDict, index) => {
66
+ if (selection !== void 0 && !selection.has(index)) return [];
56
67
  throwIfAborted(signal);
57
68
  const read = readPage(index, pageDict, doc, fontResolver, images, imageIdCache, destinationRegistry, optionalContent.layerNameOf, structure.ownerOf, sink);
58
69
  if (read.boxRow !== void 0) pageBoxRows.push(read.boxRow);
59
- return read.page;
70
+ return [selection === void 0 ? read.page : {
71
+ ...read.page,
72
+ index
73
+ }];
60
74
  });
61
75
  if (pageBoxRows.length > 0) source["page-boxes"] = {
62
76
  format: "pdf",
@@ -66,6 +80,7 @@ function readPdf(bytes, options) {
66
80
  formatVersion: 1,
67
81
  metadata: readMetadata(doc.trailer, doc, doc.catalog, sink),
68
82
  pages,
83
+ ...selection !== void 0 ? { pageCount: pageDicts.length } : {},
69
84
  images,
70
85
  ...destinationRegistry.entries.length > 0 ? { destinations: [...destinationRegistry.entries] } : {},
71
86
  ...outline.length > 0 ? { outline } : {},
@@ -6,7 +6,16 @@ const NEVER_DICT = {
6
6
  kind: "dict",
7
7
  entries: /* @__PURE__ */ new Map()
8
8
  };
9
- function readStructure(catalog, pages, resolver, sink) {
9
+ /**
10
+ * Reads the tagged-structure element tree and the (page, MCID) ownership map.
11
+ * @param catalog - the document catalog
12
+ * @param pages - every page of the page tree, in document order, so a position is a source page index
13
+ * @param includePage - whether the page at a source index is being read: ownership entries and parent-tree diagnostics are built for those pages only, while the element tree itself is document-level and always read whole
14
+ * @param resolver - the object resolver
15
+ * @param sink - where recoverable parse diagnostics go
16
+ * @returns the element tree and an owner lookup answering for included pages
17
+ */
18
+ function readStructure(catalog, pages, includePage, resolver, sink) {
10
19
  const root = resolver.resolveDict(require_objects.dictGet(catalog, "StructTreeRoot"));
11
20
  if (root === void 0) return {
12
21
  tree: [],
@@ -68,7 +77,7 @@ function readStructure(catalog, pages, resolver, sink) {
68
77
  }
69
78
  return {
70
79
  tree,
71
- ownerOf: parentTreeOwners(root, pages, resolver, idByDict, sink)
80
+ ownerOf: parentTreeOwners(root, pages, includePage, resolver, idByDict, sink)
72
81
  };
73
82
  }
74
83
  const ATTRIBUTE_KEYS = [
@@ -146,7 +155,7 @@ function numberTreeEntries(node, resolver, visited) {
146
155
  }
147
156
  return entries;
148
157
  }
149
- function parentTreeOwners(root, pages, resolver, idByDict, sink) {
158
+ function parentTreeOwners(root, pages, includePage, resolver, idByDict, sink) {
150
159
  const documentTree = resolver.resolveDict(require_objects.dictGet(root, "ParentTree"));
151
160
  if (documentTree === void 0) {
152
161
  if (require_objects.dictGet(root, "ParentTree") !== void 0) sink({
@@ -159,6 +168,7 @@ function parentTreeOwners(root, pages, resolver, idByDict, sink) {
159
168
  const entries = new Map(numberTreeEntries(documentTree, resolver, /* @__PURE__ */ new Set()));
160
169
  const owners = /* @__PURE__ */ new Map();
161
170
  pages.forEach((page, pageIndex) => {
171
+ if (!includePage(pageIndex)) return;
162
172
  const structParents = require_objects.asNumber(require_objects.dictGet(page, "StructParents"));
163
173
  if (structParents === void 0) return;
164
174
  const entry = entries.get(structParents);
@@ -1,4 +1,4 @@
1
- import { i as PdfDiagnosticSink } from "./diagnostics-C9FxfZ6H.cjs";
1
+ import { i as PdfDiagnosticSink } from "./diagnostics-BkG_Ej2x.cjs";
2
2
  import { n as PdfDict } from "./objects-e2RS3-yT.cjs";
3
3
  import { PdfObjectResolver } from "./interpret-types.cjs";
4
4
  import { LayoutStructureElement } from "./layout.cjs";
@@ -7,6 +7,15 @@ interface StructureContext {
7
7
  readonly tree: readonly LayoutStructureElement[];
8
8
  readonly ownerOf: (pageIndex: number, mcid: number) => string | undefined;
9
9
  }
10
- declare function readStructure(catalog: PdfDict, pages: readonly PdfDict[], resolver: Readonly<PdfObjectResolver>, sink: PdfDiagnosticSink): StructureContext;
10
+ /**
11
+ * Reads the tagged-structure element tree and the (page, MCID) ownership map.
12
+ * @param catalog - the document catalog
13
+ * @param pages - every page of the page tree, in document order, so a position is a source page index
14
+ * @param includePage - whether the page at a source index is being read: ownership entries and parent-tree diagnostics are built for those pages only, while the element tree itself is document-level and always read whole
15
+ * @param resolver - the object resolver
16
+ * @param sink - where recoverable parse diagnostics go
17
+ * @returns the element tree and an owner lookup answering for included pages
18
+ */
19
+ declare function readStructure(catalog: PdfDict, pages: readonly PdfDict[], includePage: (pageIndex: number) => boolean, resolver: Readonly<PdfObjectResolver>, sink: PdfDiagnosticSink): StructureContext;
11
20
  //#endregion
12
21
  export { StructureContext, readStructure };
@@ -1,4 +1,4 @@
1
- import { i as PdfDiagnosticSink } from "./diagnostics-C9FxfZ6H.js";
1
+ import { i as PdfDiagnosticSink } from "./diagnostics-BkG_Ej2x.js";
2
2
  import { n as PdfDict } from "./objects-e2RS3-yT.js";
3
3
  import { PdfObjectResolver } from "./interpret-types.js";
4
4
  import { LayoutStructureElement } from "./layout.js";
@@ -7,6 +7,15 @@ interface StructureContext {
7
7
  readonly tree: readonly LayoutStructureElement[];
8
8
  readonly ownerOf: (pageIndex: number, mcid: number) => string | undefined;
9
9
  }
10
- declare function readStructure(catalog: PdfDict, pages: readonly PdfDict[], resolver: Readonly<PdfObjectResolver>, sink: PdfDiagnosticSink): StructureContext;
10
+ /**
11
+ * Reads the tagged-structure element tree and the (page, MCID) ownership map.
12
+ * @param catalog - the document catalog
13
+ * @param pages - every page of the page tree, in document order, so a position is a source page index
14
+ * @param includePage - whether the page at a source index is being read: ownership entries and parent-tree diagnostics are built for those pages only, while the element tree itself is document-level and always read whole
15
+ * @param resolver - the object resolver
16
+ * @param sink - where recoverable parse diagnostics go
17
+ * @returns the element tree and an owner lookup answering for included pages
18
+ */
19
+ declare function readStructure(catalog: PdfDict, pages: readonly PdfDict[], includePage: (pageIndex: number) => boolean, resolver: Readonly<PdfObjectResolver>, sink: PdfDiagnosticSink): StructureContext;
11
20
  //#endregion
12
21
  export { StructureContext, readStructure };
package/dist/structure.js CHANGED
@@ -5,7 +5,16 @@ const NEVER_DICT = {
5
5
  kind: "dict",
6
6
  entries: /* @__PURE__ */ new Map()
7
7
  };
8
- function readStructure(catalog, pages, resolver, sink) {
8
+ /**
9
+ * Reads the tagged-structure element tree and the (page, MCID) ownership map.
10
+ * @param catalog - the document catalog
11
+ * @param pages - every page of the page tree, in document order, so a position is a source page index
12
+ * @param includePage - whether the page at a source index is being read: ownership entries and parent-tree diagnostics are built for those pages only, while the element tree itself is document-level and always read whole
13
+ * @param resolver - the object resolver
14
+ * @param sink - where recoverable parse diagnostics go
15
+ * @returns the element tree and an owner lookup answering for included pages
16
+ */
17
+ function readStructure(catalog, pages, includePage, resolver, sink) {
9
18
  const root = resolver.resolveDict(dictGet(catalog, "StructTreeRoot"));
10
19
  if (root === void 0) return {
11
20
  tree: [],
@@ -67,7 +76,7 @@ function readStructure(catalog, pages, resolver, sink) {
67
76
  }
68
77
  return {
69
78
  tree,
70
- ownerOf: parentTreeOwners(root, pages, resolver, idByDict, sink)
79
+ ownerOf: parentTreeOwners(root, pages, includePage, resolver, idByDict, sink)
71
80
  };
72
81
  }
73
82
  const ATTRIBUTE_KEYS = [
@@ -145,7 +154,7 @@ function numberTreeEntries(node, resolver, visited) {
145
154
  }
146
155
  return entries;
147
156
  }
148
- function parentTreeOwners(root, pages, resolver, idByDict, sink) {
157
+ function parentTreeOwners(root, pages, includePage, resolver, idByDict, sink) {
149
158
  const documentTree = resolver.resolveDict(dictGet(root, "ParentTree"));
150
159
  if (documentTree === void 0) {
151
160
  if (dictGet(root, "ParentTree") !== void 0) sink({
@@ -158,6 +167,7 @@ function parentTreeOwners(root, pages, resolver, idByDict, sink) {
158
167
  const entries = new Map(numberTreeEntries(documentTree, resolver, /* @__PURE__ */ new Set()));
159
168
  const owners = /* @__PURE__ */ new Map();
160
169
  pages.forEach((page, pageIndex) => {
170
+ if (!includePage(pageIndex)) return;
161
171
  const structParents = asNumber(dictGet(page, "StructParents"));
162
172
  if (structParents === void 0) return;
163
173
  const entry = entries.get(structParents);
package/dist/write.cjs CHANGED
@@ -69,7 +69,21 @@ function buildFontObjects(standardName, descriptorRef) {
69
69
  function codePointsOf(texts) {
70
70
  return new Set(texts.flatMap((text) => [...text].map((character) => character.codePointAt(0))));
71
71
  }
72
+ /**
73
+ * writePdf was handed a LayoutDocument from a page-selected read (one carrying `pageCount`). Such a document is a reading of part of a file, not a document to re-emit: its pages are a selection while its destinations, form widgets and outline still name source page indices, and the writer places pages by position, so writing it would point those references at the wrong pages or past the end. Thrown rather than writing a file whose links silently lie.
74
+ */
75
+ var PdfPartialDocumentError = class extends Error {
76
+ pageCount;
77
+ selectedPageCount;
78
+ constructor(pageCount, selectedPageCount) {
79
+ super(`this LayoutDocument holds ${String(selectedPageCount)} pages selected from a ${String(pageCount)}-page file (it carries pageCount, which only a page-selected read sets); its page references name pages of the source file, so it cannot be written as a document of its own`);
80
+ this.name = "PdfPartialDocumentError";
81
+ this.pageCount = pageCount;
82
+ this.selectedPageCount = selectedPageCount;
83
+ }
84
+ };
72
85
  function writePdf(doc, options = {}) {
86
+ if (doc.pageCount !== void 0) throw new PdfPartialDocumentError(doc.pageCount, doc.pages.length);
73
87
  const compress = options.compress ?? true;
74
88
  const registry = options.fonts;
75
89
  const measurer = require_measure.createFontMeasurer(registry);
@@ -478,4 +492,5 @@ function writePdf(doc, options = {}) {
478
492
  return writer.toBytes();
479
493
  }
480
494
  //#endregion
495
+ exports.PdfPartialDocumentError = PdfPartialDocumentError;
481
496
  exports.writePdf = writePdf;
package/dist/write.d.cts CHANGED
@@ -18,6 +18,14 @@ interface WritePdfOptions {
18
18
  readonly formulas?: readonly PositionedFormula[];
19
19
  readonly encryption?: PdfEncryptionOptions;
20
20
  }
21
+ /**
22
+ * writePdf was handed a LayoutDocument from a page-selected read (one carrying `pageCount`). Such a document is a reading of part of a file, not a document to re-emit: its pages are a selection while its destinations, form widgets and outline still name source page indices, and the writer places pages by position, so writing it would point those references at the wrong pages or past the end. Thrown rather than writing a file whose links silently lie.
23
+ */
24
+ declare class PdfPartialDocumentError extends Error {
25
+ readonly pageCount: number;
26
+ readonly selectedPageCount: number;
27
+ constructor(pageCount: number, selectedPageCount: number);
28
+ }
21
29
  declare function writePdf(doc: LayoutDocument, options?: WritePdfOptions): Uint8Array<ArrayBuffer>;
22
30
  //#endregion
23
- export { WritePdfOptions, writePdf };
31
+ export { PdfPartialDocumentError, WritePdfOptions, writePdf };
package/dist/write.d.ts CHANGED
@@ -18,6 +18,14 @@ interface WritePdfOptions {
18
18
  readonly formulas?: readonly PositionedFormula[];
19
19
  readonly encryption?: PdfEncryptionOptions;
20
20
  }
21
+ /**
22
+ * writePdf was handed a LayoutDocument from a page-selected read (one carrying `pageCount`). Such a document is a reading of part of a file, not a document to re-emit: its pages are a selection while its destinations, form widgets and outline still name source page indices, and the writer places pages by position, so writing it would point those references at the wrong pages or past the end. Thrown rather than writing a file whose links silently lie.
23
+ */
24
+ declare class PdfPartialDocumentError extends Error {
25
+ readonly pageCount: number;
26
+ readonly selectedPageCount: number;
27
+ constructor(pageCount: number, selectedPageCount: number);
28
+ }
21
29
  declare function writePdf(doc: LayoutDocument, options?: WritePdfOptions): Uint8Array<ArrayBuffer>;
22
30
  //#endregion
23
- export { WritePdfOptions, writePdf };
31
+ export { PdfPartialDocumentError, WritePdfOptions, writePdf };
package/dist/write.js CHANGED
@@ -68,7 +68,21 @@ function buildFontObjects(standardName, descriptorRef) {
68
68
  function codePointsOf(texts) {
69
69
  return new Set(texts.flatMap((text) => [...text].map((character) => character.codePointAt(0))));
70
70
  }
71
+ /**
72
+ * writePdf was handed a LayoutDocument from a page-selected read (one carrying `pageCount`). Such a document is a reading of part of a file, not a document to re-emit: its pages are a selection while its destinations, form widgets and outline still name source page indices, and the writer places pages by position, so writing it would point those references at the wrong pages or past the end. Thrown rather than writing a file whose links silently lie.
73
+ */
74
+ var PdfPartialDocumentError = class extends Error {
75
+ pageCount;
76
+ selectedPageCount;
77
+ constructor(pageCount, selectedPageCount) {
78
+ super(`this LayoutDocument holds ${String(selectedPageCount)} pages selected from a ${String(pageCount)}-page file (it carries pageCount, which only a page-selected read sets); its page references name pages of the source file, so it cannot be written as a document of its own`);
79
+ this.name = "PdfPartialDocumentError";
80
+ this.pageCount = pageCount;
81
+ this.selectedPageCount = selectedPageCount;
82
+ }
83
+ };
71
84
  function writePdf(doc, options = {}) {
85
+ if (doc.pageCount !== void 0) throw new PdfPartialDocumentError(doc.pageCount, doc.pages.length);
72
86
  const compress = options.compress ?? true;
73
87
  const registry = options.fonts;
74
88
  const measurer = createFontMeasurer(registry);
@@ -477,4 +491,4 @@ function writePdf(doc, options = {}) {
477
491
  return writer.toBytes();
478
492
  }
479
493
  //#endregion
480
- export { writePdf };
494
+ export { PdfPartialDocumentError, writePdf };
package/dist/xref.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { i as PdfDiagnosticSink } from "./diagnostics-C9FxfZ6H.cjs";
1
+ import { i as PdfDiagnosticSink } from "./diagnostics-BkG_Ej2x.cjs";
2
2
  import { n as PdfDict } from "./objects-e2RS3-yT.cjs";
3
3
  //#region src/xref.d.ts
4
4
  interface XrefOffsetEntry {
package/dist/xref.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { i as PdfDiagnosticSink } from "./diagnostics-C9FxfZ6H.js";
1
+ import { i as PdfDiagnosticSink } from "./diagnostics-BkG_Ej2x.js";
2
2
  import { n as PdfDict } from "./objects-e2RS3-yT.js";
3
3
  //#region src/xref.d.ts
4
4
  interface XrefOffsetEntry {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pdf-codec",
3
- "version": "5.3.32",
3
+ "version": "5.4.0",
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": {
@@ -1,22 +0,0 @@
1
- //#region src/diagnostics.d.ts
2
- type PdfDiagnosticSeverity = "info" | "warning";
3
- interface PdfDiagnostic {
4
- readonly code: string;
5
- readonly severity: PdfDiagnosticSeverity;
6
- readonly message: string;
7
- readonly pageIndex?: number;
8
- }
9
- type PdfDiagnosticSink = (diagnostic: PdfDiagnostic) => void;
10
- declare const NOOP_DIAGNOSTIC_SINK: PdfDiagnosticSink;
11
- declare class PdfParseError extends Error {
12
- readonly code: string;
13
- constructor(code: string, message: string);
14
- }
15
- declare class PdfEncryptedError extends PdfParseError {
16
- constructor(message?: string);
17
- }
18
- declare class PdfPasswordRequiredError extends PdfParseError {
19
- constructor(message?: string);
20
- }
21
- //#endregion
22
- export { PdfEncryptedError as a, PdfDiagnosticSink as i, PdfDiagnostic as n, PdfParseError as o, PdfDiagnosticSeverity as r, PdfPasswordRequiredError as s, NOOP_DIAGNOSTIC_SINK as t };
@@ -1,22 +0,0 @@
1
- //#region src/diagnostics.d.ts
2
- type PdfDiagnosticSeverity = "info" | "warning";
3
- interface PdfDiagnostic {
4
- readonly code: string;
5
- readonly severity: PdfDiagnosticSeverity;
6
- readonly message: string;
7
- readonly pageIndex?: number;
8
- }
9
- type PdfDiagnosticSink = (diagnostic: PdfDiagnostic) => void;
10
- declare const NOOP_DIAGNOSTIC_SINK: PdfDiagnosticSink;
11
- declare class PdfParseError extends Error {
12
- readonly code: string;
13
- constructor(code: string, message: string);
14
- }
15
- declare class PdfEncryptedError extends PdfParseError {
16
- constructor(message?: string);
17
- }
18
- declare class PdfPasswordRequiredError extends PdfParseError {
19
- constructor(message?: string);
20
- }
21
- //#endregion
22
- export { PdfEncryptedError as a, PdfDiagnosticSink as i, PdfDiagnostic as n, PdfParseError as o, PdfDiagnosticSeverity as r, PdfPasswordRequiredError as s, NOOP_DIAGNOSTIC_SINK as t };