pdf-codec 3.1.4 → 3.3.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 +4 -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 +272 -72
  77. package/dist/read.d.cts +2 -4
  78. package/dist/read.d.ts +2 -4
  79. package/dist/read.js +269 -67
  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/dist/write.js CHANGED
@@ -1,14 +1,14 @@
1
1
  import { a as winAnsiGlyphName, n as widthOfCode, t as STANDARD_METRICS } from "./afm-widths-Dxucrg7D.js";
2
- import { base64ToBytes } from "./util/base64.js";
2
+ import { NOTES_ANNOTATION_AUTHOR } from "./notes-annotation-author.js";
3
+ import { pdfArray, pdfDict, pdfHexString, pdfName, pdfNull, pdfNum, pdfRef, pdfStream } from "./objects.js";
3
4
  import { ByteWriter, concatBytes } from "./bytes/writer.js";
4
- import { pdfArray, pdfDict, pdfHexString, pdfName, pdfNum, pdfRef, pdfStream } from "./objects.js";
5
+ import { writeObject } from "./serialize.js";
5
6
  import { deflate } from "./bytes/flate.js";
7
+ import { base64ToBytes } from "./util/base64.js";
6
8
  import { throwIfAborted } from "./util/abort.js";
7
9
  import { readJpegInfo } from "./image/jpeg-info.js";
8
- import { NOTES_ANNOTATION_AUTHOR } from "./notes-annotation-author.js";
9
10
  import { decodePng } from "./image/png-decode.js";
10
11
  import { collectEmbeddedGlyphs } from "./embedded-font.js";
11
- import { writeObject } from "./serialize.js";
12
12
  import { writeContentStream } from "./content-write.js";
13
13
  import { buildEmbeddedFontObjects } from "./embedded-font-write.js";
14
14
  import { resolveFaceWithRegistry } from "./font-registry.js";
@@ -177,9 +177,55 @@ function buildLinkAnnotDict(link) {
177
177
  })
178
178
  });
179
179
  }
180
+ function destinationViewArray(target) {
181
+ const n = (value) => value === void 0 ? pdfNull() : pdfNum(value);
182
+ if (target.kind === "xyz") return [
183
+ pdfName("XYZ"),
184
+ n(target.leftPt),
185
+ n(target.topPt),
186
+ n(target.zoom)
187
+ ];
188
+ if (target.kind === "fitH") return [pdfName("FitH"), n(target.topPt)];
189
+ if (target.kind === "fitV") return [pdfName("FitV"), n(target.leftPt)];
190
+ if (target.kind === "fitR") return [
191
+ pdfName("FitR"),
192
+ n(target.leftPt),
193
+ n(target.bottomPt),
194
+ n(target.rightPt),
195
+ n(target.topPt)
196
+ ];
197
+ if (target.kind === "fitBH") return [pdfName("FitBH"), n(target.topPt)];
198
+ if (target.kind === "fitBV") return [pdfName("FitBV"), n(target.leftPt)];
199
+ return [pdfName(target.kind === "fitB" ? "FitB" : "Fit")];
200
+ }
201
+ function buildInternalLinkAnnotDict(link, doc, pageAllocs) {
202
+ const destination = doc.destinations?.find((d) => d.name === link.destination);
203
+ if (destination === void 0) throw new Error(`internal link names destination "${link.destination}", which the document's destinations table does not carry -- this is a caller-invariant violation`);
204
+ const targetPage = pageAllocs[destination.pageIndex];
205
+ if (targetPage === void 0) throw new Error(`destination "${link.destination}" names page index ${destination.pageIndex}, which is beyond the document's own pages -- this is a caller-invariant violation`);
206
+ return pdfDict({
207
+ Type: pdfName("Annot"),
208
+ Subtype: pdfName("Link"),
209
+ Rect: pdfArray([
210
+ link.xPt,
211
+ link.yPt,
212
+ link.xPt + link.widthPt,
213
+ link.yPt + link.heightPt
214
+ ].map((v) => pdfNum(v))),
215
+ Border: pdfArray([
216
+ 0,
217
+ 0,
218
+ 0
219
+ ].map((v) => pdfNum(v))),
220
+ Dest: pdfArray([pdfRef(targetPage.pageNum, 0), ...destinationViewArray(destination.target)])
221
+ });
222
+ }
180
223
  function isLinkItem(item) {
181
224
  return item.kind === "link";
182
225
  }
226
+ function isInternalLinkItem(item) {
227
+ return item.kind === "internalLink";
228
+ }
183
229
  const NOTES_ANNOTATION_HIDDEN_FLAG = 2;
184
230
  function buildNotesAnnotDict(notes) {
185
231
  return pdfDict({
@@ -435,7 +481,7 @@ function writePdf(doc, options = {}) {
435
481
  num: contentsNum,
436
482
  value: pdfStream(contentsDict, finalContentBytes)
437
483
  });
438
- const annots = page.items.filter(isLinkItem).map((link) => buildLinkAnnotDict(link));
484
+ const annots = [...page.items.filter(isLinkItem).map((link) => buildLinkAnnotDict(link)), ...page.items.filter(isInternalLinkItem).map((link) => buildInternalLinkAnnotDict(link, doc, pageAllocs))];
439
485
  if (page.notes !== void 0 && page.notes.length > 0) annots.push(buildNotesAnnotDict(page.notes));
440
486
  const pageEntries = /* @__PURE__ */ new Map([
441
487
  ["Type", pdfName("Page")],
package/dist/xmp.cjs ADDED
@@ -0,0 +1,46 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/xmp.ts
3
+ function readXmpMetadata(packet) {
4
+ return {
5
+ ...field(xmpValue(packet, "dc:title"), "title"),
6
+ ...field(xmpValue(packet, "dc:creator"), "author"),
7
+ ...field(xmpValue(packet, "dc:description"), "subject"),
8
+ ...field(xmpValue(packet, "xmp:CreatorTool"), "creator"),
9
+ ...field(xmpValue(packet, "pdf:Producer"), "producer"),
10
+ ...field(xmpValue(packet, "xmp:CreateDate"), "createdIso"),
11
+ ...field(xmpValue(packet, "xmp:ModifyDate"), "modifiedIso"),
12
+ ...keywordsOf(packet)
13
+ };
14
+ }
15
+ function xmpValue(packet, element) {
16
+ const match = new RegExp(`<${element}(?:\\s[^>]*)?>([\\s\\S]*?)</${element}>`).exec(packet);
17
+ if (match === null) return;
18
+ const inner = match[1] ?? "";
19
+ const items = listItems(inner);
20
+ if (items.length > 0) return items;
21
+ const text = inner.trim();
22
+ return text.length > 0 ? text : void 0;
23
+ }
24
+ function listItems(inner) {
25
+ const items = [];
26
+ const pattern = /<rdf:li(?:\s[^>]*)?>([\s\S]*?)<\/rdf:li>/g;
27
+ let match;
28
+ while ((match = pattern.exec(inner)) !== null) {
29
+ const text = (match[1] ?? "").trim();
30
+ if (text.length > 0) items.push(text);
31
+ }
32
+ return items;
33
+ }
34
+ function field(value, key) {
35
+ if (typeof value === "string") return { [key]: value };
36
+ if (Array.isArray(value) && value.length > 0) return { [key]: value.join(", ") };
37
+ return {};
38
+ }
39
+ function keywordsOf(packet) {
40
+ const match = (/* @__PURE__ */ new RegExp("<dc:subject(?:\\s[^>]*)?>([\\s\\S]*?)</dc:subject>")).exec(packet);
41
+ if (match === null) return {};
42
+ const items = listItems(match[1] ?? "");
43
+ return items.length > 0 ? { keywords: items } : {};
44
+ }
45
+ //#endregion
46
+ exports.readXmpMetadata = readXmpMetadata;
package/dist/xmp.d.cts ADDED
@@ -0,0 +1,14 @@
1
+ //#region src/xmp.d.ts
2
+ interface XmpMetadata {
3
+ readonly title?: string;
4
+ readonly author?: string;
5
+ readonly subject?: string;
6
+ readonly keywords?: string[];
7
+ readonly creator?: string;
8
+ readonly producer?: string;
9
+ readonly createdIso?: string;
10
+ readonly modifiedIso?: string;
11
+ }
12
+ declare function readXmpMetadata(packet: string): XmpMetadata;
13
+ //#endregion
14
+ export { XmpMetadata, readXmpMetadata };
package/dist/xmp.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ //#region src/xmp.d.ts
2
+ interface XmpMetadata {
3
+ readonly title?: string;
4
+ readonly author?: string;
5
+ readonly subject?: string;
6
+ readonly keywords?: string[];
7
+ readonly creator?: string;
8
+ readonly producer?: string;
9
+ readonly createdIso?: string;
10
+ readonly modifiedIso?: string;
11
+ }
12
+ declare function readXmpMetadata(packet: string): XmpMetadata;
13
+ //#endregion
14
+ export { XmpMetadata, readXmpMetadata };
package/dist/xmp.js ADDED
@@ -0,0 +1,45 @@
1
+ //#region src/xmp.ts
2
+ function readXmpMetadata(packet) {
3
+ return {
4
+ ...field(xmpValue(packet, "dc:title"), "title"),
5
+ ...field(xmpValue(packet, "dc:creator"), "author"),
6
+ ...field(xmpValue(packet, "dc:description"), "subject"),
7
+ ...field(xmpValue(packet, "xmp:CreatorTool"), "creator"),
8
+ ...field(xmpValue(packet, "pdf:Producer"), "producer"),
9
+ ...field(xmpValue(packet, "xmp:CreateDate"), "createdIso"),
10
+ ...field(xmpValue(packet, "xmp:ModifyDate"), "modifiedIso"),
11
+ ...keywordsOf(packet)
12
+ };
13
+ }
14
+ function xmpValue(packet, element) {
15
+ const match = new RegExp(`<${element}(?:\\s[^>]*)?>([\\s\\S]*?)</${element}>`).exec(packet);
16
+ if (match === null) return;
17
+ const inner = match[1] ?? "";
18
+ const items = listItems(inner);
19
+ if (items.length > 0) return items;
20
+ const text = inner.trim();
21
+ return text.length > 0 ? text : void 0;
22
+ }
23
+ function listItems(inner) {
24
+ const items = [];
25
+ const pattern = /<rdf:li(?:\s[^>]*)?>([\s\S]*?)<\/rdf:li>/g;
26
+ let match;
27
+ while ((match = pattern.exec(inner)) !== null) {
28
+ const text = (match[1] ?? "").trim();
29
+ if (text.length > 0) items.push(text);
30
+ }
31
+ return items;
32
+ }
33
+ function field(value, key) {
34
+ if (typeof value === "string") return { [key]: value };
35
+ if (Array.isArray(value) && value.length > 0) return { [key]: value.join(", ") };
36
+ return {};
37
+ }
38
+ function keywordsOf(packet) {
39
+ const match = (/* @__PURE__ */ new RegExp("<dc:subject(?:\\s[^>]*)?>([\\s\\S]*?)</dc:subject>")).exec(packet);
40
+ if (match === null) return {};
41
+ const items = listItems(match[1] ?? "");
42
+ return items.length > 0 ? { keywords: items } : {};
43
+ }
44
+ //#endregion
45
+ export { readXmpMetadata };
package/dist/xref.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_bytes_reader = require("./bytes/reader.cjs");
3
- const require_lexer = require("./lexer.cjs");
4
2
  const require_objects = require("./objects.cjs");
3
+ const require_bytes_reader = require("./bytes/reader.cjs");
5
4
  const require_filters = require("./filters.cjs");
5
+ const require_lexer = require("./lexer.cjs");
6
6
  const require_parse = require("./parse.cjs");
7
7
  //#region src/xref.ts
8
8
  const STARTXREF_BYTES = new TextEncoder().encode("startxref");
package/dist/xref.js CHANGED
@@ -1,7 +1,7 @@
1
- import { ByteReader, isAsciiWhitespace } from "./bytes/reader.js";
2
- import { nextToken } from "./lexer.js";
3
1
  import { asArray, asDict, asNumber, dictGet, isName, pdfDict, pdfRef } from "./objects.js";
2
+ import { ByteReader, isAsciiWhitespace } from "./bytes/reader.js";
4
3
  import { decodeStream } from "./filters.js";
4
+ import { nextToken } from "./lexer.js";
5
5
  import { parseIndirectObject, parseValue } from "./parse.js";
6
6
  //#region src/xref.ts
7
7
  const STARTXREF_BYTES = new TextEncoder().encode("startxref");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pdf-codec",
3
- "version": "3.1.4",
3
+ "version": "3.3.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": {
@@ -98,7 +98,7 @@
98
98
  "packageManager": "pnpm@11.6.0",
99
99
  "dependencies": {
100
100
  "byte-codec": "^1.1.13",
101
- "document-schema.js": "^4.7.0",
101
+ "document-schema.js": "^4.8.0",
102
102
  "fflate": "^0.8.3",
103
103
  "zod": "^4.4.3"
104
104
  },