@imgly/pdf-importer 0.1.0-rc.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/README.md ADDED
@@ -0,0 +1,225 @@
1
+ # PDF Importer for the CE.SDK
2
+
3
+ ## Overview
4
+
5
+ The PDF Importer for the CE.SDK allows you to seamlessly integrate PDF files into the editor while retaining essential design attributes.
6
+
7
+ Here's an overview of the main features:
8
+
9
+ - _File Format Translation_: The importer converts **PDF files** into the CE.SDK scene file format using [`pdfjs-dist`](https://github.com/mozilla/pdf.js) (Mozilla's pdf.js), in its legacy/CJS-safe build.
10
+ - _Bulk Importing_: The codebase is adaptable for bulk importing, streamlining large-scale projects.
11
+ - _Color Translation_: RGB, CMYK, and Separation spot colors from PDFs are translated into CE.SDK's native `RGBAColor`, `CMYKColor`, and `SpotColor` variants. CMYK values are preserved end-to-end instead of collapsed to sRGB; Separation inks are registered on the document's spot-color registry (via `engine.editor.setSpotColorCMYK`) with their declared alternate CMYK values. DeviceN inks degrade to their alternate-space solid for now.
12
+
13
+ The following PDF design elements will be preserved by the import:
14
+
15
+ - _Positioning and Rotation_: Elements' positioning and rotation are accurately transferred.
16
+ - _Image Elements_: Embedded images (JPEG, PNG) are extracted and placed as graphic blocks. Note that only images with formats that are [supported by CE.SDK](https://img.ly/docs/cesdk/js/file-format-support-3c4b2a/#importing-media) will be rendered.
17
+ - _Text Elements_: Font family continuity is maintained, with options to supply font URIs or use Google fonts. Bold, italic, and weight styles are supported.
18
+ - _Vector Paths_: SVG path data from the PDF is imported as vector path blocks.
19
+ - _Colors and Gradients_: Solid colors, linear gradients, and radial gradients are faithfully reproduced.
20
+ - _Spot Color Detection_: Cut/fold marks using spot colors (CutContour, Thru-cut, etc.) are detected and can be handled separately. Brand spot inks (Separation / DeviceN entries in the page's `/ColorSpace` resource dictionary) are preserved as `SpotColor` fills and registered on the CE.SDK document-level spot registry.
21
+
22
+ ## How It Works
23
+
24
+ The importer runs a three-stage pipeline on each PDF page:
25
+
26
+ 1. **Extract** — a `pdfjs-dist` operator walker emits drawable blocks (images, vector paths, text outlines) in paint order. `page.getTextContent()` produces one editable text run per line.
27
+ 2. **Post-process** — adjacent text runs with the same font/size and horizontal overlap are merged into multi-line paragraph blocks.
28
+ 3. **Emit** — the intermediate representation is written as CE.SDK blocks: text, image, vector, outline.
29
+
30
+ PDF points are converted to inches (1pt = 1/72 inch) for CE.SDK design units. Embedded images become `buffer://` URIs; engine-provided fonts use `bundle://` URIs.
31
+
32
+ ## Installation
33
+
34
+ You can install `@imgly/pdf-importer` via npm or yarn. Use the following commands to install the package:
35
+
36
+ ```shell
37
+ npm install @imgly/pdf-importer
38
+ yarn add @imgly/pdf-importer
39
+ ```
40
+
41
+ ## Browser Quick-Start Example
42
+
43
+ ```js
44
+ import CreativeEngine from "@cesdk/engine";
45
+ import { PDFParser, addGfontsAssetLibrary } from "@imgly/pdf-importer";
46
+
47
+ const blob = await fetch("https://example.com/document.pdf").then((res) =>
48
+ res.blob()
49
+ );
50
+ const engine = await CreativeEngine.init({
51
+ license: "YOUR_LICENSE",
52
+ });
53
+ // We use google fonts to replace well known fonts in the default font resolver.
54
+ await addGfontsAssetLibrary(engine);
55
+ const parser = await PDFParser.fromFile(engine, blob);
56
+
57
+ await parser.parse();
58
+
59
+ const image = await engine.block.export(
60
+ engine.block.findByType("//ly.img.ubq/page")[0],
61
+ "image/png"
62
+ );
63
+ const sceneExportUrl = window.URL.createObjectURL(image);
64
+ console.log("The imported PDF file looks like:", sceneExportUrl);
65
+ // You can now e.g export the scene as archive with engine.scene.saveToArchive()
66
+ ```
67
+
68
+ ## Saving Scenes with Stable URLs
69
+
70
+ By default, the PDF importer creates internal `buffer://` URLs for embedded images. These are transient resources that work well when saving to an archive (`engine.scene.saveToArchive()`), which bundles all assets together.
71
+
72
+ However, if you want to save scenes as JSON strings (`engine.scene.saveToString()`) with stable, permanent URLs (e.g., for storing in a database or referencing CDN-hosted assets), you need to relocate the transient resources first.
73
+
74
+ ### Why Relocate?
75
+
76
+ - **Scene Archives** (`saveToArchive`): Include all assets in a single ZIP file. Transient `buffer://` URLs work fine.
77
+ - **Scene Strings** (`saveToString`): Only contain references to assets. Transient URLs won't work when reloading the scene later. You need permanent URLs (e.g., `https://`).
78
+
79
+ ### How to Relocate Transient Resources
80
+
81
+ After parsing the PDF file, use CE.SDK's native APIs to find and relocate all transient resources:
82
+
83
+ ```js
84
+ // 1. Parse the PDF file
85
+ const parser = await PDFParser.fromFile(engine, blob);
86
+ await parser.parse();
87
+
88
+ // 2. Find all transient resources (embedded images from the PDF)
89
+ const transientResources = engine.editor.findAllTransientResources();
90
+
91
+ // 3. Upload each resource and relocate to permanent URL
92
+ for (const resource of transientResources) {
93
+ const { URL: bufferUri, size } = resource;
94
+
95
+ // Extract binary data from the buffer
96
+ const data = engine.editor.getBufferData(bufferUri, 0, size);
97
+
98
+ // Upload to your backend/CDN (implement your own upload logic)
99
+ const permanentUrl = await uploadToBackend(data);
100
+
101
+ // Relocate the resource to the permanent URL
102
+ engine.editor.relocateResource(bufferUri, permanentUrl);
103
+ }
104
+
105
+ // 4. Now save to string - all URLs will be permanent
106
+ const sceneString = await engine.scene.saveToString();
107
+ ```
108
+
109
+ ### Note on Font URLs
110
+
111
+ When using `addGfontsAssetLibrary()` (the default font resolver), the resulting scene string will contain Google CDN URLs for fonts. If you need fonts hosted on your own infrastructure, configure a custom font resolver instead of using the default Google Fonts integration.
112
+
113
+ ## NodeJS Quick-Start Example
114
+
115
+ > **Prerequisite — emoji handling.** Two CE.SDK settings need attention when
116
+ > running the importer headlessly under `@cesdk/node`:
117
+ >
118
+ > - **`ubq://forceSystemEmojis = false`** — by default the engine routes any
119
+ > codepoint that ICU classifies as `RGI_Emoji` (e.g. `♥`, `★`, the dingbats
120
+ > block) through the emoji font *even when the active typeface has a glyph
121
+ > for it*. Customer PDFs frequently embed real text fonts (ZapfDingbats,
122
+ > Webdings, …) that map these codepoints to actual glyphs; forcing the
123
+ > substitution discards the producer's intended glyph and pulls in a
124
+ > generic color emoji. Setting the flag to `false` makes the engine respect
125
+ > the embedded/substituted font when it covers the codepoint.
126
+ > - **`ubq://defaultEmojiFontFileUri = <CDN URL>`** — `@cesdk/node` ships
127
+ > only `assets/core/`, not `assets/emoji/NotoColorEmoji.ttf`. Even with
128
+ > `forceSystemEmojis=false`, true color emoji (🧐, 🎉, …) that no embedded
129
+ > text font covers still need a working emoji font URI, or
130
+ > `engine.block.export(page, "image/png")` aborts with `FILE_FETCH_FAILED`
131
+ > for the engine's synthesised local-file URL. Point the engine at the
132
+ > IMG.LY-hosted preset, or self-host the file and supply your own URI /
133
+ > `bundle://` path.
134
+ >
135
+ > ```js
136
+ > engine.editor.setSettingBool("ubq://forceSystemEmojis", false);
137
+ > engine.editor.setSettingString(
138
+ > "ubq://defaultEmojiFontFileUri",
139
+ > "https://cdn.img.ly/assets/v4/emoji/NotoColorEmoji.ttf",
140
+ > );
141
+ > ```
142
+ >
143
+ > See the [CE.SDK Emojis guide](https://img.ly/docs/cesdk/node/text/emojis-510651/)
144
+ > for the full set of options. Browser consumers initialised with the
145
+ > default IMG.LY-CDN `baseURL` already get the emoji font for free, and most
146
+ > integrations also want `forceSystemEmojis=false` for the same
147
+ > embedded-font-respect reason.
148
+
149
+ ```js
150
+ // index.mjs
151
+ import CreativeEngine from "@cesdk/node";
152
+ import { promises as fs } from "fs";
153
+ import { PDFParser, addGfontsAssetLibrary } from "@imgly/pdf-importer";
154
+
155
+ async function main() {
156
+ const engine = await CreativeEngine.init({
157
+ license: "YOUR_LICENSE",
158
+ });
159
+
160
+ // Respect embedded fonts for emoji-class codepoints (♥, ★, …) and
161
+ // give true color emoji a working font URI — see the prerequisite
162
+ // note above.
163
+ engine.editor.setSettingBool("ubq://forceSystemEmojis", false);
164
+ engine.editor.setSettingString(
165
+ "ubq://defaultEmojiFontFileUri",
166
+ "https://cdn.img.ly/assets/v4/emoji/NotoColorEmoji.ttf",
167
+ );
168
+
169
+ await addGfontsAssetLibrary(engine);
170
+
171
+ const pdfBuffer = await fs.readFile("./document.pdf");
172
+ const parser = await PDFParser.fromFile(engine, pdfBuffer.buffer);
173
+ await parser.parse();
174
+
175
+ const image = await engine.block.export(
176
+ engine.block.findByType("//ly.img.ubq/page")[0],
177
+ "image/png"
178
+ );
179
+ const imageBuffer = await image.arrayBuffer();
180
+ await fs.writeFile("./example.png", Buffer.from(imageBuffer));
181
+
182
+ engine.dispose();
183
+ }
184
+ main();
185
+ ```
186
+
187
+ ## Issues
188
+
189
+ If you encounter any issues or have questions, please don't hesitate to contact us at support@img.ly.
190
+
191
+ ## Limitations and Unsupported Features
192
+
193
+ The PDF importer has some limitations and unsupported features that you should be aware of:
194
+
195
+ 1. **Linked Images**
196
+
197
+ - Only embedded images are supported. External image references are not resolved.
198
+
199
+ 2. **Font Support**
200
+
201
+ - If a font name is not available as a typeface asset source, it will be replaced with fallback fonts.
202
+
203
+ 3. **Complex Vector Paths**
204
+
205
+ - Some complex clipping paths or compound shapes may experience minor distortion.
206
+
207
+ 4. **Annotations and Forms**
208
+
209
+ - PDF annotations, form fields, and interactive elements are not imported.
210
+
211
+ 5. **Transparency Groups**
212
+
213
+ - Advanced transparency group blending modes may not be fully reproduced.
214
+
215
+ 6. **Image SMask Compositing**
216
+
217
+ - When a PDF image has an SMask (soft mask / per-pixel alpha), the alpha channel is composited into the image data and output as an RGBA PNG. This means JPEG images with an SMask lose the JPEG pass-through optimization and are decoded then re-encoded as PNG, which increases file size.
218
+
219
+ ## Contributing
220
+
221
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) for development setup, adding test samples, and the regression workflow.
222
+
223
+ ## License
224
+
225
+ The software is free for use under the AGPL License.
@@ -0,0 +1,300 @@
1
+ // Generated by dts-bundle-generator v9.5.1
2
+
3
+ import CreativeEngine from '@cesdk/engine';
4
+ import { Font, Typeface } from '@cesdk/engine';
5
+
6
+ export interface TypefaceParams {
7
+ family: string;
8
+ style: Font["style"];
9
+ weight: Font["weight"];
10
+ }
11
+ export type TypefaceResolver = (fontParameters: TypefaceParams, engine: CreativeEngine) => Promise<FontResolverResult | null>;
12
+ export declare function addGfontsAssetLibrary(engine: CreativeEngine): Promise<void>;
13
+ export interface FontResolverResult {
14
+ typeface: Typeface;
15
+ font: Font;
16
+ substitutedFrom?: string;
17
+ }
18
+ export type WarningSeverity = "error" | "warning" | "info";
19
+ export interface WarningDefinition {
20
+ /** Default + sole severity. Call sites do not override. */
21
+ severity: WarningSeverity;
22
+ /**
23
+ * Template with `{name}` placeholders. Renderer throws if a placeholder
24
+ * has no matching key in `params` — guards against silent typos.
25
+ */
26
+ template: string;
27
+ }
28
+ export interface LogMessage<TCode extends string = string> {
29
+ /** Stable machine-readable identifier — never rename once shipped. */
30
+ code: TCode;
31
+ /** Inherited from the code definition. */
32
+ type: WarningSeverity;
33
+ /** Human-readable rendered string. */
34
+ message: string;
35
+ /** Raw structured fields — superset of all `{name}` placeholders. */
36
+ params: Record<string, unknown>;
37
+ }
38
+ /**
39
+ * Generic structured logger. Severity is registry-driven (PostgreSQL
40
+ * SQLSTATE pattern) — call sites cannot override. To change severity,
41
+ * edit the registry.
42
+ */
43
+ export declare class Logger<R extends Record<string, WarningDefinition>> {
44
+ private readonly registry;
45
+ private messages;
46
+ constructor(registry: R);
47
+ emit<K extends keyof R & string>(code: K, params?: Record<string, unknown>): void;
48
+ getMessages(): LogMessage<keyof R & string>[];
49
+ }
50
+ declare const WARNING_CODES: {
51
+ readonly DOC_PAGE_COUNT: {
52
+ readonly severity: "info";
53
+ readonly template: "PDF has {count} page(s)";
54
+ };
55
+ readonly DOC_PAGE_BLOCK_COUNT: {
56
+ readonly severity: "info";
57
+ readonly template: "Page {page}: {imageCount} image, {vectorCount} vector, {textCount} text, {outlineCount} outline";
58
+ };
59
+ readonly DOC_PAGE_ROTATION_DROPPED: {
60
+ readonly severity: "warning";
61
+ 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.";
62
+ };
63
+ readonly BLOCK_KIND_UNKNOWN: {
64
+ readonly severity: "error";
65
+ readonly template: "An unknown block kind \"{kind}\" was encountered and was skipped.";
66
+ };
67
+ readonly FONT_OPENTYPE_PARSE_FAILED: {
68
+ readonly severity: "warning";
69
+ 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.";
70
+ };
71
+ readonly FONT_CMAP_AUGMENTED: {
72
+ readonly severity: "info";
73
+ readonly template: "Font \"{fontName}\" character map was augmented (cmap patches={cmapPatches}, injected glyphs={injectedGlyphs}).";
74
+ };
75
+ readonly FONT_CMAP_AUGMENT_FAILED: {
76
+ readonly severity: "warning";
77
+ readonly template: "Font \"{fontName}\" character map could not be augmented. Using the original font data \u2014 some glyphs may render incorrectly.";
78
+ };
79
+ readonly FONT_RESOLVER_MISS: {
80
+ readonly severity: "warning";
81
+ readonly template: "Font \"{fontFamily}\" could not be resolved. The text was rendered as a vector outline.";
82
+ };
83
+ readonly FONT_NOT_FOUND_IN_ASSETS: {
84
+ readonly severity: "warning";
85
+ readonly template: "Font \"{fontFamily}\" was not found in the asset library. The text was rendered as a vector outline.";
86
+ };
87
+ readonly FONT_SUBSTITUTED: {
88
+ readonly severity: "info";
89
+ readonly template: "Font \"{fontFamily}\" was substituted with \"{substitutedName}\" from the asset library.";
90
+ };
91
+ readonly FONT_REF_NOT_EXTRACTED: {
92
+ readonly severity: "error";
93
+ readonly template: "Text run references font \"{fontRef}\" which was not extracted. The text was skipped.";
94
+ };
95
+ readonly TEXT_RENDER_MODE_SKIPPED: {
96
+ readonly severity: "warning";
97
+ 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.";
98
+ };
99
+ readonly TEXT_OVERFLOW_AFTER_AUTOSIZE: {
100
+ readonly severity: "warning";
101
+ 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.";
102
+ };
103
+ readonly TEXT_CLIP_MODE_7_DROPPED: {
104
+ readonly severity: "warning";
105
+ 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.";
106
+ };
107
+ readonly IMAGE_ENCODER_MISS: {
108
+ readonly severity: "warning";
109
+ 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.";
110
+ };
111
+ readonly IMAGE_PDFJS_UNRESOLVED: {
112
+ readonly severity: "error";
113
+ readonly template: "{prefix}image placement {idLabel} at ({bboxX}, {bboxY}) {bboxW}\u00D7{bboxH} pt could not be resolved by pdf.js and was dropped.";
114
+ };
115
+ readonly IMAGE_NO_PIXELS: {
116
+ readonly severity: "warning";
117
+ readonly template: "An image element has no pixel data and was skipped.";
118
+ };
119
+ readonly IMAGE_FLIP_UNSUPPORTED: {
120
+ readonly severity: "warning";
121
+ readonly template: "Image flip could not be applied (engine version too old). The image will display without flip.";
122
+ };
123
+ readonly IMAGE_SKEW_DROPPED: {
124
+ readonly severity: "warning";
125
+ 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.";
126
+ };
127
+ readonly IMAGE_CLIPPED_NO_BYTES: {
128
+ readonly severity: "warning";
129
+ readonly template: "{prefix}a clipped image at {bbox} had no decoded bytes; the placement was dropped before combining clip shapes.";
130
+ };
131
+ readonly IMAGE_CLIP_UNUSABLE: {
132
+ readonly severity: "warning";
133
+ 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.";
134
+ };
135
+ readonly IMAGE_CLIP_EMPTY_INTERSECTION: {
136
+ readonly severity: "warning";
137
+ readonly template: "{prefix}a clipped image at {bbox} with {clipCount} clip path(s) had an empty intersection. The image was dropped.";
138
+ };
139
+ readonly IMAGE_CLIP_COMBINE_FAILED: {
140
+ readonly severity: "error";
141
+ 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.";
142
+ };
143
+ readonly IMAGE_MASK_NO_FILL_COLOR: {
144
+ readonly severity: "warning";
145
+ 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.";
146
+ };
147
+ readonly IMAGE_MASK_COMPOSITE_FAIL: {
148
+ readonly severity: "warning";
149
+ 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.";
150
+ };
151
+ readonly IMAGE_MASK_GROUP_UNSUPPORTED: {
152
+ readonly severity: "warning";
153
+ readonly template: "{prefix}a paintImageMaskXObjectGroup batch of {count} stencil paints is not supported by the importer. The masks were dropped.";
154
+ };
155
+ readonly IMAGE_MASK_REPEAT_UNSUPPORTED: {
156
+ readonly severity: "warning";
157
+ readonly template: "{prefix}a paintImageMaskXObjectRepeat tiling {count} stencil paints is not supported by the importer. The masks were dropped.";
158
+ };
159
+ readonly CLIP_DEGENERATE_FALLBACK: {
160
+ readonly severity: "warning";
161
+ 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.";
162
+ };
163
+ readonly CLIP_COMBINE_STROKE_ONLY: {
164
+ readonly severity: "warning";
165
+ 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.";
166
+ };
167
+ readonly CLIP_COMBINE_FAILED: {
168
+ readonly severity: "warning";
169
+ 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.";
170
+ };
171
+ readonly CLIP_COMBINE_TOO_FEW_INPUTS: {
172
+ readonly severity: "warning";
173
+ readonly template: "{prefix}clip combine was called with {inputCount} input block(s) (need at least 2). The owner block was emitted without its clips.";
174
+ };
175
+ readonly CLIP_COMBINE_NOT_COMBINABLE: {
176
+ readonly severity: "warning";
177
+ 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.";
178
+ };
179
+ readonly CLIP_COMBINE_THREW: {
180
+ readonly severity: "warning";
181
+ readonly template: "{prefix}clip combine of {inputCount} input block(s) failed ({reason}). The owner block was emitted without its clips.";
182
+ };
183
+ readonly COLOR_TYPE_UNSUPPORTED: {
184
+ readonly severity: "warning";
185
+ readonly template: "{prefix}{subject}color of type \"{colorType}\" is not supported. Rendered as black.";
186
+ };
187
+ readonly COLOR_SPACE_UNSUPPORTED: {
188
+ readonly severity: "warning";
189
+ readonly template: "{prefix}{elementLabel}color space \"{space}\" ({color}) at {bbox} is not supported. Rendered as black.";
190
+ };
191
+ readonly SPOT_COLOR_NO_ALTERNATE: {
192
+ readonly severity: "warning";
193
+ readonly template: "Spot color \"{name}\" has no resolvable alternate; leaving at engine default.";
194
+ };
195
+ readonly SPOT_COLOR_REGISTER_FAILED: {
196
+ readonly severity: "warning";
197
+ readonly template: "Failed to register spot color \"{name}\": {error}";
198
+ };
199
+ readonly BLEND_MODE_UNSUPPORTED: {
200
+ readonly severity: "warning";
201
+ readonly template: "Blend mode \"{mode}\" is not supported. Using \"Normal\" as the default.";
202
+ };
203
+ readonly SHADING_UNRESOLVED: {
204
+ readonly severity: "error";
205
+ 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.";
206
+ };
207
+ readonly SHADING_MESH_UNSUPPORTED: {
208
+ readonly severity: "warning";
209
+ 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.";
210
+ };
211
+ readonly SHADING_UNKNOWN_IR_TAG: {
212
+ readonly severity: "error";
213
+ readonly template: "{prefix}gradient shading reported unknown IR tag \"{tag}\" and was dropped.";
214
+ };
215
+ readonly SHADING_INSUFFICIENT_STOPS: {
216
+ readonly severity: "warning";
217
+ readonly template: "{prefix}gradient shading produced fewer than two color stops and was dropped.";
218
+ };
219
+ readonly SHADING_NO_CLIP_FALLBACK_BBOX: {
220
+ readonly severity: "info";
221
+ 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.";
222
+ };
223
+ readonly SHADING_CLIP_DEGENERATE: {
224
+ readonly severity: "warning";
225
+ readonly template: "{prefix}gradient shading clip path was degenerate and the region was dropped.";
226
+ };
227
+ readonly SHADING_RADIAL_FOCAL_DROPPED: {
228
+ readonly severity: "info";
229
+ 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.";
230
+ };
231
+ readonly SHADING_UNKNOWN_RADIAL_SUBTYPE: {
232
+ readonly severity: "error";
233
+ readonly template: "{prefix}gradient shading reported unknown RadialAxial subtype \"{kind}\" and was dropped.";
234
+ };
235
+ readonly SHADING_NO_PATTERN_REF: {
236
+ readonly severity: "error";
237
+ readonly template: "{prefix}gradient shading was emitted without a pattern reference and was dropped.";
238
+ };
239
+ };
240
+ export type PageBounds = "trim" | "media";
241
+ export type PdfLogger = Logger<typeof WARNING_CODES>;
242
+ export declare class PDFParser {
243
+ private engine;
244
+ private pdfBytes;
245
+ private fontResolver;
246
+ private pageBounds;
247
+ private logger;
248
+ private constructor();
249
+ /**
250
+ * Create a PDFParser instance from a PDF file.
251
+ *
252
+ * @param engine - CE.SDK engine instance
253
+ * @param file - PDF file as Blob, File, or ArrayBuffer
254
+ * @param options - Optional configuration
255
+ * @param options.fontResolver - Custom font resolver. Invoked per-font
256
+ * for any text run whose font bytes are not embeddable (Type3 fonts,
257
+ * restricted-embedding fonts). On hit the importer emits an editable
258
+ * text block with the resolved typeface; on miss it falls back to a
259
+ * vector-path outline with preserved text metadata
260
+ * (`ly.img.pdf-importer.text.*`). Requires `addGfontsAssetLibrary`
261
+ * to be called on the engine before parse.
262
+ * @returns A new PDFParser instance
263
+ */
264
+ static fromFile(engine: CreativeEngine, file: Blob | File | ArrayBuffer, options?: {
265
+ fontResolver?: TypefaceResolver;
266
+ /**
267
+ * Which PDF page box the emitted CE.SDK pages are sized to. Defaults
268
+ * to `"trim"` (designed page + bleed margin, the end-user view).
269
+ * `"media"` keeps pages at MediaBox (aka `page.view`) dimensions
270
+ * with children unshifted — used by the regression pixel-diff harness
271
+ * so CE.SDK exports match `pdftoppm`'s full-MediaBox reference images.
272
+ */
273
+ pageBounds?: PageBounds;
274
+ }): Promise<PDFParser>;
275
+ /**
276
+ * @deprecated Use options object instead: `fromFile(engine, file, { fontResolver })`
277
+ */
278
+ static fromFile(engine: CreativeEngine, file: Blob | File | ArrayBuffer, fontResolver?: TypefaceResolver): Promise<PDFParser>;
279
+ /**
280
+ * Parse the PDF file and emit a CE.SDK scene with all pages.
281
+ *
282
+ * Pipeline: pdf.js → IR (`extractDocument`) → line-run grouping →
283
+ * CE.SDK scene (`emitInto`). Text extraction always produces full text
284
+ * blocks: one block per line from pdf.js `getTextContent`, with adjacent
285
+ * lines that share font/size and horizontal overlap merged into a single
286
+ * multi-line block.
287
+ *
288
+ * @returns Parse result containing the scene ID and logger
289
+ */
290
+ parse(): Promise<{
291
+ scene: number;
292
+ logger: PdfLogger;
293
+ }>;
294
+ /**
295
+ * Get the logger for this parser instance.
296
+ */
297
+ getLogger(): PdfLogger;
298
+ }
299
+
300
+ export {};