unpdf 1.6.2 → 1.8.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 (122) hide show
  1. package/README.md +61 -25
  2. package/dist/index.cjs +386 -353
  3. package/dist/index.d.cts +101 -91
  4. package/dist/index.d.mts +101 -91
  5. package/dist/index.mjs +385 -351
  6. package/dist/pdfjs.d.mts +1 -1
  7. package/dist/pdfjs.d.ts +1 -1
  8. package/dist/pdfjs.mjs +52 -59
  9. package/dist/types/external/brotli/decode.d.ts +5 -0
  10. package/dist/types/external/jbig2/jbig2.d.ts +2 -0
  11. package/dist/types/external/openjpeg/openjpeg.d.ts +2 -0
  12. package/dist/types/external/qcms/qcms.d.ts +92 -0
  13. package/dist/types/external/qcms/qcms_utils.d.ts +14 -0
  14. package/dist/types/src/core/ascii_85_stream.d.ts +7 -0
  15. package/dist/types/src/core/ascii_hex_stream.d.ts +7 -0
  16. package/dist/types/src/core/base_stream.d.ts +45 -0
  17. package/dist/types/src/core/brotli_stream.d.ts +9 -0
  18. package/dist/types/src/core/calculate_md5.d.ts +1 -0
  19. package/dist/types/src/core/calculate_sha256.d.ts +1 -0
  20. package/dist/types/src/core/calculate_sha_other.d.ts +2 -0
  21. package/dist/types/src/core/catalog.d.ts +169 -0
  22. package/dist/types/src/core/ccitt_stream.d.ts +18 -0
  23. package/dist/types/src/core/cleanup_helper.d.ts +1 -0
  24. package/dist/types/src/core/colorspace.d.ts +177 -0
  25. package/dist/types/src/core/colorspace_utils.d.ts +21 -0
  26. package/dist/types/src/core/core_utils.d.ts +132 -0
  27. package/dist/types/src/core/crypto.d.ts +136 -0
  28. package/dist/types/src/core/decode_stream.d.ts +32 -0
  29. package/dist/types/src/core/decrypt_stream.d.ts +9 -0
  30. package/dist/types/src/core/file_spec.d.ts +72 -0
  31. package/dist/types/src/core/flate_stream.d.ts +14 -0
  32. package/dist/types/src/core/icc_colorspace.d.ts +22 -0
  33. package/dist/types/src/core/image_utils.d.ts +52 -0
  34. package/dist/types/src/core/jbig2_ccittFax.d.ts +13 -0
  35. package/dist/types/src/core/jbig2_stream.d.ts +17 -0
  36. package/dist/types/src/core/jpeg_stream.d.ts +24 -0
  37. package/dist/types/src/core/jpg.d.ts +62 -0
  38. package/dist/types/src/core/jpx.d.ts +24 -0
  39. package/dist/types/src/core/jpx_stream.d.ts +15 -0
  40. package/dist/types/src/core/lzw_stream.d.ts +19 -0
  41. package/dist/types/src/core/metadata_parser.d.ts +13 -0
  42. package/dist/types/src/core/name_number_tree.d.ts +21 -0
  43. package/dist/types/src/core/parser.d.ts +121 -0
  44. package/dist/types/src/core/pattern.d.ts +63 -0
  45. package/dist/types/src/core/predictor_stream.d.ts +14 -0
  46. package/dist/types/src/core/primitives.d.ts +81 -0
  47. package/dist/types/src/core/run_length_stream.d.ts +6 -0
  48. package/dist/types/src/core/stream.d.ts +20 -0
  49. package/dist/types/src/core/string_utils.d.ts +5 -0
  50. package/dist/types/src/core/struct_tree.d.ts +93 -0
  51. package/dist/types/src/core/unicode.d.ts +5 -0
  52. package/dist/types/src/core/wasm_image.d.ts +21 -0
  53. package/dist/types/src/core/xml_parser.d.ts +79 -0
  54. package/dist/types/src/core/xref.d.ts +52 -0
  55. package/dist/types/src/display/annotation_layer.d.ts +11 -9
  56. package/dist/types/src/display/api.d.ts +92 -65
  57. package/dist/types/src/display/binary_data_factory.d.ts +0 -4
  58. package/dist/types/src/display/canvas.d.ts +58 -12
  59. package/dist/types/src/display/canvas_dependency_tracker.d.ts +2 -2
  60. package/dist/types/src/display/display_utils.d.ts +4 -156
  61. package/dist/types/src/display/draw_layer.d.ts +127 -2
  62. package/dist/types/src/display/editor/alt_text.d.ts +1 -1
  63. package/dist/types/src/display/editor/annotation_editor_layer.d.ts +3 -3
  64. package/dist/types/src/display/editor/color_picker.d.ts +3 -2
  65. package/dist/types/src/display/editor/draw.d.ts +10 -3
  66. package/dist/types/src/display/editor/drawers/freedraw.d.ts +3 -3
  67. package/dist/types/src/display/editor/drawers/highlight.d.ts +0 -1
  68. package/dist/types/src/display/editor/drawers/outline.d.ts +0 -1
  69. package/dist/types/src/display/editor/drawers/signaturedraw.d.ts +13 -13
  70. package/dist/types/src/display/editor/editor.d.ts +2 -1
  71. package/dist/types/src/display/editor/freetext.d.ts +2 -2
  72. package/dist/types/src/display/editor/highlight.d.ts +3 -3
  73. package/dist/types/src/display/editor/ink.d.ts +2 -0
  74. package/dist/types/src/display/editor/toolbar.d.ts +2 -2
  75. package/dist/types/src/display/editor/tools.d.ts +31 -14
  76. package/dist/types/src/display/filter_factory.d.ts +26 -0
  77. package/dist/types/src/display/node_utils.d.ts +2 -2
  78. package/dist/types/src/display/obj_bin_transform_display.d.ts +2 -6
  79. package/dist/types/src/display/optional_content_config.d.ts +13 -3
  80. package/dist/types/src/display/page_viewport.d.ts +137 -0
  81. package/dist/types/src/display/pattern_helper.d.ts +11 -4
  82. package/dist/types/src/display/text_layer.d.ts +13 -13
  83. package/dist/types/src/display/text_layer_images.d.ts +1 -1
  84. package/dist/types/src/display/webgpu.d.ts +11 -0
  85. package/dist/types/src/display/worker_options.d.ts +2 -2
  86. package/dist/types/src/display/xfa_layer.d.ts +10 -3
  87. package/dist/types/src/display/xfa_text.d.ts +2 -2
  88. package/dist/types/src/pdf.d.ts +10 -9
  89. package/dist/types/src/shared/image_utils.d.ts +11 -0
  90. package/dist/types/src/shared/internal_evt.d.ts +7 -0
  91. package/dist/types/src/shared/math_clamp.d.ts +1 -0
  92. package/dist/types/src/shared/util.d.ts +24 -13
  93. package/dist/types/web/annotation_editor_layer_builder.d.ts +5 -5
  94. package/dist/types/web/annotation_layer_builder.d.ts +14 -11
  95. package/dist/types/web/app_options.d.ts +1 -1
  96. package/dist/types/web/autolinker.d.ts +3 -3
  97. package/dist/types/web/base_pdf_page_view.d.ts +1 -0
  98. package/dist/types/web/comment_manager.d.ts +1 -1
  99. package/dist/types/web/draw_layer_builder.d.ts +44 -0
  100. package/dist/types/web/event_utils.d.ts +0 -8
  101. package/dist/types/web/generic_scripting.d.ts +1 -1
  102. package/dist/types/web/genericl10n.d.ts +5 -5
  103. package/dist/types/web/internal_evt.d.ts +7 -0
  104. package/dist/types/web/l10n.d.ts +2 -2
  105. package/dist/types/web/pdf_find_controller.d.ts +16 -5
  106. package/dist/types/web/pdf_history.d.ts +1 -1
  107. package/dist/types/web/pdf_link_service.d.ts +10 -2
  108. package/dist/types/web/pdf_page_view.d.ts +19 -12
  109. package/dist/types/web/pdf_rendering_queue.d.ts +3 -3
  110. package/dist/types/web/pdf_scripting_manager.d.ts +1 -1
  111. package/dist/types/web/pdf_thumbnail_view.d.ts +9 -9
  112. package/dist/types/web/pdf_thumbnail_viewer.d.ts +38 -19
  113. package/dist/types/web/pdf_viewer.d.ts +28 -14
  114. package/dist/types/web/renderable_view.d.ts +2 -2
  115. package/dist/types/web/struct_tree_layer_builder.d.ts +1 -1
  116. package/dist/types/web/text_accessibility.d.ts +1 -1
  117. package/dist/types/web/text_highlighter.d.ts +6 -6
  118. package/dist/types/web/text_layer_builder.d.ts +10 -10
  119. package/dist/types/web/xfa_layer_builder.d.ts +8 -8
  120. package/package.json +25 -24
  121. package/dist/index.d.ts +0 -182
  122. package/dist/types/src/display/webgpu_mesh.d.ts +0 -3
package/dist/index.mjs CHANGED
@@ -1,416 +1,450 @@
1
+ //#region src/_internal/dommatrix-polyfill.ts
1
2
  function polyfillDOMMatrix() {
2
- if (typeof globalThis.DOMMatrix !== "undefined")
3
- return;
4
- globalThis.DOMMatrix = class DOMMatrix {
5
- a;
6
- b;
7
- c;
8
- d;
9
- e;
10
- f;
11
- constructor(init) {
12
- if (Array.isArray(init) && init.length === 6) {
13
- this.a = init[0];
14
- this.b = init[1];
15
- this.c = init[2];
16
- this.d = init[3];
17
- this.e = init[4];
18
- this.f = init[5];
19
- } else {
20
- this.a = 1;
21
- this.b = 0;
22
- this.c = 0;
23
- this.d = 1;
24
- this.e = 0;
25
- this.f = 0;
26
- }
27
- }
28
- translateSelf(tx, ty = 0) {
29
- this.e = this.a * tx + this.c * ty + this.e;
30
- this.f = this.b * tx + this.d * ty + this.f;
31
- return this;
32
- }
33
- scaleSelf(sx, sy = sx) {
34
- this.a *= sx;
35
- this.b *= sx;
36
- this.c *= sy;
37
- this.d *= sy;
38
- return this;
39
- }
40
- };
3
+ if (typeof globalThis.DOMMatrix !== "undefined") return;
4
+ globalThis.DOMMatrix = class DOMMatrix {
5
+ constructor(init) {
6
+ if (Array.isArray(init) && init.length === 6) {
7
+ this.a = init[0];
8
+ this.b = init[1];
9
+ this.c = init[2];
10
+ this.d = init[3];
11
+ this.e = init[4];
12
+ this.f = init[5];
13
+ } else {
14
+ this.a = 1;
15
+ this.b = 0;
16
+ this.c = 0;
17
+ this.d = 1;
18
+ this.e = 0;
19
+ this.f = 0;
20
+ }
21
+ }
22
+ translateSelf(tx, ty = 0) {
23
+ this.e = this.a * tx + this.c * ty + this.e;
24
+ this.f = this.b * tx + this.d * ty + this.f;
25
+ return this;
26
+ }
27
+ scaleSelf(sx, sy = sx) {
28
+ this.a *= sx;
29
+ this.b *= sx;
30
+ this.c *= sy;
31
+ this.d *= sy;
32
+ return this;
33
+ }
34
+ };
41
35
  }
42
-
36
+ //#endregion
37
+ //#region src/_internal/env.ts
38
+ /**
39
+ * Stubs browser globals that PDF.js expects at the module level
40
+ * in non-browser environments.
41
+ *
42
+ * @remarks
43
+ * Must be called before importing any PDF.js module, since PDF.js
44
+ * accesses these globals at parse time (top-level constants).
45
+ */
43
46
  function stubBrowserGlobals() {
44
- polyfillDOMMatrix();
47
+ polyfillDOMMatrix();
45
48
  }
46
-
49
+ //#endregion
50
+ //#region src/utils.ts
47
51
  let resolvedModule;
48
52
  const isNode = globalThis.process?.release?.name === "node";
49
53
  const isBrowser = typeof window !== "undefined";
54
+ /**
55
+ * Returns a PDFDocumentProxy instance from a given binary data.
56
+ *
57
+ * Applies the following defaults:
58
+ * - `useSystemFonts: true`
59
+ *
60
+ * In Node.js environments, additionally applies:
61
+ * - `disableFontFace: true`
62
+ * - `standardFontDataUrl` resolved from the local `pdfjs-dist` package
63
+ * - `cMapUrl` and `cMapPacked` resolved from the local `pdfjs-dist` package
64
+ */
50
65
  async function getDocumentProxy(data, options = {}) {
51
- const { getDocument } = await getResolvedPDFJS();
52
- let nodeDefaults = {};
53
- if (isNode) {
54
- try {
55
- const base = import.meta.resolve("pdfjs-dist/package.json");
56
- nodeDefaults = {
57
- disableFontFace: true,
58
- standardFontDataUrl: new URL("./standard_fonts/", base).href
59
- };
60
- } catch {
61
- }
62
- }
63
- const pdf = await getDocument({
64
- data,
65
- isEvalSupported: false,
66
- // See: https://github.com/mozilla/pdf.js/issues/4244#issuecomment-1479534301
67
- useSystemFonts: true,
68
- ...nodeDefaults,
69
- ...options
70
- }).promise;
71
- return pdf;
66
+ const { getDocument } = await getResolvedPDFJS();
67
+ let nodeDefaults = {};
68
+ if (isNode) try {
69
+ const base = import.meta.resolve("pdfjs-dist/package.json");
70
+ nodeDefaults = {
71
+ disableFontFace: true,
72
+ standardFontDataUrl: new URL("./standard_fonts/", base).href,
73
+ cMapUrl: new URL("./cmaps/", base).href,
74
+ cMapPacked: true
75
+ };
76
+ } catch {}
77
+ return await getDocument({
78
+ data,
79
+ useSystemFonts: true,
80
+ ...nodeDefaults,
81
+ ...options
82
+ }).promise;
83
+ }
84
+ /**
85
+ * Runs an operation on a `PDFDocumentProxy` resolved from either binary data
86
+ * or an existing proxy. Documents created here are destroyed once the
87
+ * operation settles – caller-supplied proxies keep their lifecycle with the
88
+ * caller.
89
+ */
90
+ async function withDocument(data, operation, options) {
91
+ const pdf = isPDFDocumentProxy(data) ? data : await getDocumentProxy(data, options);
92
+ try {
93
+ return await operation(pdf);
94
+ } finally {
95
+ if (pdf !== data) await pdf.loadingTask.destroy();
96
+ }
72
97
  }
73
98
  async function getResolvedPDFJS() {
74
- if (!resolvedModule) {
75
- await resolvePDFJSImport();
76
- }
77
- return resolvedModule;
99
+ if (!resolvedModule) await resolvePDFJSImport();
100
+ return resolvedModule;
78
101
  }
79
102
  async function resolvePDFJSImport(pdfjsResolver, { reload = false } = {}) {
80
- if (resolvedModule && !reload) {
81
- return;
82
- }
83
- stubBrowserGlobals();
84
- if (pdfjsResolver) {
85
- try {
86
- resolvedModule = await interopDefault(pdfjsResolver());
87
- return;
88
- } catch (error) {
89
- throw new Error(`PDF.js could not be resolved: ${error}`);
90
- }
91
- }
92
- try {
93
- resolvedModule = await import('unpdf/pdfjs');
94
- } catch (error) {
95
- throw new Error(`Serverless PDF.js bundle could not be resolved: ${error}`);
96
- }
103
+ if (resolvedModule && !reload) return;
104
+ stubBrowserGlobals();
105
+ if (pdfjsResolver) try {
106
+ resolvedModule = await interopDefault(pdfjsResolver());
107
+ return;
108
+ } catch (error) {
109
+ throw new Error(`PDF.js could not be resolved: ${error}`);
110
+ }
111
+ try {
112
+ resolvedModule = await import("unpdf/pdfjs");
113
+ } catch (error) {
114
+ throw new Error(`Serverless PDF.js bundle could not be resolved: ${error}`);
115
+ }
97
116
  }
98
117
  function isPDFDocumentProxy(data) {
99
- return typeof data === "object" && data !== null && "_pdfInfo" in data;
118
+ return typeof data === "object" && data !== null && "_pdfInfo" in data;
100
119
  }
101
120
  async function interopDefault(m) {
102
- const resolved = await m;
103
- return resolved.default || resolved;
121
+ const resolved = await m;
122
+ return resolved.default || resolved;
104
123
  }
105
-
124
+ //#endregion
125
+ //#region src/_internal/canvas.ts
106
126
  let resolvedCanvasModule;
107
127
  /**
108
- * Derived from the PDF.js project by the Mozilla Foundation.
109
- * @see https://github.com/mozilla/pdf.js/blob/b8de9a372f9bbf7e33adb362eeae5ef1919dba73/src/display/canvas_factory.js#L18
110
- * @license Apache-2.0
111
- */
112
- class BaseCanvasFactory {
113
- #enableHWA = false;
114
- constructor({ enableHWA = false } = {}) {
115
- this.#enableHWA = enableHWA;
116
- }
117
- create(width, height) {
118
- const canvas = this._createCanvas(width, height);
119
- return {
120
- canvas,
121
- context: canvas.getContext("2d", {
122
- willReadFrequently: !this.#enableHWA
123
- })
124
- };
125
- }
126
- reset({ canvas }, width, height) {
127
- if (!canvas) {
128
- throw new Error("Canvas is not specified");
129
- }
130
- canvas.width = width;
131
- canvas.height = height;
132
- }
133
- destroy(context) {
134
- if (!context.canvas) {
135
- throw new Error("Canvas is not specified");
136
- }
137
- context.canvas.width = 0;
138
- context.canvas.height = 0;
139
- context.canvas = void 0;
140
- context.context = void 0;
141
- }
142
- // eslint-disable-next-line unused-imports/no-unused-vars
143
- _createCanvas(width, height) {
144
- throw new Error("Not implemented");
145
- }
146
- }
128
+ * Derived from the PDF.js project by the Mozilla Foundation.
129
+ * @see https://github.com/mozilla/pdf.js/blob/b8de9a372f9bbf7e33adb362eeae5ef1919dba73/src/display/canvas_factory.js#L18
130
+ * @license Apache-2.0
131
+ */
132
+ var BaseCanvasFactory = class {
133
+ #enableHWA = false;
134
+ constructor({ enableHWA = false } = {}) {
135
+ this.#enableHWA = enableHWA;
136
+ }
137
+ create(width, height) {
138
+ const canvas = this._createCanvas(width, height);
139
+ return {
140
+ canvas,
141
+ context: canvas.getContext("2d", { willReadFrequently: !this.#enableHWA })
142
+ };
143
+ }
144
+ reset({ canvas }, width, height) {
145
+ if (!canvas) throw new Error("Canvas is not specified");
146
+ canvas.width = width;
147
+ canvas.height = height;
148
+ }
149
+ destroy(context) {
150
+ if (!context.canvas) throw new Error("Canvas is not specified");
151
+ context.canvas.width = 0;
152
+ context.canvas.height = 0;
153
+ context.canvas = void 0;
154
+ context.context = void 0;
155
+ }
156
+ _createCanvas(width, height) {
157
+ throw new Error("Not implemented");
158
+ }
159
+ };
147
160
  /**
148
- * Derived from the PDF.js project by the Mozilla Foundation.
149
- * @see https://github.com/mozilla/pdf.js/blob/b8de9a372f9bbf7e33adb362eeae5ef1919dba73/src/display/canvas_factory.js#L18
150
- * @license Apache-2.0
151
- */
152
- class DOMCanvasFactory extends BaseCanvasFactory {
153
- _document;
154
- constructor({ ownerDocument = globalThis.document, enableHWA = false } = {}) {
155
- super({ enableHWA });
156
- this._document = ownerDocument;
157
- }
158
- _createCanvas(width, height) {
159
- const canvas = this._document.createElement("canvas");
160
- canvas.width = width;
161
- canvas.height = height;
162
- return canvas;
163
- }
164
- }
165
- class NodeCanvasFactory extends BaseCanvasFactory {
166
- constructor({ enableHWA = false } = {}) {
167
- super({ enableHWA });
168
- }
169
- _createCanvas(width, height) {
170
- if (!resolvedCanvasModule) {
171
- throw new Error("@napi-rs/canvas module is not resolved");
172
- }
173
- return resolvedCanvasModule.createCanvas(width, height);
174
- }
175
- }
161
+ * Derived from the PDF.js project by the Mozilla Foundation.
162
+ * @see https://github.com/mozilla/pdf.js/blob/b8de9a372f9bbf7e33adb362eeae5ef1919dba73/src/display/canvas_factory.js#L18
163
+ * @license Apache-2.0
164
+ */
165
+ var DOMCanvasFactory = class extends BaseCanvasFactory {
166
+ constructor({ ownerDocument = globalThis.document, enableHWA = false } = {}) {
167
+ super({ enableHWA });
168
+ this._document = ownerDocument;
169
+ }
170
+ _createCanvas(width, height) {
171
+ const canvas = this._document.createElement("canvas");
172
+ canvas.width = width;
173
+ canvas.height = height;
174
+ return canvas;
175
+ }
176
+ };
177
+ var NodeCanvasFactory = class extends BaseCanvasFactory {
178
+ constructor({ enableHWA = false } = {}) {
179
+ super({ enableHWA });
180
+ }
181
+ _createCanvas(width, height) {
182
+ if (!resolvedCanvasModule) throw new Error("@napi-rs/canvas module is not resolved");
183
+ return resolvedCanvasModule.createCanvas(width, height);
184
+ }
185
+ };
176
186
  async function resolveCanvasModule(canvasImport) {
177
- resolvedCanvasModule ??= await interopDefault(canvasImport());
187
+ resolvedCanvasModule ??= await interopDefault(canvasImport());
188
+ globalThis[Symbol.for("unpdf.canvasModule")] = resolvedCanvasModule;
178
189
  }
190
+ /**
191
+ * Injects `DOMMatrix`, `ImageData` and `Path2D` constructors from the
192
+ * `@napi-rs/canvas` package into the global scope. This is necessary for
193
+ * Node.js environments that do not support these constructors natively.
194
+ *
195
+ * @remarks
196
+ * `DOMMatrix` is always set because {@link stubBrowserGlobals} may have
197
+ * installed a minimal stub to allow PDF.js to import – the real
198
+ * `@napi-rs/canvas` implementation must take precedence for rendering.
199
+ */
179
200
  function injectCanvasConstructors() {
180
- if (!resolvedCanvasModule)
181
- return;
182
- globalThis.DOMMatrix = resolvedCanvasModule.DOMMatrix;
183
- if (typeof globalThis.ImageData === "undefined")
184
- globalThis.ImageData = resolvedCanvasModule.ImageData;
185
- if (typeof globalThis.Path2D === "undefined")
186
- globalThis.Path2D = resolvedCanvasModule.Path2D;
201
+ if (!resolvedCanvasModule) return;
202
+ globalThis.DOMMatrix = resolvedCanvasModule.DOMMatrix;
203
+ if (typeof globalThis.ImageData === "undefined") globalThis.ImageData = resolvedCanvasModule.ImageData;
204
+ if (typeof globalThis.Path2D === "undefined") globalThis.Path2D = resolvedCanvasModule.Path2D;
187
205
  }
188
-
206
+ //#endregion
207
+ //#region src/image.ts
208
+ /**
209
+ * Extracts images from a specific page of a PDF document, including necessary metadata,
210
+ * such as width, height, and calculated color channels.
211
+ *
212
+ * @example
213
+ * const imagesData = await extractImages(pdf, pageNum)
214
+ *
215
+ * for (const imgData of imagesData) {
216
+ * const imageIndex = totalImagesProcessed + 1
217
+ * await sharp(imgData.data, {
218
+ * raw: { width: imgData.width, height: imgData.height, channels: imgData.channels }
219
+ * })
220
+ * .png()
221
+ * .toFile(`${imageIndex}.png`)
222
+ * }
223
+ */
189
224
  async function extractImages$1(data, pageNumber) {
190
- const pdf = isPDFDocumentProxy(data) ? data : await getDocumentProxy(data);
191
- if (pageNumber < 1 || pageNumber > pdf.numPages) {
192
- throw new Error(`Invalid page number. Must be between 1 and ${pdf.numPages}.`);
193
- }
194
- const page = await pdf.getPage(pageNumber);
195
- const operatorList = await page.getOperatorList();
196
- const { OPS } = await getResolvedPDFJS();
197
- const images = [];
198
- for (let i = 0; i < operatorList.fnArray.length; i++) {
199
- const op = operatorList.fnArray[i];
200
- if (op !== OPS.paintImageXObject) {
201
- continue;
202
- }
203
- const imageKey = operatorList.argsArray[i][0];
204
- const image = await new Promise(
205
- (resolve) => (imageKey.startsWith("g_") ? page.commonObjs : page.objs).get(imageKey, resolve)
206
- );
207
- if (!image || !image.data || !image.width || !image.height) {
208
- continue;
209
- }
210
- const { width, height, data: data2 } = image;
211
- const calculatedChannels = data2.length / (width * height);
212
- if (![1, 3, 4].includes(calculatedChannels)) {
213
- continue;
214
- }
215
- const channels = calculatedChannels;
216
- images.push({
217
- data: data2,
218
- width,
219
- height,
220
- channels,
221
- key: imageKey
222
- });
223
- }
224
- return images;
225
+ return await withDocument(data, async (pdf) => {
226
+ if (pageNumber < 1 || pageNumber > pdf.numPages) throw new Error(`Invalid page number. Must be between 1 and ${pdf.numPages}.`);
227
+ const page = await pdf.getPage(pageNumber);
228
+ const operatorList = await page.getOperatorList();
229
+ const { OPS } = await getResolvedPDFJS();
230
+ const images = [];
231
+ for (let i = 0; i < operatorList.fnArray.length; i++) {
232
+ if (operatorList.fnArray[i] !== OPS.paintImageXObject) continue;
233
+ const imageKey = operatorList.argsArray[i][0];
234
+ const image = await new Promise((resolve) => (imageKey.startsWith("g_") ? page.commonObjs : page.objs).get(imageKey, resolve));
235
+ if (!image || !image.data || !image.width || !image.height) continue;
236
+ const { width, height, data } = image;
237
+ const calculatedChannels = data.length / (width * height);
238
+ if (![
239
+ 1,
240
+ 3,
241
+ 4
242
+ ].includes(calculatedChannels)) continue;
243
+ const channels = calculatedChannels;
244
+ images.push({
245
+ data,
246
+ width,
247
+ height,
248
+ channels,
249
+ key: imageKey
250
+ });
251
+ }
252
+ return images;
253
+ });
225
254
  }
226
255
  async function renderPageAsImage$1(data, pageNumber, options = {}) {
227
- const CanvasFactory = await createIsomorphicCanvasFactory(options.canvasImport);
228
- const pdf = isPDFDocumentProxy(data) ? data : await getDocumentProxy(data, { CanvasFactory });
229
- const page = await pdf.getPage(pageNumber);
230
- if (pageNumber < 1 || pageNumber > pdf.numPages) {
231
- throw new Error(`Invalid page number. Must be between 1 and ${pdf.numPages}.`);
232
- }
233
- const defaultViewport = page.getViewport({ scale: 1 });
234
- let scale = options.scale || 1;
235
- if (options.width) {
236
- scale = options.width / defaultViewport.width;
237
- } else if (options.height) {
238
- scale = options.height / defaultViewport.height;
239
- }
240
- const viewport = page.getViewport({ scale: Math.max(0, scale) });
241
- const drawingContext = new CanvasFactory().create(viewport.width, viewport.height);
242
- await page.render({
243
- canvas: drawingContext.canvas,
244
- canvasContext: drawingContext.context,
245
- viewport
246
- }).promise;
247
- const dataUrl = drawingContext.canvas.toDataURL();
248
- if (options.toDataURL) {
249
- return dataUrl;
250
- }
251
- const response = await fetch(dataUrl);
252
- return await response.arrayBuffer();
256
+ const CanvasFactory = await createIsomorphicCanvasFactory(options.canvasImport);
257
+ return await withDocument(data, async (pdf) => {
258
+ if (pageNumber < 1 || pageNumber > pdf.numPages) throw new Error(`Invalid page number. Must be between 1 and ${pdf.numPages}.`);
259
+ const page = await pdf.getPage(pageNumber);
260
+ const defaultViewport = page.getViewport({ scale: 1 });
261
+ let scale = options.scale || 1;
262
+ if (options.width) scale = options.width / defaultViewport.width;
263
+ else if (options.height) scale = options.height / defaultViewport.height;
264
+ const viewport = page.getViewport({ scale: Math.max(0, scale) });
265
+ const canvasFactory = new CanvasFactory();
266
+ const drawingContext = canvasFactory.create(viewport.width, viewport.height);
267
+ try {
268
+ await page.render({
269
+ canvas: drawingContext.canvas,
270
+ canvasContext: drawingContext.context,
271
+ viewport
272
+ }).promise;
273
+ const { canvas } = drawingContext;
274
+ if (options.toDataURL) return canvas.toDataURL();
275
+ if ("encode" in canvas) {
276
+ const buffer = await canvas.encode("png");
277
+ return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
278
+ }
279
+ const blob = await new Promise((resolve) => {
280
+ canvas.toBlob(resolve);
281
+ });
282
+ if (!blob) throw new Error("Failed to encode canvas to a PNG blob.");
283
+ return await blob.arrayBuffer();
284
+ } finally {
285
+ canvasFactory.destroy(drawingContext);
286
+ }
287
+ }, { CanvasFactory });
253
288
  }
254
289
  async function createIsomorphicCanvasFactory(canvasImport) {
255
- if (isBrowser)
256
- return DOMCanvasFactory;
257
- if (isNode) {
258
- if (!canvasImport) {
259
- throw new Error('Parameter "canvasImport" is required in Node.js environment.');
260
- }
261
- await resolveCanvasModule(canvasImport);
262
- injectCanvasConstructors();
263
- return NodeCanvasFactory;
264
- }
265
- throw new Error("Unsupported environment for canvas creation.");
290
+ if (isBrowser) return DOMCanvasFactory;
291
+ if (isNode) {
292
+ if (!canvasImport) throw new Error("Parameter \"canvasImport\" is required in Node.js environment.");
293
+ await resolveCanvasModule(canvasImport);
294
+ injectCanvasConstructors();
295
+ return NodeCanvasFactory;
296
+ }
297
+ throw new Error("Unsupported environment for canvas creation.");
266
298
  }
267
-
299
+ //#endregion
300
+ //#region src/links.ts
268
301
  async function extractLinks$1(data) {
269
- const pdf = isPDFDocumentProxy(data) ? data : await getDocumentProxy(data);
270
- const pageLinks = await Promise.all(
271
- Array.from({ length: pdf.numPages }, (_, i) => getPageLinks(pdf, i + 1))
272
- );
273
- return {
274
- totalPages: pdf.numPages,
275
- links: pageLinks.flat()
276
- };
302
+ return await withDocument(data, async (pdf) => {
303
+ const pageLinks = await Promise.all(Array.from({ length: pdf.numPages }, (_, i) => getPageLinks(pdf, i + 1)));
304
+ return {
305
+ totalPages: pdf.numPages,
306
+ links: pageLinks.flat()
307
+ };
308
+ });
277
309
  }
278
310
  async function getPageLinks(document, pageNumber) {
279
- const page = await document.getPage(pageNumber);
280
- const annotations = await page.getAnnotations();
281
- const links = [];
282
- for (const annotation of annotations) {
283
- if (annotation.subtype === "Link" && annotation.url) {
284
- links.push(annotation.url);
285
- }
286
- }
287
- return links;
311
+ const annotations = await (await document.getPage(pageNumber)).getAnnotations();
312
+ const links = [];
313
+ for (const annotation of annotations) if (annotation.subtype === "Link" && annotation.url) links.push(annotation.url);
314
+ return links;
288
315
  }
289
-
316
+ //#endregion
317
+ //#region src/meta.ts
290
318
  const XMP_DATE_PROPERTIES = [
291
- "xmp:createdate",
292
- "xmp:modifydate",
293
- "xmp:metadatadate",
294
- "xap:createdate",
295
- "xap:modifydate",
296
- "xap:metadatadate"
319
+ "xmp:createdate",
320
+ "xmp:modifydate",
321
+ "xmp:metadatadate",
322
+ "xap:createdate",
323
+ "xap:modifydate",
324
+ "xap:metadatadate"
297
325
  ];
298
326
  async function getMeta$1(data, options = {}) {
299
- const pdf = isPDFDocumentProxy(data) ? data : await getDocumentProxy(data);
300
- const meta = await pdf.getMetadata();
301
- const info = meta?.info || {};
302
- if (options.parseDates) {
303
- const { PDFDateString } = await getResolvedPDFJS();
304
- if (info?.CreationDate) {
305
- info.CreationDate = PDFDateString.toDateObject(info?.CreationDate);
306
- }
307
- if (info?.ModDate) {
308
- info.ModDate = PDFDateString.toDateObject(info?.ModDate);
309
- }
310
- if (meta.metadata) {
311
- meta.metadata = new Proxy(meta.metadata, {
312
- get(target, prop) {
313
- if (prop === "get") {
314
- return (name) => {
315
- const value = target.get(name);
316
- if (XMP_DATE_PROPERTIES.includes(name) && value) {
317
- return parseISODateString(value);
318
- }
319
- return value;
320
- };
321
- }
322
- return target[prop];
323
- }
324
- });
325
- }
326
- }
327
- return {
328
- info,
329
- metadata: meta?.metadata || {}
330
- };
327
+ return await withDocument(data, async (pdf) => {
328
+ const meta = await pdf.getMetadata();
329
+ const info = meta?.info || {};
330
+ if (options.parseDates) {
331
+ const { PDFDateString } = await getResolvedPDFJS();
332
+ if (info?.CreationDate) info.CreationDate = PDFDateString.toDateObject(info?.CreationDate);
333
+ if (info?.ModDate) info.ModDate = PDFDateString.toDateObject(info?.ModDate);
334
+ if (meta.metadata) meta.metadata = new Proxy(meta.metadata, { get(target, prop) {
335
+ if (prop === "get") return (name) => {
336
+ const value = target.get(name);
337
+ if (XMP_DATE_PROPERTIES.includes(name) && value) return parseISODateString(value);
338
+ return value;
339
+ };
340
+ return target[prop];
341
+ } });
342
+ }
343
+ return {
344
+ info,
345
+ metadata: meta?.metadata || {}
346
+ };
347
+ });
331
348
  }
332
349
  function parseISODateString(isoDateString) {
333
- if (!isoDateString)
334
- return;
335
- const parsedDate = Date.parse(isoDateString);
336
- if (!Number.isNaN(parsedDate)) {
337
- return new Date(parsedDate);
338
- }
350
+ if (!isoDateString) return;
351
+ const parsedDate = Date.parse(isoDateString);
352
+ if (!Number.isNaN(parsedDate)) return new Date(parsedDate);
339
353
  }
340
-
354
+ //#endregion
355
+ //#region src/text.ts
341
356
  async function extractTextItems$1(data) {
342
- const pdf = isPDFDocumentProxy(data) ? data : await getDocumentProxy(data);
343
- const items = await Promise.all(
344
- Array.from({ length: pdf.numPages }, (_, i) => getPageTextItems(pdf, i + 1))
345
- );
346
- return { totalPages: pdf.numPages, items };
357
+ return await withDocument(data, async (pdf) => {
358
+ const items = await Promise.all(Array.from({ length: pdf.numPages }, (_, i) => getPageTextItems(pdf, i + 1)));
359
+ return {
360
+ totalPages: pdf.numPages,
361
+ items
362
+ };
363
+ });
347
364
  }
348
365
  async function getPageTextItems(document, pageNumber) {
349
- const page = await document.getPage(pageNumber);
350
- const content = await page.getTextContent();
351
- const styles = content.styles;
352
- return content.items.filter((item) => item.str != null).map((item) => {
353
- const [_a, _b, c, d, e, f] = item.transform;
354
- return {
355
- str: item.str,
356
- x: e,
357
- y: f,
358
- width: item.width,
359
- height: item.height,
360
- fontSize: Math.hypot(c, d),
361
- fontFamily: styles[item.fontName]?.fontFamily ?? "",
362
- dir: item.dir,
363
- hasEOL: item.hasEOL
364
- };
365
- });
366
+ const content = await (await document.getPage(pageNumber)).getTextContent();
367
+ const styles = content.styles;
368
+ return content.items.filter((item) => item.str != null).map((item) => {
369
+ const [_a, _b, c, d, e, f] = item.transform;
370
+ return {
371
+ str: item.str,
372
+ x: e,
373
+ y: f,
374
+ width: item.width,
375
+ height: item.height,
376
+ fontSize: Math.hypot(c, d),
377
+ fontFamily: styles[item.fontName]?.fontFamily ?? "",
378
+ dir: item.dir,
379
+ hasEOL: item.hasEOL
380
+ };
381
+ });
366
382
  }
367
383
  async function extractText$1(data, options = {}) {
368
- const { mergePages = false } = options;
369
- const pdf = isPDFDocumentProxy(data) ? data : await getDocumentProxy(data);
370
- const texts = await Promise.all(
371
- Array.from({ length: pdf.numPages }, (_, i) => getPageText(pdf, i + 1))
372
- );
373
- return {
374
- totalPages: pdf.numPages,
375
- text: mergePages ? texts.join("\n").replace(/\s+/g, " ") : texts
376
- };
384
+ const { mergePages = false } = options;
385
+ return await withDocument(data, async (pdf) => {
386
+ const texts = await Promise.all(Array.from({ length: pdf.numPages }, (_, i) => getPageText(pdf, i + 1)));
387
+ return {
388
+ totalPages: pdf.numPages,
389
+ text: mergePages ? normalizeMergedText(texts) : texts
390
+ };
391
+ });
377
392
  }
378
393
  async function getPageText(document, pageNumber) {
379
- const page = await document.getPage(pageNumber);
380
- const content = await page.getTextContent();
381
- return content.items.filter((item) => item.str != null).map((item) => item.str + (item.hasEOL ? "\n" : "")).join("");
394
+ return (await (await document.getPage(pageNumber)).getTextContent()).items.filter((item) => item.str != null).map((item) => item.str + (item.hasEOL ? "\n" : "")).join("");
382
395
  }
383
-
396
+ /**
397
+ * Collapses whitespace without destroying the line structure: `hasEOL` and
398
+ * page-join line breaks survive, but at most one blank line remains.
399
+ */
400
+ function normalizeMergedText(texts) {
401
+ return texts.join("\n").replace(/[^\S\n]+/g, " ").replace(/ ?\n ?/g, "\n").replace(/\n{3,}/g, "\n\n");
402
+ }
403
+ //#endregion
404
+ //#region src/config.ts
405
+ /**
406
+ * By default, unpdf will use the latest version of PDF.js compiled for
407
+ * serverless environments. If you want to use a different version, you can
408
+ * provide a custom resolver function.
409
+ *
410
+ * @example
411
+ * // Use the official PDF.js build (make sure to install it first)
412
+ * import { definePDFJSModule } from 'unpdf'
413
+ *
414
+ * await definePDFJSModule(() => import('pdfjs-dist'))
415
+ */
384
416
  async function definePDFJSModule(pdfjs) {
385
- await resolvePDFJSImport(pdfjs, { reload: true });
417
+ await resolvePDFJSImport(pdfjs, { reload: true });
386
418
  }
419
+ /** @deprecated Use `definePDFJSModule` instead. Will be removed in v2. */
387
420
  async function configureUnPDF(options) {
388
- await resolvePDFJSImport(options.pdfjs, { reload: true });
421
+ await resolvePDFJSImport(options.pdfjs, { reload: true });
389
422
  }
390
-
423
+ //#endregion
424
+ //#region src/index.ts
391
425
  const getMeta = async (...args) => {
392
- await resolvePDFJSImport();
393
- return await getMeta$1(...args);
426
+ await resolvePDFJSImport();
427
+ return await getMeta$1(...args);
394
428
  };
395
429
  const extractText = async (...args) => {
396
- await resolvePDFJSImport();
397
- return await extractText$1(...args);
430
+ await resolvePDFJSImport();
431
+ return await extractText$1(...args);
398
432
  };
399
433
  const extractTextItems = async (...args) => {
400
- await resolvePDFJSImport();
401
- return await extractTextItems$1(...args);
434
+ await resolvePDFJSImport();
435
+ return await extractTextItems$1(...args);
402
436
  };
403
437
  const extractImages = async (...args) => {
404
- await resolvePDFJSImport();
405
- return await extractImages$1(...args);
438
+ await resolvePDFJSImport();
439
+ return await extractImages$1(...args);
406
440
  };
407
441
  const renderPageAsImage = async (...args) => {
408
- await resolvePDFJSImport();
409
- return await renderPageAsImage$1(...args);
442
+ await resolvePDFJSImport();
443
+ return await renderPageAsImage$1(...args);
410
444
  };
411
445
  const extractLinks = async (...args) => {
412
- await resolvePDFJSImport();
413
- return await extractLinks$1(...args);
446
+ await resolvePDFJSImport();
447
+ return await extractLinks$1(...args);
414
448
  };
415
-
449
+ //#endregion
416
450
  export { configureUnPDF, createIsomorphicCanvasFactory, definePDFJSModule, extractImages, extractLinks, extractText, extractTextItems, getDocumentProxy, getMeta, getResolvedPDFJS, renderPageAsImage, resolvePDFJSImport };