Search Docs
Loading...
Skip to content

CMYK Colors

Work with CMYK colors in CE.SDK for professional print production workflows with support for color space conversion and tint control.

8 mins
estimated time
GitHub

CMYK (Cyan, Magenta, Yellow, Key/Black) is the standard color model for print production. Unlike sRGB, which is additive and designed for screens, CMYK uses subtractive color mixing to represent how inks combine on paper. Android represents CMYK values with CMYKColor, so the same color APIs can work with sRGB, CMYK, and spot colors.

This guide covers how to create CMYK colors, apply them to fills, strokes, and drop shadows, use the tint value, read colors back, convert between color spaces, and use CMYK in gradients.

Understanding CMYK Colors#

When to Use CMYK#

Use CMYK colors when preparing designs for print workflows or when a print service provider gives you CMYK values. Screen previews convert CMYK to sRGB for display, so use proofing from your production print workflow when exact output appearance matters.

A CMYK color in CE.SDK has five properties:

  • c (Cyan): 0F to 1F
  • m (Magenta): 0F to 1F
  • y (Yellow): 0F to 1F
  • k (Key/Black): 0F to 1F
  • tint: 0F to 1F (controls overall color intensity)

Creating CMYK Colors#

Create a CMYK color with Color.fromCMYK(). Every component ranges from 0F to 1F.

// CMYK components (c, m, y, k) and tint all range from 0F to 1F.
val cmykCyan = Color.fromCMYK(c = 1F, m = 0F, y = 0F, k = 0F, tint = 1F)
val cmykMagenta = Color.fromCMYK(c = 0F, m = 1F, y = 0F, k = 0F, tint = 1F)
val cmykYellow = Color.fromCMYK(c = 0F, m = 0F, y = 1F, k = 0F, tint = 1F)
val cmykBlack = Color.fromCMYK(c = 0F, m = 0F, y = 0F, k = 1F, tint = 1F)

Applying CMYK Colors to Fills#

Apply a CMYK color to a color fill with engine.block.setColor() on the fill’s "fill/color/value" property. Create the fill with FillType.Color, assign it to a block, then set the CMYK value.

val fillBlock = engine.block.create(DesignBlockType.Graphic)
engine.block.setShape(fillBlock, shape = engine.block.createShape(ShapeType.Rect))
val fill = engine.block.createFill(FillType.Color)
engine.block.setFill(fillBlock, fill = fill)
// Color fill values currently use the generic color property key.
engine.block.setColor(fill, property = "fill/color/value", value = cmykCyan)

The same setColor() method accepts RGBAColor, CMYKColor, and SpotColor values, so you do not need a separate code path for each color space.

Using the Tint Property#

The tint value scales the color’s intensity without changing its CMYK components. A tint of 1F applies the full color; 0.5F scales it down.

// Tint scales the color intensity without changing the CMYK components.
val cmykHalfMagenta = Color.fromCMYK(c = 0F, m = 1F, y = 0F, k = 0F, tint = 0.5F)
val tintedBlock = engine.block.create(DesignBlockType.Graphic)
engine.block.setShape(tintedBlock, shape = engine.block.createShape(ShapeType.Rect))
val tintedFill = engine.block.createFill(FillType.Color)
engine.block.setFill(tintedBlock, fill = tintedFill)
engine.block.setColor(tintedFill, property = "fill/color/value", value = cmykHalfMagenta)

Applying CMYK to Strokes#

Enable the stroke and set its width, then assign a CMYK color with engine.block.setStrokeColor().

val strokeBlock = engine.block.create(DesignBlockType.Graphic)
engine.block.setShape(strokeBlock, shape = engine.block.createShape(ShapeType.Rect))
engine.block.setStrokeEnabled(strokeBlock, enabled = true)
engine.block.setStrokeWidth(strokeBlock, width = 8F)
val cmykStrokeColor = Color.fromCMYK(c = 0.8F, m = 0.2F, y = 0F, k = 0.1F, tint = 1F)
engine.block.setStrokeColor(strokeBlock, color = cmykStrokeColor)

Applying CMYK to Drop Shadows#

Enable the drop shadow, configure its offset and blur radius, then assign a CMYK color with engine.block.setDropShadowColor().

val shadowBlock = engine.block.create(DesignBlockType.Graphic)
engine.block.setShape(shadowBlock, shape = engine.block.createShape(ShapeType.Rect))
engine.block.setDropShadowEnabled(shadowBlock, enabled = true)
engine.block.setDropShadowOffsetX(shadowBlock, offsetX = 8F)
engine.block.setDropShadowOffsetY(shadowBlock, offsetY = 8F)
engine.block.setDropShadowBlurRadiusX(shadowBlock, blurRadiusX = 12F)
engine.block.setDropShadowBlurRadiusY(shadowBlock, blurRadiusY = 12F)
val cmykShadowColor = cmykBlack
engine.block.setDropShadowColor(shadowBlock, color = cmykShadowColor)

Reading CMYK Colors#

engine.block.getColor() returns the shared Color type. Use Kotlin type checking to handle CMYKColor values and read their components.

val readBlock = engine.block.create(DesignBlockType.Graphic)
engine.block.setShape(readBlock, shape = engine.block.createShape(ShapeType.Rect))
val readFill = engine.block.createFill(FillType.Color)
engine.block.setFill(readBlock, fill = readFill)
val cmykOrange = Color.fromCMYK(c = 0F, m = 0.5F, y = 1F, k = 0F, tint = 1F)
engine.block.setColor(readFill, property = "fill/color/value", value = cmykOrange)
val retrievedColor = engine.block.getColor(readFill, property = "fill/color/value")
val retrievedCmyk = when (retrievedColor) {
is CMYKColor -> retrievedColor
else -> error("Expected a CMYK color, got $retrievedColor")
}
println(
"CMYK Color - C: ${retrievedCmyk.c}, M: ${retrievedCmyk.m}, " +
"Y: ${retrievedCmyk.y}, K: ${retrievedCmyk.k}, Tint: ${retrievedCmyk.tint}",
)

Converting Between Color Spaces#

Use engine.editor.convertColorToColorSpace() with ColorSpace.CMYK or ColorSpace.SRGB to convert between color spaces.

// Converting CMYK to sRGB reads the document CMYK profile, which is a resource. Load it once
// first, so the conversion does not have to handle COLOR.PROFILE_NOT_LOADED.
engine.editor.loadCMYKProfile()
val rgbBlue = Color.fromRGBA(r = 0.2F, g = 0.4F, b = 0.9F, a = 1F)
val convertedCmyk = engine.editor.convertColorToColorSpace(
color = rgbBlue,
colorSpace = ColorSpace.CMYK,
)
val cmykGreen = Color.fromCMYK(c = 0.7F, m = 0F, y = 1F, k = 0.2F, tint = 1F)
val convertedSrgb = engine.editor.convertColorToColorSpace(
color = cmykGreen,
colorSpace = ColorSpace.SRGB,
)

Conversions may not be perfectly reversible because sRGB and CMYK have different gamuts. Some sRGB colors cannot be represented exactly in CMYK and vice versa.

Using CMYK in Gradients#

CMYK colors work in gradient color stops. Create a linear gradient fill and pass GradientColorStop values whose color is a CMYK color.

val gradientBlock = engine.block.create(DesignBlockType.Graphic)
engine.block.setShape(gradientBlock, shape = engine.block.createShape(ShapeType.Rect))
val gradientFill = engine.block.createFill(FillType.LinearGradient)
engine.block.setFill(gradientBlock, fill = gradientFill)
val gradientStops = listOf(
GradientColorStop(stop = 0F, color = cmykCyan),
GradientColorStop(stop = 0.5F, color = cmykMagenta),
GradientColorStop(stop = 1F, color = cmykYellow),
)
// Gradient fills currently expose their stops through the generic property key.
engine.block.setGradientColorStops(
gradientFill,
property = "fill/gradient/colors",
colorStops = gradientStops,
)

Setting the Document CMYK Profile#

CE.SDK previews CMYK colors through an ICC profile. The bundled default profile models a US press condition. For another print condition, for example European coated paper, set the CMYK profile of the document to the profile your printer supplies.

The engine picks the CMYK profile in this order:

  1. A CMYK image with its own embedded ICC profile uses that profile, for that image only.
  2. The CMYK profile of the document, when you set one.
  3. The profile at the fallbackCMYKProfileUri setting.
  4. The bundled default profile, when that setting is empty.

The document profile and its conversion settings belong to engine.scene. They are saved with the scene and bundled into scene archives. Undo and redo do not change these properties. The fallbackCMYKProfileUri editor setting belongs to the current editor environment.

Raster exports use the document profile for CMYK-to-RGB conversion. A scene whose scene/colorConversionMode is Legacy keeps its previous conversion until you set the property to Managed.

// Load the profile from a URI. The call returns once the profile is in effect.
engine.scene.setCMYKProfile(uri = Uri.parse("https://example.com/profiles/PSOcoated_v3.icc"))
// Or pass the profile bytes directly, in a direct ByteBuffer.
val bytes = context.assets.open("PSOcoated_v3.icc").use { it.readBytes() }
val buffer = ByteBuffer.allocateDirect(bytes.size).apply {
put(bytes)
rewind()
}
engine.scene.setCMYKProfileFromData(data = buffer)
// Read what the document names, and go back to the fallback profile.
val info = engine.scene.getCMYKProfileInfo() // CMYKProfileInfo
engine.scene.removeCMYKProfile()
// Configure the conversion.
engine.scene.setColorRenderingIntent(intent = ColorRenderingIntent.PERCEPTUAL)
engine.scene.setBlackPointCompensationEnabled(enabled = false)

An assignment is atomic. While a profile loads, the previous profile stays in effect. When the profile cannot be loaded, is not a valid ICC profile, or is not a CMYK profile, the call fails and the previous profile stays in effect. engine.scene.getCMYKProfileInfo() returns the content hash of the profile the document names and throws when none is set. engine.scene.removeCMYKProfile() goes back to the fallback profile.

A profile set from bytes is saved as a buffer:// URI when you save the scene to a string, and only the same engine can read that URI. Save the scene to an archive to keep the profile bytes with the scene.

You can also set how colors outside the destination gamut are mapped, with the rendering intent, and whether black point compensation is on. The defaults are relative colorimetric intent with black point compensation.

Troubleshooting#

Colors Look Different on Screen vs. Print#

Screen previews convert CMYK to sRGB using a standard conversion. Android PDF export currently writes DeviceRGB output, so use calibrated proofing from your print workflow when exact print appearance matters.

Tint Not Having the Expected Effect#

The tint value must be between 0F and 1F. On Android, values below 1F do not lower alpha; the sRGB preview blends the color toward white and stays opaque.

API Reference#

Method Description
Color.fromCMYK(c=_, m=_, y=_, k=_, tint=_) Create a CMYK color with normalized components and tint.
engine.block.setColor(block=_, property="fill/color/value", value=_) Set a color property on a fill. Accepts any Color type.
engine.block.getColor(block=_, property="fill/color/value") Get the current color value from a property. Returns Color.
engine.editor.convertColorToColorSpace(color=_, colorSpace=_) Convert a color between ColorSpace.SRGB and ColorSpace.CMYK.
engine.block.createFill(fillType=_) Create a fill. Use FillType.Color for solid fills or FillType.LinearGradient, FillType.RadialGradient, or FillType.ConicalGradient for gradients.
engine.block.setFill(block=_, fill=_) Assign a fill to a block.
engine.block.setStrokeColor(block=_, color=_) Set the stroke color on a block.
engine.block.setDropShadowColor(block=_, color=_) Set the drop shadow color on a block.
engine.block.setGradientColorStops(block=_, property="fill/gradient/colors", colorStops=_) Set color stops on a gradient fill.
engine.scene.setCMYKProfile(uri=_) Load a CMYK ICC profile from a URI and make it the document profile
engine.scene.setCMYKProfileFromData(data=_) Make the CMYK ICC profile in a direct ByteBuffer the document profile
engine.scene.getCMYKProfileInfo() Get the document CMYK profile hash; throws if none is set
engine.scene.removeCMYKProfile() Remove the document CMYK profile and use the fallback profile
engine.scene.setColorRenderingIntent(intent=_) Set the rendering intent of CMYK conversion
engine.scene.setBlackPointCompensationEnabled(enabled=_) Turn black point compensation on or off
Type Description
CMYKColor CMYK color for print workflows. Components and tint range from 0F to 1F.
ColorSpace.CMYK / ColorSpace.SRGB Target color space for convertColorToColorSpace().
GradientColorStop Gradient stop with a color: Color and a stop: Float position.

Next Steps#

  • Spot Colors - Work with named spot colors for brand consistency and specialized printing
  • Color Conversion - Convert colors between sRGB, CMYK, and spot color spaces
  • Apply Colors - Apply colors to design elements programmatically