IccJSON provides two command-line tools -- IccToJson and IccFromJson -- that let you convert ICC profiles to and from a human-readable JSON representation. This makes it straightforward to inspect, create, and edit ICC and iccMAX profiles using a text editor or any JSON-capable tooling.
A JSON Schema for the format is provided at docs/icc-profile.schema.json.
Editors such as VS Code can use it to provide inline validation and auto-complete.
Converts a binary ICC profile to a JSON file.
IccToJson src_profile.icc dest_profile.json [-indent=N]
-indent=N-- pretty-print with N spaces of indentation (default: 2)
Converts a JSON file back to a binary ICC profile.
IccFromJson src_profile.json dest_profile.icc [-noid]
-noid-- suppress writing the profile ID (MD5 hash) into the saved file
If the profile fails validation the binary file is still saved, but the validation report is printed so you can identify and fix issues before use.
After creating a profile with IccFromJson, use IccDumpProfile to validate it:
IccDumpProfile dest_profile.icc
The overall pass/fail result appears two lines below the Validation Report line:
grep --text -A 3 "^Validation Report" output.txt# Inspect an existing profile
IccToJson input.icc input.json
# Edit input.json in a text editor ...
# Rebuild the binary profile
IccFromJson input.json output.icc
# Validate
IccDumpProfile output.icc
Every ICC profile JSON file is wrapped in a top-level "IccProfile" key:
{
"IccProfile": {
"Header": { ... },
"Tags": [ ... ]
}
}The Header object describes the profile class, colour spaces, and metadata.
Signature fields that are zero (unset) are encoded as an empty string "". Optional non-signature fields (e.g. ProfileSubClass, SpectralPCS) are omitted when not set.
"Header": {
"ProfileVersion": "5.0",
"ProfileDeviceClass": "mntr",
"DataColourSpace": "RGB ",
"PCS": "XYZ ",
"CreationDateTime": "2024-01-15T10:30:00",
"ProfileFileSignature": "acsp",
"PrimaryPlatform": "MSFT",
"CMMFlags": {
"EmbeddedInFile": false,
"UseWithEmbeddedDataOnly": false
},
"DeviceAttributes": {
"ReflectiveOrTransparency": "reflective",
"GlossyOrMatte": "glossy",
"MediaPolarity": "positive",
"MediaColour": "colour"
},
"RenderingIntent": "Relative",
"PCSIlluminant": [0.9642, 1.0, 0.8249],
"ProfileCreator": "ICC "
}Common ProfileDeviceClass values:
| Value | Meaning |
|---|---|
scnr |
Scanner |
mntr |
Display / Monitor |
prtr |
Printer / Output |
link |
DeviceLink |
spac |
Color Space |
abst |
Abstract |
nmcl |
Named Color |
cenc |
Color Encoding |
Common DataColourSpace / PCS values: RGB , CMYK, XYZ , Lab .
For iccMAX spectral profiles, add:
"SpectralPCS": "rs0024",
"SpectralRange": { "start": 380.0, "end": 730.0, "steps": 36 }For iccMAX bispectral profiles, add:
"BiSpectralRange": { "start": 300.0, "end": 730.0, "steps": 44 }Tags is an ordered JSON array. Each element is a single-key object whose key is
the ICC tag name (e.g. "redMatrixColumnTag") and whose value holds the tag data.
"Tags": [
{
"redMatrixColumnTag": {
"data": {
"type": "XYZType",
"XYZ": [0.4361, 0.2225, 0.0139]
}
}
}
]The data object always contains a "type" field identifying the ICC tag type,
followed by the type-specific fields at the same level.
Shared tags (where two tag signatures point to the same data) are written with a
"sameAs" field referencing the key of the first occurrence:
{ "AToB1Tag": { "sameAs": "AToB0Tag" } }Private tags use a generated key ("PrivateTag_1", etc.) and carry a "sig" field
with the raw four-character signature:
{ "PrivateTag_1": { "sig": "cust", "data": { "type": "PrivateType", ... } } }Used for profileDescriptionTag (desc), copyrightTag, and similar tags.
{
"profileDescriptionTag": {
"data": {
"type": "multiLocalizedUnicodeType",
"localizedStrings": [
{ "language": "en", "country": "US", "text": "My sRGB Profile" }
]
}
}
}{
"charTargetTag": {
"data": {
"type": "utf8TextType",
"text": "My target name"
}
}
}{
"copyrightTag": {
"data": {
"type": "textType",
"text": "Copyright 2024 My Company"
}
}
}Each XYZ value is a JSON array [X, Y, Z]. The "XYZ" field is an array of such triplets.
{
"mediaWhitePointTag": {
"data": {
"type": "XYZType",
"XYZ": [0.9505, 1.0000, 1.0891]
}
}
}{
"chromaticAdaptationTag": {
"data": {
"type": "s15Fixed16ArrayType",
"values": [ 1.0479, 0.0229, -0.0502,
0.0296, 0.9904, -0.0171,
-0.0092, 0.0151, 0.7519 ]
}
}
}The "curveType" field selects one of three subtypes: "identity", "gamma", or "table".
Identity (0-entry ICC no-op):
{
"redTRCTag": {
"data": { "type": "curveType", "curveType": "identity" }
}
}Sampled identity (linear ramp with a specific number of entries — emitted instead of a full table when all entries match the ramp i/(n-1) within 16-bit quantisation tolerance):
{
"redTRCTag": {
"data": { "type": "curveType", "curveType": "identity", "size": 256 }
}
}When parsing, a "size" of 2 or more reconstructs the linear ramp m_Curve[i] = i/(size-1). When "size" is absent the 0-entry ICC identity is used.
Gamma:
{
"redTRCTag": {
"data": { "type": "curveType", "curveType": "gamma", "gamma": 2.2 }
}
}Sampled table:
{
"redTRCTag": {
"data": {
"type": "curveType",
"curveType": "table",
"table": [0.0, 0.0031, 0.0145, 0.0331, ...]
}
}
}{
"redTRCTag": {
"data": {
"type": "parametricCurveType",
"functionType": 3,
"params": [2.4, 0.9479, 0.0521, 0.0405, 0.0031]
}
}
}functionType parameter counts:
functionType |
Formula | params count |
|---|---|---|
0 |
Y = X^gamma |
1 |
1 |
Y = (aX + b)^gamma |
3 |
2 |
Y = (aX + b)^gamma + c |
4 |
3 |
sRGB-style (two-segment) | 5 |
4 |
Full CIE 122-1996 (seven-param) | 7 |
Segments cover the real line with "-infinity" / "+infinity" at the outermost boundaries.
Segment types are "FormulaSegment" or "SampledSegment".
{
"redTRCTag": {
"data": {
"type": "segmentedCurveType",
"segments": [
{
"start": "-infinity",
"end": 0.04045,
"type": "FormulaSegment",
"functionType": 0,
"parameters": [1.0, 0.07739938, 0.0125, 0.0]
},
{
"start": 0.04045,
"end": "+infinity",
"type": "FormulaSegment",
"functionType": 0,
"parameters": [2.4, 0.94786562, 0.05213438, 0.0031308]
}
]
}
}
}Segment types:
type |
Required fields |
|---|---|
FormulaSegment |
functionType, parameters |
SampledSegment |
samples |
Because ToJson/ParseJson have no access to the profile header, the encoding of the "pcs" arrays is declared explicitly via a "pcsEncoding" field:
pcsEncoding |
pcs array contents |
|---|---|
"Lab" (default) |
L* (0–100), a*, b* (−128–127) — human-readable CIE Lab |
"XYZ" |
X, Y, Z floats (0–2 range) |
"16bit" |
Raw ICC U16 integers (0–65535) |
ToJson always writes "pcsEncoding": "Lab". ParseJson defaults to "Lab" when the field is absent (backward compatible with files written before this field was added).
{
"colorantTableTag": {
"data": {
"type": "colorantTableType",
"pcsEncoding": "Lab",
"colorantTable": [
{ "name": "Cyan", "pcs": [55.11, -37.40, -5.18] },
{ "name": "Magenta", "pcs": [48.24, 74.12, -49.52] },
{ "name": "Yellow", "pcs": [89.02, -5.68, 93.14] },
{ "name": "Black", "pcs": [ 0.00, 0.00, 0.00] }
]
}
}
}{
"technologyTag": {
"data": {
"type": "signatureType",
"signature": "CRT "
}
}
}MBB (Multidimensional Black Box) LUT curve arrays (bCurves, mCurves, aCurves) use curve objects with
"type" set to "Curve", "ParametricCurve", or "SegmentedCurve".
Both CLUT data and sampled curve tables ("curveType": "table") use a "precision" field
to indicate integer encoding, matching the behaviour of iccXML:
"precision" |
Data value range | Storage |
|---|---|---|
1 |
0 - 255 | 8-bit |
2 |
0 - 65535 | 16-bit |
| (absent) | floating-point | float |
Named colour device coordinates ("deviceCoords") are always 16-bit integers (0-65535).
{
"AToB0Tag": {
"data": {
"type": "lutAtoBType",
"inputChannels": 3,
"outputChannels": 3,
"bCurves": [
{ "type": "Curve", "curveType": "identity" },
{ "type": "Curve", "curveType": "identity" },
{ "type": "Curve", "curveType": "identity" }
],
"matrix": {
"e": [ 0.4361, 0.3851, 0.1431,
0.2225, 0.7169, 0.0606,
0.0139, 0.0971, 0.7141 ]
},
"mCurves": [
{ "type": "Curve", "curveType": "identity" },
{ "type": "Curve", "curveType": "identity" },
{ "type": "Curve", "curveType": "identity" }
],
"clut": {
"gridPoints": [9, 9, 9],
"precision": 2,
"data": [0, 0, 0, 128, 64, 32, ...]
},
"aCurves": [
{ "type": "Curve", "curveType": "identity" },
{ "type": "Curve", "curveType": "identity" },
{ "type": "Curve", "curveType": "identity" }
]
}
}
}MBB curve type values:
type |
Subtype fields |
|---|---|
Curve |
curveType ("identity", "gamma", "table"), size (identity with entries), gamma, table |
ParametricCurve |
functionType, params |
SegmentedCurve |
segments array |
iccMAX profiles use multiProcessElementType tags containing an ordered pipeline
of processing elements. Each element is a flat object with a "type" discriminator
and "inputChannels" / "outputChannels" counts.
{
"AToB1Tag": {
"data": {
"type": "multiProcessElementType",
"inputChannels": 3,
"outputChannels": 3,
"elements": [
{
"type": "CurveSetElement",
"inputChannels": 3,
"outputChannels": 3,
"curves": [ ... ]
},
{
"type": "MatrixElement",
"inputChannels": 3,
"outputChannels": 3,
"matrix": [0.4361, 0.3851, 0.1431,
0.2225, 0.7169, 0.0606,
0.0139, 0.0971, 0.7141],
"constants": [0.0, 0.0, 0.0]
}
]
}
}
}type |
Purpose |
|---|---|
CurveSetElement |
Per-channel independent curves |
MatrixElement |
Linear matrix with optional additive constants |
CLutElement |
N-dimensional colour lookup table |
ExtCLutElement |
CLUT with explicit storage type |
BAcsElement / EAcsElement |
Auxiliary colour space conversion bookends |
CalculatorElement |
Programmable calculator (iccMAX) |
XYZToJabElement |
CIECAM02 forward conversion |
JabToXYZElement |
CIECAM02 inverse conversion |
TintArrayElement |
Spectral tinting via a tag array |
EmissionMatrixElement |
Spectral-to-colorimetric matrix |
EmissionCLutElement |
Spectral CLUT |
EmissionObserverElement |
Observer spectral weighting |
Each entry in "curves" is a curve object with a "type" field:
type |
Fields |
|---|---|
SegmentedCurve |
segments array (same format as segmentedCurveType) |
SingleSampledCurve |
firstEntry, lastEntry, samples, extensionType |
SampledCalculatorCurve |
firstEntry, lastEntry, desiredSize, calculator |
DuplicateCurve |
index -- source channel index (integer) |
"curves": [
{
"type": "SegmentedCurve",
"segments": [
{
"start": "-infinity", "end": "+infinity",
"type": "FormulaSegment",
"functionType": 0,
"parameters": [1.0, 0.5, 0.0, 0.0]
}
]
},
{ "type": "DuplicateCurve", "index": 0 },
{ "type": "DuplicateCurve", "index": 0 }
]{
"IccProfile": {
"Header": {
"ProfileVersion": "4.4",
"ProfileDeviceClass": "mntr",
"DataColourSpace": "RGB ",
"PCS": "XYZ ",
"CreationDateTime": "2024-01-15T00:00:00",
"ProfileFileSignature": "acsp",
"PrimaryPlatform": "MSFT",
"CMMFlags": { "EmbeddedInFile": false, "UseWithEmbeddedDataOnly": false },
"DeviceAttributes": {
"ReflectiveOrTransparency": "reflective",
"GlossyOrMatte": "glossy",
"MediaPolarity": "positive",
"MediaColour": "colour"
},
"RenderingIntent": "Relative",
"PCSIlluminant": [0.9642, 1.0, 0.8249]
},
"Tags": [
{
"profileDescriptionTag": {
"data": {
"type": "multiLocalizedUnicodeType",
"localizedStrings": [
{ "language": "en", "country": "US", "text": "Simple sRGB Display Profile" }
]
}
}
},
{
"copyrightTag": {
"data": { "type": "textType", "text": "Public Domain" }
}
},
{
"mediaWhitePointTag": {
"data": { "type": "XYZType", "XYZ": [0.9505, 1.0000, 1.0891] }
}
},
{
"redMatrixColumnTag": {
"data": { "type": "XYZType", "XYZ": [0.4361, 0.2225, 0.0139] }
}
},
{
"greenMatrixColumnTag": {
"data": { "type": "XYZType", "XYZ": [0.3851, 0.7169, 0.0971] }
}
},
{
"blueMatrixColumnTag": {
"data": { "type": "XYZType", "XYZ": [0.1431, 0.0606, 0.7141] }
}
},
{
"redTRCTag": {
"data": {
"type": "parametricCurveType",
"functionType": 3,
"params": [2.4, 0.9479, 0.0521, 0.0405, 0.0031]
}
}
},
{ "greenTRCTag": { "sameAs": "redTRCTag" } },
{ "blueTRCTag": { "sameAs": "redTRCTag" } }
]
}
}Encoding profiles declare a colour space encoding without transformation data.
{
"IccProfile": {
"Header": {
"ProfileVersion": "5.0",
"ProfileDeviceClass": "cenc",
"DataColourSpace": "RGB ",
"PCS": "XYZ ",
"CreationDateTime": "2024-01-15T00:00:00",
"ProfileFileSignature": "acsp",
"RenderingIntent": "Perceptual",
"PCSIlluminant": [0.9642, 1.0, 0.8249]
},
"Tags": [
{
"referenceNameTag": {
"data": { "type": "utf8TextType", "text": "sRGB" }
}
}
]
}
}{
"IccProfile": {
"Header": {
"ProfileVersion": "5.0",
"ProfileDeviceClass": "spac",
"DataColourSpace": "RGB ",
"PCS": "XYZ ",
"CreationDateTime": "2024-01-15T00:00:00",
"ProfileFileSignature": "acsp",
"PrimaryPlatform": "MSFT",
"CMMFlags": { "EmbeddedInFile": false, "UseWithEmbeddedDataOnly": false },
"DeviceAttributes": {
"ReflectiveOrTransparency": "reflective",
"GlossyOrMatte": "glossy",
"MediaPolarity": "positive",
"MediaColour": "colour"
},
"RenderingIntent": "Relative",
"PCSIlluminant": [0.9642, 1.0, 0.8249]
},
"Tags": [
{
"profileDescriptionTag": {
"data": {
"type": "multiLocalizedUnicodeType",
"localizedStrings": [
{ "language": "en", "country": "US", "text": "Example iccMAX Profile" }
]
}
}
},
{
"AToB1Tag": {
"data": {
"type": "multiProcessElementType",
"inputChannels": 3,
"outputChannels": 3,
"elements": [
{
"type": "CurveSetElement",
"inputChannels": 3,
"outputChannels": 3,
"curves": [
{
"type": "SegmentedCurve",
"segments": [
{
"start": "-infinity", "end": 0.04045,
"type": "FormulaSegment",
"functionType": 0,
"parameters": [1.0, 0.07739938, 0.0125, 0.0]
},
{
"start": 0.04045, "end": "+infinity",
"type": "FormulaSegment",
"functionType": 0,
"parameters": [2.4, 0.94786562, 0.05213438, 0.0031308]
}
]
},
{ "type": "DuplicateCurve", "index": 0 },
{ "type": "DuplicateCurve", "index": 0 }
]
},
{
"type": "MatrixElement",
"inputChannels": 3,
"outputChannels": 3,
"matrix": [
0.4361, 0.3851, 0.1431,
0.2225, 0.7169, 0.0606,
0.0139, 0.0971, 0.7141
],
"constants": [0.0, 0.0, 0.0]
}
]
}
}
}
]
}
}iccMAX struct tags contain named member tags. Each member in "memberTags" is a
single-key object whose key is the element name and whose value is a flat tag object
with a "type" field (same type values as top-level tag data).
{
"colorEncodingParamsTag": {
"data": {
"type": "tagStructType",
"structureType": "colorEncodingParamsStruct",
"memberTags": [
{
"blueColorantTag": {
"type": "XYZType",
"XYZ": [0.1431, 0.0606, 0.7141]
}
}
]
}
}
}iccMAX array tags contain an ordered list of typed tag objects in "arrayTags".
Each element is a flat object with a "type" field (same type values as
top-level tag data).
{
"spectralViewingConditionsTag": {
"data": {
"type": "tagArrayType",
"arrayType": "namedColorArray",
"arrayTags": [
{
"type": "utf8TextType",
"text": "D50"
}
]
}
}
}Signature fields (4-character ICC codes) appear in several tag types and the profile header. Four formats are accepted:
| Format | Example | Meaning |
|---|---|---|
| Empty string | "" | Zero / unset signature |
| 4 characters | "RGB " | Direct four-character code (space-padded) |
| 6 characters | "rs0024" | Two-char prefix + 4 hex digits |
| 8 hex digits | "73663031" | Full signature as hex |
- Always include the
"IccProfile"wrapper -- the top-level key is"IccProfile", not"Header"or"Tags"directly. - XYZ values are arrays, not objects -- write
[0.95, 1.0, 1.09]not{ "X": 0.95, "Y": 1.0, "Z": 1.09 }. "type"is always required ondataobjects, MPE elements, curve-set curves, MBB curves, and struct member tags. Missing"type"is the most common parse error.curveTypetag needs the subtype -- a curveType tag requires both"type": "curveType"(the ICC tag type) and"curveType": "gamma"(the subtype selector).- MBB curve types are
"Curve","ParametricCurve","SegmentedCurve"-- not"curveType"/"parametricCurveType"/"segmentedCurveType". - Segment types are PascalCase --
"FormulaSegment"and"SampledSegment", not"formulaSegment"/"sampledSegment". parametricCurveTypeuses"params"-- not"Parameters"or"parameters".- Segment boundaries must be contiguous -- segments must form a non-overlapping
cover of the real line. Use
"-infinity"/"+infinity"for the outermost ends. sameAsmust come after the original -- the referenced tag key must appear earlier in theTagsarray.- Validation before use -- always run
IccDumpProfileafter building a profile withIccFromJsonto catch structural issues before the profile is deployed. - Round-trip inspection -- convert an existing reference profile with
IccToJsonfirst to see the expected JSON structure for a given profile class.
The examples/hello-iccdev/ directory contains a
minimal standalone project that links IccProfLib2, IccXML2, and IccJSON2. When
built with USE_ICCJSON=1 it demonstrates JSON round-tripping via the
CIccProfileJson::ToJson() and ParseJson() APIs, factory registration, and
library version reporting. See
examples/hello-iccdev/README.md for
build instructions.
The Windows IIS ISAPI sample at
Tools/Winnt/IccIisIsapi/ conditionally invokes
iccToJson and iccFromJson as part of its tool pipeline when IccJSON is built.
Uploaded ICC profiles are converted to JSON for inspection, and XML-originated
profiles are also exported to JSON. See
api.md for the HTTP endpoint reference.