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/dist/read.js CHANGED
@@ -1,17 +1,25 @@
1
+ import "./notes-annotation-author.js";
2
+ import { asArray, asName, asNumber, dictGet, pdfArray, pdfNull } from "./objects.js";
3
+ import { decodePdfString, parsePdfDate } from "./pdf-text.js";
4
+ import { concatBytes } from "./bytes/writer.js";
5
+ import { serializeObjectToText } from "./serialize.js";
6
+ import { applyMatrix, matrixRotationDegrees, matrixScaleX, matrixScaleY, multiplyMatrices, translationMatrix } from "./matrix.js";
7
+ import { readPageAnnotations } from "./annotations.js";
8
+ import { decodeStream } from "./filters.js";
1
9
  import { bytesToBase64 } from "./util/base64.js";
10
+ import { readAttachments } from "./attachments.js";
2
11
  import { crc32 } from "./bytes/crc32.js";
3
- import { concatBytes } from "./bytes/writer.js";
12
+ import { readAcroForm } from "./form.js";
13
+ import { readXmpMetadata } from "./xmp.js";
14
+ import { readOptionalContent } from "./optional-content.js";
4
15
  import "./layout.js";
5
16
  import { NOOP_DIAGNOSTIC_SINK, PdfParseError } from "./diagnostics.js";
6
- import { asArray, asName, asNumber, dictGet } from "./objects.js";
7
- import { decodeStream } from "./filters.js";
8
17
  import { openPdfDocument } from "./document.js";
9
18
  import { throwIfAborted } from "./util/abort.js";
10
19
  import { createFontResolver } from "./font-read.js";
11
20
  import { readImageXObject } from "./images-read.js";
12
- import { applyMatrix, matrixRotationDegrees, matrixScaleX, matrixScaleY, multiplyMatrices, translationMatrix } from "./matrix.js";
13
21
  import { interpretContentStream } from "./interpret.js";
14
- import "./notes-annotation-author.js";
22
+ import { createDestinationRegistry, readOutline } from "./navigation.js";
15
23
  //#region src/read.ts
16
24
  const PDF_HEADER_BYTES = new TextEncoder().encode("%PDF-");
17
25
  const HEADER_SEARCH_WINDOW = 1024;
@@ -37,15 +45,28 @@ function readPdf(bytes, options) {
37
45
  });
38
46
  const images = {};
39
47
  const imageIdCache = /* @__PURE__ */ new Map();
40
- const pages = doc.pages().map((pageDict) => {
48
+ const pageDicts = doc.pages();
49
+ const destinationRegistry = createDestinationRegistry(doc.catalog, doc, (obj) => doc.pageIndex(obj), sink);
50
+ const outline = readOutline(doc.catalog, destinationRegistry, doc, sink);
51
+ const attachments = readAttachments(doc.catalog, pageDicts, doc, sink);
52
+ const optionalContent = readOptionalContent(doc.catalog, doc, sink);
53
+ const form = readAcroForm(doc.catalog, doc, (obj) => doc.pageIndex(obj), sink);
54
+ const source = readDocumentResidue(doc, sink);
55
+ const pages = pageDicts.map((pageDict) => {
41
56
  throwIfAborted(signal);
42
- return readPage(pageDict, doc, fontResolver, images, imageIdCache, sink);
57
+ return readPage(pageDict, doc, fontResolver, images, imageIdCache, destinationRegistry, optionalContent.layerNameOf, sink);
43
58
  });
44
59
  return {
45
60
  formatVersion: 1,
46
- metadata: readMetadata(doc.trailer, doc),
61
+ metadata: readMetadata(doc.trailer, doc, doc.catalog, sink),
47
62
  pages,
48
- images
63
+ images,
64
+ ...destinationRegistry.entries.length > 0 ? { destinations: [...destinationRegistry.entries] } : {},
65
+ ...outline.length > 0 ? { outline } : {},
66
+ ...attachments.length > 0 ? { attachments } : {},
67
+ ...optionalContent.layers.length > 0 ? { layers: [...optionalContent.layers] } : {},
68
+ ...form.length > 0 ? { form } : {},
69
+ ...Object.keys(source).length > 0 ? { source } : {}
49
70
  };
50
71
  }
51
72
  function readMediaBox(page) {
@@ -135,7 +156,7 @@ function readPageContentBytes(page, resolver, sink) {
135
156
  }
136
157
  return /* @__PURE__ */ new Uint8Array(0);
137
158
  }
138
- function readPage(page, resolver, fontResolver, images, imageIdCache, sink) {
159
+ function readPage(page, resolver, fontResolver, images, imageIdCache, destinationRegistry, layerNameOf, sink) {
139
160
  const resources = resolver.resolveDict(dictGet(page, "Resources"));
140
161
  const mediaBox = readMediaBox(page);
141
162
  const rotationResult = pageRotationTransform(normalizeRotation(asNumber(dictGet(page, "Rotate"))), mediaBox.urx - mediaBox.llx, mediaBox.ury - mediaBox.lly);
@@ -146,7 +167,8 @@ function readPage(page, resolver, fontResolver, images, imageIdCache, sink) {
146
167
  const extracted = interpretContentStream(contentBytes, resources, {
147
168
  fontMetrics: fontResolver.metrics,
148
169
  resolver,
149
- sink
170
+ sink,
171
+ layerNameOf
150
172
  });
151
173
  for (const item of extracted) {
152
174
  const converted = convertExtractedItem(item, pageMatrix, fontResolver, images, imageIdCache, resolver, sink);
@@ -157,13 +179,15 @@ function readPage(page, resolver, fontResolver, images, imageIdCache, sink) {
157
179
  severity: "warning",
158
180
  message: "page has no /Resources dict; its content stream cannot be interpreted"
159
181
  });
160
- items.push(...readLinkAnnotations(page, pageMatrix, resolver));
182
+ items.push(...readLinkAnnotations(page, pageMatrix, resolver, destinationRegistry));
161
183
  const notes = readPageNotes(page, resolver);
184
+ const annotations = readPageAnnotations(page, pageMatrix, resolver, sink);
162
185
  return {
163
186
  widthPt: rotationResult.widthPt,
164
187
  heightPt: rotationResult.heightPt,
165
188
  items,
166
- ...notes !== void 0 ? { notes } : {}
189
+ ...notes !== void 0 ? { notes } : {},
190
+ ...annotations.length > 0 ? { annotations } : {}
167
191
  };
168
192
  }
169
193
  function convertExtractedItem(item, pageMatrix, fontResolver, images, imageIdCache, resolver, sink) {
@@ -198,7 +222,10 @@ function convertText(item, pageMatrix, fontResolver) {
198
222
  sizePt: sizePt > 0 ? sizePt : item.sizePt,
199
223
  color: item.color,
200
224
  widthPt,
201
- rotationDeg: rotationDeg !== 0 ? rotationDeg : void 0
225
+ rotationDeg: rotationDeg !== 0 ? rotationDeg : void 0,
226
+ ...item.layerName !== void 0 ? { layer: item.layerName } : {},
227
+ ...item.actualText !== void 0 ? { actualText: item.actualText } : {},
228
+ ...item.alt !== void 0 ? { alt: item.alt } : {}
202
229
  };
203
230
  }
204
231
  function paintFields(paint) {
@@ -227,14 +254,16 @@ function convertRect(item, pageMatrix) {
227
254
  return {
228
255
  kind: "rect",
229
256
  ...transformBox(item, pageMatrix),
230
- ...paintFields(item)
257
+ ...paintFields(item),
258
+ ...item.layerName !== void 0 ? { layer: item.layerName } : {}
231
259
  };
232
260
  }
233
261
  function convertEllipse(item, pageMatrix) {
234
262
  return {
235
263
  kind: "ellipse",
236
264
  ...transformBox(item, pageMatrix),
237
- ...paintFields(item)
265
+ ...paintFields(item),
266
+ ...item.layerName !== void 0 ? { layer: item.layerName } : {}
238
267
  };
239
268
  }
240
269
  function convertLine(item, pageMatrix) {
@@ -253,7 +282,8 @@ function convertLine(item, pageMatrix) {
253
282
  x2Pt: p2.x,
254
283
  y2Pt: p2.y,
255
284
  color: item.color,
256
- widthPt: item.widthPt
285
+ widthPt: item.widthPt,
286
+ ...item.layerName !== void 0 ? { layer: item.layerName } : {}
257
287
  };
258
288
  }
259
289
  function transformSubpath(subpath, pageMatrix) {
@@ -308,7 +338,8 @@ function convertPath(item, pageMatrix) {
308
338
  subpaths: item.subpaths.map((subpath) => transformSubpath(subpath, pageMatrix)),
309
339
  ...item.fill !== void 0 ? { fill: item.fill } : {},
310
340
  ...item.fill !== void 0 && item.fillRule === "evenodd" ? { fillRule: "evenodd" } : {},
311
- ...item.stroke !== void 0 ? { stroke: item.stroke } : {}
341
+ ...item.stroke !== void 0 ? { stroke: item.stroke } : {},
342
+ ...item.layerName !== void 0 ? { layer: item.layerName } : {}
312
343
  };
313
344
  }
314
345
  function imagePlacementFrom(matrix) {
@@ -351,7 +382,8 @@ function convertImage(item, pageMatrix, images, cache, resolver, sink) {
351
382
  return {
352
383
  kind: "image",
353
384
  imageId,
354
- ...imagePlacementFrom(multiplyMatrices(item.matrix, pageMatrix))
385
+ ...imagePlacementFrom(multiplyMatrices(item.matrix, pageMatrix)),
386
+ ...item.layerName !== void 0 ? { layer: item.layerName } : {}
355
387
  };
356
388
  }
357
389
  function convertInlineImage(item, pageMatrix, images, resolver, sink) {
@@ -360,19 +392,22 @@ function convertInlineImage(item, pageMatrix, images, resolver, sink) {
360
392
  return {
361
393
  kind: "image",
362
394
  imageId: registerExtractedImage(decoded.format, decoded.bytes, decoded.widthPx, decoded.heightPx, images),
363
- ...imagePlacementFrom(multiplyMatrices(item.matrix, pageMatrix))
395
+ ...imagePlacementFrom(multiplyMatrices(item.matrix, pageMatrix)),
396
+ ...item.layerName !== void 0 ? { layer: item.layerName } : {}
364
397
  };
365
398
  }
366
- function readLinkAnnotations(page, pageMatrix, resolver) {
399
+ function readLinkAnnotations(page, pageMatrix, resolver, destinationRegistry) {
367
400
  const annotsArr = asArray(dictGet(page, "Annots"));
368
401
  if (annotsArr === void 0) return [];
369
402
  const links = [];
370
403
  for (const annotRef of annotsArr) {
371
404
  const annot = resolver.resolveDict(annotRef);
372
405
  if (annot === void 0 || asName(dictGet(annot, "Subtype")) !== "Link") continue;
373
- const uri = readLinkUri(annot, resolver);
374
406
  const rectArr = asArray(dictGet(annot, "Rect"));
375
- if (uri === void 0 || rectArr === void 0) continue;
407
+ if (rectArr === void 0) continue;
408
+ const uri = readLinkUri(annot, resolver);
409
+ const destination = uri === void 0 ? readInternalDestination(annot, resolver, destinationRegistry) : void 0;
410
+ if (uri === void 0 && destination === void 0) continue;
376
411
  const x1 = asNumber(rectArr[0]) ?? 0;
377
412
  const y1 = asNumber(rectArr[1]) ?? 0;
378
413
  const x2 = asNumber(rectArr[2]) ?? 0;
@@ -385,13 +420,24 @@ function readLinkAnnotations(page, pageMatrix, resolver) {
385
420
  x: Math.max(x1, x2),
386
421
  y: Math.max(y1, y2)
387
422
  });
388
- links.push({
389
- kind: "link",
390
- uri,
423
+ const contentsObj = dictGet(annot, "Contents");
424
+ const title = contentsObj?.kind === "string" ? decodePdfString(contentsObj.bytes) : void 0;
425
+ const box = {
391
426
  xPt: Math.min(p1.x, p2.x),
392
427
  yPt: Math.min(p1.y, p2.y),
393
428
  widthPt: Math.abs(p2.x - p1.x),
394
- heightPt: Math.abs(p2.y - p1.y)
429
+ heightPt: Math.abs(p2.y - p1.y),
430
+ ...title !== void 0 ? { title } : {}
431
+ };
432
+ if (uri !== void 0) links.push({
433
+ kind: "link",
434
+ uri,
435
+ ...box
436
+ });
437
+ else if (destination !== void 0) links.push({
438
+ kind: "internalLink",
439
+ destination,
440
+ ...box
395
441
  });
396
442
  }
397
443
  return links;
@@ -402,6 +448,12 @@ function readLinkUri(annot, resolver) {
402
448
  const uriObj = dictGet(action, "URI");
403
449
  return uriObj?.kind === "string" ? decodePdfString(uriObj.bytes) : void 0;
404
450
  }
451
+ function readInternalDestination(annot, resolver, destinationRegistry) {
452
+ const dest = dictGet(annot, "Dest");
453
+ if (dest !== void 0) return destinationRegistry.intern(dest);
454
+ const action = resolver.resolveDict(dictGet(annot, "A"));
455
+ if (action !== void 0 && asName(dictGet(action, "S")) === "GoTo") return destinationRegistry.intern(dictGet(action, "D"));
456
+ }
405
457
  function readPageNotes(page, resolver) {
406
458
  const annotsArr = asArray(dictGet(page, "Annots"));
407
459
  if (annotsArr === void 0) return;
@@ -414,23 +466,7 @@ function readPageNotes(page, resolver) {
414
466
  if (contentsObj?.kind === "string") return decodePdfString(contentsObj.bytes);
415
467
  }
416
468
  }
417
- function decodePdfString(bytes) {
418
- if (bytes.length >= 2 && bytes[0] === 254 && bytes[1] === 255) {
419
- let out = "";
420
- for (let i = 2; i + 1 < bytes.length; i += 2) out += String.fromCharCode((bytes[i] ?? 0) << 8 | (bytes[i + 1] ?? 0));
421
- return out;
422
- }
423
- return Array.from(bytes, (b) => String.fromCharCode(b)).join("");
424
- }
425
- const PDF_DATE_PATTERN = /^D:(\d{4})(\d{2})?(\d{2})?(\d{2})?(\d{2})?(\d{2})?([+\-Z])?(\d{2})?'?(\d{2})?'?$/;
426
- function parsePdfDate(raw) {
427
- if (raw === void 0) return;
428
- const match = PDF_DATE_PATTERN.exec(raw);
429
- if (match === null) return;
430
- const [, year, month = "01", day = "01", hour = "00", minute = "00", second = "00", tzSign, tzHour = "00", tzMinute = "00"] = match;
431
- return `${year}-${month}-${day}T${hour}:${minute}:${second}${tzSign === void 0 || tzSign === "Z" ? "Z" : `${tzSign}${tzHour}:${tzMinute}`}`;
432
- }
433
- function readMetadata(trailer, resolver) {
469
+ function readMetadata(trailer, resolver, catalog, sink) {
434
470
  const info = resolver.resolveDict(dictGet(trailer, "Info"));
435
471
  if (info === void 0) return {};
436
472
  const stringField = (key) => {
@@ -438,16 +474,52 @@ function readMetadata(trailer, resolver) {
438
474
  return obj?.kind === "string" ? decodePdfString(obj.bytes) : void 0;
439
475
  };
440
476
  const keywords = stringField("Keywords")?.split(",").map((k) => k.trim()).filter((k) => k.length > 0);
477
+ const langObj = dictGet(catalog, "Lang");
478
+ const xmp = xmpPacket(catalog, resolver, sink);
479
+ const mirrored = readXmpMetadata(xmp ?? "");
441
480
  return {
442
- title: stringField("Title"),
443
- author: stringField("Author"),
444
- subject: stringField("Subject"),
445
- keywords: keywords !== void 0 && keywords.length > 0 ? keywords : void 0,
446
- creator: stringField("Creator"),
447
- producer: stringField("Producer"),
448
- createdIso: parsePdfDate(stringField("CreationDate")),
449
- modifiedIso: parsePdfDate(stringField("ModDate"))
481
+ title: stringField("Title") ?? mirrored.title,
482
+ author: stringField("Author") ?? mirrored.author,
483
+ subject: stringField("Subject") ?? mirrored.subject,
484
+ keywords: (keywords !== void 0 && keywords.length > 0 ? keywords : void 0) ?? mirrored.keywords,
485
+ creator: stringField("Creator") ?? mirrored.creator,
486
+ producer: stringField("Producer") ?? mirrored.producer,
487
+ createdIso: parsePdfDate(stringField("CreationDate")) ?? mirrored.createdIso,
488
+ modifiedIso: parsePdfDate(stringField("ModDate")) ?? mirrored.modifiedIso,
489
+ ...langObj?.kind === "string" ? { language: decodePdfString(langObj.bytes) } : {}
490
+ };
491
+ }
492
+ function xmpPacket(catalog, resolver, sink) {
493
+ const metadataObj = resolver.resolve(dictGet(catalog, "Metadata"));
494
+ if (metadataObj?.kind !== "stream") return;
495
+ const decoded = decodeStream(metadataObj.raw, metadataObj.dict, sink);
496
+ return new TextDecoder().decode(decoded.bytes);
497
+ }
498
+ function readDocumentResidue(doc, sink) {
499
+ const source = {};
500
+ const residue = (key, obj) => {
501
+ if (obj !== void 0) source[key] = {
502
+ format: "pdf",
503
+ xml: serializeObjectToText(obj)
504
+ };
505
+ };
506
+ const catalog = doc.catalog;
507
+ const packet = xmpPacket(catalog, doc, sink);
508
+ if (packet !== void 0) source.xmp = {
509
+ format: "pdf",
510
+ xml: packet
450
511
  };
512
+ residue("viewer-preferences", dictGet(catalog, "ViewerPreferences"));
513
+ residue("page-mode", dictGet(catalog, "PageMode"));
514
+ residue("page-layout", dictGet(catalog, "PageLayout"));
515
+ residue("open-action", dictGet(catalog, "OpenAction"));
516
+ const outputIntents = asArray(dictGet(catalog, "OutputIntents"));
517
+ if (outputIntents !== void 0) residue("output-intents", pdfArray(outputIntents.map((intent) => doc.resolve(intent) ?? pdfNull())));
518
+ residue("piece-info", dictGet(catalog, "PieceInfo"));
519
+ residue("legal", dictGet(catalog, "Legal"));
520
+ residue("collection", dictGet(catalog, "Collection"));
521
+ residue("trailer-id", dictGet(doc.trailer, "ID"));
522
+ return source;
451
523
  }
452
524
  //#endregion
453
- export { decodePdfString, normalizeRotation, pageRotationTransform, parsePdfDate, readPdf };
525
+ export { normalizeRotation, pageRotationTransform, readPdf };
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_bytes_writer = require("./bytes/writer.cjs");
3
2
  const require_objects = require("./objects.cjs");
3
+ const require_bytes_writer = require("./bytes/writer.cjs");
4
4
  //#region src/serialize.ts
5
5
  const NUMBER_DECIMAL_PLACES = 4;
6
6
  const NUMBER_EPSILON = 10 ** -4;
@@ -63,7 +63,11 @@ function serializeObject(obj) {
63
63
  writeObject(writer, obj);
64
64
  return writer.toBytes();
65
65
  }
66
+ function serializeObjectToText(obj) {
67
+ return new TextDecoder().decode(serializeObject(obj));
68
+ }
66
69
  //#endregion
67
70
  exports.formatNumber = formatNumber;
68
71
  exports.serializeObject = serializeObject;
72
+ exports.serializeObjectToText = serializeObjectToText;
69
73
  exports.writeObject = writeObject;
@@ -1,8 +1,9 @@
1
- import { ByteWriter } from "./bytes/writer.cjs";
2
1
  import { PdfObject } from "./objects.cjs";
2
+ import { ByteWriter } from "./bytes/writer.cjs";
3
3
  //#region src/serialize.d.ts
4
4
  declare function formatNumber(n: number): string;
5
5
  declare function writeObject(writer: ByteWriter, obj: PdfObject): void;
6
6
  declare function serializeObject(obj: PdfObject): Uint8Array<ArrayBuffer>;
7
+ declare function serializeObjectToText(obj: PdfObject): string;
7
8
  //#endregion
8
- export { formatNumber, serializeObject, writeObject };
9
+ export { formatNumber, serializeObject, serializeObjectToText, writeObject };
@@ -1,8 +1,9 @@
1
- import { ByteWriter } from "./bytes/writer.js";
2
1
  import { PdfObject } from "./objects.js";
2
+ import { ByteWriter } from "./bytes/writer.js";
3
3
  //#region src/serialize.d.ts
4
4
  declare function formatNumber(n: number): string;
5
5
  declare function writeObject(writer: ByteWriter, obj: PdfObject): void;
6
6
  declare function serializeObject(obj: PdfObject): Uint8Array<ArrayBuffer>;
7
+ declare function serializeObjectToText(obj: PdfObject): string;
7
8
  //#endregion
8
- export { formatNumber, serializeObject, writeObject };
9
+ export { formatNumber, serializeObject, serializeObjectToText, writeObject };
package/dist/serialize.js CHANGED
@@ -1,5 +1,5 @@
1
- import { ByteWriter } from "./bytes/writer.js";
2
1
  import { pdfNum } from "./objects.js";
2
+ import { ByteWriter } from "./bytes/writer.js";
3
3
  //#region src/serialize.ts
4
4
  const NUMBER_DECIMAL_PLACES = 4;
5
5
  const NUMBER_EPSILON = 10 ** -4;
@@ -62,5 +62,8 @@ function serializeObject(obj) {
62
62
  writeObject(writer, obj);
63
63
  return writer.toBytes();
64
64
  }
65
+ function serializeObjectToText(obj) {
66
+ return new TextDecoder().decode(serializeObject(obj));
67
+ }
65
68
  //#endregion
66
- export { formatNumber, serializeObject, writeObject };
69
+ export { formatNumber, serializeObject, serializeObjectToText, writeObject };
package/dist/write.cjs CHANGED
@@ -1,15 +1,15 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_afm_widths = require("./afm-widths-BoeTOK2r.cjs");
3
- const require_util_base64 = require("./util/base64.cjs");
4
- const require_bytes_writer = require("./bytes/writer.cjs");
3
+ const require_notes_annotation_author = require("./notes-annotation-author.cjs");
5
4
  const require_objects = require("./objects.cjs");
5
+ const require_bytes_writer = require("./bytes/writer.cjs");
6
+ const require_serialize = require("./serialize.cjs");
6
7
  const require_bytes_flate = require("./bytes/flate.cjs");
8
+ const require_util_base64 = require("./util/base64.cjs");
7
9
  const require_util_abort = require("./util/abort.cjs");
8
10
  const require_image_jpeg_info = require("./image/jpeg-info.cjs");
9
- const require_notes_annotation_author = require("./notes-annotation-author.cjs");
10
11
  const require_image_png_decode = require("./image/png-decode.cjs");
11
12
  const require_embedded_font = require("./embedded-font.cjs");
12
- const require_serialize = require("./serialize.cjs");
13
13
  const require_content_write = require("./content-write.cjs");
14
14
  const require_embedded_font_write = require("./embedded-font-write.cjs");
15
15
  const require_font_registry = require("./font-registry.cjs");
@@ -178,9 +178,55 @@ function buildLinkAnnotDict(link) {
178
178
  })
179
179
  });
180
180
  }
181
+ function destinationViewArray(target) {
182
+ const n = (value) => value === void 0 ? require_objects.pdfNull() : require_objects.pdfNum(value);
183
+ if (target.kind === "xyz") return [
184
+ require_objects.pdfName("XYZ"),
185
+ n(target.leftPt),
186
+ n(target.topPt),
187
+ n(target.zoom)
188
+ ];
189
+ if (target.kind === "fitH") return [require_objects.pdfName("FitH"), n(target.topPt)];
190
+ if (target.kind === "fitV") return [require_objects.pdfName("FitV"), n(target.leftPt)];
191
+ if (target.kind === "fitR") return [
192
+ require_objects.pdfName("FitR"),
193
+ n(target.leftPt),
194
+ n(target.bottomPt),
195
+ n(target.rightPt),
196
+ n(target.topPt)
197
+ ];
198
+ if (target.kind === "fitBH") return [require_objects.pdfName("FitBH"), n(target.topPt)];
199
+ if (target.kind === "fitBV") return [require_objects.pdfName("FitBV"), n(target.leftPt)];
200
+ return [require_objects.pdfName(target.kind === "fitB" ? "FitB" : "Fit")];
201
+ }
202
+ function buildInternalLinkAnnotDict(link, doc, pageAllocs) {
203
+ const destination = doc.destinations?.find((d) => d.name === link.destination);
204
+ 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`);
205
+ const targetPage = pageAllocs[destination.pageIndex];
206
+ 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`);
207
+ return require_objects.pdfDict({
208
+ Type: require_objects.pdfName("Annot"),
209
+ Subtype: require_objects.pdfName("Link"),
210
+ Rect: require_objects.pdfArray([
211
+ link.xPt,
212
+ link.yPt,
213
+ link.xPt + link.widthPt,
214
+ link.yPt + link.heightPt
215
+ ].map((v) => require_objects.pdfNum(v))),
216
+ Border: require_objects.pdfArray([
217
+ 0,
218
+ 0,
219
+ 0
220
+ ].map((v) => require_objects.pdfNum(v))),
221
+ Dest: require_objects.pdfArray([require_objects.pdfRef(targetPage.pageNum, 0), ...destinationViewArray(destination.target)])
222
+ });
223
+ }
181
224
  function isLinkItem(item) {
182
225
  return item.kind === "link";
183
226
  }
227
+ function isInternalLinkItem(item) {
228
+ return item.kind === "internalLink";
229
+ }
184
230
  const NOTES_ANNOTATION_HIDDEN_FLAG = 2;
185
231
  function buildNotesAnnotDict(notes) {
186
232
  return require_objects.pdfDict({
@@ -436,7 +482,7 @@ function writePdf(doc, options = {}) {
436
482
  num: contentsNum,
437
483
  value: require_objects.pdfStream(contentsDict, finalContentBytes)
438
484
  });
439
- const annots = page.items.filter(isLinkItem).map((link) => buildLinkAnnotDict(link));
485
+ const annots = [...page.items.filter(isLinkItem).map((link) => buildLinkAnnotDict(link)), ...page.items.filter(isInternalLinkItem).map((link) => buildInternalLinkAnnotDict(link, doc, pageAllocs))];
440
486
  if (page.notes !== void 0 && page.notes.length > 0) annots.push(buildNotesAnnotDict(page.notes));
441
487
  const pageEntries = /* @__PURE__ */ new Map([
442
488
  ["Type", require_objects.pdfName("Page")],
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 };