@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.
@@ -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 };