Update to builder per review discussion

This commit is contained in:
rsheeter 2020-11-10 21:51:33 -08:00
parent 4171e28f32
commit cf2097f7c0
2 changed files with 262 additions and 219 deletions

View File

@ -152,7 +152,6 @@ def buildCOLR(
if colorGlyphsV1:
colr.LayerV1List, colr.BaseGlyphV1List = buildColrV1(colorGlyphsV1, glyphMap)
if version is None:
version = 1 if (varStore or colorGlyphsV1) else 0
elif version not in (0, 1):
@ -369,15 +368,6 @@ def buildColorIndex(
return self
def buildPaintSolid(
paletteIndex: int, alpha: _ScalarInput = _DEFAULT_ALPHA
) -> ot.Paint:
self = ot.Paint()
self.Format = int(ot.Paint.Format.PaintSolid)
self.Color = buildColorIndex(paletteIndex, alpha)
return self
def buildColorStop(
offset: _ScalarInput,
paletteIndex: int,
@ -433,17 +423,6 @@ def _to_color_line(obj):
raise TypeError(obj)
_PAINT_BUILDERS = {
1: lambda _, kwargs: buildPaintSolid(**kwargs),
2: lambda _, kwargs: buildPaintLinearGradient(**kwargs),
3: lambda _, kwargs: buildPaintRadialGradient(**kwargs),
4: lambda builder, kwargs: buildPaintGlyph(builder, **kwargs),
5: lambda _, kwargs: buildPaintColrGlyph(**kwargs),
6: lambda builder, kwargs: buildPaintTransform(builder, **kwargs),
7: lambda builder, kwargs: buildPaintComposite(builder, **kwargs),
}
def _as_tuple(obj) -> Tuple[Any, ...]:
# start simple, who even cares about cyclic graphs or interesting field types
def _tuple_safe(value):
@ -454,6 +433,7 @@ def _as_tuple(obj) -> Tuple[Any, ...]:
elif isinstance(value, collections.abc.MutableSequence):
return tuple(_tuple_safe(e) for e in value)
return value
return tuple(_tuple_safe(obj))
@ -468,7 +448,7 @@ def _reuse_ranges(num_layers: int) -> Generator[Tuple[int, int], None, None]:
yield (lbound, ubound)
class LayerCollector:
class LayerV1ListBuilder:
slices: List[ot.Paint]
layers: List[ot.Paint]
reusePool: Mapping[Tuple[Any, ...], int]
@ -478,21 +458,107 @@ class LayerCollector:
self.layers = []
self.reusePool = {}
def buildColrLayers(self, paints: List[_PaintInput]) -> ot.Paint:
paint = ot.Paint()
paint.Format = int(ot.Paint.Format.PaintColrLayers)
self.slices.append(paint)
def buildPaintSolid(
self, paletteIndex: int, alpha: _ScalarInput = _DEFAULT_ALPHA
) -> ot.Paint:
ot_paint = ot.Paint()
ot_paint.Format = int(ot.Paint.Format.PaintSolid)
ot_paint.Color = buildColorIndex(paletteIndex, alpha)
return ot_paint
paints = [self.build(p) for p in paints]
def buildPaintLinearGradient(
self,
colorLine: _ColorLineInput,
p0: _PointTuple,
p1: _PointTuple,
p2: Optional[_PointTuple] = None,
) -> ot.Paint:
ot_paint = ot.Paint()
ot_paint.Format = int(ot.Paint.Format.PaintLinearGradient)
ot_paint.ColorLine = _to_color_line(colorLine)
if p2 is None:
p2 = copy.copy(p1)
for i, (x, y) in enumerate((p0, p1, p2)):
setattr(ot_paint, f"x{i}", _to_variable_int16(x))
setattr(ot_paint, f"y{i}", _to_variable_int16(y))
return ot_paint
def buildPaintRadialGradient(
self,
colorLine: _ColorLineInput,
c0: _PointTuple,
c1: _PointTuple,
r0: _ScalarInput,
r1: _ScalarInput,
) -> ot.Paint:
ot_paint = ot.Paint()
ot_paint.Format = int(ot.Paint.Format.PaintRadialGradient)
ot_paint.ColorLine = _to_color_line(colorLine)
for i, (x, y), r in [(0, c0, r0), (1, c1, r1)]:
setattr(ot_paint, f"x{i}", _to_variable_int16(x))
setattr(ot_paint, f"y{i}", _to_variable_int16(y))
setattr(ot_paint, f"r{i}", _to_variable_uint16(r))
return ot_paint
def buildPaintGlyph(self, glyph: str, paint: _PaintInput) -> ot.Paint:
ot_paint = ot.Paint()
ot_paint.Format = int(ot.Paint.Format.PaintGlyph)
ot_paint.Glyph = glyph
ot_paint.Paint = self.buildPaint(paint)
return ot_paint
def buildPaintColrGlyph(self, glyph: str) -> ot.Paint:
ot_paint = ot.Paint()
ot_paint.Format = int(ot.Paint.Format.PaintColrGlyph)
ot_paint.Glyph = glyph
return ot_paint
def buildPaintTransform(
self, transform: _AffineInput, paint: _PaintInput
) -> ot.Paint:
ot_paint = ot.Paint()
ot_paint.Format = int(ot.Paint.Format.PaintTransform)
if not isinstance(transform, ot.Affine2x3):
transform = buildAffine2x3(transform)
ot_paint.Transform = transform
ot_paint.Paint = self.buildPaint(paint)
return ot_paint
def buildPaintComposite(
self,
mode: _CompositeInput,
source: _PaintInput,
backdrop: _PaintInput,
):
ot_paint = ot.Paint()
ot_paint.Format = int(ot.Paint.Format.PaintComposite)
ot_paint.SourcePaint = self.buildPaint(source)
ot_paint.CompositeMode = _to_composite_mode(mode)
ot_paint.BackdropPaint = self.buildPaint(backdrop)
return ot_paint
def buildColrLayers(self, paints: List[_PaintInput]) -> ot.Paint:
ot_paint = ot.Paint()
ot_paint.Format = int(ot.Paint.Format.PaintColrLayers)
self.slices.append(ot_paint)
paints = [self.buildPaint(p) for p in paints]
# Look for reuse, with preference to longer sequences
found_reuse = True
while found_reuse:
found_reuse = False
ranges = sorted(_reuse_ranges(len(paints)),
ranges = sorted(
_reuse_ranges(len(paints)),
key=lambda t: (t[1] - t[0], t[1], t[0]),
reverse=True)
reverse=True,
)
for lbound, ubound in ranges:
reuse_lbound = self.reusePool.get(_as_tuple(paints[lbound:ubound]), -1)
if reuse_lbound == -1:
@ -505,62 +571,56 @@ class LayerCollector:
found_reuse = True
break
paint.NumLayers = len(paints)
paint.FirstLayerIndex = len(self.layers)
ot_paint.NumLayers = len(paints)
ot_paint.FirstLayerIndex = len(self.layers)
self.layers.extend(paints)
# Register our parts for reuse
for lbound, ubound in _reuse_ranges(len(paints)):
self.reusePool[_as_tuple(paints[lbound:ubound])] = lbound + paint.FirstLayerIndex
self.reusePool[_as_tuple(paints[lbound:ubound])] = (
lbound + ot_paint.FirstLayerIndex
)
return paint
return ot_paint
def build(self, paint: _PaintInput) -> ot.Paint:
def buildPaint(self, paint: _PaintInput) -> ot.Paint:
if isinstance(paint, ot.Paint):
return paint
elif isinstance(paint, int):
paletteIndex = paint
return buildPaintSolid(paletteIndex)
return self.buildPaintSolid(paletteIndex)
elif isinstance(paint, tuple):
layerGlyph, paint = paint
return buildPaintGlyph(self, layerGlyph, paint)
return self.buildPaintGlyph(layerGlyph, paint)
elif isinstance(paint, list):
# implicit PaintColrLayers for a list of > 1
if len(paint) == 0:
raise ValueError("An empty list is hard to paint")
elif len(paint) == 1:
return self.build(paint[0])
return self.buildPaint(paint[0])
else:
return self.buildColrLayers(paint)
elif isinstance(paint, collections.abc.Mapping):
kwargs = dict(paint)
fmt = kwargs.pop("format")
try:
return _PAINT_BUILDERS[fmt](self, kwargs)
return LayerV1ListBuilder._buildFunctions[fmt](self, **kwargs)
except KeyError:
raise NotImplementedError(fmt)
raise TypeError(
f"Not sure what to do with {type(paint).__name__}: {paint!r}"
)
raise TypeError(f"Not sure what to do with {type(paint).__name__}: {paint!r}")
def build(self) -> ot.LayerV1List:
layers = ot.LayerV1List()
layers.LayerCount = len(self.layers)
layers.Paint = self.layers
return layers
def buildPaintLinearGradient(
colorLine: _ColorLineInput,
p0: _PointTuple,
p1: _PointTuple,
p2: Optional[_PointTuple] = None,
) -> ot.Paint:
self = ot.Paint()
self.Format = int(ot.Paint.Format.PaintLinearGradient)
self.ColorLine = _to_color_line(colorLine)
if p2 is None:
p2 = copy.copy(p1)
for i, (x, y) in enumerate((p0, p1, p2)):
setattr(self, f"x{i}", _to_variable_int16(x))
setattr(self, f"y{i}", _to_variable_int16(y))
return self
LayerV1ListBuilder._buildFunctions = {
pf.value: getattr(LayerV1ListBuilder, "build" + pf.name)
for pf in ot.Paint.Format
if pf != ot.Paint.Format.PaintColrLayers
}
def buildAffine2x3(transform: _AffineTuple) -> ot.Affine2x3:
@ -581,70 +641,12 @@ def buildAffine2x3(transform: _AffineTuple) -> ot.Affine2x3:
return self
def buildPaintRadialGradient(
colorLine: _ColorLineInput,
c0: _PointTuple,
c1: _PointTuple,
r0: _ScalarInput,
r1: _ScalarInput,
) -> ot.Paint:
self = ot.Paint()
self.Format = int(ot.Paint.Format.PaintRadialGradient)
self.ColorLine = _to_color_line(colorLine)
for i, (x, y), r in [(0, c0, r0), (1, c1, r1)]:
setattr(self, f"x{i}", _to_variable_int16(x))
setattr(self, f"y{i}", _to_variable_int16(y))
setattr(self, f"r{i}", _to_variable_uint16(r))
return self
def buildPaintGlyph(layerCollector: LayerCollector, glyph: str, paint: _PaintInput) -> ot.Paint:
self = ot.Paint()
self.Format = int(ot.Paint.Format.PaintGlyph)
self.Glyph = glyph
self.Paint = layerCollector.build(paint)
return self
def buildPaintColrGlyph(
glyph: str
) -> ot.Paint:
self = ot.Paint()
self.Format = int(ot.Paint.Format.PaintColrGlyph)
self.Glyph = glyph
return self
def buildPaintTransform(layerCollector: LayerCollector, transform: _AffineInput, paint: _PaintInput) -> ot.Paint:
self = ot.Paint()
self.Format = int(ot.Paint.Format.PaintTransform)
if not isinstance(transform, ot.Affine2x3):
transform = buildAffine2x3(transform)
self.Transform = transform
self.Paint = layerCollector.build(paint)
return self
def buildPaintComposite(
layerCollector: LayerCollector, mode: _CompositeInput, source: _PaintInput, backdrop: _PaintInput
):
self = ot.Paint()
self.Format = int(ot.Paint.Format.PaintComposite)
self.SourcePaint = layerCollector.build(source)
self.CompositeMode = _to_composite_mode(mode)
self.BackdropPaint = layerCollector.build(backdrop)
return self
def buildBaseGlyphV1Record(
baseGlyph: str, layerCollector: LayerCollector, paint: _PaintInput
baseGlyph: str, layerBuilder: LayerV1ListBuilder, paint: _PaintInput
) -> ot.BaseGlyphV1List:
self = ot.BaseGlyphV1Record()
self.BaseGlyph = baseGlyph
self.Paint = layerCollector.build(paint)
self.Paint = layerBuilder.buildPaint(paint)
return self
@ -668,10 +670,10 @@ def buildColrV1(
errors = {}
baseGlyphs = []
layerCollector = LayerCollector()
layerBuilder = LayerV1ListBuilder()
for baseGlyph, paint in colorGlyphItems:
try:
baseGlyphs.append(buildBaseGlyphV1Record(baseGlyph, layerCollector, paint))
baseGlyphs.append(buildBaseGlyphV1Record(baseGlyph, layerBuilder, paint))
except (ColorLibError, OverflowError, ValueError, TypeError) as e:
errors[baseGlyph] = e
@ -682,9 +684,7 @@ def buildColrV1(
exc.errors = errors
raise exc from next(iter(errors.values()))
layers = ot.LayerV1List()
layers.LayerCount = len(layerCollector.layers)
layers.Paint = layerCollector.layers
layers = layerBuilder.build()
glyphs = ot.BaseGlyphV1List()
glyphs.BaseGlyphCount = len(baseGlyphs)
glyphs.BaseGlyphV1Record = baseGlyphs

View File

@ -1,7 +1,7 @@
from fontTools.ttLib import newTable
from fontTools.ttLib.tables import otTables as ot
from fontTools.colorLib import builder
from fontTools.colorLib.builder import LayerCollector
from fontTools.colorLib.builder import LayerV1ListBuilder
from fontTools.colorLib.errors import ColorLibError
import pytest
from typing import List
@ -208,19 +208,21 @@ def test_buildColorIndex():
def test_buildPaintSolid():
p = builder.buildPaintSolid(0)
p = LayerV1ListBuilder().buildPaintSolid(0)
assert p.Format == ot.Paint.Format.PaintSolid
assert p.Color.PaletteIndex == 0
assert p.Color.Alpha.value == 1.0
assert p.Color.Alpha.varIdx == 0
p = builder.buildPaintSolid(1, alpha=0.5)
p = LayerV1ListBuilder().buildPaintSolid(1, alpha=0.5)
assert p.Format == ot.Paint.Format.PaintSolid
assert p.Color.PaletteIndex == 1
assert p.Color.Alpha.value == 0.5
assert p.Color.Alpha.varIdx == 0
p = builder.buildPaintSolid(3, alpha=builder.VariableFloat(0.5, varIdx=2))
p = LayerV1ListBuilder().buildPaintSolid(
3, alpha=builder.VariableFloat(0.5, varIdx=2)
)
assert p.Format == ot.Paint.Format.PaintSolid
assert p.Color.PaletteIndex == 3
assert p.Color.Alpha.value == 0.5
@ -297,6 +299,7 @@ def test_buildAffine2x3():
def test_buildPaintLinearGradient():
layerBuilder = LayerV1ListBuilder()
color_stops = [
builder.buildColorStop(0.0, 0),
builder.buildColorStop(0.5, 1),
@ -306,23 +309,24 @@ def test_buildPaintLinearGradient():
p0 = (builder.VariableInt(100), builder.VariableInt(200))
p1 = (builder.VariableInt(150), builder.VariableInt(250))
gradient = builder.buildPaintLinearGradient(color_line, p0, p1)
assert gradient.Format == 2
gradient = layerBuilder.buildPaintLinearGradient(color_line, p0, p1)
assert gradient.Format == 3
assert gradient.ColorLine == color_line
assert (gradient.x0, gradient.y0) == p0
assert (gradient.x1, gradient.y1) == p1
assert (gradient.x2, gradient.y2) == p1
gradient = builder.buildPaintLinearGradient({"stops": color_stops}, p0, p1)
gradient = layerBuilder.buildPaintLinearGradient({"stops": color_stops}, p0, p1)
assert gradient.ColorLine.Extend == builder.ExtendMode.PAD
assert gradient.ColorLine.ColorStop == color_stops
gradient = builder.buildPaintLinearGradient(color_line, p0, p1, p2=(150, 230))
gradient = layerBuilder.buildPaintLinearGradient(color_line, p0, p1, p2=(150, 230))
assert (gradient.x2.value, gradient.y2.value) == (150, 230)
assert (gradient.x2, gradient.y2) != (gradient.x1, gradient.y1)
def test_buildPaintRadialGradient():
layerBuilder = LayerV1ListBuilder()
color_stops = [
builder.buildColorStop(0.0, 0),
builder.buildColorStop(0.5, 1),
@ -334,7 +338,7 @@ def test_buildPaintRadialGradient():
r0 = builder.VariableInt(10)
r1 = builder.VariableInt(5)
gradient = builder.buildPaintRadialGradient(color_line, c0, c1, r0, r1)
gradient = layerBuilder.buildPaintRadialGradient(color_line, c0, c1, r0, r1)
assert gradient.Format == ot.Paint.Format.PaintRadialGradient
assert gradient.ColorLine == color_line
assert (gradient.x0, gradient.y0) == c0
@ -342,29 +346,31 @@ def test_buildPaintRadialGradient():
assert gradient.r0 == r0
assert gradient.r1 == r1
gradient = builder.buildPaintRadialGradient({"stops": color_stops}, c0, c1, r0, r1)
gradient = layerBuilder.buildPaintRadialGradient(
{"stops": color_stops}, c0, c1, r0, r1
)
assert gradient.ColorLine.Extend == builder.ExtendMode.PAD
assert gradient.ColorLine.ColorStop == color_stops
def test_buildPaintGlyph_Solid():
collector = LayerCollector()
layer = builder.buildPaintGlyph(collector, "a", 2)
layerBuilder = LayerV1ListBuilder()
layer = layerBuilder.buildPaintGlyph("a", 2)
assert layer.Glyph == "a"
assert layer.Paint.Format == ot.Paint.Format.PaintSolid
assert layer.Paint.Color.PaletteIndex == 2
layer = builder.buildPaintGlyph(collector, "a", builder.buildPaintSolid(3, 0.9))
layer = layerBuilder.buildPaintGlyph("a", layerBuilder.buildPaintSolid(3, 0.9))
assert layer.Paint.Format == ot.Paint.Format.PaintSolid
assert layer.Paint.Color.PaletteIndex == 3
assert layer.Paint.Color.Alpha.value == 0.9
def test_buildPaintGlyph_LinearGradient():
layer = builder.buildPaintGlyph(
LayerCollector(),
layerBuilder = LayerV1ListBuilder()
layer = layerBuilder.buildPaintGlyph(
"a",
builder.buildPaintLinearGradient(
layerBuilder.buildPaintLinearGradient(
{"stops": [(0.0, 3), (1.0, 4)]}, (100, 200), (150, 250)
),
)
@ -380,10 +386,10 @@ def test_buildPaintGlyph_LinearGradient():
def test_buildPaintGlyph_RadialGradient():
layer = builder.buildPaintGlyph(
LayerCollector(),
layerBuilder = LayerV1ListBuilder()
layer = layerBuilder.buildPaintGlyph(
"a",
builder.buildPaintRadialGradient(
layerBuilder.buildPaintRadialGradient(
{
"stops": [
(0.0, 5),
@ -414,18 +420,19 @@ def test_buildPaintGlyph_RadialGradient():
def test_buildPaintGlyph_Dict_Solid():
layer = builder.buildPaintGlyph(LayerCollector(), "a", {"format": 1, "paletteIndex": 0})
layerBuilder = LayerV1ListBuilder()
layer = layerBuilder.buildPaintGlyph("a", {"format": 2, "paletteIndex": 0})
assert layer.Glyph == "a"
assert layer.Paint.Format == ot.Paint.Format.PaintSolid
assert layer.Paint.Color.PaletteIndex == 0
def test_buildPaintGlyph_Dict_LinearGradient():
layer = builder.buildPaintGlyph(
LayerCollector(),
layerBuilder = LayerV1ListBuilder()
layer = layerBuilder.buildPaintGlyph(
"a",
{
"format": 2,
"format": 3,
"colorLine": {"stops": [(0.0, 0), (1.0, 1)]},
"p0": (0, 0),
"p1": (10, 10),
@ -436,11 +443,11 @@ def test_buildPaintGlyph_Dict_LinearGradient():
def test_buildPaintGlyph_Dict_RadialGradient():
layer = builder.buildPaintGlyph(
LayerCollector(),
layerBuilder = LayerV1ListBuilder()
layer = layerBuilder.buildPaintGlyph(
"a",
{
"format": 3,
"format": 4,
"colorLine": {"stops": [(0.0, 0), (1.0, 1)]},
"c0": (0, 0),
"c1": (10, 10),
@ -453,36 +460,36 @@ def test_buildPaintGlyph_Dict_RadialGradient():
def test_buildPaintColrGlyph():
paint = builder.buildPaintColrGlyph("a")
paint = LayerV1ListBuilder().buildPaintColrGlyph("a")
assert paint.Format == ot.Paint.Format.PaintColrGlyph
assert paint.Glyph == "a"
def test_buildPaintTransform():
paint = builder.buildPaintTransform(
layerCollector=LayerCollector(),
layerBuilder = LayerV1ListBuilder()
paint = layerBuilder.buildPaintTransform(
transform=builder.buildAffine2x3((1, 2, 3, 4, 5, 6)),
paint=builder.buildPaintGlyph(
layerCollector=LayerCollector(),
paint=layerBuilder.buildPaintGlyph(
glyph="a",
paint=builder.buildPaintSolid(paletteIndex=0, alpha=1.0),
paint=layerBuilder.buildPaintSolid(paletteIndex=0, alpha=1.0),
),
)
assert paint.Format == ot.Paint.Format.PaintTransform
assert paint.Paint.Format == ot.Paint.Format.PaintGlyph
assert paint.Paint.Paint.Format == ot.Paint.Format.PaintSolid
assert paint.Transform.xx.value == 1.0
assert paint.Transform.yx.value == 2.0
assert paint.Transform.xy.value == 3.0
assert paint.Transform.yy.value == 4.0
assert paint.Transform.dx.value == 5.0
assert paint.Transform.dy.value == 6.0
assert paint.Paint.Format == ot.Paint.Format.PaintGlyph
paint = builder.buildPaintTransform(
LayerCollector(),
paint = layerBuilder.buildPaintTransform(
(1, 0, 0, 0.3333, 10, 10),
{
"format": 3,
"format": 4,
"colorLine": {"stops": [(0.0, 0), (1.0, 1)]},
"c0": (100, 100),
"c1": (100, 100),
@ -502,18 +509,17 @@ def test_buildPaintTransform():
def test_buildPaintComposite():
collector = LayerCollector()
composite = builder.buildPaintComposite(
layerCollector=collector,
layerBuilder = LayerV1ListBuilder()
composite = layerBuilder.buildPaintComposite(
mode=ot.CompositeMode.SRC_OVER,
source={
"format": 7,
"format": 8,
"mode": "src_over",
"source": {"format": 4, "glyph": "c", "paint": 2},
"backdrop": {"format": 4, "glyph": "b", "paint": 1},
"source": {"format": 5, "glyph": "c", "paint": 2},
"backdrop": {"format": 5, "glyph": "b", "paint": 1},
},
backdrop=builder.buildPaintGlyph(
collector, "a", builder.buildPaintSolid(paletteIndex=0, alpha=1.0)
backdrop=layerBuilder.buildPaintGlyph(
"a", layerBuilder.buildPaintSolid(paletteIndex=0, alpha=1.0)
),
)
@ -541,11 +547,11 @@ def test_buildColrV1():
colorGlyphs = {
"a": [("b", 0), ("c", 1)],
"d": [
("e", {"format": 1, "paletteIndex": 2, "alpha": 0.8}),
("e", {"format": 2, "paletteIndex": 2, "alpha": 0.8}),
(
"f",
{
"format": 3,
"format": 4,
"colorLine": {"stops": [(0.0, 3), (1.0, 4)], "extend": "reflect"},
"c0": (0, 0),
"c1": (0, 0),
@ -583,6 +589,7 @@ def test_buildColrV1():
def test_split_color_glyphs_by_version():
layerBuilder = LayerV1ListBuilder()
colorGlyphs = {
"a": [
("b", 0),
@ -597,7 +604,9 @@ def test_split_color_glyphs_by_version():
assert colorGlyphsV0 == {"a": [("b", 0), ("c", 1), ("d", 2), ("e", 3)]}
assert not colorGlyphsV1
colorGlyphs = {"a": [("b", builder.buildPaintSolid(paletteIndex=0, alpha=0.0))]}
colorGlyphs = {
"a": [("b", layerBuilder.buildPaintSolid(paletteIndex=0, alpha=0.0))]
}
colorGlyphsV0, colorGlyphsV1 = builder._split_color_glyphs_by_version(colorGlyphs)
@ -611,7 +620,7 @@ def test_split_color_glyphs_by_version():
(
"e",
{
"format": 2,
"format": 3,
"colorLine": {"stops": [(0.0, 2), (1.0, 3)]},
"p0": (0, 0),
"p1": (10, 10),
@ -647,15 +656,16 @@ def test_build_layerv1list_empty():
colr = builder.buildCOLR(
{
"a": {
"format": 4, # PaintGlyph
"paint": {"format": 1, "paletteIndex": 2, "alpha": 0.8},
"format": 5, # PaintGlyph
"paint": {"format": 2, "paletteIndex": 2, "alpha": 0.8},
"glyph": "b",
},
# A list of 1 shouldn't become a PaintColrLayers
"b": [{
"format": 4, # PaintGlyph
"b": [
{
"format": 5, # PaintGlyph
"paint": {
"format": 2,
"format": 3,
"colorLine": {
"stops": [(0.0, 2), (1.0, 3)],
"extend": "reflect",
@ -665,7 +675,8 @@ def test_build_layerv1list_empty():
"p2": (2, 2),
},
"glyph": "bb",
}],
}
],
}
)
@ -687,26 +698,28 @@ def _paint_names(paints) -> List[str]:
if paint.Format == int(ot.Paint.Format.PaintGlyph):
result.append(paint.Glyph)
elif paint.Format == int(ot.Paint.Format.PaintColrLayers):
result.append(f"Layers[{paint.FirstLayerIndex}:{paint.FirstLayerIndex+paint.NumLayers}]")
result.append(
f"Layers[{paint.FirstLayerIndex}:{paint.FirstLayerIndex+paint.NumLayers}]"
)
return result
def test_build_layerv1list_simple():
# Two colr glyphs, each with two layers the first of which is common
# All layers use the same solid paint
solid_paint = {"format": 1, "paletteIndex": 2, "alpha": 0.8}
solid_paint = {"format": 2, "paletteIndex": 2, "alpha": 0.8}
backdrop = {
"format": 4, # PaintGlyph
"format": 5, # PaintGlyph
"paint": solid_paint,
"glyph": "back",
}
a_foreground = {
"format": 4, # PaintGlyph
"format": 5, # PaintGlyph
"paint": solid_paint,
"glyph": "a_fore",
}
b_foreground = {
"format": 4, # PaintGlyph
"format": 5, # PaintGlyph
"paint": solid_paint,
"glyph": "b_fore",
}
@ -733,41 +746,46 @@ def test_build_layerv1list_simple():
assert colr.table.BaseGlyphV1List.BaseGlyphCount == 2
assert len(colr.table.BaseGlyphV1List.BaseGlyphV1Record) == 2
assert colr.table.LayerV1List.LayerCount == 4
assert _paint_names(colr.table.LayerV1List.Paint) == ["back", "a_fore", "back", "b_fore"]
assert _paint_names(colr.table.LayerV1List.Paint) == [
"back",
"a_fore",
"back",
"b_fore",
]
def test_build_layerv1list_with_sharing():
# Three colr glyphs, each with two layers in common
solid_paint = {"format": 1, "paletteIndex": 2, "alpha": 0.8}
solid_paint = {"format": 2, "paletteIndex": 2, "alpha": 0.8}
backdrop = [
{
"format": 4, # PaintGlyph
"format": 5, # PaintGlyph
"paint": solid_paint,
"glyph": "back1",
},
{
"format": 4, # PaintGlyph
"format": 5, # PaintGlyph
"paint": solid_paint,
"glyph": "back2",
},
]
a_foreground = {
"format": 4, # PaintGlyph
"format": 5, # PaintGlyph
"paint": solid_paint,
"glyph": "a_fore",
}
b_background = {
"format": 4, # PaintGlyph
"format": 5, # PaintGlyph
"paint": solid_paint,
"glyph": "b_back",
}
b_foreground = {
"format": 4, # PaintGlyph
"format": 5, # PaintGlyph
"paint": solid_paint,
"glyph": "b_fore",
}
c_background = {
"format": 4, # PaintGlyph
"format": 5, # PaintGlyph
"paint": solid_paint,
"glyph": "c_back",
}
@ -789,17 +807,29 @@ def test_build_layerv1list_with_sharing():
baseGlyphs = colr.table.BaseGlyphV1List.BaseGlyphV1Record
assert colr.table.BaseGlyphV1List.BaseGlyphCount == 3
assert len(baseGlyphs) == 3
assert (_paint_names([b.Paint for b in baseGlyphs]) ==
["Layers[0:3]", "Layers[3:6]", "Layers[6:8]"])
assert (_paint_names(colr.table.LayerV1List.Paint) ==
["back1", "back2", "a_fore", "b_back", "Layers[0:2]", "b_fore", "c_back", "Layers[0:2]"])
assert _paint_names([b.Paint for b in baseGlyphs]) == [
"Layers[0:3]",
"Layers[3:6]",
"Layers[6:8]",
]
assert _paint_names(colr.table.LayerV1List.Paint) == [
"back1",
"back2",
"a_fore",
"b_back",
"Layers[0:2]",
"b_fore",
"c_back",
"Layers[0:2]",
]
assert colr.table.LayerV1List.LayerCount == 8
def test_build_layerv1list_with_overlaps():
paints = [
{
"format": 4, # PaintGlyph
"paint": {"format": 1, "paletteIndex": 2, "alpha": 0.8},
"format": 5, # PaintGlyph
"paint": {"format": 2, "paletteIndex": 2, "alpha": 0.8},
"glyph": c,
}
for c in "abcdefghi"
@ -818,14 +848,29 @@ def test_build_layerv1list_with_overlaps():
assertNoV0Content(colr)
baseGlyphs = colr.table.BaseGlyphV1List.BaseGlyphV1Record
#assert colr.table.BaseGlyphV1List.BaseGlyphCount == 2
# assert colr.table.BaseGlyphV1List.BaseGlyphCount == 2
assert (_paint_names(colr.table.LayerV1List.Paint) ==
["a", "b", "c", "d", "Layers[0:4]", "e", "f", "Layers[2:4]", "Layers[5:7]", "g", "h"])
assert (_paint_names([b.Paint for b in baseGlyphs]) ==
["Layers[0:4]", "Layers[4:7]", "Layers[7:11]"])
assert _paint_names(colr.table.LayerV1List.Paint) == [
"a",
"b",
"c",
"d",
"Layers[0:4]",
"e",
"f",
"Layers[2:4]",
"Layers[5:7]",
"g",
"h",
]
assert _paint_names([b.Paint for b in baseGlyphs]) == [
"Layers[0:4]",
"Layers[4:7]",
"Layers[7:11]",
]
assert colr.table.LayerV1List.LayerCount == 11
class BuildCOLRTest(object):
def test_automatic_version_all_solid_color_glyphs(self):
colr = builder.buildCOLR({"a": [("b", 0), ("c", 1)]})
@ -841,7 +886,7 @@ class BuildCOLRTest(object):
(
"b",
{
"format": 3,
"format": 4,
"colorLine": {
"stops": [(0.0, 0), (1.0, 1)],
"extend": "repeat",
@ -852,13 +897,13 @@ class BuildCOLRTest(object):
"r1": 2,
},
),
("c", {"format": 1, "paletteIndex": 2, "alpha": 0.8}),
("c", {"format": 2, "paletteIndex": 2, "alpha": 0.8}),
],
"d": [
(
"e",
{
"format": 2,
"format": 3,
"colorLine": {
"stops": [(0.0, 2), (1.0, 3)],
"extend": "reflect",
@ -885,14 +930,14 @@ class BuildCOLRTest(object):
(
"e",
{
"format": 2,
"format": 3,
"colorLine": {"stops": [(0.0, 2), (1.0, 3)]},
"p0": (1, 2),
"p1": (3, 4),
"p2": (2, 2),
},
),
("f", {"format": 1, "paletteIndex": 2, "alpha": 0.8}),
("f", {"format": 2, "paletteIndex": 2, "alpha": 0.8}),
],
}
)
@ -910,9 +955,7 @@ class BuildCOLRTest(object):
colr.table.BaseGlyphV1List.BaseGlyphV1Record[0], ot.BaseGlyphV1Record
)
assert colr.table.BaseGlyphV1List.BaseGlyphV1Record[0].BaseGlyph == "d"
assert isinstance(
colr.table.LayerV1List, ot.LayerV1List
)
assert isinstance(colr.table.LayerV1List, ot.LayerV1List)
assert colr.table.LayerV1List.Paint[0].Glyph == "e"
def test_explicit_version_0(self):