diff --git a/Lib/fontTools/colorLib/builder.py b/Lib/fontTools/colorLib/builder.py index 76b0861cc..3d75567eb 100644 --- a/Lib/fontTools/colorLib/builder.py +++ b/Lib/fontTools/colorLib/builder.py @@ -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 diff --git a/Tests/colorLib/builder_test.py b/Tests/colorLib/builder_test.py index 9d740edcd..41684cfd4 100644 --- a/Tests/colorLib/builder_test.py +++ b/Tests/colorLib/builder_test.py @@ -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,25 +656,27 @@ 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 - "paint": { - "format": 2, - "colorLine": { - "stops": [(0.0, 2), (1.0, 3)], - "extend": "reflect", + "b": [ + { + "format": 5, # PaintGlyph + "paint": { + "format": 3, + "colorLine": { + "stops": [(0.0, 2), (1.0, 3)], + "extend": "reflect", + }, + "p0": (1, 2), + "p1": (3, 4), + "p2": (2, 2), }, - "p0": (1, 2), - "p1": (3, 4), - "p2": (2, 2), - }, - "glyph": "bb", - }], + "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):