npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@imgly/psd-exporter

v0.1.0

Published

Export CE.SDK scenes as Adobe Photoshop PSD files

Readme

@imgly/psd-exporter

Export CE.SDK scenes as Adobe Photoshop PSD files.

Run the exporter in Node.js. Its native dependencies require a server runtime; browser applications need a separately implemented Node.js endpoint.

Installation

npm install @imgly/[email protected] @cesdk/[email protected]

Quick Start

import CreativeEngine from '@cesdk/node';
import { exportPsd } from '@imgly/psd-exporter';
import fs from 'fs/promises';

const engine = await CreativeEngine.init({ license: 'YOUR_LICENSE' });
await engine.scene.loadFromArchiveURL('https://example.com/scene.zip');

const { psds, logger } = await exportPsd(engine); // one PSD per page
for (const [index, { psd }] of psds.entries()) {
  await fs.writeFile(`./page-${index + 1}.psd`, psd);
}
for (const message of logger.getMessages()) {
  console.warn(`${message.type}: ${message.code} — ${message.message}`);
}

engine.dispose();

API

exportPsd(engine)

Returns PSDExportResult:

interface PSDExportResult {
  psds: { pageName: string; psd: Uint8Array }[]; // one .psd per page
  logger: Logger; // typed diagnostics; nothing is dropped silently
}

Preserved metadata round trips — x-adobe-psd metadata

Importing a PSD through importPsdWithMetadata from @imgly/psd-importer stashes PSD data CE.SDK cannot represent — layer effects, masks, unmapped blend modes, ICC/document data, hidden layers — as x-adobe-psd/... block metadata. The exporter restores it on the way out, so import → export approaches the original file. Engine state wins for mapped properties; preserved metadata wins for unmapped ones.

Graphics remain pixel layers. Their current fill appearance wins over an original PSD solid-color fill, including color edits, disabled fills and replacement gradients; the original editable fill is not restored over those pixels. Imported block identities retain the metadata association through reordering, additional layers and scene serialization, including same-named layers. Legacy scenes without those identities restore only unambiguous matches. Original effects, document resources and skipped layers do not attach to unrelated new pages with matching names or counts. Legacy scenes retain document data on their sole page; with multiple pages, only one matching original snapshot may claim it. Ambiguous page matches and copied identities are skipped with a METADATA_UNRESTORABLE warning. Modern import provenance survives removing all original layers or pages, so fresh matching artwork cannot inherit their metadata. Empty and hidden-only original pages retain document data.

For Node.js, install @imgly/psd-importer and pngjs and provide the PNG encoder as the third argument:

import {
  importPsdWithMetadata,
  createPNGJSEncodeBufferToPNG,
  clearPreserved
} from '@imgly/psd-importer';
import { PNG } from 'pngjs';

const pngEncoder = createPNGJSEncodeBufferToPNG(PNG);
await importPsdWithMetadata(engine, psdBytes, pngEncoder);

The sidecar is opt-out: pass { metadata: false } as the fourth argument to import without it (the saved .imgly then carries no x-adobe-psd data), or strip it from an existing scene with clearPreserved(engine, sceneBlock) before saving. This clears the document sidecar; clearing the imported blocks as well removes their identity markers.

Imported layer metadata follows its stable identity through renaming, reordering and saved scenes. Copied or ambiguous identities are refused with METADATA_UNRESTORABLE so metadata cannot move to the wrong layer.

await importPsdWithMetadata(engine, psdBytes, pngEncoder, { metadata: false });

What is exported

Layers (raster + native text with per-run fonts/weights/styles/colors/ sizes, tracking, leading, caps, alignment, basic underline and strikethrough), groups, all 27 CE.SDK blend modes (1:1 PSD names), opacity, visibility, rotation/flip, the page fill as a Background layer, and a flattened composite preview. RGB 8-bit output; CMYK/spot appearance is preserved visually, spot identity only via metadata. Safe spot fills carry a pixel fingerprint: re-import restores their editable identity only while their raster is unchanged. Photoshop recoloring, erasing or new masks retain the edited raster.

Blocks marked excluded from export, and their descendants, are omitted before layer creation or font loading. Excluded page, stack or scene ancestors keep the one-file-per-page result with empty content matching the native preview. Included hidden layers retain their hidden state.

New PSD documents record the scene DPI as their print resolution. Imported documents retain their preserved original resolution metadata. The bottom Background layer retains page fill and visible page stroke paint, including strokes on pages with disabled fills. Isolated child rasters exclude this page paint so the border is not repeated over content. Named spot page fills retain their name, tint and base RGB definition in the Background layer's XMP carrier. Re-import restores the spot definition and preserves the composed Background pixels; METADATA_UNRESTORABLE with reason spot-color-fill-not-swappable reports that the raster remains in place. After conversion at the scene DPI, each page dimension must be at least one pixel. Invalid dimensions or positive dimensions below this minimum cause a RangeError before rendering. Fractional page dimensions round to the nearest pixel, with both the background and composite rasters matching the PSD header.

Positive subpixel blocks and transformed strokes retain their full native paint as pixel layers. Their bounds follow the isolated page render on the same pixel grid as the composite, including antialiased edges and miter corners. Subpixel spot fills retain their raster on re-import. Genuinely zero-size graphic frames remain empty. Fractional pixel origins or dimensions and transformed graphics also use page-space paint, retaining edge coverage instead of rounding their placement. Spot fills affected by this geometry or fractional-page normalization retain their raster on re-import. Nonvisual children, including soundtracks, are skipped with UNSUPPORTED_BLOCK_TYPE diagnostics. Graphics with an enabled positive-width stroke retain their exact page-space paint in every stroke position, including fractional widths, dashes and caps. Layer bounds follow the rendered coverage instead of rounded stroke overhang.

Imported XMP packets are preserved. Spot-color carriers merge into compatible RDF metadata; unsupported packets retain their original content with a diagnostic. Preserved group effects restore only when a native group matches all its original imported leaves. Other group matches emit a diagnostic without reconstructing the original hierarchy.

Editable text leading includes the font's line gap when the engine's features/fontLineGapEnabled setting is enabled, preserving baseline spacing. Editable paragraph text also retains its first baseline in Photoshop. The Photoshop layout box compensates for the engine's first-line ascender versus Photoshop's cap-height placement, including mixed first-line fonts and sizes and enabled font line gaps; the separate imported frame remains unchanged. Photoshop paragraph boxes retain the exact native frame width, so recomposing editable text preserves its wrapping and horizontal alignment instead of joining lines inside a wider box. Otherwise editable text that actually clips overflow lines retains only its visible paint as a pixel layer with TEXT_OVERFLOW_RASTERIZED. Text that fits its frame, or has overflow clipping disabled, keeps its supported editable form. Editable text with overflow clipping disabled also retains all visible glyphs in its cached layer pixels, so choosing to keep the rendering in Photoshop does not crop lines to the smaller text frame. Its editable frame stays unchanged. The Photoshop layout box also reserves enough local leading for every visible unclipped line, preserving those lines when Photoshop updates the text. Its width, first baseline and separate imported frame remain unchanged. When formatting requires a pixel layer and overflow clipping is disabled, the layer retains the complete visible page-space paint beyond its text frame, including glyphs, decorations, strokes and shadows. Its existing formatting diagnostic explains the unsupported formatting.

Drop shadows remain in the raster appearance, including pixels outside the object frame. Text with an enabled drop shadow is exported as a pixel layer with a TEXT_SHADOW_RASTERIZED warning to preserve its appearance; that text has an additional hidden editable alternative. Spot-color restoration retains these pixels instead of replacing a shadowed rectangle with a plain fill.

Page rotation does not move exported text: layer coordinates use the same local page frame as the native page render. Page mirrors remain visible, and export restores the original page transform even when rendering fails. Filled editable text stores glyph fill in its cached pixels and its current stroke only as FrFX, preventing Photoshop from outlining a baked stroke again. Text with a disabled fill and a visible stroke keeps its complete rendered paint as pixels with TEXT_STROKE_RASTERIZED, alongside a hidden editable alternative.

Editable text retains its current fill enablement, per-range RGBA colors and single solid layer stroke with Round corners in every stroke position. Custom dash patterns (including those overriding Solid), non-solid stroke styles, Miter or Bevel corners, or a conflict with disabled, multiple or otherwise unmapped preserved PSD strokes, rasterize the text with a TEXT_STROKE_RASTERIZED warning. This retains its current paint and unmapped original effects. An offset with no custom pattern has no dash effect; cleared or all-zero patterns with Solid remain editable.

Block and per-range text backgrounds, including backgrounds with no padding, and center or bottom vertical alignment retain their exact paint and glyph placement as pixel layers, with TEXT_BACKGROUND_RASTERIZED or TEXT_VERTICAL_ALIGNMENT_RASTERIZED warnings. Hidden alternatives retain their editable text content. Top-aligned text with supported formatting keeps its native form. Lowercase and titlecase retain their visible Unicode content as editable text with a TEXT_CASE_NORMALIZED warning when native layout confirms that the materialized content matches. The original case style becomes part of the text string. If internal font fallback or incomplete layout prevents an exact match, TEXT_CASE_RASTERIZED retains the visible paint as a pixel layer. Uppercase keeps Photoshop's native all-caps style.

Native text combines block letter spacing and each range's additional kerning as Photoshop tracking in thousandths of an em. The engine's enabled or disabled font kerning is preserved independently as Photoshop's automatic kerning flag. Different range values retain their text styles and UTF-16 boundaries, including after case normalization.

Multiline text with incompatible font metrics or sizes retains its exact baseline spacing as pixels with TEXT_LEADING_RASTERIZED when editable PSD leading cannot reproduce its layout. Matching metrics and single-line text keep their supported editable formatting. Literal tabs retain their actual positions as pixels with TEXT_TABS_RASTERIZED, including custom intervals, mixed sizes and the legacy tab layout of older supported engines.

Superscript and subscript retain their glyph scale and baseline as pixel layers with TEXT_SCRIPT_RASTERIZED. Overlines, non-solid decorations and underlines with a custom color, thickness, offset or skip-ink setting retain their appearance with TEXT_DECORATION_RASTERIZED. Paragraph gaps, different per-paragraph line heights or alignments, direction-dependent Auto alignment and list markers retain their layout with TEXT_PARAGRAPH_RASTERIZED. Hidden alternatives retain editable content and supported formatting. Default underline, strikethrough and paragraph formatting remain native text.

Text on an active curved path retains its complete glyph placement and orientation as a pixel layer with TEXT_PATH_RASTERIZED. Default or cleared paths and paragraph overrides matching the block alignment remain editable.

The first release targets the current stable CE.SDK 1.83.0. Both engine peers require version 1.83.0 or newer.

Editing difficult text in Photoshop

When a text feature cannot currently be retained faithfully as native PSD text, its original layer keeps the exact visible pixels. A second, hidden text layer named <original name> [Editable text] appears in the Editable text copies folder. The existing formatting warning reports both layer names in its metadata.

To edit, hide the original appearance layer and reveal its editable alternative. The alternative retains the complete original string (including clipped content), fonts, sizes, colors and supported character formatting. Native superscript, subscript and character outlines are retained as approximations. Path layout, custom decorations, tabs, lists, backgrounds, shadows and parent-group effects may change when Photoshop recomposes or edits this alternative. The two versions are independent; editing the text does not update the raster. Ordinary supported text stays in one visible editable layer.

The visible raster and the document preview use the native page pixel grid, avoiding resampling differences in difficult text. Original layer names, groups, metadata and clipping chains remain intact. New exports identify editable alternatives by unique PSD layer IDs in their namespaced XMP metadata. The PSD importer recognizes these alternatives even with its default hidden-layer settings; their text edits and visibility survive subsequent exports. Configure font asset sources to retain their fonts. An optional copy uses the native default and reports unresolved fonts if those sources are absent. Older or unmarked hidden copies still follow the normal hidden-layer import setting. The carrier identifies only the alternative's role; it does not synchronize the two versions or give the alternative the appearance layer's metadata.

Development

pnpm install && pnpm build
pnpm test            # unit + capability + loop + metadata suites
pnpm coverage        # branch coverage over src/lib
pnpm test:mutation   # Stryker over the pure modules

The loop and metadata suites need the PSD fixture corpus from imgly/ubq (packages/psd-importer/src/test/examples, git LFS): set IMGLY_PSD_FIXTURES_DIR. The Photoshop G6 leg runs via scripts/check-exports-photoshop.sh on macOS, then IMGLY_PHOTOSHOP_RENDERS_DIR=<photoshop-renders> pnpm test:photoshop gates every render (verified: 29/29 open and render within thresholds, Photoshop 2026). Round-trip structural parity for opacity/blend needs the paired importer branch daniel/feat-psd-importer-exporter-parity in imgly/ubq.

License

See LICENSE.md. A valid CE.SDK license is required — get one at img.ly.