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. On Android, create color values with the Color factory functions and apply them with typed block APIs where available.
This guide covers how to choose the correct color space, apply colors to supported properties, and define spot colors with screen preview approximations. It also explains how CE.SDK converts colors through the document CMYK profile and what each export writes.
Color Spaces Overview#
CE.SDK represents Android colors as Color values:
Color.fromRGBA(r=_, g=_, b=_, a=_)- sRGB color for screen displayColor.fromCMYK(c=_, m=_, y=_, k=_, tint=_)- CMYK color for print workflowsColor.fromSpotColor(name=_, tint=_, externalReference=_)- named spot color for specialized printing
Use engine.block.setColor() for generic color properties such as fill colors, and use typed helpers such as engine.block.setStrokeColor() or engine.block.setDropShadowColor() when Android exposes one for the property.
Supported color properties:
'fill/color/value'- Fill color of a color fill'stroke/color'- Stroke or outline color'dropShadow/color'- Drop shadow color'backgroundColor/color'- Background color
Canvas clear color is a setting, not a regular block color property. On Android, color settings accept RGBAColor values only, so use engine.editor.setSettingColor(keypath = "clearColor", value = Color.fromRGBA(...)) instead of the deprecated 'camera/clearColor' property.
sRGB Colors#
sRGB is the default color space for screen display. Create an RGBAColor with Color.fromRGBA(), using components in the range 0.0 to 1.0. The a value controls alpha transparency.
val srgbColor = Color.fromRGBA(r = 0.2F, g = 0.4F, b = 0.9F, a = 1F)engine.block.setColor(srgbFill, property = "fill/color/value", value = srgbColor)sRGB colors are best for digital outputs such as PNG, JPEG, WebP, and screen-only editor previews.
CMYK Colors#
CMYK is the color space for print workflows. Create a CMYKColor with Color.fromCMYK(), using c, m, y, k, and tint values in the range 0.0 to 1.0. The tint value scales the ink amounts before the conversion; it does not change alpha transparency. A Legacy scene blends the converted color toward white instead.
val cmykColor = Color.fromCMYK(c = 0F, m = 0.8F, y = 0.95F, k = 0F, tint = 1F)engine.block.setColor(cmykFill, property = "fill/color/value", value = cmykColor)When CE.SDK renders CMYK colors on screen, it converts them to RGB through the document CMYK profile. A key-only black and a rich black look different, as they do on press. A scene saved before CE.SDK 1.83 loads as Legacy and keeps the earlier formula. The sections from “How CE.SDK Converts Colors” on explain the profile and both modes.
Spot Colors#
Spot colors are named colors used for specialized printing. Before using a spot color on a block, register the name with an RGB or CMYK approximation so CE.SDK can preview it on screen.
Defining Spot Colors#
Use engine.editor.setSpotColor() with either an RGBAColor or a CMYKColor approximation. RGB approximations are stored as opaque colors, so getSpotColorRGB() always returns alpha 1.0; apply print tint with Color.fromSpotColor(..., tint=_).
engine.editor.setSpotColor( name = "MyBrand Red", // RGB spot-color approximations are stored as opaque colors; apply tint on the // SpotColor reference. color = Color.fromRGBA(r = 0.95F, g = 0.25F, b = 0.21F, a = 1F),)engine.editor.setSpotColor( name = "MyBrand Blue", color = Color.fromCMYK(c = 1F, m = 0.7F, y = 0F, k = 0.1F),)val storedBrandRed = engine.editor.getSpotColorRGB("MyBrand Red")val storedBrandBlue = engine.editor.getSpotColorCMYK("MyBrand Blue")check(storedBrandRed == Color.fromRGBA(r = 0.95F, g = 0.25F, b = 0.21F, a = 1F))check(storedBrandBlue == Color.fromCMYK(c = 1F, m = 0.7F, y = 0F, k = 0.1F))Applying Spot Colors#
Reference a registered spot color by name with Color.fromSpotColor(). The tint blends an RGB approximation toward white. A CMYK approximation keeps the tint as a CMYK tint, so it follows the scene conversion mode. externalReference can store the source of the spot color.
val spotColor = Color.fromSpotColor( name = "MyBrand Red", tint = 1F, externalReference = "brand-palette-v1",)engine.block.setColor(spotFill, property = "fill/color/value", value = 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, CE.SDK saves spot colors as Separation Color Space output that preserves print information.
Applying Stroke Colors#
Strokes support all three color spaces. Check engine.block.supportsStroke() before changing arbitrary blocks, then enable the stroke, set its width, and apply a color with engine.block.setStrokeColor().
if (engine.block.supportsStroke(srgbBlock)) { engine.block.setStrokeEnabled(srgbBlock, enabled = true) engine.block.setStrokeWidth(srgbBlock, width = 4F) engine.block.setStrokeColor( srgbBlock, color = Color.fromRGBA(r = 0.1F, g = 0.2F, b = 0.5F, a = 1F), )}
val cmykStroke = Color.fromCMYK(c = 0F, m = 0.5F, y = 0.6F, k = 0.2F, tint = 1F)if (engine.block.supportsStroke(cmykBlock)) { engine.block.setStrokeEnabled(cmykBlock, enabled = true) engine.block.setStrokeWidth(cmykBlock, width = 4F) engine.block.setStrokeColor(cmykBlock, color = cmykStroke)}
if (engine.block.supportsStroke(spotBlock)) { engine.block.setStrokeEnabled(spotBlock, enabled = true) engine.block.setStrokeWidth(spotBlock, width = 4F) engine.block.setStrokeColor( spotBlock, color = Color.fromSpotColor(name = "MyBrand Red", tint = 0.7F), )}Reading Color Values#
Use engine.block.getColor() to retrieve generic color properties, and use engine.block.getStrokeColor() for stroke colors. The returned Color subtype tells you whether the value is an RGBAColor, CMYKColor, or SpotColor.
val readSrgb = engine.block.getColor(srgbFill, property = "fill/color/value")val readCmyk = engine.block.getColor(cmykFill, property = "fill/color/value")val readSpot = engine.block.getColor(spotFill, property = "fill/color/value")val readStroke = engine.block.getStrokeColor(cmykBlock)
for (color in listOf(readSrgb, readCmyk, readSpot, readStroke)) { when (color) { is RGBAColor -> Log.i(TAG, "sRGB: r=${color.r}, g=${color.g}, b=${color.b}, a=${color.a}") is CMYKColor -> Log.i( TAG, "CMYK: c=${color.c}, m=${color.m}, y=${color.y}, k=${color.k}, tint=${color.tint}", ) is SpotColor -> Log.i( TAG, "Spot: name=${color.name}, tint=${color.tint}, ref=${color.externalReference}", ) }}For drop shadows, prefer the type-safe Android helpers engine.block.setDropShadowColor() and engine.block.getDropShadowColor() after confirming support with engine.block.supportsDropShadow().
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 = ColorSpace.SRGB) 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 ColorSpace.SPOT_COLOR.
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.
Call the suspend function engine.editor.loadCMYKProfile() before you convert. Call it again after each change to the profile source: engine.scene.removeCMYKProfile(), a change of fallbackCMYKProfileUri, or a scene load.
The example loads the profile and defines a local logConversions function, which logs the sRGB value of the CMYK fill, the CMYK stroke and the MyBrand Blue spot color. The later steps call the function again.
engine.editor.loadCMYKProfile()
val swatches = listOf(cmykColor, cmykStroke, Color.fromSpotColor(name = "MyBrand Blue"))
fun logConversions(step: String) { for (color in swatches) { val srgb = engine.editor.convertColorToColorSpace( color = color, colorSpace = ColorSpace.SRGB, ) as RGBAColor Log.i(TAG, "$step: r=${srgb.r}, g=${srgb.g}, b=${srgb.b}") }}
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(uri = _)loads a profile from a URI. The call is a suspend function and atomic: the previous profile stays in effect while the new one loads, and a failure keeps it.engine.scene.setCMYKProfileFromData(data = _)sets the profile from bytes that you already have. Pass a directByteBuffer. 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, callengine.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(keypath = "fallbackCMYKProfileUri", value = _).
The example loads ISOcoated_v2_eci.icc from a remote URI and logs the content hash. engine.scene.setCMYKProfile() returns after the new profile is in effect, so the example converts right after it.
engine.scene.setCMYKProfile( uri = Uri.parse( "https://cdn.jsdelivr.net/npm/@imgly/plugin-print-ready-pdfs-web@1.83.0/dist/ISOcoated_v2_eci.icc", ),)val contentHash = engine.scene.getCMYKProfileInfo().contentHashLog.i(TAG, "Document profile hash: $contentHash")logConversions("ISOcoated_v2_eci.icc")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.RELATIVE_COLORIMETRIC, 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.ABSOLUTE_COLORIMETRICdoes 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. With the example profile, the perceptual intent gives the same values with black point compensation on or off.
engine.scene.setColorRenderingIntent(ColorRenderingIntent.PERCEPTUAL)engine.scene.setBlackPointCompensationEnabled(false)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().
val mode = engine.block.getEnum(scene, property = "scene/colorConversionMode")Log.i(TAG, "Color conversion mode: $mode")To move a saved design to accurate colors, set the mode to Managed with engine.block.setEnum(). 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.
engine.block.setEnum(scene, property = "scene/colorConversionMode", value = "Legacy")logConversions("Legacy mode")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 (
engine.block.setFillOverprint(),engine.block.setStrokeOverprint()) applies to spot colors in a PDF only.
Export for Print#
On Android, the engine PDF always writes CMYK colors as RGB. Spot colors stay named Separations. A PDF with the CMYK inks of solid colors needs the exportPdfWithDeviceCMYK option, which only CE.SDK for Web and Node.js have.
To prepare a design for print on Android, set the document CMYK profile to your press condition, keep the scene Managed, and use CMYK or spot colors.
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 | Call engine.editor.loadCMYKProfile() first |
COLOR.PROFILE_MISSING from setCMYKProfile() |
The engine cannot load the URI | Check the URI. 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. Wait for 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 with engine.scene.saveToArchive() |
API Reference#
| Method | Description |
|---|---|
Color.fromRGBA(r=_, g=_, b=_, a=_) |
Create an RGBAColor. Components range from 0.0 to 1.0. |
Color.fromCMYK(c=_, m=_, y=_, k=_, tint=_) |
Create a CMYKColor. Components and tint range from 0.0 to 1.0. |
Color.fromSpotColor(name=_, tint=_, externalReference=_) |
Create a SpotColor reference to a registered spot color name. |
engine.block.setColor(block=_, property="fill/color/value", value=_) |
Set a color property on a block or fill. |
engine.block.getColor(block=_, property="fill/color/value") |
Get the current color value from a property. |
engine.editor.setSettingColor(keypath="clearColor", value=_) |
Set the canvas clear color through the settings API. Pass an RGBAColor, for example Color.fromRGBA(...). |
engine.editor.getSettingColor(keypath="clearColor") |
Get the current canvas clear color as an RGBAColor. |
engine.block.supportsStroke(block=_) |
Check whether a block supports stroke properties. |
engine.block.setStrokeEnabled(block=_, enabled=_) |
Enable or disable a block stroke. |
engine.block.isStrokeEnabled(block=_) |
Check whether a block stroke is enabled. |
engine.block.setStrokeWidth(block=_, width=_) |
Set the stroke width before applying a stroke color. |
engine.block.getStrokeWidth(block=_) |
Get the current stroke width. |
engine.block.setStrokeColor(block=_, color=_) |
Set the stroke color with the type-safe Android stroke API. |
engine.block.getStrokeColor(block=_) |
Get the stroke color with the type-safe Android stroke API. |
engine.block.supportsDropShadow(block=_) |
Check whether a block supports drop shadow properties. |
engine.block.setDropShadowColor(block=_, color=_) |
Set the drop shadow color with the type-safe Android drop-shadow API. |
engine.block.getDropShadowColor(block=_) |
Get the drop shadow color with the type-safe Android drop-shadow API. |
engine.editor.setSpotColor(name=_, color=_) |
Define or update a spot color with an RGB or CMYK screen preview approximation. |
engine.editor.findAllSpotColors() |
List registered spot color names. |
engine.editor.getSpotColorRGB(name=_) |
Get the RGB approximation for a spot color. The returned alpha is always 1.0. |
engine.editor.getSpotColorCMYK(name=_) |
Get the CMYK approximation for a spot color. |
engine.editor.removeSpotColor(name=_) |
Remove a spot color from the registry. |
engine.scene.setCMYKProfile(uri=_) |
Load the document CMYK profile from a URI. |
engine.scene.setCMYKProfileFromData(data=_) |
Set the document CMYK profile from bytes in a direct ByteBuffer. |
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(intent=_) |
Set the rendering intent. |
engine.scene.isBlackPointCompensationEnabled() |
Read black point compensation. |
engine.scene.setBlackPointCompensationEnabled(enabled=_) |
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(keypath="fallbackCMYKProfileUri", value=_) |
Set the profile for documents that name none. |
engine.block.getEnum(block=_, property="scene/colorConversionMode") |
Read the conversion mode, Managed or Legacy. |
engine.block.setEnum(block=_, property="scene/colorConversionMode", value=_) |
Switch between Managed and Legacy. |
| Type | Properties | Description |
|---|---|---|
RGBAColor |
r, g, b, a (0.0-1.0) |
sRGB color for screen display. Alpha controls transparency. |
CMYKColor |
c, m, y, k, tint (0.0-1.0) |
CMYK color for print workflows. Tint scales the ink amounts. |
SpotColor |
name, tint, externalReference |
Named color for specialized printing. Tint blends an RGB approximation toward white. |
Next Steps#
- Apply Colors - Apply colors to design elements programmatically
- CMYK Colors — Work with CMYK colors in CE.SDK for professional print production workflows with support for color space conversion and tint control.
- Spot Colors - Define and manage spot colors for specialized printing
- Color Conversion - Convert colors between color spaces
- Export for Printing - Prepare a design for print output