Spot Colours

Spot colours in tc-lib-color: the registry, the eleven defaults, CMYK and Lab separations, and the PDF objects and resources they emit

A spot colour is a named ink rather than a mixture of the process four. In a PDF it is a Separation colour space: a name, an alternate colour space for rendering without the ink, and a tint transform between the two.

\Com\Tecnick\Color\Spot is the registry. \Com\Tecnick\Color\Pdf extends it, so one object provides the parser, the registry and the writers.

Registering One

<?php

require_once __DIR__ . '/vendor/autoload.php';

$pdf = new \Com\Tecnick\Color\Pdf();

$key = $pdf->addSpotColorFromArray('PANTONE 032 C', [
    'cyan' => 0.0,
    'magenta' => 0.91,
    'yellow' => 0.86,
    'key' => 0.0,
]);

echo $key, "\n";
echo $pdf->getSpotColorObj('PANTONE 032 C')->getCssColor(), "\n";
echo $pdf->encodeSpotColorName('PANTONE 032 C'), "\n";
pantone032c
cmyk(0%,91%,86%,0%)
PANTONE#20032#20C

addSpotColor() takes a Cmyk object where addSpotColorFromArray() takes the components. Both return the registry key, which is what lookups match on: the name lowercased with every character outside [a-z0-9] dropped, so PANTONE 032 C keys as pantone032c. The name is stored as given and escaped when written to the PDF, where it becomes /PANTONE#20032#20C.

Lab Separations

$pdf->addSpotLabColor('My Lab Spot', 50.0, 10.0, -20.0);

echo $pdf->getSpotLabColorObj('My Lab Spot')->getCssColor(), "\n";
// the stored CMYK equivalent
echo $pdf->getSpotColorObj('My Lab Spot')->getCssColor(), "\n";
lab(50% 10 -20)
cmyk(22.3139%,24.8788%,0%,40.0602%)

addSpotLabColor() takes L*, a* and b* and up to four option arrays: white point, black point, range and col0. Those describe the Lab colour space written into the PDF. The CMYK equivalent stored alongside is computed under D65 regardless of the white point passed. It is what getSpotColorObj() returns and what a device colour falls back to.

A Lab alternate space describes the ink independently of any device. The CMYK one stores a device approximation, which is what a viewer renders when the ink is absent.

The Defaults

Eleven names are known without being registered:

SwatchKeyNameC M Y KHexCSS name
noneNone0.000000 0.000000 0.000000 0.000000#ffffffnot a CSS name
allAll1.000000 1.000000 1.000000 1.000000#000000not a CSS name
cyanCyan1.000000 0.000000 0.000000 0.000000#00ffff#00ffff
magentaMagenta0.000000 1.000000 0.000000 0.000000#ff00ff#ff00ff
yellowYellow0.000000 0.000000 1.000000 0.000000#ffff00#ffff00
keyKey0.000000 0.000000 0.000000 1.000000#000000not a CSS name
whiteWhite0.000000 0.000000 0.000000 0.000000#ffffff#ffffff
blackBlack0.000000 0.000000 0.000000 1.000000#000000#000000
redRed0.000000 1.000000 1.000000 0.000000#ff0000#ff0000
greenGreen1.000000 0.000000 1.000000 0.000000#00ff00#008000
blueBlue1.000000 1.000000 0.000000 0.000000#0000ff#0000ff

Eight of them are also CSS colour names and, by default, resolve to the spot colour rather than the device colour. Seven agree with their CSS namesake. green does not: the spot Green is the full-intensity #00ff00, CSS green is the half-intensity #008000.

// the spot Green
echo $pdf->getColorObject('green')->getRgbHexColor(), "\n";
// the CSS green
echo $pdf->getColorObject('green', false)->getRgbHexColor(), "\n";

// 'key', 'all' and 'none' are spot-only names
var_dump($pdf->getColorObject('key', false));
echo '[', $pdf->getPdfFillColor('key', 1, false), "]\n";
#00ff00
#008000
NULL
[]

key, all and none are spot-only. With $allowSpot = false they do not resolve at all: getColorObject() returns null and the colour getters return an empty string.

none paints nothing and all paints on every plate, which is how registration marks are drawn. Both resolve from a plain colour string while $allowSpot is true.

Registration Is Explicit

// getColorObject() resolves without registering
$pdf->getColorObject('cyan');
echo count($pdf->getSpotColors()), "\n";

// getPdfFillColor() registers
$pdf->getPdfFillColor('cyan');
echo count($pdf->getSpotColors()), "\n";
0
1

getColorObject() resolves a spot colour without registering it. getPdfColor() and its wrappers register it, since the content stream will reference it. getPdfSpotObjects() therefore emits a resource for every spot colour the document uses and for no others.

Emitting Them

Objects first, resources second. The resource dictionary references object numbers, and a spot colour whose object was never written has none.

try {
    echo $pdf->getPdfSpotResources();
} catch (\Com\Tecnick\Color\Exception $exc) {
    echo $exc->getMessage(), "\n";
}

$pon = 10;
$pdf->getPdfSpotObjects($pon);
echo $pdf->getPdfSpotResources();
unable to reference a spot color that has no PDF object, call getPdfSpotObjects() first: pantone032c
/ColorSpace << /CS1 11 0 R >>

getPdfSpotObjects() takes the object number counter by reference, writes one object per registered spot colour, and leaves the counter on the last number it used. Put the output in the document body and the resource dictionary in the page resources.

11 0 obj
[/Separation /PANTONE#20032#20C /DeviceCMYK <</Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0] /C1 [0.000000 0.910000 0.860000 0.000000] /FunctionType 2 /Domain [0 1] /N 1>>]
endobj
12 0 obj
[/Separation /My#20Lab#20Spot [/Lab << /WhitePoint [0.950500 1.000000 1.089000] /BlackPoint [0.000000 0.000000 0.000000] /Range [-128.000000 127.000000 -128.000000 127.000000]>>] << /FunctionType 2 /Domain [0 1] /Range [0.000000 100.000000 -128.000000 127.000000 -128.000000 127.000000] /C0 [100.000000 0.000000 0.000000] /C1 [50.000000 10.000000 -20.000000] /N 1>>]
endobj
/ColorSpace << /CS1 11 0 R /CS2 12 0 R >>

The two objects are 564 bytes and the resource dictionary 42 bytes. $pon went in at 10 and came out at 12.

getPdfSpotResourcesByKeys() writes a dictionary for a subset of the registered keys.

See Also