Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 64 additions & 2 deletions src/Fonts/Color/ColorFontFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@

/**
* An open font, as each ColorGlyphSource drawing it reads it: its tables, its file, the log a glyph
* that cannot be drawn is reported to, and its outlines, decoded once for every source that draws
* with them.
* that cannot be drawn is reported to, and its outlines and palette, read once for every source that
* draws with them.
*/
class ColorFontFile
{

/**
* The palette index COLR gives a colour that is the colour of the text
*/
const FOREGROUND = 0xFFFF;

/**
* @var FileReader
*/
Expand All @@ -40,6 +45,11 @@ class ColorFontFile
*/
private $outline;

/**
* @var int[][]|null
*/
private $palette;

/**
* @param TTFontFile $font The font, its table directory read
* @param FileReader $reader The font file
Expand Down Expand Up @@ -75,4 +85,56 @@ public function outline()

return $this->outline;
}

/**
* The first palette of CPAL, the one a COLR font is drawn in. Every read is checked for coming up
* short - see FontReader::fieldsAt().
*
* @return int[][] Each colour as [red, green, blue, alpha] from 0 to 255, or none where CPAL has no
* palette or its first runs past the colour records
*/
public function palette()
{
if ($this->palette !== null) {
return $this->palette;
}

$this->palette = [];
$cpal = $this->table('CPAL')[0];

// numPaletteEntries, numPalettes, numColorRecords, colorRecordsArrayOffset, colorRecordIndices[0]
$header = $this->reader->fieldsAt($cpal + 2, 12, 'nentries/npalettes/nrecords/Ncolors/nfirst');
if ($header === null || $header[1] === 0 || $header[4] + $header[0] > $header[2]) {
return $this->palette;
}

list($entries, , , $colors, $first) = $header;

// Each colour is stored blue, green, red, alpha
$bgra = $this->reader->fieldsAt($cpal + $colors + $first * 4, $entries * 4, 'C*');
for ($i = 0; $bgra !== null && $i < 4 * $entries; $i += 4) {
$this->palette[] = [$bgra[$i + 2], $bgra[$i + 1], $bgra[$i], $bgra[$i + 3]];
}

return $this->palette;
}

/**
* @param int $index An index into the first palette, or FOREGROUND
* @param float $alpha The paint's alpha, from 0 to 1, which the colour's own is multiplied by
*
* @return array [[red, green, blue] from 0 to 1, or null for the colour of the text, alpha]. An index
* the palette does not hold is the colour of the text too.
*/
public function colour($index, $alpha)
{
$palette = $this->palette();
if ($index === self::FOREGROUND || !isset($palette[$index])) {
return [null, min(1, $alpha)];
}

list($red, $green, $blue, $own) = $palette[$index];

return [[$red / 255, $green / 255, $blue / 255], min(1, $alpha) * $own / 255];
}
}
43 changes: 19 additions & 24 deletions src/Fonts/Color/ColorFormats.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,14 @@ class ColorFormats

/**
* The formats mPDF can draw, each with the ColorGlyphSource that draws it, in the order a font that
* carries several is drawn in
* carries several is drawn in.
*
* Whether a font is drawable is kept in its cached metrics - see drawable() - so adding a format
* here calls for raising MetricsGenerator::CACHE_FORMAT, or a font of that format cached before is
* served as one that is not.
*/
const SOURCES = [
'COLRv1' => 'Mpdf\Fonts\Color\ColrV1Source',
'COLRv0' => 'Mpdf\Fonts\Color\ColrV0Source',
'CBDT' => 'Mpdf\Fonts\Color\CbdtSource',
'sbix' => 'Mpdf\Fonts\Color\SbixSource',
Expand Down Expand Up @@ -51,21 +56,24 @@ public static function inColor(Mpdf $mpdf)
}

/**
* The format a document draws a font in
* Whether a document draws a font in colour: it carries a format mPDF draws, and the document may
* draw colour
*
* @param array $font The font, as Mpdf::$fonts holds it
* @param Mpdf $mpdf The document
*
* @return string The format, or '' where the font is not drawn in colour
* @return bool
*/
public static function drawn(array $font, Mpdf $mpdf)
public static function drawsInColor(array $font, Mpdf $mpdf)
{
return empty($font['colorFormats']) ? '' : self::choose($font['colorFormats'], self::inColor($mpdf));
return !empty($font['colorFormats']) && self::drawable($font['colorFormats']) && self::inColor($mpdf);
}

/**
* What draws a font's glyphs in a document, each glyph by the first that has it: the format it is
* drawn in, where the document may draw colour, then its outlines, where it has any
* What draws a font's glyphs in a document, each glyph by the first that has it: each format the
* font carries, in the order of SOURCES, where the document may draw colour, then its outlines,
* where it has any. A COLR version 1 font's glyph with no paint is so drawn from its version 0
* layers.
*
* @param array $font The font, as Mpdf::$fonts holds it
* @param Mpdf $mpdf The document
Expand All @@ -74,8 +82,10 @@ public static function drawn(array $font, Mpdf $mpdf)
*/
public static function sources(array $font, Mpdf $mpdf)
{
$format = self::drawn($font, $mpdf);
$sources = $format === '' ? [] : [self::SOURCES[$format]];
$sources = [];
if (!empty($font['colorFormats']) && self::inColor($mpdf)) {
$sources = array_values(array_intersect_key(self::SOURCES, array_flip($font['colorFormats'])));
}
if (!empty($font['hasOutlines'])) {
$sources[] = 'Mpdf\Fonts\Color\OutlineSource';
}
Expand All @@ -96,19 +106,4 @@ public static function blank(array $font, Mpdf $mpdf)
{
return !empty($font['colorFormats']) && self::drawable($font['colorFormats']) && !self::sources($font, $mpdf);
}

/**
* The format a font is drawn in: the first of SOURCES that it carries
*
* @param string[] $fontFormats The formats the font carries
* @param bool $color Whether the document may use colour
*
* @return string The format, or '' where the font is drawn without colour
*/
public static function choose(array $fontFormats, $color)
{
$formats = $color ? array_intersect(array_keys(self::SOURCES), $fontFormats) : [];

return $formats ? reset($formats) : '';
}
}
Loading
Loading