@embedpdf/engines 1.0.3 → 1.0.5

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 (49) hide show
  1. package/dist/converters.cjs +130 -115
  2. package/dist/converters.cjs.map +1 -1
  3. package/dist/converters.d.ts +7 -12
  4. package/dist/converters.js +125 -86
  5. package/dist/converters.js.map +1 -1
  6. package/dist/index.cjs +5362 -5978
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.d.ts +1479 -6
  9. package/dist/index.js +5399 -356
  10. package/dist/index.js.map +1 -1
  11. package/dist/pdfium-direct-engine.cjs +4037 -0
  12. package/dist/pdfium-direct-engine.cjs.map +1 -0
  13. package/dist/{pdfium.d.cts → pdfium-direct-engine.d.ts} +7 -99
  14. package/dist/pdfium-direct-engine.js +4035 -0
  15. package/dist/pdfium-direct-engine.js.map +1 -0
  16. package/dist/pdfium-worker-engine.cjs +800 -0
  17. package/dist/pdfium-worker-engine.cjs.map +1 -0
  18. package/dist/{worker.d.cts → pdfium-worker-engine.d.ts} +38 -6
  19. package/dist/pdfium-worker-engine.js +798 -0
  20. package/dist/pdfium-worker-engine.js.map +1 -0
  21. package/dist/pdfium.cjs +4243 -5647
  22. package/dist/pdfium.cjs.map +1 -1
  23. package/dist/pdfium.d.ts +134 -6
  24. package/dist/pdfium.js +4288 -21
  25. package/dist/pdfium.js.map +1 -1
  26. package/dist/preact.cjs +39 -0
  27. package/dist/preact.cjs.map +1 -0
  28. package/dist/preact.d.ts +13 -0
  29. package/dist/preact.js +37 -0
  30. package/dist/preact.js.map +1 -0
  31. package/dist/react.cjs +39 -0
  32. package/dist/react.cjs.map +1 -0
  33. package/dist/react.d.ts +13 -0
  34. package/dist/react.js +37 -0
  35. package/dist/react.js.map +1 -0
  36. package/dist/worker.cjs +771 -1104
  37. package/dist/worker.cjs.map +1 -1
  38. package/dist/worker.d.ts +32 -6
  39. package/dist/worker.js +786 -11
  40. package/dist/worker.js.map +1 -1
  41. package/package.json +42 -8
  42. package/dist/chunk-FXQUMVH5.js +0 -4599
  43. package/dist/chunk-FXQUMVH5.js.map +0 -1
  44. package/dist/chunk-YZLT3A2D.js +0 -1101
  45. package/dist/chunk-YZLT3A2D.js.map +0 -1
  46. package/dist/converters.d.cts +0 -69
  47. package/dist/index.d.cts +0 -32
  48. package/dist/runner-BvRtPCKL.d.cts +0 -131
  49. package/dist/runner-BvRtPCKL.d.ts +0 -131
@@ -1,4599 +0,0 @@
1
- import {
2
- EngineRunner
3
- } from "./chunk-YZLT3A2D.js";
4
-
5
- // src/pdfium/helper.ts
6
- function readString(wasmModule, readChars, parseChars, defaultLength = 100) {
7
- let buffer = wasmModule.wasmExports.malloc(defaultLength);
8
- for (let i = 0; i < defaultLength; i++) {
9
- wasmModule.HEAP8[buffer + i] = 0;
10
- }
11
- const actualLength = readChars(buffer, defaultLength);
12
- let str;
13
- if (actualLength > defaultLength) {
14
- wasmModule.wasmExports.free(buffer);
15
- buffer = wasmModule.wasmExports.malloc(actualLength);
16
- for (let i = 0; i < actualLength; i++) {
17
- wasmModule.HEAP8[buffer + i] = 0;
18
- }
19
- readChars(buffer, actualLength);
20
- str = parseChars(buffer);
21
- } else {
22
- str = parseChars(buffer);
23
- }
24
- wasmModule.wasmExports.free(buffer);
25
- return str;
26
- }
27
- function readArrayBuffer(wasmModule, readChars) {
28
- const bufferSize = readChars(0, 0);
29
- const bufferPtr = wasmModule.wasmExports.malloc(bufferSize);
30
- readChars(bufferPtr, bufferSize);
31
- const arrayBuffer = new ArrayBuffer(bufferSize);
32
- const view = new DataView(arrayBuffer);
33
- for (let i = 0; i < bufferSize; i++) {
34
- view.setInt8(i, wasmModule.getValue(bufferPtr + i, "i8"));
35
- }
36
- wasmModule.wasmExports.free(bufferPtr);
37
- return arrayBuffer;
38
- }
39
-
40
- // src/pdfium/engine.ts
41
- import {
42
- PdfAnnotationSubtype,
43
- PdfZoomMode,
44
- NoopLogger,
45
- MatchFlag,
46
- PdfActionType,
47
- Rotation,
48
- PDF_FORM_FIELD_TYPE,
49
- transformSize,
50
- PdfAnnotationObjectStatus,
51
- AppearanceMode,
52
- PdfPageObjectType,
53
- PdfErrorCode,
54
- PdfTaskHelper,
55
- transformRect,
56
- Task,
57
- toIntRect,
58
- toIntSize,
59
- quadToRect
60
- } from "@embedpdf/models";
61
-
62
- // src/pdfium/cache.ts
63
- var PdfCache = class {
64
- constructor(pdfium) {
65
- this.pdfium = pdfium;
66
- this.docs = /* @__PURE__ */ new Map();
67
- }
68
- /** Open (or re-use) a document */
69
- setDocument(id, filePtr, docPtr) {
70
- let ctx = this.docs.get(id);
71
- if (!ctx) {
72
- ctx = new DocumentContext(filePtr, docPtr, this.pdfium);
73
- this.docs.set(id, ctx);
74
- }
75
- }
76
- /** Retrieve the DocumentContext for a given PdfDocumentObject */
77
- getContext(docId) {
78
- return this.docs.get(docId);
79
- }
80
- /** Close & fully release a document and all its pages */
81
- closeDocument(docId) {
82
- const ctx = this.docs.get(docId);
83
- if (!ctx) return false;
84
- ctx.dispose();
85
- this.docs.delete(docId);
86
- return true;
87
- }
88
- };
89
- var DocumentContext = class {
90
- constructor(filePtr, docPtr, pdfium) {
91
- this.filePtr = filePtr;
92
- this.docPtr = docPtr;
93
- this.pageCache = new PageCache(pdfium, docPtr);
94
- }
95
- /** Main accessor for pages */
96
- acquirePage(pageIdx) {
97
- return this.pageCache.acquire(pageIdx);
98
- }
99
- /** Tear down all pages + this document */
100
- dispose() {
101
- this.pageCache.forceReleaseAll();
102
- this.pageCache.pdf.FPDF_CloseDocument(this.docPtr);
103
- this.pageCache.pdf.pdfium.wasmExports.free(this.filePtr);
104
- }
105
- };
106
- var PageCache = class {
107
- constructor(pdf, docPtr) {
108
- this.pdf = pdf;
109
- this.docPtr = docPtr;
110
- this.cache = /* @__PURE__ */ new Map();
111
- }
112
- acquire(pageIdx) {
113
- let ctx = this.cache.get(pageIdx);
114
- if (!ctx) {
115
- const pagePtr = this.pdf.FPDF_LoadPage(this.docPtr, pageIdx);
116
- ctx = new PageContext(this.pdf, this.docPtr, pageIdx, pagePtr, () => {
117
- this.cache.delete(pageIdx);
118
- });
119
- this.cache.set(pageIdx, ctx);
120
- }
121
- ctx.clearExpiryTimer();
122
- ctx.bumpRefCount();
123
- return ctx;
124
- }
125
- forceReleaseAll() {
126
- for (const ctx of this.cache.values()) {
127
- ctx.disposeImmediate();
128
- }
129
- this.cache.clear();
130
- }
131
- };
132
- var PAGE_TTL = 5e3;
133
- var PageContext = class {
134
- constructor(pdf, docPtr, pageIdx, pagePtr, onFinalDispose) {
135
- this.pdf = pdf;
136
- this.docPtr = docPtr;
137
- this.pageIdx = pageIdx;
138
- this.pagePtr = pagePtr;
139
- this.onFinalDispose = onFinalDispose;
140
- this.refCount = 0;
141
- this.disposed = false;
142
- }
143
- /** Called by PageCache.acquire() */
144
- bumpRefCount() {
145
- if (this.disposed) throw new Error("Context already disposed");
146
- this.refCount++;
147
- }
148
- /** Called by PageCache.acquire() */
149
- clearExpiryTimer() {
150
- if (this.expiryTimer) {
151
- clearTimeout(this.expiryTimer);
152
- this.expiryTimer = void 0;
153
- }
154
- }
155
- /** Called by PageCache.release() internally */
156
- release() {
157
- if (this.disposed) return;
158
- this.refCount--;
159
- if (this.refCount === 0) {
160
- this.expiryTimer = setTimeout(() => this.disposeImmediate(), PAGE_TTL);
161
- }
162
- }
163
- /** Tear down _all_ sub-pointers & the page. */
164
- disposeImmediate() {
165
- if (this.disposed) return;
166
- this.disposed = true;
167
- if (this.textPagePtr !== void 0) {
168
- this.pdf.FPDFText_ClosePage(this.textPagePtr);
169
- }
170
- if (this.formHandle !== void 0) {
171
- this.pdf.FORM_OnBeforeClosePage(this.pagePtr, this.formHandle);
172
- this.pdf.PDFiumExt_ExitFormFillEnvironment(this.formHandle);
173
- }
174
- if (this.formInfoPtr !== void 0) {
175
- this.pdf.PDFiumExt_CloseFormFillInfo(this.formInfoPtr);
176
- }
177
- this.pdf.FPDF_ClosePage(this.pagePtr);
178
- this.onFinalDispose();
179
- }
180
- // ── public helpers ──
181
- /** Always safe: opens (once) and returns the text-page ptr. */
182
- getTextPage() {
183
- this.ensureAlive();
184
- if (this.textPagePtr === void 0) {
185
- this.textPagePtr = this.pdf.FPDFText_LoadPage(this.pagePtr);
186
- }
187
- return this.textPagePtr;
188
- }
189
- /** Always safe: opens (once) and returns the form-fill handle. */
190
- getFormHandle() {
191
- this.ensureAlive();
192
- if (this.formHandle === void 0) {
193
- this.formInfoPtr = this.pdf.PDFiumExt_OpenFormFillInfo();
194
- this.formHandle = this.pdf.PDFiumExt_InitFormFillEnvironment(this.docPtr, this.formInfoPtr);
195
- this.pdf.FORM_OnAfterLoadPage(this.pagePtr, this.formHandle);
196
- }
197
- return this.formHandle;
198
- }
199
- /**
200
- * Safely execute `fn` with an annotation pointer.
201
- * Pointer is ALWAYS closed afterwards.
202
- */
203
- withAnnotation(annotIdx, fn) {
204
- this.ensureAlive();
205
- const annotPtr = this.pdf.FPDFPage_GetAnnot(this.pagePtr, annotIdx);
206
- try {
207
- return fn(annotPtr);
208
- } finally {
209
- this.pdf.FPDFPage_CloseAnnot(annotPtr);
210
- }
211
- }
212
- ensureAlive() {
213
- if (this.disposed) throw new Error("PageContext already disposed");
214
- }
215
- };
216
-
217
- // src/pdfium/engine.ts
218
- var BitmapFormat = /* @__PURE__ */ ((BitmapFormat2) => {
219
- BitmapFormat2[BitmapFormat2["Bitmap_Gray"] = 1] = "Bitmap_Gray";
220
- BitmapFormat2[BitmapFormat2["Bitmap_BGR"] = 2] = "Bitmap_BGR";
221
- BitmapFormat2[BitmapFormat2["Bitmap_BGRx"] = 3] = "Bitmap_BGRx";
222
- BitmapFormat2[BitmapFormat2["Bitmap_BGRA"] = 4] = "Bitmap_BGRA";
223
- return BitmapFormat2;
224
- })(BitmapFormat || {});
225
- var RenderFlag = /* @__PURE__ */ ((RenderFlag2) => {
226
- RenderFlag2[RenderFlag2["ANNOT"] = 1] = "ANNOT";
227
- RenderFlag2[RenderFlag2["LCD_TEXT"] = 2] = "LCD_TEXT";
228
- RenderFlag2[RenderFlag2["NO_NATIVETEXT"] = 4] = "NO_NATIVETEXT";
229
- RenderFlag2[RenderFlag2["GRAYSCALE"] = 8] = "GRAYSCALE";
230
- RenderFlag2[RenderFlag2["DEBUG_INFO"] = 128] = "DEBUG_INFO";
231
- RenderFlag2[RenderFlag2["NO_CATCH"] = 256] = "NO_CATCH";
232
- RenderFlag2[RenderFlag2["RENDER_LIMITEDIMAGECACHE"] = 512] = "RENDER_LIMITEDIMAGECACHE";
233
- RenderFlag2[RenderFlag2["RENDER_FORCEHALFTONE"] = 1024] = "RENDER_FORCEHALFTONE";
234
- RenderFlag2[RenderFlag2["PRINTING"] = 2048] = "PRINTING";
235
- RenderFlag2[RenderFlag2["REVERSE_BYTE_ORDER"] = 16] = "REVERSE_BYTE_ORDER";
236
- return RenderFlag2;
237
- })(RenderFlag || {});
238
- var LOG_SOURCE = "PDFiumEngine";
239
- var LOG_CATEGORY = "Engine";
240
- var PdfiumErrorCode = /* @__PURE__ */ ((PdfiumErrorCode2) => {
241
- PdfiumErrorCode2[PdfiumErrorCode2["Success"] = 0] = "Success";
242
- PdfiumErrorCode2[PdfiumErrorCode2["Unknown"] = 1] = "Unknown";
243
- PdfiumErrorCode2[PdfiumErrorCode2["File"] = 2] = "File";
244
- PdfiumErrorCode2[PdfiumErrorCode2["Format"] = 3] = "Format";
245
- PdfiumErrorCode2[PdfiumErrorCode2["Password"] = 4] = "Password";
246
- PdfiumErrorCode2[PdfiumErrorCode2["Security"] = 5] = "Security";
247
- PdfiumErrorCode2[PdfiumErrorCode2["Page"] = 6] = "Page";
248
- PdfiumErrorCode2[PdfiumErrorCode2["XFALoad"] = 7] = "XFALoad";
249
- PdfiumErrorCode2[PdfiumErrorCode2["XFALayout"] = 8] = "XFALayout";
250
- return PdfiumErrorCode2;
251
- })(PdfiumErrorCode || {});
252
- var browserImageDataToBlobConverter = (pdfImageData) => {
253
- if (typeof OffscreenCanvas === "undefined") {
254
- throw new Error(
255
- "OffscreenCanvas is not available in this environment. This converter is intended for browser use only. Please use createNodeImageDataToBlobConverter() or createNodeCanvasImageDataToBlobConverter() for Node.js."
256
- );
257
- }
258
- const imageData = new ImageData(pdfImageData.data, pdfImageData.width, pdfImageData.height);
259
- const off = new OffscreenCanvas(imageData.width, imageData.height);
260
- off.getContext("2d").putImageData(imageData, 0, 0);
261
- return off.convertToBlob({ type: "image/webp" });
262
- };
263
- var PdfiumEngine = class {
264
- /**
265
- * Create an instance of PdfiumEngine
266
- * @param wasmModule - pdfium wasm module
267
- * @param logger - logger instance
268
- * @param imageDataToBlobConverter - function to convert ImageData to Blob
269
- */
270
- constructor(pdfiumModule, logger = new NoopLogger(), imageDataConverter = browserImageDataToBlobConverter) {
271
- this.pdfiumModule = pdfiumModule;
272
- this.logger = logger;
273
- this.imageDataConverter = imageDataConverter;
274
- this.cache = new PdfCache(this.pdfiumModule);
275
- }
276
- /**
277
- * {@inheritDoc @embedpdf/models!PdfEngine.initialize}
278
- *
279
- * @public
280
- */
281
- initialize() {
282
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "initialize");
283
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `Initialize`, "Begin", "General");
284
- this.pdfiumModule.PDFiumExt_Init();
285
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `Initialize`, "End", "General");
286
- return PdfTaskHelper.resolve(true);
287
- }
288
- /**
289
- * {@inheritDoc @embedpdf/models!PdfEngine.destroy}
290
- *
291
- * @public
292
- */
293
- destroy() {
294
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "destroy");
295
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `Destroy`, "Begin", "General");
296
- this.pdfiumModule.FPDF_DestroyLibrary();
297
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `Destroy`, "End", "General");
298
- return PdfTaskHelper.resolve(true);
299
- }
300
- /**
301
- * {@inheritDoc @embedpdf/models!PdfEngine.openDocumentUrl}
302
- *
303
- * @public
304
- */
305
- openDocumentUrl(file, options) {
306
- const mode = options?.mode ?? "auto";
307
- const password = options?.password ?? "";
308
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "openDocumentUrl called", file.url, mode);
309
- const task = PdfTaskHelper.create();
310
- (async () => {
311
- try {
312
- if (mode === "full-fetch") {
313
- const fetchFullTask = await this.fetchFullAndOpen(file, password);
314
- fetchFullTask.wait(
315
- (doc) => task.resolve(doc),
316
- (err) => task.reject(err.reason)
317
- );
318
- } else if (mode === "range-request") {
319
- const openDocumentWithRangeRequestTask = await this.openDocumentWithRangeRequest(
320
- file,
321
- password
322
- );
323
- openDocumentWithRangeRequestTask.wait(
324
- (doc) => task.resolve(doc),
325
- (err) => task.reject(err.reason)
326
- );
327
- } else {
328
- const { supportsRanges, fileLength, content } = await this.checkRangeSupport(file.url);
329
- if (supportsRanges) {
330
- const openDocumentWithRangeRequestTask = await this.openDocumentWithRangeRequest(
331
- file,
332
- password,
333
- fileLength
334
- );
335
- openDocumentWithRangeRequestTask.wait(
336
- (doc) => task.resolve(doc),
337
- (err) => task.reject(err.reason)
338
- );
339
- } else if (content) {
340
- const pdfFile = { id: file.id, content };
341
- this.openDocumentFromBuffer(pdfFile, password).wait(
342
- (doc) => task.resolve(doc),
343
- (err) => task.reject(err.reason)
344
- );
345
- } else {
346
- const fetchFullTask = await this.fetchFullAndOpen(file, password);
347
- fetchFullTask.wait(
348
- (doc) => task.resolve(doc),
349
- (err) => task.reject(err.reason)
350
- );
351
- }
352
- }
353
- } catch (err) {
354
- this.logger.error(LOG_SOURCE, LOG_CATEGORY, "openDocumentUrl error", err);
355
- task.reject({
356
- code: PdfErrorCode.Unknown,
357
- message: String(err)
358
- });
359
- }
360
- })();
361
- return task;
362
- }
363
- /**
364
- * Check if the server supports range requests:
365
- * Sends a HEAD request and sees if 'Accept-Ranges: bytes'.
366
- */
367
- async checkRangeSupport(url) {
368
- try {
369
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "checkRangeSupport", url);
370
- const headResponse = await fetch(url, { method: "HEAD" });
371
- const fileLength = headResponse.headers.get("Content-Length");
372
- const acceptRanges = headResponse.headers.get("Accept-Ranges");
373
- if (acceptRanges === "bytes") {
374
- return {
375
- supportsRanges: true,
376
- fileLength: parseInt(fileLength ?? "0"),
377
- content: null
378
- };
379
- }
380
- const testResponse = await fetch(url, {
381
- headers: { Range: "bytes=0-1" }
382
- });
383
- if (testResponse.status === 200) {
384
- const content = await testResponse.arrayBuffer();
385
- return {
386
- supportsRanges: false,
387
- fileLength: parseInt(fileLength ?? "0"),
388
- content
389
- };
390
- }
391
- return {
392
- supportsRanges: testResponse.status === 206,
393
- fileLength: parseInt(fileLength ?? "0"),
394
- content: null
395
- };
396
- } catch (e) {
397
- this.logger.error(LOG_SOURCE, LOG_CATEGORY, "checkRangeSupport failed", e);
398
- throw new Error("Failed to check range support: " + e);
399
- }
400
- }
401
- /**
402
- * Fully fetch the file (using fetch) into an ArrayBuffer,
403
- * then call openDocumentFromBuffer.
404
- */
405
- async fetchFullAndOpen(file, password) {
406
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "fetchFullAndOpen", file.url);
407
- const response = await fetch(file.url);
408
- if (!response.ok) {
409
- throw new Error(`Could not fetch PDF: ${response.statusText}`);
410
- }
411
- const arrayBuf = await response.arrayBuffer();
412
- const pdfFile = {
413
- id: file.id,
414
- content: arrayBuf
415
- };
416
- return this.openDocumentFromBuffer(pdfFile, password);
417
- }
418
- /**
419
- * Use your synchronous partial-loading approach:
420
- * - In your snippet, it's done via `openDocumentFromLoader`.
421
- * - We'll do a synchronous XHR read callback that pulls
422
- * the desired byte ranges.
423
- */
424
- async openDocumentWithRangeRequest(file, password, knownFileLength) {
425
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "openDocumentWithRangeRequest", file.url);
426
- const fileLength = knownFileLength ?? (await this.retrieveFileLength(file.url)).fileLength;
427
- const callback = (offset, length) => {
428
- const xhr = new XMLHttpRequest();
429
- xhr.open("GET", file.url, false);
430
- xhr.overrideMimeType("text/plain; charset=x-user-defined");
431
- xhr.setRequestHeader("Range", `bytes=${offset}-${offset + length - 1}`);
432
- xhr.send(null);
433
- if (xhr.status === 206 || xhr.status === 200) {
434
- return this.convertResponseToUint8Array(xhr.responseText);
435
- }
436
- throw new Error(`Range request failed with status ${xhr.status}`);
437
- };
438
- return this.openDocumentFromLoader(
439
- {
440
- id: file.id,
441
- fileLength,
442
- callback
443
- },
444
- password
445
- );
446
- }
447
- /**
448
- * Helper to do a HEAD request or partial GET to find file length.
449
- */
450
- async retrieveFileLength(url) {
451
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "retrieveFileLength", url);
452
- const resp = await fetch(url, { method: "HEAD" });
453
- if (!resp.ok) {
454
- throw new Error(`Failed HEAD request for file length: ${resp.statusText}`);
455
- }
456
- const lenStr = resp.headers.get("Content-Length") || "0";
457
- const fileLength = parseInt(lenStr, 10) || 0;
458
- if (!fileLength) {
459
- throw new Error(`Content-Length not found or zero.`);
460
- }
461
- return { fileLength };
462
- }
463
- /**
464
- * Convert response text (x-user-defined) to a Uint8Array
465
- * for partial data.
466
- */
467
- convertResponseToUint8Array(text) {
468
- const array = new Uint8Array(text.length);
469
- for (let i = 0; i < text.length; i++) {
470
- array[i] = text.charCodeAt(i) & 255;
471
- }
472
- return array;
473
- }
474
- /**
475
- * {@inheritDoc @embedpdf/models!PdfEngine.openDocument}
476
- *
477
- * @public
478
- */
479
- openDocumentFromBuffer(file, password = "") {
480
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "openDocumentFromBuffer", file, password);
481
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `OpenDocumentFromBuffer`, "Begin", file.id);
482
- const array = new Uint8Array(file.content);
483
- const length = array.length;
484
- const filePtr = this.malloc(length);
485
- this.pdfiumModule.pdfium.HEAPU8.set(array, filePtr);
486
- const docPtr = this.pdfiumModule.FPDF_LoadMemDocument(filePtr, length, password);
487
- if (!docPtr) {
488
- const lastError = this.pdfiumModule.FPDF_GetLastError();
489
- this.logger.error(LOG_SOURCE, LOG_CATEGORY, `FPDF_LoadMemDocument failed with ${lastError}`);
490
- this.free(filePtr);
491
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `OpenDocumentFromBuffer`, "End", file.id);
492
- return PdfTaskHelper.reject({
493
- code: lastError,
494
- message: `FPDF_LoadMemDocument failed`
495
- });
496
- }
497
- const pageCount = this.pdfiumModule.FPDF_GetPageCount(docPtr);
498
- const pages = [];
499
- const sizePtr = this.malloc(8);
500
- for (let index = 0; index < pageCount; index++) {
501
- const result = this.pdfiumModule.FPDF_GetPageSizeByIndexF(docPtr, index, sizePtr);
502
- if (!result) {
503
- const lastError = this.pdfiumModule.FPDF_GetLastError();
504
- this.logger.error(
505
- LOG_SOURCE,
506
- LOG_CATEGORY,
507
- `FPDF_GetPageSizeByIndexF failed with ${lastError}`
508
- );
509
- this.free(sizePtr);
510
- this.pdfiumModule.FPDF_CloseDocument(docPtr);
511
- this.free(filePtr);
512
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `OpenDocumentFromBuffer`, "End", file.id);
513
- return PdfTaskHelper.reject({
514
- code: lastError,
515
- message: `FPDF_GetPageSizeByIndexF failed`
516
- });
517
- }
518
- const page = {
519
- index,
520
- size: {
521
- width: this.pdfiumModule.pdfium.getValue(sizePtr, "float"),
522
- height: this.pdfiumModule.pdfium.getValue(sizePtr + 4, "float")
523
- }
524
- };
525
- pages.push(page);
526
- }
527
- this.free(sizePtr);
528
- const pdfDoc = {
529
- id: file.id,
530
- pageCount,
531
- pages
532
- };
533
- this.cache.setDocument(file.id, filePtr, docPtr);
534
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `OpenDocumentFromBuffer`, "End", file.id);
535
- return PdfTaskHelper.resolve(pdfDoc);
536
- }
537
- /**
538
- * {@inheritDoc @embedpdf/models!PdfEngine.openDocumentFromLoader}
539
- *
540
- * @public
541
- */
542
- openDocumentFromLoader(fileLoader, password = "") {
543
- const { fileLength, callback, ...file } = fileLoader;
544
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "openDocumentFromLoader", file, password);
545
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `OpenDocumentFromLoader`, "Begin", file.id);
546
- const readBlock = (_pThis, offset, pBuf, length) => {
547
- try {
548
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "readBlock", offset, length, pBuf);
549
- if (offset < 0 || offset >= fileLength) {
550
- this.logger.error(LOG_SOURCE, LOG_CATEGORY, "Offset out of bounds:", offset);
551
- return 0;
552
- }
553
- const data = callback(offset, length);
554
- const dest = new Uint8Array(this.pdfiumModule.pdfium.HEAPU8.buffer, pBuf, data.length);
555
- dest.set(data);
556
- return data.length;
557
- } catch (error) {
558
- this.logger.error(LOG_SOURCE, LOG_CATEGORY, "ReadBlock error:", error);
559
- return 0;
560
- }
561
- };
562
- const callbackPtr = this.pdfiumModule.pdfium.addFunction(readBlock, "iiiii");
563
- const structSize = 12;
564
- const fileAccessPtr = this.malloc(structSize);
565
- this.pdfiumModule.pdfium.setValue(fileAccessPtr, fileLength, "i32");
566
- this.pdfiumModule.pdfium.setValue(fileAccessPtr + 4, callbackPtr, "i32");
567
- this.pdfiumModule.pdfium.setValue(fileAccessPtr + 8, 0, "i32");
568
- const docPtr = this.pdfiumModule.FPDF_LoadCustomDocument(fileAccessPtr, password);
569
- if (!docPtr) {
570
- const lastError = this.pdfiumModule.FPDF_GetLastError();
571
- this.logger.error(
572
- LOG_SOURCE,
573
- LOG_CATEGORY,
574
- `FPDF_LoadCustomDocument failed with ${lastError}`
575
- );
576
- this.free(fileAccessPtr);
577
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `OpenDocumentFromLoader`, "End", file.id);
578
- return PdfTaskHelper.reject({
579
- code: lastError,
580
- message: `FPDF_LoadCustomDocument failed`
581
- });
582
- }
583
- const pageCount = this.pdfiumModule.FPDF_GetPageCount(docPtr);
584
- const pages = [];
585
- const sizePtr = this.malloc(8);
586
- for (let index = 0; index < pageCount; index++) {
587
- const result = this.pdfiumModule.FPDF_GetPageSizeByIndexF(docPtr, index, sizePtr);
588
- if (!result) {
589
- const lastError = this.pdfiumModule.FPDF_GetLastError();
590
- this.logger.error(
591
- LOG_SOURCE,
592
- LOG_CATEGORY,
593
- `FPDF_GetPageSizeByIndexF failed with ${lastError}`
594
- );
595
- this.free(sizePtr);
596
- this.pdfiumModule.FPDF_CloseDocument(docPtr);
597
- this.free(fileAccessPtr);
598
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `OpenDocumentFromLoader`, "End", file.id);
599
- return PdfTaskHelper.reject({
600
- code: lastError,
601
- message: `FPDF_GetPageSizeByIndexF failed`
602
- });
603
- }
604
- const page = {
605
- index,
606
- size: {
607
- width: this.pdfiumModule.pdfium.getValue(sizePtr, "float"),
608
- height: this.pdfiumModule.pdfium.getValue(sizePtr + 4, "float")
609
- }
610
- };
611
- pages.push(page);
612
- }
613
- this.free(sizePtr);
614
- const pdfDoc = {
615
- id: file.id,
616
- pageCount,
617
- pages
618
- };
619
- this.cache.setDocument(file.id, fileAccessPtr, docPtr);
620
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `OpenDocumentFromLoader`, "End", file.id);
621
- return PdfTaskHelper.resolve(pdfDoc);
622
- }
623
- /**
624
- * {@inheritDoc @embedpdf/models!PdfEngine.getMetadata}
625
- *
626
- * @public
627
- */
628
- getMetadata(doc) {
629
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "getMetadata", doc);
630
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `GetMetadata`, "Begin", doc.id);
631
- const ctx = this.cache.getContext(doc.id);
632
- if (!ctx) {
633
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `GetMetadata`, "End", doc.id);
634
- return PdfTaskHelper.reject({
635
- code: PdfErrorCode.DocNotOpen,
636
- message: "document does not open"
637
- });
638
- }
639
- const metadata = {
640
- title: this.readMetaText(ctx.docPtr, "Title"),
641
- author: this.readMetaText(ctx.docPtr, "Author"),
642
- subject: this.readMetaText(ctx.docPtr, "Subject"),
643
- keywords: this.readMetaText(ctx.docPtr, "Keywords"),
644
- producer: this.readMetaText(ctx.docPtr, "Producer"),
645
- creator: this.readMetaText(ctx.docPtr, "Creator"),
646
- creationDate: this.readMetaText(ctx.docPtr, "CreationDate"),
647
- modificationDate: this.readMetaText(ctx.docPtr, "ModDate")
648
- };
649
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `GetMetadata`, "End", doc.id);
650
- return PdfTaskHelper.resolve(metadata);
651
- }
652
- /**
653
- * {@inheritDoc @embedpdf/models!PdfEngine.getDocPermissions}
654
- *
655
- * @public
656
- */
657
- getDocPermissions(doc) {
658
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "getDocPermissions", doc);
659
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `getDocPermissions`, "Begin", doc.id);
660
- const ctx = this.cache.getContext(doc.id);
661
- if (!ctx) {
662
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `getDocPermissions`, "End", doc.id);
663
- return PdfTaskHelper.reject({
664
- code: PdfErrorCode.DocNotOpen,
665
- message: "document does not open"
666
- });
667
- }
668
- const permissions = this.pdfiumModule.FPDF_GetDocPermissions(ctx.docPtr);
669
- return PdfTaskHelper.resolve(permissions);
670
- }
671
- /**
672
- * {@inheritDoc @embedpdf/models!PdfEngine.getDocUserPermissions}
673
- *
674
- * @public
675
- */
676
- getDocUserPermissions(doc) {
677
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "getDocUserPermissions", doc);
678
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `getDocUserPermissions`, "Begin", doc.id);
679
- const ctx = this.cache.getContext(doc.id);
680
- if (!ctx) {
681
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `getDocUserPermissions`, "End", doc.id);
682
- return PdfTaskHelper.reject({
683
- code: PdfErrorCode.DocNotOpen,
684
- message: "document does not open"
685
- });
686
- }
687
- const permissions = this.pdfiumModule.FPDF_GetDocUserPermissions(ctx.docPtr);
688
- return PdfTaskHelper.resolve(permissions);
689
- }
690
- /**
691
- * {@inheritDoc @embedpdf/models!PdfEngine.getSignatures}
692
- *
693
- * @public
694
- */
695
- getSignatures(doc) {
696
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "getSignatures", doc);
697
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `GetSignatures`, "Begin", doc.id);
698
- const ctx = this.cache.getContext(doc.id);
699
- if (!ctx) {
700
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `GetSignatures`, "End", doc.id);
701
- return PdfTaskHelper.reject({
702
- code: PdfErrorCode.DocNotOpen,
703
- message: "document does not open"
704
- });
705
- }
706
- const signatures = [];
707
- const count = this.pdfiumModule.FPDF_GetSignatureCount(ctx.docPtr);
708
- for (let i = 0; i < count; i++) {
709
- const signatureObjPtr = this.pdfiumModule.FPDF_GetSignatureObject(ctx.docPtr, i);
710
- const contents = readArrayBuffer(this.pdfiumModule.pdfium, (buffer, bufferSize) => {
711
- return this.pdfiumModule.FPDFSignatureObj_GetContents(signatureObjPtr, buffer, bufferSize);
712
- });
713
- const byteRange = readArrayBuffer(this.pdfiumModule.pdfium, (buffer, bufferSize) => {
714
- return this.pdfiumModule.FPDFSignatureObj_GetByteRange(signatureObjPtr, buffer, bufferSize) * 4;
715
- });
716
- const subFilter = readArrayBuffer(this.pdfiumModule.pdfium, (buffer, bufferSize) => {
717
- return this.pdfiumModule.FPDFSignatureObj_GetSubFilter(signatureObjPtr, buffer, bufferSize);
718
- });
719
- const reason = readString(
720
- this.pdfiumModule.pdfium,
721
- (buffer, bufferLength) => {
722
- return this.pdfiumModule.FPDFSignatureObj_GetReason(
723
- signatureObjPtr,
724
- buffer,
725
- bufferLength
726
- );
727
- },
728
- this.pdfiumModule.pdfium.UTF16ToString
729
- );
730
- const time = readString(
731
- this.pdfiumModule.pdfium,
732
- (buffer, bufferLength) => {
733
- return this.pdfiumModule.FPDFSignatureObj_GetTime(signatureObjPtr, buffer, bufferLength);
734
- },
735
- this.pdfiumModule.pdfium.UTF8ToString
736
- );
737
- const docMDP = this.pdfiumModule.FPDFSignatureObj_GetDocMDPPermission(signatureObjPtr);
738
- signatures.push({
739
- contents,
740
- byteRange,
741
- subFilter,
742
- reason,
743
- time,
744
- docMDP
745
- });
746
- }
747
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `GetSignatures`, "End", doc.id);
748
- return PdfTaskHelper.resolve(signatures);
749
- }
750
- /**
751
- * {@inheritDoc @embedpdf/models!PdfEngine.getBookmarks}
752
- *
753
- * @public
754
- */
755
- getBookmarks(doc) {
756
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "getBookmarks", doc);
757
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `GetBookmarks`, "Begin", doc.id);
758
- const ctx = this.cache.getContext(doc.id);
759
- if (!ctx) {
760
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `getBookmarks`, "End", doc.id);
761
- return PdfTaskHelper.reject({
762
- code: PdfErrorCode.DocNotOpen,
763
- message: "document does not open"
764
- });
765
- }
766
- const bookmarks = this.readPdfBookmarks(ctx.docPtr, 0);
767
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `GetBookmarks`, "End", doc.id);
768
- return PdfTaskHelper.resolve({
769
- bookmarks
770
- });
771
- }
772
- /**
773
- * {@inheritDoc @embedpdf/models!PdfEngine.renderPage}
774
- *
775
- * @public
776
- */
777
- renderPage(doc, page, scaleFactor = 1, rotation = Rotation.Degree0, dpr = 1, options = { withAnnotations: false }) {
778
- const task = new Task();
779
- this.logger.debug(
780
- LOG_SOURCE,
781
- LOG_CATEGORY,
782
- "renderPage",
783
- doc,
784
- page,
785
- scaleFactor,
786
- rotation,
787
- dpr,
788
- options
789
- );
790
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `RenderPage`, "Begin", `${doc.id}-${page.index}`);
791
- const ctx = this.cache.getContext(doc.id);
792
- if (!ctx) {
793
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `RenderPage`, "End", `${doc.id}-${page.index}`);
794
- return PdfTaskHelper.reject({
795
- code: PdfErrorCode.DocNotOpen,
796
- message: "document does not open"
797
- });
798
- }
799
- const imageData = this.renderPageRectToImageData(
800
- ctx,
801
- page,
802
- {
803
- origin: { x: 0, y: 0 },
804
- size: page.size
805
- },
806
- scaleFactor,
807
- rotation,
808
- dpr,
809
- options
810
- );
811
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `RenderPage`, "End", `${doc.id}-${page.index}`);
812
- this.imageDataConverter(imageData).then((blob) => task.resolve(blob));
813
- return task;
814
- }
815
- /**
816
- * {@inheritDoc @embedpdf/models!PdfEngine.renderPageRect}
817
- *
818
- * @public
819
- */
820
- renderPageRect(doc, page, scaleFactor, rotation, dpr, rect, options) {
821
- const task = new Task();
822
- this.logger.debug(
823
- LOG_SOURCE,
824
- LOG_CATEGORY,
825
- "renderPageRect",
826
- doc,
827
- page,
828
- scaleFactor,
829
- rotation,
830
- dpr,
831
- rect,
832
- options
833
- );
834
- this.logger.perf(
835
- LOG_SOURCE,
836
- LOG_CATEGORY,
837
- `RenderPageRect`,
838
- "Begin",
839
- `${doc.id}-${page.index}`
840
- );
841
- const ctx = this.cache.getContext(doc.id);
842
- if (!ctx) {
843
- this.logger.perf(
844
- LOG_SOURCE,
845
- LOG_CATEGORY,
846
- `RenderPageRect`,
847
- "End",
848
- `${doc.id}-${page.index}`
849
- );
850
- return PdfTaskHelper.reject({
851
- code: PdfErrorCode.DocNotOpen,
852
- message: "document does not open"
853
- });
854
- }
855
- const imageData = this.renderPageRectToImageData(
856
- ctx,
857
- page,
858
- rect,
859
- scaleFactor,
860
- rotation,
861
- dpr,
862
- options
863
- );
864
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `RenderPageRect`, "End", `${doc.id}-${page.index}`);
865
- this.imageDataConverter(imageData).then((blob) => task.resolve(blob));
866
- return task;
867
- }
868
- /**
869
- * {@inheritDoc @embedpdf/models!PdfEngine.getAllAnnotations}
870
- *
871
- * @public
872
- */
873
- getAllAnnotations(doc) {
874
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "getAllAnnotations", doc);
875
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `GetAllAnnotations`, "Begin", doc.id);
876
- const ctx = this.cache.getContext(doc.id);
877
- if (!ctx) {
878
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `GetAllAnnotations`, "End", doc.id);
879
- return PdfTaskHelper.reject({
880
- code: PdfErrorCode.DocNotOpen,
881
- message: "document does not open"
882
- });
883
- }
884
- const annotations = this.readAllAnnotations(doc, ctx);
885
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `GetAllAnnotations`, "End", doc.id);
886
- return PdfTaskHelper.resolve(annotations);
887
- }
888
- readAllAnnotations(doc, ctx) {
889
- const annotationsByPage = {};
890
- for (let i = 0; i < doc.pageCount; i++) {
891
- const pageAnnotations = this.readPageAnnotations(ctx, doc.pages[i]);
892
- annotationsByPage[i] = pageAnnotations;
893
- }
894
- return annotationsByPage;
895
- }
896
- /**
897
- * {@inheritDoc @embedpdf/models!PdfEngine.getPageAnnotations}
898
- *
899
- * @public
900
- */
901
- getPageAnnotations(doc, page) {
902
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "getPageAnnotations", doc, page);
903
- this.logger.perf(
904
- LOG_SOURCE,
905
- LOG_CATEGORY,
906
- `GetPageAnnotations`,
907
- "Begin",
908
- `${doc.id}-${page.index}`
909
- );
910
- const ctx = this.cache.getContext(doc.id);
911
- if (!ctx) {
912
- this.logger.perf(
913
- LOG_SOURCE,
914
- LOG_CATEGORY,
915
- `GetPageAnnotations`,
916
- "End",
917
- `${doc.id}-${page.index}`
918
- );
919
- return PdfTaskHelper.reject({
920
- code: PdfErrorCode.DocNotOpen,
921
- message: "document does not open"
922
- });
923
- }
924
- const annotations = this.readPageAnnotations(ctx, page);
925
- this.logger.perf(
926
- LOG_SOURCE,
927
- LOG_CATEGORY,
928
- `GetPageAnnotations`,
929
- "End",
930
- `${doc.id}-${page.index}`
931
- );
932
- this.logger.debug(
933
- LOG_SOURCE,
934
- LOG_CATEGORY,
935
- `GetPageAnnotations`,
936
- `${doc.id}-${page.index}`,
937
- annotations
938
- );
939
- return PdfTaskHelper.resolve(annotations);
940
- }
941
- /**
942
- * {@inheritDoc @embedpdf/models!PdfEngine.createPageAnnotation}
943
- *
944
- * @public
945
- */
946
- createPageAnnotation(doc, page, annotation) {
947
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "createPageAnnotation", doc, page, annotation);
948
- this.logger.perf(
949
- LOG_SOURCE,
950
- LOG_CATEGORY,
951
- `CreatePageAnnotation`,
952
- "Begin",
953
- `${doc.id}-${page.index}`
954
- );
955
- const ctx = this.cache.getContext(doc.id);
956
- if (!ctx) {
957
- this.logger.perf(
958
- LOG_SOURCE,
959
- LOG_CATEGORY,
960
- `CreatePageAnnotation`,
961
- "End",
962
- `${doc.id}-${page.index}`
963
- );
964
- return PdfTaskHelper.reject({
965
- code: PdfErrorCode.DocNotOpen,
966
- message: "document does not open"
967
- });
968
- }
969
- const pageCtx = ctx.acquirePage(page.index);
970
- const annotationPtr = this.pdfiumModule.FPDFPage_CreateAnnot(pageCtx.pagePtr, annotation.type);
971
- if (!annotationPtr) {
972
- this.logger.perf(
973
- LOG_SOURCE,
974
- LOG_CATEGORY,
975
- `CreatePageAnnotation`,
976
- "End",
977
- `${doc.id}-${page.index}`
978
- );
979
- pageCtx.release();
980
- return PdfTaskHelper.reject({
981
- code: PdfErrorCode.CantCreateAnnot,
982
- message: "can not create annotation with specified type"
983
- });
984
- }
985
- if (!this.setPageAnnoRect(page, pageCtx.pagePtr, annotationPtr, annotation.rect)) {
986
- this.pdfiumModule.FPDFPage_CloseAnnot(annotationPtr);
987
- pageCtx.release();
988
- this.logger.perf(
989
- LOG_SOURCE,
990
- LOG_CATEGORY,
991
- `CreatePageAnnotation`,
992
- "End",
993
- `${doc.id}-${page.index}`
994
- );
995
- return PdfTaskHelper.reject({
996
- code: PdfErrorCode.CantSetAnnotRect,
997
- message: "can not set the rect of the annotation"
998
- });
999
- }
1000
- let isSucceed = false;
1001
- switch (annotation.type) {
1002
- case PdfAnnotationSubtype.INK:
1003
- isSucceed = this.addInkStroke(page, pageCtx.pagePtr, annotationPtr, annotation.inkList);
1004
- break;
1005
- case PdfAnnotationSubtype.STAMP:
1006
- isSucceed = this.addStampContent(
1007
- ctx.docPtr,
1008
- page,
1009
- pageCtx.pagePtr,
1010
- annotationPtr,
1011
- annotation.rect,
1012
- annotation.contents
1013
- );
1014
- break;
1015
- }
1016
- if (!isSucceed) {
1017
- this.pdfiumModule.FPDFPage_RemoveAnnot(pageCtx.pagePtr, annotationPtr);
1018
- pageCtx.release();
1019
- this.logger.perf(
1020
- LOG_SOURCE,
1021
- LOG_CATEGORY,
1022
- `CreatePageAnnotation`,
1023
- "End",
1024
- `${doc.id}-${page.index}`
1025
- );
1026
- return PdfTaskHelper.reject({
1027
- code: PdfErrorCode.CantSetAnnotContent,
1028
- message: "can not add content of the annotation"
1029
- });
1030
- }
1031
- this.pdfiumModule.FPDFPage_GenerateContent(pageCtx.pagePtr);
1032
- this.pdfiumModule.FPDFPage_CloseAnnot(annotationPtr);
1033
- pageCtx.release();
1034
- this.logger.perf(
1035
- LOG_SOURCE,
1036
- LOG_CATEGORY,
1037
- `CreatePageAnnotation`,
1038
- "End",
1039
- `${doc.id}-${page.index}`
1040
- );
1041
- return PdfTaskHelper.resolve(true);
1042
- }
1043
- /**
1044
- * {@inheritDoc @embedpdf/models!PdfEngine.transformPageAnnotation}
1045
- *
1046
- * @public
1047
- */
1048
- transformPageAnnotation(doc, page, annotation, transformation) {
1049
- this.logger.debug(
1050
- LOG_SOURCE,
1051
- LOG_CATEGORY,
1052
- "transformPageAnnotation",
1053
- doc,
1054
- page,
1055
- annotation,
1056
- transformation
1057
- );
1058
- this.logger.perf(
1059
- LOG_SOURCE,
1060
- LOG_CATEGORY,
1061
- `TransformPageAnnotation`,
1062
- "Begin",
1063
- `${doc.id}-${page.index}`
1064
- );
1065
- const ctx = this.cache.getContext(doc.id);
1066
- if (!ctx) {
1067
- this.logger.perf(
1068
- LOG_SOURCE,
1069
- LOG_CATEGORY,
1070
- `TransformPageAnnotation`,
1071
- "End",
1072
- `${doc.id}-${page.index}`
1073
- );
1074
- return PdfTaskHelper.reject({
1075
- code: PdfErrorCode.DocNotOpen,
1076
- message: "document does not open"
1077
- });
1078
- }
1079
- const pageCtx = ctx.acquirePage(page.index);
1080
- const annotationPtr = this.pdfiumModule.FPDFPage_GetAnnot(pageCtx.pagePtr, annotation.id);
1081
- const rect = {
1082
- origin: {
1083
- x: annotation.rect.origin.x + transformation.offset.x,
1084
- y: annotation.rect.origin.y + transformation.offset.y
1085
- },
1086
- size: {
1087
- width: annotation.rect.size.width * transformation.scale.width,
1088
- height: annotation.rect.size.height * transformation.scale.height
1089
- }
1090
- };
1091
- if (!this.setPageAnnoRect(page, pageCtx.pagePtr, annotationPtr, rect)) {
1092
- this.pdfiumModule.FPDFPage_CloseAnnot(annotationPtr);
1093
- pageCtx.release();
1094
- this.logger.perf(
1095
- LOG_SOURCE,
1096
- LOG_CATEGORY,
1097
- `TransformPageAnnotation`,
1098
- "End",
1099
- `${doc.id}-${page.index}`
1100
- );
1101
- return PdfTaskHelper.reject({
1102
- code: PdfErrorCode.CantSetAnnotRect,
1103
- message: "can not set the rect of the annotation"
1104
- });
1105
- }
1106
- switch (annotation.type) {
1107
- case PdfAnnotationSubtype.INK:
1108
- {
1109
- if (!this.pdfiumModule.FPDFAnnot_RemoveInkList(annotationPtr)) {
1110
- this.pdfiumModule.FPDFPage_CloseAnnot(annotationPtr);
1111
- pageCtx.release();
1112
- this.logger.perf(
1113
- LOG_SOURCE,
1114
- LOG_CATEGORY,
1115
- `TransformPageAnnotation`,
1116
- "End",
1117
- `${doc.id}-${page.index}`
1118
- );
1119
- return PdfTaskHelper.reject({
1120
- code: PdfErrorCode.CantRemoveInkList,
1121
- message: "can not set the rect of the annotation"
1122
- });
1123
- }
1124
- const inkList = annotation.inkList.map((inkStroke) => {
1125
- return {
1126
- points: inkStroke.points.map((point) => {
1127
- return {
1128
- x: rect.origin.x + (point.x - annotation.rect.origin.x) * transformation.scale.width,
1129
- y: rect.origin.y + (point.y - annotation.rect.origin.y) * transformation.scale.height
1130
- };
1131
- })
1132
- };
1133
- });
1134
- if (!this.addInkStroke(page, pageCtx.pagePtr, annotationPtr, inkList)) {
1135
- this.pdfiumModule.FPDFPage_CloseAnnot(annotationPtr);
1136
- pageCtx.release();
1137
- this.logger.perf(
1138
- LOG_SOURCE,
1139
- LOG_CATEGORY,
1140
- `TransformPageAnnotation`,
1141
- "End",
1142
- `${doc.id}-${page.index}`
1143
- );
1144
- return PdfTaskHelper.reject({
1145
- code: PdfErrorCode.CantAddInkStoke,
1146
- message: "can not add stroke to the ink list of annotation"
1147
- });
1148
- }
1149
- }
1150
- break;
1151
- }
1152
- this.pdfiumModule.FPDFPage_GenerateContent(pageCtx.pagePtr);
1153
- this.pdfiumModule.FPDFPage_CloseAnnot(annotationPtr);
1154
- pageCtx.release();
1155
- this.logger.perf(
1156
- LOG_SOURCE,
1157
- LOG_CATEGORY,
1158
- `TransformPageAnnotation`,
1159
- "End",
1160
- `${doc.id}-${page.index}`
1161
- );
1162
- return PdfTaskHelper.resolve(true);
1163
- }
1164
- /**
1165
- * {@inheritDoc @embedpdf/models!PdfEngine.removePageAnnotation}
1166
- *
1167
- * @public
1168
- */
1169
- removePageAnnotation(doc, page, annotation) {
1170
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "removePageAnnotation", doc, page, annotation);
1171
- this.logger.perf(
1172
- LOG_SOURCE,
1173
- LOG_CATEGORY,
1174
- `RemovePageAnnotation`,
1175
- "Begin",
1176
- `${doc.id}-${page.index}`
1177
- );
1178
- const ctx = this.cache.getContext(doc.id);
1179
- if (!ctx) {
1180
- this.logger.perf(
1181
- LOG_SOURCE,
1182
- LOG_CATEGORY,
1183
- `RemovePageAnnotation`,
1184
- "End",
1185
- `${doc.id}-${page.index}`
1186
- );
1187
- return PdfTaskHelper.reject({
1188
- code: PdfErrorCode.DocNotOpen,
1189
- message: "document does not open"
1190
- });
1191
- }
1192
- const pageCtx = ctx.acquirePage(page.index);
1193
- let result = false;
1194
- result = this.pdfiumModule.FPDFPage_RemoveAnnot(pageCtx.pagePtr, annotation.id);
1195
- if (!result) {
1196
- this.logger.error(
1197
- LOG_SOURCE,
1198
- LOG_CATEGORY,
1199
- `FPDFPage_RemoveAnnot Failed`,
1200
- `${doc.id}-${page.index}`
1201
- );
1202
- } else {
1203
- result = this.pdfiumModule.FPDFPage_GenerateContent(pageCtx.pagePtr);
1204
- if (!result) {
1205
- this.logger.error(
1206
- LOG_SOURCE,
1207
- LOG_CATEGORY,
1208
- `FPDFPage_GenerateContent Failed`,
1209
- `${doc.id}-${page.index}`
1210
- );
1211
- }
1212
- }
1213
- pageCtx.release();
1214
- this.logger.perf(
1215
- LOG_SOURCE,
1216
- LOG_CATEGORY,
1217
- `RemovePageAnnotation`,
1218
- "End",
1219
- `${doc.id}-${page.index}`
1220
- );
1221
- return PdfTaskHelper.resolve(result);
1222
- }
1223
- /**
1224
- * {@inheritDoc @embedpdf/models!PdfEngine.getPageTextRects}
1225
- *
1226
- * @public
1227
- */
1228
- getPageTextRects(doc, page, scaleFactor, rotation) {
1229
- this.logger.debug(
1230
- LOG_SOURCE,
1231
- LOG_CATEGORY,
1232
- "getPageTextRects",
1233
- doc,
1234
- page,
1235
- scaleFactor,
1236
- rotation
1237
- );
1238
- this.logger.perf(
1239
- LOG_SOURCE,
1240
- LOG_CATEGORY,
1241
- `GetPageTextRects`,
1242
- "Begin",
1243
- `${doc.id}-${page.index}`
1244
- );
1245
- const ctx = this.cache.getContext(doc.id);
1246
- if (!ctx) {
1247
- this.logger.perf(
1248
- LOG_SOURCE,
1249
- LOG_CATEGORY,
1250
- `GetPageTextRects`,
1251
- "End",
1252
- `${doc.id}-${page.index}`
1253
- );
1254
- return PdfTaskHelper.reject({
1255
- code: PdfErrorCode.DocNotOpen,
1256
- message: "document does not open"
1257
- });
1258
- }
1259
- const pageCtx = ctx.acquirePage(page.index);
1260
- const textPagePtr = this.pdfiumModule.FPDFText_LoadPage(pageCtx.pagePtr);
1261
- const textRects = this.readPageTextRects(page, pageCtx.docPtr, pageCtx.pagePtr, textPagePtr);
1262
- this.pdfiumModule.FPDFText_ClosePage(textPagePtr);
1263
- pageCtx.release();
1264
- this.logger.perf(
1265
- LOG_SOURCE,
1266
- LOG_CATEGORY,
1267
- `GetPageTextRects`,
1268
- "End",
1269
- `${doc.id}-${page.index}`
1270
- );
1271
- return PdfTaskHelper.resolve(textRects);
1272
- }
1273
- /**
1274
- * {@inheritDoc @embedpdf/models!PdfEngine.renderThumbnail}
1275
- *
1276
- * @public
1277
- */
1278
- renderThumbnail(doc, page, scaleFactor, rotation, dpr) {
1279
- this.logger.debug(
1280
- LOG_SOURCE,
1281
- LOG_CATEGORY,
1282
- "renderThumbnail",
1283
- doc,
1284
- page,
1285
- scaleFactor,
1286
- rotation,
1287
- dpr
1288
- );
1289
- this.logger.perf(
1290
- LOG_SOURCE,
1291
- LOG_CATEGORY,
1292
- `RenderThumbnail`,
1293
- "Begin",
1294
- `${doc.id}-${page.index}`
1295
- );
1296
- const ctx = this.cache.getContext(doc.id);
1297
- if (!ctx) {
1298
- this.logger.perf(
1299
- LOG_SOURCE,
1300
- LOG_CATEGORY,
1301
- `RenderThumbnail`,
1302
- "End",
1303
- `${doc.id}-${page.index}`
1304
- );
1305
- return PdfTaskHelper.reject({
1306
- code: PdfErrorCode.DocNotOpen,
1307
- message: "document does not open"
1308
- });
1309
- }
1310
- scaleFactor = Math.max(scaleFactor, 0.5);
1311
- const result = this.renderPage(doc, page, scaleFactor, rotation, dpr, {
1312
- withAnnotations: true
1313
- });
1314
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `RenderThumbnail`, "End", `${doc.id}-${page.index}`);
1315
- return result;
1316
- }
1317
- /**
1318
- * {@inheritDoc @embedpdf/models!PdfEngine.getAttachments}
1319
- *
1320
- * @public
1321
- */
1322
- getAttachments(doc) {
1323
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "getAttachments", doc);
1324
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `GetAttachments`, "Begin", doc.id);
1325
- const ctx = this.cache.getContext(doc.id);
1326
- if (!ctx) {
1327
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `GetAttachments`, "End", doc.id);
1328
- return PdfTaskHelper.reject({
1329
- code: PdfErrorCode.DocNotOpen,
1330
- message: "document does not open"
1331
- });
1332
- }
1333
- const attachments = [];
1334
- const count = this.pdfiumModule.FPDFDoc_GetAttachmentCount(ctx.docPtr);
1335
- for (let i = 0; i < count; i++) {
1336
- const attachment = this.readPdfAttachment(ctx.docPtr, i);
1337
- attachments.push(attachment);
1338
- }
1339
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `GetAttachments`, "End", doc.id);
1340
- return PdfTaskHelper.resolve(attachments);
1341
- }
1342
- /**
1343
- * {@inheritDoc @embedpdf/models!PdfEngine.readAttachmentContent}
1344
- *
1345
- * @public
1346
- */
1347
- readAttachmentContent(doc, attachment) {
1348
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "readAttachmentContent", doc, attachment);
1349
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `ReadAttachmentContent`, "Begin", doc.id);
1350
- const ctx = this.cache.getContext(doc.id);
1351
- if (!ctx) {
1352
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `ReadAttachmentContent`, "End", doc.id);
1353
- return PdfTaskHelper.reject({
1354
- code: PdfErrorCode.DocNotOpen,
1355
- message: "document does not open"
1356
- });
1357
- }
1358
- const attachmentPtr = this.pdfiumModule.FPDFDoc_GetAttachment(ctx.docPtr, attachment.index);
1359
- const sizePtr = this.malloc(8);
1360
- if (!this.pdfiumModule.FPDFAttachment_GetFile(attachmentPtr, 0, 0, sizePtr)) {
1361
- this.free(sizePtr);
1362
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `ReadAttachmentContent`, "End", doc.id);
1363
- return PdfTaskHelper.reject({
1364
- code: PdfErrorCode.CantReadAttachmentSize,
1365
- message: "can not read attachment size"
1366
- });
1367
- }
1368
- const size = this.pdfiumModule.pdfium.getValue(sizePtr, "i64");
1369
- const contentPtr = this.malloc(size);
1370
- if (!this.pdfiumModule.FPDFAttachment_GetFile(attachmentPtr, contentPtr, size, sizePtr)) {
1371
- this.free(sizePtr);
1372
- this.free(contentPtr);
1373
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `ReadAttachmentContent`, "End", doc.id);
1374
- return PdfTaskHelper.reject({
1375
- code: PdfErrorCode.CantReadAttachmentContent,
1376
- message: "can not read attachment content"
1377
- });
1378
- }
1379
- const buffer = new ArrayBuffer(size);
1380
- const view = new DataView(buffer);
1381
- for (let i = 0; i < size; i++) {
1382
- view.setInt8(i, this.pdfiumModule.pdfium.getValue(contentPtr + i, "i8"));
1383
- }
1384
- this.free(sizePtr);
1385
- this.free(contentPtr);
1386
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `ReadAttachmentContent`, "End", doc.id);
1387
- return PdfTaskHelper.resolve(buffer);
1388
- }
1389
- /**
1390
- * {@inheritDoc @embedpdf/models!PdfEngine.setFormFieldValue}
1391
- *
1392
- * @public
1393
- */
1394
- setFormFieldValue(doc, page, annotation, value) {
1395
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "SetFormFieldValue", doc, annotation, value);
1396
- this.logger.perf(
1397
- LOG_SOURCE,
1398
- LOG_CATEGORY,
1399
- `SetFormFieldValue`,
1400
- "Begin",
1401
- `${doc.id}-${annotation.id}`
1402
- );
1403
- const ctx = this.cache.getContext(doc.id);
1404
- if (!ctx) {
1405
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "SetFormFieldValue", "document is not opened");
1406
- this.logger.perf(
1407
- LOG_SOURCE,
1408
- LOG_CATEGORY,
1409
- `SetFormFieldValue`,
1410
- "End",
1411
- `${doc.id}-${annotation.id}`
1412
- );
1413
- return PdfTaskHelper.reject({
1414
- code: PdfErrorCode.DocNotOpen,
1415
- message: "document does not open"
1416
- });
1417
- }
1418
- const formFillInfoPtr = this.pdfiumModule.PDFiumExt_OpenFormFillInfo();
1419
- const formHandle = this.pdfiumModule.PDFiumExt_InitFormFillEnvironment(
1420
- ctx.docPtr,
1421
- formFillInfoPtr
1422
- );
1423
- const pageCtx = ctx.acquirePage(page.index);
1424
- this.pdfiumModule.FORM_OnAfterLoadPage(pageCtx.pagePtr, formHandle);
1425
- const annotationPtr = this.pdfiumModule.FPDFPage_GetAnnot(pageCtx.pagePtr, annotation.id);
1426
- if (!this.pdfiumModule.FORM_SetFocusedAnnot(formHandle, annotationPtr)) {
1427
- this.logger.debug(
1428
- LOG_SOURCE,
1429
- LOG_CATEGORY,
1430
- "SetFormFieldValue",
1431
- "failed to set focused annotation"
1432
- );
1433
- this.logger.perf(
1434
- LOG_SOURCE,
1435
- LOG_CATEGORY,
1436
- `SetFormFieldValue`,
1437
- "End",
1438
- `${doc.id}-${annotation.id}`
1439
- );
1440
- this.pdfiumModule.FPDFPage_CloseAnnot(annotationPtr);
1441
- this.pdfiumModule.FORM_OnBeforeClosePage(pageCtx.pagePtr, formHandle);
1442
- pageCtx.release();
1443
- this.pdfiumModule.PDFiumExt_ExitFormFillEnvironment(formHandle);
1444
- this.pdfiumModule.PDFiumExt_CloseFormFillInfo(formFillInfoPtr);
1445
- return PdfTaskHelper.reject({
1446
- code: PdfErrorCode.CantFocusAnnot,
1447
- message: "failed to set focused annotation"
1448
- });
1449
- }
1450
- switch (value.kind) {
1451
- case "text":
1452
- {
1453
- if (!this.pdfiumModule.FORM_SelectAllText(formHandle, pageCtx.pagePtr)) {
1454
- this.logger.debug(
1455
- LOG_SOURCE,
1456
- LOG_CATEGORY,
1457
- "SetFormFieldValue",
1458
- "failed to select all text"
1459
- );
1460
- this.logger.perf(
1461
- LOG_SOURCE,
1462
- LOG_CATEGORY,
1463
- `SetFormFieldValue`,
1464
- "End",
1465
- `${doc.id}-${annotation.id}`
1466
- );
1467
- this.pdfiumModule.FORM_ForceToKillFocus(formHandle);
1468
- this.pdfiumModule.FPDFPage_CloseAnnot(annotationPtr);
1469
- this.pdfiumModule.FORM_OnBeforeClosePage(pageCtx.pagePtr, formHandle);
1470
- pageCtx.release();
1471
- this.pdfiumModule.PDFiumExt_ExitFormFillEnvironment(formHandle);
1472
- this.pdfiumModule.PDFiumExt_CloseFormFillInfo(formFillInfoPtr);
1473
- return PdfTaskHelper.reject({
1474
- code: PdfErrorCode.CantSelectText,
1475
- message: "failed to select all text"
1476
- });
1477
- }
1478
- const length = 2 * (value.text.length + 1);
1479
- const textPtr = this.malloc(length);
1480
- this.pdfiumModule.pdfium.stringToUTF16(value.text, textPtr, length);
1481
- this.pdfiumModule.FORM_ReplaceSelection(formHandle, pageCtx.pagePtr, textPtr);
1482
- this.free(textPtr);
1483
- }
1484
- break;
1485
- case "selection":
1486
- {
1487
- if (!this.pdfiumModule.FORM_SetIndexSelected(
1488
- formHandle,
1489
- pageCtx.pagePtr,
1490
- value.index,
1491
- value.isSelected
1492
- )) {
1493
- this.logger.debug(
1494
- LOG_SOURCE,
1495
- LOG_CATEGORY,
1496
- "SetFormFieldValue",
1497
- "failed to set index selected"
1498
- );
1499
- this.logger.perf(
1500
- LOG_SOURCE,
1501
- LOG_CATEGORY,
1502
- `SetFormFieldValue`,
1503
- "End",
1504
- `${doc.id}-${annotation.id}`
1505
- );
1506
- this.pdfiumModule.FORM_ForceToKillFocus(formHandle);
1507
- this.pdfiumModule.FPDFPage_CloseAnnot(annotationPtr);
1508
- this.pdfiumModule.FORM_OnBeforeClosePage(pageCtx.pagePtr, formHandle);
1509
- pageCtx.release();
1510
- this.pdfiumModule.PDFiumExt_ExitFormFillEnvironment(formHandle);
1511
- this.pdfiumModule.PDFiumExt_CloseFormFillInfo(formFillInfoPtr);
1512
- return PdfTaskHelper.reject({
1513
- code: PdfErrorCode.CantSelectOption,
1514
- message: "failed to set index selected"
1515
- });
1516
- }
1517
- }
1518
- break;
1519
- case "checked":
1520
- {
1521
- const kReturn = 13;
1522
- if (!this.pdfiumModule.FORM_OnChar(formHandle, pageCtx.pagePtr, kReturn, 0)) {
1523
- this.logger.debug(
1524
- LOG_SOURCE,
1525
- LOG_CATEGORY,
1526
- "SetFormFieldValue",
1527
- "failed to set field checked"
1528
- );
1529
- this.logger.perf(
1530
- LOG_SOURCE,
1531
- LOG_CATEGORY,
1532
- `SetFormFieldValue`,
1533
- "End",
1534
- `${doc.id}-${annotation.id}`
1535
- );
1536
- this.pdfiumModule.FORM_ForceToKillFocus(formHandle);
1537
- this.pdfiumModule.FPDFPage_CloseAnnot(annotationPtr);
1538
- this.pdfiumModule.FORM_OnBeforeClosePage(pageCtx.pagePtr, formHandle);
1539
- pageCtx.release();
1540
- this.pdfiumModule.PDFiumExt_ExitFormFillEnvironment(formHandle);
1541
- this.pdfiumModule.PDFiumExt_CloseFormFillInfo(formFillInfoPtr);
1542
- return PdfTaskHelper.reject({
1543
- code: PdfErrorCode.CantCheckField,
1544
- message: "failed to set field checked"
1545
- });
1546
- }
1547
- }
1548
- break;
1549
- }
1550
- this.pdfiumModule.FORM_ForceToKillFocus(formHandle);
1551
- this.pdfiumModule.FPDFPage_CloseAnnot(annotationPtr);
1552
- this.pdfiumModule.FORM_OnBeforeClosePage(pageCtx.pagePtr, formHandle);
1553
- pageCtx.release();
1554
- this.pdfiumModule.PDFiumExt_ExitFormFillEnvironment(formHandle);
1555
- this.pdfiumModule.PDFiumExt_CloseFormFillInfo(formFillInfoPtr);
1556
- return PdfTaskHelper.resolve(true);
1557
- }
1558
- /**
1559
- * {@inheritDoc @embedpdf/models!PdfEngine.flattenPage}
1560
- *
1561
- * @public
1562
- */
1563
- flattenPage(doc, page, flag) {
1564
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "flattenPage", doc, page, flag);
1565
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `flattenPage`, "Begin", doc.id);
1566
- const ctx = this.cache.getContext(doc.id);
1567
- if (!ctx) {
1568
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `flattenPage`, "End", doc.id);
1569
- return PdfTaskHelper.reject({
1570
- code: PdfErrorCode.DocNotOpen,
1571
- message: "document does not open"
1572
- });
1573
- }
1574
- const pageCtx = ctx.acquirePage(page.index);
1575
- const result = this.pdfiumModule.FPDFPage_Flatten(pageCtx.pagePtr, flag);
1576
- pageCtx.release();
1577
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `flattenPage`, "End", doc.id);
1578
- return PdfTaskHelper.resolve(result);
1579
- }
1580
- /**
1581
- * {@inheritDoc @embedpdf/models!PdfEngine.extractPages}
1582
- *
1583
- * @public
1584
- */
1585
- extractPages(doc, pageIndexes) {
1586
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "extractPages", doc, pageIndexes);
1587
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `ExtractPages`, "Begin", doc.id);
1588
- const ctx = this.cache.getContext(doc.id);
1589
- if (!ctx) {
1590
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `ExtractPages`, "End", doc.id);
1591
- return PdfTaskHelper.reject({
1592
- code: PdfErrorCode.DocNotOpen,
1593
- message: "document does not open"
1594
- });
1595
- }
1596
- const newDocPtr = this.pdfiumModule.FPDF_CreateNewDocument();
1597
- if (!newDocPtr) {
1598
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `ExtractPages`, "End", doc.id);
1599
- return PdfTaskHelper.reject({
1600
- code: PdfErrorCode.CantCreateNewDoc,
1601
- message: "can not create new document"
1602
- });
1603
- }
1604
- const pageIndexesPtr = this.malloc(pageIndexes.length * 4);
1605
- for (let i = 0; i < pageIndexes.length; i++) {
1606
- this.pdfiumModule.pdfium.setValue(pageIndexesPtr + i * 4, pageIndexes[i], "i32");
1607
- }
1608
- if (!this.pdfiumModule.FPDF_ImportPagesByIndex(
1609
- newDocPtr,
1610
- ctx.docPtr,
1611
- pageIndexesPtr,
1612
- pageIndexes.length,
1613
- 0
1614
- )) {
1615
- this.pdfiumModule.FPDF_CloseDocument(newDocPtr);
1616
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `ExtractPages`, "End", doc.id);
1617
- return PdfTaskHelper.reject({
1618
- code: PdfErrorCode.CantImportPages,
1619
- message: "can not import pages to new document"
1620
- });
1621
- }
1622
- const buffer = this.saveDocument(newDocPtr);
1623
- this.pdfiumModule.FPDF_CloseDocument(newDocPtr);
1624
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `ExtractPages`, "End", doc.id);
1625
- return PdfTaskHelper.resolve(buffer);
1626
- }
1627
- /**
1628
- * {@inheritDoc @embedpdf/models!PdfEngine.extractText}
1629
- *
1630
- * @public
1631
- */
1632
- extractText(doc, pageIndexes) {
1633
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "extractText", doc, pageIndexes);
1634
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `ExtractText`, "Begin", doc.id);
1635
- const ctx = this.cache.getContext(doc.id);
1636
- if (!ctx) {
1637
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `ExtractText`, "End", doc.id);
1638
- return PdfTaskHelper.reject({
1639
- code: PdfErrorCode.DocNotOpen,
1640
- message: "document does not open"
1641
- });
1642
- }
1643
- const strings = [];
1644
- for (let i = 0; i < pageIndexes.length; i++) {
1645
- const pageCtx = ctx.acquirePage(pageIndexes[i]);
1646
- const textPagePtr = this.pdfiumModule.FPDFText_LoadPage(pageCtx.pagePtr);
1647
- const charCount = this.pdfiumModule.FPDFText_CountChars(textPagePtr);
1648
- const bufferPtr = this.malloc((charCount + 1) * 2);
1649
- this.pdfiumModule.FPDFText_GetText(textPagePtr, 0, charCount, bufferPtr);
1650
- const text2 = this.pdfiumModule.pdfium.UTF16ToString(bufferPtr);
1651
- this.free(bufferPtr);
1652
- strings.push(text2);
1653
- this.pdfiumModule.FPDFText_ClosePage(textPagePtr);
1654
- pageCtx.release();
1655
- }
1656
- const text = strings.join("\n\n");
1657
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `ExtractText`, "End", doc.id);
1658
- return PdfTaskHelper.resolve(text);
1659
- }
1660
- /**
1661
- * {@inheritDoc @embedpdf/models!PdfEngine.merge}
1662
- *
1663
- * @public
1664
- */
1665
- merge(files) {
1666
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "merge", files);
1667
- const fileIds = files.map((file2) => file2.id).join(".");
1668
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `Merge`, "Begin", fileIds);
1669
- const newDocPtr = this.pdfiumModule.FPDF_CreateNewDocument();
1670
- if (!newDocPtr) {
1671
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `Merge`, "End", fileIds);
1672
- return PdfTaskHelper.reject({
1673
- code: PdfErrorCode.CantCreateNewDoc,
1674
- message: "can not create new document"
1675
- });
1676
- }
1677
- const ptrs = [];
1678
- for (const file2 of files.reverse()) {
1679
- const array = new Uint8Array(file2.content);
1680
- const length = array.length;
1681
- const filePtr = this.malloc(length);
1682
- this.pdfiumModule.pdfium.HEAPU8.set(array, filePtr);
1683
- const docPtr = this.pdfiumModule.FPDF_LoadMemDocument(filePtr, length, "");
1684
- if (!docPtr) {
1685
- const lastError = this.pdfiumModule.FPDF_GetLastError();
1686
- this.logger.error(
1687
- LOG_SOURCE,
1688
- LOG_CATEGORY,
1689
- `FPDF_LoadMemDocument failed with ${lastError}`
1690
- );
1691
- this.free(filePtr);
1692
- for (const ptr of ptrs) {
1693
- this.pdfiumModule.FPDF_CloseDocument(ptr.docPtr);
1694
- this.free(ptr.filePtr);
1695
- }
1696
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `Merge`, "End", fileIds);
1697
- return PdfTaskHelper.reject({
1698
- code: lastError,
1699
- message: `FPDF_LoadMemDocument failed`
1700
- });
1701
- }
1702
- ptrs.push({ filePtr, docPtr });
1703
- if (!this.pdfiumModule.FPDF_ImportPages(newDocPtr, docPtr, "", 0)) {
1704
- this.pdfiumModule.FPDF_CloseDocument(newDocPtr);
1705
- for (const ptr of ptrs) {
1706
- this.pdfiumModule.FPDF_CloseDocument(ptr.docPtr);
1707
- this.free(ptr.filePtr);
1708
- }
1709
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `Merge`, "End", fileIds);
1710
- return PdfTaskHelper.reject({
1711
- code: PdfErrorCode.CantImportPages,
1712
- message: "can not import pages to new document"
1713
- });
1714
- }
1715
- }
1716
- const buffer = this.saveDocument(newDocPtr);
1717
- this.pdfiumModule.FPDF_CloseDocument(newDocPtr);
1718
- for (const ptr of ptrs) {
1719
- this.pdfiumModule.FPDF_CloseDocument(ptr.docPtr);
1720
- this.free(ptr.filePtr);
1721
- }
1722
- const file = {
1723
- id: `${Math.random()}`,
1724
- content: buffer
1725
- };
1726
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `Merge`, "End", fileIds);
1727
- return PdfTaskHelper.resolve(file);
1728
- }
1729
- /**
1730
- * Merges specific pages from multiple PDF documents in a custom order
1731
- *
1732
- * @param mergeConfigs Array of configurations specifying which pages to merge from which documents
1733
- * @returns A PdfTask that resolves with the merged PDF file
1734
- * @public
1735
- */
1736
- mergePages(mergeConfigs) {
1737
- const configIds = mergeConfigs.map((config) => `${config.docId}:${config.pageIndices.join(",")}`).join("|");
1738
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "mergePages", mergeConfigs);
1739
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `MergePages`, "Begin", configIds);
1740
- const newDocPtr = this.pdfiumModule.FPDF_CreateNewDocument();
1741
- if (!newDocPtr) {
1742
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `MergePages`, "End", configIds);
1743
- return PdfTaskHelper.reject({
1744
- code: PdfErrorCode.CantCreateNewDoc,
1745
- message: "Cannot create new document"
1746
- });
1747
- }
1748
- try {
1749
- for (const config of [...mergeConfigs].reverse()) {
1750
- const ctx = this.cache.getContext(config.docId);
1751
- if (!ctx) {
1752
- this.logger.warn(
1753
- LOG_SOURCE,
1754
- LOG_CATEGORY,
1755
- `Document ${config.docId} is not open, skipping`
1756
- );
1757
- continue;
1758
- }
1759
- const pageCount = this.pdfiumModule.FPDF_GetPageCount(ctx.docPtr);
1760
- const validPageIndices = config.pageIndices.filter(
1761
- (index) => index >= 0 && index < pageCount
1762
- );
1763
- if (validPageIndices.length === 0) {
1764
- continue;
1765
- }
1766
- const pageString = validPageIndices.map((index) => index + 1).join(",");
1767
- try {
1768
- if (!this.pdfiumModule.FPDF_ImportPages(
1769
- newDocPtr,
1770
- ctx.docPtr,
1771
- pageString,
1772
- 0
1773
- // Insert at the beginning
1774
- )) {
1775
- throw new Error(`Failed to import pages ${pageString} from document ${config.docId}`);
1776
- }
1777
- } finally {
1778
- }
1779
- }
1780
- const buffer = this.saveDocument(newDocPtr);
1781
- const file = {
1782
- id: `${Math.random()}`,
1783
- content: buffer
1784
- };
1785
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `MergePages`, "End", configIds);
1786
- return PdfTaskHelper.resolve(file);
1787
- } catch (error) {
1788
- this.logger.error(LOG_SOURCE, LOG_CATEGORY, "mergePages failed", error);
1789
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `MergePages`, "End", configIds);
1790
- return PdfTaskHelper.reject({
1791
- code: PdfErrorCode.CantImportPages,
1792
- message: error instanceof Error ? error.message : "Failed to merge pages"
1793
- });
1794
- } finally {
1795
- if (newDocPtr) {
1796
- this.pdfiumModule.FPDF_CloseDocument(newDocPtr);
1797
- }
1798
- }
1799
- }
1800
- /**
1801
- * {@inheritDoc @embedpdf/models!PdfEngine.saveAsCopy}
1802
- *
1803
- * @public
1804
- */
1805
- saveAsCopy(doc) {
1806
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "saveAsCopy", doc);
1807
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `SaveAsCopy`, "Begin", doc.id);
1808
- const ctx = this.cache.getContext(doc.id);
1809
- if (!ctx) {
1810
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `SaveAsCopy`, "End", doc.id);
1811
- return PdfTaskHelper.reject({
1812
- code: PdfErrorCode.DocNotOpen,
1813
- message: "document does not open"
1814
- });
1815
- }
1816
- const buffer = this.saveDocument(ctx.docPtr);
1817
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `SaveAsCopy`, "End", doc.id);
1818
- return PdfTaskHelper.resolve(buffer);
1819
- }
1820
- /**
1821
- * {@inheritDoc @embedpdf/models!PdfEngine.closeDocument}
1822
- *
1823
- * @public
1824
- */
1825
- closeDocument(doc) {
1826
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "closeDocument", doc);
1827
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `CloseDocument`, "Begin", doc.id);
1828
- const ctx = this.cache.getContext(doc.id);
1829
- if (!ctx) {
1830
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `CloseDocument`, "End", doc.id);
1831
- return PdfTaskHelper.reject({
1832
- code: PdfErrorCode.DocNotOpen,
1833
- message: "document does not open"
1834
- });
1835
- }
1836
- ctx.dispose();
1837
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `CloseDocument`, "End", doc.id);
1838
- return PdfTaskHelper.resolve(true);
1839
- }
1840
- /**
1841
- * Memory allocation
1842
- * @param size - size of memory space
1843
- * @returns pointer to memory space
1844
- *
1845
- * @public
1846
- */
1847
- malloc(size) {
1848
- const ptr = this.pdfiumModule.pdfium.wasmExports.malloc(size);
1849
- for (let i = 0; i < size; i++) {
1850
- this.pdfiumModule.pdfium.HEAP8[ptr + i] = 0;
1851
- }
1852
- return ptr;
1853
- }
1854
- /**
1855
- * Free memory space
1856
- * @param ptr pointer to memory space
1857
- *
1858
- * @public
1859
- */
1860
- free(ptr) {
1861
- this.pdfiumModule.pdfium.wasmExports.free(ptr);
1862
- }
1863
- /**
1864
- * Set the rect of specified annotation
1865
- * @param page - page info that the annotation is belonged to
1866
- * @param pagePtr - pointer of page object
1867
- * @param annotationPtr - pointer to annotation object
1868
- * @param inkList - ink lists that added to the annotation
1869
- * @returns whether the ink lists is setted
1870
- *
1871
- * @private
1872
- */
1873
- addInkStroke(page, pagePtr, annotationPtr, inkList) {
1874
- for (const inkStroke of inkList) {
1875
- const inkPointsCount = inkStroke.points.length;
1876
- const inkPointsPtr = this.malloc(inkPointsCount * 8);
1877
- for (let i = 0; i < inkPointsCount; i++) {
1878
- const point = inkStroke.points[i];
1879
- const { x, y } = this.convertDevicePointToPagePoint(page, point);
1880
- this.pdfiumModule.pdfium.setValue(inkPointsPtr + i * 8, x, "float");
1881
- this.pdfiumModule.pdfium.setValue(inkPointsPtr + i * 8 + 4, y, "float");
1882
- }
1883
- if (this.pdfiumModule.FPDFAnnot_AddInkStroke(annotationPtr, inkPointsPtr, inkPointsCount) === -1) {
1884
- this.free(inkPointsPtr);
1885
- return false;
1886
- }
1887
- this.free(inkPointsPtr);
1888
- }
1889
- return true;
1890
- }
1891
- /**
1892
- * Add contents to stamp annotation
1893
- * @param docPtr - pointer to pdf document object
1894
- * @param page - page info
1895
- * @param pagePtr - pointer to page object
1896
- * @param annotationPtr - pointer to stamp annotation
1897
- * @param rect - rect of stamp annotation
1898
- * @param contents - contents of stamp annotation
1899
- * @returns whether contents is added to annotation
1900
- *
1901
- * @private
1902
- */
1903
- addStampContent(docPtr, page, pagePtr, annotationPtr, rect, contents) {
1904
- for (const content of contents) {
1905
- switch (content.type) {
1906
- case PdfPageObjectType.IMAGE:
1907
- return this.addImageObject(
1908
- docPtr,
1909
- page,
1910
- pagePtr,
1911
- annotationPtr,
1912
- rect.origin,
1913
- content.imageData
1914
- );
1915
- }
1916
- }
1917
- return false;
1918
- }
1919
- /**
1920
- * Add image object to annotation
1921
- * @param docPtr - pointer to pdf document object
1922
- * @param page - page info
1923
- * @param pagePtr - pointer to page object
1924
- * @param annotationPtr - pointer to stamp annotation
1925
- * @param position - position of image
1926
- * @param imageData - data of image
1927
- * @returns whether image is added to annotation
1928
- *
1929
- * @private
1930
- */
1931
- addImageObject(docPtr, page, pagePtr, annotationPtr, position, imageData) {
1932
- const bytesPerPixel = 4;
1933
- const pixelCount = imageData.width * imageData.height;
1934
- const bitmapBufferPtr = this.malloc(bytesPerPixel * pixelCount);
1935
- if (!bitmapBufferPtr) {
1936
- return false;
1937
- }
1938
- for (let i = 0; i < pixelCount; i++) {
1939
- const red = imageData.data[i * bytesPerPixel];
1940
- const green = imageData.data[i * bytesPerPixel + 1];
1941
- const blue = imageData.data[i * bytesPerPixel + 2];
1942
- const alpha = imageData.data[i * bytesPerPixel + 3];
1943
- this.pdfiumModule.pdfium.setValue(bitmapBufferPtr + i * bytesPerPixel, blue, "i8");
1944
- this.pdfiumModule.pdfium.setValue(bitmapBufferPtr + i * bytesPerPixel + 1, green, "i8");
1945
- this.pdfiumModule.pdfium.setValue(bitmapBufferPtr + i * bytesPerPixel + 2, red, "i8");
1946
- this.pdfiumModule.pdfium.setValue(bitmapBufferPtr + i * bytesPerPixel + 3, alpha, "i8");
1947
- }
1948
- const format = 4 /* Bitmap_BGRA */;
1949
- const bitmapPtr = this.pdfiumModule.FPDFBitmap_CreateEx(
1950
- imageData.width,
1951
- imageData.height,
1952
- format,
1953
- bitmapBufferPtr,
1954
- 0
1955
- );
1956
- if (!bitmapPtr) {
1957
- this.free(bitmapBufferPtr);
1958
- return false;
1959
- }
1960
- const imageObjectPtr = this.pdfiumModule.FPDFPageObj_NewImageObj(docPtr);
1961
- if (!imageObjectPtr) {
1962
- this.pdfiumModule.FPDFBitmap_Destroy(bitmapPtr);
1963
- this.free(bitmapBufferPtr);
1964
- return false;
1965
- }
1966
- if (!this.pdfiumModule.FPDFImageObj_SetBitmap(pagePtr, 0, imageObjectPtr, bitmapPtr)) {
1967
- this.pdfiumModule.FPDFBitmap_Destroy(bitmapPtr);
1968
- this.pdfiumModule.FPDFPageObj_Destroy(imageObjectPtr);
1969
- this.free(bitmapBufferPtr);
1970
- return false;
1971
- }
1972
- const matrixPtr = this.malloc(6 * 4);
1973
- this.pdfiumModule.pdfium.setValue(matrixPtr, imageData.width, "float");
1974
- this.pdfiumModule.pdfium.setValue(matrixPtr + 4, 0, "float");
1975
- this.pdfiumModule.pdfium.setValue(matrixPtr + 8, 0, "float");
1976
- this.pdfiumModule.pdfium.setValue(matrixPtr + 12, imageData.height, "float");
1977
- this.pdfiumModule.pdfium.setValue(matrixPtr + 16, 0, "float");
1978
- this.pdfiumModule.pdfium.setValue(matrixPtr + 20, 0, "float");
1979
- if (!this.pdfiumModule.FPDFPageObj_SetMatrix(imageObjectPtr, matrixPtr)) {
1980
- this.free(matrixPtr);
1981
- this.pdfiumModule.FPDFBitmap_Destroy(bitmapPtr);
1982
- this.pdfiumModule.FPDFPageObj_Destroy(imageObjectPtr);
1983
- this.free(bitmapBufferPtr);
1984
- return false;
1985
- }
1986
- this.free(matrixPtr);
1987
- this.pdfiumModule.FPDFPageObj_Transform(imageObjectPtr, 1, 0, 0, 1, position.x, position.y);
1988
- if (!this.pdfiumModule.FPDFAnnot_AppendObject(annotationPtr, imageObjectPtr)) {
1989
- this.pdfiumModule.FPDFBitmap_Destroy(bitmapPtr);
1990
- this.pdfiumModule.FPDFPageObj_Destroy(imageObjectPtr);
1991
- this.free(bitmapBufferPtr);
1992
- return false;
1993
- }
1994
- this.pdfiumModule.FPDFPage_GenerateContent(pagePtr);
1995
- this.pdfiumModule.FPDFBitmap_Destroy(bitmapPtr);
1996
- this.free(bitmapBufferPtr);
1997
- return true;
1998
- }
1999
- /**
2000
- * Save document to array buffer
2001
- * @param docPtr - pointer to pdf document
2002
- * @returns array buffer contains the pdf content
2003
- *
2004
- * @private
2005
- */
2006
- saveDocument(docPtr) {
2007
- const writerPtr = this.pdfiumModule.PDFiumExt_OpenFileWriter();
2008
- this.pdfiumModule.PDFiumExt_SaveAsCopy(docPtr, writerPtr);
2009
- const size = this.pdfiumModule.PDFiumExt_GetFileWriterSize(writerPtr);
2010
- const dataPtr = this.malloc(size);
2011
- this.pdfiumModule.PDFiumExt_GetFileWriterData(writerPtr, dataPtr, size);
2012
- const buffer = new ArrayBuffer(size);
2013
- const view = new DataView(buffer);
2014
- for (let i = 0; i < size; i++) {
2015
- view.setInt8(i, this.pdfiumModule.pdfium.getValue(dataPtr + i, "i8"));
2016
- }
2017
- this.free(dataPtr);
2018
- this.pdfiumModule.PDFiumExt_CloseFileWriter(writerPtr);
2019
- return buffer;
2020
- }
2021
- /**
2022
- * Read metadata from pdf document
2023
- * @param docPtr - pointer to pdf document
2024
- * @param key - key of metadata field
2025
- * @returns metadata value
2026
- *
2027
- * @private
2028
- */
2029
- readMetaText(docPtr, key) {
2030
- return readString(
2031
- this.pdfiumModule.pdfium,
2032
- (buffer, bufferLength) => {
2033
- return this.pdfiumModule.FPDF_GetMetaText(docPtr, key, buffer, bufferLength);
2034
- },
2035
- this.pdfiumModule.pdfium.UTF16ToString
2036
- );
2037
- }
2038
- /**
2039
- * Read bookmarks in the pdf document
2040
- * @param docPtr - pointer to pdf document
2041
- * @param rootBookmarkPtr - pointer to root bookmark
2042
- * @returns bookmarks in the pdf document
2043
- *
2044
- * @private
2045
- */
2046
- readPdfBookmarks(docPtr, rootBookmarkPtr = 0) {
2047
- let bookmarkPtr = this.pdfiumModule.FPDFBookmark_GetFirstChild(docPtr, rootBookmarkPtr);
2048
- const bookmarks = [];
2049
- while (bookmarkPtr) {
2050
- const bookmark = this.readPdfBookmark(docPtr, bookmarkPtr);
2051
- bookmarks.push(bookmark);
2052
- const nextBookmarkPtr = this.pdfiumModule.FPDFBookmark_GetNextSibling(docPtr, bookmarkPtr);
2053
- bookmarkPtr = nextBookmarkPtr;
2054
- }
2055
- return bookmarks;
2056
- }
2057
- /**
2058
- * Read bookmark in the pdf document
2059
- * @param docPtr - pointer to pdf document
2060
- * @param bookmarkPtr - pointer to bookmark object
2061
- * @returns pdf bookmark object
2062
- *
2063
- * @private
2064
- */
2065
- readPdfBookmark(docPtr, bookmarkPtr) {
2066
- const title = readString(
2067
- this.pdfiumModule.pdfium,
2068
- (buffer, bufferLength) => {
2069
- return this.pdfiumModule.FPDFBookmark_GetTitle(bookmarkPtr, buffer, bufferLength);
2070
- },
2071
- this.pdfiumModule.pdfium.UTF16ToString
2072
- );
2073
- const bookmarks = this.readPdfBookmarks(docPtr, bookmarkPtr);
2074
- const target = this.readPdfBookmarkTarget(
2075
- docPtr,
2076
- () => {
2077
- return this.pdfiumModule.FPDFBookmark_GetAction(bookmarkPtr);
2078
- },
2079
- () => {
2080
- return this.pdfiumModule.FPDFBookmark_GetDest(docPtr, bookmarkPtr);
2081
- }
2082
- );
2083
- return {
2084
- title,
2085
- target,
2086
- children: bookmarks
2087
- };
2088
- }
2089
- /**
2090
- * Read text rects in pdf page
2091
- * @param page - pdf page info
2092
- * @param docPtr - pointer to pdf document
2093
- * @param pagePtr - pointer to pdf page
2094
- * @param textPagePtr - pointer to pdf text page
2095
- * @returns text rects in the pdf page
2096
- *
2097
- * @public
2098
- */
2099
- readPageTextRects(page, docPtr, pagePtr, textPagePtr) {
2100
- const rectsCount = this.pdfiumModule.FPDFText_CountRects(textPagePtr, 0, -1);
2101
- const textRects = [];
2102
- for (let i = 0; i < rectsCount; i++) {
2103
- const topPtr = this.malloc(8);
2104
- const leftPtr = this.malloc(8);
2105
- const rightPtr = this.malloc(8);
2106
- const bottomPtr = this.malloc(8);
2107
- const isSucceed = this.pdfiumModule.FPDFText_GetRect(
2108
- textPagePtr,
2109
- i,
2110
- leftPtr,
2111
- topPtr,
2112
- rightPtr,
2113
- bottomPtr
2114
- );
2115
- if (!isSucceed) {
2116
- this.free(leftPtr);
2117
- this.free(topPtr);
2118
- this.free(rightPtr);
2119
- this.free(bottomPtr);
2120
- continue;
2121
- }
2122
- const left = this.pdfiumModule.pdfium.getValue(leftPtr, "double");
2123
- const top = this.pdfiumModule.pdfium.getValue(topPtr, "double");
2124
- const right = this.pdfiumModule.pdfium.getValue(rightPtr, "double");
2125
- const bottom = this.pdfiumModule.pdfium.getValue(bottomPtr, "double");
2126
- this.free(leftPtr);
2127
- this.free(topPtr);
2128
- this.free(rightPtr);
2129
- this.free(bottomPtr);
2130
- const deviceXPtr = this.malloc(4);
2131
- const deviceYPtr = this.malloc(4);
2132
- this.pdfiumModule.FPDF_PageToDevice(
2133
- pagePtr,
2134
- 0,
2135
- 0,
2136
- page.size.width,
2137
- page.size.height,
2138
- 0,
2139
- left,
2140
- top,
2141
- deviceXPtr,
2142
- deviceYPtr
2143
- );
2144
- const x = this.pdfiumModule.pdfium.getValue(deviceXPtr, "i32");
2145
- const y = this.pdfiumModule.pdfium.getValue(deviceYPtr, "i32");
2146
- this.free(deviceXPtr);
2147
- this.free(deviceYPtr);
2148
- const rect = {
2149
- origin: {
2150
- x,
2151
- y
2152
- },
2153
- size: {
2154
- width: Math.ceil(Math.abs(right - left)),
2155
- height: Math.ceil(Math.abs(top - bottom))
2156
- }
2157
- };
2158
- const utf16Length = this.pdfiumModule.FPDFText_GetBoundedText(
2159
- textPagePtr,
2160
- left,
2161
- top,
2162
- right,
2163
- bottom,
2164
- 0,
2165
- 0
2166
- );
2167
- const bytesCount = (utf16Length + 1) * 2;
2168
- const textBuffer = this.malloc(bytesCount);
2169
- this.pdfiumModule.FPDFText_GetBoundedText(
2170
- textPagePtr,
2171
- left,
2172
- top,
2173
- right,
2174
- bottom,
2175
- textBuffer,
2176
- utf16Length
2177
- );
2178
- const content = this.pdfiumModule.pdfium.UTF16ToString(textBuffer);
2179
- this.free(textBuffer);
2180
- const charIndex = this.pdfiumModule.FPDFText_GetCharIndexAtPos(textPagePtr, left, top, 2, 2);
2181
- let fontFamily = "";
2182
- let fontSize = rect.size.height;
2183
- if (charIndex >= 0) {
2184
- fontSize = this.pdfiumModule.FPDFText_GetFontSize(textPagePtr, charIndex);
2185
- const fontNameLength = this.pdfiumModule.FPDFText_GetFontInfo(
2186
- textPagePtr,
2187
- charIndex,
2188
- 0,
2189
- 0,
2190
- 0
2191
- );
2192
- const bytesCount2 = fontNameLength + 1;
2193
- const textBufferPtr = this.malloc(bytesCount2);
2194
- const flagsPtr = this.malloc(4);
2195
- this.pdfiumModule.FPDFText_GetFontInfo(
2196
- textPagePtr,
2197
- charIndex,
2198
- textBufferPtr,
2199
- bytesCount2,
2200
- flagsPtr
2201
- );
2202
- fontFamily = this.pdfiumModule.pdfium.UTF8ToString(textBufferPtr);
2203
- this.free(textBufferPtr);
2204
- this.free(flagsPtr);
2205
- }
2206
- const textRect = {
2207
- content,
2208
- rect,
2209
- font: {
2210
- family: fontFamily,
2211
- size: fontSize
2212
- }
2213
- };
2214
- textRects.push(textRect);
2215
- }
2216
- return textRects;
2217
- }
2218
- /**
2219
- * Return geometric + logical text layout for one page
2220
- * (glyph-only implementation, no FPDFText_GetRect).
2221
- *
2222
- * @public
2223
- */
2224
- getPageGeometry(doc, page) {
2225
- const label = "getPageGeometry";
2226
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, label, "Begin", doc.id);
2227
- const ctx = this.cache.getContext(doc.id);
2228
- if (!ctx) {
2229
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, label, "End", doc.id);
2230
- return PdfTaskHelper.reject({
2231
- code: PdfErrorCode.DocNotOpen,
2232
- message: "document does not open"
2233
- });
2234
- }
2235
- const pageCtx = ctx.acquirePage(page.index);
2236
- const textPagePtr = pageCtx.getTextPage();
2237
- const glyphCount = this.pdfiumModule.FPDFText_CountChars(textPagePtr);
2238
- const glyphs = [];
2239
- for (let i = 0; i < glyphCount; i++) {
2240
- const g = this.readGlyphInfo(page, pageCtx.pagePtr, textPagePtr, i);
2241
- glyphs.push(g);
2242
- }
2243
- const runs = this.buildRunsFromGlyphs(glyphs, textPagePtr);
2244
- pageCtx.release();
2245
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, label, "End", doc.id);
2246
- return PdfTaskHelper.resolve({ runs });
2247
- }
2248
- /**
2249
- * Group consecutive glyphs that belong to the same CPDF_TextObject
2250
- * using FPDFText_GetTextObject(), and calculate rotation from glyph positions.
2251
- */
2252
- buildRunsFromGlyphs(glyphs, textPagePtr) {
2253
- const runs = [];
2254
- let current = null;
2255
- let curObjPtr = null;
2256
- for (let i = 0; i < glyphs.length; i++) {
2257
- const g = glyphs[i];
2258
- const objPtr = this.pdfiumModule.FPDFText_GetTextObject(textPagePtr, i);
2259
- if (g.isEmpty) {
2260
- continue;
2261
- }
2262
- if (objPtr !== curObjPtr) {
2263
- curObjPtr = objPtr;
2264
- current = {
2265
- rect: {
2266
- x: g.origin.x,
2267
- y: g.origin.y,
2268
- width: g.size.width,
2269
- height: g.size.height
2270
- },
2271
- charStart: i,
2272
- glyphs: []
2273
- };
2274
- runs.push(current);
2275
- }
2276
- current.glyphs.push({
2277
- x: g.origin.x,
2278
- y: g.origin.y,
2279
- width: g.size.width,
2280
- height: g.size.height,
2281
- flags: g.isSpace ? 1 : 0
2282
- });
2283
- const right = g.origin.x + g.size.width;
2284
- const bottom = g.origin.y + g.size.height;
2285
- current.rect.width = Math.max(current.rect.x + current.rect.width, right) - current.rect.x;
2286
- current.rect.y = Math.min(current.rect.y, g.origin.y);
2287
- current.rect.height = Math.max(current.rect.y + current.rect.height, bottom) - current.rect.y;
2288
- }
2289
- return runs;
2290
- }
2291
- /**
2292
- * Extract glyph geometry + metadata for `charIndex`
2293
- *
2294
- * Returns device–space coordinates:
2295
- * x,y → **top-left** corner (integer-pixels)
2296
- * w,h → width / height (integer-pixels, ≥ 1)
2297
- *
2298
- * And two flags:
2299
- * isSpace → true if the glyph's Unicode code-point is U+0020
2300
- */
2301
- readGlyphInfo(page, pagePtr, textPagePtr, charIndex) {
2302
- const dx1Ptr = this.malloc(4);
2303
- const dy1Ptr = this.malloc(4);
2304
- const dx2Ptr = this.malloc(4);
2305
- const dy2Ptr = this.malloc(4);
2306
- const rectPtr = this.malloc(16);
2307
- let x = 0, y = 0, width = 0, height = 0, isSpace = false;
2308
- if (this.pdfiumModule.FPDFText_GetLooseCharBox(textPagePtr, charIndex, rectPtr)) {
2309
- const left = this.pdfiumModule.pdfium.getValue(rectPtr, "float");
2310
- const top = this.pdfiumModule.pdfium.getValue(rectPtr + 4, "float");
2311
- const right = this.pdfiumModule.pdfium.getValue(rectPtr + 8, "float");
2312
- const bottom = this.pdfiumModule.pdfium.getValue(rectPtr + 12, "float");
2313
- if (left === right || top === bottom) {
2314
- return {
2315
- origin: { x: 0, y: 0 },
2316
- size: { width: 0, height: 0 },
2317
- isEmpty: true
2318
- };
2319
- }
2320
- this.pdfiumModule.FPDF_PageToDevice(
2321
- pagePtr,
2322
- 0,
2323
- 0,
2324
- page.size.width,
2325
- page.size.height,
2326
- /*rotate=*/
2327
- 0,
2328
- left,
2329
- top,
2330
- dx1Ptr,
2331
- dy1Ptr
2332
- );
2333
- this.pdfiumModule.FPDF_PageToDevice(
2334
- pagePtr,
2335
- 0,
2336
- 0,
2337
- page.size.width,
2338
- page.size.height,
2339
- /*rotate=*/
2340
- 0,
2341
- right,
2342
- bottom,
2343
- dx2Ptr,
2344
- dy2Ptr
2345
- );
2346
- const x1 = this.pdfiumModule.pdfium.getValue(dx1Ptr, "i32");
2347
- const y1 = this.pdfiumModule.pdfium.getValue(dy1Ptr, "i32");
2348
- const x2 = this.pdfiumModule.pdfium.getValue(dx2Ptr, "i32");
2349
- const y2 = this.pdfiumModule.pdfium.getValue(dy2Ptr, "i32");
2350
- x = Math.min(x1, x2);
2351
- y = Math.min(y1, y2);
2352
- width = Math.max(1, Math.abs(x2 - x1));
2353
- height = Math.max(1, Math.abs(y2 - y1));
2354
- const uc = this.pdfiumModule.FPDFText_GetUnicode(textPagePtr, charIndex);
2355
- isSpace = uc === 32;
2356
- }
2357
- [rectPtr, dx1Ptr, dy1Ptr, dx2Ptr, dy2Ptr].forEach((p) => this.free(p));
2358
- return {
2359
- origin: { x, y },
2360
- size: { width, height },
2361
- ...isSpace && { isSpace }
2362
- };
2363
- }
2364
- /**
2365
- * Geometry-only text extraction
2366
- * ------------------------------------------
2367
- * Returns every glyph on the requested page
2368
- * in the logical order delivered by PDFium.
2369
- *
2370
- * The promise resolves to an array of objects:
2371
- * {
2372
- * idx: number; // glyph index on the page (0…n-1)
2373
- * origin: { x: number; y: number };
2374
- * size: { width: number; height: number };
2375
- * angle: number; // degrees, counter-clock-wise
2376
- * isSpace: boolean; // true → U+0020
2377
- * }
2378
- *
2379
- * No Unicode is included; front-end decides whether to hydrate it.
2380
- */
2381
- getPageGlyphs(doc, page) {
2382
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "getPageGlyphs", doc, page);
2383
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, "getPageGlyphs", "Begin", doc.id);
2384
- const ctx = this.cache.getContext(doc.id);
2385
- if (!ctx) {
2386
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, "getPageGlyphs", "End", doc.id);
2387
- return PdfTaskHelper.reject({
2388
- code: PdfErrorCode.DocNotOpen,
2389
- message: "document does not open"
2390
- });
2391
- }
2392
- const pageCtx = ctx.acquirePage(page.index);
2393
- const textPagePtr = pageCtx.getTextPage();
2394
- const total = this.pdfiumModule.FPDFText_CountChars(textPagePtr);
2395
- const glyphs = new Array(total);
2396
- for (let i = 0; i < total; i++) {
2397
- const g = this.readGlyphInfo(page, pageCtx.pagePtr, textPagePtr, i);
2398
- if (g.isEmpty) {
2399
- continue;
2400
- }
2401
- glyphs[i] = { ...g };
2402
- }
2403
- pageCtx.release();
2404
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, "getPageGlyphs", "End", doc.id);
2405
- return PdfTaskHelper.resolve(glyphs);
2406
- }
2407
- readCharBox(page, pagePtr, textPagePtr, charIndex) {
2408
- const topPtr = this.malloc(8);
2409
- const leftPtr = this.malloc(8);
2410
- const bottomPtr = this.malloc(8);
2411
- const rightPtr = this.malloc(8);
2412
- let x = 0;
2413
- let y = 0;
2414
- let width = 0;
2415
- let height = 0;
2416
- if (this.pdfiumModule.FPDFText_GetCharBox(
2417
- textPagePtr,
2418
- charIndex,
2419
- leftPtr,
2420
- rightPtr,
2421
- bottomPtr,
2422
- topPtr
2423
- )) {
2424
- const top = this.pdfiumModule.pdfium.getValue(topPtr, "double");
2425
- const left = this.pdfiumModule.pdfium.getValue(leftPtr, "double");
2426
- const bottom = this.pdfiumModule.pdfium.getValue(bottomPtr, "double");
2427
- const right = this.pdfiumModule.pdfium.getValue(rightPtr, "double");
2428
- const deviceXPtr = this.malloc(4);
2429
- const deviceYPtr = this.malloc(4);
2430
- this.pdfiumModule.FPDF_PageToDevice(
2431
- pagePtr,
2432
- 0,
2433
- 0,
2434
- page.size.width,
2435
- page.size.height,
2436
- 0,
2437
- left,
2438
- top,
2439
- deviceXPtr,
2440
- deviceYPtr
2441
- );
2442
- x = this.pdfiumModule.pdfium.getValue(deviceXPtr, "i32");
2443
- y = this.pdfiumModule.pdfium.getValue(deviceYPtr, "i32");
2444
- this.free(deviceXPtr);
2445
- this.free(deviceYPtr);
2446
- width = Math.ceil(Math.abs(right - left));
2447
- height = Math.ceil(Math.abs(top - bottom));
2448
- }
2449
- this.free(topPtr);
2450
- this.free(leftPtr);
2451
- this.free(bottomPtr);
2452
- this.free(rightPtr);
2453
- return {
2454
- origin: {
2455
- x,
2456
- y
2457
- },
2458
- size: {
2459
- width,
2460
- height
2461
- }
2462
- };
2463
- }
2464
- /**
2465
- * Read page annotations
2466
- * @param page - page info
2467
- * @param docPtr - pointer to pdf document
2468
- * @param pagePtr - pointer to pdf page
2469
- * @param textPagePtr - pointe to pdf text page
2470
- * @param scaleFactor - scale factor
2471
- * @param rotation - rotation angle
2472
- * @returns annotations on the pdf page
2473
- *
2474
- * @private
2475
- */
2476
- readPageAnnotations(ctx, page) {
2477
- const pageCtx = ctx.acquirePage(page.index);
2478
- const annotationCount = this.pdfiumModule.FPDFPage_GetAnnotCount(pageCtx.pagePtr);
2479
- const annotations = [];
2480
- for (let i = 0; i < annotationCount; i++) {
2481
- pageCtx.withAnnotation(i, (annotPtr) => {
2482
- const annotation = this.readPageAnnotation(page, pageCtx, annotPtr, i);
2483
- if (annotation) {
2484
- annotations.push(annotation);
2485
- }
2486
- });
2487
- }
2488
- return annotations;
2489
- }
2490
- /**
2491
- * Read pdf annotation from pdf document
2492
- * @param page - pdf page infor
2493
- * @param docPtr - pointer to pdf document object
2494
- * @param pagePtr - pointer to pdf page object
2495
- * @param textPagePtr - pointer to pdf text page object
2496
- * @param formHandle - form handle
2497
- * @param index - index of annotation in the pdf page
2498
- * @param scaleFactor - factor of scalling
2499
- * @param rotation - rotation angle
2500
- * @returns pdf annotation
2501
- *
2502
- * @private
2503
- */
2504
- readPageAnnotation(page, pageCtx, annotationPtr, index) {
2505
- const subType = this.pdfiumModule.FPDFAnnot_GetSubtype(
2506
- annotationPtr
2507
- );
2508
- let annotation;
2509
- switch (subType) {
2510
- case PdfAnnotationSubtype.TEXT:
2511
- {
2512
- annotation = this.readPdfTextAnno(page, pageCtx.pagePtr, annotationPtr, index);
2513
- }
2514
- break;
2515
- case PdfAnnotationSubtype.FREETEXT:
2516
- {
2517
- annotation = this.readPdfFreeTextAnno(page, pageCtx.pagePtr, annotationPtr, index);
2518
- }
2519
- break;
2520
- case PdfAnnotationSubtype.LINK:
2521
- {
2522
- annotation = this.readPdfLinkAnno(
2523
- page,
2524
- pageCtx.docPtr,
2525
- pageCtx.pagePtr,
2526
- pageCtx.getTextPage(),
2527
- annotationPtr,
2528
- index
2529
- );
2530
- }
2531
- break;
2532
- case PdfAnnotationSubtype.WIDGET:
2533
- {
2534
- annotation = this.readPdfWidgetAnno(
2535
- page,
2536
- pageCtx.pagePtr,
2537
- annotationPtr,
2538
- pageCtx.getFormHandle(),
2539
- index
2540
- );
2541
- }
2542
- break;
2543
- case PdfAnnotationSubtype.FILEATTACHMENT:
2544
- {
2545
- annotation = this.readPdfFileAttachmentAnno(page, pageCtx.pagePtr, annotationPtr, index);
2546
- }
2547
- break;
2548
- case PdfAnnotationSubtype.INK:
2549
- {
2550
- annotation = this.readPdfInkAnno(page, pageCtx.pagePtr, annotationPtr, index);
2551
- }
2552
- break;
2553
- case PdfAnnotationSubtype.POLYGON:
2554
- {
2555
- annotation = this.readPdfPolygonAnno(page, pageCtx.pagePtr, annotationPtr, index);
2556
- }
2557
- break;
2558
- case PdfAnnotationSubtype.POLYLINE:
2559
- {
2560
- annotation = this.readPdfPolylineAnno(page, pageCtx.pagePtr, annotationPtr, index);
2561
- }
2562
- break;
2563
- case PdfAnnotationSubtype.LINE:
2564
- {
2565
- annotation = this.readPdfLineAnno(page, pageCtx.pagePtr, annotationPtr, index);
2566
- }
2567
- break;
2568
- case PdfAnnotationSubtype.HIGHLIGHT:
2569
- annotation = this.readPdfHighlightAnno(page, pageCtx.pagePtr, annotationPtr, index);
2570
- break;
2571
- case PdfAnnotationSubtype.STAMP:
2572
- {
2573
- annotation = this.readPdfStampAnno(
2574
- pageCtx.docPtr,
2575
- page,
2576
- pageCtx.pagePtr,
2577
- annotationPtr,
2578
- index
2579
- );
2580
- }
2581
- break;
2582
- case PdfAnnotationSubtype.SQUARE:
2583
- {
2584
- annotation = this.readPdfSquareAnno(page, pageCtx.pagePtr, annotationPtr, index);
2585
- }
2586
- break;
2587
- case PdfAnnotationSubtype.CIRCLE:
2588
- {
2589
- annotation = this.readPdfCircleAnno(page, pageCtx.pagePtr, annotationPtr, index);
2590
- }
2591
- break;
2592
- case PdfAnnotationSubtype.UNDERLINE:
2593
- {
2594
- annotation = this.readPdfUnderlineAnno(page, pageCtx.pagePtr, annotationPtr, index);
2595
- }
2596
- break;
2597
- case PdfAnnotationSubtype.SQUIGGLY:
2598
- {
2599
- annotation = this.readPdfSquigglyAnno(page, pageCtx.pagePtr, annotationPtr, index);
2600
- }
2601
- break;
2602
- case PdfAnnotationSubtype.STRIKEOUT:
2603
- {
2604
- annotation = this.readPdfStrikeOutAnno(page, pageCtx.pagePtr, annotationPtr, index);
2605
- }
2606
- break;
2607
- case PdfAnnotationSubtype.CARET:
2608
- {
2609
- annotation = this.readPdfCaretAnno(page, pageCtx.pagePtr, annotationPtr, index);
2610
- }
2611
- break;
2612
- case PdfAnnotationSubtype.POPUP:
2613
- break;
2614
- default:
2615
- {
2616
- annotation = this.readPdfAnno(page, pageCtx.pagePtr, subType, annotationPtr, index);
2617
- }
2618
- break;
2619
- }
2620
- return annotation;
2621
- }
2622
- /**
2623
- * Return the colour stored directly in the annotation dictionary's `/C` entry.
2624
- *
2625
- * Most PDFs created by Acrobat, Microsoft Office, LaTeX, etc. include this entry.
2626
- * When the key is absent (common in macOS Preview, Chrome, Drawboard) the call
2627
- * fails and the function returns `undefined`.
2628
- *
2629
- * @param annotationPtr - pointer to an `FPDF_ANNOTATION`
2630
- * @returns An RGBA tuple (0-255 channels) or `undefined` if no `/C` entry exists
2631
- *
2632
- * @private
2633
- */
2634
- readAnnotationColor(annotationPtr) {
2635
- const rPtr = this.malloc(4);
2636
- const gPtr = this.malloc(4);
2637
- const bPtr = this.malloc(4);
2638
- const aPtr = this.malloc(4);
2639
- const ok = this.pdfiumModule.FPDFAnnot_GetColor(
2640
- annotationPtr,
2641
- /* colorType = */
2642
- 0,
2643
- rPtr,
2644
- gPtr,
2645
- bPtr,
2646
- aPtr
2647
- );
2648
- let colour;
2649
- if (ok) {
2650
- colour = {
2651
- red: this.pdfiumModule.pdfium.getValue(rPtr, "i32") & 255,
2652
- green: this.pdfiumModule.pdfium.getValue(gPtr, "i32") & 255,
2653
- blue: this.pdfiumModule.pdfium.getValue(bPtr, "i32") & 255,
2654
- alpha: this.pdfiumModule.pdfium.getValue(aPtr, "i32") & 255
2655
- // 0 = transparent, 255 = opaque
2656
- };
2657
- }
2658
- this.free(rPtr);
2659
- this.free(gPtr);
2660
- this.free(bPtr);
2661
- this.free(aPtr);
2662
- return colour;
2663
- }
2664
- /* --------------------------------------------------------------------------- */
2665
- /**
2666
- * Extract the fill (or, if absent, the stroke) colour from a **path object**
2667
- * inside an appearance stream.
2668
- *
2669
- * Works for simple highlights produced by Chrome, Preview, etc. that paint a
2670
- * single filled rectangle with the desired tint.
2671
- *
2672
- * @param pathPtr - pointer to a `FPDF_PAGEOBJECT` of type **PATH**
2673
- * @returns RGBA tuple or `undefined` when no colour is set on the path
2674
- *
2675
- * @private
2676
- */
2677
- getColorFromPath(pathPtr) {
2678
- const r = this.malloc(4), g = this.malloc(4), b = this.malloc(4), a = this.malloc(4);
2679
- const fillOk = this.pdfiumModule.FPDFPageObj_GetFillColor(pathPtr, r, g, b, a);
2680
- const strokeOk = !fillOk && // try stroke only if fill failed
2681
- this.pdfiumModule.FPDFPageObj_GetStrokeColor(pathPtr, r, g, b, a);
2682
- const ok = fillOk || strokeOk;
2683
- let c;
2684
- if (ok) {
2685
- c = {
2686
- red: this.pdfiumModule.pdfium.getValue(r, "i32") & 255,
2687
- green: this.pdfiumModule.pdfium.getValue(g, "i32") & 255,
2688
- blue: this.pdfiumModule.pdfium.getValue(b, "i32") & 255,
2689
- alpha: this.pdfiumModule.pdfium.getValue(a, "i32") & 255
2690
- };
2691
- }
2692
- this.free(r);
2693
- this.free(g);
2694
- this.free(b);
2695
- this.free(a);
2696
- return c;
2697
- }
2698
- /* --------------------------------------------------------------------------- */
2699
- /**
2700
- * Recursively walk a page-object tree (PATHs and nested FORM XObjects) until
2701
- * a colour can be extracted.
2702
- *
2703
- * Acrobat often wraps its highlight rectangle in a Form XObject referenced by
2704
- * the "Do" operator, so this function drills down unlimited depth.
2705
- *
2706
- * @param objPtr - pointer to a `FPDF_PAGEOBJECT`
2707
- * @returns First RGBA tint found, or `undefined` if none of the descendants
2708
- * carry an explicit fill/stroke colour
2709
- *
2710
- * @private
2711
- */
2712
- walkPageObjTree(objPtr) {
2713
- const type = this.pdfiumModule.FPDFPageObj_GetType(objPtr);
2714
- if (type === PdfPageObjectType.PATH) return this.getColorFromPath(objPtr);
2715
- if (type !== PdfPageObjectType.FORM) return void 0;
2716
- const cnt = this.pdfiumModule.FPDFFormObj_CountObjects(objPtr);
2717
- for (let i = 0; i < cnt; i++) {
2718
- const child = this.pdfiumModule.FPDFFormObj_GetObject(objPtr, i);
2719
- if (!child) continue;
2720
- const c = this.walkPageObjTree(child);
2721
- if (c) return c;
2722
- }
2723
- return void 0;
2724
- }
2725
- /* --------------------------------------------------------------------------- */
2726
- /**
2727
- * Iterate over every top-level object in the annotation's **appearance stream**
2728
- * and invoke {@link walkPageObjTree} to locate a usable tint.
2729
- *
2730
- * Catches:
2731
- * • Simple filled path (Preview, Chrome)
2732
- * • Form XObject containing the path (Acrobat)
2733
- *
2734
- * @param annotPtr - pointer to an `FPDF_ANNOTATION`
2735
- * @returns RGBA tuple or `undefined` when no colour can be resolved from AP
2736
- *
2737
- * @private
2738
- */
2739
- colorFromAppearance(annotPtr) {
2740
- const n = this.pdfiumModule.FPDFAnnot_GetObjectCount(annotPtr);
2741
- for (let i = 0; i < n; i++) {
2742
- const obj = this.pdfiumModule.FPDFAnnot_GetObject(annotPtr, i);
2743
- if (!obj) continue;
2744
- const c = this.walkPageObjTree(obj);
2745
- if (c) return c;
2746
- }
2747
- return void 0;
2748
- }
2749
- /* --------------------------------------------------------------------------- */
2750
- /**
2751
- * Resolve the visible fill colour for **Highlight / Underline / StrikeOut /
2752
- * Squiggly** markup annotations.
2753
- *
2754
- * Resolution order (first non-`undefined` wins):
2755
- * 1. `/C` dictionary entry – fast, present in Acrobat / Office PDFs
2756
- * 2. Appearance-stream objects – drills into paths & nested forms
2757
- * 3. Hard-coded fallback (Acrobat-style opaque yellow)
2758
- *
2759
- * @param annotationPtr - pointer to an `FPDF_ANNOTATION`
2760
- * @param fallback - colour to use when the PDF stores no tint at all
2761
- * @returns Guaranteed RGBA tuple (never `undefined`)
2762
- *
2763
- * @private
2764
- */
2765
- resolveAnnotationColor(annotationPtr, fallback = { red: 255, green: 245, blue: 155, alpha: 255 }) {
2766
- return this.readAnnotationColor(annotationPtr) ?? // 1 – /C entry
2767
- this.colorFromAppearance(annotationPtr) ?? // 2 – AP stream walk
2768
- fallback;
2769
- }
2770
- /**
2771
- * Read `/QuadPoints` from any annotation and convert each quadrilateral to
2772
- * device-space coordinates.
2773
- *
2774
- * The four points are returned in natural reading order:
2775
- * `p1 → p2` (top edge) and `p4 → p3` (bottom edge).
2776
- * This preserves the true shape for rotated / skewed text, whereas callers
2777
- * that only need axis-aligned boxes can collapse each quad themselves.
2778
- *
2779
- * @param page - logical page info object (`PdfPageObject`)
2780
- * @param annotationPtr - pointer to the annotation whose quads are needed
2781
- * @returns Array of `Quad` objects (`[]` if the annotation has no quads)
2782
- *
2783
- * @private
2784
- */
2785
- readAnnotationQuads(page, annotationPtr) {
2786
- const quadCount = this.pdfiumModule.FPDFAnnot_CountAttachmentPoints(annotationPtr);
2787
- if (quadCount === 0) return [];
2788
- const FS_QUADPOINTSF_SIZE = 8 * 4;
2789
- const quads = [];
2790
- for (let qi = 0; qi < quadCount; qi++) {
2791
- const quadPtr = this.malloc(FS_QUADPOINTSF_SIZE);
2792
- const ok = this.pdfiumModule.FPDFAnnot_GetAttachmentPoints(annotationPtr, qi, quadPtr);
2793
- if (ok) {
2794
- const xs = [];
2795
- const ys = [];
2796
- for (let i = 0; i < 4; i++) {
2797
- const base = quadPtr + i * 8;
2798
- xs.push(this.pdfiumModule.pdfium.getValue(base, "float"));
2799
- ys.push(this.pdfiumModule.pdfium.getValue(base + 4, "float"));
2800
- }
2801
- const p1 = this.convertPagePointToDevicePoint(page, { x: xs[0], y: ys[0] });
2802
- const p2 = this.convertPagePointToDevicePoint(page, { x: xs[1], y: ys[1] });
2803
- const p3 = this.convertPagePointToDevicePoint(page, { x: xs[2], y: ys[2] });
2804
- const p4 = this.convertPagePointToDevicePoint(page, { x: xs[3], y: ys[3] });
2805
- quads.push({ p1, p2, p3, p4 });
2806
- }
2807
- this.free(quadPtr);
2808
- }
2809
- return quads;
2810
- }
2811
- /**
2812
- * Read pdf text annotation
2813
- * @param page - pdf page infor
2814
- * @param pagePtr - pointer to pdf page object
2815
- * @param annotationPtr - pointer to pdf annotation
2816
- * @param index - index of annotation in the pdf page
2817
- * @returns pdf text annotation
2818
- *
2819
- * @private
2820
- */
2821
- readPdfTextAnno(page, pagePtr, annotationPtr, index) {
2822
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
2823
- const annoRect = this.readPageAnnoRect(annotationPtr);
2824
- const rect = this.convertPageRectToDeviceRect(page, pagePtr, annoRect);
2825
- const author = this.getAnnotString(annotationPtr, "T");
2826
- const modifiedRaw = this.getAnnotString(annotationPtr, "M");
2827
- const modified = this.toIsoDate(modifiedRaw);
2828
- const contents = this.getAnnotString(annotationPtr, "Contents") || "";
2829
- const state = this.getAnnotString(annotationPtr, "State");
2830
- const stateModel = this.getAnnotString(annotationPtr, "StateModel");
2831
- const color = this.resolveAnnotationColor(annotationPtr);
2832
- const inReplyToId = this.getInReplyToId(pagePtr, annotationPtr);
2833
- const popup = !inReplyToId ? this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index) : void 0;
2834
- return {
2835
- status: PdfAnnotationObjectStatus.Committed,
2836
- pageIndex: page.index,
2837
- id: index,
2838
- type: PdfAnnotationSubtype.TEXT,
2839
- contents,
2840
- color,
2841
- rect,
2842
- popup,
2843
- appearances,
2844
- inReplyToId,
2845
- author,
2846
- modified,
2847
- state,
2848
- stateModel
2849
- };
2850
- }
2851
- /**
2852
- * Read pdf freetext annotation
2853
- * @param page - pdf page infor
2854
- * @param pagePtr - pointer to pdf page object
2855
- * @param annotationPtr - pointer to pdf annotation
2856
- * @param index - index of annotation in the pdf page
2857
- * @returns pdf freetext annotation
2858
- *
2859
- * @private
2860
- */
2861
- readPdfFreeTextAnno(page, pagePtr, annotationPtr, index) {
2862
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
2863
- const annoRect = this.readPageAnnoRect(annotationPtr);
2864
- const rect = this.convertPageRectToDeviceRect(page, pagePtr, annoRect);
2865
- const contents = this.getAnnotString(annotationPtr, "Contents") || "";
2866
- const author = this.getAnnotString(annotationPtr, "T");
2867
- const modifiedRaw = this.getAnnotString(annotationPtr, "M");
2868
- const modified = this.toIsoDate(modifiedRaw);
2869
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
2870
- return {
2871
- status: PdfAnnotationObjectStatus.Committed,
2872
- pageIndex: page.index,
2873
- id: index,
2874
- type: PdfAnnotationSubtype.FREETEXT,
2875
- contents,
2876
- author,
2877
- modified,
2878
- rect,
2879
- popup,
2880
- appearances
2881
- };
2882
- }
2883
- /**
2884
- * Read pdf link annotation from pdf document
2885
- * @param page - pdf page infor
2886
- * @param docPtr - pointer to pdf document object
2887
- * @param pagePtr - pointer to pdf page object
2888
- * @param textPagePtr - pointer to pdf text page object
2889
- * @param annotationPtr - pointer to pdf annotation
2890
- * @param index - index of annotation in the pdf page
2891
- * @returns pdf link annotation
2892
- *
2893
- * @private
2894
- */
2895
- readPdfLinkAnno(page, docPtr, pagePtr, textPagePtr, annotationPtr, index) {
2896
- const linkPtr = this.pdfiumModule.FPDFAnnot_GetLink(annotationPtr);
2897
- if (!linkPtr) {
2898
- return;
2899
- }
2900
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
2901
- const annoRect = this.readPageAnnoRect(annotationPtr);
2902
- const { left, top, right, bottom } = annoRect;
2903
- const rect = this.convertPageRectToDeviceRect(page, pagePtr, annoRect);
2904
- const author = this.getAnnotString(annotationPtr, "T");
2905
- const modifiedRaw = this.getAnnotString(annotationPtr, "M");
2906
- const modified = this.toIsoDate(modifiedRaw);
2907
- const utf16Length = this.pdfiumModule.FPDFText_GetBoundedText(
2908
- textPagePtr,
2909
- left,
2910
- top,
2911
- right,
2912
- bottom,
2913
- 0,
2914
- 0
2915
- );
2916
- const bytesCount = (utf16Length + 1) * 2;
2917
- const textBufferPtr = this.malloc(bytesCount);
2918
- this.pdfiumModule.FPDFText_GetBoundedText(
2919
- textPagePtr,
2920
- left,
2921
- top,
2922
- right,
2923
- bottom,
2924
- textBufferPtr,
2925
- utf16Length
2926
- );
2927
- const text = this.pdfiumModule.pdfium.UTF16ToString(textBufferPtr);
2928
- this.free(textBufferPtr);
2929
- const target = this.readPdfLinkAnnoTarget(
2930
- docPtr,
2931
- () => {
2932
- return this.pdfiumModule.FPDFLink_GetAction(linkPtr);
2933
- },
2934
- () => {
2935
- return this.pdfiumModule.FPDFLink_GetDest(docPtr, linkPtr);
2936
- }
2937
- );
2938
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
2939
- return {
2940
- status: PdfAnnotationObjectStatus.Committed,
2941
- pageIndex: page.index,
2942
- id: index,
2943
- type: PdfAnnotationSubtype.LINK,
2944
- text,
2945
- target,
2946
- rect,
2947
- popup,
2948
- appearances,
2949
- author,
2950
- modified
2951
- };
2952
- }
2953
- /**
2954
- * Read pdf widget annotation
2955
- * @param page - pdf page infor
2956
- * @param pagePtr - pointer to pdf page object
2957
- * @param annotationPtr - pointer to pdf annotation
2958
- * @param formHandle - form handle
2959
- * @param index - index of annotation in the pdf page
2960
- * @returns pdf widget annotation
2961
- *
2962
- * @private
2963
- */
2964
- readPdfWidgetAnno(page, pagePtr, annotationPtr, formHandle, index) {
2965
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
2966
- const pageRect = this.readPageAnnoRect(annotationPtr);
2967
- const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
2968
- const author = this.getAnnotString(annotationPtr, "T");
2969
- const modifiedRaw = this.getAnnotString(annotationPtr, "M");
2970
- const modified = this.toIsoDate(modifiedRaw);
2971
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
2972
- const field = this.readPdfWidgetAnnoField(formHandle, annotationPtr);
2973
- return {
2974
- status: PdfAnnotationObjectStatus.Committed,
2975
- pageIndex: page.index,
2976
- id: index,
2977
- type: PdfAnnotationSubtype.WIDGET,
2978
- rect,
2979
- field,
2980
- popup,
2981
- appearances,
2982
- author,
2983
- modified
2984
- };
2985
- }
2986
- /**
2987
- * Read pdf file attachment annotation
2988
- * @param page - pdf page infor
2989
- * @param pagePtr - pointer to pdf page object
2990
- * @param annotationPtr - pointer to pdf annotation
2991
- * @param index - index of annotation in the pdf page
2992
- * @returns pdf file attachment annotation
2993
- *
2994
- * @private
2995
- */
2996
- readPdfFileAttachmentAnno(page, pagePtr, annotationPtr, index) {
2997
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
2998
- const pageRect = this.readPageAnnoRect(annotationPtr);
2999
- const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
3000
- const author = this.getAnnotString(annotationPtr, "T");
3001
- const modifiedRaw = this.getAnnotString(annotationPtr, "M");
3002
- const modified = this.toIsoDate(modifiedRaw);
3003
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
3004
- return {
3005
- status: PdfAnnotationObjectStatus.Committed,
3006
- pageIndex: page.index,
3007
- id: index,
3008
- type: PdfAnnotationSubtype.FILEATTACHMENT,
3009
- rect,
3010
- popup,
3011
- appearances,
3012
- author,
3013
- modified
3014
- };
3015
- }
3016
- /**
3017
- * Read pdf ink annotation
3018
- * @param page - pdf page infor
3019
- * @param pagePtr - pointer to pdf page object
3020
- * @param annotationPtr - pointer to pdf annotation
3021
- * @param index - index of annotation in the pdf page
3022
- * @returns pdf ink annotation
3023
- *
3024
- * @private
3025
- */
3026
- readPdfInkAnno(page, pagePtr, annotationPtr, index) {
3027
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
3028
- const pageRect = this.readPageAnnoRect(annotationPtr);
3029
- const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
3030
- const author = this.getAnnotString(annotationPtr, "T");
3031
- const modifiedRaw = this.getAnnotString(annotationPtr, "M");
3032
- const modified = this.toIsoDate(modifiedRaw);
3033
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
3034
- const inkList = [];
3035
- const count = this.pdfiumModule.FPDFAnnot_GetInkListCount(annotationPtr);
3036
- for (let i = 0; i < count; i++) {
3037
- const points = [];
3038
- const pointsCount = this.pdfiumModule.FPDFAnnot_GetInkListPath(annotationPtr, i, 0, 0);
3039
- if (pointsCount > 0) {
3040
- const pointMemorySize = 8;
3041
- const pointsPtr = this.malloc(pointsCount * pointMemorySize);
3042
- this.pdfiumModule.FPDFAnnot_GetInkListPath(annotationPtr, i, pointsPtr, pointsCount);
3043
- for (let j = 0; j < pointsCount; j++) {
3044
- const pointX = this.pdfiumModule.pdfium.getValue(pointsPtr + j * 8, "float");
3045
- const pointY = this.pdfiumModule.pdfium.getValue(pointsPtr + j * 8 + 4, "float");
3046
- const { x, y } = this.convertPagePointToDevicePoint(page, {
3047
- x: pointX,
3048
- y: pointY
3049
- });
3050
- points.push({ x, y });
3051
- }
3052
- this.free(pointsPtr);
3053
- }
3054
- inkList.push({ points });
3055
- }
3056
- return {
3057
- status: PdfAnnotationObjectStatus.Committed,
3058
- pageIndex: page.index,
3059
- id: index,
3060
- type: PdfAnnotationSubtype.INK,
3061
- rect,
3062
- popup,
3063
- inkList,
3064
- appearances,
3065
- author,
3066
- modified
3067
- };
3068
- }
3069
- /**
3070
- * Read pdf polygon annotation
3071
- * @param page - pdf page infor
3072
- * @param pagePtr - pointer to pdf page object
3073
- * @param annotationPtr - pointer to pdf annotation
3074
- * @param index - index of annotation in the pdf page
3075
- * @returns pdf polygon annotation
3076
- *
3077
- * @private
3078
- */
3079
- readPdfPolygonAnno(page, pagePtr, annotationPtr, index) {
3080
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
3081
- const pageRect = this.readPageAnnoRect(annotationPtr);
3082
- const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
3083
- const author = this.getAnnotString(annotationPtr, "T");
3084
- const modifiedRaw = this.getAnnotString(annotationPtr, "M");
3085
- const modified = this.toIsoDate(modifiedRaw);
3086
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
3087
- const vertices = this.readPdfAnnoVertices(page, pagePtr, annotationPtr);
3088
- return {
3089
- status: PdfAnnotationObjectStatus.Committed,
3090
- pageIndex: page.index,
3091
- id: index,
3092
- type: PdfAnnotationSubtype.POLYGON,
3093
- rect,
3094
- popup,
3095
- vertices,
3096
- appearances,
3097
- author,
3098
- modified
3099
- };
3100
- }
3101
- /**
3102
- * Read pdf polyline annotation
3103
- * @param page - pdf page infor
3104
- * @param pagePtr - pointer to pdf page object
3105
- * @param annotationPtr - pointer to pdf annotation
3106
- * @param index - index of annotation in the pdf page
3107
- * @returns pdf polyline annotation
3108
- *
3109
- * @private
3110
- */
3111
- readPdfPolylineAnno(page, pagePtr, annotationPtr, index) {
3112
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
3113
- const pageRect = this.readPageAnnoRect(annotationPtr);
3114
- const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
3115
- const author = this.getAnnotString(annotationPtr, "T");
3116
- const modifiedRaw = this.getAnnotString(annotationPtr, "M");
3117
- const modified = this.toIsoDate(modifiedRaw);
3118
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
3119
- const vertices = this.readPdfAnnoVertices(page, pagePtr, annotationPtr);
3120
- return {
3121
- status: PdfAnnotationObjectStatus.Committed,
3122
- pageIndex: page.index,
3123
- id: index,
3124
- type: PdfAnnotationSubtype.POLYLINE,
3125
- rect,
3126
- popup,
3127
- vertices,
3128
- appearances,
3129
- author,
3130
- modified
3131
- };
3132
- }
3133
- /**
3134
- * Read pdf line annotation
3135
- * @param page - pdf page infor
3136
- * @param pagePtr - pointer to pdf page object
3137
- * @param annotationPtr - pointer to pdf annotation
3138
- * @param index - index of annotation in the pdf page
3139
- * @returns pdf line annotation
3140
- *
3141
- * @private
3142
- */
3143
- readPdfLineAnno(page, pagePtr, annotationPtr, index) {
3144
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
3145
- const pageRect = this.readPageAnnoRect(annotationPtr);
3146
- const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
3147
- const author = this.getAnnotString(annotationPtr, "T");
3148
- const modifiedRaw = this.getAnnotString(annotationPtr, "M");
3149
- const modified = this.toIsoDate(modifiedRaw);
3150
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
3151
- const startPointPtr = this.malloc(8);
3152
- const endPointPtr = this.malloc(8);
3153
- this.pdfiumModule.FPDFAnnot_GetLine(annotationPtr, startPointPtr, endPointPtr);
3154
- const startPointX = this.pdfiumModule.pdfium.getValue(startPointPtr, "float");
3155
- const startPointY = this.pdfiumModule.pdfium.getValue(startPointPtr + 4, "float");
3156
- const startPoint = this.convertPagePointToDevicePoint(page, {
3157
- x: startPointX,
3158
- y: startPointY
3159
- });
3160
- const endPointX = this.pdfiumModule.pdfium.getValue(endPointPtr, "float");
3161
- const endPointY = this.pdfiumModule.pdfium.getValue(endPointPtr + 4, "float");
3162
- const endPoint = this.convertPagePointToDevicePoint(page, {
3163
- x: endPointX,
3164
- y: endPointY
3165
- });
3166
- this.free(startPointPtr);
3167
- this.free(endPointPtr);
3168
- return {
3169
- status: PdfAnnotationObjectStatus.Committed,
3170
- pageIndex: page.index,
3171
- id: index,
3172
- type: PdfAnnotationSubtype.LINE,
3173
- rect,
3174
- popup,
3175
- startPoint,
3176
- endPoint,
3177
- appearances,
3178
- author,
3179
- modified
3180
- };
3181
- }
3182
- /**
3183
- * Read pdf highlight annotation
3184
- * @param page - pdf page infor
3185
- * @param pagePtr - pointer to pdf page object
3186
- * @param annotationPtr - pointer to pdf annotation
3187
- * @param index - index of annotation in the pdf page
3188
- * @returns pdf highlight annotation
3189
- *
3190
- * @private
3191
- */
3192
- readPdfHighlightAnno(page, pagePtr, annotationPtr, index) {
3193
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
3194
- const pageRect = this.readPageAnnoRect(annotationPtr);
3195
- const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
3196
- const quads = this.readAnnotationQuads(page, annotationPtr);
3197
- const color = this.resolveAnnotationColor(annotationPtr);
3198
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
3199
- const author = this.getAnnotString(annotationPtr, "T");
3200
- const modifiedRaw = this.getAnnotString(annotationPtr, "M");
3201
- const modified = this.toIsoDate(modifiedRaw);
3202
- return {
3203
- status: PdfAnnotationObjectStatus.Committed,
3204
- pageIndex: page.index,
3205
- id: index,
3206
- type: PdfAnnotationSubtype.HIGHLIGHT,
3207
- rect,
3208
- popup,
3209
- appearances,
3210
- segmentRects: quads.map(quadToRect),
3211
- color,
3212
- author,
3213
- modified
3214
- };
3215
- }
3216
- /**
3217
- * Read pdf underline annotation
3218
- * @param page - pdf page infor
3219
- * @param pagePtr - pointer to pdf page object
3220
- * @param annotationPtr - pointer to pdf annotation
3221
- * @param index - index of annotation in the pdf page
3222
- * @returns pdf underline annotation
3223
- *
3224
- * @private
3225
- */
3226
- readPdfUnderlineAnno(page, pagePtr, annotationPtr, index) {
3227
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
3228
- const pageRect = this.readPageAnnoRect(annotationPtr);
3229
- const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
3230
- const author = this.getAnnotString(annotationPtr, "T");
3231
- const modifiedRaw = this.getAnnotString(annotationPtr, "M");
3232
- const modified = this.toIsoDate(modifiedRaw);
3233
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
3234
- return {
3235
- status: PdfAnnotationObjectStatus.Committed,
3236
- pageIndex: page.index,
3237
- id: index,
3238
- type: PdfAnnotationSubtype.UNDERLINE,
3239
- rect,
3240
- popup,
3241
- appearances,
3242
- author,
3243
- modified
3244
- };
3245
- }
3246
- /**
3247
- * Read strikeout annotation
3248
- * @param page - pdf page infor
3249
- * @param pagePtr - pointer to pdf page object
3250
- * @param annotationPtr - pointer to pdf annotation
3251
- * @param index - index of annotation in the pdf page
3252
- * @returns pdf strikeout annotation
3253
- *
3254
- * @private
3255
- */
3256
- readPdfStrikeOutAnno(page, pagePtr, annotationPtr, index) {
3257
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
3258
- const pageRect = this.readPageAnnoRect(annotationPtr);
3259
- const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
3260
- const author = this.getAnnotString(annotationPtr, "T");
3261
- const modifiedRaw = this.getAnnotString(annotationPtr, "M");
3262
- const modified = this.toIsoDate(modifiedRaw);
3263
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
3264
- return {
3265
- status: PdfAnnotationObjectStatus.Committed,
3266
- pageIndex: page.index,
3267
- id: index,
3268
- type: PdfAnnotationSubtype.STRIKEOUT,
3269
- rect,
3270
- popup,
3271
- appearances,
3272
- author,
3273
- modified
3274
- };
3275
- }
3276
- /**
3277
- * Read pdf squiggly annotation
3278
- * @param page - pdf page infor
3279
- * @param pagePtr - pointer to pdf page object
3280
- * @param annotationPtr - pointer to pdf annotation
3281
- * @param index - index of annotation in the pdf page
3282
- * @returns pdf squiggly annotation
3283
- *
3284
- * @private
3285
- */
3286
- readPdfSquigglyAnno(page, pagePtr, annotationPtr, index) {
3287
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
3288
- const pageRect = this.readPageAnnoRect(annotationPtr);
3289
- const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
3290
- const author = this.getAnnotString(annotationPtr, "T");
3291
- const modifiedRaw = this.getAnnotString(annotationPtr, "M");
3292
- const modified = this.toIsoDate(modifiedRaw);
3293
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
3294
- return {
3295
- status: PdfAnnotationObjectStatus.Committed,
3296
- pageIndex: page.index,
3297
- id: index,
3298
- type: PdfAnnotationSubtype.SQUIGGLY,
3299
- rect,
3300
- popup,
3301
- appearances,
3302
- author,
3303
- modified
3304
- };
3305
- }
3306
- /**
3307
- * Read pdf caret annotation
3308
- * @param page - pdf page infor
3309
- * @param pagePtr - pointer to pdf page object
3310
- * @param annotationPtr - pointer to pdf annotation
3311
- * @param index - index of annotation in the pdf page
3312
- * @returns pdf caret annotation
3313
- *
3314
- * @private
3315
- */
3316
- readPdfCaretAnno(page, pagePtr, annotationPtr, index) {
3317
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
3318
- const pageRect = this.readPageAnnoRect(annotationPtr);
3319
- const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
3320
- const author = this.getAnnotString(annotationPtr, "T");
3321
- const modifiedRaw = this.getAnnotString(annotationPtr, "M");
3322
- const modified = this.toIsoDate(modifiedRaw);
3323
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
3324
- return {
3325
- status: PdfAnnotationObjectStatus.Committed,
3326
- pageIndex: page.index,
3327
- id: index,
3328
- type: PdfAnnotationSubtype.CARET,
3329
- rect,
3330
- popup,
3331
- appearances,
3332
- author,
3333
- modified
3334
- };
3335
- }
3336
- /**
3337
- * Read pdf stamp annotation
3338
- * @param docPtr - pointer to pdf document object
3339
- * @param page - pdf page infor
3340
- * @param pagePtr - pointer to pdf page object
3341
- * @param annotationPtr - pointer to pdf annotation
3342
- * @param index - index of annotation in the pdf page
3343
- * @returns pdf stamp annotation
3344
- *
3345
- * @private
3346
- */
3347
- readPdfStampAnno(docPtr, page, pagePtr, annotationPtr, index) {
3348
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
3349
- const pageRect = this.readPageAnnoRect(annotationPtr);
3350
- const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
3351
- const author = this.getAnnotString(annotationPtr, "T");
3352
- const modifiedRaw = this.getAnnotString(annotationPtr, "M");
3353
- const modified = this.toIsoDate(modifiedRaw);
3354
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
3355
- const contents = [];
3356
- const objectCount = this.pdfiumModule.FPDFAnnot_GetObjectCount(annotationPtr);
3357
- for (let i = 0; i < objectCount; i++) {
3358
- const annotationObjectPtr = this.pdfiumModule.FPDFAnnot_GetObject(annotationPtr, i);
3359
- const pageObj = this.readPdfPageObject(annotationObjectPtr);
3360
- if (pageObj) {
3361
- contents.push(pageObj);
3362
- }
3363
- }
3364
- return {
3365
- status: PdfAnnotationObjectStatus.Committed,
3366
- pageIndex: page.index,
3367
- id: index,
3368
- type: PdfAnnotationSubtype.STAMP,
3369
- rect,
3370
- popup,
3371
- contents,
3372
- appearances,
3373
- author,
3374
- modified
3375
- };
3376
- }
3377
- /**
3378
- * Read pdf object in pdf page
3379
- * @param pageObjectPtr - pointer to pdf object in page
3380
- * @returns pdf object in page
3381
- *
3382
- * @private
3383
- */
3384
- readPdfPageObject(pageObjectPtr) {
3385
- const type = this.pdfiumModule.FPDFPageObj_GetType(pageObjectPtr);
3386
- switch (type) {
3387
- case PdfPageObjectType.PATH:
3388
- return this.readPathObject(pageObjectPtr);
3389
- case PdfPageObjectType.IMAGE:
3390
- return this.readImageObject(pageObjectPtr);
3391
- case PdfPageObjectType.FORM:
3392
- return this.readFormObject(pageObjectPtr);
3393
- }
3394
- }
3395
- /**
3396
- * Read pdf path object
3397
- * @param pathObjectPtr - pointer to pdf path object in page
3398
- * @returns pdf path object
3399
- *
3400
- * @private
3401
- */
3402
- readPathObject(pathObjectPtr) {
3403
- const segmentCount = this.pdfiumModule.FPDFPath_CountSegments(pathObjectPtr);
3404
- const leftPtr = this.malloc(4);
3405
- const bottomPtr = this.malloc(4);
3406
- const rightPtr = this.malloc(4);
3407
- const topPtr = this.malloc(4);
3408
- this.pdfiumModule.FPDFPageObj_GetBounds(pathObjectPtr, leftPtr, bottomPtr, rightPtr, topPtr);
3409
- const left = this.pdfiumModule.pdfium.getValue(leftPtr, "float");
3410
- const bottom = this.pdfiumModule.pdfium.getValue(bottomPtr, "float");
3411
- const right = this.pdfiumModule.pdfium.getValue(rightPtr, "float");
3412
- const top = this.pdfiumModule.pdfium.getValue(topPtr, "float");
3413
- const bounds = { left, bottom, right, top };
3414
- this.free(leftPtr);
3415
- this.free(bottomPtr);
3416
- this.free(rightPtr);
3417
- this.free(topPtr);
3418
- const segments = [];
3419
- for (let i = 0; i < segmentCount; i++) {
3420
- const segment = this.readPdfSegment(pathObjectPtr, i);
3421
- segments.push(segment);
3422
- }
3423
- const matrix = this.readPdfPageObjectTransformMatrix(pathObjectPtr);
3424
- return {
3425
- type: PdfPageObjectType.PATH,
3426
- bounds,
3427
- segments,
3428
- matrix
3429
- };
3430
- }
3431
- /**
3432
- * Read segment of pdf path object
3433
- * @param annotationObjectPtr - pointer to pdf path object
3434
- * @param segmentIndex - index of segment
3435
- * @returns pdf segment in pdf path
3436
- *
3437
- * @private
3438
- */
3439
- readPdfSegment(annotationObjectPtr, segmentIndex) {
3440
- const segmentPtr = this.pdfiumModule.FPDFPath_GetPathSegment(annotationObjectPtr, segmentIndex);
3441
- const segmentType = this.pdfiumModule.FPDFPathSegment_GetType(segmentPtr);
3442
- const isClosed = this.pdfiumModule.FPDFPathSegment_GetClose(segmentPtr);
3443
- const pointXPtr = this.malloc(4);
3444
- const pointYPtr = this.malloc(4);
3445
- this.pdfiumModule.FPDFPathSegment_GetPoint(segmentPtr, pointXPtr, pointYPtr);
3446
- const pointX = this.pdfiumModule.pdfium.getValue(pointXPtr, "float");
3447
- const pointY = this.pdfiumModule.pdfium.getValue(pointYPtr, "float");
3448
- this.free(pointXPtr);
3449
- this.free(pointYPtr);
3450
- return {
3451
- type: segmentType,
3452
- point: { x: pointX, y: pointY },
3453
- isClosed
3454
- };
3455
- }
3456
- /**
3457
- * Read pdf image object from pdf document
3458
- * @param pageObjectPtr - pointer to pdf image object in page
3459
- * @returns pdf image object
3460
- *
3461
- * @private
3462
- */
3463
- readImageObject(imageObjectPtr) {
3464
- const bitmapPtr = this.pdfiumModule.FPDFImageObj_GetBitmap(imageObjectPtr);
3465
- const bitmapBufferPtr = this.pdfiumModule.FPDFBitmap_GetBuffer(bitmapPtr);
3466
- const bitmapWidth = this.pdfiumModule.FPDFBitmap_GetWidth(bitmapPtr);
3467
- const bitmapHeight = this.pdfiumModule.FPDFBitmap_GetHeight(bitmapPtr);
3468
- const format = this.pdfiumModule.FPDFBitmap_GetFormat(bitmapPtr);
3469
- const pixelCount = bitmapWidth * bitmapHeight;
3470
- const bytesPerPixel = 4;
3471
- const array = new Uint8ClampedArray(pixelCount * bytesPerPixel);
3472
- for (let i = 0; i < pixelCount; i++) {
3473
- switch (format) {
3474
- case 2 /* Bitmap_BGR */:
3475
- {
3476
- const blue = this.pdfiumModule.pdfium.getValue(bitmapBufferPtr + i * 3, "i8");
3477
- const green = this.pdfiumModule.pdfium.getValue(bitmapBufferPtr + i * 3 + 1, "i8");
3478
- const red = this.pdfiumModule.pdfium.getValue(bitmapBufferPtr + i * 3 + 2, "i8");
3479
- array[i * bytesPerPixel] = red;
3480
- array[i * bytesPerPixel + 1] = green;
3481
- array[i * bytesPerPixel + 2] = blue;
3482
- array[i * bytesPerPixel + 3] = 100;
3483
- }
3484
- break;
3485
- }
3486
- }
3487
- const imageData = new ImageData(array, bitmapWidth, bitmapHeight);
3488
- const matrix = this.readPdfPageObjectTransformMatrix(imageObjectPtr);
3489
- return {
3490
- type: PdfPageObjectType.IMAGE,
3491
- imageData,
3492
- matrix
3493
- };
3494
- }
3495
- /**
3496
- * Read form object from pdf document
3497
- * @param formObjectPtr - pointer to pdf form object in page
3498
- * @returns pdf form object
3499
- *
3500
- * @private
3501
- */
3502
- readFormObject(formObjectPtr) {
3503
- const objectCount = this.pdfiumModule.FPDFFormObj_CountObjects(formObjectPtr);
3504
- const objects = [];
3505
- for (let i = 0; i < objectCount; i++) {
3506
- const pageObjectPtr = this.pdfiumModule.FPDFFormObj_GetObject(formObjectPtr, i);
3507
- const pageObj = this.readPdfPageObject(pageObjectPtr);
3508
- if (pageObj) {
3509
- objects.push(pageObj);
3510
- }
3511
- }
3512
- const matrix = this.readPdfPageObjectTransformMatrix(formObjectPtr);
3513
- return {
3514
- type: PdfPageObjectType.FORM,
3515
- objects,
3516
- matrix
3517
- };
3518
- }
3519
- /**
3520
- * Read pdf object in pdf page
3521
- * @param pageObjectPtr - pointer to pdf object in page
3522
- * @returns pdf object in page
3523
- *
3524
- * @private
3525
- */
3526
- readPdfPageObjectTransformMatrix(pageObjectPtr) {
3527
- const matrixPtr = this.malloc(4 * 6);
3528
- if (this.pdfiumModule.FPDFPageObj_GetMatrix(pageObjectPtr, matrixPtr)) {
3529
- const a = this.pdfiumModule.pdfium.getValue(matrixPtr, "float");
3530
- const b = this.pdfiumModule.pdfium.getValue(matrixPtr + 4, "float");
3531
- const c = this.pdfiumModule.pdfium.getValue(matrixPtr + 8, "float");
3532
- const d = this.pdfiumModule.pdfium.getValue(matrixPtr + 12, "float");
3533
- const e = this.pdfiumModule.pdfium.getValue(matrixPtr + 16, "float");
3534
- const f = this.pdfiumModule.pdfium.getValue(matrixPtr + 20, "float");
3535
- this.free(matrixPtr);
3536
- return { a, b, c, d, e, f };
3537
- }
3538
- this.free(matrixPtr);
3539
- return { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 };
3540
- }
3541
- /**
3542
- * Read circle annotation
3543
- * @param page - pdf page infor
3544
- * @param pagePtr - pointer to pdf page object
3545
- * @param annotationPtr - pointer to pdf annotation
3546
- * @param index - index of annotation in the pdf page
3547
- * @returns pdf circle annotation
3548
- *
3549
- * @private
3550
- */
3551
- readPdfCircleAnno(page, pagePtr, annotationPtr, index) {
3552
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
3553
- const pageRect = this.readPageAnnoRect(annotationPtr);
3554
- const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
3555
- const author = this.getAnnotString(annotationPtr, "T");
3556
- const modifiedRaw = this.getAnnotString(annotationPtr, "M");
3557
- const modified = this.toIsoDate(modifiedRaw);
3558
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
3559
- return {
3560
- status: PdfAnnotationObjectStatus.Committed,
3561
- pageIndex: page.index,
3562
- id: index,
3563
- type: PdfAnnotationSubtype.CIRCLE,
3564
- rect,
3565
- popup,
3566
- appearances,
3567
- author,
3568
- modified
3569
- };
3570
- }
3571
- /**
3572
- * Read square annotation
3573
- * @param page - pdf page infor
3574
- * @param pagePtr - pointer to pdf page object
3575
- * @param annotationPtr - pointer to pdf annotation
3576
- * @param index - index of annotation in the pdf page
3577
- * @returns pdf square annotation
3578
- *
3579
- * @private
3580
- */
3581
- readPdfSquareAnno(page, pagePtr, annotationPtr, index) {
3582
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
3583
- const pageRect = this.readPageAnnoRect(annotationPtr);
3584
- const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
3585
- const author = this.getAnnotString(annotationPtr, "T");
3586
- const modifiedRaw = this.getAnnotString(annotationPtr, "M");
3587
- const modified = this.toIsoDate(modifiedRaw);
3588
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
3589
- return {
3590
- status: PdfAnnotationObjectStatus.Committed,
3591
- pageIndex: page.index,
3592
- id: index,
3593
- type: PdfAnnotationSubtype.SQUARE,
3594
- rect,
3595
- popup,
3596
- appearances,
3597
- author,
3598
- modified
3599
- };
3600
- }
3601
- /**
3602
- * Read basic info of unsupported pdf annotation
3603
- * @param page - pdf page infor
3604
- * @param pagePtr - pointer to pdf page object
3605
- * @param type - type of annotation
3606
- * @param annotationPtr - pointer to pdf annotation
3607
- * @param index - index of annotation in the pdf page
3608
- * @returns pdf annotation
3609
- *
3610
- * @private
3611
- */
3612
- readPdfAnno(page, pagePtr, type, annotationPtr, index) {
3613
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
3614
- const pageRect = this.readPageAnnoRect(annotationPtr);
3615
- const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
3616
- const author = this.getAnnotString(annotationPtr, "T");
3617
- const modifiedRaw = this.getAnnotString(annotationPtr, "M");
3618
- const modified = this.toIsoDate(modifiedRaw);
3619
- const popup = this.readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index);
3620
- return {
3621
- status: PdfAnnotationObjectStatus.Committed,
3622
- pageIndex: page.index,
3623
- id: index,
3624
- type,
3625
- rect,
3626
- popup,
3627
- appearances,
3628
- author,
3629
- modified
3630
- };
3631
- }
3632
- /**
3633
- * Resolve `/IRT` → parent-annotation index on the same page.
3634
- *
3635
- * @param pagePtr - pointer to FPDF_PAGE
3636
- * @param annotationPtr - pointer to FPDF_ANNOTATION
3637
- * @returns index (`0…count-1`) or `undefined` when the annotation is *not* a reply
3638
- *
3639
- * @private
3640
- */
3641
- getInReplyToId(pagePtr, annotationPtr) {
3642
- const parentPtr = this.pdfiumModule.FPDFAnnot_GetLinkedAnnot(annotationPtr, "IRT");
3643
- if (!parentPtr) return;
3644
- const idx = this.pdfiumModule.FPDFPage_GetAnnotIndex(pagePtr, parentPtr);
3645
- return idx >= 0 ? idx : void 0;
3646
- }
3647
- /**
3648
- * Parse a PDF date string **D:YYYYMMDDHHmmSSOHH'mm'** to ISO-8601.
3649
- *
3650
- * Returns `undefined` if the input is malformed.
3651
- *
3652
- * @private
3653
- */
3654
- toIsoDate(pdfDate) {
3655
- if (!pdfDate?.startsWith("D:")) return;
3656
- const y = pdfDate.substring(2, 6);
3657
- const m = pdfDate.substring(6, 8) || "01";
3658
- const d = pdfDate.substring(8, 10) || "01";
3659
- const H = pdfDate.substring(10, 12) || "00";
3660
- const M = pdfDate.substring(12, 14) || "00";
3661
- const S = pdfDate.substring(14, 16) || "00";
3662
- return `${y}-${m}-${d}T${H}:${M}:${S}`;
3663
- }
3664
- /**
3665
- * Fetch a string value (`/T`, `/M`, `/State`, …) from an annotation.
3666
- *
3667
- * @returns decoded UTF-8 string or `undefined` when the key is absent
3668
- *
3669
- * @private
3670
- */
3671
- getAnnotString(annotationPtr, key) {
3672
- const len = this.pdfiumModule.FPDFAnnot_GetStringValue(annotationPtr, key, 0, 0);
3673
- if (len === 0) return;
3674
- const bytes = (len + 1) * 2;
3675
- const ptr = this.malloc(bytes);
3676
- this.pdfiumModule.FPDFAnnot_GetStringValue(annotationPtr, key, ptr, bytes);
3677
- const value = this.pdfiumModule.pdfium.UTF16ToString(ptr);
3678
- this.free(ptr);
3679
- return value || void 0;
3680
- }
3681
- /**
3682
- * Read linked popup of pdf annotation
3683
- * @param page - pdf page infor
3684
- * @param pagePtr - pointer to pdf page object
3685
- * @param annotationPtr - pointer to pdf annotation
3686
- * @param index - index of annotation in the pdf page
3687
- * @returns pdf popup linked to annotation
3688
- *
3689
- * @private
3690
- */
3691
- readPdfAnnoLinkedPopup(page, pagePtr, annotationPtr, index) {
3692
- const appearances = this.readPageAnnoAppearanceStreams(annotationPtr);
3693
- const popupAnnotationPtr = this.pdfiumModule.FPDFAnnot_GetLinkedAnnot(annotationPtr, "Popup");
3694
- if (!popupAnnotationPtr) {
3695
- return;
3696
- }
3697
- const pageRect = this.readPageAnnoRect(popupAnnotationPtr);
3698
- const rect = this.convertPageRectToDeviceRect(page, pagePtr, pageRect);
3699
- const author = this.getAnnotString(annotationPtr, "T");
3700
- const modifiedRaw = this.getAnnotString(annotationPtr, "M");
3701
- const modified = this.toIsoDate(modifiedRaw);
3702
- const contents = this.getAnnotString(annotationPtr, "Contents") || "";
3703
- const open = this.getAnnotString(annotationPtr, "Open") || "false";
3704
- this.pdfiumModule.FPDFPage_CloseAnnot(popupAnnotationPtr);
3705
- return {
3706
- status: PdfAnnotationObjectStatus.Committed,
3707
- pageIndex: page.index,
3708
- id: index,
3709
- type: PdfAnnotationSubtype.POPUP,
3710
- rect,
3711
- contents,
3712
- open: open === "true",
3713
- appearances,
3714
- author,
3715
- modified
3716
- };
3717
- }
3718
- /**
3719
- * Read vertices of pdf annotation
3720
- * @param page - pdf page infor
3721
- * @param pagePtr - pointer to pdf page object
3722
- * @param annotationPtr - pointer to pdf annotation
3723
- * @returns vertices of pdf annotation
3724
- *
3725
- * @private
3726
- */
3727
- readPdfAnnoVertices(page, pagePtr, annotationPtr) {
3728
- const vertices = [];
3729
- const count = this.pdfiumModule.FPDFAnnot_GetVertices(annotationPtr, 0, 0);
3730
- const pointMemorySize = 8;
3731
- const pointsPtr = this.malloc(count * pointMemorySize);
3732
- this.pdfiumModule.FPDFAnnot_GetVertices(annotationPtr, pointsPtr, count);
3733
- for (let i = 0; i < count; i++) {
3734
- const pointX = this.pdfiumModule.pdfium.getValue(pointsPtr + i * pointMemorySize, "float");
3735
- const pointY = this.pdfiumModule.pdfium.getValue(
3736
- pointsPtr + i * pointMemorySize + 4,
3737
- "float"
3738
- );
3739
- const { x, y } = this.convertPagePointToDevicePoint(page, {
3740
- x: pointX,
3741
- y: pointY
3742
- });
3743
- vertices.push({
3744
- x,
3745
- y
3746
- });
3747
- }
3748
- this.free(pointsPtr);
3749
- return vertices;
3750
- }
3751
- /**
3752
- * Read the target of pdf bookmark
3753
- * @param docPtr - pointer to pdf document object
3754
- * @param getActionPtr - callback function to retrive the pointer of action
3755
- * @param getDestinationPtr - callback function to retrive the pointer of destination
3756
- * @returns target of pdf bookmark
3757
- *
3758
- * @private
3759
- */
3760
- readPdfBookmarkTarget(docPtr, getActionPtr, getDestinationPtr) {
3761
- const actionPtr = getActionPtr();
3762
- if (actionPtr) {
3763
- const action = this.readPdfAction(docPtr, actionPtr);
3764
- return {
3765
- type: "action",
3766
- action
3767
- };
3768
- } else {
3769
- const destinationPtr = getDestinationPtr();
3770
- if (destinationPtr) {
3771
- const destination = this.readPdfDestination(docPtr, destinationPtr);
3772
- return {
3773
- type: "destination",
3774
- destination
3775
- };
3776
- }
3777
- }
3778
- }
3779
- /**
3780
- * Read field of pdf widget annotation
3781
- * @param formHandle - form handle
3782
- * @param annotationPtr - pointer to pdf annotation
3783
- * @returns field of pdf widget annotation
3784
- *
3785
- * @private
3786
- */
3787
- readPdfWidgetAnnoField(formHandle, annotationPtr) {
3788
- const flag = this.pdfiumModule.FPDFAnnot_GetFormFieldFlags(
3789
- formHandle,
3790
- annotationPtr
3791
- );
3792
- const type = this.pdfiumModule.FPDFAnnot_GetFormFieldType(
3793
- formHandle,
3794
- annotationPtr
3795
- );
3796
- const name = readString(
3797
- this.pdfiumModule.pdfium,
3798
- (buffer, bufferLength) => {
3799
- return this.pdfiumModule.FPDFAnnot_GetFormFieldName(
3800
- formHandle,
3801
- annotationPtr,
3802
- buffer,
3803
- bufferLength
3804
- );
3805
- },
3806
- this.pdfiumModule.pdfium.UTF16ToString
3807
- );
3808
- const alternateName = readString(
3809
- this.pdfiumModule.pdfium,
3810
- (buffer, bufferLength) => {
3811
- return this.pdfiumModule.FPDFAnnot_GetFormFieldAlternateName(
3812
- formHandle,
3813
- annotationPtr,
3814
- buffer,
3815
- bufferLength
3816
- );
3817
- },
3818
- this.pdfiumModule.pdfium.UTF16ToString
3819
- );
3820
- const value = readString(
3821
- this.pdfiumModule.pdfium,
3822
- (buffer, bufferLength) => {
3823
- return this.pdfiumModule.FPDFAnnot_GetFormFieldValue(
3824
- formHandle,
3825
- annotationPtr,
3826
- buffer,
3827
- bufferLength
3828
- );
3829
- },
3830
- this.pdfiumModule.pdfium.UTF16ToString
3831
- );
3832
- const options = [];
3833
- if (type === PDF_FORM_FIELD_TYPE.COMBOBOX || type === PDF_FORM_FIELD_TYPE.LISTBOX) {
3834
- const count = this.pdfiumModule.FPDFAnnot_GetOptionCount(formHandle, annotationPtr);
3835
- for (let i = 0; i < count; i++) {
3836
- const label = readString(
3837
- this.pdfiumModule.pdfium,
3838
- (buffer, bufferLength) => {
3839
- return this.pdfiumModule.FPDFAnnot_GetOptionLabel(
3840
- formHandle,
3841
- annotationPtr,
3842
- i,
3843
- buffer,
3844
- bufferLength
3845
- );
3846
- },
3847
- this.pdfiumModule.pdfium.UTF16ToString
3848
- );
3849
- const isSelected = this.pdfiumModule.FPDFAnnot_IsOptionSelected(
3850
- formHandle,
3851
- annotationPtr,
3852
- i
3853
- );
3854
- options.push({
3855
- label,
3856
- isSelected
3857
- });
3858
- }
3859
- }
3860
- let isChecked = false;
3861
- if (type === PDF_FORM_FIELD_TYPE.CHECKBOX || type === PDF_FORM_FIELD_TYPE.RADIOBUTTON) {
3862
- isChecked = this.pdfiumModule.FPDFAnnot_IsChecked(formHandle, annotationPtr);
3863
- }
3864
- return {
3865
- flag,
3866
- type,
3867
- name,
3868
- alternateName,
3869
- value,
3870
- isChecked,
3871
- options
3872
- };
3873
- }
3874
- /**
3875
- * render rectangle of pdf page to image
3876
- * @param docPtr - pointer to pdf document object
3877
- * @param page - pdf page infor
3878
- * @param rect - rectangle info
3879
- * @param scaleFactor - factor of scalling
3880
- * @param rotation - rotation angle
3881
- * @param options - render options
3882
- * @returns image data
3883
- *
3884
- * @private
3885
- */
3886
- renderPageRectToImageData(ctx, page, rect, scaleFactor, rotation, dpr, options) {
3887
- const format = 4 /* Bitmap_BGRA */;
3888
- const bytesPerPixel = 4;
3889
- const rectSize = toIntRect(transformRect(page.size, rect, rotation, scaleFactor * dpr));
3890
- const pageSize = toIntSize(transformSize(page.size, rotation, scaleFactor * dpr));
3891
- const bitmapHeapLength = rectSize.size.width * rectSize.size.height * bytesPerPixel;
3892
- const bitmapHeapPtr = this.malloc(bitmapHeapLength);
3893
- const bitmapPtr = this.pdfiumModule.FPDFBitmap_CreateEx(
3894
- rectSize.size.width,
3895
- rectSize.size.height,
3896
- format,
3897
- bitmapHeapPtr,
3898
- rectSize.size.width * bytesPerPixel
3899
- );
3900
- this.pdfiumModule.FPDFBitmap_FillRect(
3901
- bitmapPtr,
3902
- 0,
3903
- 0,
3904
- rectSize.size.width,
3905
- rectSize.size.height,
3906
- 4294967295
3907
- );
3908
- let flags = 16 /* REVERSE_BYTE_ORDER */;
3909
- if (options?.withAnnotations) {
3910
- flags = flags | 1 /* ANNOT */;
3911
- }
3912
- const pageCtx = ctx.acquirePage(page.index);
3913
- this.pdfiumModule.FPDF_RenderPageBitmap(
3914
- bitmapPtr,
3915
- pageCtx.pagePtr,
3916
- -rectSize.origin.x,
3917
- -rectSize.origin.y,
3918
- pageSize.width,
3919
- pageSize.height,
3920
- rotation,
3921
- flags
3922
- );
3923
- this.pdfiumModule.FPDFBitmap_Destroy(bitmapPtr);
3924
- pageCtx.release();
3925
- const data = this.pdfiumModule.pdfium.HEAPU8.subarray(
3926
- bitmapHeapPtr,
3927
- bitmapHeapPtr + bitmapHeapLength
3928
- );
3929
- const imageData = {
3930
- data: new Uint8ClampedArray(data),
3931
- width: rectSize.size.width,
3932
- height: rectSize.size.height
3933
- };
3934
- this.free(bitmapHeapPtr);
3935
- return imageData;
3936
- }
3937
- /**
3938
- * Read the target of pdf link annotation
3939
- * @param docPtr - pointer to pdf document object
3940
- * @param getActionPtr - callback function to retrive the pointer of action
3941
- * @param getDestinationPtr - callback function to retrive the pointer of destination
3942
- * @returns target of link
3943
- *
3944
- * @private
3945
- */
3946
- readPdfLinkAnnoTarget(docPtr, getActionPtr, getDestinationPtr) {
3947
- const destinationPtr = getDestinationPtr();
3948
- if (destinationPtr) {
3949
- const destination = this.readPdfDestination(docPtr, destinationPtr);
3950
- return {
3951
- type: "destination",
3952
- destination
3953
- };
3954
- } else {
3955
- const actionPtr = getActionPtr();
3956
- if (actionPtr) {
3957
- const action = this.readPdfAction(docPtr, actionPtr);
3958
- return {
3959
- type: "action",
3960
- action
3961
- };
3962
- }
3963
- }
3964
- }
3965
- /**
3966
- * Read pdf action from pdf document
3967
- * @param docPtr - pointer to pdf document object
3968
- * @param actionPtr - pointer to pdf action object
3969
- * @returns pdf action object
3970
- *
3971
- * @private
3972
- */
3973
- readPdfAction(docPtr, actionPtr) {
3974
- const actionType = this.pdfiumModule.FPDFAction_GetType(actionPtr);
3975
- let action;
3976
- switch (actionType) {
3977
- case PdfActionType.Unsupported:
3978
- action = {
3979
- type: PdfActionType.Unsupported
3980
- };
3981
- break;
3982
- case PdfActionType.Goto:
3983
- {
3984
- const destinationPtr = this.pdfiumModule.FPDFAction_GetDest(docPtr, actionPtr);
3985
- if (destinationPtr) {
3986
- const destination = this.readPdfDestination(docPtr, destinationPtr);
3987
- action = {
3988
- type: PdfActionType.Goto,
3989
- destination
3990
- };
3991
- } else {
3992
- action = {
3993
- type: PdfActionType.Unsupported
3994
- };
3995
- }
3996
- }
3997
- break;
3998
- case PdfActionType.RemoteGoto:
3999
- {
4000
- action = {
4001
- type: PdfActionType.Unsupported
4002
- };
4003
- }
4004
- break;
4005
- case PdfActionType.URI:
4006
- {
4007
- const uri = readString(
4008
- this.pdfiumModule.pdfium,
4009
- (buffer, bufferLength) => {
4010
- return this.pdfiumModule.FPDFAction_GetURIPath(
4011
- docPtr,
4012
- actionPtr,
4013
- buffer,
4014
- bufferLength
4015
- );
4016
- },
4017
- this.pdfiumModule.pdfium.UTF8ToString
4018
- );
4019
- action = {
4020
- type: PdfActionType.URI,
4021
- uri
4022
- };
4023
- }
4024
- break;
4025
- case PdfActionType.LaunchAppOrOpenFile:
4026
- {
4027
- const path = readString(
4028
- this.pdfiumModule.pdfium,
4029
- (buffer, bufferLength) => {
4030
- return this.pdfiumModule.FPDFAction_GetFilePath(actionPtr, buffer, bufferLength);
4031
- },
4032
- this.pdfiumModule.pdfium.UTF8ToString
4033
- );
4034
- action = {
4035
- type: PdfActionType.LaunchAppOrOpenFile,
4036
- path
4037
- };
4038
- }
4039
- break;
4040
- }
4041
- return action;
4042
- }
4043
- /**
4044
- * Read pdf destination object
4045
- * @param docPtr - pointer to pdf document object
4046
- * @param destinationPtr - pointer to pdf destination
4047
- * @returns pdf destination object
4048
- *
4049
- * @private
4050
- */
4051
- readPdfDestination(docPtr, destinationPtr) {
4052
- const pageIndex = this.pdfiumModule.FPDFDest_GetDestPageIndex(docPtr, destinationPtr);
4053
- const maxParmamsCount = 4;
4054
- const paramsCountPtr = this.malloc(maxParmamsCount);
4055
- const paramsPtr = this.malloc(maxParmamsCount * 4);
4056
- const zoomMode = this.pdfiumModule.FPDFDest_GetView(
4057
- destinationPtr,
4058
- paramsCountPtr,
4059
- paramsPtr
4060
- );
4061
- const paramsCount = this.pdfiumModule.pdfium.getValue(paramsCountPtr, "i32");
4062
- const view = [];
4063
- for (let i = 0; i < paramsCount; i++) {
4064
- const paramPtr = paramsPtr + i * 4;
4065
- view.push(this.pdfiumModule.pdfium.getValue(paramPtr, "float"));
4066
- }
4067
- this.free(paramsCountPtr);
4068
- this.free(paramsPtr);
4069
- if (zoomMode === PdfZoomMode.XYZ) {
4070
- const hasXPtr = this.malloc(1);
4071
- const hasYPtr = this.malloc(1);
4072
- const hasZPtr = this.malloc(1);
4073
- const xPtr = this.malloc(4);
4074
- const yPtr = this.malloc(4);
4075
- const zPtr = this.malloc(4);
4076
- const isSucceed = this.pdfiumModule.FPDFDest_GetLocationInPage(
4077
- destinationPtr,
4078
- hasXPtr,
4079
- hasYPtr,
4080
- hasZPtr,
4081
- xPtr,
4082
- yPtr,
4083
- zPtr
4084
- );
4085
- if (isSucceed) {
4086
- const hasX = this.pdfiumModule.pdfium.getValue(hasXPtr, "i8");
4087
- const hasY = this.pdfiumModule.pdfium.getValue(hasYPtr, "i8");
4088
- const hasZ = this.pdfiumModule.pdfium.getValue(hasZPtr, "i8");
4089
- const x = hasX ? this.pdfiumModule.pdfium.getValue(xPtr, "float") : 0;
4090
- const y = hasY ? this.pdfiumModule.pdfium.getValue(yPtr, "float") : 0;
4091
- const zoom = hasZ ? this.pdfiumModule.pdfium.getValue(zPtr, "float") : 0;
4092
- this.free(hasXPtr);
4093
- this.free(hasYPtr);
4094
- this.free(hasZPtr);
4095
- this.free(xPtr);
4096
- this.free(yPtr);
4097
- this.free(zPtr);
4098
- return {
4099
- pageIndex,
4100
- zoom: {
4101
- mode: zoomMode,
4102
- params: {
4103
- x,
4104
- y,
4105
- zoom
4106
- }
4107
- },
4108
- view
4109
- };
4110
- }
4111
- this.free(hasXPtr);
4112
- this.free(hasYPtr);
4113
- this.free(hasZPtr);
4114
- this.free(xPtr);
4115
- this.free(yPtr);
4116
- this.free(zPtr);
4117
- return {
4118
- pageIndex,
4119
- zoom: {
4120
- mode: zoomMode,
4121
- params: {
4122
- x: 0,
4123
- y: 0,
4124
- zoom: 0
4125
- }
4126
- },
4127
- view
4128
- };
4129
- }
4130
- return {
4131
- pageIndex,
4132
- zoom: {
4133
- mode: zoomMode
4134
- },
4135
- view
4136
- };
4137
- }
4138
- /**
4139
- * Read attachmet from pdf document
4140
- * @param docPtr - pointer to pdf document object
4141
- * @param index - index of attachment
4142
- * @returns attachment content
4143
- *
4144
- * @private
4145
- */
4146
- readPdfAttachment(docPtr, index) {
4147
- const attachmentPtr = this.pdfiumModule.FPDFDoc_GetAttachment(docPtr, index);
4148
- const name = readString(
4149
- this.pdfiumModule.pdfium,
4150
- (buffer, bufferLength) => {
4151
- return this.pdfiumModule.FPDFAttachment_GetName(attachmentPtr, buffer, bufferLength);
4152
- },
4153
- this.pdfiumModule.pdfium.UTF16ToString
4154
- );
4155
- const creationDate = readString(
4156
- this.pdfiumModule.pdfium,
4157
- (buffer, bufferLength) => {
4158
- return this.pdfiumModule.FPDFAttachment_GetStringValue(
4159
- attachmentPtr,
4160
- "CreationDate",
4161
- buffer,
4162
- bufferLength
4163
- );
4164
- },
4165
- this.pdfiumModule.pdfium.UTF16ToString
4166
- );
4167
- const checksum = readString(
4168
- this.pdfiumModule.pdfium,
4169
- (buffer, bufferLength) => {
4170
- return this.pdfiumModule.FPDFAttachment_GetStringValue(
4171
- attachmentPtr,
4172
- "Checksum",
4173
- buffer,
4174
- bufferLength
4175
- );
4176
- },
4177
- this.pdfiumModule.pdfium.UTF16ToString
4178
- );
4179
- return {
4180
- index,
4181
- name,
4182
- creationDate,
4183
- checksum
4184
- };
4185
- }
4186
- /**
4187
- * Convert coordinate of point from device coordinate to page coordinate
4188
- * @param page - pdf page infor
4189
- * @param position - position of point
4190
- * @returns converted position
4191
- *
4192
- * @private
4193
- */
4194
- convertDevicePointToPagePoint(page, position) {
4195
- const x = position.x;
4196
- const y = page.size.height - position.y;
4197
- return { x, y };
4198
- }
4199
- /**
4200
- * Convert coordinate of point from page coordinate to device coordinate
4201
- * @param page - pdf page infor
4202
- * @param position - position of point
4203
- * @returns converted position
4204
- *
4205
- * @private
4206
- */
4207
- convertPagePointToDevicePoint(page, position) {
4208
- const x = position.x;
4209
- const y = page.size.height - position.y;
4210
- return { x, y };
4211
- }
4212
- /**
4213
- * Convert coordinate of rectangle from page coordinate to device coordinate
4214
- * @param page - pdf page infor
4215
- * @param pagePtr - pointer to pdf page object
4216
- * @param pageRect - rectangle that needs to be converted
4217
- * @returns converted rectangle
4218
- *
4219
- * @private
4220
- */
4221
- convertPageRectToDeviceRect(page, pagePtr, pageRect) {
4222
- const { x, y } = this.convertPagePointToDevicePoint(page, {
4223
- x: pageRect.left,
4224
- y: pageRect.top
4225
- });
4226
- const rect = {
4227
- origin: {
4228
- x,
4229
- y
4230
- },
4231
- size: {
4232
- width: Math.abs(pageRect.right - pageRect.left),
4233
- height: Math.abs(pageRect.top - pageRect.bottom)
4234
- }
4235
- };
4236
- return rect;
4237
- }
4238
- /**
4239
- * Read the appearance stream of annotation
4240
- * @param annotationPtr - pointer to pdf annotation
4241
- * @param mode - appearance mode
4242
- * @returns appearance stream
4243
- *
4244
- * @private
4245
- */
4246
- readPageAnnoAppearanceStreams(annotationPtr) {
4247
- return {
4248
- normal: this.readPageAnnoAppearanceStream(annotationPtr, AppearanceMode.Normal),
4249
- rollover: this.readPageAnnoAppearanceStream(annotationPtr, AppearanceMode.Rollover),
4250
- down: this.readPageAnnoAppearanceStream(annotationPtr, AppearanceMode.Down)
4251
- };
4252
- }
4253
- /**
4254
- * Read the appearance stream of annotation
4255
- * @param annotationPtr - pointer to pdf annotation
4256
- * @param mode - appearance mode
4257
- * @returns appearance stream
4258
- *
4259
- * @private
4260
- */
4261
- readPageAnnoAppearanceStream(annotationPtr, mode = AppearanceMode.Normal) {
4262
- const utf16Length = this.pdfiumModule.FPDFAnnot_GetAP(annotationPtr, mode, 0, 0);
4263
- const bytesCount = (utf16Length + 1) * 2;
4264
- const bufferPtr = this.malloc(bytesCount);
4265
- this.pdfiumModule.FPDFAnnot_GetAP(annotationPtr, mode, bufferPtr, bytesCount);
4266
- const ap = this.pdfiumModule.pdfium.UTF16ToString(bufferPtr);
4267
- this.free(bufferPtr);
4268
- return ap;
4269
- }
4270
- /**
4271
- * Set the rect of specified annotation
4272
- * @param page - page info that the annotation is belonged to
4273
- * @param pagePtr - pointer of page object
4274
- * @param annotationPtr - pointer to annotation object
4275
- * @param rect - target rectangle
4276
- * @returns whether the rect is setted
4277
- *
4278
- * @private
4279
- */
4280
- setPageAnnoRect(page, pagePtr, annotationPtr, rect) {
4281
- const pageXPtr = this.malloc(8);
4282
- const pageYPtr = this.malloc(8);
4283
- if (!this.pdfiumModule.FPDF_DeviceToPage(
4284
- pagePtr,
4285
- 0,
4286
- 0,
4287
- page.size.width,
4288
- page.size.height,
4289
- 0,
4290
- rect.origin.x,
4291
- rect.origin.y,
4292
- pageXPtr,
4293
- pageYPtr
4294
- )) {
4295
- this.free(pageXPtr);
4296
- this.free(pageYPtr);
4297
- return false;
4298
- }
4299
- const pageX = this.pdfiumModule.pdfium.getValue(pageXPtr, "double");
4300
- const pageY = this.pdfiumModule.pdfium.getValue(pageYPtr, "double");
4301
- this.free(pageXPtr);
4302
- this.free(pageYPtr);
4303
- const pageRectPtr = this.malloc(4 * 4);
4304
- this.pdfiumModule.pdfium.setValue(pageRectPtr, pageX, "float");
4305
- this.pdfiumModule.pdfium.setValue(pageRectPtr + 4, pageY, "float");
4306
- this.pdfiumModule.pdfium.setValue(pageRectPtr + 8, pageX + rect.size.width, "float");
4307
- this.pdfiumModule.pdfium.setValue(pageRectPtr + 12, pageY - rect.size.height, "float");
4308
- if (!this.pdfiumModule.FPDFAnnot_SetRect(annotationPtr, pageRectPtr)) {
4309
- this.free(pageRectPtr);
4310
- return false;
4311
- }
4312
- this.free(pageRectPtr);
4313
- return true;
4314
- }
4315
- /**
4316
- * Read the rectangle of annotation
4317
- * @param annotationPtr - pointer to pdf annotation
4318
- * @returns rectangle of annotation
4319
- *
4320
- * @private
4321
- */
4322
- readPageAnnoRect(annotationPtr) {
4323
- const pageRectPtr = this.malloc(4 * 4);
4324
- const pageRect = {
4325
- left: 0,
4326
- top: 0,
4327
- right: 0,
4328
- bottom: 0
4329
- };
4330
- if (this.pdfiumModule.FPDFAnnot_GetRect(annotationPtr, pageRectPtr)) {
4331
- pageRect.left = this.pdfiumModule.pdfium.getValue(pageRectPtr, "float");
4332
- pageRect.top = this.pdfiumModule.pdfium.getValue(pageRectPtr + 4, "float");
4333
- pageRect.right = this.pdfiumModule.pdfium.getValue(pageRectPtr + 8, "float");
4334
- pageRect.bottom = this.pdfiumModule.pdfium.getValue(pageRectPtr + 12, "float");
4335
- }
4336
- this.free(pageRectPtr);
4337
- return pageRect;
4338
- }
4339
- /**
4340
- * Get highlight rects for a specific character range (for search highlighting)
4341
- * @param page - pdf page info
4342
- * @param pagePtr - pointer to pdf page
4343
- * @param textPagePtr - pointer to pdf text page
4344
- * @param startIndex - starting character index
4345
- * @param charCount - number of characters in the range
4346
- * @returns array of rectangles for highlighting the specified character range
4347
- *
4348
- * @private
4349
- */
4350
- getHighlightRects(page, pagePtr, textPagePtr, startIndex, charCount) {
4351
- const rectsCount = this.pdfiumModule.FPDFText_CountRects(textPagePtr, startIndex, charCount);
4352
- const highlightRects = [];
4353
- for (let i = 0; i < rectsCount; i++) {
4354
- const topPtr = this.malloc(8);
4355
- const leftPtr = this.malloc(8);
4356
- const rightPtr = this.malloc(8);
4357
- const bottomPtr = this.malloc(8);
4358
- const isSucceed = this.pdfiumModule.FPDFText_GetRect(
4359
- textPagePtr,
4360
- i,
4361
- leftPtr,
4362
- topPtr,
4363
- rightPtr,
4364
- bottomPtr
4365
- );
4366
- if (!isSucceed) {
4367
- this.free(leftPtr);
4368
- this.free(topPtr);
4369
- this.free(rightPtr);
4370
- this.free(bottomPtr);
4371
- continue;
4372
- }
4373
- const left = this.pdfiumModule.pdfium.getValue(leftPtr, "double");
4374
- const top = this.pdfiumModule.pdfium.getValue(topPtr, "double");
4375
- const right = this.pdfiumModule.pdfium.getValue(rightPtr, "double");
4376
- const bottom = this.pdfiumModule.pdfium.getValue(bottomPtr, "double");
4377
- this.free(leftPtr);
4378
- this.free(topPtr);
4379
- this.free(rightPtr);
4380
- this.free(bottomPtr);
4381
- const deviceXPtr = this.malloc(4);
4382
- const deviceYPtr = this.malloc(4);
4383
- this.pdfiumModule.FPDF_PageToDevice(
4384
- pagePtr,
4385
- 0,
4386
- 0,
4387
- page.size.width,
4388
- page.size.height,
4389
- 0,
4390
- left,
4391
- top,
4392
- deviceXPtr,
4393
- deviceYPtr
4394
- );
4395
- const x = this.pdfiumModule.pdfium.getValue(deviceXPtr, "i32");
4396
- const y = this.pdfiumModule.pdfium.getValue(deviceYPtr, "i32");
4397
- this.free(deviceXPtr);
4398
- this.free(deviceYPtr);
4399
- const width = Math.ceil(Math.abs(right - left));
4400
- const height = Math.ceil(Math.abs(top - bottom));
4401
- highlightRects.push({
4402
- origin: { x, y },
4403
- size: { width, height }
4404
- });
4405
- }
4406
- return highlightRects;
4407
- }
4408
- /**
4409
- * Search for a keyword across all pages in the document
4410
- * Returns all search results throughout the entire document
4411
- *
4412
- * @param doc - Pdf document object
4413
- * @param keyword - Search keyword
4414
- * @param flags - Match flags for search
4415
- * @returns Promise of all search results in the document
4416
- *
4417
- * @public
4418
- */
4419
- searchAllPages(doc, keyword, flags = []) {
4420
- this.logger.debug(LOG_SOURCE, LOG_CATEGORY, "searchAllPages", doc, keyword, flags);
4421
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `SearchAllPages`, "Begin", doc.id);
4422
- const ctx = this.cache.getContext(doc.id);
4423
- if (!ctx) {
4424
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `SearchAllPages`, "End", doc.id);
4425
- return PdfTaskHelper.resolve({ results: [], total: 0 });
4426
- }
4427
- const length = 2 * (keyword.length + 1);
4428
- const keywordPtr = this.malloc(length);
4429
- this.pdfiumModule.pdfium.stringToUTF16(keyword, keywordPtr, length);
4430
- const flag = flags.reduce((flag2, currFlag) => {
4431
- return flag2 | currFlag;
4432
- }, MatchFlag.None);
4433
- const results = [];
4434
- const searchAllPagesTask = PdfTaskHelper.create();
4435
- const executeSearch = async () => {
4436
- for (let pageIndex = 0; pageIndex < doc.pageCount; pageIndex++) {
4437
- const pageResults = this.searchAllInPage(ctx, doc.pages[pageIndex], keywordPtr, flag);
4438
- results.push(...pageResults);
4439
- }
4440
- this.free(keywordPtr);
4441
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `SearchAllPages`, "End", doc.id);
4442
- searchAllPagesTask.resolve({
4443
- results,
4444
- total: results.length
4445
- });
4446
- };
4447
- executeSearch().catch((error) => {
4448
- this.free(keywordPtr);
4449
- this.logger.perf(LOG_SOURCE, LOG_CATEGORY, `SearchAllPages`, "End", doc.id);
4450
- searchAllPagesTask.reject({
4451
- code: PdfErrorCode.Unknown,
4452
- message: `Error searching document: ${error}`
4453
- });
4454
- });
4455
- return searchAllPagesTask;
4456
- }
4457
- /**
4458
- * Extract word-aligned context for a search hit.
4459
- *
4460
- * @param fullText full UTF-16 page text (fetch this once per page!)
4461
- * @param start index of 1st char that matched
4462
- * @param count number of chars in the match
4463
- * @param windowChars minimum context chars to keep left & right
4464
- */
4465
- buildContext(fullText, start, count, windowChars = 30) {
4466
- const WORD_BREAK = /[\s\u00A0.,;:!?()\[\]{}<>/\\\-"'`"”\u2013\u2014]/;
4467
- const findWordStart = (index) => {
4468
- while (index > 0 && !WORD_BREAK.test(fullText[index - 1])) index--;
4469
- return index;
4470
- };
4471
- const findWordEnd = (index) => {
4472
- while (index < fullText.length && !WORD_BREAK.test(fullText[index])) index++;
4473
- return index;
4474
- };
4475
- let left = start;
4476
- while (left > 0 && WORD_BREAK.test(fullText[left - 1])) left--;
4477
- let collected = 0;
4478
- while (left > 0 && collected < windowChars) {
4479
- left--;
4480
- if (!WORD_BREAK.test(fullText[left])) collected++;
4481
- }
4482
- left = findWordStart(left);
4483
- let right = start + count;
4484
- while (right < fullText.length && WORD_BREAK.test(fullText[right])) right++;
4485
- collected = 0;
4486
- while (right < fullText.length && collected < windowChars) {
4487
- if (!WORD_BREAK.test(fullText[right])) collected++;
4488
- right++;
4489
- }
4490
- right = findWordEnd(right);
4491
- const before = fullText.slice(left, start).replace(/\s+/g, " ").trimStart();
4492
- const match = fullText.slice(start, start + count);
4493
- const after = fullText.slice(start + count, right).replace(/\s+/g, " ").trimEnd();
4494
- return {
4495
- before: this.tidy(before),
4496
- match: this.tidy(match),
4497
- after: this.tidy(after),
4498
- truncatedLeft: left > 0,
4499
- truncatedRight: right < fullText.length
4500
- };
4501
- }
4502
- /**
4503
- * Tidy the text to remove any non-printable characters and whitespace
4504
- * @param s - text to tidy
4505
- * @returns tidied text
4506
- *
4507
- * @private
4508
- */
4509
- tidy(s) {
4510
- return s.replace(/-\uFFFE\s*/g, "").replace(/[\uFFFE\u00AD\u200B\u2060\uFEFF]/g, "").replace(/\s+/g, " ");
4511
- }
4512
- /**
4513
- * Search for all occurrences of a keyword on a single page
4514
- * This method efficiently loads the page only once and finds all matches
4515
- *
4516
- * @param docPtr - pointer to pdf document
4517
- * @param page - pdf page object
4518
- * @param pageIndex - index of the page
4519
- * @param keywordPtr - pointer to the search keyword
4520
- * @param flag - search flags
4521
- * @returns array of search results on this page
4522
- *
4523
- * @private
4524
- */
4525
- searchAllInPage(ctx, page, keywordPtr, flag) {
4526
- const pageIndex = page.index;
4527
- const pageCtx = ctx.acquirePage(pageIndex);
4528
- const textPagePtr = pageCtx.getTextPage();
4529
- const total = this.pdfiumModule.FPDFText_CountChars(textPagePtr);
4530
- const bufPtr = this.malloc(2 * (total + 1));
4531
- this.pdfiumModule.FPDFText_GetText(textPagePtr, 0, total, bufPtr);
4532
- const fullText = this.pdfiumModule.pdfium.UTF16ToString(bufPtr);
4533
- this.free(bufPtr);
4534
- const pageResults = [];
4535
- const searchHandle = this.pdfiumModule.FPDFText_FindStart(
4536
- textPagePtr,
4537
- keywordPtr,
4538
- flag,
4539
- 0
4540
- // Start from the beginning of the page
4541
- );
4542
- while (this.pdfiumModule.FPDFText_FindNext(searchHandle)) {
4543
- const charIndex = this.pdfiumModule.FPDFText_GetSchResultIndex(searchHandle);
4544
- const charCount = this.pdfiumModule.FPDFText_GetSchCount(searchHandle);
4545
- const rects = this.getHighlightRects(
4546
- page,
4547
- pageCtx.pagePtr,
4548
- textPagePtr,
4549
- charIndex,
4550
- charCount
4551
- );
4552
- const context = this.buildContext(fullText, charIndex, charCount);
4553
- pageResults.push({
4554
- pageIndex,
4555
- charIndex,
4556
- charCount,
4557
- rects,
4558
- context
4559
- });
4560
- }
4561
- this.pdfiumModule.FPDFText_FindClose(searchHandle);
4562
- pageCtx.release();
4563
- return pageResults;
4564
- }
4565
- };
4566
-
4567
- // src/pdfium/runner.ts
4568
- import { init } from "@embedpdf/pdfium";
4569
- var PdfiumEngineRunner = class extends EngineRunner {
4570
- /**
4571
- * Create an instance of PdfiumEngineRunner
4572
- * @param wasmBinary - wasm binary that contains the pdfium wasm file
4573
- */
4574
- constructor(wasmBinary) {
4575
- super();
4576
- this.wasmBinary = wasmBinary;
4577
- }
4578
- /**
4579
- * Initialize runner
4580
- */
4581
- async prepare() {
4582
- const wasmBinary = this.wasmBinary;
4583
- const wasmModule = await init({ wasmBinary });
4584
- this.engine = new PdfiumEngine(wasmModule);
4585
- this.ready();
4586
- }
4587
- };
4588
-
4589
- export {
4590
- readString,
4591
- readArrayBuffer,
4592
- BitmapFormat,
4593
- RenderFlag,
4594
- PdfiumErrorCode,
4595
- browserImageDataToBlobConverter,
4596
- PdfiumEngine,
4597
- PdfiumEngineRunner
4598
- };
4599
- //# sourceMappingURL=chunk-FXQUMVH5.js.map