Update to builder per review discussion
This commit is contained in:
parent
4171e28f32
commit
cf2097f7c0
@ -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
|
||||
|
@ -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):
|
||||
|
Loading…
x
Reference in New Issue
Block a user