@namahapdf/core 0.1.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 +16 -0
- package/README.md +24 -0
- package/dist/chunk-X7U5LSSK.js +1 -0
- package/dist/fontkit.es-K7R5OPSY.js +47 -0
- package/dist/index.cjs +89 -0
- package/dist/index.d.cts +1360 -0
- package/dist/index.d.ts +1360 -0
- package/dist/index.js +42 -0
- package/dist/pdf.image_decoders.min-5CGUOWQR.js +2 -0
- package/package.json +33 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,1360 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Avni Engine Core Types
|
|
3
|
+
* Defines the standard interfaces for document processing
|
|
4
|
+
*/
|
|
5
|
+
type DocumentType = 'pdf' | 'docx' | 'image' | 'text' | 'csv';
|
|
6
|
+
interface AvniDocument {
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
blob: Blob;
|
|
10
|
+
type: DocumentType;
|
|
11
|
+
size: number;
|
|
12
|
+
metadata: Record<string, unknown>;
|
|
13
|
+
history: ProcessingStep[];
|
|
14
|
+
}
|
|
15
|
+
interface ProcessingStep {
|
|
16
|
+
taskId: string;
|
|
17
|
+
processorName: string;
|
|
18
|
+
timestamp: number;
|
|
19
|
+
params: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
interface ProcessorResult {
|
|
22
|
+
success: boolean;
|
|
23
|
+
document: AvniDocument;
|
|
24
|
+
error?: string;
|
|
25
|
+
metrics?: {
|
|
26
|
+
duration: number;
|
|
27
|
+
originalSize: number;
|
|
28
|
+
newSize: number;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
interface IAvniProcessor {
|
|
32
|
+
name: string;
|
|
33
|
+
supportedTypes: DocumentType[];
|
|
34
|
+
process(doc: AvniDocument, params: Record<string, unknown>): Promise<ProcessorResult>;
|
|
35
|
+
}
|
|
36
|
+
interface PipelineConfig {
|
|
37
|
+
tasks: {
|
|
38
|
+
processorName: string;
|
|
39
|
+
params: Record<string, unknown>;
|
|
40
|
+
}[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
declare class AvniOrchestrator {
|
|
44
|
+
private processors;
|
|
45
|
+
/**
|
|
46
|
+
* Register a new tool (processor) to the engine
|
|
47
|
+
*/
|
|
48
|
+
registerProcessor(processor: IAvniProcessor): void;
|
|
49
|
+
/**
|
|
50
|
+
* Run a sequence of tasks on a document
|
|
51
|
+
*/
|
|
52
|
+
runPipeline(initialDoc: AvniDocument, config: PipelineConfig): Promise<ProcessorResult[]>;
|
|
53
|
+
getAvailableProcessors(): string[];
|
|
54
|
+
}
|
|
55
|
+
declare const avniEngine: AvniOrchestrator;
|
|
56
|
+
|
|
57
|
+
type PDFNull = {
|
|
58
|
+
type: 'null';
|
|
59
|
+
};
|
|
60
|
+
type PDFBoolean = {
|
|
61
|
+
type: 'boolean';
|
|
62
|
+
value: boolean;
|
|
63
|
+
};
|
|
64
|
+
type PDFInteger = {
|
|
65
|
+
type: 'integer';
|
|
66
|
+
value: number;
|
|
67
|
+
};
|
|
68
|
+
type PDFReal = {
|
|
69
|
+
type: 'real';
|
|
70
|
+
value: number;
|
|
71
|
+
};
|
|
72
|
+
type PDFString = {
|
|
73
|
+
type: 'string';
|
|
74
|
+
value: Uint8Array;
|
|
75
|
+
encoding: 'literal' | 'hex';
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* All possible PDF object types
|
|
79
|
+
*/
|
|
80
|
+
type PDFObject = PDFNull | PDFBoolean | PDFInteger | PDFReal | PDFString | PDFName | PDFArray | PDFDict | PDFStream | PDFRef;
|
|
81
|
+
/**
|
|
82
|
+
* PDF Name object - always starts with /
|
|
83
|
+
* Examples: /Type, /Font, /Contents
|
|
84
|
+
*/
|
|
85
|
+
type PDFName = {
|
|
86
|
+
type: 'name';
|
|
87
|
+
value: string;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* PDF Array - [ item1 item2 ... ]
|
|
91
|
+
*/
|
|
92
|
+
type PDFArray = {
|
|
93
|
+
type: 'array';
|
|
94
|
+
items: PDFObject[];
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* PDF Dictionary - << key1 value1 key2 value2 ... >>
|
|
98
|
+
*/
|
|
99
|
+
type PDFDict = {
|
|
100
|
+
type: 'dict';
|
|
101
|
+
entries: Map<string, PDFObject>;
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* PDF Stream - binary data with associated dictionary
|
|
105
|
+
* << dict >>
|
|
106
|
+
* stream
|
|
107
|
+
* ... binary data ...
|
|
108
|
+
* endstream
|
|
109
|
+
*/
|
|
110
|
+
type PDFStream = {
|
|
111
|
+
type: 'stream';
|
|
112
|
+
dict: PDFDict;
|
|
113
|
+
rawBytes: Uint8Array;
|
|
114
|
+
decode: () => Promise<Uint8Array>;
|
|
115
|
+
data?: Uint8Array;
|
|
116
|
+
} | {
|
|
117
|
+
type: 'stream';
|
|
118
|
+
dict: PDFDict;
|
|
119
|
+
data: Uint8Array;
|
|
120
|
+
rawBytes?: Uint8Array;
|
|
121
|
+
decode?: () => Promise<Uint8Array>;
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* PDF Indirect Reference - R indirect object reference
|
|
125
|
+
* Example: 5 0 R refers to object 5, generation 0
|
|
126
|
+
*/
|
|
127
|
+
type PDFRef = {
|
|
128
|
+
type: 'ref';
|
|
129
|
+
objNum: number;
|
|
130
|
+
genNum: number;
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Cross-reference table entry
|
|
134
|
+
* Points to where an object starts in the file
|
|
135
|
+
*/
|
|
136
|
+
type XRefEntry = {
|
|
137
|
+
type: 'uncompressed';
|
|
138
|
+
offset: number;
|
|
139
|
+
genNum: number;
|
|
140
|
+
inUse: boolean;
|
|
141
|
+
} | {
|
|
142
|
+
type: 'compressed';
|
|
143
|
+
streamNum: number;
|
|
144
|
+
indexNum: number;
|
|
145
|
+
genNum: number;
|
|
146
|
+
inUse: boolean;
|
|
147
|
+
};
|
|
148
|
+
/**
|
|
149
|
+
* Parsed PDF document - the object tree
|
|
150
|
+
*/
|
|
151
|
+
type PDFDocument = {
|
|
152
|
+
rawBytes: Uint8Array;
|
|
153
|
+
xrefTables: Map<number, Map<number, XRefEntry>>;
|
|
154
|
+
objectCache: Map<string, PDFObject>;
|
|
155
|
+
rootRef: PDFRef;
|
|
156
|
+
trailer: PDFDict;
|
|
157
|
+
version: string;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* PDF Parser - Main Entry Point
|
|
162
|
+
*
|
|
163
|
+
* Orchestrates the parsing pipeline:
|
|
164
|
+
* 1. Find xref offset
|
|
165
|
+
* 2. Parse cross-reference table(s)
|
|
166
|
+
* 3. Create lazy-loaded object tree
|
|
167
|
+
* 4. Return PDFDocument
|
|
168
|
+
*
|
|
169
|
+
* Supports both compressed (xref stream) and uncompressed (traditional xref) formats.
|
|
170
|
+
*/
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Main PDF Parser
|
|
174
|
+
*/
|
|
175
|
+
declare class PDFParser {
|
|
176
|
+
private bytes;
|
|
177
|
+
private document;
|
|
178
|
+
private objectStreamCache;
|
|
179
|
+
private encryptRef;
|
|
180
|
+
private decryptionHandler;
|
|
181
|
+
constructor(pdfBytes: Uint8Array);
|
|
182
|
+
/**
|
|
183
|
+
* Main parse entry point
|
|
184
|
+
*/
|
|
185
|
+
parse(): PDFDocument;
|
|
186
|
+
/**
|
|
187
|
+
* Extract PDF version from header
|
|
188
|
+
* Format: %PDF-X.Y
|
|
189
|
+
*/
|
|
190
|
+
private extractVersion;
|
|
191
|
+
/**
|
|
192
|
+
* Parse all xref tables (including previous versions for incremental updates)
|
|
193
|
+
*/
|
|
194
|
+
private parseXRefTables;
|
|
195
|
+
/**
|
|
196
|
+
* Extract root reference from trailer
|
|
197
|
+
*/
|
|
198
|
+
private extractRootRef;
|
|
199
|
+
/**
|
|
200
|
+
* Load an object by reference (lazy loading)
|
|
201
|
+
* This is called by users of the parser to load specific objects
|
|
202
|
+
*/
|
|
203
|
+
loadObject(ref: PDFRef): PDFObject;
|
|
204
|
+
private loadObjectStream;
|
|
205
|
+
private repairStreamBytesFromLengthRef;
|
|
206
|
+
private findStreamDataStart;
|
|
207
|
+
private decryptObject;
|
|
208
|
+
private decryptObjectRecursive;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
declare function loadPageContentStream(contentsEntry: PDFObject | undefined, loadObject: (ref: PDFRef) => PDFObject | Promise<PDFObject>): Promise<{
|
|
212
|
+
combinedBytes: Uint8Array;
|
|
213
|
+
streamCount: number;
|
|
214
|
+
streamByteLengths: number[];
|
|
215
|
+
}>;
|
|
216
|
+
|
|
217
|
+
type Matrix = [number, number, number, number, number, number];
|
|
218
|
+
type DrawCommand = {
|
|
219
|
+
op: 'save_state';
|
|
220
|
+
} | {
|
|
221
|
+
op: 'restore_state';
|
|
222
|
+
} | {
|
|
223
|
+
op: 'set_ctm';
|
|
224
|
+
matrix: Matrix;
|
|
225
|
+
} | {
|
|
226
|
+
op: 'begin_text';
|
|
227
|
+
} | {
|
|
228
|
+
op: 'end_text';
|
|
229
|
+
} | {
|
|
230
|
+
op: 'set_font';
|
|
231
|
+
name: string;
|
|
232
|
+
size: number;
|
|
233
|
+
} | {
|
|
234
|
+
op: 'move_text';
|
|
235
|
+
tx: number;
|
|
236
|
+
ty: number;
|
|
237
|
+
} | {
|
|
238
|
+
op: 'move_text_set_leading';
|
|
239
|
+
tx: number;
|
|
240
|
+
ty: number;
|
|
241
|
+
} | {
|
|
242
|
+
op: 'set_text_matrix';
|
|
243
|
+
matrix: Matrix;
|
|
244
|
+
} | {
|
|
245
|
+
op: 'next_line';
|
|
246
|
+
} | {
|
|
247
|
+
op: 'show_string';
|
|
248
|
+
data: Uint8Array;
|
|
249
|
+
} | {
|
|
250
|
+
op: 'show_strings';
|
|
251
|
+
items: Array<Uint8Array | number>;
|
|
252
|
+
} | {
|
|
253
|
+
op: 'next_line_show_string';
|
|
254
|
+
data: Uint8Array;
|
|
255
|
+
} | {
|
|
256
|
+
op: 'set_spacing_next_line_show_string';
|
|
257
|
+
wordSpacing: number;
|
|
258
|
+
charSpacing: number;
|
|
259
|
+
data: Uint8Array;
|
|
260
|
+
} | {
|
|
261
|
+
op: 'set_char_spacing';
|
|
262
|
+
spacing: number;
|
|
263
|
+
} | {
|
|
264
|
+
op: 'set_word_spacing';
|
|
265
|
+
spacing: number;
|
|
266
|
+
} | {
|
|
267
|
+
op: 'set_horiz_scale';
|
|
268
|
+
scale: number;
|
|
269
|
+
} | {
|
|
270
|
+
op: 'set_leading';
|
|
271
|
+
leading: number;
|
|
272
|
+
} | {
|
|
273
|
+
op: 'set_text_render';
|
|
274
|
+
mode: number;
|
|
275
|
+
} | {
|
|
276
|
+
op: 'set_text_rise';
|
|
277
|
+
rise: number;
|
|
278
|
+
} | {
|
|
279
|
+
op: 'move_to';
|
|
280
|
+
x: number;
|
|
281
|
+
y: number;
|
|
282
|
+
} | {
|
|
283
|
+
op: 'line_to';
|
|
284
|
+
x: number;
|
|
285
|
+
y: number;
|
|
286
|
+
} | {
|
|
287
|
+
op: 'curve_to';
|
|
288
|
+
x1: number;
|
|
289
|
+
y1: number;
|
|
290
|
+
x2: number;
|
|
291
|
+
y2: number;
|
|
292
|
+
x3: number;
|
|
293
|
+
y3: number;
|
|
294
|
+
} | {
|
|
295
|
+
op: 'rect';
|
|
296
|
+
x: number;
|
|
297
|
+
y: number;
|
|
298
|
+
w: number;
|
|
299
|
+
h: number;
|
|
300
|
+
} | {
|
|
301
|
+
op: 'close_path';
|
|
302
|
+
} | {
|
|
303
|
+
op: 'stroke';
|
|
304
|
+
} | {
|
|
305
|
+
op: 'fill';
|
|
306
|
+
} | {
|
|
307
|
+
op: 'fill_even_odd';
|
|
308
|
+
} | {
|
|
309
|
+
op: 'fill_stroke';
|
|
310
|
+
} | {
|
|
311
|
+
op: 'clip';
|
|
312
|
+
} | {
|
|
313
|
+
op: 'clip_even_odd';
|
|
314
|
+
} | {
|
|
315
|
+
op: 'end_path';
|
|
316
|
+
} | {
|
|
317
|
+
op: 'set_line_width';
|
|
318
|
+
width: number;
|
|
319
|
+
} | {
|
|
320
|
+
op: 'set_line_cap';
|
|
321
|
+
cap: number;
|
|
322
|
+
} | {
|
|
323
|
+
op: 'set_line_join';
|
|
324
|
+
join: number;
|
|
325
|
+
} | {
|
|
326
|
+
op: 'set_miter_limit';
|
|
327
|
+
limit: number;
|
|
328
|
+
} | {
|
|
329
|
+
op: 'set_dash';
|
|
330
|
+
array: number[];
|
|
331
|
+
phase: number;
|
|
332
|
+
} | {
|
|
333
|
+
op: 'set_rendering_intent';
|
|
334
|
+
intent: string;
|
|
335
|
+
} | {
|
|
336
|
+
op: 'fill_stroke_even_odd';
|
|
337
|
+
} | {
|
|
338
|
+
op: 'close_stroke';
|
|
339
|
+
} | {
|
|
340
|
+
op: 'curve_to_v';
|
|
341
|
+
x2: number;
|
|
342
|
+
y2: number;
|
|
343
|
+
x3: number;
|
|
344
|
+
y3: number;
|
|
345
|
+
} | {
|
|
346
|
+
op: 'set_fill_color_rgb';
|
|
347
|
+
r: number;
|
|
348
|
+
g: number;
|
|
349
|
+
b: number;
|
|
350
|
+
} | {
|
|
351
|
+
op: 'set_stroke_color_rgb';
|
|
352
|
+
r: number;
|
|
353
|
+
g: number;
|
|
354
|
+
b: number;
|
|
355
|
+
} | {
|
|
356
|
+
op: 'set_fill_gray';
|
|
357
|
+
gray: number;
|
|
358
|
+
} | {
|
|
359
|
+
op: 'set_stroke_gray';
|
|
360
|
+
gray: number;
|
|
361
|
+
} | {
|
|
362
|
+
op: 'set_fill_cmyk';
|
|
363
|
+
c: number;
|
|
364
|
+
m: number;
|
|
365
|
+
y: number;
|
|
366
|
+
k: number;
|
|
367
|
+
} | {
|
|
368
|
+
op: 'set_stroke_cmyk';
|
|
369
|
+
c: number;
|
|
370
|
+
m: number;
|
|
371
|
+
y: number;
|
|
372
|
+
k: number;
|
|
373
|
+
} | {
|
|
374
|
+
op: 'set_fill_color_space';
|
|
375
|
+
name: string;
|
|
376
|
+
} | {
|
|
377
|
+
op: 'set_stroke_color_space';
|
|
378
|
+
name: string;
|
|
379
|
+
} | {
|
|
380
|
+
op: 'set_fill_color';
|
|
381
|
+
values: number[];
|
|
382
|
+
patternName?: string;
|
|
383
|
+
} | {
|
|
384
|
+
op: 'set_stroke_color';
|
|
385
|
+
values: number[];
|
|
386
|
+
patternName?: string;
|
|
387
|
+
} | {
|
|
388
|
+
op: 'set_graphics_state';
|
|
389
|
+
name: string;
|
|
390
|
+
} | {
|
|
391
|
+
op: 'xobject';
|
|
392
|
+
name: string;
|
|
393
|
+
} | {
|
|
394
|
+
op: 'begin_marked_content';
|
|
395
|
+
tag: string;
|
|
396
|
+
properties?: PDFDict;
|
|
397
|
+
} | {
|
|
398
|
+
op: 'end_marked_content';
|
|
399
|
+
} | {
|
|
400
|
+
op: 'type3_glyph_width';
|
|
401
|
+
wx: number;
|
|
402
|
+
wy: number;
|
|
403
|
+
} | {
|
|
404
|
+
op: 'shade';
|
|
405
|
+
name: string;
|
|
406
|
+
} | {
|
|
407
|
+
op: 'close_fill_stroke';
|
|
408
|
+
} | {
|
|
409
|
+
op: 'close_fill_stroke_even_odd';
|
|
410
|
+
} | {
|
|
411
|
+
op: 'inline_image';
|
|
412
|
+
params: PDFDict;
|
|
413
|
+
data: Uint8Array;
|
|
414
|
+
} | {
|
|
415
|
+
op: 'unknown';
|
|
416
|
+
keyword: string;
|
|
417
|
+
operands: PDFObject[];
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
declare function parseContentStream(bytes: Uint8Array): DrawCommand[];
|
|
421
|
+
|
|
422
|
+
type Color = {
|
|
423
|
+
space: 'rgb';
|
|
424
|
+
r: number;
|
|
425
|
+
g: number;
|
|
426
|
+
b: number;
|
|
427
|
+
} | {
|
|
428
|
+
space: 'gray';
|
|
429
|
+
gray: number;
|
|
430
|
+
} | {
|
|
431
|
+
space: 'cmyk';
|
|
432
|
+
c: number;
|
|
433
|
+
m: number;
|
|
434
|
+
y: number;
|
|
435
|
+
k: number;
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
type RenderGlyphBox = {
|
|
439
|
+
canvasX: number;
|
|
440
|
+
canvasY: number;
|
|
441
|
+
canvasWidth: number;
|
|
442
|
+
canvasHeight: number;
|
|
443
|
+
canvasBaselineY: number;
|
|
444
|
+
char: string;
|
|
445
|
+
charCode: number;
|
|
446
|
+
runIndex: number;
|
|
447
|
+
};
|
|
448
|
+
type RenderTextRun = {
|
|
449
|
+
index: number;
|
|
450
|
+
text: string;
|
|
451
|
+
fontName: string;
|
|
452
|
+
fontSize: number;
|
|
453
|
+
canvasX: number;
|
|
454
|
+
canvasY: number;
|
|
455
|
+
canvasWidth: number;
|
|
456
|
+
canvasHeight: number;
|
|
457
|
+
canvasBaselineX: number;
|
|
458
|
+
canvasBaselineY: number;
|
|
459
|
+
fillColor: {
|
|
460
|
+
r: number;
|
|
461
|
+
g: number;
|
|
462
|
+
b: number;
|
|
463
|
+
};
|
|
464
|
+
streamOperatorIndex: number;
|
|
465
|
+
/** True when captured inside a Form XObject — streamOperatorIndex is then
|
|
466
|
+
* relative to the form's command list, not the page content stream. */
|
|
467
|
+
fromXObject: boolean;
|
|
468
|
+
glyphs: RenderGlyphBox[];
|
|
469
|
+
/** The exact CSS font-family the canvas drew this run with (registered
|
|
470
|
+
* embedded family + substitute fallback, or just the substitute family).
|
|
471
|
+
* Lets the inline editor render identical text — no size/width jump. */
|
|
472
|
+
fontFamily?: string;
|
|
473
|
+
};
|
|
474
|
+
/** A drawn image (XObject or inline), captured for editor selection. */
|
|
475
|
+
type RenderImageBox = {
|
|
476
|
+
canvasX: number;
|
|
477
|
+
canvasY: number;
|
|
478
|
+
canvasWidth: number;
|
|
479
|
+
canvasHeight: number;
|
|
480
|
+
/** PDF user-space CTM at draw time (image occupies the unit square). */
|
|
481
|
+
ctm: Matrix;
|
|
482
|
+
kind: 'xobject' | 'inline';
|
|
483
|
+
xobjectName: string | null;
|
|
484
|
+
streamOperatorIndex: number;
|
|
485
|
+
fromXObject: boolean;
|
|
486
|
+
};
|
|
487
|
+
/** A clustered vector-graphics object, captured for editor selection. */
|
|
488
|
+
type RenderGraphicBox = {
|
|
489
|
+
canvasX: number;
|
|
490
|
+
canvasY: number;
|
|
491
|
+
canvasWidth: number;
|
|
492
|
+
canvasHeight: number;
|
|
493
|
+
operatorStart: number;
|
|
494
|
+
operatorEnd: number;
|
|
495
|
+
paintOperatorIndices: number[];
|
|
496
|
+
/** PDF user-space CTM when the cluster's first path began. */
|
|
497
|
+
ctmAtStart: Matrix;
|
|
498
|
+
fromXObject: boolean;
|
|
499
|
+
};
|
|
500
|
+
declare class PageRenderer {
|
|
501
|
+
private readonly canvas;
|
|
502
|
+
private readonly pageWidth;
|
|
503
|
+
private readonly pageHeight;
|
|
504
|
+
private readonly scale;
|
|
505
|
+
private stateStack;
|
|
506
|
+
private state;
|
|
507
|
+
private ctx;
|
|
508
|
+
private readonly disableClip;
|
|
509
|
+
private readonly debug;
|
|
510
|
+
private readonly debugAtomic;
|
|
511
|
+
private readonly maxAtomicLogs;
|
|
512
|
+
private atomicLogCount;
|
|
513
|
+
private commandIndex;
|
|
514
|
+
private xobjectDepth;
|
|
515
|
+
private pageResources;
|
|
516
|
+
private objectLoader;
|
|
517
|
+
private fontCache;
|
|
518
|
+
/** fontName -> registered FontFace family, populated by preloadEmbeddedFonts
|
|
519
|
+
* before each async render pass. Read by loadFontInfo. */
|
|
520
|
+
private embeddedFamilies;
|
|
521
|
+
/** Memoized canvas glyph widths keyed by `${ctx.font}|${char}`, cleared per
|
|
522
|
+
* render pass. Avoids a measureText (GPU readback) per glyph for fonts that
|
|
523
|
+
* lack PDF width metrics. */
|
|
524
|
+
private glyphWidthCache;
|
|
525
|
+
/** Cached resolvers for Separation/DeviceN color spaces (keyed by resource name). */
|
|
526
|
+
private separationColorCache;
|
|
527
|
+
/** Currently-active Separation/DeviceN resolver for fill/stroke, mirrored with stateStack. */
|
|
528
|
+
private fillColorResolver;
|
|
529
|
+
private strokeColorResolver;
|
|
530
|
+
private resolverStack;
|
|
531
|
+
private capturedTextRuns;
|
|
532
|
+
private fontDiagnosticsLogged;
|
|
533
|
+
private rawByteDiagnosticsLogged;
|
|
534
|
+
private currentPathX;
|
|
535
|
+
private currentPathY;
|
|
536
|
+
private pathStartX;
|
|
537
|
+
private pathStartY;
|
|
538
|
+
private readonly initialTransform;
|
|
539
|
+
private readonly forcedColor;
|
|
540
|
+
private readonly patternDepth;
|
|
541
|
+
/** Resolved patterns per (name, paint color); scoped to the active
|
|
542
|
+
* resource dict — swapped around Form XObjects, cleared per render pass. */
|
|
543
|
+
private patternCache;
|
|
544
|
+
/** Decoded mesh-shading triangles per shading dict, one render pass. */
|
|
545
|
+
private meshCache;
|
|
546
|
+
/** The real page canvas context; this.ctx may point at a layer. */
|
|
547
|
+
private baseCtx;
|
|
548
|
+
/** Offscreen compositing layers: transparency groups + ExtGState SMasks. */
|
|
549
|
+
private layerStack;
|
|
550
|
+
private layerPool;
|
|
551
|
+
private static readonly MAX_LAYER_DEPTH;
|
|
552
|
+
/** Object-capture state for the editor (images + vector clusters). */
|
|
553
|
+
private capturedImages;
|
|
554
|
+
private paintRecords;
|
|
555
|
+
private breakerIndices;
|
|
556
|
+
private pendingPath;
|
|
557
|
+
/** >0 while rendering content that is not page content (Type3 glyph
|
|
558
|
+
* charprocs, soft-mask forms) — capture hooks become no-ops. */
|
|
559
|
+
private captureSuppressed;
|
|
560
|
+
constructor(canvas: HTMLCanvasElement, pageWidth: number, pageHeight: number, scale: number, options?: {
|
|
561
|
+
disableClip?: boolean;
|
|
562
|
+
debug?: boolean;
|
|
563
|
+
debugAtomic?: boolean;
|
|
564
|
+
maxAtomicLogs?: number;
|
|
565
|
+
/** Skip the opaque white page fill (pattern cells, mask layers). */
|
|
566
|
+
transparentBackground?: boolean;
|
|
567
|
+
/** Replace the standard viewport transform (content space → device
|
|
568
|
+
* pixels); used to render pattern cells in pattern space. */
|
|
569
|
+
initialTransform?: Matrix;
|
|
570
|
+
/** PaintType 2 (uncolored) tiling patterns: ignore all color
|
|
571
|
+
* operators and paint everything with this color. */
|
|
572
|
+
forcedColor?: Color;
|
|
573
|
+
/** Nested pattern-cell recursion depth (cap guards cycles). */
|
|
574
|
+
patternDepth?: number;
|
|
575
|
+
});
|
|
576
|
+
render(commands: DrawCommand[]): void;
|
|
577
|
+
renderAsync(commands: DrawCommand[]): Promise<void>;
|
|
578
|
+
/**
|
|
579
|
+
* Paint a page's pre-existing annotations (highlights, strikethrough, ink,
|
|
580
|
+
* shapes, stamps, FreeText, …) by rendering each annotation's normal
|
|
581
|
+
* appearance stream (/AP /N) as a Form XObject, mapping the appearance BBox
|
|
582
|
+
* onto the annotation /Rect per PDF 32000-1 §12.5.5. Call after renderAsync().
|
|
583
|
+
*
|
|
584
|
+
* Widget (form-field) annotations are skipped — those are handled by the
|
|
585
|
+
* AcroForm path; Popup/Link and Hidden/NoView annotations are skipped too.
|
|
586
|
+
*/
|
|
587
|
+
renderAnnotations(pageDict: PDFDict | undefined): Promise<void>;
|
|
588
|
+
private renderOneAnnotation;
|
|
589
|
+
/** Resolve an annotation's normal appearance (/AP /N), following the /AS
|
|
590
|
+
* appearance-state sub-dictionary when present (e.g. checkbox On/Off). */
|
|
591
|
+
private resolveAppearanceStream;
|
|
592
|
+
private runCommandsAsync;
|
|
593
|
+
getCapturedTextRuns(): RenderTextRun[];
|
|
594
|
+
getCapturedImages(): RenderImageBox[];
|
|
595
|
+
getCapturedGraphics(): RenderGraphicBox[];
|
|
596
|
+
/** Track path geometry in device pixels for graphics capture. */
|
|
597
|
+
private extendPendingPath;
|
|
598
|
+
private recordPaintForCapture;
|
|
599
|
+
/** Text and images split vector clusters in the paint stream. */
|
|
600
|
+
private recordBreaker;
|
|
601
|
+
private captureImageBox;
|
|
602
|
+
setFontContext(pageResources: PDFDict | undefined, objectLoader?: (ref: PDFRef) => PDFObject): void;
|
|
603
|
+
private execute;
|
|
604
|
+
private moveToNextTextLine;
|
|
605
|
+
private renderText;
|
|
606
|
+
private renderType3Text;
|
|
607
|
+
private commandPayload;
|
|
608
|
+
private decodeText;
|
|
609
|
+
/** Canvas glyph width, memoized per (current font, text) for the render pass. */
|
|
610
|
+
private measureGlyphWidthCached;
|
|
611
|
+
/**
|
|
612
|
+
* Resolve the page's Font dictionary and register each embedded font program
|
|
613
|
+
* (TrueType/OpenType with a Unicode cmap) as a browser FontFace, recording
|
|
614
|
+
* fontName -> family in embeddedFamilies. Best-effort: any failure leaves the
|
|
615
|
+
* font absent from the map, so loadFontInfo simply falls back to substitution.
|
|
616
|
+
*/
|
|
617
|
+
private preloadEmbeddedFonts;
|
|
618
|
+
private getCurrentFontInfo;
|
|
619
|
+
private loadFontInfo;
|
|
620
|
+
private logFontDiagnostic;
|
|
621
|
+
private resolveObject;
|
|
622
|
+
private resolveColorSpaceName;
|
|
623
|
+
/**
|
|
624
|
+
* Build and cache a color resolver for Separation / DeviceN color spaces.
|
|
625
|
+
* Returns null for device color spaces (handled by colorFromComponents).
|
|
626
|
+
*/
|
|
627
|
+
private buildComplexColorResolver;
|
|
628
|
+
private renderXObject;
|
|
629
|
+
private resolveImageColorSpace;
|
|
630
|
+
/** Resolve the parameter dict for a filter, handling the array form of
|
|
631
|
+
* /DecodeParms (pick the entry that is actually a dict). */
|
|
632
|
+
private resolveDecodeParms;
|
|
633
|
+
/**
|
|
634
|
+
* Decode a JBIG2-filtered image stream to packed 1-bpp filter bytes using
|
|
635
|
+
* pdf.js's `Jbig2Image`. Mirrors pdf.js's Jbig2Stream: optional
|
|
636
|
+
* /JBIG2Globals shared segments are prepended, and the result is bit-inverted
|
|
637
|
+
* (JBIG2 uses 1=black, but PDF's DeviceGray 1-bpc / ImageMask sample
|
|
638
|
+
* convention is 0=black). Returns null on failure so the caller can skip
|
|
639
|
+
* drawing rather than paint garbage.
|
|
640
|
+
*/
|
|
641
|
+
private decodeJbig2;
|
|
642
|
+
/**
|
|
643
|
+
* Decode a CCITTFax (Group 3/4 fax) image stream to packed 1-bpp filter bytes
|
|
644
|
+
* via pdf.js's `CCITTFaxDecoder`. Polarity (BlackIs1) and the other
|
|
645
|
+
* parameters come from /DecodeParms; the decoder yields one packed byte per
|
|
646
|
+
* `readNextChar()`. Returns null on failure.
|
|
647
|
+
*/
|
|
648
|
+
private decodeCcitt;
|
|
649
|
+
/** Composite an RGBA ImageData through a scratch canvas onto the page (the
|
|
650
|
+
* shared tail of every sampled-image path). */
|
|
651
|
+
private drawRgbaImageData;
|
|
652
|
+
private renderImageXObject;
|
|
653
|
+
/**
|
|
654
|
+
* Decode a CMYK/YCCK JPEG with pdf.js's JpegImage (the browser's
|
|
655
|
+
* createImageBitmap mis-handles Adobe CMYK markers) and composite it,
|
|
656
|
+
* applying any /SMask. forceRGBA gives a ready-to-paint RGBA buffer.
|
|
657
|
+
*/
|
|
658
|
+
private renderJpegViaPdfjs;
|
|
659
|
+
/**
|
|
660
|
+
* Decode a JPEG 2000 image with pdf.js's JpxImage and composite it. The
|
|
661
|
+
* decoded tile holds interleaved component samples; assemble RGBA (CMYK→RGB
|
|
662
|
+
* for 4-component data). Used because createImageBitmap('image/jp2') only
|
|
663
|
+
* works on Safari.
|
|
664
|
+
*/
|
|
665
|
+
private renderJpxViaPdfjs;
|
|
666
|
+
/**
|
|
667
|
+
* Inline images (BI…ID…EI) reuse the XObject image pipeline: the
|
|
668
|
+
* abbreviated keys/values are expanded to their full equivalents and the
|
|
669
|
+
* payload is wrapped in a synthetic stream object.
|
|
670
|
+
*/
|
|
671
|
+
private renderInlineImage;
|
|
672
|
+
/** Resolve a named entry from a page Resources sub-dictionary. */
|
|
673
|
+
private lookupResource;
|
|
674
|
+
/**
|
|
675
|
+
* Extract a soft mask's grayscale alpha at the base image's resolution. The
|
|
676
|
+
* mask's own /Filter must be honoured: a DCT/JPX-compressed SMask has to be
|
|
677
|
+
* image-decoded — `decodeStreamSync` returns the raw JPEG bytes for those
|
|
678
|
+
* filters, and dumping those into the alpha channel blacks the image out.
|
|
679
|
+
* Flate/raw masks keep the sync path.
|
|
680
|
+
*/
|
|
681
|
+
private decodeSMaskAlpha;
|
|
682
|
+
private renderImageWithSMask;
|
|
683
|
+
private renderStencilMask;
|
|
684
|
+
private drawImageBitmapLike;
|
|
685
|
+
private decodeTextWithFont;
|
|
686
|
+
private logDebug;
|
|
687
|
+
private captureAtomicState;
|
|
688
|
+
private samplePixels;
|
|
689
|
+
private logAtomic;
|
|
690
|
+
private applyExtGState;
|
|
691
|
+
/** Content space → device pixels, before the CTM: the y-flipped, scaled
|
|
692
|
+
* page viewport — or the caller-supplied transform for pattern cells. */
|
|
693
|
+
private deviceBase;
|
|
694
|
+
private applyCurrentTransform;
|
|
695
|
+
/**
|
|
696
|
+
* Sync-render fallback (legacy): shading patterns approximate to a
|
|
697
|
+
* gradient with the pattern matrix baked into the coordinates. The async
|
|
698
|
+
* path resolves patterns properly via resolvePattern/paintFill instead.
|
|
699
|
+
*/
|
|
700
|
+
private resolvePatternFillStyle;
|
|
701
|
+
/** Resolve a pattern resource (async path: tiling cells fully rendered). */
|
|
702
|
+
private resolvePattern;
|
|
703
|
+
/** scn/SCN with a pattern name on the async path. */
|
|
704
|
+
private applyPatternColor;
|
|
705
|
+
/** Render one tiling-pattern cell with a child renderer in pattern space. */
|
|
706
|
+
private renderTilingCell;
|
|
707
|
+
/** Fill the current path: plain color, or pattern painted in pattern space. */
|
|
708
|
+
private paintFill;
|
|
709
|
+
/**
|
|
710
|
+
* Stroke the current path. Pattern strokes approximate by tiling in the
|
|
711
|
+
* current user space (canvas cannot clip to a stroke region).
|
|
712
|
+
*/
|
|
713
|
+
private paintStroke;
|
|
714
|
+
/**
|
|
715
|
+
* Clip to the current path, switch the context into pattern space
|
|
716
|
+
* (deviceBase · patternMatrix), and flood the visible region. Canvas path
|
|
717
|
+
* coordinates were captured at construction time, so re-transforming here
|
|
718
|
+
* only affects the paint — the standard pattern-space painting technique.
|
|
719
|
+
*/
|
|
720
|
+
private paintPatternThroughPath;
|
|
721
|
+
/** Fill the active clip with the shading's /Background color, if any. */
|
|
722
|
+
private paintShadingBackground;
|
|
723
|
+
/**
|
|
724
|
+
* Paint a shading of any supported type, mapping shading space → device
|
|
725
|
+
* via `userToDevice`. Assumes the caller has set clip + alpha; manages its
|
|
726
|
+
* own transform. Types 2/3 → canvas gradient; 1 → sampled f(x,y) tile;
|
|
727
|
+
* 4–7 → Gouraud-rasterized triangle mesh.
|
|
728
|
+
*/
|
|
729
|
+
private paintShadingWithBase;
|
|
730
|
+
/** ShadingType 1: sample f(x, y) over Domain into a tile, draw under the matrix. */
|
|
731
|
+
private paintFunctionShading;
|
|
732
|
+
/** ShadingTypes 4–7: decode the vertex stream and blit a Gouraud raster. */
|
|
733
|
+
private paintMeshShading;
|
|
734
|
+
/** fillRect covering the whole canvas, expressed in the current user space. */
|
|
735
|
+
private floodCanvasExtent;
|
|
736
|
+
/**
|
|
737
|
+
* Gradient for axial/radial shadings, honoring Domain and Extend, sampled
|
|
738
|
+
* at 64 stops. Coordinates are in shading space unless `coordMatrix` bakes
|
|
739
|
+
* a transform in (sync legacy path).
|
|
740
|
+
*/
|
|
741
|
+
private createCanvasGradient;
|
|
742
|
+
private acquireLayerCanvas;
|
|
743
|
+
private releaseLayerCanvas;
|
|
744
|
+
/**
|
|
745
|
+
* Redirect drawing into a fresh full-size offscreen layer. The current
|
|
746
|
+
* transform and line/paint styles carry over; clip does NOT (it applies
|
|
747
|
+
* when the layer composites back onto the saved context). Returns false
|
|
748
|
+
* (and draws inline as before) past the depth cap.
|
|
749
|
+
*/
|
|
750
|
+
private pushLayer;
|
|
751
|
+
/** Apply the layer's mask (if any) and composite it onto the saved context. */
|
|
752
|
+
private popLayer;
|
|
753
|
+
/**
|
|
754
|
+
* ExtGState /SMask handling (async path). A dict pushes a masked layer
|
|
755
|
+
* scoped to the enclosing q/Q; /None pops mask layers opened at this
|
|
756
|
+
* state depth.
|
|
757
|
+
*/
|
|
758
|
+
private applySoftMaskFromExtGState;
|
|
759
|
+
/** Render an SMask's /G form into an alpha-mask canvas at the current CTM. */
|
|
760
|
+
private renderSMaskCanvas;
|
|
761
|
+
/**
|
|
762
|
+
* Render a Form XObject stream inline against the current context — the
|
|
763
|
+
* shared body used by soft-mask generation. Resource/state handling
|
|
764
|
+
* mirrors the renderXObject form branch.
|
|
765
|
+
*/
|
|
766
|
+
private renderFormStreamInline;
|
|
767
|
+
private renderShadeOperator;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* Styled-text model shared by the rich inline editor (UI) and the PDF writer
|
|
772
|
+
* (`PDFEditSession`). A styled run is an ordered list of {@link StyledSpan}s;
|
|
773
|
+
* each span carries its own weight/style/decoration/size/color so a single line
|
|
774
|
+
* can mix, e.g., a bold word inside plain text.
|
|
775
|
+
*
|
|
776
|
+
* This module is pure and dependency-free (only the `PDFColor` type) so it can be
|
|
777
|
+
* unit-tested and imported from both the browser editor and the server edit
|
|
778
|
+
* pipeline. Font *family* resolution stays in `PDFEditSession.mapPdfFontName` —
|
|
779
|
+
* here we only canonicalize a name string that mapper already understands, so
|
|
780
|
+
* the two never drift.
|
|
781
|
+
*/
|
|
782
|
+
|
|
783
|
+
type TextStyle = {
|
|
784
|
+
bold?: boolean;
|
|
785
|
+
italic?: boolean;
|
|
786
|
+
underline?: boolean;
|
|
787
|
+
strikethrough?: boolean;
|
|
788
|
+
/** Font size in PDF points. Falls back to the run/op size when omitted. */
|
|
789
|
+
sizePt?: number;
|
|
790
|
+
color?: PDFColor;
|
|
791
|
+
};
|
|
792
|
+
/** A contiguous slice of text drawn in one uniform style. */
|
|
793
|
+
type StyledSpan = {
|
|
794
|
+
text: string;
|
|
795
|
+
style: TextStyle;
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
/**
|
|
799
|
+
* Unified annotation model for the editor's annotation layer.
|
|
800
|
+
*
|
|
801
|
+
* Everything is stored in **PDF points, bottom-left origin** (not container
|
|
802
|
+
* pixels) so annotations survive zoom changes and map directly onto PDF
|
|
803
|
+
* `/Annot` rects on export. The UI renders them by projecting through
|
|
804
|
+
* `pdfToContainer`; the engine writes them as real annotation objects via
|
|
805
|
+
* `annotationWriter`.
|
|
806
|
+
*/
|
|
807
|
+
type PdfPoint = {
|
|
808
|
+
x: number;
|
|
809
|
+
y: number;
|
|
810
|
+
};
|
|
811
|
+
type Rect = {
|
|
812
|
+
x: number;
|
|
813
|
+
y: number;
|
|
814
|
+
width: number;
|
|
815
|
+
height: number;
|
|
816
|
+
};
|
|
817
|
+
/**
|
|
818
|
+
* One line's worth of selected text, PDF space. Kept axis-aligned (PDF
|
|
819
|
+
* `/QuadPoints` allow rotation, but the editor only produces upright text
|
|
820
|
+
* selections). `baselineY` lets underline/strikeout sit on the right line.
|
|
821
|
+
*/
|
|
822
|
+
type Quad = {
|
|
823
|
+
x: number;
|
|
824
|
+
y: number;
|
|
825
|
+
width: number;
|
|
826
|
+
height: number;
|
|
827
|
+
baselineY: number;
|
|
828
|
+
};
|
|
829
|
+
type AnnotStyle = {
|
|
830
|
+
/** Stroke / markup color. */
|
|
831
|
+
color: PDFColor;
|
|
832
|
+
/** Constant opacity 0..1 → PDF `/CA`. */
|
|
833
|
+
opacity: number;
|
|
834
|
+
/** Stroke width in points (shapes / ink). */
|
|
835
|
+
strokeWidth?: number;
|
|
836
|
+
/** Interior fill for closed shapes; `null`/absent = no fill. */
|
|
837
|
+
fill?: PDFColor | null;
|
|
838
|
+
};
|
|
839
|
+
type TextMarkupKind = 'highlight' | 'underline' | 'strikeout' | 'squiggly';
|
|
840
|
+
type ShapeKind = 'rect' | 'ellipse' | 'line' | 'arrow' | 'polygon' | 'polyline';
|
|
841
|
+
type AnnotBase = {
|
|
842
|
+
id: string;
|
|
843
|
+
pageIndex: number;
|
|
844
|
+
style: AnnotStyle;
|
|
845
|
+
author?: string;
|
|
846
|
+
/** ms epoch; surfaces as `/CreationDate` + `/M`. */
|
|
847
|
+
createdAt: number;
|
|
848
|
+
};
|
|
849
|
+
/** Highlight / underline / strikeout / squiggly over selected text. */
|
|
850
|
+
type TextMarkupAnnotation = AnnotBase & {
|
|
851
|
+
kind: TextMarkupKind;
|
|
852
|
+
/** Per-line rects covering the selected glyphs. */
|
|
853
|
+
quads: Quad[];
|
|
854
|
+
/** The covered text (annotation `/Contents`). */
|
|
855
|
+
text: string;
|
|
856
|
+
};
|
|
857
|
+
/**
|
|
858
|
+
* Vector shape. `points` semantics by kind:
|
|
859
|
+
* - `rect` / `ellipse`: two opposite corners.
|
|
860
|
+
* - `line` / `arrow`: two endpoints.
|
|
861
|
+
* - `polygon` / `polyline`: ordered vertices (>= 2).
|
|
862
|
+
*/
|
|
863
|
+
type ShapeAnnotation = AnnotBase & {
|
|
864
|
+
kind: ShapeKind;
|
|
865
|
+
points: PdfPoint[];
|
|
866
|
+
};
|
|
867
|
+
/** Freehand brush / pencil: one or more captured strokes. */
|
|
868
|
+
type InkAnnotation = AnnotBase & {
|
|
869
|
+
kind: 'ink';
|
|
870
|
+
strokes: PdfPoint[][];
|
|
871
|
+
};
|
|
872
|
+
/** Sticky note (`/Text`) or text box / callout (`/FreeText`). */
|
|
873
|
+
type NoteAnnotation = AnnotBase & {
|
|
874
|
+
kind: 'note' | 'freetext';
|
|
875
|
+
rect: Rect;
|
|
876
|
+
text: string;
|
|
877
|
+
};
|
|
878
|
+
type Annotation = TextMarkupAnnotation | ShapeAnnotation | InkAnnotation | NoteAnnotation;
|
|
879
|
+
|
|
880
|
+
type PDFColor = {
|
|
881
|
+
r: number;
|
|
882
|
+
g: number;
|
|
883
|
+
b: number;
|
|
884
|
+
};
|
|
885
|
+
type PDFFontSpec = {
|
|
886
|
+
cssFamily?: string;
|
|
887
|
+
pdfName?: string;
|
|
888
|
+
sizePt: number;
|
|
889
|
+
weight?: string;
|
|
890
|
+
color?: PDFColor;
|
|
891
|
+
lineHeight?: number;
|
|
892
|
+
charSpacing?: number;
|
|
893
|
+
};
|
|
894
|
+
type PdfMatrix = [number, number, number, number, number, number];
|
|
895
|
+
|
|
896
|
+
type InsertTextOperation = {
|
|
897
|
+
type: 'insert-text';
|
|
898
|
+
pageIndex: number;
|
|
899
|
+
text: string;
|
|
900
|
+
x: number;
|
|
901
|
+
y: number;
|
|
902
|
+
size?: number;
|
|
903
|
+
color?: PDFColor;
|
|
904
|
+
/** When present, the text is drawn as styled spans (bold/italic/underline/
|
|
905
|
+
* strikethrough/size/color per span) instead of the flat `text`+`size`+
|
|
906
|
+
* `color`. `text` should still hold the plain concatenation for logging. */
|
|
907
|
+
spans?: StyledSpan[];
|
|
908
|
+
/** Base font name whose family the spans inherit (e.g. the original run's
|
|
909
|
+
* font). Bold/italic come from each span's style. */
|
|
910
|
+
fontName?: string;
|
|
911
|
+
};
|
|
912
|
+
type RedactAreaOperation = {
|
|
913
|
+
type: 'redact-area';
|
|
914
|
+
pageIndex: number;
|
|
915
|
+
x: number;
|
|
916
|
+
y: number;
|
|
917
|
+
width: number;
|
|
918
|
+
height: number;
|
|
919
|
+
color?: PDFColor;
|
|
920
|
+
padding?: number;
|
|
921
|
+
/** When set, the show-text operators under the rect are removed from the
|
|
922
|
+
* content stream (true redaction) so the covered text can't be extracted. */
|
|
923
|
+
removeOperators?: {
|
|
924
|
+
streamOperatorIndices: number[];
|
|
925
|
+
};
|
|
926
|
+
};
|
|
927
|
+
/**
|
|
928
|
+
* Bounds (in PDF space) that the erase/whiteout rectangle of a replace-text
|
|
929
|
+
* operation must not cross, derived from neighboring text runs so the cover
|
|
930
|
+
* box never swallows adjacent lines or words.
|
|
931
|
+
*/
|
|
932
|
+
type EraseClamp = {
|
|
933
|
+
/** Erase rect bottom edge must stay >= this. */
|
|
934
|
+
minY?: number;
|
|
935
|
+
/** Erase rect top edge must stay <= this. */
|
|
936
|
+
maxY?: number;
|
|
937
|
+
/** Erase rect right edge must stay <= this. */
|
|
938
|
+
maxRight?: number;
|
|
939
|
+
};
|
|
940
|
+
type ReplaceTextOperation = {
|
|
941
|
+
type: 'replace-text';
|
|
942
|
+
pageIndex: number;
|
|
943
|
+
x: number;
|
|
944
|
+
y: number;
|
|
945
|
+
width: number;
|
|
946
|
+
height: number;
|
|
947
|
+
baselineY?: number;
|
|
948
|
+
newText: string;
|
|
949
|
+
size?: number;
|
|
950
|
+
font?: PDFFontSpec;
|
|
951
|
+
color?: PDFColor;
|
|
952
|
+
backgroundColor?: PDFColor;
|
|
953
|
+
erasePadding?: number;
|
|
954
|
+
eraseClamp?: EraseClamp;
|
|
955
|
+
/** When set, the original show-text operators are removed from the content
|
|
956
|
+
* stream (Adobe-style) instead of being covered with a rectangle; the
|
|
957
|
+
* cover box remains the fallback if the rewrite fails. */
|
|
958
|
+
removeOperators?: {
|
|
959
|
+
streamOperatorIndices: number[];
|
|
960
|
+
};
|
|
961
|
+
/** When present, the replacement is drawn as styled spans (per-span bold/
|
|
962
|
+
* italic/underline/strikethrough/size/color) using `font.pdfName` as the
|
|
963
|
+
* base family. Supersedes `newText` for drawing; keep `newText` as the plain
|
|
964
|
+
* concatenation for width/erase sizing and history. */
|
|
965
|
+
spans?: StyledSpan[];
|
|
966
|
+
};
|
|
967
|
+
/**
|
|
968
|
+
* Remove visual objects (images / vector clusters) by blanking their operator
|
|
969
|
+
* spans in the content stream. Fails safe: when a span cannot be removed
|
|
970
|
+
* without side effects, the whole operation no-ops.
|
|
971
|
+
*/
|
|
972
|
+
type DeleteObjectOperation = {
|
|
973
|
+
type: 'delete-object';
|
|
974
|
+
pageIndex: number;
|
|
975
|
+
spans: Array<{
|
|
976
|
+
start: number;
|
|
977
|
+
end: number;
|
|
978
|
+
}>;
|
|
979
|
+
};
|
|
980
|
+
/**
|
|
981
|
+
* Move/resize a visual object by wrapping its operator span in `q T' cm … Q`,
|
|
982
|
+
* where T' = baseCtm⁻¹ · matrix · baseCtm maps the page-space transform into
|
|
983
|
+
* the span's local user space.
|
|
984
|
+
*/
|
|
985
|
+
type TransformObjectOperation = {
|
|
986
|
+
type: 'transform-object';
|
|
987
|
+
pageIndex: number;
|
|
988
|
+
span: {
|
|
989
|
+
start: number;
|
|
990
|
+
end: number;
|
|
991
|
+
};
|
|
992
|
+
/** Desired page-space transform T (PDF points, bottom-left origin). */
|
|
993
|
+
matrix: PdfMatrix;
|
|
994
|
+
/** PDF user-space CTM at the span start, captured at render time. */
|
|
995
|
+
baseCtm: PdfMatrix;
|
|
996
|
+
};
|
|
997
|
+
/** Place a new raster image (uploaded by the user) onto a page. */
|
|
998
|
+
type InsertImageOperation = {
|
|
999
|
+
type: 'insert-image';
|
|
1000
|
+
pageIndex: number;
|
|
1001
|
+
/** PDF points, bottom-left corner of the placement. */
|
|
1002
|
+
x: number;
|
|
1003
|
+
y: number;
|
|
1004
|
+
width: number;
|
|
1005
|
+
height: number;
|
|
1006
|
+
bytes: Uint8Array;
|
|
1007
|
+
format: 'png' | 'jpeg';
|
|
1008
|
+
};
|
|
1009
|
+
type RotatePageOperation = {
|
|
1010
|
+
type: 'rotate-page';
|
|
1011
|
+
pageIndex: number;
|
|
1012
|
+
degrees: 90 | 180 | 270;
|
|
1013
|
+
};
|
|
1014
|
+
type DeletePageOperation = {
|
|
1015
|
+
type: 'delete-page';
|
|
1016
|
+
pageIndex: number;
|
|
1017
|
+
};
|
|
1018
|
+
type FormFieldType$1 = 'text' | 'checkbox' | 'radio' | 'dropdown' | 'signature';
|
|
1019
|
+
/**
|
|
1020
|
+
* Field-authoring properties shared by create / update operations. Geometry,
|
|
1021
|
+
* options and flags fully describe a field so an update can be expressed as a
|
|
1022
|
+
* delete + recreate from the same spec.
|
|
1023
|
+
*/
|
|
1024
|
+
type FormFieldSpec = {
|
|
1025
|
+
pageIndex: number;
|
|
1026
|
+
fieldType: FormFieldType$1;
|
|
1027
|
+
/** Fully-qualified AcroForm field name (/T). */
|
|
1028
|
+
name: string;
|
|
1029
|
+
/** PDF points, bottom-left origin. For radios, the bounding rect of option 0. */
|
|
1030
|
+
rect: {
|
|
1031
|
+
x: number;
|
|
1032
|
+
y: number;
|
|
1033
|
+
width: number;
|
|
1034
|
+
height: number;
|
|
1035
|
+
};
|
|
1036
|
+
value?: string;
|
|
1037
|
+
/** Dropdown / radio option export values. Radios use one rect per option. */
|
|
1038
|
+
options?: string[];
|
|
1039
|
+
/** Per-option rects for radio groups (same length/order as `options`). */
|
|
1040
|
+
optionRects?: Array<{
|
|
1041
|
+
x: number;
|
|
1042
|
+
y: number;
|
|
1043
|
+
width: number;
|
|
1044
|
+
height: number;
|
|
1045
|
+
}>;
|
|
1046
|
+
fontSize?: number;
|
|
1047
|
+
required?: boolean;
|
|
1048
|
+
readOnly?: boolean;
|
|
1049
|
+
/** Text fields: allow multiple lines. */
|
|
1050
|
+
multiline?: boolean;
|
|
1051
|
+
/** Text-field text alignment (quadding). */
|
|
1052
|
+
align?: 'left' | 'center' | 'right';
|
|
1053
|
+
/** Text fields: maximum character count (0 / undefined = unlimited). */
|
|
1054
|
+
maxLength?: number;
|
|
1055
|
+
/** Dropdown: combo box that also accepts free-typed text. */
|
|
1056
|
+
editable?: boolean;
|
|
1057
|
+
};
|
|
1058
|
+
/** Author a new interactive form field on a page. */
|
|
1059
|
+
type CreateFormFieldOperation = FormFieldSpec & {
|
|
1060
|
+
type: 'create-form-field';
|
|
1061
|
+
};
|
|
1062
|
+
/**
|
|
1063
|
+
* Replace an existing field (authored or parsed) with a new spec. Implemented as
|
|
1064
|
+
* remove(oldName) + create(spec); covers geometry, options, name and flag edits.
|
|
1065
|
+
*/
|
|
1066
|
+
type UpdateFormFieldOperation = FormFieldSpec & {
|
|
1067
|
+
type: 'update-form-field';
|
|
1068
|
+
/** Name of the field to replace (may differ from the new `name`). */
|
|
1069
|
+
oldName: string;
|
|
1070
|
+
};
|
|
1071
|
+
/** Set the value of one or more existing fields (fill). */
|
|
1072
|
+
type SetFormValuesOperation = {
|
|
1073
|
+
type: 'set-form-values';
|
|
1074
|
+
values: Array<{
|
|
1075
|
+
name: string;
|
|
1076
|
+
fieldType: FormFieldType$1;
|
|
1077
|
+
value: string;
|
|
1078
|
+
}>;
|
|
1079
|
+
/** UI-only hint: page to re-render after committing (PDFEditSession ignores it). */
|
|
1080
|
+
pageIndex?: number;
|
|
1081
|
+
};
|
|
1082
|
+
type DeleteFormFieldOperation = {
|
|
1083
|
+
type: 'delete-form-field';
|
|
1084
|
+
name: string;
|
|
1085
|
+
};
|
|
1086
|
+
/** Bake all field values into page content and remove the interactive form. */
|
|
1087
|
+
type FlattenFormOperation = {
|
|
1088
|
+
type: 'flatten-form';
|
|
1089
|
+
};
|
|
1090
|
+
/**
|
|
1091
|
+
* Write one annotation as a real PDF `/Annot` object (with an appearance
|
|
1092
|
+
* stream + `/CA` opacity) onto its page. Non-destructive: the underlying page
|
|
1093
|
+
* content and selectable text are untouched. See annotationWriter.ts.
|
|
1094
|
+
*/
|
|
1095
|
+
type AddAnnotationOperation = {
|
|
1096
|
+
type: 'add-annotation';
|
|
1097
|
+
annotation: Annotation;
|
|
1098
|
+
};
|
|
1099
|
+
type PDFEditOperation = InsertTextOperation | RedactAreaOperation | ReplaceTextOperation | DeleteObjectOperation | TransformObjectOperation | InsertImageOperation | RotatePageOperation | DeletePageOperation | CreateFormFieldOperation | UpdateFormFieldOperation | SetFormValuesOperation | DeleteFormFieldOperation | FlattenFormOperation | AddAnnotationOperation;
|
|
1100
|
+
|
|
1101
|
+
declare class PDFEditSession {
|
|
1102
|
+
private readonly pdfDoc;
|
|
1103
|
+
private readonly pdfBytes;
|
|
1104
|
+
private readonly standardFontCache;
|
|
1105
|
+
private readonly embeddedFontCache;
|
|
1106
|
+
private embeddedFonts;
|
|
1107
|
+
private fontkitRegistered;
|
|
1108
|
+
/** Bytes matching the document's last loaded/saved state — the same bytes
|
|
1109
|
+
* the UI parsed when it captured stream operator indices. */
|
|
1110
|
+
private currentBytes;
|
|
1111
|
+
/** Per-page combined content bytes after rewrites within this save cycle,
|
|
1112
|
+
* so consecutive rewrites on one page compose instead of resetting. */
|
|
1113
|
+
private readonly rewrittenPages;
|
|
1114
|
+
/** Pages whose command indices were invalidated by a length-changing
|
|
1115
|
+
* rewrite (transform-object) this save cycle — further index-based
|
|
1116
|
+
* rewrites on them must refuse rather than corrupt. */
|
|
1117
|
+
private readonly indexInvalidPages;
|
|
1118
|
+
private constructor();
|
|
1119
|
+
static fromAvniDocument(doc: AvniDocument): Promise<PDFEditSession>;
|
|
1120
|
+
/** Open a session straight from PDF bytes (server-side edit pipeline) —
|
|
1121
|
+
* mirrors fromAvniDocument without needing an AvniDocument wrapper. */
|
|
1122
|
+
static fromBytes(bytes: Uint8Array): Promise<PDFEditSession>;
|
|
1123
|
+
getPageCount(): number;
|
|
1124
|
+
applyOperations(operations: PDFEditOperation[]): Promise<void>;
|
|
1125
|
+
/** Create / fill / delete / flatten interactive form fields via pdf-lib. */
|
|
1126
|
+
private applyFormOperations;
|
|
1127
|
+
private createFormField;
|
|
1128
|
+
private setFieldValue;
|
|
1129
|
+
toBlob(): Promise<Blob>;
|
|
1130
|
+
/**
|
|
1131
|
+
* Adobe-style text removal: blank the original show-text operators inside
|
|
1132
|
+
* the page's content stream(s) so no cover rectangle is needed. Returns
|
|
1133
|
+
* false when the rewrite cannot be applied safely (caller falls back to
|
|
1134
|
+
* the cover-box erase).
|
|
1135
|
+
*/
|
|
1136
|
+
private tryRemoveTextOperators;
|
|
1137
|
+
/**
|
|
1138
|
+
* Blank arbitrary operator spans (delete-object). Same stream-update
|
|
1139
|
+
* mechanics as tryRemoveTextOperators; false on any anomaly.
|
|
1140
|
+
*/
|
|
1141
|
+
private tryNeutralizeSpans;
|
|
1142
|
+
/**
|
|
1143
|
+
* Move/resize: wrap the object's span in `q M cm … Q` where
|
|
1144
|
+
* M = C⁻¹·T·C maps the page-space transform into the span's local space.
|
|
1145
|
+
* Length-changing — marks the page so later index-based rewrites refuse.
|
|
1146
|
+
*/
|
|
1147
|
+
private tryWrapSpanWithTransform;
|
|
1148
|
+
private deletePage;
|
|
1149
|
+
/** Cache per image content so repeated placements reuse one XObject. */
|
|
1150
|
+
private readonly embeddedImageCache;
|
|
1151
|
+
private getEmbeddedImage;
|
|
1152
|
+
private getStandardFont;
|
|
1153
|
+
/**
|
|
1154
|
+
* Draw a list of styled spans left-to-right starting at (`startX`,
|
|
1155
|
+
* `baselineY`). Each span resolves its own standard font (family inherited
|
|
1156
|
+
* from `baseFontName`, weight/style from the span) via the same
|
|
1157
|
+
* fit/transliterate path as a flat edit, then advances x by the drawn width.
|
|
1158
|
+
* Underline / strikethrough are thin rectangles spanning the drawn width.
|
|
1159
|
+
*/
|
|
1160
|
+
private drawStyledSpans;
|
|
1161
|
+
private getRenderableFont;
|
|
1162
|
+
private supportedCodePoints;
|
|
1163
|
+
private unsupportedCount;
|
|
1164
|
+
/**
|
|
1165
|
+
* Best-effort projection of text onto what `font` can encode: known
|
|
1166
|
+
* lookalikes are transliterated, anything still unencodable is dropped
|
|
1167
|
+
* with a warning — an edit must degrade, never fail the pipeline
|
|
1168
|
+
* (e.g. WinAnsi cannot encode "⋄" from extracted symbol text).
|
|
1169
|
+
*/
|
|
1170
|
+
private fitTextToFont;
|
|
1171
|
+
private fontSupportsText;
|
|
1172
|
+
private getEmbeddedFont;
|
|
1173
|
+
private getEmbeddedFontPrograms;
|
|
1174
|
+
private ensureFontkitRegistered;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
/**
|
|
1178
|
+
* Extract `/Link` annotations from a page so the View tool can overlay clickable
|
|
1179
|
+
* regions. External links carry a `uri`; internal go-to links resolve to a
|
|
1180
|
+
* `destPageIndex`. Geometry is PDF points, bottom-left origin (the same space the
|
|
1181
|
+
* overlay positions with `pdfToContainer`).
|
|
1182
|
+
*/
|
|
1183
|
+
type LinkAnnotation = {
|
|
1184
|
+
rect: {
|
|
1185
|
+
x: number;
|
|
1186
|
+
y: number;
|
|
1187
|
+
width: number;
|
|
1188
|
+
height: number;
|
|
1189
|
+
};
|
|
1190
|
+
/** External link target (from /A /URI). */
|
|
1191
|
+
uri?: string;
|
|
1192
|
+
/** Resolved 0-based page index for internal go-to links (/Dest or /A /GoTo). */
|
|
1193
|
+
destPageIndex?: number;
|
|
1194
|
+
};
|
|
1195
|
+
/**
|
|
1196
|
+
* Read a page's `/Annots` for `/Subtype /Link` entries. `pageRefIndex` maps a
|
|
1197
|
+
* page object ref key (`objNum:gen`) to its index so internal links resolve.
|
|
1198
|
+
*/
|
|
1199
|
+
declare function parseLinkAnnotations(pageDict: PDFDict | undefined, pageRefIndex: Map<string, number>, loadObject: (ref: PDFRef) => PDFObject | undefined): LinkAnnotation[];
|
|
1200
|
+
|
|
1201
|
+
/**
|
|
1202
|
+
* Unified, viewer-agnostic representation of an interactive form field. Both
|
|
1203
|
+
* fields parsed from an existing AcroForm and fields authored in the editor are
|
|
1204
|
+
* expressed with this model; the overlay UI renders it and pdf-lib persists it.
|
|
1205
|
+
*/
|
|
1206
|
+
type FormFieldType = 'text' | 'checkbox' | 'radio' | 'dropdown' | 'signature';
|
|
1207
|
+
/** PDF-space rectangle (points, bottom-left origin). */
|
|
1208
|
+
type FormFieldRect = {
|
|
1209
|
+
x: number;
|
|
1210
|
+
y: number;
|
|
1211
|
+
width: number;
|
|
1212
|
+
height: number;
|
|
1213
|
+
};
|
|
1214
|
+
type FormField = {
|
|
1215
|
+
/** Stable key — existing fields use the fully-qualified name; new fields a generated id. */
|
|
1216
|
+
id: string;
|
|
1217
|
+
/** AcroForm field name (/T, qualified through the parent chain with '.'). */
|
|
1218
|
+
name: string;
|
|
1219
|
+
type: FormFieldType;
|
|
1220
|
+
pageIndex: number;
|
|
1221
|
+
rect: FormFieldRect;
|
|
1222
|
+
/** Text value; 'On'/export value for check & radio; selected option for dropdown. */
|
|
1223
|
+
value?: string;
|
|
1224
|
+
/** Selectable export values for dropdown / radio. */
|
|
1225
|
+
options?: string[];
|
|
1226
|
+
/** The "on" export name for a checkbox/radio widget (from /AP /N, the non-Off key). */
|
|
1227
|
+
onState?: string;
|
|
1228
|
+
/** Font size from /DA; 0 (or undefined) means auto-size. */
|
|
1229
|
+
fontSize?: number;
|
|
1230
|
+
multiline?: boolean;
|
|
1231
|
+
readOnly?: boolean;
|
|
1232
|
+
required?: boolean;
|
|
1233
|
+
/** Text-field text alignment (quadding). */
|
|
1234
|
+
align?: 'left' | 'center' | 'right';
|
|
1235
|
+
/** Text fields: maximum character count (0 / undefined = unlimited). */
|
|
1236
|
+
maxLength?: number;
|
|
1237
|
+
/** Dropdown: combo box that also accepts free-typed text. */
|
|
1238
|
+
editable?: boolean;
|
|
1239
|
+
/** Per-option rects for radio groups (same order as `options`). */
|
|
1240
|
+
optionRects?: FormFieldRect[];
|
|
1241
|
+
/** True when authored in this editing session (not parsed from the source PDF). */
|
|
1242
|
+
isNew?: boolean;
|
|
1243
|
+
};
|
|
1244
|
+
|
|
1245
|
+
/**
|
|
1246
|
+
* Walk the AcroForm field tree (resolving /Kids) and flatten terminal fields and
|
|
1247
|
+
* their widgets into `FormField`s. Radio groups yield one entry per option widget
|
|
1248
|
+
* (sharing `name`); other fields yield one entry per widget.
|
|
1249
|
+
*/
|
|
1250
|
+
declare function parseFormFields(catalog: PDFDict | undefined, pageDictsByIndex: Map<number, PDFDict>, loadObject: (ref: PDFRef) => PDFObject | undefined): FormField[];
|
|
1251
|
+
|
|
1252
|
+
declare class PDFEditProcessor implements IAvniProcessor {
|
|
1253
|
+
name: string;
|
|
1254
|
+
supportedTypes: "pdf"[];
|
|
1255
|
+
process(doc: AvniDocument, params: Record<string, unknown>): Promise<ProcessorResult>;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
declare class PDFCompressor implements IAvniProcessor {
|
|
1259
|
+
name: string;
|
|
1260
|
+
supportedTypes: "pdf"[];
|
|
1261
|
+
process(doc: AvniDocument, params: Record<string, unknown>): Promise<ProcessorResult>;
|
|
1262
|
+
private compressImageInBrowser;
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
/** Shared license/activation contracts used by both the engine gate and the server. */
|
|
1266
|
+
/** Capabilities a license can unlock. `view`/`annotate` are the always-available base. */
|
|
1267
|
+
type LicenseFeature = 'view' | 'annotate' | 'edit-text' | 'sign' | 'redact' | 'forms' | 'export-clean';
|
|
1268
|
+
type LicenseEdition = 'trial' | 'pro' | 'enterprise';
|
|
1269
|
+
type LicenseStatus = 'UNLICENSED' | 'INVALID' | 'LICENSED' | 'GRACE' | 'DEGRADED';
|
|
1270
|
+
interface LicenseState {
|
|
1271
|
+
status: LicenseStatus;
|
|
1272
|
+
edition: LicenseEdition | null;
|
|
1273
|
+
features: LicenseFeature[];
|
|
1274
|
+
/** True when render/export should be watermarked. */
|
|
1275
|
+
watermark: boolean;
|
|
1276
|
+
/** Human-readable reason (for diagnostics; never user-facing copy). */
|
|
1277
|
+
reason: string;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
/**
|
|
1281
|
+
* Soft-fail watermark used when the license gate is not satisfied (unlicensed,
|
|
1282
|
+
* invalid, degraded, or trial). Two surfaces:
|
|
1283
|
+
* - `drawWatermarkOnCanvas` — diagonal repeated text over the on-screen preview.
|
|
1284
|
+
* - `stampWatermarkOnPdf` — bakes the same notice onto every page of an export,
|
|
1285
|
+
* so a downloaded file is visibly unlicensed (the commercial deterrent).
|
|
1286
|
+
*
|
|
1287
|
+
* pdf-lib is already a project dependency; it's imported lazily so the watermark
|
|
1288
|
+
* code only loads when actually needed.
|
|
1289
|
+
*/
|
|
1290
|
+
declare const WATERMARK_TEXT = "NamahaPDF \u00B7 Unlicensed SDK";
|
|
1291
|
+
/** Tile a faint diagonal watermark across a 2D canvas context. */
|
|
1292
|
+
declare function drawWatermarkOnCanvas(ctx: CanvasRenderingContext2D, width: number, height: number, text?: string): void;
|
|
1293
|
+
/**
|
|
1294
|
+
* Stamp the watermark onto every page of a PDF and return the new bytes.
|
|
1295
|
+
* Best-effort: if pdf-lib fails to load/parse, the original bytes are returned
|
|
1296
|
+
* (we never want the watermark step to block a download outright).
|
|
1297
|
+
*/
|
|
1298
|
+
declare function stampWatermarkOnPdf(bytes: Uint8Array, text?: string): Promise<Uint8Array>;
|
|
1299
|
+
|
|
1300
|
+
/**
|
|
1301
|
+
* Client-side license gate for the NamahaPDF engine.
|
|
1302
|
+
*
|
|
1303
|
+
* Flow (see configureLicense):
|
|
1304
|
+
* 1. Verify the license key OFFLINE (signature, expiry, domain) — instant, no network.
|
|
1305
|
+
* 2. If a cached activation token is still valid → LICENSED immediately.
|
|
1306
|
+
* 3. Kick a NON-BLOCKING background activation that exchanges the key for a fresh,
|
|
1307
|
+
* short-TTL activation token (which is also what the server can revoke).
|
|
1308
|
+
*
|
|
1309
|
+
* Offline grace is weighted heavily on purpose: transient network/server failures
|
|
1310
|
+
* never punish a paying customer. We tolerate up to MAX_FAILURES (5) attempts AND a
|
|
1311
|
+
* BOOTSTRAP_GRACE window before degrading — the SDK degrades immediately ONLY on an
|
|
1312
|
+
* explicit signed `revoked`/`invalid` from the server.
|
|
1313
|
+
*
|
|
1314
|
+
* Everything is injectable (storage / fetch / clock / public key) so it is unit-testable
|
|
1315
|
+
* without a browser or a server.
|
|
1316
|
+
*/
|
|
1317
|
+
|
|
1318
|
+
interface StorageLike {
|
|
1319
|
+
getItem(key: string): string | null;
|
|
1320
|
+
setItem(key: string, value: string): void;
|
|
1321
|
+
}
|
|
1322
|
+
interface LicenseConfig {
|
|
1323
|
+
/** The signed license key the developer received. */
|
|
1324
|
+
licenseKey?: string;
|
|
1325
|
+
/** Activation endpoint. Defaults to a same-origin `/api/license/activate`. */
|
|
1326
|
+
activationUrl?: string;
|
|
1327
|
+
/** Stable per-install id; auto-generated + persisted when omitted. */
|
|
1328
|
+
deviceId?: string;
|
|
1329
|
+
/** First-party bypass — our own app passes this so it never watermarks itself. */
|
|
1330
|
+
owner?: boolean;
|
|
1331
|
+
/** Override the embedded public key (tests only). */
|
|
1332
|
+
publicKeyHex?: string;
|
|
1333
|
+
/** Injected clock (tests). */
|
|
1334
|
+
now?: () => number;
|
|
1335
|
+
/** Injected storage (tests / Node). */
|
|
1336
|
+
storage?: StorageLike;
|
|
1337
|
+
/** Injected fetch (tests / Node). */
|
|
1338
|
+
fetchImpl?: typeof fetch;
|
|
1339
|
+
/** Host to domain-check against; defaults to location.hostname in the browser. */
|
|
1340
|
+
host?: string;
|
|
1341
|
+
/** Background retry backoff in ms (tests can shorten). */
|
|
1342
|
+
retryDelays?: number[];
|
|
1343
|
+
}
|
|
1344
|
+
type Listener = (s: LicenseState) => void;
|
|
1345
|
+
/**
|
|
1346
|
+
* Configure (or reconfigure) the license gate. Verifies the key synchronously and
|
|
1347
|
+
* starts a background activation. Returns the immediate state; subscribe with
|
|
1348
|
+
* `onLicenseChange` for updates as activation completes.
|
|
1349
|
+
*/
|
|
1350
|
+
declare function configureLicense(config?: LicenseConfig): LicenseState;
|
|
1351
|
+
/** Current license state (synchronous, cheap). */
|
|
1352
|
+
declare function getLicenseState(): LicenseState;
|
|
1353
|
+
declare function isFeatureEnabled(feature: LicenseFeature): boolean;
|
|
1354
|
+
/** Throws if a feature is not licensed — use to guard gated engine operations. */
|
|
1355
|
+
declare function assertFeature(feature: LicenseFeature): void;
|
|
1356
|
+
declare function shouldWatermark(): boolean;
|
|
1357
|
+
/** Subscribe to state changes (activation success/failure). Returns an unsubscribe. */
|
|
1358
|
+
declare function onLicenseChange(cb: Listener): () => void;
|
|
1359
|
+
|
|
1360
|
+
export { type AvniDocument, AvniOrchestrator, type DocumentType, type DrawCommand, type IAvniProcessor, type LicenseConfig, type LicenseEdition, type LicenseFeature, type LicenseState, type LicenseStatus, type LinkAnnotation, type Matrix, PDFCompressor, type PDFEditOperation, PDFEditProcessor, PDFEditSession, PDFParser, PageRenderer, type PipelineConfig, type ProcessingStep, type ProcessorResult, type RenderGlyphBox, type RenderGraphicBox, type RenderImageBox, type RenderTextRun, WATERMARK_TEXT, assertFeature, avniEngine, configureLicense, drawWatermarkOnCanvas, getLicenseState, isFeatureEnabled, loadPageContentStream, onLicenseChange, parseContentStream, parseFormFields, parseLinkAnnotations, shouldWatermark, stampWatermarkOnPdf };
|