Build reusable design templates entirely through code for automation, batch generation, and custom template creation tools.

CE.SDK lets you create a template without starting from an existing scene. You can define the page size, add text and graphic blocks, bind text variables, mark media as a placeholder, restrict editing with scopes, and save the result for reuse.
This guide uses a promotional card template with variable-driven text and one swappable image area. The sample assumes an existing Engine instance and focuses on the scene and block APIs that create the reusable template.
Create a Blank Scene#
Create the scene with pixel units for layout and text sizing, add a page, and define the template canvas size. On Android, page dimensions are set on the page block after the scene is created.
val scene = engine.scene.create(designUnit = DesignUnit.PIXEL, fontSizeUnit = FontUnit.PIXEL)
val page = engine.block.create(DesignBlockType.Page)engine.block.setWidth(page, value = 800F)engine.block.setHeight(page, value = 1000F)engine.block.appendChild(parent = scene, child = page)Set Page Background#
Use a color fill for the page background so every generated template starts from the same base appearance.
val backgroundFill = engine.block.createFill(FillType.Color)engine.block.setColor( block = backgroundFill, property = "fill/color/value", value = Color.fromRGBA(r = 0.98F, g = 0.98F, b = 0.99F, a = 1F),)engine.block.setFill(block = page, fill = backgroundFill)The fill stores the color value, and setFill assigns that fill to the page.
Add Text Blocks#
Text blocks hold the template copy. The sample uses a bundled font, variable tokens for dynamic content, and fixed positions so the layout remains predictable.
val bundledAssetsBaseUri = "file:///android_asset/imgly-assets"val brandTypeface = Typeface( name = "Brand Sans", fonts = listOf( Font( uri = Uri.parse("$bundledAssetsBaseUri/ly.img.typeface/fonts/FiraSans/FiraSans-Regular.ttf"), subFamily = "Regular", weight = FontWeight.NORMAL, style = FontStyle.NORMAL, ), Font( uri = Uri.parse("$bundledAssetsBaseUri/ly.img.typeface/fonts/FiraSans/FiraSans-Bold.ttf"), subFamily = "Bold", weight = FontWeight.BOLD, style = FontStyle.NORMAL, ), ),)val brandRegularFont = brandTypeface.fonts.firstOrNull { it.weight == FontWeight.NORMAL && it.style == FontStyle.NORMAL} ?: error("Brand Sans must include a regular font.")
val headline = engine.block.create(DesignBlockType.Text)engine.block.replaceText(headline, text = "{{title}}")engine.block.setFont(headline, fontFileUri = brandRegularFont.uri, typeface = brandTypeface)engine.block.setTextFontSize(headline, fontSize = 72F)engine.block.setTextColor(headline, color = Color.fromHex("#171717"))engine.block.setPositionX(headline, value = 72F)engine.block.setPositionY(headline, value = 96F)engine.block.setWidth(headline, value = 656F)engine.block.setHeightMode(headline, mode = SizeMode.AUTO)engine.block.appendChild(parent = page, child = headline)
val subtitle = engine.block.create(DesignBlockType.Text)engine.block.replaceText(subtitle, text = "{{subtitle}}")engine.block.setFont(subtitle, fontFileUri = brandRegularFont.uri, typeface = brandTypeface)engine.block.setTextFontSize(subtitle, fontSize = 32F)engine.block.setTextColor(subtitle, color = Color.fromHex("#525252"))engine.block.setPositionX(subtitle, value = 72F)engine.block.setPositionY(subtitle, value = 194F)engine.block.setWidth(subtitle, value = 620F)engine.block.setHeightMode(subtitle, mode = SizeMode.AUTO)engine.block.appendChild(parent = page, child = subtitle)
val cta = engine.block.create(DesignBlockType.Text)engine.block.replaceText(cta, text = "{{cta}}")engine.block.setFont(cta, fontFileUri = brandRegularFont.uri, typeface = brandTypeface)engine.block.setTextFontSize(cta, fontSize = 36F)engine.block.setTextColor(cta, color = Color.fromHex("#171717"))engine.block.setPositionX(cta, value = 72F)engine.block.setPositionY(cta, value = 858F)engine.block.setWidth(cta, value = 400F)engine.block.setHeightMode(cta, mode = SizeMode.AUTO)engine.block.appendChild(parent = page, child = cta)Each text block is appended to the page after its content, typography, position, and sizing are configured.
Add Text Variables#
Variables populate the {{title}}, {{subtitle}}, and {{cta}} tokens in the text blocks. Set defaults while authoring the template so the design has meaningful preview content.
engine.variable.set(key = "title", value = "Summer Sale")engine.variable.set(key = "subtitle", value = "Up to 50% off all items")engine.variable.set(key = "cta", value = "Learn More")
val variableNames = engine.variable.findAll()When your app applies the template later, call engine.variable.set again with the runtime values.
Add Graphic Blocks#
Graphic blocks are the image containers in the template. Give the image block a name so your app can find it later, then assign a rectangle shape and image fill.
val imageBlock = engine.block.create(DesignBlockType.Graphic)engine.block.setName(imageBlock, name = "hero-image")engine.block.setShape(imageBlock, shape = engine.block.createShape(ShapeType.Rect))engine.block.setPositionX(imageBlock, value = 72F)engine.block.setPositionY(imageBlock, value = 300F)engine.block.setWidth(imageBlock, value = 656F)engine.block.setHeight(imageBlock, value = 500F)engine.block.setContentFillMode(imageBlock, mode = ContentFillMode.COVER)
val imageFill = engine.block.createFill(FillType.Image)engine.block.setString( block = imageFill, property = "fill/image/imageFileURI", value = "$bundledAssetsBaseUri/ly.img.image/images/sample_1.jpg",)engine.block.setFill(block = imageBlock, fill = imageFill)engine.block.appendChild(parent = page, child = imageBlock)The sample uses an Android asset URI for the preview image. In your app, replace it with a stable local or remote URI that can be resolved when the template is loaded.
Configure Placeholders#
Placeholder behavior makes the image fill act as swappable content, while placeholder controls make the containing block interactive in editor UI workflows.
val placeholderFill = engine.block.getFill(imageBlock)if (engine.block.supportsPlaceholderBehavior(placeholderFill)) { engine.block.setPlaceholderBehaviorEnabled(placeholderFill, enabled = true)}
engine.block.setPlaceholderEnabled(imageBlock, enabled = true)if (engine.block.supportsPlaceholderControls(imageBlock)) { engine.block.setPlaceholderControlsOverlayEnabled(imageBlock, enabled = true) engine.block.setPlaceholderControlsButtonEnabled(imageBlock, enabled = true)}Enable placeholder behavior on the fill, then enable placeholder interaction and controls on the graphic block.
Apply Editing Constraints#
Scopes protect the layout while keeping the image content replaceable. Defer the global scopes to block-level settings, then lock movement and resizing on the template elements.
engine.editor.setGlobalScope(key = "layer/move", globalScope = GlobalScope.DEFER)engine.editor.setGlobalScope(key = "layer/resize", globalScope = GlobalScope.DEFER)engine.editor.setGlobalScope(key = "fill/change", globalScope = GlobalScope.DEFER)
listOf(headline, subtitle, cta, imageBlock).forEach { block -> engine.block.setScopeEnabled(block = block, key = "layer/move", enabled = false) engine.block.setScopeEnabled(block = block, key = "layer/resize", enabled = false)}engine.block.setScopeEnabled(imageBlock, key = "fill/change", enabled = true)The image block keeps fill/change enabled so users or automation can replace the image without moving the placeholder.
Save the Template#
Save the finished scene as a string when assets stay externally resolvable, or as an archive when you need a portable package with accessible assets included.
engine.block.forceLoadResources(blocks = listOf(page))val templateString = engine.scene.saveToString(scene = scene)val templateArchive = engine.scene.saveToArchive(scene = scene)saveToString is compact and works well for templates stored alongside stable asset URLs. saveToArchive creates a ZIP archive that bundles assets the engine can access at save time.
Troubleshooting#
Blocks do not appear: Verify that every page, text block, and graphic block is attached with engine.block.appendChild.
Variables do not resolve: Check that each text token uses the same key as engine.variable.set, including the double curly braces in the text.
The placeholder is not interactive: Enable placeholder behavior on the fill and placeholder interaction on the graphic block.
Constraints are not enforced: Set the relevant global scope to GlobalScope.DEFER before applying block-level scope settings.
API Reference#
| Method | Purpose |
|---|---|
engine.scene.create(designUnit=DesignUnit.PIXEL, fontSizeUnit=FontUnit.PIXEL) |
Create the empty template scene with pixel measurements and pixel text sizing. |
engine.block.create(blockType=DesignBlockType.Page) |
Create the template page. |
engine.block.create(blockType=DesignBlockType.Text) |
Create text blocks for variable-driven copy. |
engine.block.create(blockType=DesignBlockType.Graphic) |
Create the image placeholder block. |
engine.block.setWidth(block=_, value=_) |
Set page or block width. |
engine.block.setHeight(block=_, value=_) |
Set page or block height. |
engine.block.setPositionX(block=_, value=_) |
Set a block’s horizontal position. |
engine.block.setPositionY(block=_, value=_) |
Set a block’s vertical position. |
engine.block.setHeightMode(block=_, mode=SizeMode.AUTO) |
Let text block height fit its content. |
engine.block.appendChild(parent=_, child=_) |
Add a page or block to the scene hierarchy. |
engine.block.createFill(fillType=FillType.Color) |
Create the page background fill. |
engine.block.createFill(fillType=FillType.Image) |
Create the image fill used by the placeholder. |
engine.block.setColor(block=_, property="fill/color/value", value=_) |
Set the background fill color. |
engine.block.setFill(block=_, fill=_) |
Assign a fill to a page or graphic block. |
engine.block.replaceText(block=_, text=_) |
Set text block content. |
engine.block.setFont(block=_, fontFileUri=_, typeface=_) |
Apply the selected font to a text block. |
engine.block.setTextFontSize(block=_, fontSize=_) |
Set text size. |
engine.block.setTextColor(block=_, color=_) |
Set text color. |
engine.variable.set(key=_, value=_) |
Set a text variable value. |
engine.variable.findAll() |
Read the variables registered in the scene. |
engine.block.setName(block=_, name=_) |
Name a block for later lookup. |
engine.block.createShape(type=ShapeType.Rect) |
Create a rectangular shape for the graphic block. |
engine.block.setShape(block=_, shape=_) |
Assign the shape to the graphic block. |
engine.block.setContentFillMode(block=_, mode=ContentFillMode.COVER) |
Crop the image to cover its placeholder bounds. |
engine.block.setString(block=_, property="fill/image/imageFileURI", value=_) |
Set the image URI on the image fill. |
engine.block.getFill(block=_) |
Read the fill block attached to the graphic block. |
engine.block.supportsPlaceholderBehavior(block=_) |
Check whether the fill supports placeholder behavior. |
engine.block.setPlaceholderBehaviorEnabled(block=_, enabled=_) |
Enable placeholder behavior on the fill. |
engine.block.setPlaceholderEnabled(block=_, enabled=_) |
Enable placeholder interaction on the graphic block. |
engine.block.supportsPlaceholderControls(block=_) |
Check whether the graphic block supports placeholder controls. |
engine.block.setPlaceholderControlsOverlayEnabled(block=_, enabled=_) |
Show the placeholder overlay. |
engine.block.setPlaceholderControlsButtonEnabled(block=_, enabled=_) |
Show the placeholder action button. |
engine.editor.setGlobalScope(key=_, globalScope=GlobalScope.DEFER) |
Defer a scope to block-level settings. |
engine.block.setScopeEnabled(block=_, key=_, enabled=_) |
Allow or deny a scope on one block. |
engine.block.forceLoadResources(blocks=_) |
Ensure referenced assets are loaded before saving. |
engine.scene.saveToString(scene=_) |
Serialize the template scene as a string. |
engine.scene.saveToArchive(scene=_) |
Save the template scene and accessible assets as an archive. |
Next Steps#
- Placeholders - Configure placeholder behavior and visual controls in depth.
- Text Variables - Implement dynamic text personalization with variables.
- Add to Template Library — Save and organize templates in an asset source for users to browse and apply from the template library.