pdf-parser.js 3.0.7 → 4.5.1

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 (240) hide show
  1. package/README.md +105 -54
  2. package/dist/{afm-widths-Dxucrg7D.js → afm-widths-BTu0IDp2.cjs} +1517 -4
  3. package/dist/{afm-widths-DyDq56Ph.d.cts → afm-widths-CneRyu5l.d.cts} +1 -1
  4. package/dist/{afm-widths-DyDq56Ph.d.ts → afm-widths-CneRyu5l.d.ts} +1 -1
  5. package/dist/{afm-widths-BoeTOK2r.cjs → afm-widths-De_QMWgC.js} +1416 -51
  6. package/dist/afm-widths.cjs +1 -1
  7. package/dist/afm-widths.d.cts +1 -1
  8. package/dist/afm-widths.d.ts +1 -1
  9. package/dist/afm-widths.js +1 -1
  10. package/dist/annotations.cjs +104 -0
  11. package/dist/annotations.d.cts +9 -0
  12. package/dist/annotations.d.ts +9 -0
  13. package/dist/annotations.js +103 -0
  14. package/dist/attachments.cjs +65 -0
  15. package/dist/attachments.d.cts +8 -0
  16. package/dist/attachments.d.ts +8 -0
  17. package/dist/attachments.js +64 -0
  18. package/dist/builtin-encoding.cjs +221 -0
  19. package/dist/builtin-encoding.d.cts +8 -0
  20. package/dist/builtin-encoding.d.ts +8 -0
  21. package/dist/builtin-encoding.js +220 -0
  22. package/dist/bytes/flate.cjs +1 -1
  23. package/dist/bytes/flate.js +1 -1
  24. package/dist/cff.cjs +14 -0
  25. package/dist/cff.d.cts +4 -1
  26. package/dist/cff.d.ts +4 -1
  27. package/dist/cff.js +12 -1
  28. package/dist/cmap-table.cjs +76 -13
  29. package/dist/cmap-table.d.cts +9 -1
  30. package/dist/cmap-table.d.ts +9 -1
  31. package/dist/cmap-table.js +77 -15
  32. package/dist/codec.cjs +1 -1
  33. package/dist/codec.d.cts +134 -0
  34. package/dist/codec.d.ts +134 -0
  35. package/dist/codec.js +1 -1
  36. package/dist/content-read.cjs +1 -1
  37. package/dist/content-read.d.cts +3 -3
  38. package/dist/content-read.d.ts +3 -3
  39. package/dist/content-read.js +1 -1
  40. package/dist/content-write.cjs +32 -10
  41. package/dist/content-write.d.cts +12 -5
  42. package/dist/content-write.d.ts +12 -5
  43. package/dist/content-write.js +32 -10
  44. package/dist/crypto/random.cjs +9 -0
  45. package/dist/crypto/random.d.cts +4 -0
  46. package/dist/crypto/random.d.ts +4 -0
  47. package/dist/crypto/random.js +8 -0
  48. package/dist/diagnostics.d.cts +1 -1
  49. package/dist/diagnostics.d.ts +1 -1
  50. package/dist/document.cjs +12 -3
  51. package/dist/document.d.cts +3 -1
  52. package/dist/document.d.ts +3 -1
  53. package/dist/document.js +12 -3
  54. package/dist/{embedded-font-fREdbxnr.d.ts → embedded-font-B4p9a3O3.d.ts} +3 -1
  55. package/dist/{embedded-font-BU6Jgcof.d.cts → embedded-font-oc65VGlr.d.cts} +3 -1
  56. package/dist/embedded-font-write.cjs +1 -1
  57. package/dist/embedded-font-write.d.cts +3 -3
  58. package/dist/embedded-font-write.d.ts +3 -3
  59. package/dist/embedded-font-write.js +1 -1
  60. package/dist/embedded-font.cjs +46 -15
  61. package/dist/embedded-font.d.cts +1 -1
  62. package/dist/embedded-font.d.ts +1 -1
  63. package/dist/embedded-font.js +46 -15
  64. package/dist/encoding.cjs +10 -1
  65. package/dist/encoding.d.cts +10 -1
  66. package/dist/encoding.d.ts +10 -1
  67. package/dist/encoding.js +2 -2
  68. package/dist/encrypt-write.cjs +234 -0
  69. package/dist/encrypt-write.d.cts +31 -0
  70. package/dist/encrypt-write.d.ts +31 -0
  71. package/dist/encrypt-write.js +231 -0
  72. package/dist/encrypt.cjs +51 -21
  73. package/dist/encrypt.d.cts +19 -3
  74. package/dist/encrypt.d.ts +19 -3
  75. package/dist/encrypt.js +36 -22
  76. package/dist/filters.cjs +18 -14
  77. package/dist/filters.d.cts +1 -1
  78. package/dist/filters.d.ts +1 -1
  79. package/dist/filters.js +18 -14
  80. package/dist/font-read.cjs +68 -13
  81. package/dist/font-read.d.cts +1 -1
  82. package/dist/font-read.d.ts +1 -1
  83. package/dist/font-read.js +69 -14
  84. package/dist/font-registry.cjs +1 -1
  85. package/dist/font-registry.d.cts +4 -4
  86. package/dist/font-registry.d.ts +4 -4
  87. package/dist/font-registry.js +1 -1
  88. package/dist/font-substitutes.d.cts +1 -1
  89. package/dist/font-substitutes.d.ts +1 -1
  90. package/dist/font-tables.cjs +31 -0
  91. package/dist/font-tables.d.cts +3 -1
  92. package/dist/font-tables.d.ts +3 -1
  93. package/dist/font-tables.js +31 -2
  94. package/dist/fonts.d.cts +2 -2
  95. package/dist/fonts.d.ts +2 -2
  96. package/dist/form.cjs +138 -0
  97. package/dist/form.d.cts +9 -0
  98. package/dist/form.d.ts +9 -0
  99. package/dist/form.js +137 -0
  100. package/dist/gsub-table-BRTQ8EUW.d.ts +10 -0
  101. package/dist/gsub-table-URczLd85.d.cts +10 -0
  102. package/dist/gsub-table.cjs +225 -0
  103. package/dist/gsub-table.d.cts +2 -0
  104. package/dist/gsub-table.d.ts +2 -0
  105. package/dist/gsub-table.js +224 -0
  106. package/dist/image/ccitt-encode.cjs +119 -0
  107. package/dist/image/ccitt-encode.d.cts +9 -0
  108. package/dist/image/ccitt-encode.d.ts +9 -0
  109. package/dist/image/ccitt-encode.js +118 -0
  110. package/dist/image/ccitt.cjs +3 -0
  111. package/dist/image/ccitt.d.cts +4 -1
  112. package/dist/image/ccitt.d.ts +4 -1
  113. package/dist/image/ccitt.js +1 -1
  114. package/dist/image/jbig2-bitmap.d.cts +1 -1
  115. package/dist/image/jbig2-bitmap.d.ts +1 -1
  116. package/dist/image/jbig2.cjs +3 -1
  117. package/dist/image/jbig2.js +3 -1
  118. package/dist/image/jp2-boxes.d.cts +1 -1
  119. package/dist/image/jp2-boxes.d.ts +1 -1
  120. package/dist/image/jpeg2000-codestream.d.cts +3 -3
  121. package/dist/image/jpeg2000-codestream.d.ts +3 -3
  122. package/dist/image/jpeg2000-t1.d.cts +1 -1
  123. package/dist/image/jpeg2000-t1.d.ts +1 -1
  124. package/dist/image/jpeg2000.cjs +1 -0
  125. package/dist/image/jpeg2000.d.cts +3 -0
  126. package/dist/image/jpeg2000.d.ts +3 -0
  127. package/dist/image/jpeg2000.js +1 -0
  128. package/dist/image/png-decode.cjs +1 -1
  129. package/dist/image/png-decode.js +1 -1
  130. package/dist/image/png-filter.d.cts +1 -1
  131. package/dist/image/png-filter.d.ts +1 -1
  132. package/dist/images-read.cjs +18 -12
  133. package/dist/images-read.d.cts +2 -2
  134. package/dist/images-read.d.ts +2 -2
  135. package/dist/images-read.js +15 -9
  136. package/dist/index.cjs +17 -4
  137. package/dist/index.d.cts +7 -6
  138. package/dist/index.d.ts +7 -6
  139. package/dist/index.js +6 -5
  140. package/dist/interpret.cjs +178 -56
  141. package/dist/interpret.d.cts +27 -12
  142. package/dist/interpret.d.ts +27 -12
  143. package/dist/interpret.js +178 -56
  144. package/dist/layout.cjs +149 -2
  145. package/dist/layout.d.cts +394 -1
  146. package/dist/layout.d.ts +394 -1
  147. package/dist/layout.js +140 -4
  148. package/dist/lexer.d.cts +9 -9
  149. package/dist/lexer.d.ts +9 -9
  150. package/dist/math-content-write.cjs +1 -1
  151. package/dist/math-content-write.js +1 -1
  152. package/dist/math-font-write.cjs +1 -1
  153. package/dist/math-font-write.d.cts +1 -1
  154. package/dist/math-font-write.d.ts +1 -1
  155. package/dist/math-font-write.js +1 -1
  156. package/dist/math-font.cjs +2 -2
  157. package/dist/math-font.d.cts +1 -1
  158. package/dist/math-font.d.ts +1 -1
  159. package/dist/math-font.js +2 -2
  160. package/dist/{math-stretch-a_rHNRvc.d.ts → math-stretch-BbC2o4Br.d.ts} +1 -1
  161. package/dist/{math-stretch-E26z6byT.d.cts → math-stretch-C2JanDAz.d.cts} +1 -1
  162. package/dist/math-stretch.d.cts +1 -1
  163. package/dist/math-stretch.d.ts +1 -1
  164. package/dist/measure.cjs +1 -1
  165. package/dist/measure.d.cts +1 -1
  166. package/dist/measure.d.ts +1 -1
  167. package/dist/measure.js +1 -1
  168. package/dist/names.cjs +41 -0
  169. package/dist/names.d.cts +11 -0
  170. package/dist/names.d.ts +11 -0
  171. package/dist/names.js +40 -0
  172. package/dist/navigation.cjs +210 -0
  173. package/dist/navigation.d.cts +18 -0
  174. package/dist/navigation.d.ts +18 -0
  175. package/dist/navigation.js +207 -0
  176. package/dist/notes-annotation-author.cjs +5 -0
  177. package/dist/notes-annotation-author.d.cts +4 -0
  178. package/dist/notes-annotation-author.d.ts +4 -0
  179. package/dist/notes-annotation-author.js +4 -0
  180. package/dist/objects-B7HdMeWS.d.cts +59 -0
  181. package/dist/objects-B7HdMeWS.d.ts +59 -0
  182. package/dist/objects.d.cts +1 -58
  183. package/dist/objects.d.ts +1 -58
  184. package/dist/optional-content.cjs +63 -0
  185. package/dist/optional-content.d.cts +12 -0
  186. package/dist/optional-content.d.ts +12 -0
  187. package/dist/optional-content.js +62 -0
  188. package/dist/parse.cjs +1 -1
  189. package/dist/parse.d.cts +2 -2
  190. package/dist/parse.d.ts +2 -2
  191. package/dist/parse.js +1 -1
  192. package/dist/pdf-text.cjs +21 -0
  193. package/dist/pdf-text.d.cts +5 -0
  194. package/dist/pdf-text.d.ts +5 -0
  195. package/dist/pdf-text.js +19 -0
  196. package/dist/predictors.d.cts +1 -1
  197. package/dist/predictors.d.ts +1 -1
  198. package/dist/read.cjs +287 -72
  199. package/dist/read.d.cts +2 -4
  200. package/dist/read.d.ts +2 -4
  201. package/dist/read.js +284 -67
  202. package/dist/serialize.cjs +5 -1
  203. package/dist/serialize.d.cts +3 -2
  204. package/dist/serialize.d.ts +3 -2
  205. package/dist/serialize.js +5 -2
  206. package/dist/sfnt-subset.cjs +10 -3
  207. package/dist/sfnt-subset.d.cts +1 -1
  208. package/dist/sfnt-subset.d.ts +1 -1
  209. package/dist/sfnt-subset.js +10 -3
  210. package/dist/structure.cjs +184 -0
  211. package/dist/structure.d.cts +12 -0
  212. package/dist/structure.d.ts +12 -0
  213. package/dist/structure.js +183 -0
  214. package/dist/tounicode.cjs +8 -3
  215. package/dist/tounicode.d.cts +2 -2
  216. package/dist/tounicode.d.ts +2 -2
  217. package/dist/tounicode.js +8 -3
  218. package/dist/util/base64.cjs +4 -4
  219. package/dist/util/base64.js +4 -4
  220. package/dist/winansi.cjs +1 -1
  221. package/dist/winansi.d.cts +1 -1
  222. package/dist/winansi.d.ts +1 -1
  223. package/dist/winansi.js +1 -1
  224. package/dist/write.cjs +464 -21
  225. package/dist/write.d.cts +4 -3
  226. package/dist/write.d.ts +4 -3
  227. package/dist/write.js +464 -20
  228. package/dist/xmp.cjs +46 -0
  229. package/dist/xmp.d.cts +14 -0
  230. package/dist/xmp.d.ts +14 -0
  231. package/dist/xmp.js +45 -0
  232. package/dist/xref.cjs +2 -2
  233. package/dist/xref.d.cts +3 -3
  234. package/dist/xref.d.ts +3 -3
  235. package/dist/xref.js +2 -2
  236. package/package.json +46 -31
  237. package/dist/image/png-encode.cjs +0 -64
  238. package/dist/image/png-encode.d.cts +0 -8
  239. package/dist/image/png-encode.d.ts +0 -8
  240. package/dist/image/png-encode.js +0 -63
package/dist/read.js CHANGED
@@ -1,17 +1,26 @@
1
+ import "./notes-annotation-author.js";
2
+ import { asArray, asName, asNumber, dictGet, pdfArray, pdfDict, pdfNull, pdfNum } 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, rotatePointAboutCenter, 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";
15
+ import { readStructure } from "./structure.js";
4
16
  import "./layout.js";
5
17
  import { NOOP_DIAGNOSTIC_SINK, PdfParseError } from "./diagnostics.js";
6
- import { asArray, asName, asNumber, dictGet } from "./objects.js";
7
- import { decodeStream } from "./filters.js";
8
18
  import { openPdfDocument } from "./document.js";
9
19
  import { throwIfAborted } from "./util/abort.js";
10
20
  import { createFontResolver } from "./font-read.js";
11
21
  import { readImageXObject } from "./images-read.js";
12
- import { applyMatrix, matrixRotationDegrees, matrixScaleX, matrixScaleY, multiplyMatrices, translationMatrix } from "./matrix.js";
13
22
  import { interpretContentStream } from "./interpret.js";
14
- import "./write.js";
23
+ import { createDestinationRegistry, readOutline } from "./navigation.js";
15
24
  //#region src/read.ts
16
25
  const PDF_HEADER_BYTES = new TextEncoder().encode("%PDF-");
17
26
  const HEADER_SEARCH_WINDOW = 1024;
@@ -29,6 +38,7 @@ function readPdf(bytes, options) {
29
38
  const sink = options?.sink ?? NOOP_DIAGNOSTIC_SINK;
30
39
  const signal = options?.signal;
31
40
  if (!hasPdfHeader(bytes)) throw new PdfParseError("pdf/no-header", "no \"%PDF-\" header found within the first bytes of the file; this does not look like a PDF at all");
41
+ throwIfAborted(signal);
32
42
  const doc = openPdfDocument(bytes, sink);
33
43
  const fontResolver = createFontResolver({
34
44
  resolver: doc,
@@ -36,29 +46,44 @@ function readPdf(bytes, options) {
36
46
  });
37
47
  const images = {};
38
48
  const imageIdCache = /* @__PURE__ */ new Map();
39
- const pages = doc.pages().map((pageDict) => {
49
+ const pageDicts = doc.pages();
50
+ const destinationRegistry = createDestinationRegistry(doc.catalog, doc, (obj) => doc.pageIndex(obj), sink);
51
+ const outline = readOutline(doc.catalog, destinationRegistry, doc, sink);
52
+ const attachments = readAttachments(doc.catalog, pageDicts, doc, sink);
53
+ const optionalContent = readOptionalContent(doc.catalog, doc, sink);
54
+ const form = readAcroForm(doc.catalog, doc, (obj) => doc.pageIndex(obj), sink);
55
+ const structure = readStructure(doc.catalog, pageDicts, doc, sink);
56
+ const source = readDocumentResidue(doc, sink);
57
+ const pageBoxRows = [];
58
+ const pages = pageDicts.map((pageDict, index) => {
40
59
  throwIfAborted(signal);
41
- return readPage(pageDict, doc, fontResolver, images, imageIdCache, sink);
60
+ return readPage(index, pageDict, doc, fontResolver, images, imageIdCache, destinationRegistry, optionalContent.layerNameOf, structure.ownerOf, pageBoxRows, sink);
42
61
  });
62
+ if (pageBoxRows.length > 0) source["page-boxes"] = {
63
+ format: "pdf",
64
+ xml: serializeObjectToText(pdfArray(pageBoxRows))
65
+ };
43
66
  return {
44
67
  formatVersion: 1,
45
- metadata: readMetadata(doc.trailer, doc),
68
+ metadata: readMetadata(doc.trailer, doc, doc.catalog, sink),
46
69
  pages,
47
- images
70
+ images,
71
+ ...destinationRegistry.entries.length > 0 ? { destinations: [...destinationRegistry.entries] } : {},
72
+ ...outline.length > 0 ? { outline } : {},
73
+ ...attachments.length > 0 ? { attachments } : {},
74
+ ...optionalContent.layers.length > 0 ? { layers: [...optionalContent.layers] } : {},
75
+ ...form.length > 0 ? { form } : {},
76
+ ...structure.tree.length > 0 ? { structure: [...structure.tree] } : {},
77
+ ...Object.keys(source).length > 0 ? { source } : {}
48
78
  };
49
79
  }
50
- function readMediaBox(page) {
51
- const arr = asArray(dictGet(page, "MediaBox"));
52
- if (arr === void 0) return {
53
- llx: 0,
54
- lly: 0,
55
- urx: DEFAULT_PAGE_WIDTH_PT,
56
- ury: DEFAULT_PAGE_HEIGHT_PT
57
- };
80
+ function readDeclaredPageBox(page, key) {
81
+ const arr = asArray(dictGet(page, key));
82
+ if (arr === void 0) return;
58
83
  const a = asNumber(arr[0]) ?? 0;
59
84
  const b = asNumber(arr[1]) ?? 0;
60
- const c = asNumber(arr[2]) ?? DEFAULT_PAGE_WIDTH_PT;
61
- const d = asNumber(arr[3]) ?? DEFAULT_PAGE_HEIGHT_PT;
85
+ const c = asNumber(arr[2]) ?? 0;
86
+ const d = asNumber(arr[3]) ?? 0;
62
87
  return {
63
88
  llx: Math.min(a, c),
64
89
  lly: Math.min(b, d),
@@ -66,6 +91,33 @@ function readMediaBox(page) {
66
91
  ury: Math.max(b, d)
67
92
  };
68
93
  }
94
+ function readMediaBox(page) {
95
+ return readDeclaredPageBox(page, "MediaBox") ?? {
96
+ llx: 0,
97
+ lly: 0,
98
+ urx: DEFAULT_PAGE_WIDTH_PT,
99
+ ury: DEFAULT_PAGE_HEIGHT_PT
100
+ };
101
+ }
102
+ function rotatedRectBounds(rect, matrix) {
103
+ const transformed = [
104
+ [rect.llx, rect.lly],
105
+ [rect.urx, rect.lly],
106
+ [rect.llx, rect.ury],
107
+ [rect.urx, rect.ury]
108
+ ].map(([x, y]) => applyMatrix(matrix, {
109
+ x,
110
+ y
111
+ }));
112
+ const xs = transformed.map((point) => point.x);
113
+ const ys = transformed.map((point) => point.y);
114
+ return {
115
+ minX: Math.min(...xs),
116
+ minY: Math.min(...ys),
117
+ maxX: Math.max(...xs),
118
+ maxY: Math.max(...ys)
119
+ };
120
+ }
69
121
  function normalizeRotation(rotate) {
70
122
  if (rotate === void 0) return 0;
71
123
  const normalized = (Math.round(rotate / 90) * 90 % 360 + 360) % 360;
@@ -134,35 +186,150 @@ function readPageContentBytes(page, resolver, sink) {
134
186
  }
135
187
  return /* @__PURE__ */ new Uint8Array(0);
136
188
  }
137
- function readPage(page, resolver, fontResolver, images, imageIdCache, sink) {
189
+ function contentItemBounds(item) {
190
+ if (item.kind === "link" || item.kind === "internalLink") return;
191
+ const frameBounds = (xPt, yPt, widthPt, heightPt) => ({
192
+ minX: xPt,
193
+ minY: yPt,
194
+ maxX: xPt + widthPt,
195
+ maxY: yPt + heightPt
196
+ });
197
+ if (item.kind === "text" || item.kind === "image" || item.kind === "rect" || item.kind === "ellipse") {
198
+ if (item.kind !== "text" || item.rotationDeg === void 0 || item.rotationDeg % 180 === 0) return frameBounds(item.xPt, item.yPt, item.widthPt ?? 0, item.kind === "text" ? item.sizePt : item.heightPt);
199
+ const rotationDeg = item.rotationDeg;
200
+ const anchorX = item.xPt;
201
+ const anchorY = item.yPt;
202
+ const rotated = [
203
+ [0, 0],
204
+ [item.widthPt ?? 0, 0],
205
+ [0, item.sizePt],
206
+ [item.widthPt ?? 0, item.sizePt]
207
+ ].map(([dx, dy]) => rotatePointAboutCenter({
208
+ x: anchorX + dx,
209
+ y: anchorY + dy
210
+ }, {
211
+ x: anchorX,
212
+ y: anchorY
213
+ }, rotationDeg));
214
+ const xs = rotated.map((point) => point.x);
215
+ const ys = rotated.map((point) => point.y);
216
+ return {
217
+ minX: Math.min(...xs),
218
+ minY: Math.min(...ys),
219
+ maxX: Math.max(...xs),
220
+ maxY: Math.max(...ys)
221
+ };
222
+ }
223
+ if (item.kind === "line") return {
224
+ minX: Math.min(item.x1Pt, item.x2Pt),
225
+ minY: Math.min(item.y1Pt, item.y2Pt),
226
+ maxX: Math.max(item.x1Pt, item.x2Pt),
227
+ maxY: Math.max(item.y1Pt, item.y2Pt)
228
+ };
229
+ let minX = Number.POSITIVE_INFINITY;
230
+ let minY = Number.POSITIVE_INFINITY;
231
+ let maxX = Number.NEGATIVE_INFINITY;
232
+ let maxY = Number.NEGATIVE_INFINITY;
233
+ const include = (x, y) => {
234
+ minX = Math.min(minX, x);
235
+ minY = Math.min(minY, y);
236
+ maxX = Math.max(maxX, x);
237
+ maxY = Math.max(maxY, y);
238
+ };
239
+ for (const subpath of item.subpaths) {
240
+ include(subpath.startXPt, subpath.startYPt);
241
+ for (const segment of subpath.segments) {
242
+ include(segment.xPt, segment.yPt);
243
+ if (segment.kind === "cubic") {
244
+ include(segment.c1xPt, segment.c1yPt);
245
+ include(segment.c2xPt, segment.c2yPt);
246
+ }
247
+ }
248
+ }
249
+ return {
250
+ minX,
251
+ minY,
252
+ maxX,
253
+ maxY
254
+ };
255
+ }
256
+ function itemIntersectsVisibleRegion(item, widthPt, heightPt) {
257
+ if (item.kind === "link" || item.kind === "internalLink") return true;
258
+ const bounds = contentItemBounds(item);
259
+ if (bounds === void 0) return true;
260
+ return bounds.maxX >= 0 && bounds.minX <= widthPt && bounds.maxY >= 0 && bounds.minY <= heightPt;
261
+ }
262
+ function pageBoxResidueEntry(pageIndex, page, mediaBox, cropBox) {
263
+ const declaredBoxes = [
264
+ ["MediaBox", dictGet(page, "MediaBox")],
265
+ ["CropBox", dictGet(page, "CropBox")],
266
+ ["BleedBox", dictGet(page, "BleedBox")],
267
+ ["TrimBox", dictGet(page, "TrimBox")],
268
+ ["ArtBox", dictGet(page, "ArtBox")]
269
+ ];
270
+ const cropDeclared = dictGet(page, "CropBox") !== void 0;
271
+ const productionBoxDeclared = declaredBoxes.slice(2).some(([, value]) => value !== void 0);
272
+ if (!(cropDeclared && (cropBox.llx !== mediaBox.llx || cropBox.lly !== mediaBox.lly || cropBox.urx !== mediaBox.urx || cropBox.ury !== mediaBox.ury)) && !productionBoxDeclared) return;
273
+ const entries = { Page: pdfNum(pageIndex) };
274
+ for (const [key, value] of declaredBoxes) if (value !== void 0) entries[key] = value;
275
+ return pdfDict(entries);
276
+ }
277
+ function readPage(pageIndex, page, resolver, fontResolver, images, imageIdCache, destinationRegistry, layerNameOf, structureOwnerOf, pageBoxRows, sink) {
138
278
  const resources = resolver.resolveDict(dictGet(page, "Resources"));
139
279
  const mediaBox = readMediaBox(page);
280
+ let cropBox = readDeclaredPageBox(page, "CropBox") ?? mediaBox;
281
+ if (cropBox.urx - cropBox.llx <= 0 || cropBox.ury - cropBox.lly <= 0) {
282
+ sink({
283
+ code: "pdf/invalid-crop-box",
284
+ severity: "warning",
285
+ pageIndex,
286
+ message: "page /CropBox is degenerate (zero width or height); falling back to the /MediaBox as the visible region"
287
+ });
288
+ cropBox = mediaBox;
289
+ }
140
290
  const rotationResult = pageRotationTransform(normalizeRotation(asNumber(dictGet(page, "Rotate"))), mediaBox.urx - mediaBox.llx, mediaBox.ury - mediaBox.lly);
141
- const pageMatrix = multiplyMatrices(translationMatrix(-mediaBox.llx, -mediaBox.lly), rotationResult.matrix);
291
+ const visibleRect = rotatedRectBounds(cropBox, rotationResult.matrix);
292
+ const widthPt = visibleRect.maxX - visibleRect.minX;
293
+ const heightPt = visibleRect.maxY - visibleRect.minY;
294
+ const pageMatrix = multiplyMatrices(rotationResult.matrix, translationMatrix(-visibleRect.minX, -visibleRect.minY));
295
+ const boxRow = pageBoxResidueEntry(pageIndex, page, mediaBox, cropBox);
296
+ if (boxRow !== void 0) pageBoxRows.push(boxRow);
142
297
  const items = [];
143
298
  if (resources !== void 0) {
144
299
  const contentBytes = readPageContentBytes(page, resolver, sink);
145
300
  const extracted = interpretContentStream(contentBytes, resources, {
146
301
  fontMetrics: fontResolver.metrics,
147
302
  resolver,
148
- sink
303
+ sink,
304
+ layerNameOf
149
305
  });
150
306
  for (const item of extracted) {
151
307
  const converted = convertExtractedItem(item, pageMatrix, fontResolver, images, imageIdCache, resolver, sink);
152
- if (converted !== void 0) items.push(converted);
308
+ if (converted === void 0 || !itemIntersectsVisibleRegion(converted, widthPt, heightPt)) continue;
309
+ const owner = item.mcid === void 0 ? void 0 : structureOwnerOf(pageIndex, item.mcid);
310
+ items.push(owner === void 0 ? converted : withStructure(converted, owner));
153
311
  }
154
312
  } else sink({
155
313
  code: "pdf/object-missing-value",
156
314
  severity: "warning",
157
315
  message: "page has no /Resources dict; its content stream cannot be interpreted"
158
316
  });
159
- items.push(...readLinkAnnotations(page, pageMatrix, resolver));
317
+ items.push(...readLinkAnnotations(page, pageMatrix, resolver, destinationRegistry));
160
318
  const notes = readPageNotes(page, resolver);
319
+ const annotations = readPageAnnotations(page, pageMatrix, resolver, sink);
161
320
  return {
162
- widthPt: rotationResult.widthPt,
163
- heightPt: rotationResult.heightPt,
321
+ widthPt,
322
+ heightPt,
164
323
  items,
165
- ...notes !== void 0 ? { notes } : {}
324
+ ...notes !== void 0 ? { notes } : {},
325
+ ...annotations.length > 0 ? { annotations } : {}
326
+ };
327
+ }
328
+ function withStructure(item, structure) {
329
+ if (item.kind === "link" || item.kind === "internalLink") return item;
330
+ return {
331
+ ...item,
332
+ structure
166
333
  };
167
334
  }
168
335
  function convertExtractedItem(item, pageMatrix, fontResolver, images, imageIdCache, resolver, sink) {
@@ -197,7 +364,10 @@ function convertText(item, pageMatrix, fontResolver) {
197
364
  sizePt: sizePt > 0 ? sizePt : item.sizePt,
198
365
  color: item.color,
199
366
  widthPt,
200
- rotationDeg: rotationDeg !== 0 ? rotationDeg : void 0
367
+ rotationDeg: rotationDeg !== 0 ? rotationDeg : void 0,
368
+ ...item.layerName !== void 0 ? { layer: item.layerName } : {},
369
+ ...item.actualText !== void 0 ? { actualText: item.actualText } : {},
370
+ ...item.alt !== void 0 ? { alt: item.alt } : {}
201
371
  };
202
372
  }
203
373
  function paintFields(paint) {
@@ -226,14 +396,16 @@ function convertRect(item, pageMatrix) {
226
396
  return {
227
397
  kind: "rect",
228
398
  ...transformBox(item, pageMatrix),
229
- ...paintFields(item)
399
+ ...paintFields(item),
400
+ ...item.layerName !== void 0 ? { layer: item.layerName } : {}
230
401
  };
231
402
  }
232
403
  function convertEllipse(item, pageMatrix) {
233
404
  return {
234
405
  kind: "ellipse",
235
406
  ...transformBox(item, pageMatrix),
236
- ...paintFields(item)
407
+ ...paintFields(item),
408
+ ...item.layerName !== void 0 ? { layer: item.layerName } : {}
237
409
  };
238
410
  }
239
411
  function convertLine(item, pageMatrix) {
@@ -252,7 +424,9 @@ function convertLine(item, pageMatrix) {
252
424
  x2Pt: p2.x,
253
425
  y2Pt: p2.y,
254
426
  color: item.color,
255
- widthPt: item.widthPt
427
+ widthPt: item.widthPt,
428
+ ...item.style !== void 0 ? { style: item.style } : {},
429
+ ...item.layerName !== void 0 ? { layer: item.layerName } : {}
256
430
  };
257
431
  }
258
432
  function transformSubpath(subpath, pageMatrix) {
@@ -307,7 +481,9 @@ function convertPath(item, pageMatrix) {
307
481
  subpaths: item.subpaths.map((subpath) => transformSubpath(subpath, pageMatrix)),
308
482
  ...item.fill !== void 0 ? { fill: item.fill } : {},
309
483
  ...item.fill !== void 0 && item.fillRule === "evenodd" ? { fillRule: "evenodd" } : {},
310
- ...item.stroke !== void 0 ? { stroke: item.stroke } : {}
484
+ ...item.stroke !== void 0 ? { stroke: item.stroke } : {},
485
+ ...item.style !== void 0 ? { style: item.style } : {},
486
+ ...item.layerName !== void 0 ? { layer: item.layerName } : {}
311
487
  };
312
488
  }
313
489
  function imagePlacementFrom(matrix) {
@@ -350,7 +526,8 @@ function convertImage(item, pageMatrix, images, cache, resolver, sink) {
350
526
  return {
351
527
  kind: "image",
352
528
  imageId,
353
- ...imagePlacementFrom(multiplyMatrices(item.matrix, pageMatrix))
529
+ ...imagePlacementFrom(multiplyMatrices(item.matrix, pageMatrix)),
530
+ ...item.layerName !== void 0 ? { layer: item.layerName } : {}
354
531
  };
355
532
  }
356
533
  function convertInlineImage(item, pageMatrix, images, resolver, sink) {
@@ -359,19 +536,22 @@ function convertInlineImage(item, pageMatrix, images, resolver, sink) {
359
536
  return {
360
537
  kind: "image",
361
538
  imageId: registerExtractedImage(decoded.format, decoded.bytes, decoded.widthPx, decoded.heightPx, images),
362
- ...imagePlacementFrom(multiplyMatrices(item.matrix, pageMatrix))
539
+ ...imagePlacementFrom(multiplyMatrices(item.matrix, pageMatrix)),
540
+ ...item.layerName !== void 0 ? { layer: item.layerName } : {}
363
541
  };
364
542
  }
365
- function readLinkAnnotations(page, pageMatrix, resolver) {
543
+ function readLinkAnnotations(page, pageMatrix, resolver, destinationRegistry) {
366
544
  const annotsArr = asArray(dictGet(page, "Annots"));
367
545
  if (annotsArr === void 0) return [];
368
546
  const links = [];
369
547
  for (const annotRef of annotsArr) {
370
548
  const annot = resolver.resolveDict(annotRef);
371
549
  if (annot === void 0 || asName(dictGet(annot, "Subtype")) !== "Link") continue;
372
- const uri = readLinkUri(annot, resolver);
373
550
  const rectArr = asArray(dictGet(annot, "Rect"));
374
- if (uri === void 0 || rectArr === void 0) continue;
551
+ if (rectArr === void 0) continue;
552
+ const uri = readLinkUri(annot, resolver);
553
+ const destination = uri === void 0 ? readInternalDestination(annot, resolver, destinationRegistry) : void 0;
554
+ if (uri === void 0 && destination === void 0) continue;
375
555
  const x1 = asNumber(rectArr[0]) ?? 0;
376
556
  const y1 = asNumber(rectArr[1]) ?? 0;
377
557
  const x2 = asNumber(rectArr[2]) ?? 0;
@@ -384,13 +564,24 @@ function readLinkAnnotations(page, pageMatrix, resolver) {
384
564
  x: Math.max(x1, x2),
385
565
  y: Math.max(y1, y2)
386
566
  });
387
- links.push({
388
- kind: "link",
389
- uri,
567
+ const contentsObj = dictGet(annot, "Contents");
568
+ const title = contentsObj?.kind === "string" ? decodePdfString(contentsObj.bytes) : void 0;
569
+ const box = {
390
570
  xPt: Math.min(p1.x, p2.x),
391
571
  yPt: Math.min(p1.y, p2.y),
392
572
  widthPt: Math.abs(p2.x - p1.x),
393
- heightPt: Math.abs(p2.y - p1.y)
573
+ heightPt: Math.abs(p2.y - p1.y),
574
+ ...title !== void 0 ? { title } : {}
575
+ };
576
+ if (uri !== void 0) links.push({
577
+ kind: "link",
578
+ uri,
579
+ ...box
580
+ });
581
+ else if (destination !== void 0) links.push({
582
+ kind: "internalLink",
583
+ destination,
584
+ ...box
394
585
  });
395
586
  }
396
587
  return links;
@@ -401,6 +592,12 @@ function readLinkUri(annot, resolver) {
401
592
  const uriObj = dictGet(action, "URI");
402
593
  return uriObj?.kind === "string" ? decodePdfString(uriObj.bytes) : void 0;
403
594
  }
595
+ function readInternalDestination(annot, resolver, destinationRegistry) {
596
+ const dest = dictGet(annot, "Dest");
597
+ if (dest !== void 0) return destinationRegistry.intern(dest);
598
+ const action = resolver.resolveDict(dictGet(annot, "A"));
599
+ if (action !== void 0 && asName(dictGet(action, "S")) === "GoTo") return destinationRegistry.intern(dictGet(action, "D"));
600
+ }
404
601
  function readPageNotes(page, resolver) {
405
602
  const annotsArr = asArray(dictGet(page, "Annots"));
406
603
  if (annotsArr === void 0) return;
@@ -413,23 +610,7 @@ function readPageNotes(page, resolver) {
413
610
  if (contentsObj?.kind === "string") return decodePdfString(contentsObj.bytes);
414
611
  }
415
612
  }
416
- function decodePdfString(bytes) {
417
- if (bytes.length >= 2 && bytes[0] === 254 && bytes[1] === 255) {
418
- let out = "";
419
- for (let i = 2; i + 1 < bytes.length; i += 2) out += String.fromCharCode((bytes[i] ?? 0) << 8 | (bytes[i + 1] ?? 0));
420
- return out;
421
- }
422
- return Array.from(bytes, (b) => String.fromCharCode(b)).join("");
423
- }
424
- const PDF_DATE_PATTERN = /^D:(\d{4})(\d{2})?(\d{2})?(\d{2})?(\d{2})?(\d{2})?([+\-Z])?(\d{2})?'?(\d{2})?'?$/;
425
- function parsePdfDate(raw) {
426
- if (raw === void 0) return;
427
- const match = PDF_DATE_PATTERN.exec(raw);
428
- if (match === null) return;
429
- const [, year, month = "01", day = "01", hour = "00", minute = "00", second = "00", tzSign, tzHour = "00", tzMinute = "00"] = match;
430
- return `${year}-${month}-${day}T${hour}:${minute}:${second}${tzSign === void 0 || tzSign === "Z" ? "Z" : `${tzSign}${tzHour}:${tzMinute}`}`;
431
- }
432
- function readMetadata(trailer, resolver) {
613
+ function readMetadata(trailer, resolver, catalog, sink) {
433
614
  const info = resolver.resolveDict(dictGet(trailer, "Info"));
434
615
  if (info === void 0) return {};
435
616
  const stringField = (key) => {
@@ -437,16 +618,52 @@ function readMetadata(trailer, resolver) {
437
618
  return obj?.kind === "string" ? decodePdfString(obj.bytes) : void 0;
438
619
  };
439
620
  const keywords = stringField("Keywords")?.split(",").map((k) => k.trim()).filter((k) => k.length > 0);
621
+ const langObj = dictGet(catalog, "Lang");
622
+ const xmp = xmpPacket(catalog, resolver, sink);
623
+ const mirrored = readXmpMetadata(xmp ?? "");
440
624
  return {
441
- title: stringField("Title"),
442
- author: stringField("Author"),
443
- subject: stringField("Subject"),
444
- keywords: keywords !== void 0 && keywords.length > 0 ? keywords : void 0,
445
- creator: stringField("Creator"),
446
- producer: stringField("Producer"),
447
- createdIso: parsePdfDate(stringField("CreationDate")),
448
- modifiedIso: parsePdfDate(stringField("ModDate"))
625
+ title: stringField("Title") ?? mirrored.title,
626
+ author: stringField("Author") ?? mirrored.author,
627
+ subject: stringField("Subject") ?? mirrored.subject,
628
+ keywords: (keywords !== void 0 && keywords.length > 0 ? keywords : void 0) ?? mirrored.keywords,
629
+ creator: stringField("Creator") ?? mirrored.creator,
630
+ producer: stringField("Producer") ?? mirrored.producer,
631
+ createdIso: parsePdfDate(stringField("CreationDate")) ?? mirrored.createdIso,
632
+ modifiedIso: parsePdfDate(stringField("ModDate")) ?? mirrored.modifiedIso,
633
+ ...langObj?.kind === "string" ? { language: decodePdfString(langObj.bytes) } : {}
634
+ };
635
+ }
636
+ function xmpPacket(catalog, resolver, sink) {
637
+ const metadataObj = resolver.resolve(dictGet(catalog, "Metadata"));
638
+ if (metadataObj?.kind !== "stream") return;
639
+ const decoded = decodeStream(metadataObj.raw, metadataObj.dict, sink);
640
+ return new TextDecoder().decode(decoded.bytes);
641
+ }
642
+ function readDocumentResidue(doc, sink) {
643
+ const source = {};
644
+ const residue = (key, obj) => {
645
+ if (obj !== void 0) source[key] = {
646
+ format: "pdf",
647
+ xml: serializeObjectToText(obj)
648
+ };
649
+ };
650
+ const catalog = doc.catalog;
651
+ const packet = xmpPacket(catalog, doc, sink);
652
+ if (packet !== void 0) source.xmp = {
653
+ format: "pdf",
654
+ xml: packet
449
655
  };
656
+ residue("viewer-preferences", dictGet(catalog, "ViewerPreferences"));
657
+ residue("page-mode", dictGet(catalog, "PageMode"));
658
+ residue("page-layout", dictGet(catalog, "PageLayout"));
659
+ residue("open-action", dictGet(catalog, "OpenAction"));
660
+ const outputIntents = asArray(dictGet(catalog, "OutputIntents"));
661
+ if (outputIntents !== void 0) residue("output-intents", pdfArray(outputIntents.map((intent) => doc.resolve(intent) ?? pdfNull())));
662
+ residue("piece-info", dictGet(catalog, "PieceInfo"));
663
+ residue("legal", dictGet(catalog, "Legal"));
664
+ residue("collection", dictGet(catalog, "Collection"));
665
+ residue("trailer-id", dictGet(doc.trailer, "ID"));
666
+ return source;
450
667
  }
451
668
  //#endregion
452
- export { decodePdfString, normalizeRotation, pageRotationTransform, parsePdfDate, readPdf };
669
+ 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 { r as PdfObject } from "./objects-B7HdMeWS.cjs";
1
2
  import { ByteWriter } from "./bytes/writer.cjs";
2
- import { PdfObject } from "./objects.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 { r as PdfObject } from "./objects-B7HdMeWS.js";
1
2
  import { ByteWriter } from "./bytes/writer.js";
2
- import { PdfObject } from "./objects.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 };
@@ -71,7 +71,7 @@ function buildPostStub(font) {
71
71
  if (sourcePost !== void 0 && require_sfnt.hasBytes(sourcePost, POST_METRICS_OFFSET, POST_METRICS_SIZE)) post.set(sourcePost.subarray(POST_METRICS_OFFSET, 16), POST_METRICS_OFFSET);
72
72
  return post;
73
73
  }
74
- function collectGlyphIds(glyf, cmap, numGlyphs, codePoints) {
74
+ function collectGlyphIds(glyf, cmap, numGlyphs, codePoints, extraGlyphIds) {
75
75
  const used = /* @__PURE__ */ new Set([0]);
76
76
  const unmapped = /* @__PURE__ */ new Set();
77
77
  const pending = [0];
@@ -87,6 +87,13 @@ function collectGlyphIds(glyf, cmap, numGlyphs, codePoints) {
87
87
  pending.push(glyphId);
88
88
  }
89
89
  }
90
+ for (const glyphId of extraGlyphIds) {
91
+ if (glyphId >= numGlyphs) return;
92
+ if (!used.has(glyphId)) {
93
+ used.add(glyphId);
94
+ pending.push(glyphId);
95
+ }
96
+ }
90
97
  while (pending.length > 0) {
91
98
  const glyphId = pending.pop();
92
99
  if (glyf.glyphBytes(glyphId) === void 0) return;
@@ -107,7 +114,7 @@ function collectGlyphIds(glyf, cmap, numGlyphs, codePoints) {
107
114
  unmapped: [...unmapped].sort((a, b) => a - b)
108
115
  };
109
116
  }
110
- function subsetSfnt(font, codePoints) {
117
+ function subsetSfnt(font, codePoints, extraGlyphIds = []) {
111
118
  const head = require_font_tables.parseHead(font);
112
119
  const maxp = require_font_tables.parseMaxp(font);
113
120
  const sourceHead = require_sfnt.sfntTableBytes(font, "head");
@@ -124,7 +131,7 @@ function subsetSfnt(font, codePoints) {
124
131
  });
125
132
  const cmap = require_cmap_table.buildCmapLookup(font);
126
133
  if (glyf === void 0 || cmap === void 0) return;
127
- const collected = collectGlyphIds(glyf, cmap, maxp.numGlyphs, codePoints);
134
+ const collected = collectGlyphIds(glyf, cmap, maxp.numGlyphs, codePoints, extraGlyphIds);
128
135
  if (collected === void 0) return;
129
136
  const glyphIds = [...collected.used].sort((a, b) => a - b);
130
137
  const numGlyphs = glyphIds[glyphIds.length - 1] + 1;
@@ -6,6 +6,6 @@ interface SfntSubsetResult {
6
6
  readonly glyphIds: readonly number[];
7
7
  readonly unmappedCodePoints: readonly number[];
8
8
  }
9
- declare function subsetSfnt(font: SfntFont, codePoints: Iterable<number>): SfntSubsetResult | undefined;
9
+ declare function subsetSfnt(font: SfntFont, codePoints: Iterable<number>, extraGlyphIds?: Iterable<number>): SfntSubsetResult | undefined;
10
10
  //#endregion
11
11
  export { SfntSubsetResult, subsetSfnt };
@@ -6,6 +6,6 @@ interface SfntSubsetResult {
6
6
  readonly glyphIds: readonly number[];
7
7
  readonly unmappedCodePoints: readonly number[];
8
8
  }
9
- declare function subsetSfnt(font: SfntFont, codePoints: Iterable<number>): SfntSubsetResult | undefined;
9
+ declare function subsetSfnt(font: SfntFont, codePoints: Iterable<number>, extraGlyphIds?: Iterable<number>): SfntSubsetResult | undefined;
10
10
  //#endregion
11
11
  export { SfntSubsetResult, subsetSfnt };
@@ -70,7 +70,7 @@ function buildPostStub(font) {
70
70
  if (sourcePost !== void 0 && hasBytes(sourcePost, POST_METRICS_OFFSET, POST_METRICS_SIZE)) post.set(sourcePost.subarray(POST_METRICS_OFFSET, 16), POST_METRICS_OFFSET);
71
71
  return post;
72
72
  }
73
- function collectGlyphIds(glyf, cmap, numGlyphs, codePoints) {
73
+ function collectGlyphIds(glyf, cmap, numGlyphs, codePoints, extraGlyphIds) {
74
74
  const used = /* @__PURE__ */ new Set([0]);
75
75
  const unmapped = /* @__PURE__ */ new Set();
76
76
  const pending = [0];
@@ -86,6 +86,13 @@ function collectGlyphIds(glyf, cmap, numGlyphs, codePoints) {
86
86
  pending.push(glyphId);
87
87
  }
88
88
  }
89
+ for (const glyphId of extraGlyphIds) {
90
+ if (glyphId >= numGlyphs) return;
91
+ if (!used.has(glyphId)) {
92
+ used.add(glyphId);
93
+ pending.push(glyphId);
94
+ }
95
+ }
89
96
  while (pending.length > 0) {
90
97
  const glyphId = pending.pop();
91
98
  if (glyf.glyphBytes(glyphId) === void 0) return;
@@ -106,7 +113,7 @@ function collectGlyphIds(glyf, cmap, numGlyphs, codePoints) {
106
113
  unmapped: [...unmapped].sort((a, b) => a - b)
107
114
  };
108
115
  }
109
- function subsetSfnt(font, codePoints) {
116
+ function subsetSfnt(font, codePoints, extraGlyphIds = []) {
110
117
  const head = parseHead(font);
111
118
  const maxp = parseMaxp(font);
112
119
  const sourceHead = sfntTableBytes(font, "head");
@@ -123,7 +130,7 @@ function subsetSfnt(font, codePoints) {
123
130
  });
124
131
  const cmap = buildCmapLookup(font);
125
132
  if (glyf === void 0 || cmap === void 0) return;
126
- const collected = collectGlyphIds(glyf, cmap, maxp.numGlyphs, codePoints);
133
+ const collected = collectGlyphIds(glyf, cmap, maxp.numGlyphs, codePoints, extraGlyphIds);
127
134
  if (collected === void 0) return;
128
135
  const glyphIds = [...collected.used].sort((a, b) => a - b);
129
136
  const numGlyphs = glyphIds[glyphIds.length - 1] + 1;