Build compositions entirely through code using the CE.SDK Engine for automation, batch processing, and headless rendering.
CE.SDK provides a complete Engine API for building designs through code. Instead of relying on user interactions through an editor UI, you can create scenes, add blocks like text, images, and shapes, and position them programmatically. This approach enables automation workflows, batch processing, server-side rendering, and integration with custom interfaces.
This guide covers how to create a scene structure with social media dimensions, set background colors, add text with mixed styling, line shapes, images, and export the finished composition.
Initialize the Engine#
We start by declaring a Roboto typeface that includes all variants needed for mixed styling. Setting up the typeface up front means later code can toggle bold or italic without reconfiguring fonts.
// Roboto typeface with all variants for mixed stylinglet robotoBase = "https://cdn.img.ly/packages/imgly/cesdk-swift/1.74.1/assets/ly.img.typeface/fonts/Roboto"let robotoTypeface = Typeface( name: "Roboto", fonts: [ Font( uri: URL(string: "\(robotoBase)/Roboto-Regular.ttf")!, subFamily: "Regular", weight: .normal, style: .normal, ), Font( uri: URL(string: "\(robotoBase)/Roboto-Bold.ttf")!, subFamily: "Bold", weight: .bold, style: .normal, ), Font( uri: URL(string: "\(robotoBase)/Roboto-Italic.ttf")!, subFamily: "Italic", weight: .normal, style: .italic, ), Font( uri: URL(string: "\(robotoBase)/Roboto-BoldItalic.ttf")!, subFamily: "Bold Italic", weight: .bold, style: .italic, ), ],)The Engine is created once by the caller (for example try Engine(license: "<your license key>")) and passed to the function that builds the composition.
Create Scene Structure#
We create the foundation of the composition with social media dimensions (1080x1080 pixels for Instagram). A scene contains one or more pages, and pages contain the design blocks.
// Create a scene and a page with social media dimensions (1080x1080)let scene = try engine.scene.create()try engine.block.setFloat(scene, property: "scene/dpi", value: 300)
let page = try engine.block.create(.page)try engine.block.setWidth(page, value: 1080)try engine.block.setHeight(page, value: 1080)try engine.block.appendChild(to: scene, child: page)engine.scene.create() returns a scene handle. Create a page with engine.block.create(.page), set its dimensions with setWidth() and setHeight(), then attach it to the scene with appendChild(to:child:).
Set Page Background#
We set the page background using a color fill. This demonstrates how to create and assign fills to blocks.
// Set page background to a light lavender colorlet backgroundFill = try engine.block.createFill(.color)try engine.block.setColor( backgroundFill, property: "fill/color/value", color: .rgba(r: 0.94, g: 0.93, b: 0.98, a: 1.0),)try engine.block.setFill(page, fill: backgroundFill)We create a color fill using createFill(.color), set the color via setColor(_:property:color:) with the fill/color/value property, then assign the fill to the page.
Add Text Blocks#
Text blocks allow you to add and style text content. We demonstrate three different approaches to text sizing and styling.
Create Text and Set Content#
Create a text block, set its content with replaceText(), then bind the Roboto typeface we declared earlier:
// Add the main headline text with the Roboto typefacelet headline = try engine.block.create(.text)try engine.block.replaceText(headline, text: "Integrate\nCreative Editing\ninto your App")try engine.block.setFont(headline, fontFileURL: robotoTypeface.fonts[0].uri, typeface: robotoTypeface)try engine.block.setFloat(headline, property: "text/lineHeight", value: 0.78)Style Entire Text Block#
Apply styling to the entire text block using toggleBoldFont() and setTextColor():
// Apply bold weight and a black color to the whole headlineif try engine.block.canToggleBoldFont(headline) { try engine.block.toggleBoldFont(headline)}try engine.block.setTextColor(headline, color: .rgba(r: 0.0, g: 0.0, b: 0.0, a: 1.0))Enable Automatic Font Sizing#
Configure the text block to automatically scale its font size to fit within fixed dimensions:
// Fix the container size and let the font scale automaticallytry engine.block.setWidthMode(headline, mode: .absolute)try engine.block.setHeightMode(headline, mode: .absolute)try engine.block.setWidth(headline, value: 960)try engine.block.setHeight(headline, value: 300)try engine.block.setBool(headline, property: "text/automaticFontSizeEnabled", value: true)Range-based Text Styling#
Apply different styles to specific character ranges within a single text block:
// Style "in hours," — purple and italiclet inHoursRange = taglineText.range(of: "in hours,")!try engine.block.setTextColor(tagline, color: .rgba(r: 0.2, g: 0.2, b: 0.8, a: 1.0), in: inHoursRange)if try engine.block.canToggleItalicFont(tagline, in: inHoursRange) { try engine.block.toggleItalicFont(tagline, in: inHoursRange)}
// Style "not months." — black and boldlet notMonthsRange = taglineText.range(of: "not months.")!try engine.block.setTextColor(tagline, color: .rgba(r: 0.0, g: 0.0, b: 0.0, a: 1.0), in: notMonthsRange)if try engine.block.canToggleBoldFont(tagline, in: notMonthsRange) { try engine.block.toggleBoldFont(tagline, in: notMonthsRange)}Swift’s range-based overloads take a Range<String.Index> rather than integer offsets, so you can use idiomatic String.range(of:) to locate the subrange. Passing nil (or omitting the in: parameter) targets the entire string.
setTextColor(_:color:in:)— apply color to a specific character rangecanToggleBoldFont(_:in:)/toggleBoldFont(_:in:)— toggle bold styling for a rangecanToggleItalicFont(_:in:)/toggleItalicFont(_:in:)— toggle italic styling for a range
Fixed Font Size#
Set an explicit font size instead of using automatic sizing:
// Add the CTA title with an explicit font sizelet ctaTitle = try engine.block.create(.text)try engine.block.replaceText(ctaTitle, text: "Start a Free Trial")try engine.block.setFont(ctaTitle, fontFileURL: robotoTypeface.fonts[0].uri, typeface: robotoTypeface)try engine.block.setFloat(ctaTitle, property: "text/fontSize", value: 80)try engine.block.setFloat(ctaTitle, property: "text/lineHeight", value: 1.0)Add Shapes#
We create shapes using graphic blocks. CE.SDK supports rect, line, ellipse, polygon, star, and vectorPath shapes through the ShapeType enum.
Create a Shape Block#
Create a graphic block and assign a shape to it:
// Add a horizontal divider linelet dividerLine = try engine.block.create(.graphic)let lineShape = try engine.block.createShape(.line)try engine.block.setShape(dividerLine, shape: lineShape)Apply Fill to Shape#
Create a color fill and apply it to the shape:
let lineFill = try engine.block.createFill(.color)try engine.block.setColor(lineFill, property: "fill/color/value", color: .rgba(r: 0.0, g: 0.0, b: 0.0, a: 1.0))try engine.block.setFill(dividerLine, fill: lineFill)Add Images#
We add images using graphic blocks with image fills.
Create an Image Block#
Create a graphic block with a rect shape and an image fill:
// Add the IMG.LY logo imagelet logo = try engine.block.create(.graphic)let logoShape = try engine.block.createShape(.rect)try engine.block.setShape(logo, shape: logoShape)
let logoFill = try engine.block.createFill(.image)try engine.block.setString( logoFill, property: "fill/image/imageFileURI", value: "https://img.ly/static/ubq_samples/imgly_logo.jpg",)try engine.block.setFill(logo, fill: logoFill)We set the image URL via setString(_:property:value:) with the fill/image/imageFileURI property.
Position and Size Blocks#
All blocks use the same positioning and sizing APIs:
try engine.block.setContentFillMode(logo, mode: .contain)try engine.block.setWidth(logo, value: 200)try engine.block.setHeight(logo, value: 65)try engine.block.setPositionX(logo, value: 820)try engine.block.setPositionY(logo, value: 960)try engine.block.appendChild(to: page, child: logo)setWidth(_:value:)/setHeight(_:value:)— set block dimensionssetPositionX(_:value:)/setPositionY(_:value:)— set block positionsetContentFillMode(_:mode:)— control how content fills the block (.crop,.cover,.contain)appendChild(to:child:)— add the block to the page hierarchy
Export the Composition#
We export the finished composition using the engine API.
Export Using the Engine API#
engine.block.export(_:mimeType:options:) returns the rendered bytes as Data (aliased as Blob):
// Export the composition as a PNGlet options = ExportOptions(targetWidth: 1080, targetHeight: 1080)let blob = try await engine.block.export(page, mimeType: .png, options: options)Write to File System#
Write the returned Data to disk using write(to:):
// Write the exported data to disklet outputURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("composition.png")try blob.write(to: outputURL)Troubleshooting#
- Blocks not appearing: Verify that
appendChild(to:child:)attaches blocks to the page. Blocks must be part of the scene hierarchy to render. - Text styling not applied: Verify character ranges are correct for range-based APIs. Swift uses
Range<String.Index>, which correctly handles multi-byte characters when you locate the subrange withString.range(of:). - Image stretched: Use
setContentFillMode(_:mode: .contain)to maintain the image’s aspect ratio. - Export fails: Verify that page dimensions are set before export. The export requires valid dimensions.
- Typeface missing variants: If
canToggleBoldFont(_:)returnsfalse, the configuredTypefaceis missing a boldFontentry with the matching style. Check that each weight/style combination has aFontin the typeface.
Next Steps#
- Layer Management — Control block stacking and organization
- Positioning and Alignment — Precise block placement
- Group and Ungroup — Group blocks for unified transforms
- Blend Modes — Control how blocks interact visually
- Export — Export options and formats