pdf-codec 3.1.4 → 3.2.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 (92) hide show
  1. package/README.md +2 -2
  2. package/dist/annotations.cjs +104 -0
  3. package/dist/annotations.d.cts +9 -0
  4. package/dist/annotations.d.ts +9 -0
  5. package/dist/annotations.js +103 -0
  6. package/dist/attachments.cjs +65 -0
  7. package/dist/attachments.d.cts +8 -0
  8. package/dist/attachments.d.ts +8 -0
  9. package/dist/attachments.js +64 -0
  10. package/dist/bytes/flate.cjs +1 -1
  11. package/dist/bytes/flate.js +1 -1
  12. package/dist/codec.d.cts +109 -0
  13. package/dist/codec.d.ts +109 -0
  14. package/dist/content-read.cjs +1 -1
  15. package/dist/content-read.js +1 -1
  16. package/dist/content-write.cjs +2 -2
  17. package/dist/content-write.js +2 -2
  18. package/dist/document.cjs +12 -3
  19. package/dist/document.d.cts +2 -0
  20. package/dist/document.d.ts +2 -0
  21. package/dist/document.js +12 -3
  22. package/dist/embedded-font-write.cjs +1 -1
  23. package/dist/embedded-font-write.js +1 -1
  24. package/dist/encrypt.cjs +1 -1
  25. package/dist/encrypt.js +1 -1
  26. package/dist/filters.cjs +1 -1
  27. package/dist/filters.js +1 -1
  28. package/dist/font-read.cjs +1 -1
  29. package/dist/font-read.js +1 -1
  30. package/dist/font-registry.cjs +1 -1
  31. package/dist/font-registry.js +1 -1
  32. package/dist/form.cjs +138 -0
  33. package/dist/form.d.cts +9 -0
  34. package/dist/form.d.ts +9 -0
  35. package/dist/form.js +137 -0
  36. package/dist/image/png-decode.cjs +1 -1
  37. package/dist/image/png-decode.js +1 -1
  38. package/dist/image/png-encode.cjs +1 -1
  39. package/dist/image/png-encode.js +1 -1
  40. package/dist/index.cjs +12 -2
  41. package/dist/index.d.cts +3 -3
  42. package/dist/index.d.ts +3 -3
  43. package/dist/index.js +3 -3
  44. package/dist/interpret.cjs +70 -10
  45. package/dist/interpret.d.cts +9 -1
  46. package/dist/interpret.d.ts +9 -1
  47. package/dist/interpret.js +70 -10
  48. package/dist/layout.cjs +132 -2
  49. package/dist/layout.d.cts +335 -1
  50. package/dist/layout.d.ts +335 -1
  51. package/dist/layout.js +124 -4
  52. package/dist/math-content-write.cjs +1 -1
  53. package/dist/math-content-write.js +1 -1
  54. package/dist/math-font.cjs +1 -1
  55. package/dist/math-font.js +1 -1
  56. package/dist/names.cjs +41 -0
  57. package/dist/names.d.cts +11 -0
  58. package/dist/names.d.ts +11 -0
  59. package/dist/names.js +40 -0
  60. package/dist/navigation.cjs +210 -0
  61. package/dist/navigation.d.cts +18 -0
  62. package/dist/navigation.d.ts +18 -0
  63. package/dist/navigation.js +207 -0
  64. package/dist/optional-content.cjs +63 -0
  65. package/dist/optional-content.d.cts +12 -0
  66. package/dist/optional-content.d.ts +12 -0
  67. package/dist/optional-content.js +62 -0
  68. package/dist/parse.cjs +1 -1
  69. package/dist/parse.d.cts +1 -1
  70. package/dist/parse.d.ts +1 -1
  71. package/dist/parse.js +1 -1
  72. package/dist/pdf-text.cjs +21 -0
  73. package/dist/pdf-text.d.cts +5 -0
  74. package/dist/pdf-text.d.ts +5 -0
  75. package/dist/pdf-text.js +19 -0
  76. package/dist/read.cjs +128 -58
  77. package/dist/read.d.cts +2 -4
  78. package/dist/read.d.ts +2 -4
  79. package/dist/read.js +125 -53
  80. package/dist/serialize.cjs +5 -1
  81. package/dist/serialize.d.cts +3 -2
  82. package/dist/serialize.d.ts +3 -2
  83. package/dist/serialize.js +5 -2
  84. package/dist/write.cjs +51 -5
  85. package/dist/write.js +51 -5
  86. package/dist/xmp.cjs +46 -0
  87. package/dist/xmp.d.cts +14 -0
  88. package/dist/xmp.d.ts +14 -0
  89. package/dist/xmp.js +45 -0
  90. package/dist/xref.cjs +2 -2
  91. package/dist/xref.js +2 -2
  92. package/package.json +2 -2
package/README.md CHANGED
@@ -193,7 +193,7 @@ One subpath is a declared entry point in its own right: **`pdf-codec/read`** (an
193
193
  import { readPdf } from 'pdf-codec/read';
194
194
  ```
195
195
 
196
- `src/read-graph.test.ts` walks the entry's static import graph and fails the build if `write.ts`, `math-font.ts`, `font-registry.ts`, or any asset module becomes reachable (type-only imports are exempt — they erase at compile time — so a read-side module can keep typing against the root barrel's types while its runtime graph stays narrow). The entry carries `readPdf` and the read pipeline's own helpers (`normalizeRotation`, `pageRotationTransform`, `decodePdfString`, `parsePdfDate`); the other read-adjacent surfaces it does not itself own stay deep-importable through the wildcard and are asset-free the same way — the diagnostics vocabulary (`pdf-codec/diagnostics`), the `LayoutDocument` item family (`pdf-codec/layout`), and standard-14 resolution/AFM metrics (`pdf-codec/fonts`, `pdf-codec/afm-widths`).
196
+ `src/read-graph.test.ts` walks the entry's static import graph and fails the build if `write.ts`, `math-font.ts`, `font-registry.ts`, or any asset module becomes reachable (type-only imports are exempt — they erase at compile time — so a read-side module can keep typing against the root barrel's types while its runtime graph stays narrow). The entry carries `readPdf` and the read pipeline's own helpers (`normalizeRotation`, `pageRotationTransform`); the other read-adjacent surfaces it does not itself own stay deep-importable through the wildcard and are asset-free the same way — the diagnostics vocabulary (`pdf-codec/diagnostics`), the `LayoutDocument` item family (`pdf-codec/layout`), PDF string/date scalar decoding (`pdf-codec/pdf-text`), and standard-14 resolution/AFM metrics (`pdf-codec/fonts`, `pdf-codec/afm-widths`).
197
197
 
198
198
  ## Architecture
199
199
 
@@ -212,7 +212,7 @@ The package is layered from generic primitives outward to the codec itself:
212
212
  - **Embedded text faces**: `embedded-font.ts` (parses one TrueType-outline face's metrics and `GPOS` pair kerning, and `encodeForShowEmbedded` — the single code path both measurement and text-showing go through so encoding and measuring cannot disagree). Every geometry field is converted into PDF's 1000-units-per-em glyph space. `embedded-font-write.ts` builds the `/Type0`/`/CIDFontType2`/`/FontDescriptor`/`/FontFile2`/ToUnicode group, with `/CIDToGIDMap /Identity` written explicitly and `/Length1` set to the **uncompressed** subset length. Its subset tag is a CRC32 over the face's PostScript name and glyph-ID list, so identical input yields byte-identical output.
213
213
  - **ToUnicode CMaps**: `tounicode.ts`, shared by both embedded-font writers — a character code → Unicode code point mapping written as a bfchar CMap (9.10.3), with supplementary-plane code points encoded as UTF-16BE surrogate pairs and entries emitted in blocks of at most 100.
214
214
  - **CFF reading**: `cff.ts` (shared `INDEX`/`DICT` container structures), `cff-bounds.ts` (a Type 2 charstring interpreter computing each glyph's tight ink bounding box by tracking the current point through every path operator and solving each cubic's real extrema from the roots of its derivative — a path walker, not a rasteriser; verified against the vendored STIX Two Math font's whole 5,543-glyph repertoire, matching fontTools' `BoundsPen` to within 0.01 design units). `cff-probe.ts` reads a bare CFF program's header/Name INDEX/Top DICT to detect the `ROS` operator defining a CID-keyed font — the guard a future source-embedded-font phase needs before it can trust CID == GID against an arbitrary caller-supplied font.
215
- - **Read**: `lexer.ts`/`parse.ts` (byte tokenizer and tokens → `PdfObject`), `filters.ts`/`predictors.ts` (Flate/LZW/ASCII85/ASCIIHex/RunLength/CCITTFax, TIFF/PNG predictors), `xref.ts`/`document.ts` (classic and cross-reference-stream resolution, object streams, `/Prev` chains, linear-scan recovery, the page tree with attribute inheritance), `encrypt.ts` (standard security handler: `/Encrypt` parsing, empty-user-password key derivation and `/U` verification, per-object keys, transparent string/stream decryption), `content-read.ts`/`interpret.ts` (content-stream tokenizer and graphics/text state machine, form-XObject recursion, general vector-path tracking), `cmap.ts`/`font-style.ts`/`font-read.ts` (`/ToUnicode` CMaps, font-dictionary resolution), `images-read.ts` (Image XObjects → PNG/JPEG bytes), `read.ts` (`readPdf`, assembling all of the above into a `LayoutDocument`).
215
+ - **Read**: `lexer.ts`/`parse.ts` (byte tokenizer and tokens → `PdfObject`), `filters.ts`/`predictors.ts` (Flate/LZW/ASCII85/ASCIIHex/RunLength/CCITTFax, TIFF/PNG predictors), `xref.ts`/`document.ts` (classic and cross-reference-stream resolution, object streams, `/Prev` chains, linear-scan recovery, the page tree with attribute inheritance), `encrypt.ts` (standard security handler: `/Encrypt` parsing, empty-user-password key derivation and `/U` verification, per-object keys, transparent string/stream decryption), `content-read.ts`/`interpret.ts` (content-stream tokenizer and graphics/text state machine, form-XObject recursion, general vector-path tracking), `cmap.ts`/`font-style.ts`/`font-read.ts` (`/ToUnicode` CMaps, font-dictionary resolution), `images-read.ts` (Image XObjects → PNG/JPEG bytes), `names.ts` (the document-level name-tree walker: one flattening pass for every `/Names` tenant), `navigation.ts` (named destinations from `/Dests` and `/Names` `/Dests`, reader-minted entries for direct destination arrays, and the `/Outlines` bookmark tree), `attachments.ts` (embedded files from the name tree, `/FileAttachment` filespecs, and `/AF`), `optional-content.ts` (`/OCProperties` groups and default-configuration visibility, plus the `/OC`-to-name resolution `interpret.ts` stamps onto span items), `annotations.ts` (sticky notes, FreeText, the `/QuadPoints` markup family, and residue for the opaque kinds), `form.ts` (the AcroForm field tree), `xmp.ts` (a bounded Dublin Core extractor for the `/Metadata` packet), `pdf-text.ts` (PDF string/date scalar decoding), `read.ts` (`readPdf`, assembling all of the above into a `LayoutDocument`).
216
216
  - `codec.ts` — `pdfCodec`, a `z.codec()` pair over `readPdf`/`writePdf`, plus a standalone local copy of the `%PDF-` header check.
217
217
  - **`src/test-support/`** — hand-built PDF fixtures (`pdf.ts`) built by literal byte/string concatenation and deliberately importing NOTHING from this package's own writer (a fixture built by `writePdf` would let a writer bug hide from the corresponding reader test). `encrypted-pdfs.ts` applies the same principle: real PDFs encrypted by [qpdf](https://qpdf.sourceforge.io/), embedded as base64, so a bug in key derivation cannot cancel out between write and read halves. `fonts.ts` holds the real vendored Carlito and Caladea faces as raw sfnt bytes, and asserts values read out of the `.ttf` files by a standalone script with a bare `DataView`, not by this package's own parsers — external cross-checks rather than a parser's output compared against itself.
218
218
 
@@ -0,0 +1,104 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ require("./notes-annotation-author.cjs");
3
+ const require_objects = require("./objects.cjs");
4
+ const require_pdf_text = require("./pdf-text.cjs");
5
+ const require_serialize = require("./serialize.cjs");
6
+ const require_matrix = require("./matrix.cjs");
7
+ //#region src/annotations.ts
8
+ const SEMANTIC_SUBTYPES = /* @__PURE__ */ new Set([
9
+ "Text",
10
+ "FreeText",
11
+ "Highlight",
12
+ "Underline",
13
+ "StrikeOut",
14
+ "Squiggly"
15
+ ]);
16
+ const OWNED_ELSEWHERE_SUBTYPES = /* @__PURE__ */ new Set([
17
+ "Link",
18
+ "FileAttachment",
19
+ "Widget",
20
+ "Popup"
21
+ ]);
22
+ function readPageAnnotations(page, pageMatrix, resolver, sink) {
23
+ const annotsArr = require_objects.asArray(require_objects.dictGet(page, "Annots"));
24
+ if (annotsArr === void 0) return [];
25
+ const annotations = [];
26
+ for (const annotRef of annotsArr) {
27
+ const annot = resolver.resolveDict(annotRef);
28
+ if (annot === void 0) continue;
29
+ const subtype = require_objects.asName(require_objects.dictGet(annot, "Subtype"));
30
+ if (subtype === void 0 || OWNED_ELSEWHERE_SUBTYPES.has(subtype)) continue;
31
+ if (subtype === "Text" && annotString(annot, "T") === "documents.js:notes") continue;
32
+ const rectArr = require_objects.asArray(require_objects.dictGet(annot, "Rect"));
33
+ if (rectArr === void 0) {
34
+ sink({
35
+ code: "pdf/annotation-missing-rect",
36
+ severity: "warning",
37
+ message: `a /${subtype} annotation carries no /Rect; skipping it`
38
+ });
39
+ continue;
40
+ }
41
+ const x1 = require_objects.asNumber(rectArr[0]) ?? 0;
42
+ const y1 = require_objects.asNumber(rectArr[1]) ?? 0;
43
+ const x2 = require_objects.asNumber(rectArr[2]) ?? 0;
44
+ const y2 = require_objects.asNumber(rectArr[3]) ?? 0;
45
+ const p1 = require_matrix.applyMatrix(pageMatrix, {
46
+ x: Math.min(x1, x2),
47
+ y: Math.min(y1, y2)
48
+ });
49
+ const p2 = require_matrix.applyMatrix(pageMatrix, {
50
+ x: Math.max(x1, x2),
51
+ y: Math.max(y1, y2)
52
+ });
53
+ const contents = annotString(annot, "Contents");
54
+ const author = annotString(annot, "T");
55
+ const modifiedIso = require_pdf_text.parsePdfDate(annotString(annot, "M"));
56
+ annotations.push({
57
+ subtype,
58
+ xPt: Math.min(p1.x, p2.x),
59
+ yPt: Math.min(p1.y, p2.y),
60
+ widthPt: Math.abs(p2.x - p1.x),
61
+ heightPt: Math.abs(p2.y - p1.y),
62
+ ...contents !== void 0 ? { contents } : {},
63
+ ...author !== void 0 ? { author } : {},
64
+ ...modifiedIso !== void 0 ? { modifiedIso } : {},
65
+ ...SEMANTIC_SUBTYPES.has(subtype) ? markupFields(annot, pageMatrix) : { source: {
66
+ format: "pdf",
67
+ xml: require_serialize.serializeObjectToText(annot)
68
+ } }
69
+ });
70
+ }
71
+ return annotations;
72
+ }
73
+ function annotString(annot, key) {
74
+ const obj = require_objects.dictGet(annot, key);
75
+ return obj?.kind === "string" ? require_pdf_text.decodePdfString(obj.bytes) : void 0;
76
+ }
77
+ function markupFields(annot, pageMatrix) {
78
+ const quadPoints = require_objects.asArray(require_objects.dictGet(annot, "QuadPoints"));
79
+ if (quadPoints === void 0 || quadPoints.length < 8 || quadPoints.length % 8 !== 0) return {};
80
+ const quads = [];
81
+ for (let i = 0; i < quadPoints.length; i += 8) {
82
+ const corner = (index) => {
83
+ const x = require_objects.asNumber(quadPoints[i + index * 2]) ?? 0;
84
+ const y = require_objects.asNumber(quadPoints[i + index * 2 + 1]) ?? 0;
85
+ const transformed = require_matrix.applyMatrix(pageMatrix, {
86
+ x,
87
+ y
88
+ });
89
+ return {
90
+ xPt: transformed.x,
91
+ yPt: transformed.y
92
+ };
93
+ };
94
+ quads.push([
95
+ corner(0),
96
+ corner(1),
97
+ corner(2),
98
+ corner(3)
99
+ ]);
100
+ }
101
+ return { quads };
102
+ }
103
+ //#endregion
104
+ exports.readPageAnnotations = readPageAnnotations;
@@ -0,0 +1,9 @@
1
+ import { PdfDiagnosticSink } from "./diagnostics.cjs";
2
+ import { r as Matrix } from "./matrix-B7_SBKQ5.cjs";
3
+ import { PdfDict } from "./objects.cjs";
4
+ import { PdfObjectResolver } from "./interpret.cjs";
5
+ import { LayoutAnnotation } from "./layout.cjs";
6
+ //#region src/annotations.d.ts
7
+ declare function readPageAnnotations(page: PdfDict, pageMatrix: Matrix, resolver: PdfObjectResolver, sink: PdfDiagnosticSink): LayoutAnnotation[];
8
+ //#endregion
9
+ export { readPageAnnotations };
@@ -0,0 +1,9 @@
1
+ import { PdfDiagnosticSink } from "./diagnostics.js";
2
+ import { r as Matrix } from "./matrix-B7_SBKQ5.js";
3
+ import { PdfDict } from "./objects.js";
4
+ import { PdfObjectResolver } from "./interpret.js";
5
+ import { LayoutAnnotation } from "./layout.js";
6
+ //#region src/annotations.d.ts
7
+ declare function readPageAnnotations(page: PdfDict, pageMatrix: Matrix, resolver: PdfObjectResolver, sink: PdfDiagnosticSink): LayoutAnnotation[];
8
+ //#endregion
9
+ export { readPageAnnotations };
@@ -0,0 +1,103 @@
1
+ import "./notes-annotation-author.js";
2
+ import { asArray, asName, asNumber, dictGet } from "./objects.js";
3
+ import { decodePdfString, parsePdfDate } from "./pdf-text.js";
4
+ import { serializeObjectToText } from "./serialize.js";
5
+ import { applyMatrix } from "./matrix.js";
6
+ //#region src/annotations.ts
7
+ const SEMANTIC_SUBTYPES = /* @__PURE__ */ new Set([
8
+ "Text",
9
+ "FreeText",
10
+ "Highlight",
11
+ "Underline",
12
+ "StrikeOut",
13
+ "Squiggly"
14
+ ]);
15
+ const OWNED_ELSEWHERE_SUBTYPES = /* @__PURE__ */ new Set([
16
+ "Link",
17
+ "FileAttachment",
18
+ "Widget",
19
+ "Popup"
20
+ ]);
21
+ function readPageAnnotations(page, pageMatrix, resolver, sink) {
22
+ const annotsArr = asArray(dictGet(page, "Annots"));
23
+ if (annotsArr === void 0) return [];
24
+ const annotations = [];
25
+ for (const annotRef of annotsArr) {
26
+ const annot = resolver.resolveDict(annotRef);
27
+ if (annot === void 0) continue;
28
+ const subtype = asName(dictGet(annot, "Subtype"));
29
+ if (subtype === void 0 || OWNED_ELSEWHERE_SUBTYPES.has(subtype)) continue;
30
+ if (subtype === "Text" && annotString(annot, "T") === "documents.js:notes") continue;
31
+ const rectArr = asArray(dictGet(annot, "Rect"));
32
+ if (rectArr === void 0) {
33
+ sink({
34
+ code: "pdf/annotation-missing-rect",
35
+ severity: "warning",
36
+ message: `a /${subtype} annotation carries no /Rect; skipping it`
37
+ });
38
+ continue;
39
+ }
40
+ const x1 = asNumber(rectArr[0]) ?? 0;
41
+ const y1 = asNumber(rectArr[1]) ?? 0;
42
+ const x2 = asNumber(rectArr[2]) ?? 0;
43
+ const y2 = asNumber(rectArr[3]) ?? 0;
44
+ const p1 = applyMatrix(pageMatrix, {
45
+ x: Math.min(x1, x2),
46
+ y: Math.min(y1, y2)
47
+ });
48
+ const p2 = applyMatrix(pageMatrix, {
49
+ x: Math.max(x1, x2),
50
+ y: Math.max(y1, y2)
51
+ });
52
+ const contents = annotString(annot, "Contents");
53
+ const author = annotString(annot, "T");
54
+ const modifiedIso = parsePdfDate(annotString(annot, "M"));
55
+ annotations.push({
56
+ subtype,
57
+ xPt: Math.min(p1.x, p2.x),
58
+ yPt: Math.min(p1.y, p2.y),
59
+ widthPt: Math.abs(p2.x - p1.x),
60
+ heightPt: Math.abs(p2.y - p1.y),
61
+ ...contents !== void 0 ? { contents } : {},
62
+ ...author !== void 0 ? { author } : {},
63
+ ...modifiedIso !== void 0 ? { modifiedIso } : {},
64
+ ...SEMANTIC_SUBTYPES.has(subtype) ? markupFields(annot, pageMatrix) : { source: {
65
+ format: "pdf",
66
+ xml: serializeObjectToText(annot)
67
+ } }
68
+ });
69
+ }
70
+ return annotations;
71
+ }
72
+ function annotString(annot, key) {
73
+ const obj = dictGet(annot, key);
74
+ return obj?.kind === "string" ? decodePdfString(obj.bytes) : void 0;
75
+ }
76
+ function markupFields(annot, pageMatrix) {
77
+ const quadPoints = asArray(dictGet(annot, "QuadPoints"));
78
+ if (quadPoints === void 0 || quadPoints.length < 8 || quadPoints.length % 8 !== 0) return {};
79
+ const quads = [];
80
+ for (let i = 0; i < quadPoints.length; i += 8) {
81
+ const corner = (index) => {
82
+ const x = asNumber(quadPoints[i + index * 2]) ?? 0;
83
+ const y = asNumber(quadPoints[i + index * 2 + 1]) ?? 0;
84
+ const transformed = applyMatrix(pageMatrix, {
85
+ x,
86
+ y
87
+ });
88
+ return {
89
+ xPt: transformed.x,
90
+ yPt: transformed.y
91
+ };
92
+ };
93
+ quads.push([
94
+ corner(0),
95
+ corner(1),
96
+ corner(2),
97
+ corner(3)
98
+ ]);
99
+ }
100
+ return { quads };
101
+ }
102
+ //#endregion
103
+ export { readPageAnnotations };
@@ -0,0 +1,65 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_objects = require("./objects.cjs");
3
+ const require_pdf_text = require("./pdf-text.cjs");
4
+ const require_filters = require("./filters.cjs");
5
+ const require_names = require("./names.cjs");
6
+ const require_util_base64 = require("./util/base64.cjs");
7
+ //#region src/attachments.ts
8
+ function readAttachments(catalog, pages, resolver, sink) {
9
+ const attachments = [];
10
+ const seen = /* @__PURE__ */ new Set();
11
+ const collect = (filespecRef) => {
12
+ const filespec = resolver.resolveDict(filespecRef);
13
+ if (filespec === void 0) return;
14
+ const name = filespecName(filespec);
15
+ if (name === void 0 || seen.has(name)) return;
16
+ const attachment = readFilespec(name, filespec, resolver, sink);
17
+ if (attachment !== void 0) {
18
+ seen.add(name);
19
+ attachments.push(attachment);
20
+ }
21
+ };
22
+ const namesRoot = resolver.resolveDict(require_objects.dictGet(catalog, "Names"));
23
+ for (const entry of require_names.walkNameTree(namesRoot === void 0 ? void 0 : require_objects.dictGet(namesRoot, "EmbeddedFiles"), resolver, sink)) collect(entry.value);
24
+ for (const page of pages) {
25
+ const annots = require_objects.asArray(require_objects.dictGet(page, "Annots"));
26
+ if (annots === void 0) continue;
27
+ for (const annotRef of annots) {
28
+ const annot = resolver.resolveDict(annotRef);
29
+ if (annot !== void 0 && require_objects.asName(require_objects.dictGet(annot, "Subtype")) === "FileAttachment") collect(require_objects.dictGet(annot, "FS"));
30
+ }
31
+ }
32
+ const af = require_objects.asArray(resolver.resolve(require_objects.dictGet(catalog, "AF")));
33
+ if (af !== void 0) for (const filespecRef of af) collect(filespecRef);
34
+ return attachments;
35
+ }
36
+ function filespecName(filespec) {
37
+ for (const key of ["UF", "F"]) {
38
+ const obj = require_objects.dictGet(filespec, key);
39
+ if (obj?.kind === "string") return require_pdf_text.decodePdfString(obj.bytes);
40
+ }
41
+ }
42
+ function readFilespec(name, filespec, resolver, sink) {
43
+ const ef = resolver.resolveDict(require_objects.dictGet(filespec, "EF"));
44
+ if (ef === void 0) return;
45
+ for (const key of ["F", "UF"]) {
46
+ const streamObj = resolver.resolve(require_objects.dictGet(ef, key));
47
+ if (streamObj?.kind !== "stream") continue;
48
+ const decoded = require_filters.decodeStream(streamObj.raw, streamObj.dict, sink);
49
+ const descObj = require_objects.dictGet(filespec, "Desc");
50
+ const subtype = require_objects.asName(require_objects.dictGet(streamObj.dict, "Subtype"));
51
+ return {
52
+ name,
53
+ ...descObj?.kind === "string" ? { description: require_pdf_text.decodePdfString(descObj.bytes) } : {},
54
+ ...subtype !== void 0 ? { mimeType: subtype } : {},
55
+ base64: require_util_base64.bytesToBase64(decoded.bytes)
56
+ };
57
+ }
58
+ sink({
59
+ code: "pdf/embedded-file-missing-stream",
60
+ severity: "warning",
61
+ message: "a filespec declares /EF but neither /F nor /UF resolves to an embedded stream"
62
+ });
63
+ }
64
+ //#endregion
65
+ exports.readAttachments = readAttachments;
@@ -0,0 +1,8 @@
1
+ import { PdfDiagnosticSink } from "./diagnostics.cjs";
2
+ import { PdfDict } from "./objects.cjs";
3
+ import { PdfObjectResolver } from "./interpret.cjs";
4
+ import { LayoutAttachment } from "./layout.cjs";
5
+ //#region src/attachments.d.ts
6
+ declare function readAttachments(catalog: PdfDict, pages: readonly PdfDict[], resolver: PdfObjectResolver, sink: PdfDiagnosticSink): LayoutAttachment[];
7
+ //#endregion
8
+ export { readAttachments };
@@ -0,0 +1,8 @@
1
+ import { PdfDiagnosticSink } from "./diagnostics.js";
2
+ import { PdfDict } from "./objects.js";
3
+ import { PdfObjectResolver } from "./interpret.js";
4
+ import { LayoutAttachment } from "./layout.js";
5
+ //#region src/attachments.d.ts
6
+ declare function readAttachments(catalog: PdfDict, pages: readonly PdfDict[], resolver: PdfObjectResolver, sink: PdfDiagnosticSink): LayoutAttachment[];
7
+ //#endregion
8
+ export { readAttachments };
@@ -0,0 +1,64 @@
1
+ import { asArray, asName, dictGet } from "./objects.js";
2
+ import { decodePdfString } from "./pdf-text.js";
3
+ import { decodeStream } from "./filters.js";
4
+ import { walkNameTree } from "./names.js";
5
+ import { bytesToBase64 } from "./util/base64.js";
6
+ //#region src/attachments.ts
7
+ function readAttachments(catalog, pages, resolver, sink) {
8
+ const attachments = [];
9
+ const seen = /* @__PURE__ */ new Set();
10
+ const collect = (filespecRef) => {
11
+ const filespec = resolver.resolveDict(filespecRef);
12
+ if (filespec === void 0) return;
13
+ const name = filespecName(filespec);
14
+ if (name === void 0 || seen.has(name)) return;
15
+ const attachment = readFilespec(name, filespec, resolver, sink);
16
+ if (attachment !== void 0) {
17
+ seen.add(name);
18
+ attachments.push(attachment);
19
+ }
20
+ };
21
+ const namesRoot = resolver.resolveDict(dictGet(catalog, "Names"));
22
+ for (const entry of walkNameTree(namesRoot === void 0 ? void 0 : dictGet(namesRoot, "EmbeddedFiles"), resolver, sink)) collect(entry.value);
23
+ for (const page of pages) {
24
+ const annots = asArray(dictGet(page, "Annots"));
25
+ if (annots === void 0) continue;
26
+ for (const annotRef of annots) {
27
+ const annot = resolver.resolveDict(annotRef);
28
+ if (annot !== void 0 && asName(dictGet(annot, "Subtype")) === "FileAttachment") collect(dictGet(annot, "FS"));
29
+ }
30
+ }
31
+ const af = asArray(resolver.resolve(dictGet(catalog, "AF")));
32
+ if (af !== void 0) for (const filespecRef of af) collect(filespecRef);
33
+ return attachments;
34
+ }
35
+ function filespecName(filespec) {
36
+ for (const key of ["UF", "F"]) {
37
+ const obj = dictGet(filespec, key);
38
+ if (obj?.kind === "string") return decodePdfString(obj.bytes);
39
+ }
40
+ }
41
+ function readFilespec(name, filespec, resolver, sink) {
42
+ const ef = resolver.resolveDict(dictGet(filespec, "EF"));
43
+ if (ef === void 0) return;
44
+ for (const key of ["F", "UF"]) {
45
+ const streamObj = resolver.resolve(dictGet(ef, key));
46
+ if (streamObj?.kind !== "stream") continue;
47
+ const decoded = decodeStream(streamObj.raw, streamObj.dict, sink);
48
+ const descObj = dictGet(filespec, "Desc");
49
+ const subtype = asName(dictGet(streamObj.dict, "Subtype"));
50
+ return {
51
+ name,
52
+ ...descObj?.kind === "string" ? { description: decodePdfString(descObj.bytes) } : {},
53
+ ...subtype !== void 0 ? { mimeType: subtype } : {},
54
+ base64: bytesToBase64(decoded.bytes)
55
+ };
56
+ }
57
+ sink({
58
+ code: "pdf/embedded-file-missing-stream",
59
+ severity: "warning",
60
+ message: "a filespec declares /EF but neither /F nor /UF resolves to an embedded stream"
61
+ });
62
+ }
63
+ //#endregion
64
+ export { readAttachments };
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_bytes_reader = require("./reader.cjs");
3
2
  const require_bytes_writer = require("./writer.cjs");
3
+ const require_bytes_reader = require("./reader.cjs");
4
4
  let fflate = require("fflate");
5
5
  //#region src/bytes/flate.ts
6
6
  const MAX_INFLATE_OUTPUT_BYTES = 536870912;
@@ -1,5 +1,5 @@
1
- import { isAsciiWhitespace } from "./reader.js";
2
1
  import { concatBytes } from "./writer.js";
2
+ import { isAsciiWhitespace } from "./reader.js";
3
3
  import { Unzlib, inflateSync, unzlibSync, zlibSync } from "fflate";
4
4
  //#region src/bytes/flate.ts
5
5
  const MAX_INFLATE_OUTPUT_BYTES = 536870912;
package/dist/codec.d.cts CHANGED
@@ -1,3 +1,4 @@
1
+ import { LayoutFormField, LayoutOutlineItem } from "./layout.cjs";
1
2
  import { z } from "zod";
2
3
  //#region src/codec.d.ts
3
4
  declare const PdfBytesSchema: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
@@ -12,6 +13,7 @@ declare const pdfCodec: z.ZodCodec<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Arr
12
13
  producer: z.ZodOptional<z.ZodString>;
13
14
  createdIso: z.ZodOptional<z.ZodString>;
14
15
  modifiedIso: z.ZodOptional<z.ZodString>;
16
+ language: z.ZodOptional<z.ZodString>;
15
17
  }, z.core.$strip>;
16
18
  pages: z.ZodArray<z.ZodObject<{
17
19
  widthPt: z.ZodNumber;
@@ -41,6 +43,9 @@ declare const pdfCodec: z.ZodCodec<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Arr
41
43
  widthPt: z.ZodOptional<z.ZodNumber>;
42
44
  rotationDeg: z.ZodOptional<z.ZodNumber>;
43
45
  underline: z.ZodOptional<z.ZodBoolean>;
46
+ layer: z.ZodOptional<z.ZodString>;
47
+ actualText: z.ZodOptional<z.ZodString>;
48
+ alt: z.ZodOptional<z.ZodString>;
44
49
  sourcePath: z.ZodOptional<z.ZodString>;
45
50
  }, z.core.$strip>, z.ZodObject<{
46
51
  kind: z.ZodLiteral<"image">;
@@ -50,6 +55,7 @@ declare const pdfCodec: z.ZodCodec<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Arr
50
55
  widthPt: z.ZodNumber;
51
56
  heightPt: z.ZodNumber;
52
57
  rotationDeg: z.ZodOptional<z.ZodNumber>;
58
+ layer: z.ZodOptional<z.ZodString>;
53
59
  sourcePath: z.ZodOptional<z.ZodString>;
54
60
  }, z.core.$strip>, z.ZodObject<{
55
61
  kind: z.ZodLiteral<"rect">;
@@ -70,6 +76,7 @@ declare const pdfCodec: z.ZodCodec<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Arr
70
76
  }, z.core.$strip>;
71
77
  widthPt: z.ZodNumber;
72
78
  }, z.core.$strip>>;
79
+ layer: z.ZodOptional<z.ZodString>;
73
80
  sourcePath: z.ZodOptional<z.ZodString>;
74
81
  }, z.core.$strip>, z.ZodObject<{
75
82
  kind: z.ZodLiteral<"line">;
@@ -89,6 +96,7 @@ declare const pdfCodec: z.ZodCodec<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Arr
89
96
  dotted: "dotted";
90
97
  double: "double";
91
98
  }>>;
99
+ layer: z.ZodOptional<z.ZodString>;
92
100
  sourcePath: z.ZodOptional<z.ZodString>;
93
101
  }, z.core.$strip>, z.ZodObject<{
94
102
  kind: z.ZodLiteral<"ellipse">;
@@ -109,6 +117,7 @@ declare const pdfCodec: z.ZodCodec<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Arr
109
117
  }, z.core.$strip>;
110
118
  widthPt: z.ZodNumber;
111
119
  }, z.core.$strip>>;
120
+ layer: z.ZodOptional<z.ZodString>;
112
121
  sourcePath: z.ZodOptional<z.ZodString>;
113
122
  }, z.core.$strip>, z.ZodObject<{
114
123
  kind: z.ZodLiteral<"path">;
@@ -153,6 +162,7 @@ declare const pdfCodec: z.ZodCodec<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Arr
153
162
  dotted: "dotted";
154
163
  double: "double";
155
164
  }>>;
165
+ layer: z.ZodOptional<z.ZodString>;
156
166
  sourcePath: z.ZodOptional<z.ZodString>;
157
167
  }, z.core.$strip>, z.ZodObject<{
158
168
  kind: z.ZodLiteral<"link">;
@@ -161,8 +171,57 @@ declare const pdfCodec: z.ZodCodec<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Arr
161
171
  yPt: z.ZodNumber;
162
172
  widthPt: z.ZodNumber;
163
173
  heightPt: z.ZodNumber;
174
+ title: z.ZodOptional<z.ZodString>;
164
175
  sourcePath: z.ZodOptional<z.ZodString>;
176
+ }, z.core.$strip>, z.ZodObject<{
177
+ kind: z.ZodLiteral<"internalLink">;
178
+ destination: z.ZodString;
179
+ xPt: z.ZodNumber;
180
+ yPt: z.ZodNumber;
181
+ widthPt: z.ZodNumber;
182
+ heightPt: z.ZodNumber;
183
+ title: z.ZodOptional<z.ZodString>;
165
184
  }, z.core.$strip>], "kind">>;
185
+ annotations: z.ZodOptional<z.ZodArray<z.ZodObject<{
186
+ subtype: z.ZodString;
187
+ xPt: z.ZodNumber;
188
+ yPt: z.ZodNumber;
189
+ widthPt: z.ZodNumber;
190
+ heightPt: z.ZodNumber;
191
+ contents: z.ZodOptional<z.ZodString>;
192
+ author: z.ZodOptional<z.ZodString>;
193
+ modifiedIso: z.ZodOptional<z.ZodString>;
194
+ quads: z.ZodOptional<z.ZodArray<z.ZodTuple<[z.ZodObject<{
195
+ xPt: z.ZodNumber;
196
+ yPt: z.ZodNumber;
197
+ }, z.core.$strip>, z.ZodObject<{
198
+ xPt: z.ZodNumber;
199
+ yPt: z.ZodNumber;
200
+ }, z.core.$strip>, z.ZodObject<{
201
+ xPt: z.ZodNumber;
202
+ yPt: z.ZodNumber;
203
+ }, z.core.$strip>, z.ZodObject<{
204
+ xPt: z.ZodNumber;
205
+ yPt: z.ZodNumber;
206
+ }, z.core.$strip>], null>>>;
207
+ source: z.ZodOptional<z.ZodObject<{
208
+ format: z.ZodEnum<{
209
+ docx: "docx";
210
+ pptx: "pptx";
211
+ xlsx: "xlsx";
212
+ odt: "odt";
213
+ ods: "ods";
214
+ odp: "odp";
215
+ odg: "odg";
216
+ odm: "odm";
217
+ odb: "odb";
218
+ odf: "odf";
219
+ markdown: "markdown";
220
+ pdf: "pdf";
221
+ }>;
222
+ xml: z.ZodString;
223
+ }, z.core.$strict>>;
224
+ }, z.core.$strip>>>;
166
225
  notes: z.ZodOptional<z.ZodString>;
167
226
  }, z.core.$strip>>;
168
227
  images: z.ZodRecord<z.ZodString, z.ZodObject<{
@@ -174,6 +233,56 @@ declare const pdfCodec: z.ZodCodec<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Arr
174
233
  widthPx: z.ZodNumber;
175
234
  heightPx: z.ZodNumber;
176
235
  }, z.core.$strip>>;
236
+ destinations: z.ZodOptional<z.ZodArray<z.ZodObject<{
237
+ name: z.ZodString;
238
+ pageIndex: z.ZodNumber;
239
+ target: z.ZodObject<{
240
+ kind: z.ZodEnum<{
241
+ xyz: "xyz";
242
+ fit: "fit";
243
+ fitH: "fitH";
244
+ fitV: "fitV";
245
+ fitR: "fitR";
246
+ fitB: "fitB";
247
+ fitBH: "fitBH";
248
+ fitBV: "fitBV";
249
+ }>;
250
+ leftPt: z.ZodOptional<z.ZodNumber>;
251
+ topPt: z.ZodOptional<z.ZodNumber>;
252
+ bottomPt: z.ZodOptional<z.ZodNumber>;
253
+ rightPt: z.ZodOptional<z.ZodNumber>;
254
+ zoom: z.ZodOptional<z.ZodNumber>;
255
+ }, z.core.$strip>;
256
+ }, z.core.$strip>>>;
257
+ outline: z.ZodOptional<z.ZodArray<z.ZodType<LayoutOutlineItem, LayoutOutlineItem, z.core.$ZodTypeInternals<LayoutOutlineItem, LayoutOutlineItem>>>>;
258
+ attachments: z.ZodOptional<z.ZodArray<z.ZodObject<{
259
+ name: z.ZodString;
260
+ description: z.ZodOptional<z.ZodString>;
261
+ mimeType: z.ZodOptional<z.ZodString>;
262
+ base64: z.ZodString;
263
+ }, z.core.$strip>>>;
264
+ layers: z.ZodOptional<z.ZodArray<z.ZodObject<{
265
+ name: z.ZodString;
266
+ visible: z.ZodBoolean;
267
+ }, z.core.$strip>>>;
268
+ form: z.ZodOptional<z.ZodArray<z.ZodType<LayoutFormField, LayoutFormField, z.core.$ZodTypeInternals<LayoutFormField, LayoutFormField>>>>;
269
+ source: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
270
+ format: z.ZodEnum<{
271
+ docx: "docx";
272
+ pptx: "pptx";
273
+ xlsx: "xlsx";
274
+ odt: "odt";
275
+ ods: "ods";
276
+ odp: "odp";
277
+ odg: "odg";
278
+ odm: "odm";
279
+ odb: "odb";
280
+ odf: "odf";
281
+ markdown: "markdown";
282
+ pdf: "pdf";
283
+ }>;
284
+ xml: z.ZodString;
285
+ }, z.core.$strict>>>;
177
286
  }, z.core.$strip>>;
178
287
  //#endregion
179
288
  export { PdfBytesSchema, pdfCodec };