Search Docs
Loading...
Skip to content

Import and Export Files

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 plugins must include googleFonts(): 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 .imgly archive. 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:

  • pdf keeps CMYK colors (the four-ink color model print uses) as CMYK. pdfx is print-ready PDF/X with IMG.LY’s defaults.
  • html is one self-contained HTML file with its images embedded. Your own codesign.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.
  • imgly is the editable design with its images, for a person who wants the file itself. It carries images from private sources too.
  • mp4, wav, and m4a are not supported (see Status). The model’s request is refused with a message, and your own call fails with video_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#