@imgly/idml-exporter
v0.1.0
Published
Export CE.SDK scenes as Adobe InDesign IDML files
Readme
@imgly/idml-exporter
Export CE.SDK scenes as Adobe InDesign IDML files.
Installation
npm install @imgly/[email protected] @cesdk/[email protected]Quick Start
import CreativeEngine from '@cesdk/node'; // or @cesdk/engine in the browser
import { exportIdml } from '@imgly/idml-exporter';
const engine = await CreativeEngine.init({ license: 'YOUR_LICENSE' });
await engine.scene.loadFromArchiveURL('https://example.com/scene.zip');
const { idml, logger } = await exportIdml(engine);
// Write the .idml package to disk (Node.js)
import fs from 'fs/promises';
await fs.writeFile('./design.idml', idml);
// Everything IDML cannot represent is reported, never dropped silently.
for (const message of logger.getMessages()) {
console.warn(`${message.type}: ${message.code} — ${message.message}`);
}
engine.dispose();The generated file opens directly in Adobe InDesign (validated against
Adobe's IDML schema and verified in InDesign 2026 — see
docs/capability-verification.md).
API
exportIdml(engine)
For a self-contained hand-off, exportIdmlPackage(engine, { name })
returns a zip of <name>/<name>.idml plus a Document fonts/ folder with
every font the text uses (fetched and converted to raw TTF/OTF) — Adobe's
package convention, so InDesign activates them on open. IDML itself cannot
embed fonts.
Distinct font subsets that share a family/style keep separate packaged faces.
Their internal names and Story references receive matching aliases; exact
prepared-font duplicates share one file. CFF glyph data remains unchanged and
the renamed TTF/OTF files retain valid OpenType checksums.
Standalone exportIdml emits FONT_NAMES_ALIASED when these aliases are
required: use exportIdmlPackage, or place the collectDocumentFonts files in
the adjacent Document fonts folder. Fonts installed under their original
colliding names cannot resolve the new unique Story names.
Visible letters, numbers, punctuation or symbols missing from the declared
font's Unicode cmap may use native
fallback fonts that public text runs do not report. TEXT_FONT_FALLBACK_RASTERIZED
preserves the complete native text paint as an embedded image. Covered text stays
editable, including canonically covered compositions. Missing visible bases
within combining or ZWJ sequences are detected; marks and default ignorable
characters alone do not trigger a fallback. Coverage does not require
Intl.Segmenter. If rendering
fails, TEXT_FONT_FALLBACK_RASTERIZATION_FAILED reports the possible glyph loss.
Blocks excluded from export, including descendants of excluded groups, pages or scene ancestors, are omitted before rendering or collecting fonts. Excluded pages retain their dimensions as empty page containers. Visibility remains a separate editable state for included objects.
Exports the engine's current scene — every page — as one IDML package.
const result = await exportIdml(engine);Returns
interface IDMLExportResult {
idml: Uint8Array; // The .idml package (a zip archive)
logger: Logger; // Diagnostics collected while exporting
}Log messages
logger.getMessages() returns LogMessage[]:
interface LogMessage {
type: 'info' | 'warning' | 'error';
code: WarningCode; // e.g. "BLEND_MODE_DROPPED", "EFFECTS_BAKED"
message: string;
meta?: Record<string, unknown>;
}| Code | Type | Meaning |
| --------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------- |
| FILL_ALPHA_FOLDED | info | A fill's alpha became fill-target opacity, independently of the stroke |
| GRADIENT_ALPHA_AS_FEATHER | info | Varying gradient stop alpha became an InDesign Gradient Feather |
| DEGENERATE_GRADIENT_AS_SOLID | info | Coincident linear handles or a zero radial radius became editable terminal-stop solid paint |
| EFFECTS_BAKED | info | A block's effects/blur were baked into an embedded image |
| EFFECTS_BAKE_FAILED | warning | Rendering enabled effects/blur failed; the editable object omits those effects/blur |
| BLEND_MODE_DROPPED | warning | A CE.SDK-only blend mode (LinearBurn, VividLight, …) has no IDML form |
| VIDEO_FILL_DROPPED | warning | No poster frame could be rendered for a video fill |
| UNSUPPORTED_BLOCK_TYPE | warning | A block type (e.g. audio) was skipped |
| LINE_SHAPE_RASTERIZED | warning | Native line paint became an image instead of a rectangle approximation; the line is no longer editable |
| LINE_SHAPE_RASTERIZATION_FAILED | warning | Rendering the line failed; its rectangle approximation may differ |
| UNSUPPORTED_SHAPE_TYPE | warning | A shape with no IDML form was exported as a plain rectangle |
| UNSUPPORTED_PATH_DATA | warning | A vector path with unsupported commands was exported as a plain rectangle |
| CROP_FLIP_ROTATION_APPROX | warning | Legacy code, no longer emitted: crop rotation and frame reflection are preserved separately |
| IMAGE_BYTES_UNAVAILABLE | warning | An image's bytes could not be read |
| TEXT_WITHOUT_FONT | warning | A text block's font could not be resolved; leading exported as auto |
| TEXT_CASE_NORMALIZED | info | Lowercase/titlecase became editable normal text; the original case-transform style is flattened |
| TEXT_CASE_RASTERIZED | warning | Font-dependent case could not be reproduced as editable text; native appearance became an image |
| TEXT_CASE_RASTERIZATION_FAILED | warning | Rendering font-dependent case failed; normalized editable text may have a different visible case |
| TEXT_PARAGRAPH_SPACING_RASTERIZED | warning | Mixed-font paragraph gaps preserve their terminal visual line as an image; text is no longer editable |
| TEXT_PARAGRAPH_SPACING_RASTERIZATION_FAILED | warning | Rendering mixed-font paragraph gaps failed; editable gaps use each paragraph's first font |
| TEXT_LEADING_RASTERIZED | warning | Combined ascender/descender metrics cannot be represented by character leading; native line placement became an image |
| TEXT_LEADING_RASTERIZATION_FAILED | warning | Rendering combined leading failed; individual character leading may change line placement |
| TEXT_TABS_RASTERIZED | warning | Native tab placement became an embedded image; text is no longer editable |
| TEXT_TABS_RASTERIZATION_FAILED | warning | Rendering native tabs failed; editable tab placement may differ |
| TEXT_PAINT_RASTERIZED | warning | Independent glyph paints became an embedded image; the text is no longer editable |
| TEXT_PAINT_RASTERIZATION_FAILED | warning | Rendering independent glyph paints failed; paint transparency may differ |
| UNDERLINE_METRICS_UNAVAILABLE | warning | The underline's font metrics were unavailable; custom dimensions could not be written |
| TEXT_SCRIPT_RASTERIZED | warning | Superscript/subscript became an embedded image; the text is no longer editable |
| TEXT_SCRIPT_RASTERIZATION_FAILED | warning | Rendering script styling failed; its position could not be preserved |
| SHADOW_RASTERIZED | warning | Different horizontal/vertical shadow blur radii became an embedded image; the object is no longer editable |
| SHADOW_RASTERIZATION_FAILED | warning | Rendering a nonuniform shadow failed; vertical blur uses the horizontal radius |
| SHADOW_CLIP_RASTERIZED | warning | Shape shadow clipping and complete object paint became an image; the graphic is no longer editable |
| SHADOW_CLIP_RASTERIZATION_FAILED | warning | Rendering a clipped shape shadow failed; native editable shadow paint omits shape/stroke clipping |
| SHADOW_SIZE_RASTERIZED | warning | Shadow blur outside IDML's 0..144 point range became an embedded image with complete object paint |
| SHADOW_SIZE_RASTERIZATION_FAILED | warning | Rendering an unsupported shadow blur failed; the shadow was omitted rather than writing invalid IDML |
| SHADOW_OFFSET_RASTERIZED | warning | Shadow offsets outside IDML's -1000..1000 point range became an embedded image with complete object paint |
| SHADOW_OFFSET_RASTERIZATION_FAILED | warning | Rendering unsupported shadow offsets failed; the shadow was omitted rather than writing invalid IDML |
| SHAPE_CORNERS_RASTERIZED | warning | Rounded polygon/star/vector-path appearance became an embedded image; the shape is no longer editable |
| SHAPE_CORNERS_RASTERIZATION_FAILED | warning | Rendering rounded corners failed; the shape's corners remain sharp |
| TEXT_PATH_RASTERIZED | warning | Curved text became an embedded image preserving path offset/underside placement; it is no longer editable |
| TEXT_PATH_RASTERIZATION_FAILED | warning | Rendering curved text failed; editable text uses a straight baseline |
| FILL_RULE_RASTERIZED | warning | Winding-sensitive NonZero path fill became an embedded image; the path is no longer editable |
| FILL_RULE_RASTERIZATION_FAILED | warning | Rendering NonZero path fill failed; winding-sensitive areas may differ |
| UNSUPPORTED_FILL_RASTERIZED | warning | Conical, stripe or other non-native fill paint became an embedded image |
| UNSUPPORTED_FILL_RASTERIZATION_FAILED | warning | Rendering a non-native fill failed; the fill was explicitly omitted |
| PAGE_APPEARANCE_RASTERIZED | warning | Page background effects/blur became an image; foreground objects remain separately editable |
| PAGE_APPEARANCE_RASTERIZATION_FAILED | warning | Rendering page background effects/blur failed; those effects were omitted |
| STROKE_RASTERIZED | warning | Graphic dash lengths, phase and endpoint caps became an image; the graphic is no longer editable |
| STROKE_RASTERIZATION_FAILED | warning | Rendering the graphic contour failed; native stroke attributes approximate its pattern/caps |
| PAGE_STROKE_RASTERIZED | warning | The page contour and background became an image; foreground objects remain separately editable |
| PAGE_STROKE_RASTERIZATION_FAILED | warning | Rendering the page contour failed; native rectangle attributes approximate its paint |
| PAGE_VIDEO_RASTERIZED | warning | The video page background became a still poster image; foreground objects remain separately editable |
| PAGE_VIDEO_RASTERIZATION_FAILED | warning | Rendering the video page background failed; its poster could not be preserved |
| TEXT_STROKE_RASTERIZED | warning | Glyph stroke dashes or inside alignment became an image; text is no longer editable |
| TEXT_STROKE_RASTERIZATION_FAILED | warning | Rendering glyph stroke geometry failed; editable text uses a solid centered outline |
| TEXT_DECORATION_RASTERIZED | warning | Overline and all text paint became an embedded image; the text is no longer editable |
| TEXT_DECORATION_RASTERIZATION_FAILED | warning | Rendering overline failed; editable text omits the overline |
| TEXT_RANGE_BACKGROUND_RASTERIZED | warning | Text range highlights and all text paint became an image; the text is no longer editable |
| TEXT_RANGE_BACKGROUND_RASTERIZATION_FAILED | warning | Rendering range highlights failed; editable text omits those backgrounds |
| TEXT_BACKGROUND_BLEND_RASTERIZED | warning | Text and its background became one embedded image so their blend mode is applied once; text is no longer editable |
| TEXT_BACKGROUND_BLEND_RASTERIZATION_FAILED | warning | Rendering combined text/background paints failed; separately blended editable paints may differ in color |
| TEXT_XML_RASTERIZED | warning | Text or required font names containing characters forbidden by XML 1.0 became an image preserving appearance |
| TEXT_XML_RASTERIZATION_FAILED | warning | Rendering text forbidden by XML 1.0 failed; those characters were replaced in editable text |
| XML_CHARACTERS_REPLACED | warning | A forbidden XML scalar in a name or other field became U+FFFD; diagnostic metadata identifies the field |
| XML_ID_REPLACED | warning | A metadata object ID containing forbidden XML scalars was replaced with a new unique ID |
What is exported
Clipped shape shadows use a diagnosed image fallback to preserve exclusion of the complete shape and its outer/center stroke, including translucent or hollow objects. Unclipped shadows within IDML's limits remain native. Disabled and transparent shadows do not trigger a fallback; text's inactive shadow clip setting does not trigger one either.
Conical and stripe fills, page background effects and nonstandard glyph strokes
are preserved through the renderer when IDML has no matching native paint.
Background bakes exclude copied foreground objects, which remain separately
editable. Solid glyph strokes retain centered/outside alignment and miter,
round or bevel joins as native editable IDML character attributes.
Graphic stroke patterns, custom dash arrays/phase and nondefault open-path
endpoint caps use a diagnosed image fallback. Page contours are preserved
even with their fill disabled, and video page backgrounds become still posters
when the engine runtime can decode their video. If decoding is unavailable,
PAGE_VIDEO_RASTERIZATION_FAILED warns that the poster could not be preserved;
the exporter cannot recover a frame from an unsupported decoder.
Text with a visible block background and a non-normal blend mode uses one
combined image so glyphs do not blend again against their own background.
Object opacity, glyph shadows and the containing group's appearance remain
part of that same composition.
Zero-length linear gradients and zero-radius radial gradients become their
terminal-stop solid color; positive fill and feather lengths remain nonzero
even below ordinary XML number-rounding precision.
Paragraph spacing follows each paragraph's own font metrics. Mixed-font
paragraphs with nonzero spacing use the renderer to retain the combined metrics
of their terminal visual line, including wraps. Text decorations follow the
original UTF-16 ranges across emoji and combining sequences. Fixed-size
text frames with clipping enabled retain their dimensions, as do frames with
actually clipped lines, so auto-sizing cannot reveal intentionally hidden
content. Automatically sized frames without clipped lines retain their authored
Auto width and/or height even when clipping is enabled. Unavailable clipping
observations keep the existing fixed-frame behavior. The paired importer
restores fixed-frame clipping.
XML 1.0 forbids some characters accepted by the engine: visible text and
required font names use the same diagnosed image fallback; names and other fields use U+FFFD with
field diagnostics, and invalid metadata IDs receive new unique IDs.
Object/resource IDs and spread filenames are allocated uniquely even when
imported metadata IDs collide with generated or built-in IDs, or different
page IDs produce the same sanitized filename.
- Pages → one IDML spread per page (size, name, bleed), preserving horizontal and vertical content reflection, including editable text baselines and already rasterized backgrounds
- Shapes →
Rectangle/Oval/Polygon(star, polygon, and vector paths), with corner radii Line shapes preserve native paint as diagnosed images instead of native paths. - Fills → solid RGB/CMYK, spot colors (as IDML spot swatches with tint), linear and radial gradients; varying stop alpha becomes a native InDesign Gradient Feather
- Strokes → native width, color, position and miter/round/bevel joins; patterned contours and nondefault open-path caps become diagnosed images
- Images → embedded raster (PNG/JPEG/…) and SVG, with crop scale/translation/rotation and flips, in InDesign's native pixel-space geometry
- Text → per-run fonts, weights, styles, colors, sizes, case, tracking/letter spacing, underline/strikethrough (style, weight, offset, color), alignment, per-paragraph leading, paragraph spacing, and lists
- Blend modes shared with IDML (Multiply, Screen, Overlay, …) map by name
- Effects/blur → the block's rendered appearance, including its true contour, stroke and shadow, baked into an embedded image
- Groups, page background fills, opacity, drop shadows, rotation and flips
Fonts are referenced by family name — InDesign substitutes families that are not installed on the opening machine. Images are always embedded, so packages are self-contained.
Node.js exports read local file:// images and fonts, as well as custom
resource schemes resolved by the engine. Browser exports use resolved
network, data or buffer resources.
Text remains editable when IDML can preserve its paints. Independent
translucent glyph fills and outlines, mixed run opacity, and conflicting
background or decoration paints require a rendered PNG. This fallback
preserves stroke overflow, rotation and reflections on a transparent page
canvas, at four pixels per point up to 2600 pixels on its longest side.
Object opacity and blending remain separate. The exporter reports this loss
of text editability with TEXT_PAINT_RASTERIZED.
Overline uses this full appearance fallback with TEXT_DECORATION_RASTERIZED,
preserving mixed decorations and line styles. Underline and strikethrough
remain editable, including decoration ranges after emoji and combining
sequences. Hidden objects retain their embedded pixels and exported
visibility, so revealing a baked object restores its rendered content.
Visible text range backgrounds use this fallback with
TEXT_RANGE_BACKGROUND_RASTERIZED, preserving highlight coverage across
styled runs and line breaks. A block-level background stays a separate native
rectangle and does not acquire the text's glyph shadow.
Lowercase and titlecase become editable normal text only when the warmed
native renderer confirms the same visible case. Font-fallback boundaries can
change that case; these frames preserve their actual rendered appearance with
TEXT_CASE_RASTERIZED. Intentionally clipped frames retain their fixed size
and their clipping when imported again.
Superscript and subscript also use this image fallback, with
TEXT_SCRIPT_RASTERIZED, because font-specific scale and baseline shifts
cannot be reproduced reliably by InDesign's script defaults. Effects use the
same transparent canvas so strokes retain their actual shape and placement.
Spot gradient stops and shadow colors use native Tint swatches, and spot
underlines retain their separate tint; these paints remain editable in InDesign.
Tinted CMYK shadows also retain their ink strength, with the reader resolving
their appearance through the native color profile.
CMYK fills, outlines, text backgrounds, glyph colors and underlines retain
their separate tint attributes too.
SDK 1.81's native shadow renderer paints a named spot at full ink even when its stored color has a partial tint. IDML preserves the public color/tint contract. Its minimum-version regression compares against an independently authored literal RGB shadow; current native rendering matches that same reference exactly.
Linear gradients with coincident handles render as the terminal stop's solid
color. They retain that paint and its fill transparency as an editable solid,
with DEGENERATE_GRADIENT_AS_SOLID reporting the style conversion. Very short
positive spans keep nonzero gradient and feather lengths in the XML.
Shadows with different horizontal and vertical blur radii use the same full
appearance fallback and report SHADOW_RASTERIZED. Shadow blur outside Adobe's
0..144 point range uses the complete-paint fallback and reports
SHADOW_SIZE_RASTERIZED. Values inside the range, including 144 points, remain
editable when the shadow is unclipped and symmetric. Failed effects/blur
rendering reports EFFECTS_BAKE_FAILED instead of silently discarding the paint.
Each shadow offset is converted to points before checking Adobe's inclusive
-1000..1000 range. Visible shadows outside either axis's range preserve full
paint with SHADOW_OFFSET_RASTERIZED; if rendering fails, the unsupported
shadow is omitted with SHADOW_OFFSET_RASTERIZATION_FAILED. Valid individual
offsets remain editable, including a diagonal distance greater than 1000 points.
Mixed-direction paragraphs select their base direction from the
first strong character, while retaining the World-Ready composer for shaping.
Paragraph leading uses each character range's own font metrics and size with
the paragraph's line-height multiplier, including styled paragraph breaks.
Compatible mixed fonts remain editable. If native layout combines one font's
ascender with another font's descender beyond every individual character's
leading, TEXT_LEADING_RASTERIZED preserves line placement through the full
appearance fallback.
Disabled font-pair kerning is retained as an explicit numeric zero override,
without changing tracking or ligatures.
Text containing literal tabs preserves the runtime's actual tab placement
through the full appearance fallback with TEXT_TABS_RASTERIZED. The native
global tab rule and its first-run font dependence cannot be retained by
portable editable paragraph tab stops. Text without tabs stays editable;
the exporter does not change editor-wide tab settings.
Paragraph gaps also use each paragraph's own font metrics. Mixed-font
paragraphs preserve the spacing of their terminal visual line through the
full appearance fallback with TEXT_PARAGRAPH_SPACING_RASTERIZED; paragraphs
with uniform metrics remain editable.
Each paragraph retains its horizontal alignment; Auto follows that paragraph's
base direction. Justify preserves justified word spacing with the final line
aligned to that direction. Single-line auto-sizing and tracking allowances use
the paragraph's alignment, including overrides, to retain its anchor.
Spot-color text backgrounds retain their separate tint.
Packaged fonts retain valid OpenType table checksums after renaming.
Even-sided polygons retain the engine's native orientation. Rounded polygon,
star and vector-path frames use the full appearance fallback and report
SHAPE_CORNERS_RASTERIZED; this also preserves rounded image clipping.
Text on a path uses this fallback with TEXT_PATH_RASTERIZED, preserving its
baseline offset and underside placement. Straight text and unrounded paths
remain editable. Percent-encoded data images retain their original bytes.
Compound, curved and self-intersecting NonZero paths use this fallback with
FILL_RULE_RASTERIZED to retain winding-dependent fill coverage. Simple linear
contours where both fill rules paint the same area remain editable.
Paragraph direction uses Unicode 15.1 bidi data; its license notice is included
in LICENSE.md.
Round trip with @imgly/idml-importer
Exports are designed to round-trip through
@imgly/idml-importer:
importing an exported file reproduces the source scene. The test suite
pixel-gates this per capability (most at exactly 0.000% difference) and
additionally validates every package against Adobe's IDML schema.
Round-trip parity requires @imgly/idml-importer >= 2.0.0.
Development
pnpm install
pnpm build # dist/ (ESM + CJS + bundled .d.ts)
pnpm test # round trip + synthetic + capability + schema suites
pnpm test:unit # pure unit testsThree suites need external inputs and are opt-in:
- Fixture round trip — the IDML fixture corpus from
imgly/ubq(packages/idml-importer/src/test/examples, git LFS): setIMGLY_IDML_FIXTURES_DIR. - Importer parity —
@imgly/idml-importer>= 2.0.0, orIMGLY_IMPORTER_PAIRED=1with a linked development importer. - InDesign verification — run
scripts/check-capabilities-indesign.shon macOS with InDesign, thenIMGLY_INDESIGN_RENDERS_DIR=<indesign-pages> pnpm test:indesign.
CI runs the self-contained suites: unit tests plus building the 21-capability matrix, exporting it, and validating it against Adobe's RELAX NG schema with jing.
License
See LICENSE.md. A valid CE.SDK license is required — get one at img.ly.
