tc-lib-color

Technical guide for integrating tc-lib-color: color parsing, conversion, and PDF/web output

Overview

tc-lib-color provides parsing, conversion, and formatting of color values for web and PDF rendering pipelines.

One normalization layer covers RGB, CMYK, HSL, grayscale, Lab, and spot colors, in both their CSS and their PDF representations. Conversions written ad hoc in three places tend to round differently in all three; here they do not.

Repository and API Docs

Project Metadata

ItemValue
Namespace\Com\Tecnick\Color
LicenseGNU LGPL v3

Installation

composer require tecnickcom/tc-lib-color

Where It Fits

Use this package whenever rendering needs explicit color conversions and repeatable output across digital and print contexts. It is a direct dependency of tecnickcom/tc-lib-pdf and is used internally for all color operations during PDF composition.

Color Models

ModelClass
RGB / RGBA\Com\Tecnick\Color\Model\Rgb
HSL / HSLA\Com\Tecnick\Color\Model\Hsl
CMYK\Com\Tecnick\Color\Model\Cmyk
CIE Lab\Com\Tecnick\Color\Model\Lab
Grayscale\Com\Tecnick\Color\Model\Gray

Spot colors (Separation) are handled by \Com\Tecnick\Color\Spot, with DeviceCMYK and Lab alternate color spaces for PDF output.

Integration Helpers

  • CSS output that parses back to the same color
  • PDF and Acrobat JavaScript color output
  • Cross-model conversion helpers on all color models
  • Named web color lookup (CSS Color Module Level 4 names) and nearest-color matching in sRGB or CIE Lab

Main Classes

ClassPurpose
\Com\Tecnick\Color\WebNamed web colors, hex parsing, nearest-color lookup
\Com\Tecnick\Color\PdfPDF color operators, spot color objects, JS color strings
\Com\Tecnick\Color\CssCSS color string parsing and normalization
\Com\Tecnick\Color\SpotSpot color registry and PDF spot color resource generation
\Com\Tecnick\Color\ComponentNormalizerScaling of parsed component values into the [0..1] range

Pdf extends Spot extends Web extends Css, so a single object provides the parser, the spot registry and the PDF writer. Each role also has its own interface to type against:

InterfaceRole
\Com\Tecnick\Color\ColorParserInterfaceColor string parsing and named color lookup
\Com\Tecnick\Color\SpotRegistryInterfaceSpot color registration and PDF spot resource output
\Com\Tecnick\Color\PdfColorWriterInterfacePDF and Acrobat JavaScript color output
\Com\Tecnick\Color\ExceptionInterfaceCommon interface of the library exceptions

Web::getColorObj(), Spot::getSpotColor(), Pdf::getPdfColor() and Pdf::getColorObject() can be overridden, along with the protected parser methods on Css and Spot::resolveSpotColorData(). Every other public method is final.

Supported Color Notations

NotationExamples
Hexadecimal#RGB, #RGBA, #RRGGBB, #RRGGBBAA
Namesteelblue, color.steelblue, transparent
Grayg(128), g(50%)
RGBrgb(51,102,153), rgb(20% 40% 60%), rgba(51,102,153,0.85), rgb(51 102 153 / 85%)
HSLhsl(210,50%,40%), hsl(210deg 50% 40%), hsla(210,50%,40%,0.85)
CMYKcmyk(67%,33%,0%,40%), cmyka(67,33,0,40,0.85)
CIE Lablab(41% -2 -25), lab(41 -2 -25 / 0.85)
Acrobat JavaScript["T"], ["G",0.5], ["RGB",0.2,0.4,0.6], ["CMYK",0.67,0.33,0,0.4]

Components are separated either by commas or by spaces, not by a mixture of the two, and the alpha channel by a comma in the first form or by a slash in the second. A hue accepts the CSS angle units (deg, grad, rad, turn). Out-of-range components are clamped and hues wrap, as CSS Color Level 4 requires. Anything else is rejected with a \Com\Tecnick\Color\Exception.

g(), cmyk() and cmyka() are notations of this library, not CSS functions, and round-trip through getCssColor() and getColorObj() like the others.

Typed Enums

\Com\Tecnick\Color\ColorModelType is a backed enum listing the supported color models (GRAY, RGB, HSL, CMYK, LAB). Its backing value is the canonical model string returned by the model objects, so it can be used interchangeably with the plain string wherever a model type is accepted. Model::create() builds a model object from an enum case and a component array.

Exceptions

\Com\Tecnick\Color\Exception (a \Exception) signals invalid input. \Com\Tecnick\Color\UnknownComponentException (a \LogicException) signals a component name a model does not define, and is not swallowed by the lenient accessors tryGetColorObj() and getColorObject(). Both implement \Com\Tecnick\Color\ExceptionInterface, so a single catch covers them.

Version 3.0 Changes

Release 3.0 is a breaking change:

  • Rgb::getCssColor() emits rgb(255,0,0) and Gray::getCssColor() emits g(128) instead of percentage forms; alpha is capped at four decimals.
  • The PDF color getters take a trailing bool $allowSpot = true argument, and most public methods of Web, Spot and Pdf are final.
  • Model::__construct() is removed from the abstract base: each model validates its own components and raises UnknownComponentException.
  • Malformed numbers and mixed comma/space separators are rejected; out-of-range components clamp and hues wrap.
  • Css::normalizeValue() is concrete and delegates scaling to the ComponentNormalizer collaborator.
  • New API: the role interfaces, Model::withInvertedColor(), Web::getClosestWebColorByDeltaE(), CSS angle units, slash-alpha and percentage alpha.
  • addSpotLabColor() clamps its range to [-128..127], the spot color accessors return copies, and a spot resource with no PDF object raises instead of being emitted.

How Integration Works

  1. Instantiate Web or Pdf depending on target output context.
  2. Parse input color strings (hex, CSS, named, spot) into a color model object.
  3. Use model output methods to produce CSS strings, PDF operators, or normalized arrays.
  4. For PDF output, use Pdf::getPdfColor() to emit the correct PDF color operator.

Web Colors

<?php

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

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

// Parse a hex color
$rgb = $web->getRgbObjFromHex('#336699');
echo $rgb->getCssColor();         // rgb(51,102,153)
echo $rgb->getRgbHexColor();      // #336699

// Parse a named color
$rgb2 = $web->getRgbObjFromName('cornflowerblue');
echo $rgb2->getCssColor();

// Convert to HSL
$hsl = new \Com\Tecnick\Color\Model\Hsl($rgb->toHslArray());
echo $hsl->getCssColor();         // hsl(210,50%,40%)

// Normalized array (components scaled to 0–255, alpha to 0–1)
$arr = $rgb->getNormalizedArray(255);
// ['R' => 51, 'G' => 102, 'B' => 153, 'A' => 1]

// Invert without mutating the receiver, then find the closest named web color
$name = $web->getClosestWebColor($rgb->withInvertedColor()->toRgbArray());

// Perceptual match in CIE Lab instead of Euclidean distance in sRGB
echo $web->getClosestWebColorFromString('#9577a6');          // lightslategray
echo $web->getClosestWebColorByDeltaEFromString('#9577a6');  // plum

PDF Colors

<?php

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

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

// Emit a PDF fill color operator from any CSS/hex/named color string
echo $pdf->getPdfColor('#ff0000');         // 1.000000 0.000000 0.000000 rg
echo $pdf->getPdfColor('#ff0000', true);   // 1.000000 0.000000 0.000000 RG  (stroke)

// Emit PDF color components only (for custom operators)
echo $pdf->getPdfRgbComponents('#336699'); // 0.200000 0.400000 0.600000

// JavaScript color string for PDF annotations
echo $pdf->getJsColorString('red');        // color.red
echo $pdf->getJsColorString('#336699');    // ["RGB",0.200000,0.400000,0.600000]

// Get a color model object from any string
$obj = $pdf->getColorObject('cmyk(0,50%,100%,0)');
echo $obj->getPdfColor();

// Force the device color for a name that is also a spot color name
echo $pdf->getPdfFillColor('green', 1, false);

Spot Colors

<?php

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

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

// Register a custom spot color (CMYK components 0–1)
$pdf->addSpotColorFromArray('PANTONE 032 C', [
    'cyan' => 0,
    'magenta' => 0.91,
    'yellow' => 0.86,
    'key' => 0,
]);

// Or register a Lab-based spot color
$pdf->addSpotLabColor('My Lab Spot', 50.0, 10.0, -20.0);

// Retrieve a copy of the spot color object
$spot = $pdf->getSpotColorObj('PANTONE 032 C');
echo $spot->getPdfColor();

// Generate PDF spot color resource objects for the document header,
// then the resource dictionary that references them
$pon = 10;
echo $pdf->getPdfSpotObjects($pon);
echo $pdf->getPdfSpotResources();

Every registered spot color must be emitted by getPdfSpotObjects() before getPdfSpotResources() is called, otherwise the resource writers raise a \Com\Tecnick\Color\Exception.

Eight of the eleven default spot color names are also CSS color names (red, green, blue, cyan, magenta, yellow, black, white) and resolve to the spot color. They agree with their CSS namesake except green: the spot Green is #00ff00 while CSS green is #008000. Pass $allowSpot = false to force the device color. The remaining three, key, all and none, are spot-only names: with $allowSpot = false they do not resolve, and the color getters return an empty string while getColorObject() returns null.

Behavior Worth Knowing

  • Spot colors are registered only when you add them explicitly. The color getters do not register one as a side effect, so the spot resources a document emits match the ones it actually uses.
  • Spot color names are escaped when written to PDF, and the CSS parser recognizes the standard spot color names.
  • Spot colors take a DeviceCMYK or a Lab alternate color space in PDF output.
  • HSL saturation and lightness are always parsed as percentages, as the CSS specification requires.

Integration Notes

Parse color notation once, at the edge of your application, and pass typed model objects inward. Strings that reach the rendering layer get reparsed, sometimes under different assumptions.

RGB and CMYK do not cover the same gamut, so a screen preview and a press sheet will differ for saturated colors no matter how the conversion is done. Decide which one is authoritative for a given document and convert in one direction only.

Lab is the device-independent option, and it is what ISO 19005 prefers. A DeviceCMYK color in a PDF/A document is only valid when the output intent is a CMYK profile, and tc-lib-pdf reports the mismatch through getWarnings(); see /docs/standards/.

Pin your brand colors with golden-file tests. Rounding in a conversion chain drifts quietly, and a logo two units off is not something code review catches.

Requirements

  • PHP 8.2 or later
  • Extension: pcre
  • Composer

Development and Packaging

  • QA and local checks: make deps, make help, make qa
  • Coverage report: make qa-coverage
  • Local example server: make server
  • Packaging: make rpm, make deb

Support and Contribution