Merge pull request #2177 from fonttools/subset-colrv1
Subset COLRv1 & prune unused CPAL entries
This commit is contained in:
commit
6fa5aa5ea6
@ -229,7 +229,7 @@ def buildCOLR(
|
|||||||
self.version = colr.Version = version
|
self.version = colr.Version = version
|
||||||
|
|
||||||
if version == 0:
|
if version == 0:
|
||||||
self._fromOTTable(colr)
|
self.ColorLayers = self._decompileColorLayersV0(colr)
|
||||||
else:
|
else:
|
||||||
colr.VarStore = varStore
|
colr.VarStore = varStore
|
||||||
self.table = colr
|
self.table = colr
|
||||||
|
@ -14,7 +14,7 @@ import sys
|
|||||||
import struct
|
import struct
|
||||||
import array
|
import array
|
||||||
import logging
|
import logging
|
||||||
from collections import Counter
|
from collections import Counter, defaultdict
|
||||||
from types import MethodType
|
from types import MethodType
|
||||||
|
|
||||||
__usage__ = "pyftsubset font-file [glyph...] [--option=value]..."
|
__usage__ = "pyftsubset font-file [glyph...] [--option=value]..."
|
||||||
@ -1983,27 +1983,130 @@ def subset_glyphs(self, s):
|
|||||||
else:
|
else:
|
||||||
assert False, "unknown 'prop' format %s" % prop.Format
|
assert False, "unknown 'prop' format %s" % prop.Format
|
||||||
|
|
||||||
|
def _paint_glyph_names(paint, colr):
|
||||||
|
result = set()
|
||||||
|
|
||||||
|
def callback(paint):
|
||||||
|
if paint.Format in {
|
||||||
|
otTables.PaintFormat.PaintGlyph,
|
||||||
|
otTables.PaintFormat.PaintColrGlyph,
|
||||||
|
}:
|
||||||
|
result.add(paint.Glyph)
|
||||||
|
|
||||||
|
paint.traverse(colr, callback)
|
||||||
|
return result
|
||||||
|
|
||||||
@_add_method(ttLib.getTableClass('COLR'))
|
@_add_method(ttLib.getTableClass('COLR'))
|
||||||
def closure_glyphs(self, s):
|
def closure_glyphs(self, s):
|
||||||
|
if self.version > 0:
|
||||||
|
# on decompiling COLRv1, we only keep around the raw otTables
|
||||||
|
# but for subsetting we need dicts with fully decompiled layers;
|
||||||
|
# we store them temporarily in the C_O_L_R_ instance and delete
|
||||||
|
# them after we have finished subsetting.
|
||||||
|
self.ColorLayers = self._decompileColorLayersV0(self.table)
|
||||||
|
self.ColorLayersV1 = {
|
||||||
|
rec.BaseGlyph: rec.Paint
|
||||||
|
for rec in self.table.BaseGlyphV1List.BaseGlyphV1Record
|
||||||
|
}
|
||||||
|
|
||||||
decompose = s.glyphs
|
decompose = s.glyphs
|
||||||
while decompose:
|
while decompose:
|
||||||
layers = set()
|
layers = set()
|
||||||
for g in decompose:
|
for g in decompose:
|
||||||
for l in self.ColorLayers.get(g, []):
|
for layer in self.ColorLayers.get(g, []):
|
||||||
layers.add(l.name)
|
layers.add(layer.name)
|
||||||
|
|
||||||
|
if self.version > 0:
|
||||||
|
paint = self.ColorLayersV1.get(g)
|
||||||
|
if paint is not None:
|
||||||
|
layers.update(_paint_glyph_names(paint, self.table))
|
||||||
|
|
||||||
layers -= s.glyphs
|
layers -= s.glyphs
|
||||||
s.glyphs.update(layers)
|
s.glyphs.update(layers)
|
||||||
decompose = layers
|
decompose = layers
|
||||||
|
|
||||||
@_add_method(ttLib.getTableClass('COLR'))
|
@_add_method(ttLib.getTableClass('COLR'))
|
||||||
def subset_glyphs(self, s):
|
def subset_glyphs(self, s):
|
||||||
self.ColorLayers = {g: self.ColorLayers[g] for g in s.glyphs if g in self.ColorLayers}
|
from fontTools.colorLib.unbuilder import unbuildColrV1
|
||||||
return bool(self.ColorLayers)
|
from fontTools.colorLib.builder import buildColrV1, populateCOLRv0
|
||||||
|
|
||||||
|
self.ColorLayers = {g: self.ColorLayers[g] for g in s.glyphs if g in self.ColorLayers}
|
||||||
|
if self.version == 0:
|
||||||
|
return bool(self.ColorLayers)
|
||||||
|
|
||||||
|
colorGlyphsV1 = unbuildColrV1(self.table.LayerV1List, self.table.BaseGlyphV1List)
|
||||||
|
self.table.LayerV1List, self.table.BaseGlyphV1List = buildColrV1(
|
||||||
|
{g: colorGlyphsV1[g] for g in colorGlyphsV1 if g in s.glyphs}
|
||||||
|
)
|
||||||
|
del self.ColorLayersV1
|
||||||
|
|
||||||
|
layersV0 = self.ColorLayers
|
||||||
|
if not self.table.BaseGlyphV1List.BaseGlyphV1Record:
|
||||||
|
# no more COLRv1 glyphs: downgrade to version 0
|
||||||
|
self.version = 0
|
||||||
|
del self.table
|
||||||
|
return bool(layersV0)
|
||||||
|
|
||||||
|
if layersV0:
|
||||||
|
populateCOLRv0(
|
||||||
|
self.table,
|
||||||
|
{
|
||||||
|
g: [(layer.name, layer.colorID) for layer in layersV0[g]]
|
||||||
|
for g in layersV0
|
||||||
|
},
|
||||||
|
)
|
||||||
|
del self.ColorLayers
|
||||||
|
|
||||||
|
# TODO: also prune ununsed varIndices in COLR.VarStore
|
||||||
|
return True
|
||||||
|
|
||||||
# TODO: prune unused palettes
|
|
||||||
@_add_method(ttLib.getTableClass('CPAL'))
|
@_add_method(ttLib.getTableClass('CPAL'))
|
||||||
def prune_post_subset(self, font, options):
|
def prune_post_subset(self, font, options):
|
||||||
return True
|
colr = font.get("COLR")
|
||||||
|
if not colr: # drop CPAL if COLR was subsetted to empty
|
||||||
|
return False
|
||||||
|
|
||||||
|
colors_by_index = defaultdict(list)
|
||||||
|
|
||||||
|
def collect_colors_by_index(paint):
|
||||||
|
if hasattr(paint, "Color"): # either solid colors...
|
||||||
|
colors_by_index[paint.Color.PaletteIndex].append(paint.Color)
|
||||||
|
elif hasattr(paint, "ColorLine"): # ... or gradient color stops
|
||||||
|
for stop in paint.ColorLine.ColorStop:
|
||||||
|
colors_by_index[stop.Color.PaletteIndex].append(stop.Color)
|
||||||
|
|
||||||
|
if colr.version == 0:
|
||||||
|
for layers in colr.ColorLayers.values():
|
||||||
|
for layer in layers:
|
||||||
|
colors_by_index[layer.colorID].append(layer)
|
||||||
|
else:
|
||||||
|
if colr.table.LayerRecordArray:
|
||||||
|
for layer in colr.table.LayerRecordArray.LayerRecord:
|
||||||
|
colors_by_index[layer.PaletteIndex].append(layer)
|
||||||
|
for record in colr.table.BaseGlyphV1List.BaseGlyphV1Record:
|
||||||
|
record.Paint.traverse(colr.table, collect_colors_by_index)
|
||||||
|
|
||||||
|
retained_palette_indices = set(colors_by_index.keys())
|
||||||
|
for palette in self.palettes:
|
||||||
|
palette[:] = [c for i, c in enumerate(palette) if i in retained_palette_indices]
|
||||||
|
assert len(palette) == len(retained_palette_indices)
|
||||||
|
|
||||||
|
for new_index, old_index in enumerate(sorted(retained_palette_indices)):
|
||||||
|
for record in colors_by_index[old_index]:
|
||||||
|
if hasattr(record, "colorID"): # v0
|
||||||
|
record.colorID = new_index
|
||||||
|
elif hasattr(record, "PaletteIndex"): # v1
|
||||||
|
record.PaletteIndex = new_index
|
||||||
|
else:
|
||||||
|
raise AssertionError(record)
|
||||||
|
|
||||||
|
self.numPaletteEntries = len(self.palettes[0])
|
||||||
|
|
||||||
|
if self.version == 1:
|
||||||
|
self.paletteEntryLabels = [
|
||||||
|
label for i, label in self.paletteEntryLabels if i in retained_palette_indices
|
||||||
|
]
|
||||||
|
return bool(self.numPaletteEntries)
|
||||||
|
|
||||||
@_add_method(otTables.MathGlyphConstruction)
|
@_add_method(otTables.MathGlyphConstruction)
|
||||||
def closure_glyphs(self, glyphs):
|
def closure_glyphs(self, glyphs):
|
||||||
|
@ -14,9 +14,11 @@ class table_C_O_L_R_(DefaultTable.DefaultTable):
|
|||||||
ttFont['COLR'][<glyphName>] = <value> will set the color layers for any glyph.
|
ttFont['COLR'][<glyphName>] = <value> will set the color layers for any glyph.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _fromOTTable(self, table):
|
@staticmethod
|
||||||
self.version = 0
|
def _decompileColorLayersV0(table):
|
||||||
self.ColorLayers = colorLayerLists = {}
|
if not table.LayerRecordArray:
|
||||||
|
return {}
|
||||||
|
colorLayerLists = {}
|
||||||
layerRecords = table.LayerRecordArray.LayerRecord
|
layerRecords = table.LayerRecordArray.LayerRecord
|
||||||
numLayerRecords = len(layerRecords)
|
numLayerRecords = len(layerRecords)
|
||||||
for baseRec in table.BaseGlyphRecordArray.BaseGlyphRecord:
|
for baseRec in table.BaseGlyphRecordArray.BaseGlyphRecord:
|
||||||
@ -31,6 +33,7 @@ class table_C_O_L_R_(DefaultTable.DefaultTable):
|
|||||||
LayerRecord(layerRec.LayerGlyph, layerRec.PaletteIndex)
|
LayerRecord(layerRec.LayerGlyph, layerRec.PaletteIndex)
|
||||||
)
|
)
|
||||||
colorLayerLists[baseGlyph] = layers
|
colorLayerLists[baseGlyph] = layers
|
||||||
|
return colorLayerLists
|
||||||
|
|
||||||
def _toOTTable(self, ttFont):
|
def _toOTTable(self, ttFont):
|
||||||
from . import otTables
|
from . import otTables
|
||||||
@ -61,12 +64,12 @@ class table_C_O_L_R_(DefaultTable.DefaultTable):
|
|||||||
table = tableClass()
|
table = tableClass()
|
||||||
table.decompile(reader, ttFont)
|
table.decompile(reader, ttFont)
|
||||||
|
|
||||||
if table.Version == 0:
|
self.version = table.Version
|
||||||
self._fromOTTable(table)
|
if self.version == 0:
|
||||||
|
self.ColorLayers = self._decompileColorLayersV0(table)
|
||||||
else:
|
else:
|
||||||
# for new versions, keep the raw otTables around
|
# for new versions, keep the raw otTables around
|
||||||
self.table = table
|
self.table = table
|
||||||
self.version = table.Version
|
|
||||||
|
|
||||||
def compile(self, ttFont):
|
def compile(self, ttFont):
|
||||||
from .otBase import OTTableWriter
|
from .otBase import OTTableWriter
|
||||||
@ -120,6 +123,7 @@ class table_C_O_L_R_(DefaultTable.DefaultTable):
|
|||||||
self.table = tableClass()
|
self.table = tableClass()
|
||||||
self.table.fromXML(name, attrs, content, ttFont)
|
self.table.fromXML(name, attrs, content, ttFont)
|
||||||
self.table.populateDefaults()
|
self.table.populateDefaults()
|
||||||
|
self.version = self.table.Version
|
||||||
|
|
||||||
def __getitem__(self, glyphName):
|
def __getitem__(self, glyphName):
|
||||||
if not isinstance(glyphName, str):
|
if not isinstance(glyphName, str):
|
||||||
|
@ -1367,6 +1367,40 @@ class Paint(getFormatSwitchingBaseTableClass("uint8")):
|
|||||||
xmlWriter.endtag(tableName)
|
xmlWriter.endtag(tableName)
|
||||||
xmlWriter.newline()
|
xmlWriter.newline()
|
||||||
|
|
||||||
|
def getChildren(self, colr):
|
||||||
|
if self.Format == PaintFormat.PaintColrLayers:
|
||||||
|
return colr.LayerV1List.Paint[
|
||||||
|
self.FirstLayerIndex : self.FirstLayerIndex + self.NumLayers
|
||||||
|
]
|
||||||
|
|
||||||
|
if self.Format == PaintFormat.PaintColrGlyph:
|
||||||
|
for record in colr.BaseGlyphV1List.BaseGlyphV1Record:
|
||||||
|
if record.BaseGlyph == self.Glyph:
|
||||||
|
return [record.Paint]
|
||||||
|
else:
|
||||||
|
raise KeyError(f"{self.Glyph!r} not in colr.BaseGlyphV1List")
|
||||||
|
|
||||||
|
children = []
|
||||||
|
for conv in self.getConverters():
|
||||||
|
if conv.tableClass is not None and issubclass(conv.tableClass, type(self)):
|
||||||
|
children.append(getattr(self, conv.name))
|
||||||
|
|
||||||
|
return children
|
||||||
|
|
||||||
|
def traverse(self, colr: COLR, callback):
|
||||||
|
"""Depth-first traversal of graph rooted at self, callback on each node."""
|
||||||
|
if not callable(callback):
|
||||||
|
raise TypeError("callback must be callable")
|
||||||
|
stack = [self]
|
||||||
|
visited = set()
|
||||||
|
while stack:
|
||||||
|
current = stack.pop()
|
||||||
|
if id(current) in visited:
|
||||||
|
continue
|
||||||
|
callback(current)
|
||||||
|
visited.add(id(current))
|
||||||
|
stack.extend(reversed(current.getChildren(colr)))
|
||||||
|
|
||||||
|
|
||||||
# For each subtable format there is a class. However, we don't really distinguish
|
# For each subtable format there is a class. However, we don't really distinguish
|
||||||
# between "field name" and "format name": often these are the same. Yet there's
|
# between "field name" and "format name": often these are the same. Yet there's
|
||||||
|
@ -3,7 +3,9 @@ from fontTools.misc.py23 import *
|
|||||||
from fontTools.misc.testTools import getXML
|
from fontTools.misc.testTools import getXML
|
||||||
from fontTools import subset
|
from fontTools import subset
|
||||||
from fontTools.fontBuilder import FontBuilder
|
from fontTools.fontBuilder import FontBuilder
|
||||||
|
from fontTools.pens.ttGlyphPen import TTGlyphPen
|
||||||
from fontTools.ttLib import TTFont, newTable
|
from fontTools.ttLib import TTFont, newTable
|
||||||
|
from fontTools.ttLib.tables import otTables as ot
|
||||||
from fontTools.misc.loggingTools import CapturingLogHandler
|
from fontTools.misc.loggingTools import CapturingLogHandler
|
||||||
import difflib
|
import difflib
|
||||||
import logging
|
import logging
|
||||||
@ -930,5 +932,256 @@ def test_subset_empty_glyf(tmp_path, ttf_path):
|
|||||||
assert all(loc == 0 for loc in loca)
|
assert all(loc == 0 for loc in loca)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def colrv1_path(tmp_path):
|
||||||
|
base_glyph_names = ["uni%04X" % i for i in range(0xE000, 0xE000 + 10)]
|
||||||
|
layer_glyph_names = ["glyph%05d" % i for i in range(10, 20)]
|
||||||
|
glyph_order = [".notdef"] + base_glyph_names + layer_glyph_names
|
||||||
|
|
||||||
|
pen = TTGlyphPen(glyphSet=None)
|
||||||
|
pen.moveTo((0, 0))
|
||||||
|
pen.lineTo((0, 500))
|
||||||
|
pen.lineTo((500, 500))
|
||||||
|
pen.lineTo((500, 0))
|
||||||
|
pen.closePath()
|
||||||
|
glyph = pen.glyph()
|
||||||
|
glyphs = {g: glyph for g in glyph_order}
|
||||||
|
|
||||||
|
fb = FontBuilder(unitsPerEm=1024, isTTF=True)
|
||||||
|
fb.setupGlyphOrder(glyph_order)
|
||||||
|
fb.setupCharacterMap({int(name[3:], 16): name for name in base_glyph_names})
|
||||||
|
fb.setupGlyf(glyphs)
|
||||||
|
fb.setupHorizontalMetrics({g: (500, 0) for g in glyph_order})
|
||||||
|
fb.setupHorizontalHeader()
|
||||||
|
fb.setupOS2()
|
||||||
|
fb.setupPost()
|
||||||
|
fb.setupNameTable({"familyName": "TestCOLRv1", "styleName": "Regular"})
|
||||||
|
|
||||||
|
fb.setupCOLR(
|
||||||
|
{
|
||||||
|
"uniE000": (
|
||||||
|
ot.PaintFormat.PaintColrLayers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"Format": ot.PaintFormat.PaintGlyph,
|
||||||
|
"Paint": (ot.PaintFormat.PaintSolid, 0),
|
||||||
|
"Glyph": "glyph00010",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Format": ot.PaintFormat.PaintGlyph,
|
||||||
|
"Paint": (ot.PaintFormat.PaintSolid, (2, 0.3)),
|
||||||
|
"Glyph": "glyph00011",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
"uniE001": (
|
||||||
|
ot.PaintFormat.PaintColrLayers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"Format": ot.PaintFormat.PaintTransform,
|
||||||
|
"Paint": {
|
||||||
|
"Format": ot.PaintFormat.PaintGlyph,
|
||||||
|
"Paint": {
|
||||||
|
"Format": ot.PaintFormat.PaintRadialGradient,
|
||||||
|
"x0": 250,
|
||||||
|
"y0": 250,
|
||||||
|
"r0": 250,
|
||||||
|
"x1": 200,
|
||||||
|
"y1": 200,
|
||||||
|
"r1": 0,
|
||||||
|
"ColorLine": {
|
||||||
|
"ColorStop": [(0.0, 1), (1.0, 2)],
|
||||||
|
"Extend": "repeat",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Glyph": "glyph00012",
|
||||||
|
},
|
||||||
|
"Transform": (0.7071, 0.7071, -0.7071, 0.7071, 0, 0),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Format": ot.PaintFormat.PaintGlyph,
|
||||||
|
"Paint": (ot.PaintFormat.PaintSolid, (1, 0.5)),
|
||||||
|
"Glyph": "glyph00013",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
"uniE002": (
|
||||||
|
ot.PaintFormat.PaintColrLayers,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"Format": ot.PaintFormat.PaintGlyph,
|
||||||
|
"Paint": {
|
||||||
|
"Format": ot.PaintFormat.PaintLinearGradient,
|
||||||
|
"x0": 0,
|
||||||
|
"y0": 0,
|
||||||
|
"x1": 500,
|
||||||
|
"y1": 500,
|
||||||
|
"x2": -500,
|
||||||
|
"y2": 500,
|
||||||
|
"ColorLine": {"ColorStop": [(0.0, 1), (1.0, 2)]},
|
||||||
|
},
|
||||||
|
"Glyph": "glyph00014",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Format": ot.PaintFormat.PaintTransform,
|
||||||
|
"Paint": {
|
||||||
|
"Format": ot.PaintFormat.PaintGlyph,
|
||||||
|
"Paint": (ot.PaintFormat.PaintSolid, 1),
|
||||||
|
"Glyph": "glyph00015",
|
||||||
|
},
|
||||||
|
"Transform": (1, 0, 0, 1, 400, 400),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
"uniE003": {
|
||||||
|
"Format": ot.PaintFormat.PaintRotate,
|
||||||
|
"Paint": {
|
||||||
|
"Format": ot.PaintFormat.PaintColrGlyph,
|
||||||
|
"Glyph": "uniE001",
|
||||||
|
},
|
||||||
|
"angle": 45,
|
||||||
|
"centerX": 250,
|
||||||
|
"centerY": 250,
|
||||||
|
},
|
||||||
|
"uniE004": [
|
||||||
|
("glyph00016", 1),
|
||||||
|
("glyph00017", 2),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
fb.setupCPAL(
|
||||||
|
[
|
||||||
|
[
|
||||||
|
(1.0, 0.0, 0.0, 1.0), # red
|
||||||
|
(0.0, 1.0, 0.0, 1.0), # green
|
||||||
|
(0.0, 0.0, 1.0, 1.0), # blue
|
||||||
|
],
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
output_path = tmp_path / "TestCOLRv1.ttf"
|
||||||
|
fb.save(output_path)
|
||||||
|
|
||||||
|
return output_path
|
||||||
|
|
||||||
|
|
||||||
|
def test_subset_COLRv1_and_CPAL(colrv1_path):
|
||||||
|
subset_path = colrv1_path.parent / (colrv1_path.name + ".subset")
|
||||||
|
|
||||||
|
subset.main(
|
||||||
|
[
|
||||||
|
str(colrv1_path),
|
||||||
|
"--glyph-names",
|
||||||
|
f"--output-file={subset_path}",
|
||||||
|
"--unicodes=E002,E003,E004",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
subset_font = TTFont(subset_path)
|
||||||
|
|
||||||
|
glyph_set = set(subset_font.getGlyphOrder())
|
||||||
|
|
||||||
|
# uniE000 and its children are excluded from subset
|
||||||
|
assert "uniE000" not in glyph_set
|
||||||
|
assert "glyph00010" not in glyph_set
|
||||||
|
assert "glyph00011" not in glyph_set
|
||||||
|
|
||||||
|
# uniE001 and children are pulled in indirectly as PaintColrGlyph by uniE003
|
||||||
|
assert "uniE001" in glyph_set
|
||||||
|
assert "glyph00012" in glyph_set
|
||||||
|
assert "glyph00013" in glyph_set
|
||||||
|
|
||||||
|
assert "uniE002" in glyph_set
|
||||||
|
assert "glyph00014" in glyph_set
|
||||||
|
assert "glyph00015" in glyph_set
|
||||||
|
|
||||||
|
assert "uniE003" in glyph_set
|
||||||
|
|
||||||
|
assert "uniE004" in glyph_set
|
||||||
|
assert "glyph00016" in glyph_set
|
||||||
|
assert "glyph00017" in glyph_set
|
||||||
|
|
||||||
|
assert "COLR" in subset_font
|
||||||
|
colr = subset_font["COLR"].table
|
||||||
|
assert colr.Version == 1
|
||||||
|
assert len(colr.BaseGlyphRecordArray.BaseGlyphRecord) == 1
|
||||||
|
assert len(colr.BaseGlyphV1List.BaseGlyphV1Record) == 3 # was 4
|
||||||
|
|
||||||
|
base = colr.BaseGlyphV1List.BaseGlyphV1Record[0]
|
||||||
|
assert base.BaseGlyph == "uniE001"
|
||||||
|
layers = colr.LayerV1List.Paint[
|
||||||
|
base.Paint.FirstLayerIndex: base.Paint.FirstLayerIndex + base.Paint.NumLayers
|
||||||
|
]
|
||||||
|
assert len(layers) == 2
|
||||||
|
# check v1 palette indices were remapped
|
||||||
|
assert layers[0].Paint.Paint.ColorLine.ColorStop[0].Color.PaletteIndex == 0
|
||||||
|
assert layers[0].Paint.Paint.ColorLine.ColorStop[1].Color.PaletteIndex == 1
|
||||||
|
assert layers[1].Paint.Color.PaletteIndex == 0
|
||||||
|
|
||||||
|
baseRecV0 = colr.BaseGlyphRecordArray.BaseGlyphRecord[0]
|
||||||
|
assert baseRecV0.BaseGlyph == "uniE004"
|
||||||
|
layersV0 = colr.LayerRecordArray.LayerRecord
|
||||||
|
assert len(layersV0) == 2
|
||||||
|
# check v0 palette indices were remapped
|
||||||
|
assert layersV0[0].PaletteIndex == 0
|
||||||
|
assert layersV0[1].PaletteIndex == 1
|
||||||
|
|
||||||
|
assert "CPAL" in subset_font
|
||||||
|
cpal = subset_font["CPAL"]
|
||||||
|
assert [
|
||||||
|
tuple(v / 255 for v in (c.red, c.green, c.blue, c.alpha))
|
||||||
|
for c in cpal.palettes[0]
|
||||||
|
] == [
|
||||||
|
# the first color 'red' was pruned
|
||||||
|
(0.0, 1.0, 0.0, 1.0), # green
|
||||||
|
(0.0, 0.0, 1.0, 1.0), # blue
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_subset_COLRv1_and_CPAL_drop_empty(colrv1_path):
|
||||||
|
subset_path = colrv1_path.parent / (colrv1_path.name + ".subset")
|
||||||
|
|
||||||
|
subset.main(
|
||||||
|
[
|
||||||
|
str(colrv1_path),
|
||||||
|
"--glyph-names",
|
||||||
|
f"--output-file={subset_path}",
|
||||||
|
"--glyphs=glyph00010",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
subset_font = TTFont(subset_path)
|
||||||
|
|
||||||
|
glyph_set = set(subset_font.getGlyphOrder())
|
||||||
|
|
||||||
|
assert "glyph00010" in glyph_set
|
||||||
|
assert "uniE000" not in glyph_set
|
||||||
|
|
||||||
|
assert "COLR" not in subset_font
|
||||||
|
assert "CPAL" not in subset_font
|
||||||
|
|
||||||
|
|
||||||
|
def test_subset_COLRv1_downgrade_version(colrv1_path):
|
||||||
|
subset_path = colrv1_path.parent / (colrv1_path.name + ".subset")
|
||||||
|
|
||||||
|
subset.main(
|
||||||
|
[
|
||||||
|
str(colrv1_path),
|
||||||
|
"--glyph-names",
|
||||||
|
f"--output-file={subset_path}",
|
||||||
|
"--unicodes=E004",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
subset_font = TTFont(subset_path)
|
||||||
|
|
||||||
|
assert set(subset_font.getGlyphOrder()) == {
|
||||||
|
".notdef",
|
||||||
|
"uniE004",
|
||||||
|
"glyph00016",
|
||||||
|
"glyph00017",
|
||||||
|
}
|
||||||
|
|
||||||
|
assert "COLR" in subset_font
|
||||||
|
assert subset_font["COLR"].version == 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
sys.exit(unittest.main())
|
sys.exit(unittest.main())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user