pdf-codec 1.3.0 → 1.4.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 (198) hide show
  1. package/README.md +38 -0
  2. package/dist/afm-widths-BoeTOK2r.cjs +826 -0
  3. package/dist/afm-widths-Dxucrg7D.js +779 -0
  4. package/dist/afm-widths-DyDq56Ph.d.cts +19 -0
  5. package/dist/afm-widths-DyDq56Ph.d.ts +19 -0
  6. package/dist/afm-widths.cjs +4 -0
  7. package/dist/afm-widths.d.cts +2 -0
  8. package/dist/afm-widths.d.ts +2 -0
  9. package/dist/afm-widths.js +2 -0
  10. package/dist/assets/stix-two-math-font.cjs +5 -0
  11. package/dist/assets/stix-two-math-font.d.cts +4 -0
  12. package/dist/assets/stix-two-math-font.d.ts +4 -0
  13. package/dist/assets/stix-two-math-font.js +4 -0
  14. package/dist/bytes/crc32.cjs +21 -0
  15. package/dist/bytes/crc32.d.cts +4 -0
  16. package/dist/bytes/crc32.d.ts +4 -0
  17. package/dist/bytes/crc32.js +20 -0
  18. package/dist/bytes/flate.cjs +53 -0
  19. package/dist/bytes/flate.d.cts +12 -0
  20. package/dist/bytes/flate.d.ts +12 -0
  21. package/dist/bytes/flate.js +49 -0
  22. package/dist/bytes/reader.cjs +60 -0
  23. package/dist/bytes/reader.d.cts +20 -0
  24. package/dist/bytes/reader.d.ts +20 -0
  25. package/dist/bytes/reader.js +58 -0
  26. package/dist/bytes/writer.cjs +37 -0
  27. package/dist/bytes/writer.d.cts +13 -0
  28. package/dist/bytes/writer.d.ts +13 -0
  29. package/dist/bytes/writer.js +35 -0
  30. package/dist/cmap.cjs +110 -0
  31. package/dist/cmap.d.cts +8 -0
  32. package/dist/cmap.d.ts +8 -0
  33. package/dist/cmap.js +109 -0
  34. package/dist/codec.cjs +34 -0
  35. package/dist/codec.d.cts +167 -0
  36. package/dist/codec.d.ts +167 -0
  37. package/dist/codec.js +32 -0
  38. package/dist/content-read.cjs +126 -0
  39. package/dist/content-read.d.cts +21 -0
  40. package/dist/content-read.d.ts +21 -0
  41. package/dist/content-read.js +125 -0
  42. package/dist/content-write.cjs +138 -0
  43. package/dist/content-write.d.cts +24 -0
  44. package/dist/content-write.d.ts +24 -0
  45. package/dist/content-write.js +137 -0
  46. package/dist/diagnostics.cjs +21 -0
  47. package/dist/diagnostics.d.cts +19 -0
  48. package/dist/diagnostics.d.ts +19 -0
  49. package/dist/diagnostics.js +18 -0
  50. package/dist/document.cjs +162 -0
  51. package/dist/document.d.cts +12 -0
  52. package/dist/document.d.ts +12 -0
  53. package/dist/document.js +161 -0
  54. package/dist/encoding.cjs +5 -0
  55. package/dist/encoding.d.cts +6 -0
  56. package/dist/encoding.d.ts +6 -0
  57. package/dist/encoding.js +2 -0
  58. package/dist/filters.cjs +223 -0
  59. package/dist/filters.d.cts +14 -0
  60. package/dist/filters.d.ts +14 -0
  61. package/dist/filters.js +218 -0
  62. package/dist/font-read.cjs +199 -0
  63. package/dist/font-read.d.cts +23 -0
  64. package/dist/font-read.d.ts +23 -0
  65. package/dist/font-read.js +198 -0
  66. package/dist/font-style.cjs +35 -0
  67. package/dist/font-style.d.cts +14 -0
  68. package/dist/font-style.d.ts +14 -0
  69. package/dist/font-style.js +34 -0
  70. package/dist/fonts.cjs +124 -0
  71. package/dist/fonts.d.cts +15 -0
  72. package/dist/fonts.d.ts +15 -0
  73. package/dist/fonts.js +122 -0
  74. package/dist/formula.cjs +0 -0
  75. package/dist/formula.d.cts +10 -0
  76. package/dist/formula.d.ts +10 -0
  77. package/dist/formula.js +0 -0
  78. package/dist/image/jpeg-info.cjs +71 -0
  79. package/dist/image/jpeg-info.d.cts +12 -0
  80. package/dist/image/jpeg-info.d.ts +12 -0
  81. package/dist/image/jpeg-info.js +70 -0
  82. package/dist/image/png-decode.cjs +181 -0
  83. package/dist/image/png-decode.d.cts +14 -0
  84. package/dist/image/png-decode.d.ts +14 -0
  85. package/dist/image/png-decode.js +180 -0
  86. package/dist/image/png-encode.cjs +64 -0
  87. package/dist/image/png-encode.d.cts +8 -0
  88. package/dist/image/png-encode.d.ts +8 -0
  89. package/dist/image/png-encode.js +63 -0
  90. package/dist/image/png-filter.cjs +95 -0
  91. package/dist/image/png-filter.d.cts +6 -0
  92. package/dist/image/png-filter.d.ts +6 -0
  93. package/dist/image/png-filter.js +93 -0
  94. package/dist/images-read.cjs +250 -0
  95. package/dist/images-read.d.cts +13 -0
  96. package/dist/images-read.d.ts +13 -0
  97. package/dist/images-read.js +249 -0
  98. package/dist/index.cjs +45 -5912
  99. package/dist/index.d.cts +21 -483
  100. package/dist/index.d.ts +21 -483
  101. package/dist/index.js +18 -5885
  102. package/dist/interpret.cjs +587 -0
  103. package/dist/interpret.d.cts +84 -0
  104. package/dist/interpret.d.ts +84 -0
  105. package/dist/interpret.js +586 -0
  106. package/dist/lexer.cjs +180 -0
  107. package/dist/lexer.d.cts +29 -0
  108. package/dist/lexer.d.ts +29 -0
  109. package/dist/lexer.js +179 -0
  110. package/dist/math-cmap.cjs +81 -0
  111. package/dist/math-cmap.d.cts +6 -0
  112. package/dist/math-cmap.d.ts +6 -0
  113. package/dist/math-cmap.js +80 -0
  114. package/dist/math-content-write.cjs +83 -0
  115. package/dist/math-content-write.d.cts +11 -0
  116. package/dist/math-content-write.d.ts +11 -0
  117. package/dist/math-content-write.js +81 -0
  118. package/dist/math-font-write.cjs +100 -0
  119. package/dist/math-font-write.d.cts +19 -0
  120. package/dist/math-font-write.d.ts +19 -0
  121. package/dist/math-font-write.js +99 -0
  122. package/dist/math-font.cjs +103 -0
  123. package/dist/math-font.d.cts +25 -0
  124. package/dist/math-font.d.ts +25 -0
  125. package/dist/math-font.js +102 -0
  126. package/dist/math-hmtx.cjs +17 -0
  127. package/dist/math-hmtx.d.cts +8 -0
  128. package/dist/math-hmtx.d.ts +8 -0
  129. package/dist/math-hmtx.js +16 -0
  130. package/dist/math-table.cjs +117 -0
  131. package/dist/math-table.d.cts +45 -0
  132. package/dist/math-table.d.ts +45 -0
  133. package/dist/math-table.js +116 -0
  134. package/dist/math-types-Ba8qAf1p.d.cts +82 -0
  135. package/dist/math-types-Ba8qAf1p.d.ts +82 -0
  136. package/dist/math-types.cjs +0 -0
  137. package/dist/math-types.d.cts +2 -0
  138. package/dist/math-types.d.ts +2 -0
  139. package/dist/math-types.js +0 -0
  140. package/dist/matrix-B3c2_a2f.d.cts +18 -0
  141. package/dist/matrix-B3c2_a2f.d.ts +18 -0
  142. package/dist/matrix.cjs +93 -0
  143. package/dist/matrix.d.cts +2 -0
  144. package/dist/matrix.d.ts +2 -0
  145. package/dist/matrix.js +83 -0
  146. package/dist/measure.cjs +54 -0
  147. package/dist/measure.d.cts +20 -0
  148. package/dist/measure.d.ts +20 -0
  149. package/dist/measure.js +53 -0
  150. package/dist/objects.cjs +100 -0
  151. package/dist/objects.d.cts +58 -0
  152. package/dist/objects.d.ts +58 -0
  153. package/dist/objects.js +84 -0
  154. package/dist/parse.cjs +230 -0
  155. package/dist/parse.d.cts +13 -0
  156. package/dist/parse.d.ts +13 -0
  157. package/dist/parse.js +228 -0
  158. package/dist/predictors.cjs +64 -0
  159. package/dist/predictors.d.cts +13 -0
  160. package/dist/predictors.d.ts +13 -0
  161. package/dist/predictors.js +62 -0
  162. package/dist/read.cjs +418 -0
  163. package/dist/read.d.cts +21 -0
  164. package/dist/read.d.ts +21 -0
  165. package/dist/read.js +413 -0
  166. package/dist/serialize.cjs +69 -0
  167. package/dist/serialize.d.cts +8 -0
  168. package/dist/serialize.d.ts +8 -0
  169. package/dist/serialize.js +66 -0
  170. package/dist/sfnt.cjs +52 -0
  171. package/dist/sfnt.d.cts +17 -0
  172. package/dist/sfnt.d.ts +17 -0
  173. package/dist/sfnt.js +46 -0
  174. package/dist/text-layout.cjs +224 -0
  175. package/dist/text-layout.d.cts +37 -0
  176. package/dist/text-layout.d.ts +37 -0
  177. package/dist/text-layout.js +222 -0
  178. package/dist/util/abort.cjs +7 -0
  179. package/dist/util/abort.d.cts +4 -0
  180. package/dist/util/abort.d.ts +4 -0
  181. package/dist/util/abort.js +6 -0
  182. package/dist/util/base64.cjs +48 -0
  183. package/dist/util/base64.d.cts +5 -0
  184. package/dist/util/base64.d.ts +5 -0
  185. package/dist/util/base64.js +46 -0
  186. package/dist/winansi.cjs +5 -0
  187. package/dist/winansi.d.cts +20 -0
  188. package/dist/winansi.d.ts +20 -0
  189. package/dist/winansi.js +2 -0
  190. package/dist/write.cjs +421 -0
  191. package/dist/write.d.cts +16 -0
  192. package/dist/write.d.ts +16 -0
  193. package/dist/write.js +419 -0
  194. package/dist/xref.cjs +325 -0
  195. package/dist/xref.d.cts +21 -0
  196. package/dist/xref.d.ts +21 -0
  197. package/dist/xref.js +324 -0
  198. package/package.json +9 -1
package/dist/read.js ADDED
@@ -0,0 +1,413 @@
1
+ import { bytesToBase64 } from "./util/base64.js";
2
+ import { crc32 } from "./bytes/crc32.js";
3
+ import { concatBytes } from "./bytes/writer.js";
4
+ import { NOOP_DIAGNOSTIC_SINK, PdfParseError } from "./diagnostics.js";
5
+ import { asArray, asName, asNumber, dictGet } from "./objects.js";
6
+ import { decodeStream } from "./filters.js";
7
+ import { openPdfDocument } from "./document.js";
8
+ import { throwIfAborted } from "./util/abort.js";
9
+ import { createFontResolver } from "./font-read.js";
10
+ import { readImageXObject } from "./images-read.js";
11
+ import { applyMatrix, matrixRotationDegrees, matrixScaleX, matrixScaleY, multiplyMatrices, translationMatrix } from "./matrix.js";
12
+ import { interpretContentStream } from "./interpret.js";
13
+ import "./write.js";
14
+ import { LAYOUT_FORMAT_VERSION } from "document-schema.js";
15
+ //#region src/read.ts
16
+ const PDF_HEADER_BYTES = new TextEncoder().encode("%PDF-");
17
+ const HEADER_SEARCH_WINDOW = 1024;
18
+ const DEFAULT_PAGE_WIDTH_PT = 612;
19
+ const DEFAULT_PAGE_HEIGHT_PT = 792;
20
+ function hasPdfHeader(bytes) {
21
+ const window = bytes.subarray(0, Math.min(HEADER_SEARCH_WINDOW, bytes.length));
22
+ outer: for (let i = 0; i <= window.length - PDF_HEADER_BYTES.length; i++) {
23
+ for (let j = 0; j < PDF_HEADER_BYTES.length; j++) if (window[i + j] !== PDF_HEADER_BYTES[j]) continue outer;
24
+ return true;
25
+ }
26
+ return false;
27
+ }
28
+ function readPdf(bytes, options) {
29
+ const sink = options?.sink ?? NOOP_DIAGNOSTIC_SINK;
30
+ const signal = options?.signal;
31
+ 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");
32
+ const doc = openPdfDocument(bytes, sink);
33
+ const fontResolver = createFontResolver({
34
+ resolver: doc,
35
+ sink
36
+ });
37
+ const images = {};
38
+ const imageIdCache = /* @__PURE__ */ new Map();
39
+ const pages = doc.pages().map((pageDict) => {
40
+ throwIfAborted(signal);
41
+ return readPage(pageDict, doc, fontResolver, images, imageIdCache, sink);
42
+ });
43
+ return {
44
+ formatVersion: LAYOUT_FORMAT_VERSION,
45
+ metadata: readMetadata(doc.trailer, doc),
46
+ pages,
47
+ images
48
+ };
49
+ }
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
+ };
58
+ const a = asNumber(arr[0]) ?? 0;
59
+ 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;
62
+ return {
63
+ llx: Math.min(a, c),
64
+ lly: Math.min(b, d),
65
+ urx: Math.max(a, c),
66
+ ury: Math.max(b, d)
67
+ };
68
+ }
69
+ function normalizeRotation(rotate) {
70
+ if (rotate === void 0) return 0;
71
+ const normalized = (Math.round(rotate / 90) * 90 % 360 + 360) % 360;
72
+ return normalized === 90 || normalized === 180 || normalized === 270 ? normalized : 0;
73
+ }
74
+ function pageRotationTransform(rotation, w, h) {
75
+ if (rotation === 90) return {
76
+ matrix: [
77
+ 0,
78
+ -1,
79
+ 1,
80
+ 0,
81
+ 0,
82
+ w
83
+ ],
84
+ widthPt: h,
85
+ heightPt: w
86
+ };
87
+ if (rotation === 180) return {
88
+ matrix: [
89
+ -1,
90
+ 0,
91
+ 0,
92
+ -1,
93
+ w,
94
+ h
95
+ ],
96
+ widthPt: w,
97
+ heightPt: h
98
+ };
99
+ if (rotation === 270) return {
100
+ matrix: [
101
+ 0,
102
+ 1,
103
+ -1,
104
+ 0,
105
+ h,
106
+ 0
107
+ ],
108
+ widthPt: h,
109
+ heightPt: w
110
+ };
111
+ return {
112
+ matrix: [
113
+ 1,
114
+ 0,
115
+ 0,
116
+ 1,
117
+ 0,
118
+ 0
119
+ ],
120
+ widthPt: w,
121
+ heightPt: h
122
+ };
123
+ }
124
+ function readPageContentBytes(page, resolver, sink) {
125
+ const contentsObj = resolver.resolve(dictGet(page, "Contents"));
126
+ if (contentsObj?.kind === "stream") return decodeStream(contentsObj.raw, contentsObj.dict, sink).bytes;
127
+ if (contentsObj?.kind === "array") {
128
+ const chunks = [];
129
+ for (const item of contentsObj.items) {
130
+ const streamObj = resolver.resolve(item);
131
+ if (streamObj?.kind === "stream") chunks.push(decodeStream(streamObj.raw, streamObj.dict, sink).bytes, new Uint8Array([10]));
132
+ }
133
+ return concatBytes(chunks);
134
+ }
135
+ return /* @__PURE__ */ new Uint8Array(0);
136
+ }
137
+ function readPage(page, resolver, fontResolver, images, imageIdCache, sink) {
138
+ const resources = resolver.resolveDict(dictGet(page, "Resources"));
139
+ const mediaBox = readMediaBox(page);
140
+ 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);
142
+ const items = [];
143
+ if (resources !== void 0) {
144
+ const contentBytes = readPageContentBytes(page, resolver, sink);
145
+ const extracted = interpretContentStream(contentBytes, resources, {
146
+ fontMetrics: fontResolver.metrics,
147
+ resolver,
148
+ sink
149
+ });
150
+ for (const item of extracted) {
151
+ const converted = convertExtractedItem(item, pageMatrix, fontResolver, images, imageIdCache, resolver, sink);
152
+ if (converted !== void 0) items.push(converted);
153
+ }
154
+ } else sink({
155
+ code: "pdf/object-missing-value",
156
+ severity: "warning",
157
+ message: "page has no /Resources dict; its content stream cannot be interpreted"
158
+ });
159
+ items.push(...readLinkAnnotations(page, pageMatrix, resolver));
160
+ const notes = readPageNotes(page, resolver);
161
+ return {
162
+ widthPt: rotationResult.widthPt,
163
+ heightPt: rotationResult.heightPt,
164
+ items,
165
+ ...notes !== void 0 ? { notes } : {}
166
+ };
167
+ }
168
+ function convertExtractedItem(item, pageMatrix, fontResolver, images, imageIdCache, resolver, sink) {
169
+ if (item.kind === "text") return convertText(item, pageMatrix, fontResolver);
170
+ if (item.kind === "rect") return convertRect(item, pageMatrix);
171
+ if (item.kind === "path") return convertPath(item, pageMatrix);
172
+ if (item.kind === "image") return convertImage(item, pageMatrix, images, imageIdCache, resolver, sink);
173
+ return convertInlineImage(item, pageMatrix, images, resolver, sink);
174
+ }
175
+ function convertText(item, pageMatrix, fontResolver) {
176
+ const font = fontResolver.resolve(item.fontResourceName, item.resources);
177
+ const text = font?.decodeToUnicode(item.codes) ?? "";
178
+ if (text.length === 0) return;
179
+ const startTrm = multiplyMatrices(item.startMatrix, pageMatrix);
180
+ const endTrm = multiplyMatrices(item.endMatrix, pageMatrix);
181
+ const widthPt = Math.hypot(endTrm[4] - startTrm[4], endTrm[5] - startTrm[5]);
182
+ const sizePt = matrixScaleX(startTrm);
183
+ const rotationDeg = matrixRotationDegrees(startTrm);
184
+ const layoutFont = {
185
+ family: font?.family ?? "Helvetica",
186
+ weight: font?.bold === true ? "bold" : "normal",
187
+ style: font?.italic === true ? "italic" : "normal"
188
+ };
189
+ return {
190
+ kind: "text",
191
+ text,
192
+ xPt: startTrm[4],
193
+ yPt: startTrm[5],
194
+ font: layoutFont,
195
+ sizePt: sizePt > 0 ? sizePt : item.sizePt,
196
+ color: item.color,
197
+ widthPt,
198
+ rotationDeg: rotationDeg !== 0 ? rotationDeg : void 0
199
+ };
200
+ }
201
+ function convertRect(item, pageMatrix) {
202
+ const p1 = applyMatrix(pageMatrix, {
203
+ x: item.xPt,
204
+ y: item.yPt
205
+ });
206
+ const p2 = applyMatrix(pageMatrix, {
207
+ x: item.xPt + item.widthPt,
208
+ y: item.yPt + item.heightPt
209
+ });
210
+ return {
211
+ kind: "rect",
212
+ xPt: Math.min(p1.x, p2.x),
213
+ yPt: Math.min(p1.y, p2.y),
214
+ widthPt: Math.abs(p2.x - p1.x),
215
+ heightPt: Math.abs(p2.y - p1.y),
216
+ fill: item.color
217
+ };
218
+ }
219
+ function transformSubpath(subpath, pageMatrix) {
220
+ const start = applyMatrix(pageMatrix, {
221
+ x: subpath.startXPt,
222
+ y: subpath.startYPt
223
+ });
224
+ const segments = subpath.segments.map((segment) => {
225
+ if (segment.kind === "line") {
226
+ const p = applyMatrix(pageMatrix, {
227
+ x: segment.xPt,
228
+ y: segment.yPt
229
+ });
230
+ return {
231
+ kind: "line",
232
+ xPt: p.x,
233
+ yPt: p.y
234
+ };
235
+ }
236
+ const c1 = applyMatrix(pageMatrix, {
237
+ x: segment.c1xPt,
238
+ y: segment.c1yPt
239
+ });
240
+ const c2 = applyMatrix(pageMatrix, {
241
+ x: segment.c2xPt,
242
+ y: segment.c2yPt
243
+ });
244
+ const p = applyMatrix(pageMatrix, {
245
+ x: segment.xPt,
246
+ y: segment.yPt
247
+ });
248
+ return {
249
+ kind: "cubic",
250
+ c1xPt: c1.x,
251
+ c1yPt: c1.y,
252
+ c2xPt: c2.x,
253
+ c2yPt: c2.y,
254
+ xPt: p.x,
255
+ yPt: p.y
256
+ };
257
+ });
258
+ return {
259
+ startXPt: start.x,
260
+ startYPt: start.y,
261
+ segments,
262
+ closed: subpath.closed
263
+ };
264
+ }
265
+ function convertPath(item, pageMatrix) {
266
+ return {
267
+ kind: "path",
268
+ subpaths: item.subpaths.map((subpath) => transformSubpath(subpath, pageMatrix)),
269
+ ...item.fill !== void 0 ? { fill: item.fill } : {},
270
+ ...item.fill !== void 0 && item.fillRule === "evenodd" ? { fillRule: "evenodd" } : {},
271
+ ...item.stroke !== void 0 ? { stroke: item.stroke } : {}
272
+ };
273
+ }
274
+ function imagePlacementFrom(matrix) {
275
+ const rotationDeg = matrixRotationDegrees(matrix);
276
+ return {
277
+ xPt: matrix[4],
278
+ yPt: matrix[5],
279
+ widthPt: matrixScaleX(matrix),
280
+ heightPt: matrixScaleY(matrix),
281
+ rotationDeg: rotationDeg !== 0 ? rotationDeg : void 0
282
+ };
283
+ }
284
+ function registerExtractedImage(format, bytes, widthPx, heightPx, images) {
285
+ const imageId = `img${crc32(bytes).toString(16)}`;
286
+ if (!(imageId in images)) images[imageId] = {
287
+ format,
288
+ base64: bytesToBase64(bytes),
289
+ widthPx,
290
+ heightPx
291
+ };
292
+ return imageId;
293
+ }
294
+ function resolveCachedImageId(dict, raw, images, cache, resolver, sink) {
295
+ if (cache.has(dict)) return cache.get(dict) ?? void 0;
296
+ const decoded = readImageXObject(dict, raw, resolver, sink);
297
+ if (decoded === void 0) {
298
+ cache.set(dict, null);
299
+ return;
300
+ }
301
+ const imageId = registerExtractedImage(decoded.format, decoded.bytes, decoded.widthPx, decoded.heightPx, images);
302
+ cache.set(dict, imageId);
303
+ return imageId;
304
+ }
305
+ function convertImage(item, pageMatrix, images, cache, resolver, sink) {
306
+ const xobjects = resolver.resolveDict(dictGet(item.resources, "XObject"));
307
+ const xobj = xobjects !== void 0 ? resolver.resolve(dictGet(xobjects, item.resourceName)) : void 0;
308
+ if (xobj?.kind !== "stream") return;
309
+ const imageId = resolveCachedImageId(xobj.dict, xobj.raw, images, cache, resolver, sink);
310
+ if (imageId === void 0) return;
311
+ return {
312
+ kind: "image",
313
+ imageId,
314
+ ...imagePlacementFrom(multiplyMatrices(item.matrix, pageMatrix))
315
+ };
316
+ }
317
+ function convertInlineImage(item, pageMatrix, images, resolver, sink) {
318
+ const decoded = readImageXObject(item.dict, item.data, resolver, sink);
319
+ if (decoded === void 0) return;
320
+ return {
321
+ kind: "image",
322
+ imageId: registerExtractedImage(decoded.format, decoded.bytes, decoded.widthPx, decoded.heightPx, images),
323
+ ...imagePlacementFrom(multiplyMatrices(item.matrix, pageMatrix))
324
+ };
325
+ }
326
+ function readLinkAnnotations(page, pageMatrix, resolver) {
327
+ const annotsArr = asArray(dictGet(page, "Annots"));
328
+ if (annotsArr === void 0) return [];
329
+ const links = [];
330
+ for (const annotRef of annotsArr) {
331
+ const annot = resolver.resolveDict(annotRef);
332
+ if (annot === void 0 || asName(dictGet(annot, "Subtype")) !== "Link") continue;
333
+ const uri = readLinkUri(annot, resolver);
334
+ const rectArr = asArray(dictGet(annot, "Rect"));
335
+ if (uri === void 0 || rectArr === void 0) continue;
336
+ const x1 = asNumber(rectArr[0]) ?? 0;
337
+ const y1 = asNumber(rectArr[1]) ?? 0;
338
+ const x2 = asNumber(rectArr[2]) ?? 0;
339
+ const y2 = asNumber(rectArr[3]) ?? 0;
340
+ const p1 = applyMatrix(pageMatrix, {
341
+ x: Math.min(x1, x2),
342
+ y: Math.min(y1, y2)
343
+ });
344
+ const p2 = applyMatrix(pageMatrix, {
345
+ x: Math.max(x1, x2),
346
+ y: Math.max(y1, y2)
347
+ });
348
+ links.push({
349
+ kind: "link",
350
+ uri,
351
+ xPt: Math.min(p1.x, p2.x),
352
+ yPt: Math.min(p1.y, p2.y),
353
+ widthPt: Math.abs(p2.x - p1.x),
354
+ heightPt: Math.abs(p2.y - p1.y)
355
+ });
356
+ }
357
+ return links;
358
+ }
359
+ function readLinkUri(annot, resolver) {
360
+ const action = resolver.resolveDict(dictGet(annot, "A"));
361
+ if (action === void 0 || asName(dictGet(action, "S")) !== "URI") return;
362
+ const uriObj = dictGet(action, "URI");
363
+ return uriObj?.kind === "string" ? decodePdfString(uriObj.bytes) : void 0;
364
+ }
365
+ function readPageNotes(page, resolver) {
366
+ const annotsArr = asArray(dictGet(page, "Annots"));
367
+ if (annotsArr === void 0) return;
368
+ for (const annotRef of annotsArr) {
369
+ const annot = resolver.resolveDict(annotRef);
370
+ if (annot === void 0 || asName(dictGet(annot, "Subtype")) !== "Text") continue;
371
+ const titleObj = dictGet(annot, "T");
372
+ if ((titleObj?.kind === "string" ? decodePdfString(titleObj.bytes) : void 0) !== "documents.js:notes") continue;
373
+ const contentsObj = dictGet(annot, "Contents");
374
+ if (contentsObj?.kind === "string") return decodePdfString(contentsObj.bytes);
375
+ }
376
+ }
377
+ function decodePdfString(bytes) {
378
+ if (bytes.length >= 2 && bytes[0] === 254 && bytes[1] === 255) {
379
+ let out = "";
380
+ for (let i = 2; i + 1 < bytes.length; i += 2) out += String.fromCharCode((bytes[i] ?? 0) << 8 | (bytes[i + 1] ?? 0));
381
+ return out;
382
+ }
383
+ return Array.from(bytes, (b) => String.fromCharCode(b)).join("");
384
+ }
385
+ const PDF_DATE_PATTERN = /^D:(\d{4})(\d{2})?(\d{2})?(\d{2})?(\d{2})?(\d{2})?([+\-Z])?(\d{2})?'?(\d{2})?'?$/;
386
+ function parsePdfDate(raw) {
387
+ if (raw === void 0) return;
388
+ const match = PDF_DATE_PATTERN.exec(raw);
389
+ if (match === null) return;
390
+ const [, year, month = "01", day = "01", hour = "00", minute = "00", second = "00", tzSign, tzHour = "00", tzMinute = "00"] = match;
391
+ return `${year}-${month}-${day}T${hour}:${minute}:${second}${tzSign === void 0 || tzSign === "Z" ? "Z" : `${tzSign}${tzHour}:${tzMinute}`}`;
392
+ }
393
+ function readMetadata(trailer, resolver) {
394
+ const info = resolver.resolveDict(dictGet(trailer, "Info"));
395
+ if (info === void 0) return {};
396
+ const stringField = (key) => {
397
+ const obj = dictGet(info, key);
398
+ return obj?.kind === "string" ? decodePdfString(obj.bytes) : void 0;
399
+ };
400
+ const keywords = stringField("Keywords")?.split(",").map((k) => k.trim()).filter((k) => k.length > 0);
401
+ return {
402
+ title: stringField("Title"),
403
+ author: stringField("Author"),
404
+ subject: stringField("Subject"),
405
+ keywords: keywords !== void 0 && keywords.length > 0 ? keywords : void 0,
406
+ creator: stringField("Creator"),
407
+ producer: stringField("Producer"),
408
+ createdIso: parsePdfDate(stringField("CreationDate")),
409
+ modifiedIso: parsePdfDate(stringField("ModDate"))
410
+ };
411
+ }
412
+ //#endregion
413
+ export { decodePdfString, normalizeRotation, pageRotationTransform, parsePdfDate, readPdf };
@@ -0,0 +1,69 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_bytes_writer = require("./bytes/writer.cjs");
3
+ const require_objects = require("./objects.cjs");
4
+ //#region src/serialize.ts
5
+ const NUMBER_DECIMAL_PLACES = 4;
6
+ const NUMBER_EPSILON = 10 ** -4;
7
+ function formatNumber(n) {
8
+ if (Math.abs(n) < NUMBER_EPSILON) return "0";
9
+ let formatted = n.toFixed(NUMBER_DECIMAL_PLACES);
10
+ if (formatted.includes(".")) formatted = formatted.replace(/0+$/, "").replace(/\.$/, "");
11
+ return formatted === "-0" ? "0" : formatted;
12
+ }
13
+ const NAME_ESCAPE_PATTERN = /[^!-~]|[#()<>[\]{}/%]/;
14
+ function escapeName(name) {
15
+ if (!NAME_ESCAPE_PATTERN.test(name)) return name;
16
+ let out = "";
17
+ for (const ch of name) {
18
+ const code = ch.codePointAt(0);
19
+ if (code < 33 || code > 126 || "#()<>[]{}/%".includes(ch)) out += `#${code.toString(16).padStart(2, "0")}`;
20
+ else out += ch;
21
+ }
22
+ return out;
23
+ }
24
+ function bytesToHex(bytes) {
25
+ let out = "";
26
+ for (const byte of bytes) out += byte.toString(16).padStart(2, "0");
27
+ return out;
28
+ }
29
+ function writeDictBody(writer, entries) {
30
+ writer.writeAscii("<<");
31
+ for (const [key, value] of entries) {
32
+ writer.writeAscii(`/${escapeName(key)} `);
33
+ writeObject(writer, value);
34
+ writer.writeAscii(" ");
35
+ }
36
+ writer.writeAscii(">>");
37
+ }
38
+ function writeObject(writer, obj) {
39
+ if (obj.kind === "null") writer.writeAscii("null");
40
+ else if (obj.kind === "bool") writer.writeAscii(obj.value ? "true" : "false");
41
+ else if (obj.kind === "number") writer.writeAscii(formatNumber(obj.value));
42
+ else if (obj.kind === "name") writer.writeAscii(`/${escapeName(obj.name)}`);
43
+ else if (obj.kind === "string") writer.writeAscii(`<${bytesToHex(obj.bytes)}>`);
44
+ else if (obj.kind === "array") {
45
+ writer.writeAscii("[");
46
+ obj.items.forEach((item, i) => {
47
+ if (i > 0) writer.writeAscii(" ");
48
+ writeObject(writer, item);
49
+ });
50
+ writer.writeAscii("]");
51
+ } else if (obj.kind === "dict") writeDictBody(writer, obj.entries);
52
+ else if (obj.kind === "stream") {
53
+ const entries = new Map(obj.dict.entries);
54
+ entries.set("Length", require_objects.pdfNum(obj.raw.length));
55
+ writeDictBody(writer, entries);
56
+ writer.writeAscii("\nstream\n");
57
+ writer.writeBytes(obj.raw);
58
+ writer.writeAscii("\nendstream");
59
+ } else writer.writeAscii(`${obj.num} ${obj.gen} R`);
60
+ }
61
+ function serializeObject(obj) {
62
+ const writer = new require_bytes_writer.ByteWriter();
63
+ writeObject(writer, obj);
64
+ return writer.toBytes();
65
+ }
66
+ //#endregion
67
+ exports.formatNumber = formatNumber;
68
+ exports.serializeObject = serializeObject;
69
+ exports.writeObject = writeObject;
@@ -0,0 +1,8 @@
1
+ import { ByteWriter } from "./bytes/writer.cjs";
2
+ import { PdfObject } from "./objects.cjs";
3
+ //#region src/serialize.d.ts
4
+ declare function formatNumber(n: number): string;
5
+ declare function writeObject(writer: ByteWriter, obj: PdfObject): void;
6
+ declare function serializeObject(obj: PdfObject): Uint8Array<ArrayBuffer>;
7
+ //#endregion
8
+ export { formatNumber, serializeObject, writeObject };
@@ -0,0 +1,8 @@
1
+ import { ByteWriter } from "./bytes/writer.js";
2
+ import { PdfObject } from "./objects.js";
3
+ //#region src/serialize.d.ts
4
+ declare function formatNumber(n: number): string;
5
+ declare function writeObject(writer: ByteWriter, obj: PdfObject): void;
6
+ declare function serializeObject(obj: PdfObject): Uint8Array<ArrayBuffer>;
7
+ //#endregion
8
+ export { formatNumber, serializeObject, writeObject };
@@ -0,0 +1,66 @@
1
+ import { ByteWriter } from "./bytes/writer.js";
2
+ import { pdfNum } from "./objects.js";
3
+ //#region src/serialize.ts
4
+ const NUMBER_DECIMAL_PLACES = 4;
5
+ const NUMBER_EPSILON = 10 ** -4;
6
+ function formatNumber(n) {
7
+ if (Math.abs(n) < NUMBER_EPSILON) return "0";
8
+ let formatted = n.toFixed(NUMBER_DECIMAL_PLACES);
9
+ if (formatted.includes(".")) formatted = formatted.replace(/0+$/, "").replace(/\.$/, "");
10
+ return formatted === "-0" ? "0" : formatted;
11
+ }
12
+ const NAME_ESCAPE_PATTERN = /[^!-~]|[#()<>[\]{}/%]/;
13
+ function escapeName(name) {
14
+ if (!NAME_ESCAPE_PATTERN.test(name)) return name;
15
+ let out = "";
16
+ for (const ch of name) {
17
+ const code = ch.codePointAt(0);
18
+ if (code < 33 || code > 126 || "#()<>[]{}/%".includes(ch)) out += `#${code.toString(16).padStart(2, "0")}`;
19
+ else out += ch;
20
+ }
21
+ return out;
22
+ }
23
+ function bytesToHex(bytes) {
24
+ let out = "";
25
+ for (const byte of bytes) out += byte.toString(16).padStart(2, "0");
26
+ return out;
27
+ }
28
+ function writeDictBody(writer, entries) {
29
+ writer.writeAscii("<<");
30
+ for (const [key, value] of entries) {
31
+ writer.writeAscii(`/${escapeName(key)} `);
32
+ writeObject(writer, value);
33
+ writer.writeAscii(" ");
34
+ }
35
+ writer.writeAscii(">>");
36
+ }
37
+ function writeObject(writer, obj) {
38
+ if (obj.kind === "null") writer.writeAscii("null");
39
+ else if (obj.kind === "bool") writer.writeAscii(obj.value ? "true" : "false");
40
+ else if (obj.kind === "number") writer.writeAscii(formatNumber(obj.value));
41
+ else if (obj.kind === "name") writer.writeAscii(`/${escapeName(obj.name)}`);
42
+ else if (obj.kind === "string") writer.writeAscii(`<${bytesToHex(obj.bytes)}>`);
43
+ else if (obj.kind === "array") {
44
+ writer.writeAscii("[");
45
+ obj.items.forEach((item, i) => {
46
+ if (i > 0) writer.writeAscii(" ");
47
+ writeObject(writer, item);
48
+ });
49
+ writer.writeAscii("]");
50
+ } else if (obj.kind === "dict") writeDictBody(writer, obj.entries);
51
+ else if (obj.kind === "stream") {
52
+ const entries = new Map(obj.dict.entries);
53
+ entries.set("Length", pdfNum(obj.raw.length));
54
+ writeDictBody(writer, entries);
55
+ writer.writeAscii("\nstream\n");
56
+ writer.writeBytes(obj.raw);
57
+ writer.writeAscii("\nendstream");
58
+ } else writer.writeAscii(`${obj.num} ${obj.gen} R`);
59
+ }
60
+ function serializeObject(obj) {
61
+ const writer = new ByteWriter();
62
+ writeObject(writer, obj);
63
+ return writer.toBytes();
64
+ }
65
+ //#endregion
66
+ export { formatNumber, serializeObject, writeObject };
package/dist/sfnt.cjs ADDED
@@ -0,0 +1,52 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/sfnt.ts
3
+ function readUint16(bytes, offset) {
4
+ return bytes[offset] << 8 | bytes[offset + 1];
5
+ }
6
+ function readUint32(bytes, offset) {
7
+ return (bytes[offset] << 24 | bytes[offset + 1] << 16 | bytes[offset + 2] << 8 | bytes[offset + 3]) >>> 0;
8
+ }
9
+ const TABLE_DIRECTORY_HEADER_SIZE = 12;
10
+ const TABLE_RECORD_SIZE = 16;
11
+ function parseSfnt(bytes) {
12
+ const numTables = readUint16(bytes, 4);
13
+ const tables = /* @__PURE__ */ new Map();
14
+ for (let i = 0; i < numTables; i++) {
15
+ const recordOffset = TABLE_DIRECTORY_HEADER_SIZE + i * TABLE_RECORD_SIZE;
16
+ const tag = new TextDecoder("ascii").decode(bytes.subarray(recordOffset, recordOffset + 4));
17
+ const offset = readUint32(bytes, recordOffset + 8);
18
+ const length = readUint32(bytes, recordOffset + 12);
19
+ tables.set(tag, {
20
+ offset,
21
+ length
22
+ });
23
+ }
24
+ return {
25
+ bytes,
26
+ tables
27
+ };
28
+ }
29
+ function sfntTableBytes(font, tag) {
30
+ const table = font.tables.get(tag);
31
+ return table === void 0 ? void 0 : font.bytes.subarray(table.offset, table.offset + table.length);
32
+ }
33
+ function u8(bytes, offset) {
34
+ return bytes[offset];
35
+ }
36
+ function u16(bytes, offset) {
37
+ return readUint16(bytes, offset);
38
+ }
39
+ function i16(bytes, offset) {
40
+ const value = readUint16(bytes, offset);
41
+ return value >= 32768 ? value - 65536 : value;
42
+ }
43
+ function u32(bytes, offset) {
44
+ return readUint32(bytes, offset);
45
+ }
46
+ //#endregion
47
+ exports.i16 = i16;
48
+ exports.parseSfnt = parseSfnt;
49
+ exports.sfntTableBytes = sfntTableBytes;
50
+ exports.u16 = u16;
51
+ exports.u32 = u32;
52
+ exports.u8 = u8;
@@ -0,0 +1,17 @@
1
+ //#region src/sfnt.d.ts
2
+ interface SfntTable {
3
+ readonly offset: number;
4
+ readonly length: number;
5
+ }
6
+ interface SfntFont {
7
+ readonly bytes: Uint8Array<ArrayBuffer>;
8
+ readonly tables: ReadonlyMap<string, SfntTable>;
9
+ }
10
+ declare function parseSfnt(bytes: Uint8Array<ArrayBuffer>): SfntFont;
11
+ declare function sfntTableBytes(font: SfntFont, tag: string): Uint8Array<ArrayBuffer> | undefined;
12
+ declare function u8(bytes: Uint8Array<ArrayBuffer>, offset: number): number;
13
+ declare function u16(bytes: Uint8Array<ArrayBuffer>, offset: number): number;
14
+ declare function i16(bytes: Uint8Array<ArrayBuffer>, offset: number): number;
15
+ declare function u32(bytes: Uint8Array<ArrayBuffer>, offset: number): number;
16
+ //#endregion
17
+ export { SfntFont, SfntTable, i16, parseSfnt, sfntTableBytes, u16, u32, u8 };
package/dist/sfnt.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ //#region src/sfnt.d.ts
2
+ interface SfntTable {
3
+ readonly offset: number;
4
+ readonly length: number;
5
+ }
6
+ interface SfntFont {
7
+ readonly bytes: Uint8Array<ArrayBuffer>;
8
+ readonly tables: ReadonlyMap<string, SfntTable>;
9
+ }
10
+ declare function parseSfnt(bytes: Uint8Array<ArrayBuffer>): SfntFont;
11
+ declare function sfntTableBytes(font: SfntFont, tag: string): Uint8Array<ArrayBuffer> | undefined;
12
+ declare function u8(bytes: Uint8Array<ArrayBuffer>, offset: number): number;
13
+ declare function u16(bytes: Uint8Array<ArrayBuffer>, offset: number): number;
14
+ declare function i16(bytes: Uint8Array<ArrayBuffer>, offset: number): number;
15
+ declare function u32(bytes: Uint8Array<ArrayBuffer>, offset: number): number;
16
+ //#endregion
17
+ export { SfntFont, SfntTable, i16, parseSfnt, sfntTableBytes, u16, u32, u8 };