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 CoDesign 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; }}
// Bleed is a setting of each page, not an export option: 3 mm outside the trim size.export async function addBleed(codesign: CoDesign): Promise<void> { await codesign.edit(`for (const page of await engine.design.findByType('page')) { await engine.design.setProps(page, { page: { marginEnabled: true, margin: { top: '3mm', right: '3mm', bottom: '3mm', left: '3mm' } } });}`);}
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.
Print#
A print file needs bleed: a strip outside the trim size that the printer cuts
off, so a background that runs into it leaves no white edge. Bleed is a
setting of each page, not an export option. A page with bleed exports in
pdf and pdfx as the trim size plus the bleed, with the trim size marked in
the PDF (its TrimBox); a page without it exports at the trim size alone.
IMG.LY’s design rules tell the model to set 3 mm of bleed for print, so say in the brief that the design is for print, with the size in millimeters: “a business card, 85 × 55 mm, for print, with 3 mm bleed”. A model can still leave it out, so set it in your code before your own export:
// Bleed is a setting of each page, not an export option: 3 mm outside the trim size.export async function addBleed(codesign: CoDesign): Promise<void> { await codesign.edit(`for (const page of await engine.design.findByType('page')) { await engine.design.setProps(page, { page: { marginEnabled: true, margin: { top: '3mm', right: '3mm', bottom: '3mm', left: '3mm' } } });}`);}Then export with export('pdfx', { pdfx }) as above. A background that should
fill the bleed has to reach the bleed’s outer edge, which the same rules ask
of the model. The quickstart kits export a PNG; for a print file,
change the format in the script’s export to 'pdfx', which gives PDF/X-4 with
the FOGRA39 profile unless you pass pdfx.
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.