Search Docs
Loading...
Skip to content

Color Basics

Understand the three color spaces in CE.SDK and when to use each for screen or print workflows.

15 mins
estimated time
GitHub

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 display
  • Color.fromCMYK(c=_, m=_, y=_, k=_, tint=_) - CMYK color for print workflows
  • Color.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 fallbackCMYKProfileUri setting.
  • 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 with COLOR.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 direct ByteBuffer. The change is in effect when the call returns.
  • engine.scene.getCMYKProfileInfo() returns the contentHash of the profile that the document names, also when that profile did not load. To learn if the profile is usable, call engine.editor.loadCMYKProfile(). The getter fails with COLOR.DOCUMENT_PROFILE_NOT_SET when the document names no profile. It never reports the fallback profile.
  • engine.scene.removeCMYKProfile() goes back to the fallback profile. While fallbackCMYKProfileUri is not set, that is the bundled profile. Color management stays on.
  • The fallbackCMYKProfileUri setting changes the profile for every document that names none. Set it with engine.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().contentHash
Log.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:

  • PERCEPTUAL compresses 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.
  • SATURATION keeps colors saturated. Use it for charts and graphics, not for photos.
  • ABSOLUTE_COLORIMETRIC does 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() is Managed and 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 Legacy scene 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 Legacy scene never needs a profile and never fails a conversion for a missing profile. A profile that you set on a Legacy scene has no effect until the scene becomes Managed.
  • 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 Legacy scene.

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
PDF DeviceRGB DeviceRGB, converted through the profile Separation with a DeviceRGB alternate RGB
  • A Legacy scene 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