pdf-codec 4.0.13 → 4.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 (66) hide show
  1. package/README.md +6 -5
  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/content-read.d.cts +1 -1
  7. package/dist/content-read.d.ts +1 -1
  8. package/dist/crypto/random.cjs +9 -0
  9. package/dist/crypto/random.d.cts +4 -0
  10. package/dist/crypto/random.d.ts +4 -0
  11. package/dist/crypto/random.js +8 -0
  12. package/dist/document.d.cts +1 -1
  13. package/dist/document.d.ts +1 -1
  14. package/dist/embedded-font-write.d.cts +1 -1
  15. package/dist/embedded-font-write.d.ts +1 -1
  16. package/dist/encrypt-write.cjs +234 -0
  17. package/dist/encrypt-write.d.cts +31 -0
  18. package/dist/encrypt-write.d.ts +31 -0
  19. package/dist/encrypt-write.js +231 -0
  20. package/dist/encrypt.cjs +50 -20
  21. package/dist/encrypt.d.cts +18 -2
  22. package/dist/encrypt.d.ts +18 -2
  23. package/dist/encrypt.js +35 -21
  24. package/dist/filters.d.cts +1 -1
  25. package/dist/filters.d.ts +1 -1
  26. package/dist/font-read.d.cts +1 -1
  27. package/dist/font-read.d.ts +1 -1
  28. package/dist/form.d.cts +1 -1
  29. package/dist/form.d.ts +1 -1
  30. package/dist/images-read.d.cts +1 -1
  31. package/dist/images-read.d.ts +1 -1
  32. package/dist/index.cjs +2 -0
  33. package/dist/index.d.cts +2 -1
  34. package/dist/index.d.ts +2 -1
  35. package/dist/index.js +2 -1
  36. package/dist/interpret.d.cts +1 -1
  37. package/dist/interpret.d.ts +1 -1
  38. package/dist/math-font-write.d.cts +1 -1
  39. package/dist/math-font-write.d.ts +1 -1
  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/objects-B7HdMeWS.d.cts +59 -0
  45. package/dist/objects-B7HdMeWS.d.ts +59 -0
  46. package/dist/objects.d.cts +1 -58
  47. package/dist/objects.d.ts +1 -58
  48. package/dist/optional-content.d.cts +1 -1
  49. package/dist/optional-content.d.ts +1 -1
  50. package/dist/parse.d.cts +1 -1
  51. package/dist/parse.d.ts +1 -1
  52. package/dist/predictors.d.cts +1 -1
  53. package/dist/predictors.d.ts +1 -1
  54. package/dist/serialize.d.cts +1 -1
  55. package/dist/serialize.d.ts +1 -1
  56. package/dist/structure.d.cts +1 -1
  57. package/dist/structure.d.ts +1 -1
  58. package/dist/tounicode.d.cts +1 -1
  59. package/dist/tounicode.d.ts +1 -1
  60. package/dist/write.cjs +123 -15
  61. package/dist/write.d.cts +2 -0
  62. package/dist/write.d.ts +2 -0
  63. package/dist/write.js +124 -16
  64. package/dist/xref.d.cts +1 -1
  65. package/dist/xref.d.ts +1 -1
  66. package/package.json +1 -1
package/dist/write.cjs CHANGED
@@ -7,6 +7,8 @@ const require_serialize = require("./serialize.cjs");
7
7
  const require_bytes_flate = require("./bytes/flate.cjs");
8
8
  const require_util_base64 = require("./util/base64.cjs");
9
9
  const require_util_abort = require("./util/abort.cjs");
10
+ const require_crypto_random = require("./crypto/random.cjs");
11
+ const require_encrypt_write = require("./encrypt-write.cjs");
10
12
  const require_image_jpeg_info = require("./image/jpeg-info.cjs");
11
13
  const require_image_png_decode = require("./image/png-decode.cjs");
12
14
  const require_embedded_font = require("./embedded-font.cjs");
@@ -30,6 +32,7 @@ const FLAG_SERIF = 2;
30
32
  const FLAG_NONSYMBOLIC = 32;
31
33
  const FLAG_ITALIC = 64;
32
34
  const FLAG_FORCE_BOLD = 262144;
35
+ const FILE_ID_BYTES = 16;
33
36
  function textToPdfString(text) {
34
37
  const bytes = new Uint8Array(2 + text.length * 2);
35
38
  bytes[0] = 254;
@@ -199,11 +202,14 @@ function destinationViewArray(target) {
199
202
  if (target.kind === "fitBV") return [require_objects.pdfName("FitBV"), n(target.leftPt)];
200
203
  return [require_objects.pdfName(target.kind === "fitB" ? "FitB" : "Fit")];
201
204
  }
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
+ function resolveDestinationArray(doc, pageAllocs, name, what) {
206
+ const destination = doc.destinations?.find((d) => d.name === name);
207
+ if (destination === void 0) throw new Error(`${what} names destination "${name}", which the document's destinations table does not carry -- this is a caller-invariant violation`);
205
208
  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`);
209
+ if (targetPage === void 0) throw new Error(`destination "${destination.name}" names page index ${destination.pageIndex}, which is beyond the document's own pages -- this is a caller-invariant violation`);
210
+ return [require_objects.pdfRef(targetPage.pageNum, 0), ...destinationViewArray(destination.target)];
211
+ }
212
+ function buildInternalLinkAnnotDict(link, doc, pageAllocs) {
207
213
  return require_objects.pdfDict({
208
214
  Type: require_objects.pdfName("Annot"),
209
215
  Subtype: require_objects.pdfName("Link"),
@@ -218,7 +224,7 @@ function buildInternalLinkAnnotDict(link, doc, pageAllocs) {
218
224
  0,
219
225
  0
220
226
  ].map((v) => require_objects.pdfNum(v))),
221
- Dest: require_objects.pdfArray([require_objects.pdfRef(targetPage.pageNum, 0), ...destinationViewArray(destination.target)])
227
+ Dest: require_objects.pdfArray(resolveDestinationArray(doc, pageAllocs, link.destination, "internal link"))
222
228
  });
223
229
  }
224
230
  function isLinkItem(item) {
@@ -318,13 +324,29 @@ function writePdf(doc, options = {}) {
318
324
  pageNum: nextObjNum++,
319
325
  contentsNum: nextObjNum++
320
326
  }));
327
+ const encryptDictNum = options.encryption === void 0 ? void 0 : nextObjNum++;
328
+ const attachmentAllocs = (doc.attachments ?? []).map(() => ({
329
+ fileNum: nextObjNum++,
330
+ specNum: nextObjNum++
331
+ }));
332
+ const attachmentsNamesNum = attachmentAllocs.length > 0 ? nextObjNum++ : void 0;
333
+ const outlineRootNum = (doc.outline ?? []).length > 0 ? nextObjNum++ : void 0;
334
+ const outlineItemNums = [];
335
+ if (outlineRootNum !== void 0) {
336
+ const countItems = (items) => {
337
+ let n = 0;
338
+ for (const item of items) n += 1 + countItems(item.children);
339
+ return n;
340
+ };
341
+ for (let i = 0; i < countItems(doc.outline ?? []); i += 1) outlineItemNums.push(nextObjNum++);
342
+ }
321
343
  const objects = [];
344
+ const catalogEntries = [["Type", require_objects.pdfName("Catalog")], ["Pages", require_objects.pdfRef(pagesNum, 0)]];
345
+ if (attachmentsNamesNum !== void 0) catalogEntries.push(["Names", require_objects.pdfRef(attachmentsNamesNum, 0)]);
346
+ if (outlineRootNum !== void 0) catalogEntries.push(["Outlines", require_objects.pdfRef(outlineRootNum, 0)]);
322
347
  objects.push({
323
348
  num: catalogNum,
324
- value: require_objects.pdfDict({
325
- Type: require_objects.pdfName("Catalog"),
326
- Pages: require_objects.pdfRef(pagesNum, 0)
327
- })
349
+ value: require_objects.pdfDict(Object.fromEntries(catalogEntries))
328
350
  });
329
351
  objects.push({
330
352
  num: pagesNum,
@@ -338,6 +360,73 @@ function writePdf(doc, options = {}) {
338
360
  num: infoNum,
339
361
  value: buildInfoDict(doc)
340
362
  });
363
+ for (const [index, attachment] of (doc.attachments ?? []).entries()) {
364
+ const alloc = attachmentAllocs[index];
365
+ const fileEntries = [["Type", require_objects.pdfName("EmbeddedFile")]];
366
+ if (attachment.mimeType !== void 0) fileEntries.push(["Subtype", require_objects.pdfName(attachment.mimeType)]);
367
+ objects.push({
368
+ num: alloc.fileNum,
369
+ value: require_objects.pdfStream(require_objects.pdfDict(Object.fromEntries(fileEntries)), require_util_base64.base64ToBytes(attachment.base64))
370
+ });
371
+ const specEntries = [
372
+ ["Type", require_objects.pdfName("Filespec")],
373
+ ["F", require_objects.pdfLiteralString(new TextEncoder().encode(attachment.name))],
374
+ ["EF", require_objects.pdfDict({ F: require_objects.pdfRef(alloc.fileNum, 0) })]
375
+ ];
376
+ if (attachment.description !== void 0) specEntries.push(["Desc", require_objects.pdfLiteralString(new TextEncoder().encode(attachment.description))]);
377
+ objects.push({
378
+ num: alloc.specNum,
379
+ value: require_objects.pdfDict(Object.fromEntries(specEntries))
380
+ });
381
+ }
382
+ if (attachmentsNamesNum !== void 0) {
383
+ const names = [];
384
+ for (const [index, attachment] of (doc.attachments ?? []).entries()) {
385
+ names.push(require_objects.pdfLiteralString(new TextEncoder().encode(attachment.name)));
386
+ names.push(require_objects.pdfRef(attachmentAllocs[index].specNum, 0));
387
+ }
388
+ objects.push({
389
+ num: attachmentsNamesNum,
390
+ value: require_objects.pdfDict({ EmbeddedFiles: require_objects.pdfDict({ Names: require_objects.pdfArray(names) }) })
391
+ });
392
+ }
393
+ if (outlineRootNum !== void 0 && (doc.outline ?? []).length > 0) {
394
+ let itemCursor = 0;
395
+ const emitItems = (items, parentNum) => {
396
+ const siblingNums = [];
397
+ let prevNum;
398
+ for (const item of items) {
399
+ const ownNum = outlineItemNums[itemCursor];
400
+ itemCursor += 1;
401
+ siblingNums.push(ownNum);
402
+ const childNums = emitItems(item.children, ownNum);
403
+ const entries = [["Title", require_objects.pdfLiteralString(new TextEncoder().encode(item.title))], ["Parent", require_objects.pdfRef(parentNum, 0)]];
404
+ if (item.destination !== void 0) entries.push(["Dest", require_objects.pdfArray(resolveDestinationArray(doc, pageAllocs, item.destination, `outline item "${item.title}"`))]);
405
+ if (prevNum !== void 0) entries.push(["Prev", require_objects.pdfRef(prevNum, 0)]);
406
+ if (siblingNums.length < items.length) entries.push(["Next", require_objects.pdfRef(outlineItemNums[itemCursor], 0)]);
407
+ if (childNums.length > 0) {
408
+ entries.push(["First", require_objects.pdfRef(childNums[0], 0)]);
409
+ entries.push(["Last", require_objects.pdfRef(childNums[childNums.length - 1], 0)]);
410
+ entries.push(["Count", require_objects.pdfNum(childNums.length)]);
411
+ }
412
+ objects.push({
413
+ num: ownNum,
414
+ value: require_objects.pdfDict(Object.fromEntries(entries))
415
+ });
416
+ prevNum = ownNum;
417
+ }
418
+ return siblingNums;
419
+ };
420
+ const topLevelNums = emitItems(doc.outline ?? [], outlineRootNum);
421
+ objects.push({
422
+ num: outlineRootNum,
423
+ value: require_objects.pdfDict({
424
+ Type: require_objects.pdfName("Outlines"),
425
+ First: require_objects.pdfRef(topLevelNums[0], 0),
426
+ Last: require_objects.pdfRef(topLevelNums[topLevelNums.length - 1], 0)
427
+ })
428
+ });
429
+ }
341
430
  for (const [standardName, alloc] of fontAllocs) {
342
431
  const { font, descriptor } = buildFontObjects(standardName, require_objects.pdfRef(alloc.descNum, 0));
343
432
  objects.push({
@@ -502,10 +591,24 @@ function writePdf(doc, options = {}) {
502
591
  value: require_objects.pdfDict(pageEntries)
503
592
  });
504
593
  });
594
+ let fileId;
595
+ let encryptedObjects = objects;
596
+ if (options.encryption !== void 0 && encryptDictNum !== void 0) {
597
+ fileId = require_crypto_random.randomBytes(FILE_ID_BYTES);
598
+ const encryptor = require_encrypt_write.createStandardEncryptor(options.encryption, fileId);
599
+ encryptedObjects = objects.map(({ num, value }) => ({
600
+ num,
601
+ value: require_encrypt_write.encryptIndirectObject(value, num, 0, encryptor)
602
+ }));
603
+ encryptedObjects.push({
604
+ num: encryptDictNum,
605
+ value: encryptor.encryptDict
606
+ });
607
+ }
505
608
  const writer = new require_bytes_writer.ByteWriter();
506
609
  writer.writeAscii("%PDF-1.7\n");
507
610
  const offsets = /* @__PURE__ */ new Map();
508
- for (const { num, value } of objects) {
611
+ for (const { num, value } of encryptedObjects) {
509
612
  offsets.set(num, writer.length);
510
613
  writer.writeAscii(`${num} 0 obj\n`);
511
614
  require_serialize.writeObject(writer, value);
@@ -521,12 +624,17 @@ function writePdf(doc, options = {}) {
521
624
  if (offset === void 0) throw new Error(`object ${num} was allocated but never written -- this is a writePdf internal invariant violation`);
522
625
  writer.writeAscii(xrefEntry(offset, 0, true));
523
626
  }
627
+ const trailerEntries = /* @__PURE__ */ new Map([
628
+ ["Size", require_objects.pdfNum(maxObjNum + 1)],
629
+ ["Root", require_objects.pdfRef(catalogNum, 0)],
630
+ ["Info", require_objects.pdfRef(infoNum, 0)]
631
+ ]);
632
+ if (fileId !== void 0 && encryptDictNum !== void 0) {
633
+ trailerEntries.set("ID", require_objects.pdfArray([require_objects.pdfHexString(fileId), require_objects.pdfHexString(fileId)]));
634
+ trailerEntries.set("Encrypt", require_objects.pdfRef(encryptDictNum, 0));
635
+ }
524
636
  writer.writeAscii("trailer\n");
525
- require_serialize.writeObject(writer, require_objects.pdfDict({
526
- Size: require_objects.pdfNum(maxObjNum + 1),
527
- Root: require_objects.pdfRef(catalogNum, 0),
528
- Info: require_objects.pdfRef(infoNum, 0)
529
- }));
637
+ require_serialize.writeObject(writer, require_objects.pdfDict(trailerEntries));
530
638
  writer.writeAscii("\nstartxref\n");
531
639
  writer.writeAscii(`${xrefOffset}\n`);
532
640
  writer.writeAscii("%%EOF");
package/dist/write.d.cts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { LayoutDocument } from "./layout.cjs";
2
2
  import { r as EmbeddedFaceSubstitution } from "./embedded-font-BU6Jgcof.cjs";
3
3
  import { WinAnsiSubstitution } from "./winansi.cjs";
4
+ import { PdfEncryptionOptions } from "./encrypt-write.cjs";
4
5
  import { FontRegistry } from "./font-registry.cjs";
5
6
  import { PositionedFormula } from "document-schema.js";
6
7
  //#region src/write.d.ts
@@ -15,6 +16,7 @@ interface WritePdfOptions {
15
16
  }) => void;
16
17
  readonly fonts?: FontRegistry;
17
18
  readonly formulas?: readonly PositionedFormula[];
19
+ readonly encryption?: PdfEncryptionOptions;
18
20
  }
19
21
  declare function writePdf(doc: LayoutDocument, options?: WritePdfOptions): Uint8Array<ArrayBuffer>;
20
22
  //#endregion
package/dist/write.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { LayoutDocument } from "./layout.js";
2
2
  import { r as EmbeddedFaceSubstitution } from "./embedded-font-fREdbxnr.js";
3
3
  import { WinAnsiSubstitution } from "./winansi.js";
4
+ import { PdfEncryptionOptions } from "./encrypt-write.js";
4
5
  import { FontRegistry } from "./font-registry.js";
5
6
  import { PositionedFormula } from "document-schema.js";
6
7
  //#region src/write.d.ts
@@ -15,6 +16,7 @@ interface WritePdfOptions {
15
16
  }) => void;
16
17
  readonly fonts?: FontRegistry;
17
18
  readonly formulas?: readonly PositionedFormula[];
19
+ readonly encryption?: PdfEncryptionOptions;
18
20
  }
19
21
  declare function writePdf(doc: LayoutDocument, options?: WritePdfOptions): Uint8Array<ArrayBuffer>;
20
22
  //#endregion
package/dist/write.js CHANGED
@@ -1,11 +1,13 @@
1
1
  import { n as widthOfCode, p as winAnsiGlyphName, t as STANDARD_METRICS } from "./afm-widths-De_QMWgC.js";
2
2
  import { NOTES_ANNOTATION_AUTHOR } from "./notes-annotation-author.js";
3
- import { pdfArray, pdfDict, pdfHexString, pdfName, pdfNull, pdfNum, pdfRef, pdfStream } from "./objects.js";
3
+ import { pdfArray, pdfDict, pdfHexString, pdfLiteralString, pdfName, pdfNull, pdfNum, pdfRef, pdfStream } from "./objects.js";
4
4
  import { ByteWriter, concatBytes } from "./bytes/writer.js";
5
5
  import { writeObject } from "./serialize.js";
6
6
  import { deflate } from "./bytes/flate.js";
7
7
  import { base64ToBytes } from "./util/base64.js";
8
8
  import { throwIfAborted } from "./util/abort.js";
9
+ import { randomBytes } from "./crypto/random.js";
10
+ import { createStandardEncryptor, encryptIndirectObject } from "./encrypt-write.js";
9
11
  import { readJpegInfo } from "./image/jpeg-info.js";
10
12
  import { decodePng } from "./image/png-decode.js";
11
13
  import { collectEmbeddedGlyphs } from "./embedded-font.js";
@@ -29,6 +31,7 @@ const FLAG_SERIF = 2;
29
31
  const FLAG_NONSYMBOLIC = 32;
30
32
  const FLAG_ITALIC = 64;
31
33
  const FLAG_FORCE_BOLD = 262144;
34
+ const FILE_ID_BYTES = 16;
32
35
  function textToPdfString(text) {
33
36
  const bytes = new Uint8Array(2 + text.length * 2);
34
37
  bytes[0] = 254;
@@ -198,11 +201,14 @@ function destinationViewArray(target) {
198
201
  if (target.kind === "fitBV") return [pdfName("FitBV"), n(target.leftPt)];
199
202
  return [pdfName(target.kind === "fitB" ? "FitB" : "Fit")];
200
203
  }
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
+ function resolveDestinationArray(doc, pageAllocs, name, what) {
205
+ const destination = doc.destinations?.find((d) => d.name === name);
206
+ if (destination === void 0) throw new Error(`${what} names destination "${name}", which the document's destinations table does not carry -- this is a caller-invariant violation`);
204
207
  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`);
208
+ if (targetPage === void 0) throw new Error(`destination "${destination.name}" names page index ${destination.pageIndex}, which is beyond the document's own pages -- this is a caller-invariant violation`);
209
+ return [pdfRef(targetPage.pageNum, 0), ...destinationViewArray(destination.target)];
210
+ }
211
+ function buildInternalLinkAnnotDict(link, doc, pageAllocs) {
206
212
  return pdfDict({
207
213
  Type: pdfName("Annot"),
208
214
  Subtype: pdfName("Link"),
@@ -217,7 +223,7 @@ function buildInternalLinkAnnotDict(link, doc, pageAllocs) {
217
223
  0,
218
224
  0
219
225
  ].map((v) => pdfNum(v))),
220
- Dest: pdfArray([pdfRef(targetPage.pageNum, 0), ...destinationViewArray(destination.target)])
226
+ Dest: pdfArray(resolveDestinationArray(doc, pageAllocs, link.destination, "internal link"))
221
227
  });
222
228
  }
223
229
  function isLinkItem(item) {
@@ -317,13 +323,29 @@ function writePdf(doc, options = {}) {
317
323
  pageNum: nextObjNum++,
318
324
  contentsNum: nextObjNum++
319
325
  }));
326
+ const encryptDictNum = options.encryption === void 0 ? void 0 : nextObjNum++;
327
+ const attachmentAllocs = (doc.attachments ?? []).map(() => ({
328
+ fileNum: nextObjNum++,
329
+ specNum: nextObjNum++
330
+ }));
331
+ const attachmentsNamesNum = attachmentAllocs.length > 0 ? nextObjNum++ : void 0;
332
+ const outlineRootNum = (doc.outline ?? []).length > 0 ? nextObjNum++ : void 0;
333
+ const outlineItemNums = [];
334
+ if (outlineRootNum !== void 0) {
335
+ const countItems = (items) => {
336
+ let n = 0;
337
+ for (const item of items) n += 1 + countItems(item.children);
338
+ return n;
339
+ };
340
+ for (let i = 0; i < countItems(doc.outline ?? []); i += 1) outlineItemNums.push(nextObjNum++);
341
+ }
320
342
  const objects = [];
343
+ const catalogEntries = [["Type", pdfName("Catalog")], ["Pages", pdfRef(pagesNum, 0)]];
344
+ if (attachmentsNamesNum !== void 0) catalogEntries.push(["Names", pdfRef(attachmentsNamesNum, 0)]);
345
+ if (outlineRootNum !== void 0) catalogEntries.push(["Outlines", pdfRef(outlineRootNum, 0)]);
321
346
  objects.push({
322
347
  num: catalogNum,
323
- value: pdfDict({
324
- Type: pdfName("Catalog"),
325
- Pages: pdfRef(pagesNum, 0)
326
- })
348
+ value: pdfDict(Object.fromEntries(catalogEntries))
327
349
  });
328
350
  objects.push({
329
351
  num: pagesNum,
@@ -337,6 +359,73 @@ function writePdf(doc, options = {}) {
337
359
  num: infoNum,
338
360
  value: buildInfoDict(doc)
339
361
  });
362
+ for (const [index, attachment] of (doc.attachments ?? []).entries()) {
363
+ const alloc = attachmentAllocs[index];
364
+ const fileEntries = [["Type", pdfName("EmbeddedFile")]];
365
+ if (attachment.mimeType !== void 0) fileEntries.push(["Subtype", pdfName(attachment.mimeType)]);
366
+ objects.push({
367
+ num: alloc.fileNum,
368
+ value: pdfStream(pdfDict(Object.fromEntries(fileEntries)), base64ToBytes(attachment.base64))
369
+ });
370
+ const specEntries = [
371
+ ["Type", pdfName("Filespec")],
372
+ ["F", pdfLiteralString(new TextEncoder().encode(attachment.name))],
373
+ ["EF", pdfDict({ F: pdfRef(alloc.fileNum, 0) })]
374
+ ];
375
+ if (attachment.description !== void 0) specEntries.push(["Desc", pdfLiteralString(new TextEncoder().encode(attachment.description))]);
376
+ objects.push({
377
+ num: alloc.specNum,
378
+ value: pdfDict(Object.fromEntries(specEntries))
379
+ });
380
+ }
381
+ if (attachmentsNamesNum !== void 0) {
382
+ const names = [];
383
+ for (const [index, attachment] of (doc.attachments ?? []).entries()) {
384
+ names.push(pdfLiteralString(new TextEncoder().encode(attachment.name)));
385
+ names.push(pdfRef(attachmentAllocs[index].specNum, 0));
386
+ }
387
+ objects.push({
388
+ num: attachmentsNamesNum,
389
+ value: pdfDict({ EmbeddedFiles: pdfDict({ Names: pdfArray(names) }) })
390
+ });
391
+ }
392
+ if (outlineRootNum !== void 0 && (doc.outline ?? []).length > 0) {
393
+ let itemCursor = 0;
394
+ const emitItems = (items, parentNum) => {
395
+ const siblingNums = [];
396
+ let prevNum;
397
+ for (const item of items) {
398
+ const ownNum = outlineItemNums[itemCursor];
399
+ itemCursor += 1;
400
+ siblingNums.push(ownNum);
401
+ const childNums = emitItems(item.children, ownNum);
402
+ const entries = [["Title", pdfLiteralString(new TextEncoder().encode(item.title))], ["Parent", pdfRef(parentNum, 0)]];
403
+ if (item.destination !== void 0) entries.push(["Dest", pdfArray(resolveDestinationArray(doc, pageAllocs, item.destination, `outline item "${item.title}"`))]);
404
+ if (prevNum !== void 0) entries.push(["Prev", pdfRef(prevNum, 0)]);
405
+ if (siblingNums.length < items.length) entries.push(["Next", pdfRef(outlineItemNums[itemCursor], 0)]);
406
+ if (childNums.length > 0) {
407
+ entries.push(["First", pdfRef(childNums[0], 0)]);
408
+ entries.push(["Last", pdfRef(childNums[childNums.length - 1], 0)]);
409
+ entries.push(["Count", pdfNum(childNums.length)]);
410
+ }
411
+ objects.push({
412
+ num: ownNum,
413
+ value: pdfDict(Object.fromEntries(entries))
414
+ });
415
+ prevNum = ownNum;
416
+ }
417
+ return siblingNums;
418
+ };
419
+ const topLevelNums = emitItems(doc.outline ?? [], outlineRootNum);
420
+ objects.push({
421
+ num: outlineRootNum,
422
+ value: pdfDict({
423
+ Type: pdfName("Outlines"),
424
+ First: pdfRef(topLevelNums[0], 0),
425
+ Last: pdfRef(topLevelNums[topLevelNums.length - 1], 0)
426
+ })
427
+ });
428
+ }
340
429
  for (const [standardName, alloc] of fontAllocs) {
341
430
  const { font, descriptor } = buildFontObjects(standardName, pdfRef(alloc.descNum, 0));
342
431
  objects.push({
@@ -501,10 +590,24 @@ function writePdf(doc, options = {}) {
501
590
  value: pdfDict(pageEntries)
502
591
  });
503
592
  });
593
+ let fileId;
594
+ let encryptedObjects = objects;
595
+ if (options.encryption !== void 0 && encryptDictNum !== void 0) {
596
+ fileId = randomBytes(FILE_ID_BYTES);
597
+ const encryptor = createStandardEncryptor(options.encryption, fileId);
598
+ encryptedObjects = objects.map(({ num, value }) => ({
599
+ num,
600
+ value: encryptIndirectObject(value, num, 0, encryptor)
601
+ }));
602
+ encryptedObjects.push({
603
+ num: encryptDictNum,
604
+ value: encryptor.encryptDict
605
+ });
606
+ }
504
607
  const writer = new ByteWriter();
505
608
  writer.writeAscii("%PDF-1.7\n");
506
609
  const offsets = /* @__PURE__ */ new Map();
507
- for (const { num, value } of objects) {
610
+ for (const { num, value } of encryptedObjects) {
508
611
  offsets.set(num, writer.length);
509
612
  writer.writeAscii(`${num} 0 obj\n`);
510
613
  writeObject(writer, value);
@@ -520,12 +623,17 @@ function writePdf(doc, options = {}) {
520
623
  if (offset === void 0) throw new Error(`object ${num} was allocated but never written -- this is a writePdf internal invariant violation`);
521
624
  writer.writeAscii(xrefEntry(offset, 0, true));
522
625
  }
626
+ const trailerEntries = /* @__PURE__ */ new Map([
627
+ ["Size", pdfNum(maxObjNum + 1)],
628
+ ["Root", pdfRef(catalogNum, 0)],
629
+ ["Info", pdfRef(infoNum, 0)]
630
+ ]);
631
+ if (fileId !== void 0 && encryptDictNum !== void 0) {
632
+ trailerEntries.set("ID", pdfArray([pdfHexString(fileId), pdfHexString(fileId)]));
633
+ trailerEntries.set("Encrypt", pdfRef(encryptDictNum, 0));
634
+ }
523
635
  writer.writeAscii("trailer\n");
524
- writeObject(writer, pdfDict({
525
- Size: pdfNum(maxObjNum + 1),
526
- Root: pdfRef(catalogNum, 0),
527
- Info: pdfRef(infoNum, 0)
528
- }));
636
+ writeObject(writer, pdfDict(trailerEntries));
529
637
  writer.writeAscii("\nstartxref\n");
530
638
  writer.writeAscii(`${xrefOffset}\n`);
531
639
  writer.writeAscii("%%EOF");
package/dist/xref.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { PdfDiagnosticSink } from "./diagnostics.cjs";
2
- import { PdfDict } from "./objects.cjs";
2
+ import { n as PdfDict } from "./objects-B7HdMeWS.cjs";
3
3
  //#region src/xref.d.ts
4
4
  interface XrefOffsetEntry {
5
5
  readonly type: "offset";
package/dist/xref.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { PdfDiagnosticSink } from "./diagnostics.js";
2
- import { PdfDict } from "./objects.js";
2
+ import { n as PdfDict } from "./objects-B7HdMeWS.js";
3
3
  //#region src/xref.d.ts
4
4
  interface XrefOffsetEntry {
5
5
  readonly type: "offset";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pdf-codec",
3
- "version": "4.0.13",
3
+ "version": "4.2.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": {