@imgly/pdf-importer 0.1.0-rc.2 → 0.2.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,854 @@
1
+ // Generated by dts-bundle-generator v9.5.1
2
+
3
+ import CreativeEngine from '@cesdk/engine';
4
+ import { AssetAPI, Font, FontStyle, FontWeight, Typeface } from '@cesdk/engine';
5
+
6
+ type WarningSeverity = "error" | "warning" | "info";
7
+ interface WarningDefinition {
8
+ /** Default + sole severity. Call sites do not override. */
9
+ severity: WarningSeverity;
10
+ /**
11
+ * Template with `{name}` placeholders. Renderer throws if a placeholder
12
+ * has no matching key in `params` — guards against silent typos.
13
+ */
14
+ template: string;
15
+ }
16
+ interface LogMessage<TCode extends string = string> {
17
+ /** Stable machine-readable identifier — never rename once shipped. */
18
+ code: TCode;
19
+ /** Inherited from the code definition. */
20
+ type: WarningSeverity;
21
+ /** Human-readable rendered string. */
22
+ message: string;
23
+ /** Raw structured fields — superset of all `{name}` placeholders. */
24
+ params: Record<string, unknown>;
25
+ }
26
+ /**
27
+ * Generic structured logger. Severity is registry-driven (PostgreSQL
28
+ * SQLSTATE pattern) — call sites cannot override. To change severity,
29
+ * edit the registry.
30
+ */
31
+ export declare class Logger<R extends Record<string, WarningDefinition>> {
32
+ private readonly registry;
33
+ private messages;
34
+ constructor(registry: R);
35
+ emit<K extends keyof R & string>(code: K, params?: Record<string, unknown>): void;
36
+ getMessages(): LogMessage<keyof R & string>[];
37
+ }
38
+ type AssetQueryAPI = Pick<AssetAPI, "findAllSources" | "addLocalAssetSourceFromJSONURI" | "findAssets">;
39
+ type AssetEngine = {
40
+ asset: AssetQueryAPI;
41
+ };
42
+ interface TypefaceParams {
43
+ family: string;
44
+ style?: Font["style"];
45
+ weight?: Font["weight"];
46
+ }
47
+ interface FontResolverOptions {
48
+ /**
49
+ * When the requested weight is not available in the matched typeface,
50
+ * pick the closest available weight using the CSS Font Matching algorithm.
51
+ * Defaults to false — return null instead so the caller can decide what
52
+ * to do (e.g. log a warning, use a different font).
53
+ *
54
+ * Only enable when the typeface name is known to be a good match. The
55
+ * typeface query uses fuzzy matching, so applying weight fallback to a
56
+ * poor name match can silently produce wildly wrong results.
57
+ */
58
+ closestWeightMatch?: boolean;
59
+ }
60
+ interface FontResolverResult {
61
+ typeface: Typeface;
62
+ font: Font;
63
+ /**
64
+ * Set to the originally requested family name when the typeface came from
65
+ * the proprietary-fallbacks source (e.g. requested "Helvetica", got Roboto).
66
+ * Undefined when the family matched the main Google Fonts catalog directly.
67
+ * Always populated when applicable; consumers may ignore it.
68
+ */
69
+ substitutedFrom?: string;
70
+ }
71
+ type TypefaceResolver = (params: TypefaceParams, engine: AssetEngine, options?: FontResolverOptions) => Promise<FontResolverResult | null>;
72
+ /**
73
+ * Register the @imgly/gfonts asset sources (Google Fonts catalog + proprietary
74
+ * font fallbacks) with the CE.SDK engine.
75
+ *
76
+ * Adds two sources:
77
+ * - `ly.img.gfonts` — 1,394 Google Fonts typefaces (fuzzy-matched).
78
+ * - `ly.img.gfonts-fallbacks` — 16 proprietary-font alias entries
79
+ * (e.g. Helvetica → Roboto), strict-matched.
80
+ */
81
+ export declare function addGfontsAssetLibrary(engine: AssetEngine): Promise<void>;
82
+ declare const WARNING_CODES: {
83
+ readonly DOC_PAGE_COUNT: {
84
+ readonly severity: "info";
85
+ readonly template: "PDF has {count} page(s)";
86
+ };
87
+ readonly DOC_PAGE_BLOCK_COUNT: {
88
+ readonly severity: "info";
89
+ readonly template: "Page {page}: {imageCount} image, {vectorCount} vector, {textCount} text, {outlineCount} outline";
90
+ };
91
+ readonly DOC_PAGE_ROTATION_DROPPED: {
92
+ readonly severity: "warning";
93
+ readonly template: "{prefix}PDF declares /Rotate {pageRotation}; the importer does not apply page rotation, so the imported page is in its un-rotated (native) orientation. Viewer-rendered output may differ from the source's display orientation.";
94
+ };
95
+ readonly BLOCK_KIND_UNKNOWN: {
96
+ readonly severity: "error";
97
+ readonly template: "An unknown block kind \"{kind}\" was encountered and was skipped.";
98
+ };
99
+ readonly PAGE_INDEX_OUT_OF_RANGE: {
100
+ readonly severity: "warning";
101
+ readonly template: "Requested page index {requested} is out of range; PDF has {available} page(s). No blocks were emitted.";
102
+ };
103
+ readonly FONT_OPENTYPE_PARSE_FAILED: {
104
+ readonly severity: "warning";
105
+ readonly template: "{prefix}font \"{fontName}\" could not be parsed as OpenType{reason}. Glyph outlines will fall back to pdf.js raster paths \u2014 some shapes may render without stroke/anti-aliasing detail.";
106
+ };
107
+ readonly FONT_CMAP_AUGMENTED: {
108
+ readonly severity: "info";
109
+ readonly template: "Font \"{fontName}\" character map was augmented (cmap patches={cmapPatches}, injected glyphs={injectedGlyphs}).";
110
+ };
111
+ readonly FONT_CMAP_AUGMENT_FAILED: {
112
+ readonly severity: "warning";
113
+ readonly template: "Font \"{fontName}\" character map could not be augmented. Using the original font data \u2014 some glyphs may render incorrectly.";
114
+ };
115
+ readonly FONT_RESOLVER_MISS: {
116
+ readonly severity: "warning";
117
+ readonly template: "Font \"{fontFamily}\" could not be resolved. The text was rendered as a vector outline.";
118
+ };
119
+ readonly FONT_NOT_FOUND_IN_ASSETS: {
120
+ readonly severity: "warning";
121
+ readonly template: "Font \"{fontFamily}\" was not found in the asset library. The text was rendered as a vector outline.";
122
+ };
123
+ readonly FONT_SUBSTITUTED: {
124
+ readonly severity: "info";
125
+ readonly template: "Font \"{fontFamily}\" was substituted with \"{substitutedName}\" from the asset library.";
126
+ };
127
+ readonly FONT_REF_NOT_EXTRACTED: {
128
+ readonly severity: "error";
129
+ readonly template: "Text run references font \"{fontRef}\" which was not extracted. The text was skipped.";
130
+ };
131
+ readonly FONT_SUBSTITUTIONS: {
132
+ readonly severity: "warning";
133
+ readonly template: "Substituted {count} font famil{plural} during import: {pairs}.";
134
+ };
135
+ readonly FONT_SUBSET_FALLBACK: {
136
+ readonly severity: "warning";
137
+ readonly template: string;
138
+ };
139
+ readonly TEXT_OUTLINE_NO_FONT_FAMILY: {
140
+ readonly severity: "warning";
141
+ readonly template: "Text outline has no declared font family. The run was skipped.";
142
+ };
143
+ readonly TEXT_RUN_SKIPPED_NO_RESOLUTION: {
144
+ readonly severity: "warning";
145
+ readonly template: "No typeface or glyph outlines for \"{fontFamily}\"; run skipped.";
146
+ };
147
+ readonly TEXT_RENDER_MODE_SKIPPED: {
148
+ readonly severity: "warning";
149
+ readonly template: "Text item{pageTag}: skipped \"{text}\" \u2014 PDF text rendering mode {modeLabel}. This text paints no ink; for mode 7 it acts as a clipping mask for following images, an effect the importer does not currently reproduce.";
150
+ };
151
+ readonly TEXT_OVERFLOW_AFTER_AUTOSIZE: {
152
+ readonly severity: "warning";
153
+ readonly template: "Text \"{text}\" did not fit after auto-sizing; it still overflows by {overflowLines} line(s). The text was kept; some content may be clipped.";
154
+ };
155
+ readonly TEXT_CLIP_MODE_7_DROPPED: {
156
+ readonly severity: "warning";
157
+ readonly template: "{prefix}{label} at ({bbox}) {bboxSize} pt: dropped because the surrounding region was clipped by text in PDF render mode 7 (text-as-clipping-mask). The masked artwork cannot be reproduced as text-clipped content.";
158
+ };
159
+ readonly IMAGE_ENCODER_MISS: {
160
+ readonly severity: "warning";
161
+ readonly template: "{prefix}an image{idLabel} ({width}\u00D7{height} raw pixels{kind}) could not be encoded ({reason}). The image was dropped from the imported design.";
162
+ };
163
+ readonly IMAGE_PDFJS_UNRESOLVED: {
164
+ readonly severity: "error";
165
+ readonly template: "{prefix}image placement {idLabel} at ({bboxX}, {bboxY}) {bboxW}\u00D7{bboxH} pt could not be resolved by pdf.js (reason={reason}) and was dropped.";
166
+ };
167
+ readonly IMAGE_NO_PIXELS: {
168
+ readonly severity: "warning";
169
+ readonly template: "An image element has no pixel data and was skipped.";
170
+ };
171
+ readonly IMAGE_FLIP_UNSUPPORTED: {
172
+ readonly severity: "warning";
173
+ readonly template: "Image flip could not be applied (engine version too old). The image will display without flip.";
174
+ };
175
+ readonly IMAGE_SKEW_DROPPED: {
176
+ readonly severity: "warning";
177
+ readonly template: "{prefix}an image{idLabel} at ({bboxX}, {bboxY}) {bboxW}\u00D7{bboxH} pt has a skewed transform ctm={ctm}{residualStr} that cannot be represented. Placed axis-aligned \u2014 image content will appear un-skewed.";
178
+ };
179
+ readonly IMAGE_CLIPPED_NO_BYTES: {
180
+ readonly severity: "warning";
181
+ readonly template: "{prefix}a clipped image at {bbox} had no decoded bytes; the placement was dropped before combining clip shapes.";
182
+ };
183
+ readonly IMAGE_CLIP_UNUSABLE: {
184
+ readonly severity: "warning";
185
+ readonly template: "{prefix}a clipped image at {bbox} with {clipCount} clip path(s) has no usable clip \u2014 {degenerateCount} degenerate, {disjointCount} disjoint from the image. The image was dropped.";
186
+ };
187
+ readonly IMAGE_CLIP_EMPTY_INTERSECTION: {
188
+ readonly severity: "warning";
189
+ readonly template: "{prefix}a clipped image at {bbox} with {clipCount} clip path(s) had an empty intersection. The image was dropped.";
190
+ };
191
+ readonly IMAGE_CLIP_COMBINE_FAILED: {
192
+ readonly severity: "error";
193
+ readonly template: "{prefix}a clipped image at {bbox} with {clipCount} clip path(s) could not be combined. The image was dropped from the imported design.";
194
+ };
195
+ readonly IMAGE_MASK_NO_FILL_COLOR: {
196
+ readonly severity: "warning";
197
+ readonly template: "{prefix}a stencil ImageMask paint (objId={objId}) had no current fill colour to composite with. The mask was dropped from the imported design.";
198
+ };
199
+ readonly IMAGE_MASK_COMPOSITE_FAIL: {
200
+ readonly severity: "warning";
201
+ readonly template: "{prefix}a stencil ImageMask (objId={objId}) at ({bboxX}, {bboxY}) {bboxW}\u00D7{bboxH} pt could not be composited with the current fill colour (space={colourSpace}). The mask was dropped from the imported design.";
202
+ };
203
+ readonly IMAGE_MASK_GROUP_UNSUPPORTED: {
204
+ readonly severity: "warning";
205
+ readonly template: "{prefix}a paintImageMaskXObjectGroup batch of {count} stencil paints is not supported by the importer. The masks were dropped.";
206
+ };
207
+ readonly IMAGE_MASK_REPEAT_UNSUPPORTED: {
208
+ readonly severity: "warning";
209
+ readonly template: "{prefix}a paintImageMaskXObjectRepeat tiling {count} stencil paints is not supported by the importer. The masks were dropped.";
210
+ };
211
+ readonly CLIP_DEGENERATE_FALLBACK: {
212
+ readonly severity: "warning";
213
+ readonly template: "{prefix}a clipped vector path at {bbox} with {clipCount} clip path(s) has no usable clip \u2014 {degenerateCount} degenerate (zero width or height). Falling back to the unclipped path.";
214
+ };
215
+ readonly CLIP_COMBINE_STROKE_ONLY: {
216
+ readonly severity: "warning";
217
+ readonly template: "{prefix}a clipped vector path at {bbox} with {clipCount} clip path(s) has no fill color (stroke-only) which cannot survive combine. Falling back to the unclipped path.";
218
+ };
219
+ readonly CLIP_COMBINE_FAILED: {
220
+ readonly severity: "warning";
221
+ readonly template: "{prefix}a vector path at {bbox} with {clipCount} clip path(s) could not be combined. Rendered without its clip \u2014 some content may extend beyond the intended mask.";
222
+ };
223
+ readonly CLIP_COMBINE_TOO_FEW_INPUTS: {
224
+ readonly severity: "warning";
225
+ readonly template: "{prefix}clip combine was called with {inputCount} input block(s) (need at least 2). The owner block was emitted without its clips.";
226
+ };
227
+ readonly CLIP_COMBINE_NOT_COMBINABLE: {
228
+ readonly severity: "warning";
229
+ readonly template: "{prefix}clip combine rejected {inputCount} input block(s) as not combinable (nested or incompatible shapes). The owner block was emitted without its clips.";
230
+ };
231
+ readonly CLIP_COMBINE_THREW: {
232
+ readonly severity: "warning";
233
+ readonly template: "{prefix}clip combine of {inputCount} input block(s) failed ({reason}). The owner block was emitted without its clips.";
234
+ };
235
+ readonly COLOR_TYPE_UNSUPPORTED: {
236
+ readonly severity: "warning";
237
+ readonly template: "{prefix}{subject}color of type \"{colorType}\" is not supported. Rendered as black.";
238
+ };
239
+ readonly COLOR_SPACE_UNSUPPORTED: {
240
+ readonly severity: "warning";
241
+ readonly template: "{prefix}{elementLabel}color space \"{space}\" ({color}) at {bbox} is not supported. Rendered as black.";
242
+ };
243
+ readonly SPOT_COLOR_NO_ALTERNATE: {
244
+ readonly severity: "warning";
245
+ readonly template: "Spot color \"{name}\" has no resolvable alternate; leaving at engine default.";
246
+ };
247
+ readonly SPOT_COLOR_REGISTER_FAILED: {
248
+ readonly severity: "warning";
249
+ readonly template: "Failed to register spot color \"{name}\": {error}";
250
+ };
251
+ readonly VECTOR_PATH_FILL_RULE_UNSUPPORTED: {
252
+ readonly severity: "warning";
253
+ readonly template: "CE.SDK does not expose `{property}` on `vector_path` shapes (added in CE.SDK {sinceVersion}); the source PDF used non-zero winding fill, but imported paths render with the engine default (even-odd) winding. Self-overlapping or compound paths may render with holes inverted. Upgrade `@cesdk/engine` to {sinceVersion} or newer to preserve the source winding rule.";
254
+ };
255
+ readonly BLEND_MODE_UNSUPPORTED: {
256
+ readonly severity: "warning";
257
+ readonly template: "Blend mode \"{mode}\" is not supported. Using \"Normal\" as the default.";
258
+ };
259
+ readonly SHADING_UNRESOLVED: {
260
+ readonly severity: "error";
261
+ readonly template: "{prefix}gradient shading could not be resolved by pdf.js and was removed. The painted region will be missing in the imported scene.";
262
+ };
263
+ readonly SHADING_MESH_UNSUPPORTED: {
264
+ readonly severity: "warning";
265
+ readonly template: "{prefix}gradient shading uses an unsupported mesh variant (PDF ShadingType 4\u20137). The region was dropped because mesh gradients can't be represented as a CE.SDK gradient fill.";
266
+ };
267
+ readonly SHADING_UNKNOWN_IR_TAG: {
268
+ readonly severity: "error";
269
+ readonly template: "{prefix}gradient shading reported unknown IR tag \"{tag}\" and was dropped.";
270
+ };
271
+ readonly SHADING_INSUFFICIENT_STOPS: {
272
+ readonly severity: "warning";
273
+ readonly template: "{prefix}gradient shading produced fewer than two color stops and was dropped.";
274
+ };
275
+ readonly SHADING_NO_CLIP_FALLBACK_BBOX: {
276
+ readonly severity: "info";
277
+ readonly template: "{prefix}gradient shading was painted without an active clip path. Falling back to the shading /BBox; if this scene shows gradient bands outside the intended shape, file a bug.";
278
+ };
279
+ readonly SHADING_CLIP_DEGENERATE: {
280
+ readonly severity: "warning";
281
+ readonly template: "{prefix}gradient shading clip path was degenerate and the region was dropped.";
282
+ };
283
+ readonly SHADING_RADIAL_FOCAL_DROPPED: {
284
+ readonly severity: "info";
285
+ readonly template: "{prefix}radial gradient declared a non-zero inner radius (PDF Shading Type 3 focal point). It was approximated as a centered radial; the focal offset was dropped.";
286
+ };
287
+ readonly SHADING_UNKNOWN_RADIAL_SUBTYPE: {
288
+ readonly severity: "error";
289
+ readonly template: "{prefix}gradient shading reported unknown RadialAxial subtype \"{kind}\" and was dropped.";
290
+ };
291
+ readonly SHADING_NO_PATTERN_REF: {
292
+ readonly severity: "error";
293
+ readonly template: "{prefix}gradient shading was emitted without a pattern reference and was dropped.";
294
+ };
295
+ readonly TILING_PATTERN_AS_IMAGE: {
296
+ readonly severity: "info";
297
+ readonly template: "{prefix}tiling pattern \"{patternId}\" was imported as an image (single-image tiling pattern, e.g. Skia/Chrome PDF export of an embedded bitmap).";
298
+ };
299
+ readonly TILING_PATTERN_UNSUPPORTED: {
300
+ readonly severity: "warning";
301
+ readonly template: "{prefix}tiling pattern \"{patternId}\" contains {imageCount} image XObject(s) and {otherOpCount} other paint op(s); only single-image tiling patterns are imported. The pattern fill was dropped.";
302
+ };
303
+ readonly TILING_PATTERN_UNRESOLVED: {
304
+ readonly severity: "error";
305
+ readonly template: "{prefix}tiling pattern \"{patternId}\" could not be resolved by pdf.js and was dropped.";
306
+ };
307
+ readonly TILING_PATTERN_IMAGE_DECODE_FAILED: {
308
+ readonly severity: "warning";
309
+ readonly template: "{prefix}tiling pattern \"{patternId}\" referenced image \"{imageObjId}\" but the image bytes could not be decoded. The pattern fill was dropped.";
310
+ };
311
+ };
312
+ interface Color {
313
+ space: "rgb" | "cmyk" | "gray";
314
+ /** in [0..1]. rgb:[r,g,b] cmyk:[c,m,y,k] gray:[g] */
315
+ values: number[];
316
+ /** in [0..1]; defaults to 1 */
317
+ alpha?: number;
318
+ }
319
+ type CesdkFontWeight = "thin" | "extraLight" | "light" | "normal" | "medium" | "semiBold" | "bold" | "extraBold" | "heavy";
320
+ interface TextOutline {
321
+ kind: "text-outline";
322
+ /** pt, run bbox top-left */
323
+ x: number;
324
+ y: number;
325
+ width: number;
326
+ height: number;
327
+ /** SVG path data (all glyphs concatenated) */
328
+ d: string;
329
+ /** Original text (for diffing / accessibility) */
330
+ text: string;
331
+ /**
332
+ * Family base parsed from the PDF PostScript name (subset prefix and
333
+ * recognized weight/style/width suffix stripped — e.g.
334
+ * `WTNEYF+Poppins-ExtraBold` → `Poppins`). Falls back to the full
335
+ * subset-stripped name when no recognized suffix is present, so families
336
+ * with internal hyphens like `SVN-BlogScript` survive intact. Suitable
337
+ * as a font-resolver query.
338
+ */
339
+ fontFamily: string;
340
+ /** pt */
341
+ fontSize: number;
342
+ /** Derived from fontObj.italic + name suffix. */
343
+ fontStyle?: "normal" | "italic";
344
+ /**
345
+ * CE.SDK weight string. Together with `fontStyle` and `fontFamily` lets
346
+ * the font-resolver substitute the correct typeface variant for an
347
+ * editable text block, or — on resolver miss — be preserved as block
348
+ * metadata.
349
+ */
350
+ fontWeight?: CesdkFontWeight;
351
+ fill: Color | null;
352
+ /**
353
+ * pt, pen x of the first glyph in device coords. Used by the
354
+ * resolver-substitution path to place the editable text block at the
355
+ * generator's setPositionX rather than the ink-bbox left edge (which is
356
+ * shifted by the first glyph's LSB, typically ~1–2pt for caps).
357
+ */
358
+ textOriginX?: number;
359
+ /**
360
+ * Radians in CE.SDK screen (y-down) frame, 0 for horizontal. Optional;
361
+ * absent on truly horizontal runs. Mirrors the `rotation` field on
362
+ * TextRun so both emit paths can apply `setRotation` from the same
363
+ * source. Sign convention: PDF's y-up Tm rotation atan2(b,a) negated to
364
+ * match CE.SDK's y-down screen frame.
365
+ */
366
+ rotation?: number;
367
+ /**
368
+ * pt, ink-bbox width in the un-rotated text frame. Equals `width` for
369
+ * horizontal runs; differs for ±90°/180° runs where `width`/`height`
370
+ * describe the rotated AABB.
371
+ */
372
+ unrotWidth?: number;
373
+ /**
374
+ * pt, ink-bbox height in the un-rotated text frame. Pair with
375
+ * `unrotWidth` to size the editable text block at its natural
376
+ * pre-rotation dimensions; CE.SDK then auto-fits to its own typeface
377
+ * metrics.
378
+ */
379
+ unrotHeight?: number;
380
+ /**
381
+ * [0..1], default 1. Like `VectorPath.opacity` and `ImageBox.opacity`,
382
+ * lets the emitter apply `setOpacity` to text-outline blocks (the
383
+ * walker uses this for runs that inherited a reduced fill alpha).
384
+ */
385
+ opacity?: number;
386
+ /**
387
+ * pt, baseline y in device (top-down) coords. Pen origin's y, used by
388
+ * the resolver-substitution path to position the editable text block
389
+ * at the baseline rather than the ink-bbox top.
390
+ */
391
+ textOriginY?: number;
392
+ }
393
+ interface EmbeddedFont {
394
+ /**
395
+ * Unique per subset. When a PDF embeds multiple subsets that share a PS
396
+ * family name (common: one subset per chapter/page), this field is
397
+ * suffixed with pdf.js's `loadedName` (e.g.
398
+ * `PublicSans-Light#g_d0_f3`) so each subset keeps its own entry in
399
+ * `Document.fonts`. Use `family` for display.
400
+ */
401
+ postScriptName: string;
402
+ /**
403
+ * Human typeface name from the font's opentype name table (falls back to
404
+ * the PS family name).
405
+ */
406
+ family: string;
407
+ /** 400 | 700 | 900 (pdf.js bold/black flags) */
408
+ weight?: number;
409
+ style?: "normal" | "italic";
410
+ /**
411
+ * Width-axis label (OS/2 `usWidthClass` mapped to a normalized string,
412
+ * or parsed from the PostScript suffix's width token: `cond`,
413
+ * `extended`, `narrow`, …). Distinguishes condensed-width subsets
414
+ * from normal-width subsets so the merger does not collapse them.
415
+ * Absent when neither source carries width information.
416
+ */
417
+ width?: string;
418
+ /** Raw TTF/OTF bytes */
419
+ data: Uint8Array;
420
+ /** 'font/otf' | 'font/ttf' */
421
+ mimeType: string;
422
+ }
423
+ interface ColorSpan {
424
+ from: number;
425
+ to: number;
426
+ fill: Color | null;
427
+ }
428
+ interface StyleSpan {
429
+ from: number;
430
+ to: number;
431
+ /** PostScript name from the originating run. */
432
+ fontRef: string;
433
+ /** CSS 100..900; matches `EmbeddedFont.weight`. */
434
+ fontWeight?: number;
435
+ /** Matches `EmbeddedFont.style`. */
436
+ fontStyle?: "normal" | "italic";
437
+ }
438
+ interface TextRun {
439
+ kind: "text-run";
440
+ /** pt, unrotated bbox top-left */
441
+ x: number;
442
+ /** pt */
443
+ y: number;
444
+ /** pt (with a small advance padding) */
445
+ width: number;
446
+ /** pt (ascender - descender, scaled by fontSize) */
447
+ height: number;
448
+ /** Rendered string (Unicode, not glyph codes) */
449
+ text: string;
450
+ /** postScriptName; resolves via Document.fonts */
451
+ fontRef: string;
452
+ /**
453
+ * Logical font family (subset prefix + weight/style suffix stripped),
454
+ * mirroring `EmbeddedFont.family`. Populated by `text-items.ts` from
455
+ * the resolved EmbeddedFont so `postprocess-text.ts::canGroup` can
456
+ * decide whether two distinct `fontRef`s (e.g. `Helvetica` +
457
+ * `Helvetica-Bold`) belong to the same logical typeface and should
458
+ * merge into one block with `styleSpans`. Absent on legacy inputs
459
+ * (synthetic IR, older callers) — `canGroup` then falls back to the
460
+ * stricter `fontRef === fontRef` test and keeps the runs split.
461
+ */
462
+ family?: string;
463
+ /**
464
+ * CSS 100..900, mirroring `EmbeddedFont.weight`. Used as the per-run
465
+ * weight contribution when the merger bridges variants of the same
466
+ * `family`. Absent on legacy inputs (merger then refuses to merge
467
+ * across `fontRef` mismatches — see `family`).
468
+ */
469
+ fontWeight?: number;
470
+ /** Mirrors `EmbeddedFont.style`. Pairs with `fontWeight`. */
471
+ fontStyle?: "normal" | "italic";
472
+ /**
473
+ * Width-axis label, mirroring `EmbeddedFont.width`. Distinguishes
474
+ * `Helvetica-Condensed` from `Helvetica-Regular` so the merger does
475
+ * not collapse them — CE.SDK's per-range setters (`setTextFontWeight`,
476
+ * `setTextFontStyle`) have no width parameter and would silently
477
+ * render condensed glyphs at normal width.
478
+ *
479
+ * Values follow OS/2 `usWidthClass` semantics, normalized to a small
480
+ * string set: `'ultraCondensed' | 'extraCondensed' | 'condensed' |
481
+ * 'semiCondensed' | 'normal' | 'semiExpanded' | 'expanded' |
482
+ * 'extraExpanded' | 'ultraExpanded'`. Absent on legacy inputs.
483
+ */
484
+ fontWidth?: string;
485
+ /** pt */
486
+ fontSize: number;
487
+ /**
488
+ * Radians, 0 for horizontal text. x/y place the block so rotation around
489
+ * its center matches the PDF.
490
+ */
491
+ rotation: number;
492
+ fill: Color | null;
493
+ /**
494
+ * pt, pen origin x in PDF view-frame (top-left origin), pre-pivot-comp.
495
+ * Populated by `text-items.ts`; used by `groupLineRuns` to test
496
+ * pen-origin continuity between runs that pdf.js split mid-line for
497
+ * kerning.
498
+ */
499
+ baselineXPt?: number;
500
+ /** pt, pen origin y, same frame. */
501
+ baselineYPt?: number;
502
+ /**
503
+ * pt, raw along-baseline advance from pdf.js (`item.width`). Distinct
504
+ * from `width`, which carries a small rendering padding that is
505
+ * intentionally invisible to merger logic.
506
+ */
507
+ advanceWidthPt?: number;
508
+ /**
509
+ * Per-character (UTF-16) color ranges within `text`. Set by `mergeRuns`
510
+ * only when constituent runs differ in fill; absent for single-run
511
+ * blocks and for merged groups with uniform fill (back-compat). Range
512
+ * indices match `engine.block.setTextColor(id, color, from, to)`'s
513
+ * contract.
514
+ */
515
+ colorSpans?: ColorSpan[];
516
+ /**
517
+ * Per-character (UTF-16) weight/style ranges within `text`. Set by
518
+ * `mergeRuns` only when the merger bridged adjacent same-family runs
519
+ * that use different weight/style variants (e.g. inline bold inside a
520
+ * paragraph). Absent for single-run blocks and for merged groups with
521
+ * uniform variant. Range indices match `engine.block.setTextFontWeight
522
+ * (id, weight, from, to)` / `setTextFontStyle(id, style, from, to)`.
523
+ */
524
+ styleSpans?: StyleSpan[];
525
+ /**
526
+ * Lazy builder for the run's glyph outline as a vector path. Captures
527
+ * the walker's pending run + glyph + font references; invoked by
528
+ * `emit/cesdk.ts::emitGlyphsAsVectorPath` only when the font-strategy
529
+ * cascade returns null and emission needs a fallback. Returns the SVG
530
+ * `d` plus bbox geometry, or null when path extraction is unavailable
531
+ * (Type 3 glyph charproc missing, opentype parse failure).
532
+ *
533
+ * Populated by `text-items.ts::extractTextRuns` from a per-BT/ET queue
534
+ * stashed by the walker (`placeholder._lazyGlyphOutlines`). Holds
535
+ * references — discarded when the IR is GC'd after emit. Builder is
536
+ * called at most once per run (cascade-null is rare for asset-library
537
+ * coverage).
538
+ */
539
+ _buildGlyphOutline?: () => GlyphOutlineData | null;
540
+ }
541
+ interface GlyphOutlineData {
542
+ /** SVG path data in local bbox coordinates (top-left origin). */
543
+ d: string;
544
+ /** pt, bbox top-left in page coords. */
545
+ x: number;
546
+ /** pt */
547
+ y: number;
548
+ /** pt */
549
+ width: number;
550
+ /** pt */
551
+ height: number;
552
+ }
553
+ /**
554
+ * Per-run input to the cascade and emission layers. Built by
555
+ * `lib/make-request.ts` from the source IR block (either `TextRun` or
556
+ * `TextOutline`).
557
+ *
558
+ * Vector-outline data has two shapes depending on source kind:
559
+ * - `TextOutline` sources carry `glyphPaths` eagerly (the walker built
560
+ * the path because the font wasn't embeddable — Type 3 / restricted /
561
+ * PUA).
562
+ * - `TextRun` sources carry `buildGlyphOutline`, a lazy builder set up
563
+ * by the walker + text-items pairing. Emission only invokes it when
564
+ * the cascade returns null and a vector fallback is needed — most
565
+ * documents have asset-library coverage and never call it.
566
+ */
567
+ export interface FontRequest {
568
+ /** Display family (subset prefix stripped). */
569
+ family: string;
570
+ style: "normal" | "italic";
571
+ weight: Font["weight"];
572
+ /** Original text content; preserved for metadata + recovery. */
573
+ text: string;
574
+ /** Embedded subset bytes — present when the source was a TextRun. */
575
+ embeddedFont?: EmbeddedFont;
576
+ /** Concatenated glyph paths in local coords — present when the source was a TextOutline. */
577
+ glyphPaths?: string;
578
+ /** Lazy glyph-outline builder (TextRun sources). Built by walker;
579
+ * invoked at most once by emission when the cascade returns null. */
580
+ buildGlyphOutline?: () => GlyphOutlineData | null;
581
+ /** Original IR block — used by emission for geometry, fills, rotation, etc. */
582
+ source: TextRun | TextOutline;
583
+ }
584
+ export type CascadeResolution = {
585
+ kind: "typeface";
586
+ typeface: Typeface;
587
+ font?: Font;
588
+ /** Identifies which stage produced this resolution. */
589
+ provenance: string;
590
+ /** Forwarded from `FontResolverResult.substitutedFrom`. When set, the
591
+ * emission layer treats the resolution as substituted and recomputes
592
+ * layout. */
593
+ substitutedFrom?: string;
594
+ } | {
595
+ kind: "subset";
596
+ embeddedFont: EmbeddedFont;
597
+ provenance: "embedded-subset";
598
+ };
599
+ /**
600
+ * Stage outcome. Distinguishing "pass with reason" from `null` lets debug
601
+ * logs explain *why* the cascade reached a given stage's neighbour. Cheap
602
+ * to carry now; expensive to retrofit.
603
+ */
604
+ export type StageOutcome = CascadeResolution | {
605
+ kind: "pass";
606
+ reason: string;
607
+ };
608
+ interface CascadeLogger {
609
+ emit?: (code: string, params: Record<string, unknown>) => void;
610
+ log?: (message: string, level?: string) => void;
611
+ }
612
+ export interface FontStageContext {
613
+ engine: CreativeEngine;
614
+ resolver: TypefaceResolver | null;
615
+ /**
616
+ * Per-import-call cache. Same `family|style|weight` resolves once per
617
+ * `PDFParser.fromFile` invocation; reopening the PDF produces a fresh
618
+ * cache. Created in the parser entry, discarded on completion.
619
+ */
620
+ resolverCache: Map<string, FontResolverResult | null>;
621
+ logger: CascadeLogger | null;
622
+ }
623
+ export interface FontStage {
624
+ /** Stable identifier for logs and tests. */
625
+ readonly name: string;
626
+ resolve(req: FontRequest, ctx: FontStageContext): Promise<StageOutcome>;
627
+ }
628
+ export interface FontCascade {
629
+ readonly stages: readonly FontStage[];
630
+ resolve(req: FontRequest, ctx: FontStageContext): Promise<CascadeResolution | null>;
631
+ }
632
+ /**
633
+ * Result of a single emission call. The emission layer may produce zero,
634
+ * one, or more blocks; the `blocks` array is in z-order (back to front).
635
+ * `substituted` is true when the cascade's answer triggered a
636
+ * substitution that the dispatcher should account against the
637
+ * per-import substitution-count log.
638
+ */
639
+ export interface EmissionResult {
640
+ /** CE.SDK block ids created. Empty array means the run was skipped. */
641
+ blocks: number[];
642
+ /** True when the resolution involved a font substitution. */
643
+ substituted: boolean;
644
+ /** Display name of the typeface the run resolved to, if any. */
645
+ resolvedFamily?: string;
646
+ }
647
+ /**
648
+ * Block-creation primitives that emission delegates to. The dispatcher
649
+ * fills these in so emission stays decoupled from `engine.block.*` and
650
+ * from the geometry helpers that already live in `emit/cesdk.ts`. Keeps
651
+ * the emission policy testable in isolation: each callback can be
652
+ * stubbed.
653
+ */
654
+ export interface EmissionHelpers {
655
+ /**
656
+ * Create an editable text block with the request's geometry and the
657
+ * caller-supplied `applyFont` callback (called between layout and
658
+ * properties — order matters for CE.SDK's `replaceText` wrap-marker).
659
+ * `layoutIr` may be the original block or a typeface-aware substitute
660
+ * (see `resolveLayoutForSubstitution` below).
661
+ */
662
+ createEditableText(req: FontRequest, layoutIr: TextOutline | TextRun, applyFont: (engine: CreativeEngine, block: number) => void): number;
663
+ /**
664
+ * Emit the request's `glyphPaths` as a `vector_path` graphic block at
665
+ * the source bbox. Returns null when the request carries no
666
+ * `glyphPaths` (TextRun source).
667
+ */
668
+ emitGlyphsAsVectorPath(req: FontRequest): number | null;
669
+ /**
670
+ * Compute the layout-aware IR for a substituted typeface. For TextRun
671
+ * sources this is a no-op (returns `req.source`); for TextOutline
672
+ * sources it derives the unrotated bbox CE.SDK needs using the
673
+ * substitute's font metrics.
674
+ */
675
+ resolveLayoutForSubstitution(req: FontRequest, resolution: CascadeResolution): Promise<TextOutline | TextRun>;
676
+ }
677
+ export interface EmissionContext {
678
+ engine: CreativeEngine;
679
+ logger: CascadeLogger | null;
680
+ /** Page index (1-based) for diagnostic context. */
681
+ pageIndex?: number;
682
+ helpers: EmissionHelpers;
683
+ }
684
+ export interface FontEmission {
685
+ readonly name: string;
686
+ emit(req: FontRequest, resolution: CascadeResolution | null, ctx: EmissionContext): Promise<EmissionResult>;
687
+ }
688
+ export interface FontStrategy {
689
+ readonly cascade: FontCascade;
690
+ readonly emission: FontEmission;
691
+ }
692
+ export declare const perfectMatchResolverStage: FontStage;
693
+ export declare const embeddedSubsetStage: FontStage;
694
+ export declare const anyMatchResolverStage: FontStage;
695
+ export declare const defaultEmission: FontEmission;
696
+ /**
697
+ * Build a cascade from an ordered list of stages. The cascade walks the
698
+ * list and returns the first non-pass outcome. When every stage passes,
699
+ * the cascade returns `null` (emission decides what to do).
700
+ */
701
+ export declare function createFontCascade(stages: FontStage[]): FontCascade;
702
+ interface FontStrategyDef {
703
+ cascade: FontCascade;
704
+ emission: FontEmission;
705
+ }
706
+ export declare function createFontStrategy(def: FontStrategyDef): FontStrategy;
707
+ /**
708
+ * Default for `PDFParser.fromFile({ fontStrategy })`. Embedded subset
709
+ * bytes are used as an intermediate fallback to preserve the source's
710
+ * exact rendering when the family isn't in the asset library — vector
711
+ * outline extraction can produce incorrect bboxes or partial glyph
712
+ * coverage for some fonts, while the engine renders subset bytes
713
+ * faithfully.
714
+ *
715
+ * - Run's family resolves perfectly in the asset library →
716
+ * perfect-match typeface (HTTPS URI).
717
+ * - Embedded subset present (TextRun source) → register the bytes and
718
+ * emit as editable text with `setFont`. The resulting scene depends
719
+ * on the bytes living in a `buffer://` URI; this is the offline-safe
720
+ * path.
721
+ * - Otherwise → any-match resolver (possibly substituted via the
722
+ * fallbacks source; a per-import warning summarizes substitutions).
723
+ * - Neither hits → cascade returns null → emission falls through to a
724
+ * vector outline (TextOutline-source runs always have glyph paths;
725
+ * TextRun-source runs use the Phase-2 lazy closure built by the
726
+ * walker).
727
+ *
728
+ * For tools that explicitly want vectorization over subset bytes
729
+ * (e.g. brand-locked editors), use `assetLibraryStrategy`.
730
+ */
731
+ export declare const editableFirstStrategy: FontStrategy;
732
+ /**
733
+ * For print finalization, PDF-viewer-style integrations, and anywhere a
734
+ * silent Helvetica → Roboto substitution is unacceptable. Skips
735
+ * `anyMatchResolverStage`; non-embeddable runs without a perfect match
736
+ * fall through to vector outline.
737
+ */
738
+ export declare const exactFidelityStrategy: FontStrategy;
739
+ /**
740
+ * For brand-locked tools: every editable text must come from the asset
741
+ * library (or the resolver's alias fallbacks). Drops the subset stage so
742
+ * PDF-embedded subset bytes never leak through; runs whose family isn't
743
+ * in the library go straight to the any-match resolver.
744
+ */
745
+ export declare const assetLibraryStrategy: FontStrategy;
746
+ export declare const defaultStrategy: FontStrategy;
747
+ type PageBounds = "trim" | "media";
748
+ type PdfLogger = Logger<typeof WARNING_CODES>;
749
+ export declare class PDFParser {
750
+ private engine;
751
+ private pdfBytes;
752
+ private fontResolver;
753
+ private fontStrategy;
754
+ private pageBounds;
755
+ private logger;
756
+ private ir?;
757
+ private constructor();
758
+ /**
759
+ * Create a PDFParser instance from a PDF file.
760
+ *
761
+ * @param engine - CE.SDK engine instance
762
+ * @param file - PDF file as Blob, File, or ArrayBuffer
763
+ * @param options - Optional configuration
764
+ * @param options.fontResolver - Custom font resolver. Invoked by the
765
+ * cascade's resolver-using stages (`perfectMatchResolverStage`,
766
+ * `anyMatchResolverStage`). Requires `addGfontsAssetLibrary` to be
767
+ * called on the engine before parse.
768
+ * @param options.fontStrategy - Strategy controlling how text runs
769
+ * become blocks. Default: `editableFirstStrategy` — prefers a perfect
770
+ * asset-library match, falls back to the PDF-embedded subset, then to
771
+ * resolver substitution (any-match), and finally to vector outline
772
+ * when nothing else hits. Other shipped presets:
773
+ * `exactFidelityStrategy` (never substitute), `assetLibraryStrategy`
774
+ * (drop subset). Build custom strategies with `createFontStrategy({
775
+ * cascade, emission })`. See `packages/pdf-importer/CLAUDE.md` →
776
+ * "Font resolution" for the mental model and offline-safe recipe.
777
+ * @returns A new PDFParser instance
778
+ */
779
+ static fromFile(engine: CreativeEngine, file: Blob | File | ArrayBuffer, options?: {
780
+ fontResolver?: TypefaceResolver;
781
+ fontStrategy?: FontStrategy;
782
+ /**
783
+ * Which PDF page box the emitted CE.SDK pages are sized to. Defaults
784
+ * to `"trim"` (designed page + bleed margin, the end-user view).
785
+ * `"media"` keeps pages at MediaBox (aka `page.view`) dimensions
786
+ * with children unshifted — used by the regression pixel-diff harness
787
+ * so CE.SDK exports match `pdftoppm`'s full-MediaBox reference images.
788
+ */
789
+ pageBounds?: PageBounds;
790
+ }): Promise<PDFParser>;
791
+ /**
792
+ * @deprecated Use options object instead: `fromFile(engine, file, { fontResolver })`
793
+ */
794
+ static fromFile(engine: CreativeEngine, file: Blob | File | ArrayBuffer, fontResolver?: TypefaceResolver): Promise<PDFParser>;
795
+ /**
796
+ * Parse the PDF file and emit a CE.SDK scene with all pages.
797
+ *
798
+ * Pipeline: pdf.js → IR (`extractDocument`) → line-run grouping →
799
+ * CE.SDK scene (`emitInto`). Text extraction always produces full text
800
+ * blocks: one block per line from pdf.js `getTextContent`, with adjacent
801
+ * lines that share font/size and horizontal overlap merged into a single
802
+ * multi-line block.
803
+ *
804
+ * Mirrors `engine.scene.loadFromString`: replaces the engine's current
805
+ * scene with the imported PDF.
806
+ *
807
+ * @returns Parse result containing the scene ID and logger
808
+ */
809
+ parse(): Promise<{
810
+ scene: number;
811
+ logger: PdfLogger;
812
+ }>;
813
+ /**
814
+ * Parse the PDF and return the imported pages as detached CE.SDK blocks.
815
+ *
816
+ * Mirrors `engine.block.loadFromString`: returned blocks are NOT attached
817
+ * to a scene. Caller appends and positions them however they want.
818
+ *
819
+ * Each PDF page yields one returned `page` block, sized to the PDF's
820
+ * MediaBox in inches with the page's content as children. CE.SDK pages
821
+ * are the right wrapper here — `setWidth`/`setHeight` scales children
822
+ * proportionally (so callers can fit a PDF page into an arbitrary host
823
+ * frame), and pages clip to their bounds (matching IDML's frame
824
+ * semantics when content overflows).
825
+ *
826
+ * @param options
827
+ * @param options.pageIndex 0-based page to load. Omit to load all pages
828
+ * (returns one block per page, in document order).
829
+ * @returns Detached page block IDs (length 1 for single-page mode,
830
+ * `pdf.numPages` for all-pages mode), plus the logger so callers can
831
+ * forward parser warnings into their own diagnostic stream.
832
+ */
833
+ loadAsBlocks(options?: {
834
+ pageIndex?: number;
835
+ }): Promise<{
836
+ blocks: number[];
837
+ logger: PdfLogger;
838
+ }>;
839
+ /**
840
+ * Number of pages in the PDF document.
841
+ *
842
+ * Triggers a full IR extraction on first call (the same work `parse` /
843
+ * `loadAsBlocks` does), then caches it. Subsequent calls — including
844
+ * `parse` and `loadAsBlocks` themselves — reuse the cached IR.
845
+ */
846
+ getPageCount(): Promise<number>;
847
+ /**
848
+ * Get the logger for this parser instance.
849
+ */
850
+ getLogger(): PdfLogger;
851
+ private ensureIR;
852
+ }
853
+
854
+ export {};