Understand the three color spaces in CE.SDK and when to use each for screen or print workflows.
CE.SDK supports three color spaces: sRGB for screen display, CMYK for print workflows, and Spot Color for specialized printing. Each color space is represented as a case of the Swift Color enum, and all three work with the unified setColor() / getColor() API.
This guide covers how to choose the correct color space, define and apply colors using the Color enum, and configure spot colors with screen preview approximations. It also shows how to set the document CMYK profile, the rendering intent and black point compensation, and what each export format writes.
Color Spaces Overview#
CE.SDK represents colors as cases of the Color enum:
Color.rgba(r:g:b:a:)— sRGB color for screen displayColor.cmyk(c:m:y:k:tint:)— CMYK color for printColor.spot(name:tint:externalReference:)— Named spot color for specialized printing
Use engine.block.setColor(_:property:color:) to apply any color type to supported properties.
Supported color properties:
'fill/color/value'— Fill color of a block'stroke/color'— Stroke/outline color'dropShadow/color'— Drop shadow color'backgroundColor/color'— Background color'camera/clearColor'— Canvas clear color
sRGB Colors#
sRGB is the default color space for screen display. Create a Color.rgba value with r, g, b, a components, each in the range 0.0 to 1.0. The a (alpha) component controls transparency.
// Create a graphic block with a color filllet srgbBlock = try engine.block.create(.graphic)try engine.block.setShape(srgbBlock, shape: engine.block.createShape(.rect))let srgbFill = try engine.block.createFill(.color)try engine.block.setFill(srgbBlock, fill: srgbFill)try engine.block.appendChild(to: page, child: srgbBlock)
// Set fill color using an sRGB color (values 0.0-1.0)let srgbColor = Color.rgba(r: 0.2, g: 0.4, b: 0.9, a: 1.0)try engine.block.setColor(srgbFill, property: "fill/color/value", color: srgbColor)sRGB colors are ideal for web and digital content where the output is displayed on screens.
CMYK Colors#
CMYK is the color space for print workflows. Create a Color.cmyk value with c, m, y, k components (0.0 to 1.0) plus a tint value that scales the ink amounts (1.0 is full ink, 0.0 is no ink).
// Create another block with a CMYK colorlet cmykBlock = try engine.block.create(.graphic)try engine.block.setShape(cmykBlock, shape: engine.block.createShape(.rect))let cmykFill = try engine.block.createFill(.color)try engine.block.setFill(cmykBlock, fill: cmykFill)try engine.block.appendChild(to: page, child: cmykBlock)
// Set fill color using a CMYK color (values 0.0-1.0, tint scales ink)let cmykColor = Color.cmyk(c: 0.0, m: 0.8, y: 0.95, k: 0.0, tint: 1.0)try engine.block.setColor(cmykFill, property: "fill/color/value", color: cmykColor)On screen and in raster exports, CMYK colors convert to RGB through the document CMYK profile. A key-only black and a rich black look different, as they do on press. The tint value (0.0 to 1.0) scales the ink amounts before the conversion, so a lower tint gives a lighter color that stays opaque. A scene saved before CE.SDK 1.83 loads as Legacy and keeps the earlier formula. The section “Managed and Legacy Scenes” below explains both modes.
Spot Colors#
Spot colors are named colors used for specialized printing. Before using a spot color, you must define it with an RGB or CMYK approximation for screen preview.
Defining Spot Colors#
Use engine.editor.setSpotColor(name:r:g:b:) or engine.editor.setSpotColor(name:c:m:y:k:) to register a spot color with its screen preview approximation.
// Define a spot color with an RGB approximation for screen previewengine.editor.setSpotColor(name: "MyBrand Red", r: 0.95, g: 0.25, b: 0.21)// You can also define a spot color with a CMYK approximationengine.editor.setSpotColor(name: "MyBrand Blue", c: 1.0, m: 0.7, y: 0.0, k: 0.1)Applying Spot Colors#
Reference a defined spot color using Color.spot(name:tint:externalReference:). The tint lightens the color on screen. It does not add transparency.
// Create a block and apply the defined spot colorlet spotBlock = try engine.block.create(.graphic)try engine.block.setShape(spotBlock, shape: engine.block.createShape(.rect))let spotFill = try engine.block.createFill(.color)try engine.block.setFill(spotBlock, fill: spotFill)try engine.block.appendChild(to: page, child: spotBlock)
// Reference the spot color by name, with a tint and optional external referencelet spotColor = Color.spot(name: "MyBrand Red", tint: 1.0, externalReference: "")try engine.block.setColor(spotFill, property: "fill/color/value", color: spotColor)When rendered on screen, the spot color uses its RGB approximation. It uses the CMYK approximation only when no RGB approximation is set. During PDF export, spot colors are saved as a Separation Color Space that preserves print information.
Applying Stroke Colors#
Strokes support all three color spaces. Enable the stroke, set its width, then apply a color using the 'stroke/color' property.
// Enable stroke and apply a stroke color using sRGBtry engine.block.setStrokeEnabled(srgbBlock, enabled: true)try engine.block.setStrokeWidth(srgbBlock, width: 4)try engine.block.setColor(srgbBlock, property: "stroke/color", color: .rgba(r: 0.1, g: 0.2, b: 0.5, a: 1.0))
// Apply a CMYK stroke colortry engine.block.setStrokeEnabled(cmykBlock, enabled: true)try engine.block.setStrokeWidth(cmykBlock, width: 4)let cmykStroke = Color.cmyk(c: 0.0, m: 0.5, y: 0.6, k: 0.2, tint: 1.0)try engine.block.setColor(cmykBlock, property: "stroke/color", color: cmykStroke)
// Apply a spot color stroke with reduced tinttry engine.block.setStrokeEnabled(spotBlock, enabled: true)try engine.block.setStrokeWidth(spotBlock, width: 4)try engine.block.setColor(spotBlock, property: "stroke/color", color: .spot(name: "MyBrand Red", tint: 0.7))Reading Color Values#
Use engine.block.getColor(_:property:) to retrieve the current color value from a property. The returned Color enum indicates the color space through its case.
// Read back color values from a propertylet readSrgb: Color = try engine.block.getColor(srgbFill, property: "fill/color/value")let readCmyk: Color = try engine.block.getColor(cmykFill, property: "fill/color/value")let readSpot: Color = try engine.block.getColor(spotFill, property: "fill/color/value")
// The returned Color enum indicates the color space through its casefor color in [readSrgb, readCmyk, readSpot] { switch color { case let .rgba(r, g, b, a): print("sRGB: r=\(r), g=\(g), b=\(b), a=\(a)") case let .cmyk(c, m, y, k, tint): print("CMYK: c=\(c), m=\(m), y=\(y), k=\(k), tint=\(tint)") case let .spot(name, tint, externalReference): print("Spot: name=\(name), tint=\(tint), ref=\(externalReference)") @unknown default: print("Unknown color space") }}How CE.SDK Converts Colors#
To show a CMYK color on screen, CE.SDK converts it through an ICC profile, the document CMYK profile.
The engine converts a color in these steps:
- The document CMYK profile. Each scene can name one CMYK ICC profile. The profile converts CMYK colors, the CMYK approximation of a spot color, and CMYK images that have no profile of their own.
- Profile priority. The engine uses the document profile when the document names one. Otherwise, it uses the profile in the
fallbackCMYKProfileUrisetting. - A profile that does not load. When a loaded scene names a profile that does not load, the engine logs a warning and uses the fallback profile. When the fallback profile does not load, the engine does not use the bundled profile instead. CMYK colors then show with the unmanaged formula, and
loadCMYKProfile()and a conversion that needs the profile fail withCOLOR.PROFILE_MISSING. - Rendering intent and black point compensation. Both apply to every conversion through the document profile. The defaults are relative colorimetric and black point compensation on.
- What changes on screen. A key-only black and a rich black are two different blacks, as they are on press. A tinted CMYK color scales its inks. It does not fade toward white.
- Images. An image with an embedded ICC profile, RGB or CMYK, uses its own profile. The document profile does not change it. A CMYK or YCCK JPEG without a profile decodes through the document CMYK profile.
Converting Colors in Code#
engine.editor.convertColorToColorSpace(color:colorSpace:) with .sRGB or .cmyk converts through the same profile as the canvas, so a swatch that you show matches what the user sees. The call is synchronous. You cannot convert to .spotColor.
A scene that you create with engine.scene.create() is Managed, which means that it converts through the profile. Applying a template or loading a scene takes the mode stored in that file. A template or scene saved before CE.SDK 1.83 is Legacy, and this includes the templates of the demo asset library. The section “Managed and Legacy Scenes” below explains the other mode. In a Managed scene, a conversion that needs the profile fails with COLOR.PROFILE_NOT_LOADED while the profile loads. The call starts the load.
Await engine.editor.loadCMYKProfile() before you convert. Await it again after each change to the profile source: removeCMYKProfile(), a change of fallbackCMYKProfileUri, or a scene load.
The example awaits the profile and defines a logConversions helper, which logs the sRGB value of each swatch. The swatches are the CMYK fill, the CMYK stroke and the spot color MyBrand Blue. The later steps call the helper again.
try await engine.editor.loadCMYKProfile()
let swatches: [(label: String, color: Color)] = [ ("CMYK fill", cmykColor), ("CMYK stroke", cmykStroke), ("Spot MyBrand Blue", .spot(name: "MyBrand Blue", tint: 1.0)),]
func logConversions(_ step: String) throws { print(step) for swatch in swatches { let converted = try engine.editor.convertColorToColorSpace(color: swatch.color, colorSpace: .sRGB) print("\(swatch.label): \(converted)") }}
try logConversions("Bundled default profile")- sRGB to CMYK uses the reverse tables of the profile. A profile that has no reverse tables uses a simple formula.
- A spot color converts to CMYK through its CMYK approximation, when it has one.
The logged values change when the profile, the intent, black point compensation or the mode changes. The Color Conversion guide has more conversion examples.
Setting the Document CMYK Profile#
Set the document CMYK profile to the profile of your press and paper.
engine.scene.setCMYKProfile(from:)with aURLloads a profile from a URL. The call is async and atomic: the previous profile stays in effect while the new one loads, and a failure keeps it.engine.scene.setCMYKProfile(from:)withDatasets the profile from bytes that you already have. The change is in effect when the call returns.engine.scene.getCMYKProfileInfo()returns thecontentHashof the profile that the document names, also when that profile did not load. To learn if the profile is usable, awaitengine.editor.loadCMYKProfile(). The getter fails withCOLOR.DOCUMENT_PROFILE_NOT_SETwhen the document names no profile. It never reports the fallback profile.engine.scene.removeCMYKProfile()goes back to the fallback profile. WhilefallbackCMYKProfileUriis not set, that is the bundled profile. Color management stays on.- The
fallbackCMYKProfileUrisetting changes the profile for every document that names none. Set it withengine.editor.setSettingString("fallbackCMYKProfileUri", value: profileURI).
The example sets the bundled profile as the document profile and logs the content hash. The bundled profile is also the fallback profile, so the logged values stay the same. Replace the URL with the profile of your press and paper. try await engine.scene.setCMYKProfile(from:) waits for the new profile, so the example converts right after it.
let profileURL = URL(string: "bundle://ly.img.cesdk/colorprofiles/CGATS001Compat-v2-micro.icc")!try await engine.scene.setCMYKProfile(from: profileURL)let profileInfo = try engine.scene.getCMYKProfileInfo()print("Document profile hash: \(profileInfo.contentHash)")try logConversions("Document profile")Rendering Intent and Black Point Compensation#
The rendering intent selects how the profile maps colors into the destination, for example from the press to the screen:
.perceptualcompresses the full gamut, so the relations between colors stay the same..relativeColorimetric, the default, keeps the colors inside the gamut and clips the others. It maps the paper white of the profile to the white of the screen..saturationkeeps colors saturated. Use it for charts and graphics, not for photos..absoluteColorimetricdoes not map the paper white to the white of the screen. The screen shows the paper tint of the profile, so the preview simulates the paper color.
Black point compensation maps the darkest black of the profile to the darkest black of the screen. It is on by default. With the relative colorimetric intent, turning it off leaves the press black as a dark gray, so dark areas look lighter. The absolute colorimetric intent does not use black point compensation.
The example sets the .perceptual intent, turns black point compensation off and logs the conversions again.
try engine.scene.setColorRenderingIntent(.perceptual)engine.scene.setBlackPointCompensationEnabled(false)try logConversions("Perceptual intent, black point compensation off")Both settings are document state. CE.SDK saves them with the scene, and undo does not change them.
Managed and Legacy Scenes#
The scene/colorConversionMode property selects how the scene converts CMYK colors.
- A scene that you create with
engine.scene.create()isManagedand converts through the profile. - Applying a template or loading a scene takes the mode stored in that file. A template or scene saved before CE.SDK 1.83 is
Legacy, and this includes the templates of the demo asset library. - A
Legacyscene keeps the old CMYK conversion, which inverts the ink channels and blends a tint toward white. It also keeps the old decode of untagged CMYK JPEGs. So its CMYK colors and untagged CMYK JPEGs look as they did when you saved them. - A
Legacyscene never needs a profile and never fails a conversion for a missing profile. A profile that you set on aLegacyscene has no effect until the scene becomesManaged. - Spot colors use the corrected approximation and tint in both modes. A CMYK spot approximation follows the mode. So spot gradient stops and tinted spot drop shadows can look different from the saved design, also in a
Legacyscene.
Read the mode with engine.block.getEnum(_:property:).
let mode = try engine.block.getEnum(scene, property: "scene/colorConversionMode")print("Color conversion mode: \(mode)")To move a saved design to accurate colors, set the mode to Managed with engine.block.setEnum(_:property:value:). Its CMYK colors then change on screen and in raster exports, so review the design after the switch. The example switches to Legacy to show the old values, then back to Managed.
try engine.block.setEnum(scene, property: "scene/colorConversionMode", value: "Legacy")try logConversions("Legacy mode")try engine.block.setEnum(scene, property: "scene/colorConversionMode", value: "Managed")What Each Export Writes#
| Output | sRGB colors | CMYK colors | Spot colors | Images |
|---|---|---|---|---|
| PNG, JPEG, WebP | sRGB | Converted through the profile, the same as the preview | Approximation | RGB |
| DeviceRGB | DeviceRGB, converted through the profile | Separation with a DeviceRGB alternate | RGB |
- A
Legacyscene uses the earlier formula for these outputs, not the profile. - Gradient stops are always converted to RGB. This includes CMYK and spot color stops.
- The engine PDF embeds no OutputIntent and no ICC profile.
- Overprint (
setFillOverprint(_:overprint:),setStrokeOverprint(_:overprint:)) applies to spot colors in a PDF only.
Export for Print#
On this platform, the engine writes an RGB PDF. CMYK colors are written as RGB values, converted through the profile. Spot colors stay named Separations. A PDF with DeviceCMYK colors needs a Web or Node export.
To prepare a design for print:
- Set the document CMYK profile to your press condition, keep the scene
Managed, and use CMYK or spot colors. - Export the PDF with
engine.block.export(_:mimeType:)and.pdf.
The engine PDF has no OutputIntent and keeps images in RGB. The Export for Printing guide shows the next steps.
Troubleshooting#
The Error Catalog lists every COLOR.* error.
| Symptom or error | Cause | Fix |
|---|---|---|
| The CMYK colors of an old design look the same after the upgrade | It loaded as Legacy |
Set scene/colorConversionMode to Managed |
| The profile, intent or black point compensation has no effect after you apply a template | The template was saved before 1.83, so the scene is Legacy |
Set scene/colorConversionMode to Managed |
| Black areas look lighter than before | A key-only black now shows as it prints | Use a rich black, or keep the accurate preview |
COLOR.PROFILE_NOT_LOADED |
A conversion ran before the profile loaded | Await engine.editor.loadCMYKProfile() |
COLOR.PROFILE_MISSING from setCMYKProfile(from:) |
The engine cannot load the URL | Check the URL. The previous profile stays in effect |
COLOR.PROFILE_MISSING from loadCMYKProfile() or a conversion |
The fallback profile does not load, so no profile is ready | Check the URI in the error and the fallbackCMYKProfileUri setting |
COLOR.PROFILE_SPACE_MISMATCH, COLOR.PROFILE_DATA_SPACE_MISMATCH |
The profile is not a CMYK profile | Use a CMYK output profile |
COLOR.PROFILE_INVALID, COLOR.PROFILE_UNSUPPORTED_SPACE |
The bytes are not a usable ICC profile | Check the file |
COLOR.PROFILE_ASSIGNMENT_SUPERSEDED |
A later set, remove or scene load replaced a pending set | Expected. Await the latest call |
COLOR.DOCUMENT_PROFILE_NOT_SET |
getCMYKProfileInfo() ran when the document names no profile |
Set a profile, or treat it as “the document uses the fallback” |
COLOR.IMAGE_DECODE_FAILED, COLOR.IMAGE_TRANSFORM_FAILED |
The engine cannot read a CMYK image or its profile | The image loads unmanaged. Replace the file |
| A profile set from bytes is lost after a reload | The scene was saved with saveToString() |
Save an archive |
API Reference#
| Method | Description |
|---|---|
engine.block.setColor(_:property:color:) |
Set a color property on a block. Pass a Color.rgba, Color.cmyk, or Color.spot value. |
engine.block.getColor(_:property:) |
Get the current color value from a property. Returns a Color enum value. |
engine.editor.setSpotColor(name:r:g:b:) |
Define a spot color with an RGB approximation for screen preview. Components range from 0.0 to 1.0. |
engine.editor.setSpotColor(name:c:m:y:k:) |
Define a spot color with a CMYK approximation for screen preview. Components range from 0.0 to 1.0. |
engine.scene.setCMYKProfile(from:) with a URL |
Load the document CMYK profile from a URL. |
engine.scene.setCMYKProfile(from:) with Data |
Set the document CMYK profile from bytes. |
engine.scene.getCMYKProfileInfo() |
Read the content hash of the document profile. |
engine.scene.removeCMYKProfile() |
Go back to the fallback profile. |
engine.scene.getColorRenderingIntent() |
Read the rendering intent. |
engine.scene.setColorRenderingIntent(_:) |
Set the rendering intent. |
engine.scene.isBlackPointCompensationEnabled() |
Read black point compensation. |
engine.scene.setBlackPointCompensationEnabled(_:) |
Turn black point compensation on or off. |
engine.editor.loadCMYKProfile() |
Wait until a CMYK profile is ready. |
engine.editor.convertColorToColorSpace(color:colorSpace:) |
Convert a color through the document profile. |
engine.editor.setSettingString("fallbackCMYKProfileUri", value:) |
Set the profile for documents that name none. |
engine.block.getEnum(_:property:) / setEnum(_:property:value:) with scene/colorConversionMode |
Read or switch Managed and Legacy. |
| Type | Properties | Description |
|---|---|---|
Color.rgba |
r, g, b, a (0.0-1.0) |
sRGB color for screen display. Alpha controls transparency. |
Color.cmyk |
c, m, y, k, tint (0.0-1.0) |
CMYK color for print. Tint scales the ink amounts. |
Color.spot |
name, tint, externalReference |
Named color for specialized printing. |
Next Steps#
- Apply Colors — Apply colors to design elements programmatically
- CMYK Colors — Work with CMYK for print workflows
- Spot Colors — Define and manage spot colors for specialized printing
- Color Conversion
- Export for Printing
- Scenes
- Error Catalog