@embedpdf/engines 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +81 -0
- package/dist/chunk-NDTYBBMQ.js +4615 -0
- package/dist/chunk-NDTYBBMQ.js.map +1 -0
- package/dist/chunk-YZLT3A2D.js +1101 -0
- package/dist/chunk-YZLT3A2D.js.map +1 -0
- package/dist/converters.cjs +124 -0
- package/dist/converters.cjs.map +1 -0
- package/dist/converters.d.cts +69 -0
- package/dist/converters.d.ts +69 -0
- package/dist/converters.js +94 -0
- package/dist/converters.js.map +1 -0
- package/dist/index.cjs +6054 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +32 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +367 -0
- package/dist/index.js.map +1 -0
- package/dist/pdfium.cjs +5715 -0
- package/dist/pdfium.cjs.map +1 -0
- package/dist/pdfium.d.cts +1083 -0
- package/dist/pdfium.d.ts +1083 -0
- package/dist/pdfium.js +22 -0
- package/dist/pdfium.js.map +1 -0
- package/dist/runner-BvRtPCKL.d.cts +131 -0
- package/dist/runner-BvRtPCKL.d.ts +131 -0
- package/dist/worker.cjs +1122 -0
- package/dist/worker.cjs.map +1 -0
- package/dist/worker.d.cts +274 -0
- package/dist/worker.d.ts +274 -0
- package/dist/worker.js +11 -0
- package/dist/worker.js.map +1 -0
- package/package.json +73 -0
|
@@ -0,0 +1,4615 @@
|
|
|
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
|