pdf-oxide 0.3.24
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/README.md +218 -0
- package/binding.gyp +35 -0
- package/package.json +78 -0
- package/src/builders/annotation-builder.ts +367 -0
- package/src/builders/conversion-options-builder.ts +257 -0
- package/src/builders/index.ts +12 -0
- package/src/builders/metadata-builder.ts +317 -0
- package/src/builders/pdf-builder.ts +386 -0
- package/src/builders/search-options-builder.ts +151 -0
- package/src/document-editor-manager.ts +318 -0
- package/src/errors.ts +1629 -0
- package/src/form-field-manager.ts +666 -0
- package/src/hybrid-ml-manager.ts +283 -0
- package/src/index.ts +453 -0
- package/src/managers/accessibility-manager.ts +338 -0
- package/src/managers/annotation-manager.ts +439 -0
- package/src/managers/barcode-manager.ts +235 -0
- package/src/managers/batch-manager.ts +533 -0
- package/src/managers/cache-manager.ts +486 -0
- package/src/managers/compliance-manager.ts +375 -0
- package/src/managers/content-manager.ts +339 -0
- package/src/managers/document-utility-manager.ts +922 -0
- package/src/managers/dom-pdf-creator.ts +365 -0
- package/src/managers/editing-manager.ts +514 -0
- package/src/managers/enterprise-manager.ts +478 -0
- package/src/managers/extended-managers.ts +437 -0
- package/src/managers/extraction-manager.ts +583 -0
- package/src/managers/final-utilities.ts +429 -0
- package/src/managers/hybrid-ml-advanced.ts +479 -0
- package/src/managers/index.ts +239 -0
- package/src/managers/layer-manager.ts +500 -0
- package/src/managers/metadata-manager.ts +303 -0
- package/src/managers/ocr-manager.ts +756 -0
- package/src/managers/optimization-manager.ts +262 -0
- package/src/managers/outline-manager.ts +196 -0
- package/src/managers/page-manager.ts +289 -0
- package/src/managers/pattern-detection.ts +440 -0
- package/src/managers/rendering-manager.ts +863 -0
- package/src/managers/search-manager.ts +385 -0
- package/src/managers/security-manager.ts +345 -0
- package/src/managers/signature-manager.ts +1664 -0
- package/src/managers/streams.ts +618 -0
- package/src/managers/xfa-manager.ts +500 -0
- package/src/pdf-creator-manager.ts +494 -0
- package/src/properties.ts +522 -0
- package/src/result-accessors-manager.ts +867 -0
- package/src/tests/advanced-features.test.ts +414 -0
- package/src/tests/advanced.test.ts +266 -0
- package/src/tests/extended-managers.test.ts +316 -0
- package/src/tests/final-utilities.test.ts +455 -0
- package/src/tests/foundation.test.ts +315 -0
- package/src/tests/high-demand.test.ts +257 -0
- package/src/tests/specialized.test.ts +97 -0
- package/src/thumbnail-manager.ts +272 -0
- package/src/types/common.ts +142 -0
- package/src/types/document-types.ts +457 -0
- package/src/types/index.ts +6 -0
- package/src/types/manager-types.ts +284 -0
- package/src/types/native-bindings.ts +517 -0
- package/src/workers/index.ts +7 -0
- package/src/workers/pool.ts +274 -0
- package/src/workers/worker.ts +131 -0
|
@@ -0,0 +1,867 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ResultAccessorsManager for extracting extended properties from PDF operations
|
|
3
|
+
*
|
|
4
|
+
* Provides detailed metadata from search results, fonts, images, and annotations.
|
|
5
|
+
* Enables advanced features like context extraction, font metrics analysis, and annotation tracking.
|
|
6
|
+
* API is consistent with Python, Java, C#, Go, and Swift implementations.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { EventEmitter } from 'events';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Extended properties from a search result
|
|
13
|
+
*/
|
|
14
|
+
export interface SearchResultProperties {
|
|
15
|
+
context: string;
|
|
16
|
+
lineNumber: number;
|
|
17
|
+
paragraphNumber: number;
|
|
18
|
+
confidence: number;
|
|
19
|
+
isHighlighted: boolean;
|
|
20
|
+
fontInfo: string; // JSON format
|
|
21
|
+
color: [number, number, number]; // RGB
|
|
22
|
+
rotation: number;
|
|
23
|
+
objectId: number;
|
|
24
|
+
streamIndex: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Extended font metric information
|
|
29
|
+
*/
|
|
30
|
+
export interface FontProperties {
|
|
31
|
+
baseFontName: string;
|
|
32
|
+
descriptor: string; // JSON format
|
|
33
|
+
descendantFont: string;
|
|
34
|
+
toUnicodeCmap: string;
|
|
35
|
+
isVertical: boolean;
|
|
36
|
+
widths: Float32Array;
|
|
37
|
+
ascender: number;
|
|
38
|
+
descender: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Extended image metadata
|
|
43
|
+
*/
|
|
44
|
+
export interface ImageProperties {
|
|
45
|
+
hasAlphaChannel: boolean;
|
|
46
|
+
iccProfile: Uint8Array;
|
|
47
|
+
filterChain: string; // JSON format
|
|
48
|
+
decodedData: Uint8Array;
|
|
49
|
+
width: number;
|
|
50
|
+
height: number;
|
|
51
|
+
colorSpace: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Extended annotation properties
|
|
56
|
+
*/
|
|
57
|
+
export interface AnnotationProperties {
|
|
58
|
+
modifiedDate: number; // timestamp in milliseconds
|
|
59
|
+
subject: string;
|
|
60
|
+
replyToIndex: number;
|
|
61
|
+
pageNumber: number;
|
|
62
|
+
iconName: string;
|
|
63
|
+
author: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Result Accessors Manager for extracting extended properties
|
|
68
|
+
*
|
|
69
|
+
* Provides methods to:
|
|
70
|
+
* - Extract context from search results
|
|
71
|
+
* - Get detailed font metrics
|
|
72
|
+
* - Inspect image metadata and ICC profiles
|
|
73
|
+
* - Track annotation relationships and metadata
|
|
74
|
+
* - Filter and analyze results by properties
|
|
75
|
+
*
|
|
76
|
+
* Matches: Python ResultAccessorsManager, Java ResultAccessorsManager, etc.
|
|
77
|
+
*/
|
|
78
|
+
export class ResultAccessorsManager extends EventEmitter {
|
|
79
|
+
private document: any;
|
|
80
|
+
private resultCache = new Map<string, any>();
|
|
81
|
+
private maxCacheSize = 100;
|
|
82
|
+
private native: any;
|
|
83
|
+
|
|
84
|
+
constructor(document: any) {
|
|
85
|
+
super();
|
|
86
|
+
this.document = document;
|
|
87
|
+
try {
|
|
88
|
+
this.native = require('../index.node');
|
|
89
|
+
} catch {
|
|
90
|
+
// Fall back to framework defaults if native module not available
|
|
91
|
+
this.native = null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private setCached(key: string, value: any): void {
|
|
96
|
+
if (this.resultCache.size >= this.maxCacheSize) {
|
|
97
|
+
// Remove oldest entry
|
|
98
|
+
const firstKey = this.resultCache.keys().next().value;
|
|
99
|
+
if (firstKey) this.resultCache.delete(firstKey);
|
|
100
|
+
}
|
|
101
|
+
this.resultCache.set(key, value);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ========== Search Result Accessors (10 functions) ==========
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Gets context text around a search result
|
|
108
|
+
* Includes words before and after the match
|
|
109
|
+
* @param results Search results handle
|
|
110
|
+
* @param index Index of the result
|
|
111
|
+
* @param contextWidth Number of characters for context
|
|
112
|
+
* @returns Context text with highlighted match
|
|
113
|
+
*/
|
|
114
|
+
async getSearchResultContext(
|
|
115
|
+
results: any,
|
|
116
|
+
index: number,
|
|
117
|
+
contextWidth: number = 50
|
|
118
|
+
): Promise<string> {
|
|
119
|
+
const cacheKey = `search:context:${index}:${contextWidth}`;
|
|
120
|
+
if (this.resultCache.has(cacheKey)) {
|
|
121
|
+
return this.resultCache.get(cacheKey);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const context = this.native?.search_result_context?.(index, contextWidth) ?? '';
|
|
125
|
+
this.setCached(cacheKey, context);
|
|
126
|
+
this.emit('searchContextExtracted', index);
|
|
127
|
+
return context;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Gets the line number of a search result
|
|
132
|
+
* @param results Search results handle
|
|
133
|
+
* @param index Index of the result
|
|
134
|
+
* @returns Line number (0-based)
|
|
135
|
+
*/
|
|
136
|
+
async getSearchResultLineNumber(results: any, index: number): Promise<number> {
|
|
137
|
+
const cacheKey = `search:linenum:${index}`;
|
|
138
|
+
if (this.resultCache.has(cacheKey)) {
|
|
139
|
+
return this.resultCache.get(cacheKey);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const lineNumber = this.native?.search_result_line_number?.(index) ?? 0;
|
|
143
|
+
this.setCached(cacheKey, lineNumber);
|
|
144
|
+
return lineNumber;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Gets the paragraph number of a search result
|
|
149
|
+
* @param results Search results handle
|
|
150
|
+
* @param index Index of the result
|
|
151
|
+
* @returns Paragraph number (0-based)
|
|
152
|
+
*/
|
|
153
|
+
async getSearchResultParagraphNumber(
|
|
154
|
+
results: any,
|
|
155
|
+
index: number
|
|
156
|
+
): Promise<number> {
|
|
157
|
+
const cacheKey = `search:paragraphnum:${index}`;
|
|
158
|
+
if (this.resultCache.has(cacheKey)) {
|
|
159
|
+
return this.resultCache.get(cacheKey);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const paragraphNumber = this.native?.search_result_paragraph_number?.(index) ?? 0;
|
|
163
|
+
this.setCached(cacheKey, paragraphNumber);
|
|
164
|
+
return paragraphNumber;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Gets the confidence score of a search result
|
|
169
|
+
* Useful for OCR results where confidence varies
|
|
170
|
+
* @param results Search results handle
|
|
171
|
+
* @param index Index of the result
|
|
172
|
+
* @returns Confidence score (0.0 to 1.0)
|
|
173
|
+
*/
|
|
174
|
+
async getSearchResultConfidence(results: any, index: number): Promise<number> {
|
|
175
|
+
const cacheKey = `search:confidence:${index}`;
|
|
176
|
+
if (this.resultCache.has(cacheKey)) {
|
|
177
|
+
return this.resultCache.get(cacheKey);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const confidence = this.native?.search_result_confidence?.(index) ?? 1.0;
|
|
181
|
+
this.setCached(cacheKey, confidence);
|
|
182
|
+
return confidence;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Checks if a search result is highlighted in the document
|
|
187
|
+
* @param results Search results handle
|
|
188
|
+
* @param index Index of the result
|
|
189
|
+
* @returns True if the result is highlighted
|
|
190
|
+
*/
|
|
191
|
+
async isSearchResultHighlighted(results: any, index: number): Promise<boolean> {
|
|
192
|
+
const cacheKey = `search:highlighted:${index}`;
|
|
193
|
+
if (this.resultCache.has(cacheKey)) {
|
|
194
|
+
return this.resultCache.get(cacheKey);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const highlighted = this.native?.search_result_is_highlighted?.(index) ?? false;
|
|
198
|
+
this.setCached(cacheKey, highlighted);
|
|
199
|
+
return highlighted;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Gets font information for a search result
|
|
204
|
+
* Returns JSON with font name, size, family, etc.
|
|
205
|
+
* @param results Search results handle
|
|
206
|
+
* @param index Index of the result
|
|
207
|
+
* @returns Font info as JSON string
|
|
208
|
+
*/
|
|
209
|
+
async getSearchResultFontInfo(results: any, index: number): Promise<string> {
|
|
210
|
+
const cacheKey = `search:fontinfo:${index}`;
|
|
211
|
+
if (this.resultCache.has(cacheKey)) {
|
|
212
|
+
return this.resultCache.get(cacheKey);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const fontInfo = this.native?.search_result_font_info?.(index) ?? '{}';
|
|
216
|
+
this.setCached(cacheKey, fontInfo);
|
|
217
|
+
return fontInfo;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Gets RGB color of a search result
|
|
222
|
+
* @param results Search results handle
|
|
223
|
+
* @param index Index of the result
|
|
224
|
+
* @returns Color as [R, G, B] array (0-255)
|
|
225
|
+
*/
|
|
226
|
+
async getSearchResultColor(
|
|
227
|
+
results: any,
|
|
228
|
+
index: number
|
|
229
|
+
): Promise<[number, number, number]> {
|
|
230
|
+
const cacheKey = `search:color:${index}`;
|
|
231
|
+
if (this.resultCache.has(cacheKey)) {
|
|
232
|
+
return this.resultCache.get(cacheKey);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
let color: [number, number, number] = [0, 0, 0];
|
|
236
|
+
if (this.native?.search_result_color) {
|
|
237
|
+
try {
|
|
238
|
+
const colorJson = this.native.search_result_color(index);
|
|
239
|
+
const parsed = JSON.parse(colorJson);
|
|
240
|
+
color = [parsed.r ?? 0, parsed.g ?? 0, parsed.b ?? 0];
|
|
241
|
+
} catch {
|
|
242
|
+
color = [0, 0, 0];
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
this.setCached(cacheKey, color);
|
|
246
|
+
return color;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Gets the rotation angle of a search result
|
|
251
|
+
* @param results Search results handle
|
|
252
|
+
* @param index Index of the result
|
|
253
|
+
* @returns Rotation in degrees (0, 90, 180, 270)
|
|
254
|
+
*/
|
|
255
|
+
async getSearchResultRotation(results: any, index: number): Promise<number> {
|
|
256
|
+
const cacheKey = `search:rotation:${index}`;
|
|
257
|
+
if (this.resultCache.has(cacheKey)) {
|
|
258
|
+
return this.resultCache.get(cacheKey);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const rotation = this.native?.search_result_rotation?.(index) ?? 0;
|
|
262
|
+
this.setCached(cacheKey, rotation);
|
|
263
|
+
return rotation;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Gets the object ID of a search result
|
|
268
|
+
* @param results Search results handle
|
|
269
|
+
* @param index Index of the result
|
|
270
|
+
* @returns PDF object ID
|
|
271
|
+
*/
|
|
272
|
+
async getSearchResultObjectId(results: any, index: number): Promise<number> {
|
|
273
|
+
const cacheKey = `search:objectid:${index}`;
|
|
274
|
+
if (this.resultCache.has(cacheKey)) {
|
|
275
|
+
return this.resultCache.get(cacheKey);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const objectId = this.native?.search_result_object_id?.(index) ?? 0;
|
|
279
|
+
this.setCached(cacheKey, objectId);
|
|
280
|
+
return objectId;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Gets the stream index of a search result
|
|
285
|
+
* @param results Search results handle
|
|
286
|
+
* @param index Index of the result
|
|
287
|
+
* @returns Stream index in the content
|
|
288
|
+
*/
|
|
289
|
+
async getSearchResultStreamIndex(results: any, index: number): Promise<number> {
|
|
290
|
+
const cacheKey = `search:streamindex:${index}`;
|
|
291
|
+
if (this.resultCache.has(cacheKey)) {
|
|
292
|
+
return this.resultCache.get(cacheKey);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const streamIndex = this.native?.search_result_stream_index?.(index) ?? 0;
|
|
296
|
+
this.setCached(cacheKey, streamIndex);
|
|
297
|
+
return streamIndex;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Gets all properties of a search result at once
|
|
302
|
+
* More efficient than individual property calls
|
|
303
|
+
* @param results Search results handle
|
|
304
|
+
* @param index Index of the result
|
|
305
|
+
* @returns Object with all properties
|
|
306
|
+
*/
|
|
307
|
+
async getSearchResultAllProperties(
|
|
308
|
+
results: any,
|
|
309
|
+
index: number
|
|
310
|
+
): Promise<SearchResultProperties> {
|
|
311
|
+
const cacheKey = `search:all:${index}`;
|
|
312
|
+
if (this.resultCache.has(cacheKey)) {
|
|
313
|
+
return this.resultCache.get(cacheKey);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Aggregate all properties by calling individual native functions
|
|
317
|
+
const context = this.native?.search_result_context?.(index, 50) ?? '';
|
|
318
|
+
const lineNumber = this.native?.search_result_line_number?.(index) ?? 0;
|
|
319
|
+
const paragraphNumber = this.native?.search_result_paragraph_number?.(index) ?? 0;
|
|
320
|
+
const confidence = this.native?.search_result_confidence?.(index) ?? 1.0;
|
|
321
|
+
const isHighlighted = this.native?.search_result_is_highlighted?.(index) ?? false;
|
|
322
|
+
const fontInfo = this.native?.search_result_font_info?.(index) ?? '{}';
|
|
323
|
+
|
|
324
|
+
let color: [number, number, number] = [0, 0, 0];
|
|
325
|
+
if (this.native?.search_result_color) {
|
|
326
|
+
try {
|
|
327
|
+
const colorJson = this.native.search_result_color(index);
|
|
328
|
+
const parsed = JSON.parse(colorJson);
|
|
329
|
+
color = [parsed.r ?? 0, parsed.g ?? 0, parsed.b ?? 0];
|
|
330
|
+
} catch {
|
|
331
|
+
color = [0, 0, 0];
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const rotation = this.native?.search_result_rotation?.(index) ?? 0;
|
|
336
|
+
const objectId = this.native?.search_result_object_id?.(index) ?? 0;
|
|
337
|
+
const streamIndex = this.native?.search_result_stream_index?.(index) ?? 0;
|
|
338
|
+
|
|
339
|
+
const props: SearchResultProperties = {
|
|
340
|
+
context,
|
|
341
|
+
lineNumber,
|
|
342
|
+
paragraphNumber,
|
|
343
|
+
confidence,
|
|
344
|
+
isHighlighted,
|
|
345
|
+
fontInfo,
|
|
346
|
+
color,
|
|
347
|
+
rotation,
|
|
348
|
+
objectId,
|
|
349
|
+
streamIndex,
|
|
350
|
+
};
|
|
351
|
+
this.setCached(cacheKey, props);
|
|
352
|
+
this.emit('searchPropertiesExtracted', index);
|
|
353
|
+
return props;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// ========== Font Accessors (8 functions) ==========
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Gets the base font name
|
|
360
|
+
* @param fonts Font handle
|
|
361
|
+
* @param index Index of the font
|
|
362
|
+
* @returns Font name (e.g., "Helvetica", "Arial")
|
|
363
|
+
*/
|
|
364
|
+
async getFontBaseFontName(fonts: any, index: number): Promise<string> {
|
|
365
|
+
const cacheKey = `font:basename:${index}`;
|
|
366
|
+
if (this.resultCache.has(cacheKey)) {
|
|
367
|
+
return this.resultCache.get(cacheKey);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const name = this.native?.font_get_base_font_name?.(index) ?? '';
|
|
371
|
+
this.setCached(cacheKey, name);
|
|
372
|
+
return name;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Gets the font descriptor JSON
|
|
377
|
+
* Contains details about font metrics and characteristics
|
|
378
|
+
* @param fonts Font handle
|
|
379
|
+
* @param index Index of the font
|
|
380
|
+
* @returns Font descriptor as JSON string
|
|
381
|
+
*/
|
|
382
|
+
async getFontDescriptor(fonts: any, index: number): Promise<string> {
|
|
383
|
+
const cacheKey = `font:descriptor:${index}`;
|
|
384
|
+
if (this.resultCache.has(cacheKey)) {
|
|
385
|
+
return this.resultCache.get(cacheKey);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const descriptor = this.native?.font_get_descriptor?.(index) ?? '{}';
|
|
389
|
+
this.setCached(cacheKey, descriptor);
|
|
390
|
+
return descriptor;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Gets the descendant font name (for composite fonts)
|
|
395
|
+
* @param fonts Font handle
|
|
396
|
+
* @param index Index of the font
|
|
397
|
+
* @returns Descendant font name or empty string
|
|
398
|
+
*/
|
|
399
|
+
async getFontDescendantFont(fonts: any, index: number): Promise<string> {
|
|
400
|
+
const cacheKey = `font:descendant:${index}`;
|
|
401
|
+
if (this.resultCache.has(cacheKey)) {
|
|
402
|
+
return this.resultCache.get(cacheKey);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const descendant = this.native?.font_get_descendant_font?.(index) ?? '';
|
|
406
|
+
this.setCached(cacheKey, descendant);
|
|
407
|
+
return descendant;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Gets the ToUnicode CMap for character to Unicode mapping
|
|
412
|
+
* @param fonts Font handle
|
|
413
|
+
* @param index Index of the font
|
|
414
|
+
* @returns ToUnicode CMap as string
|
|
415
|
+
*/
|
|
416
|
+
async getFontToUnicodeCmap(fonts: any, index: number): Promise<string> {
|
|
417
|
+
const cacheKey = `font:tounicode:${index}`;
|
|
418
|
+
if (this.resultCache.has(cacheKey)) {
|
|
419
|
+
return this.resultCache.get(cacheKey);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const cmap = this.native?.font_get_to_unicode_cmap?.(index) ?? '';
|
|
423
|
+
this.setCached(cacheKey, cmap);
|
|
424
|
+
return cmap;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Checks if font is vertical (top-to-bottom layout)
|
|
429
|
+
* @param fonts Font handle
|
|
430
|
+
* @param index Index of the font
|
|
431
|
+
* @returns True if font is vertical
|
|
432
|
+
*/
|
|
433
|
+
async isFontVertical(fonts: any, index: number): Promise<boolean> {
|
|
434
|
+
const cacheKey = `font:isvertical:${index}`;
|
|
435
|
+
if (this.resultCache.has(cacheKey)) {
|
|
436
|
+
return this.resultCache.get(cacheKey);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const vertical = this.native?.font_is_vertical?.(index) ?? false;
|
|
440
|
+
this.setCached(cacheKey, vertical);
|
|
441
|
+
return vertical;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Gets character widths for the font
|
|
446
|
+
* @param fonts Font handle
|
|
447
|
+
* @param index Index of the font
|
|
448
|
+
* @returns Array of character widths
|
|
449
|
+
*/
|
|
450
|
+
async getFontWidths(fonts: any, index: number): Promise<Float32Array> {
|
|
451
|
+
const cacheKey = `font:widths:${index}`;
|
|
452
|
+
if (this.resultCache.has(cacheKey)) {
|
|
453
|
+
return this.resultCache.get(cacheKey);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const widths = this.native?.font_get_widths?.(index) ?? new Float32Array();
|
|
457
|
+
this.setCached(cacheKey, widths);
|
|
458
|
+
return widths;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Gets the ascender metric (height above baseline)
|
|
463
|
+
* @param fonts Font handle
|
|
464
|
+
* @param index Index of the font
|
|
465
|
+
* @returns Ascender value in font units
|
|
466
|
+
*/
|
|
467
|
+
async getFontAscender(fonts: any, index: number): Promise<number> {
|
|
468
|
+
const cacheKey = `font:ascender:${index}`;
|
|
469
|
+
if (this.resultCache.has(cacheKey)) {
|
|
470
|
+
return this.resultCache.get(cacheKey);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const ascender = this.native?.font_get_ascender?.(index) ?? 0;
|
|
474
|
+
this.setCached(cacheKey, ascender);
|
|
475
|
+
return ascender;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Gets the descender metric (depth below baseline)
|
|
480
|
+
* @param fonts Font handle
|
|
481
|
+
* @param index Index of the font
|
|
482
|
+
* @returns Descender value in font units (usually negative)
|
|
483
|
+
*/
|
|
484
|
+
async getFontDescender(fonts: any, index: number): Promise<number> {
|
|
485
|
+
const cacheKey = `font:descender:${index}`;
|
|
486
|
+
if (this.resultCache.has(cacheKey)) {
|
|
487
|
+
return this.resultCache.get(cacheKey);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
const descender = this.native?.font_get_descender?.(index) ?? 0;
|
|
491
|
+
this.setCached(cacheKey, descender);
|
|
492
|
+
return descender;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Gets all font properties at once
|
|
497
|
+
* More efficient than individual property calls
|
|
498
|
+
* @param fonts Font handle
|
|
499
|
+
* @param index Index of the font
|
|
500
|
+
* @returns Object with all font properties
|
|
501
|
+
*/
|
|
502
|
+
async getFontAllProperties(fonts: any, index: number): Promise<FontProperties> {
|
|
503
|
+
const cacheKey = `font:all:${index}`;
|
|
504
|
+
if (this.resultCache.has(cacheKey)) {
|
|
505
|
+
return this.resultCache.get(cacheKey);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// Aggregate all font properties by calling individual native functions
|
|
509
|
+
const baseFontName = this.native?.font_get_base_font_name?.(index) ?? '';
|
|
510
|
+
const descriptor = this.native?.font_get_descriptor?.(index) ?? '{}';
|
|
511
|
+
const descendantFont = this.native?.font_get_descendant_font?.(index) ?? '';
|
|
512
|
+
const toUnicodeCmap = this.native?.font_get_to_unicode_cmap?.(index) ?? '';
|
|
513
|
+
const isVertical = this.native?.font_is_vertical?.(index) ?? false;
|
|
514
|
+
const widths = this.native?.font_get_widths?.(index) ?? new Float32Array();
|
|
515
|
+
const ascender = this.native?.font_get_ascender?.(index) ?? 0;
|
|
516
|
+
const descender = this.native?.font_get_descender?.(index) ?? 0;
|
|
517
|
+
|
|
518
|
+
const props: FontProperties = {
|
|
519
|
+
baseFontName,
|
|
520
|
+
descriptor,
|
|
521
|
+
descendantFont,
|
|
522
|
+
toUnicodeCmap,
|
|
523
|
+
isVertical,
|
|
524
|
+
widths,
|
|
525
|
+
ascender,
|
|
526
|
+
descender,
|
|
527
|
+
};
|
|
528
|
+
this.setCached(cacheKey, props);
|
|
529
|
+
this.emit('fontPropertiesExtracted', index);
|
|
530
|
+
return props;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// ========== Image Accessors (5 functions) ==========
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Checks if image has an alpha channel
|
|
537
|
+
* @param images Image handle
|
|
538
|
+
* @param index Index of the image
|
|
539
|
+
* @returns True if alpha channel is present
|
|
540
|
+
*/
|
|
541
|
+
async hasImageAlphaChannel(images: any, index: number): Promise<boolean> {
|
|
542
|
+
const cacheKey = `image:hasalpha:${index}`;
|
|
543
|
+
if (this.resultCache.has(cacheKey)) {
|
|
544
|
+
return this.resultCache.get(cacheKey);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const hasAlpha = this.native?.image_has_alpha_channel?.(index) ?? false;
|
|
548
|
+
this.setCached(cacheKey, hasAlpha);
|
|
549
|
+
return hasAlpha;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Gets the ICC color profile
|
|
554
|
+
* @param images Image handle
|
|
555
|
+
* @param index Index of the image
|
|
556
|
+
* @returns ICC profile as binary data
|
|
557
|
+
*/
|
|
558
|
+
async getImageIccProfile(images: any, index: number): Promise<Uint8Array> {
|
|
559
|
+
const cacheKey = `image:iccprofile:${index}`;
|
|
560
|
+
if (this.resultCache.has(cacheKey)) {
|
|
561
|
+
return this.resultCache.get(cacheKey);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
const profile = this.native?.image_get_icc_profile?.(index) ?? new Uint8Array();
|
|
565
|
+
this.setCached(cacheKey, profile);
|
|
566
|
+
return profile;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Gets the filter chain applied to the image
|
|
571
|
+
* (e.g., ["FlateDecode", "DCTDecode"])
|
|
572
|
+
* @param images Image handle
|
|
573
|
+
* @param index Index of the image
|
|
574
|
+
* @returns Filter chain as JSON string
|
|
575
|
+
*/
|
|
576
|
+
async getImageFilterChain(images: any, index: number): Promise<string> {
|
|
577
|
+
const cacheKey = `image:filterchain:${index}`;
|
|
578
|
+
if (this.resultCache.has(cacheKey)) {
|
|
579
|
+
return this.resultCache.get(cacheKey);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const filterChain = this.native?.image_get_filter_chain?.(index) ?? '[]';
|
|
583
|
+
this.setCached(cacheKey, filterChain);
|
|
584
|
+
return filterChain;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Gets the decoded image data
|
|
589
|
+
* @param images Image handle
|
|
590
|
+
* @param index Index of the image
|
|
591
|
+
* @returns Decoded image data as binary
|
|
592
|
+
*/
|
|
593
|
+
async getImageDecodedData(images: any, index: number): Promise<Uint8Array> {
|
|
594
|
+
const cacheKey = `image:decoded:${index}`;
|
|
595
|
+
if (this.resultCache.has(cacheKey)) {
|
|
596
|
+
return this.resultCache.get(cacheKey);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
const data = this.native?.image_get_decoded_data?.(index) ?? new Uint8Array();
|
|
600
|
+
this.setCached(cacheKey, data);
|
|
601
|
+
return data;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Gets the image width in pixels
|
|
606
|
+
* @param images Image handle
|
|
607
|
+
* @param index Index of the image
|
|
608
|
+
* @returns Width in pixels
|
|
609
|
+
*/
|
|
610
|
+
async getImageWidth(images: any, index: number): Promise<number> {
|
|
611
|
+
const cacheKey = `image:width:${index}`;
|
|
612
|
+
if (this.resultCache.has(cacheKey)) {
|
|
613
|
+
return this.resultCache.get(cacheKey);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
const width = this.native?.image_get_width?.(index) ?? 0;
|
|
617
|
+
this.setCached(cacheKey, width);
|
|
618
|
+
return width;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Gets the image height in pixels
|
|
623
|
+
* @param images Image handle
|
|
624
|
+
* @param index Index of the image
|
|
625
|
+
* @returns Height in pixels
|
|
626
|
+
*/
|
|
627
|
+
async getImageHeight(images: any, index: number): Promise<number> {
|
|
628
|
+
const cacheKey = `image:height:${index}`;
|
|
629
|
+
if (this.resultCache.has(cacheKey)) {
|
|
630
|
+
return this.resultCache.get(cacheKey);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
const height = this.native?.image_get_height?.(index) ?? 0;
|
|
634
|
+
this.setCached(cacheKey, height);
|
|
635
|
+
return height;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Gets the color space of the image
|
|
640
|
+
* @param images Image handle
|
|
641
|
+
* @param index Index of the image
|
|
642
|
+
* @returns Color space name (e.g., "RGB", "CMYK", "Gray")
|
|
643
|
+
*/
|
|
644
|
+
async getImageColorSpace(images: any, index: number): Promise<string> {
|
|
645
|
+
const cacheKey = `image:colorspace:${index}`;
|
|
646
|
+
if (this.resultCache.has(cacheKey)) {
|
|
647
|
+
return this.resultCache.get(cacheKey);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
const colorSpace = this.native?.image_get_color_space?.(index) ?? 'RGB';
|
|
651
|
+
this.setCached(cacheKey, colorSpace);
|
|
652
|
+
return colorSpace;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* Gets all image properties at once
|
|
657
|
+
* More efficient than individual property calls
|
|
658
|
+
* @param images Image handle
|
|
659
|
+
* @param index Index of the image
|
|
660
|
+
* @returns Object with all image properties
|
|
661
|
+
*/
|
|
662
|
+
async getImageAllProperties(images: any, index: number): Promise<ImageProperties> {
|
|
663
|
+
const cacheKey = `image:all:${index}`;
|
|
664
|
+
if (this.resultCache.has(cacheKey)) {
|
|
665
|
+
return this.resultCache.get(cacheKey);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// Aggregate all image properties by calling individual native functions
|
|
669
|
+
const hasAlphaChannel = this.native?.image_has_alpha_channel?.(index) ?? false;
|
|
670
|
+
const iccProfile = this.native?.image_get_icc_profile?.(index) ?? new Uint8Array();
|
|
671
|
+
const filterChain = this.native?.image_get_filter_chain?.(index) ?? '[]';
|
|
672
|
+
const decodedData = this.native?.image_get_decoded_data?.(index) ?? new Uint8Array();
|
|
673
|
+
const width = this.native?.image_get_width?.(index) ?? 0;
|
|
674
|
+
const height = this.native?.image_get_height?.(index) ?? 0;
|
|
675
|
+
const colorSpace = this.native?.image_get_color_space?.(index) ?? 'RGB';
|
|
676
|
+
|
|
677
|
+
const props: ImageProperties = {
|
|
678
|
+
hasAlphaChannel,
|
|
679
|
+
iccProfile,
|
|
680
|
+
filterChain,
|
|
681
|
+
decodedData,
|
|
682
|
+
width,
|
|
683
|
+
height,
|
|
684
|
+
colorSpace,
|
|
685
|
+
};
|
|
686
|
+
this.setCached(cacheKey, props);
|
|
687
|
+
this.emit('imagePropertiesExtracted', index);
|
|
688
|
+
return props;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// ========== Annotation Accessors (6 functions) ==========
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* Gets the modified date of an annotation
|
|
695
|
+
* @param annotations Annotation handle
|
|
696
|
+
* @param index Index of the annotation
|
|
697
|
+
* @returns Timestamp in milliseconds
|
|
698
|
+
*/
|
|
699
|
+
async getAnnotationModifiedDate(
|
|
700
|
+
annotations: any,
|
|
701
|
+
index: number
|
|
702
|
+
): Promise<number> {
|
|
703
|
+
const cacheKey = `annotation:modifieddate:${index}`;
|
|
704
|
+
if (this.resultCache.has(cacheKey)) {
|
|
705
|
+
return this.resultCache.get(cacheKey);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
const timestamp = this.native?.annotation_get_modified_date?.(index) ?? 0;
|
|
709
|
+
this.setCached(cacheKey, timestamp);
|
|
710
|
+
return timestamp;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* Gets the subject/title of an annotation
|
|
715
|
+
* @param annotations Annotation handle
|
|
716
|
+
* @param index Index of the annotation
|
|
717
|
+
* @returns Subject text
|
|
718
|
+
*/
|
|
719
|
+
async getAnnotationSubject(annotations: any, index: number): Promise<string> {
|
|
720
|
+
const cacheKey = `annotation:subject:${index}`;
|
|
721
|
+
if (this.resultCache.has(cacheKey)) {
|
|
722
|
+
return this.resultCache.get(cacheKey);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
const subject = this.native?.annotation_get_subject?.(index) ?? '';
|
|
726
|
+
this.setCached(cacheKey, subject);
|
|
727
|
+
return subject;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* Gets the index of the annotation this is replying to
|
|
732
|
+
* @param annotations Annotation handle
|
|
733
|
+
* @param index Index of the annotation
|
|
734
|
+
* @returns Index of parent annotation, or -1 if not a reply
|
|
735
|
+
*/
|
|
736
|
+
async getAnnotationReplyToIndex(annotations: any, index: number): Promise<number> {
|
|
737
|
+
const cacheKey = `annotation:replyto:${index}`;
|
|
738
|
+
if (this.resultCache.has(cacheKey)) {
|
|
739
|
+
return this.resultCache.get(cacheKey);
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
const replyToIndex = this.native?.annotation_get_reply_to?.(index) ?? -1;
|
|
743
|
+
this.setCached(cacheKey, replyToIndex);
|
|
744
|
+
return replyToIndex;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
/**
|
|
748
|
+
* Gets the page number where annotation appears
|
|
749
|
+
* @param annotations Annotation handle
|
|
750
|
+
* @param index Index of the annotation
|
|
751
|
+
* @returns Page number (0-based)
|
|
752
|
+
*/
|
|
753
|
+
async getAnnotationPageNumber(annotations: any, index: number): Promise<number> {
|
|
754
|
+
const cacheKey = `annotation:pagenum:${index}`;
|
|
755
|
+
if (this.resultCache.has(cacheKey)) {
|
|
756
|
+
return this.resultCache.get(cacheKey);
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
const pageNumber = this.native?.annotation_get_page_number?.(index) ?? 0;
|
|
760
|
+
this.setCached(cacheKey, pageNumber);
|
|
761
|
+
return pageNumber;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* Gets the icon name for the annotation
|
|
766
|
+
* (e.g., "Comment", "Note", "Help")
|
|
767
|
+
* @param annotations Annotation handle
|
|
768
|
+
* @param index Index of the annotation
|
|
769
|
+
* @returns Icon name
|
|
770
|
+
*/
|
|
771
|
+
async getAnnotationIconName(annotations: any, index: number): Promise<string> {
|
|
772
|
+
const cacheKey = `annotation:icon:${index}`;
|
|
773
|
+
if (this.resultCache.has(cacheKey)) {
|
|
774
|
+
return this.resultCache.get(cacheKey);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
const icon = this.native?.annotation_get_icon_name?.(index) ?? '';
|
|
778
|
+
this.setCached(cacheKey, icon);
|
|
779
|
+
return icon;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* Gets the author/creator of the annotation
|
|
784
|
+
* @param annotations Annotation handle
|
|
785
|
+
* @param index Index of the annotation
|
|
786
|
+
* @returns Author name
|
|
787
|
+
*/
|
|
788
|
+
async getAnnotationAuthor(annotations: any, index: number): Promise<string> {
|
|
789
|
+
const cacheKey = `annotation:author:${index}`;
|
|
790
|
+
if (this.resultCache.has(cacheKey)) {
|
|
791
|
+
return this.resultCache.get(cacheKey);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
const author = this.native?.annotation_get_author?.(index) ?? '';
|
|
795
|
+
this.setCached(cacheKey, author);
|
|
796
|
+
return author;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
/**
|
|
800
|
+
* Gets all annotation properties at once
|
|
801
|
+
* More efficient than individual property calls
|
|
802
|
+
* @param annotations Annotation handle
|
|
803
|
+
* @param index Index of the annotation
|
|
804
|
+
* @returns Object with all annotation properties
|
|
805
|
+
*/
|
|
806
|
+
async getAnnotationAllProperties(
|
|
807
|
+
annotations: any,
|
|
808
|
+
index: number
|
|
809
|
+
): Promise<AnnotationProperties> {
|
|
810
|
+
const cacheKey = `annotation:all:${index}`;
|
|
811
|
+
if (this.resultCache.has(cacheKey)) {
|
|
812
|
+
return this.resultCache.get(cacheKey);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// Aggregate all annotation properties by calling individual native functions
|
|
816
|
+
const modifiedDate = this.native?.annotation_get_modified_date?.(index) ?? 0;
|
|
817
|
+
const subject = this.native?.annotation_get_subject?.(index) ?? '';
|
|
818
|
+
const replyToIndex = this.native?.annotation_get_reply_to?.(index) ?? -1;
|
|
819
|
+
const pageNumber = this.native?.annotation_get_page_number?.(index) ?? 0;
|
|
820
|
+
const iconName = this.native?.annotation_get_icon_name?.(index) ?? '';
|
|
821
|
+
const author = this.native?.annotation_get_author?.(index) ?? '';
|
|
822
|
+
|
|
823
|
+
const props: AnnotationProperties = {
|
|
824
|
+
modifiedDate,
|
|
825
|
+
subject,
|
|
826
|
+
replyToIndex,
|
|
827
|
+
pageNumber,
|
|
828
|
+
iconName,
|
|
829
|
+
author,
|
|
830
|
+
};
|
|
831
|
+
this.setCached(cacheKey, props);
|
|
832
|
+
this.emit('annotationPropertiesExtracted', index);
|
|
833
|
+
return props;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
// ========== Cache Management ==========
|
|
837
|
+
|
|
838
|
+
/**
|
|
839
|
+
* Clears the result cache
|
|
840
|
+
*/
|
|
841
|
+
clearCache(): void {
|
|
842
|
+
this.resultCache.clear();
|
|
843
|
+
this.emit('cacheCleared');
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
/**
|
|
847
|
+
* Gets cache statistics
|
|
848
|
+
* @returns Object with cache information
|
|
849
|
+
*/
|
|
850
|
+
getCacheStats(): Record<string, any> {
|
|
851
|
+
return {
|
|
852
|
+
cacheSize: this.resultCache.size,
|
|
853
|
+
maxCacheSize: this.maxCacheSize,
|
|
854
|
+
entries: Array.from(this.resultCache.keys()),
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
private clearCachePattern(pattern: string): void {
|
|
859
|
+
const regex = new RegExp(pattern);
|
|
860
|
+
const keysToDelete = Array.from(this.resultCache.keys()).filter((key) =>
|
|
861
|
+
regex.test(key)
|
|
862
|
+
);
|
|
863
|
+
keysToDelete.forEach((key) => this.resultCache.delete(key));
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
export default ResultAccessorsManager;
|