Colour Notations

Every colour string tc-lib-color parses: hex, names, rgb, hsl, cmyk, lab, Acrobat JavaScript arrays, and the separator and angle rules

getColorObj() turns a string into a colour model object. It accepts hexadecimal codes, CSS colour names, seven colour functions and the Acrobat JavaScript array forms. The notation selects the model: rgb() returns an Rgb, lab() returns a Lab.

An unrecognised string raises \Com\Tecnick\Color\Exception. tryGetColorObj() runs the same parser and returns null instead of raising.

Hexadecimal

Three, four, six or eight digits, with or without the leading #. Three and four digit forms expand by doubling each digit, and the fourth pair is alpha.

<?php

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

$web = new \Com\Tecnick\Color\Web();

foreach (['#369', '#3699', '#336699', '#336699cc'] as $hex) {
    $col = $web->getColorObj($hex);
    echo $hex, ' -> ', $col->getCssColor(), ' ', $col->getRgbaHexColor(), "\n";
}
#369 -> rgb(51,102,153) #336699ff
#3699 -> rgba(51,102,153,0.6) #33669999
#336699 -> rgb(51,102,153) #336699ff
#336699cc -> rgba(51,102,153,0.8) #336699cc

Names

The 150 names of the web colour table, matched case-insensitively. A color. prefix is stripped first, so the Acrobat JavaScript spelling resolves to the same colour.

Colour Functions

Seven functions, listed here with the model each one produces:

$notations = [
    'g(128)',
    'g(50%)',
    'rgb(51,102,153)',
    'rgb(20% 40% 60%)',
    'rgba(51,102,153,0.85)',
    'hsl(210,50%,40%)',
    'hsla(210,50%,40%,0.85)',
    'cmyk(67%,33%,0%,40%)',
    'cmyka(67,33,0,40,0.85)',
    'lab(41% -2 -25)',
    'lab(41 -2 -25 / 0.85)',
];

foreach ($notations as $notation) {
    $col = $web->getColorObj($notation);
    printf("%-22s %-5s %s\n", $notation, $col->getType(), $col->getCssColor());
}
g(128)                 GRAY  g(128)
g(50%)                 GRAY  g(128)
rgb(51,102,153)        RGB   rgb(51,102,153)
rgb(20% 40% 60%)       RGB   rgb(51,102,153)
rgba(51,102,153,0.85)  RGB   rgba(51,102,153,0.85)
hsl(210,50%,40%)       HSL   hsl(210,50%,40%)
hsla(210,50%,40%,0.85) HSL   hsla(210,50%,40%,0.85)
cmyk(67%,33%,0%,40%)   CMYK  cmyk(67%,33%,0%,40%)
cmyka(67,33,0,40,0.85) CMYK  cmyka(67%,33%,0%,40%,0.85)
lab(41% -2 -25)        LAB   lab(41% -2 -25)
lab(41 -2 -25 / 0.85)  LAB   lab(41% -2 -25 / 0.85)

g(), cmyk() and cmyka() are notations of this library rather than CSS functions. They round-trip: getCssColor() emits them and getColorObj() reads them back.

Component values are read as a fraction of their reference maximum, or as a percentage when they carry a %. Saturation and lightness are the exception: the CSS specification defines them as percentages, so hsl(210,50,40) and hsl(210,50%,40%) are the same colour.

Separators

Commas or spaces, throughout. A notation that uses both raises.

echo $web->getColorObj('rgb(51,102,153)')->getCssColor(), "\n";
echo $web->getColorObj('rgb(51 102 153)')->getCssColor(), "\n";

try {
    $web->getColorObj('rgb(51, 102 153)');
} catch (\Com\Tecnick\Color\Exception $exc) {
    echo $exc->getMessage(), "\n";
}
rgb(51,102,153)
rgb(51,102,153)
mixed css color separators: rgb(51, 102 153)

Hue Angles

The hue takes the CSS angle units. A bare number and a percentage are read as degrees. Out-of-range angles wrap rather than clamp, so -150deg and 570deg resolve to the same hue as 210deg.

foreach (['210', '210deg', '233.333grad', '3.665191rad', '0.583333turn', '570deg', '-150deg'] as $hue) {
    printf("%-14s %s\n", $hue, $web->getColorObj('hsl(' . $hue . ' 50% 40%)')->getCssColor());
}
210            hsl(210,50%,40%)
210deg         hsl(210,50%,40%)
233.333grad    hsl(209.9997,50%,40%)
3.665191rad    hsl(210,50%,40%)
0.583333turn   hsl(209.9999,50%,40%)
570deg         hsl(210,50%,40%)
-150deg        hsl(210,50%,40%)

Every other component clamps. rgb(300,-20,0) is rgb(255,0,0).

Alpha

A fourth component after a comma, or after a slash, as a fraction or a percentage. A missing alpha is 1.0.

foreach (['rgb(51,102,153)', 'rgba(51,102,153,0.85)', 'rgb(51 102 153 / 85%)', 'rgb(51 102 153 / 0.85)'] as $notation) {
    printf("%-24s %s\n", $notation, $web->getColorObj($notation)->getRgbaHexColor());
}
rgb(51,102,153)          #336699ff
rgba(51,102,153,0.85)    #336699d9
rgb(51 102 153 / 85%)    #336699d9
rgb(51 102 153 / 0.85)   #336699d9

rgb(), hsl() and cmyk() take the same alpha component as their a forms.

Acrobat JavaScript

The array form used by PDF annotation scripts, with the components already in the [0..1] range. The leading token picks the colour space: G is grayscale, RGB is three channels, CMYK is four, T is transparent.

foreach (['["G",0.5]', '["RGB",0.2,0.4,0.6]', '["CMYK",0.67,0.33,0,0.4]'] as $notation) {
    printf("%-26s %s\n", $notation, $web->getColorObj($notation)->getCssColor());
}

var_dump($web->getColorObj('["T"]'));
["G",0.5]                  g(128)
["RGB",0.2,0.4,0.6]        rgb(51,102,153)
["CMYK",0.67,0.33,0,0.4]   cmyk(67%,33%,0%,40%)
NULL

Transparent

transparent, color.transparent, ["T"] and the empty string all return null. None of them raises: a transparent colour is represented by the absence of a model object.

var_dump($web->getColorObj('transparent'));
var_dump($web->getColorObj('color.transparent'));
var_dump($web->getColorObj(''));

// tryGetColorObj() returns null for bad input too, instead of raising
var_dump($web->tryGetColorObj('notacolor'));
NULL
NULL
NULL
NULL

A caller that expects a model object has to test for null. tryGetColorObj() returns null for unparseable input as well, so one test covers both cases.

The Normalizer

ComponentNormalizer does the scaling: a value into [0..1], by dividing by the stated maximum, or by 100 when it ends in %. It has a single method, normalize(). The Css constructor takes an instance, so a different scaling rule is injected rather than subclassed in.

$normalizer = new \Com\Tecnick\Color\ComponentNormalizer();

var_dump($normalizer->normalize('50%', 255));
var_dump($normalizer->normalize(128, 255));
var_dump($normalizer->normalize('abc', 255));
var_dump($normalizer->normalize(300, 255));

$web = new \Com\Tecnick\Color\Web($normalizer);
echo $web->getColorObj('rgb(50%,50%,50%)')->getCssColor(), "\n";
float(0.5)
float(0.5019607843137255)
float(0)
float(1)
rgb(128,128,128)

A non-numeric value normalizes to 0.0 rather than raising. The parser rejects a malformed string before it reaches the normalizer, so this applies only to direct calls.

Extending The Parser

getColorObj() is not final, and neither are the protected getColorObjFromCss() and getColorObjFromJs() it dispatches to. A subclass can add a notation by matching it first and deferring to the parent otherwise. Everything else on Css and Web is final.

See Also