@embedpdf/engines 1.0.2 → 1.0.4

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