Open Photoshop, InDesign, PowerPoint, PDF, and image files as editable designs, and export finished PNG, PDF, PDF/X, and HTML files.
This guide covers which files go in and out, who starts each step, and which
exports are charged. No example product runs the code on this page; it is
checked against the SDK’s types, and each function marked Yours is yours to
write.
Supported Files#
Every file format is read and written on the executor, the container from
IMG.LY’s Docker image where the CE.SDK engine runs. The executor carries the
importers (code that turns a foreign format into an editable design) and
the exporters, so you configure no format. A scene is CE.SDK’s text
document of a design (a .scene file); an .imgly file is a scene packed
with its images.
| Direction | Formats | By the model | By your code |
|---|---|---|---|
| In | .imgly, .scene, PNG, JPEG, WebP, GIF, SVG, PDF, PSD (Photoshop), IDML (InDesign), PPTX (PowerPoint) |
Starting a design from a file one of your sources holds | codesign.load (a file’s bytes or URL), or codesign.create (a template or file asset a source holds, or a copy of a design) |
| Out | PNG, JPEG, WebP, SVG, PDF, PDF/X (the PDF standard for print), HTML, .imgly |
When you pass uploadExport |
codesign.export |
| Out | The scene | No | codesign.export('scene') |
codesign is the object prepareCoDesign returns for a conversation.
Exporting a scene or an .imgly file is free, because both are the design
itself; every other file the executor exports is charged (see
Export Counting). The executor recognizes every file going
in by its content, not its name or extension.
Import#
The model imports a file your product holds. Keep the files people attach in an asset source of their own (a searchable list of files a design can use) in your plugin module, the JavaScript file your runner (the backend process that calls the SDK) sends for each conversation, as described in Bring Your Brand. The source declares the kinds of file it holds:
getSupportedMimeTypes: () => [ 'application/pdf', 'image/vnd.adobe.photoshop', 'application/vnd.adobe.indesign-idml-package'],A source that holds PDF, PSD, or IDML is never offered as a picture to place,
so keep these files apart from the pictures your photo library offers. The
model finds the file by searching your asset sources and starts a design from
it, which reads it through the source’s fetchAsset. The new design is
written at once to your design store, where your product keeps designs
(see The Design Store), and
the result names it and its version, the revision your store gives each
save. In the prompt, tell the model that the person attached a file and give
the asset’s id, so it need not search. The model can also start a design
from a template or as a copy of a saved design.
Your code imports without a model, before the first turn, with
codesign.load(bytes). It replaces a design’s content with the file; on a new
design id, its first write creates the design. It returns the design’s id, its
version, and the importer’s warnings. Put the version into the model’s
first message: every edit names the version it changes, so the model’s first
edit is refused without it.
// A person uploaded a PSD: open it as a new design, before the model's first turn.export async function openUpload( tenantId: string, chatId: string, psd: Uint8Array): Promise<{ codesign: CoDesign; firstMessage: string }> { const codesign = prepareTools(tenantId, chatId, crypto.randomUUID()); // a new design id // The format is read from the bytes. The first write creates the design. const loaded = await codesign.load(psd); const touchUps = (loaded.warnings ?? []).join('\n'); // a substituted font, a dropped effect return { codesign, firstMessage: `The person's file is open as design ${loaded.design}, at version ${loaded.version}.\n` + (touchUps === '' ? '' : `The import reported:\n${touchUps}\n`) };}The whole file, files.ts
import { googleFonts, isCoDesignError, prepareCoDesign, remoteExecutors, type AcquiredExecutor, type CoDesign, type DesignStore, type EnginePluginModule} from '@imgly/codesign-sdk';
// A person uploaded a PSD: open it as a new design, before the model's first turn.export async function openUpload( tenantId: string, chatId: string, psd: Uint8Array): Promise<{ codesign: CoDesign; firstMessage: string }> { const codesign = prepareTools(tenantId, chatId, crypto.randomUUID()); // a new design id // The format is read from the bytes. The first write creates the design. const loaded = await codesign.load(psd); const touchUps = (loaded.warnings ?? []).join('\n'); // a substituted font, a dropped effect return { codesign, firstMessage: `The person's file is open as design ${loaded.design}, at version ${loaded.version}.\n` + (touchUps === '' ? '' : `The import reported:\n${touchUps}\n`) };}
// Your own export, without a model: a print-ready PDF of the design as stored now.export async function exportForPrint( codesign: CoDesign): Promise<Uint8Array | undefined> { try { return await codesign.export('pdfx', { pdfx: { outputStandard: 'PDF/X-4', outputProfile: 'fogra39' } }); } catch (err) { // No credits, a license without CoDesign, or over the limits: the message says which. if (isCoDesignError(err) && err.kind === 'export_refused') { await tellThePerson(err.message); return undefined; } throw err; }}
function prepareTools( tenantId: string, chatId: string, designId: string): CoDesign { return prepareCoDesign({ conversation: `${tenantId}.${chatId}`, executors: remoteExecutors({ acquire, token: process.env.EXECUTOR_TOKEN }), designs: designStoreFor(tenantId), design: designId, // googleFonts() is required for PSD, IDML, PDF, and PPTX imports. plugins: [...yourPluginModules(tenantId), googleFonts()], gateway: false });}
// Yours: hands a conversation its executor (see Host the Executor).async function acquire(_req: { conversation: string;}): Promise<AcquiredExecutor | undefined> { throw new Error('yours to write');}
// Yours: your design store (see Connect Your Storage).function designStoreFor(_tenantId: string): DesignStore { throw new Error('yours to write');}
// Yours: your plugin modules with this tenant's options (see Connect Your Storage).function yourPluginModules(_tenantId: string): EnginePluginModule[] { throw new Error('yours to write');}
// Yours: shows a message in your product.async function tellThePerson(_message: string): Promise<void> { throw new Error('yours to write');}load also takes a URL, which your runner fetches itself. An address that
does not answer fails with kind fetch_failed, and the call’s signal stops
the download. Pass only addresses you checked are yours, because the runner
fetches them from inside your network; for why, see
Security.
Sketch and Adobe XD files, fonts, audio, video, and any bytes that are no
format in the table (a damaged PNG, say) are refused: your load fails with
kind unsupported_format, and the model is told “This file is not a design or
image this SDK reads.”
What happens to what a file carries:
- Fonts. The fonts of a PSD, IDML, or PDF file are looked up in your typeface sources
first, then in Google Fonts. A PowerPoint file resolves its fonts against
Google Fonts only. So
pluginsmust includegoogleFonts(): without it, every PSD, IDML, PDF, or PPTX import fails with an error that names the missing font sources. See Google Fonts. - Warnings. Each importer reports what it could not carry over, such as a
substituted font or a dropped effect, in
warnings. The model reads them as a touch-up list. Check them for your own files before you promise a client that their brand font survives an import. - Pictures. Each picture inside the file is offered to the sources that
can keep files, and pictures no source keeps stay inside the design, which
is then saved as an
.imglyarchive. Declining is usually right, because a PSD’s layers do not belong in the photo library, so your design store must accept archives; for both, see Connect Your Storage.
Export#
The model exports when you pass uploadExport to prepareCoDesign; without
it, the model cannot export. The executor renders the file, charges it,
and the SDK hands the bytes to your function, which stores them and answers
where the person gets them, as described in
Exported Files. The model
picks the format and, for PDF/X and HTML, the settings. It exports the version it last checked, and an export never changes the
design.
Your code exports with codesign.export(format), which returns the file’s
bytes as a Uint8Array and never calls uploadExport. 'scene' returns the scene string. In your
own call, only PDF/X takes options, as export('pdfx', { pdfx }). The pdfx
options are the standard (PDF/X-4, PDF/X-3, or PDF/X-1a), the output
profile (fogra39 for European coated paper, gracol for US printing, srgb,
or custom with your own profileUri), and a few more. Ask your printer which
they need:
// Your own export, without a model: a print-ready PDF of the design as stored now.export async function exportForPrint( codesign: CoDesign): Promise<Uint8Array | undefined> { try { return await codesign.export('pdfx', { pdfx: { outputStandard: 'PDF/X-4', outputProfile: 'fogra39' } }); } catch (err) { // No credits, a license without CoDesign, or over the limits: the message says which. if (isCoDesignError(err) && err.kind === 'export_refused') { await tellThePerson(err.message); return undefined; } throw err; }}What each format is:
pdfkeeps CMYK colors (the four-ink color model print uses) as CMYK.pdfxis print-ready PDF/X with IMG.LY’s defaults.htmlis one self-contained HTML file with its images embedded. Your owncodesign.export('html')renders the design’s first page. When the model exports, it can ask for the images as separate files, which gives a zip of the HTML file and its images.imglyis the editable design with its images, for a person who wants the file itself. It carries images from private sources too.mp4,wav, andm4aare not supported (see Status). The model’s request is refused with a message, and your own call fails withvideo_unsupported, before anything is charged.
The bytes travel from the executor to your runner and on to your storage, held in memory on the way, so size the runner for the largest export you allow.
Export Counting#
Every file the executor exports is charged to your IMG.LY account, whether the model or your own code asked; see Billing for what is counted and when an export is refused.
Next Steps#
- Connect Your Storage — write the design store that
keeps archives, and
uploadExport. - Bring Your Brand — add brand fonts that imports resolve first.
- Generate Designs — build a service that exports one file per requested format.