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
- tc-lib-color : the library overview.
- Colour Models : what a parsed string becomes.
- Web Colours : the 150 names.
- Errors : every message the parser raises.
- API reference : the generated class documentation.