diff options
Diffstat (limited to 'Tests/colorLib')
-rw-r--r-- | Tests/colorLib/builder_test.py | 1240 | ||||
-rw-r--r-- | Tests/colorLib/table_builder_test.py | 15 | ||||
-rw-r--r-- | Tests/colorLib/unbuilder_test.py | 210 |
3 files changed, 1065 insertions, 400 deletions
diff --git a/Tests/colorLib/builder_test.py b/Tests/colorLib/builder_test.py index 43ec96a4..81da2818 100644 --- a/Tests/colorLib/builder_test.py +++ b/Tests/colorLib/builder_test.py @@ -3,11 +3,20 @@ from fontTools.ttLib.tables import otTables as ot from fontTools.colorLib import builder from fontTools.colorLib.geometry import round_start_circle_stable_containment, Circle from fontTools.colorLib.builder import LayerV1ListBuilder, _build_n_ary_tree +from fontTools.colorLib.table_builder import TableBuilder from fontTools.colorLib.errors import ColorLibError import pytest from typing import List +def _build(cls, source): + return LayerV1ListBuilder().tableBuilder.build(cls, source) + + +def _buildPaint(source): + return LayerV1ListBuilder().buildPaint(source) + + def test_buildCOLR_v0(): color_layer_lists = { "a": [("a.color0", 0), ("a.color1", 1)], @@ -222,191 +231,361 @@ def test_buildCPAL_invalid_color(): builder.buildCPAL([[(0, 0, 0, 0)], [(1, 1, -1, 2)]]) -def test_buildColorIndex(): - c = builder.buildColorIndex(0) - assert c.PaletteIndex == 0 +def test_buildColorIndex_Minimal(): + c = _build(ot.ColorIndex, 1) + assert c.PaletteIndex == 1 + assert c.Alpha == 1.0 + + +def test_buildVarColorIndex_Minimal(): + c = _build(ot.VarColorIndex, 1) + assert c.PaletteIndex == 1 assert c.Alpha.value == 1.0 assert c.Alpha.varIdx == 0 - c = builder.buildColorIndex(1, alpha=0.5) + +def test_buildColorIndex(): + c = _build(ot.ColorIndex, (1, 0.5)) assert c.PaletteIndex == 1 - assert c.Alpha.value == 0.5 - assert c.Alpha.varIdx == 0 + assert c.Alpha == 0.5 + - c = builder.buildColorIndex(3, alpha=builder.VariableFloat(0.5, varIdx=2)) +def test_buildVarColorIndex(): + c = _build(ot.VarColorIndex, (3, builder.VariableFloat(0.5, varIdx=2))) assert c.PaletteIndex == 3 assert c.Alpha.value == 0.5 assert c.Alpha.varIdx == 2 def test_buildPaintSolid(): - p = LayerV1ListBuilder().buildPaintSolid(0) - assert p.Format == ot.Paint.Format.PaintSolid + p = _buildPaint((ot.PaintFormat.PaintSolid, 0)) + assert p.Format == ot.PaintFormat.PaintSolid assert p.Color.PaletteIndex == 0 - assert p.Color.Alpha.value == 1.0 - assert p.Color.Alpha.varIdx == 0 + assert p.Color.Alpha == 1.0 - p = LayerV1ListBuilder().buildPaintSolid(1, alpha=0.5) - assert p.Format == ot.Paint.Format.PaintSolid + +def test_buildPaintSolid_Alpha(): + p = _buildPaint((ot.PaintFormat.PaintSolid, (1, 0.5))) + assert p.Format == ot.PaintFormat.PaintSolid assert p.Color.PaletteIndex == 1 - assert p.Color.Alpha.value == 0.5 - assert p.Color.Alpha.varIdx == 0 + assert p.Color.Alpha == 0.5 - p = LayerV1ListBuilder().buildPaintSolid( - 3, alpha=builder.VariableFloat(0.5, varIdx=2) + +def test_buildPaintVarSolid(): + p = _buildPaint( + (ot.PaintFormat.PaintVarSolid, (3, builder.VariableFloat(0.5, varIdx=2))) ) - assert p.Format == ot.Paint.Format.PaintSolid + assert p.Format == ot.PaintFormat.PaintVarSolid assert p.Color.PaletteIndex == 3 assert p.Color.Alpha.value == 0.5 assert p.Color.Alpha.varIdx == 2 -def test_buildColorStop(): - s = builder.buildColorStop(0.1, 2) +def test_buildVarColorStop_DefaultAlpha(): + s = _build(ot.ColorStop, (0.1, 2)) + assert s.StopOffset == 0.1 + assert s.Color.PaletteIndex == 2 + assert s.Color.Alpha == builder._DEFAULT_ALPHA.value + + +def test_buildVarColorStop_DefaultAlpha(): + s = _build(ot.VarColorStop, (0.1, 2)) assert s.StopOffset == builder.VariableFloat(0.1) assert s.Color.PaletteIndex == 2 assert s.Color.Alpha == builder._DEFAULT_ALPHA - s = builder.buildColorStop(offset=0.2, paletteIndex=3, alpha=0.4) - assert s.StopOffset == builder.VariableFloat(0.2) - assert s.Color == builder.buildColorIndex(3, alpha=0.4) - s = builder.buildColorStop( - offset=builder.VariableFloat(0.0, varIdx=1), - paletteIndex=0, - alpha=builder.VariableFloat(0.3, varIdx=2), +def test_buildColorStop(): + s = _build( + ot.ColorStop, {"StopOffset": 0.2, "Color": {"PaletteIndex": 3, "Alpha": 0.4}} + ) + assert s.StopOffset == 0.2 + assert s.Color == _build(ot.ColorIndex, (3, 0.4)) + + +def test_buildColorStop_Variable(): + s = _build( + ot.VarColorStop, + { + "StopOffset": builder.VariableFloat(0.0, varIdx=1), + "Color": { + "PaletteIndex": 0, + "Alpha": builder.VariableFloat(0.3, varIdx=2), + }, + }, ) assert s.StopOffset == builder.VariableFloat(0.0, varIdx=1) assert s.Color.PaletteIndex == 0 assert s.Color.Alpha == builder.VariableFloat(0.3, varIdx=2) -def test_buildColorLine(): +def test_buildColorLine_StopList(): stops = [(0.0, 0), (0.5, 1), (1.0, 2)] - cline = builder.buildColorLine(stops) + cline = _build(ot.ColorLine, {"ColorStop": stops}) assert cline.Extend == builder.ExtendMode.PAD assert cline.StopCount == 3 - assert [ - (cs.StopOffset.value, cs.Color.PaletteIndex) for cs in cline.ColorStop - ] == stops + assert [(cs.StopOffset, cs.Color.PaletteIndex) for cs in cline.ColorStop] == stops - cline = builder.buildColorLine(stops, extend="pad") + cline = _build(ot.ColorLine, {"Extend": "pad", "ColorStop": stops}) assert cline.Extend == builder.ExtendMode.PAD - cline = builder.buildColorLine(stops, extend=builder.ExtendMode.REPEAT) + cline = _build( + ot.ColorLine, {"ColorStop": stops, "Extend": builder.ExtendMode.REPEAT} + ) assert cline.Extend == builder.ExtendMode.REPEAT - cline = builder.buildColorLine(stops, extend=builder.ExtendMode.REFLECT) + cline = _build( + ot.ColorLine, {"ColorStop": stops, "Extend": builder.ExtendMode.REFLECT} + ) assert cline.Extend == builder.ExtendMode.REFLECT - cline = builder.buildColorLine([builder.buildColorStop(*s) for s in stops]) - assert [ - (cs.StopOffset.value, cs.Color.PaletteIndex) for cs in cline.ColorStop - ] == stops + cline = _build( + ot.ColorLine, {"ColorStop": [_build(ot.ColorStop, s) for s in stops]} + ) + assert [(cs.StopOffset, cs.Color.PaletteIndex) for cs in cline.ColorStop] == stops + +def test_buildVarColorLine_StopMap(): stops = [ - {"offset": (0.0, 1), "paletteIndex": 0, "alpha": (0.5, 2)}, - {"offset": (1.0, 3), "paletteIndex": 1, "alpha": (0.3, 4)}, + {"StopOffset": (0.0, (1,)), "Color": {"PaletteIndex": 0, "Alpha": (0.5, 2)}}, + {"StopOffset": (1.0, (3,)), "Color": {"PaletteIndex": 1, "Alpha": (0.3, 4)}}, ] - cline = builder.buildColorLine(stops) + cline = _build(ot.VarColorLine, {"ColorStop": stops}) assert [ { - "offset": cs.StopOffset, - "paletteIndex": cs.Color.PaletteIndex, - "alpha": cs.Color.Alpha, + "StopOffset": cs.StopOffset, + "Color": { + "PaletteIndex": cs.Color.PaletteIndex, + "Alpha": cs.Color.Alpha, + }, } for cs in cline.ColorStop ] == stops +def checkBuildAffine2x3(cls, resultMapFn): + matrix = _build(cls, (1.5, 0, 0.5, 2.0, 1.0, -3.0)) + assert matrix.xx == resultMapFn(1.5) + assert matrix.yx == resultMapFn(0.0) + assert matrix.xy == resultMapFn(0.5) + assert matrix.yy == resultMapFn(2.0) + assert matrix.dx == resultMapFn(1.0) + assert matrix.dy == resultMapFn(-3.0) + + def test_buildAffine2x3(): - matrix = builder.buildAffine2x3((1.5, 0, 0.5, 2.0, 1.0, -3.0)) - assert matrix.xx == builder.VariableFloat(1.5) - assert matrix.yx == builder.VariableFloat(0.0) - assert matrix.xy == builder.VariableFloat(0.5) - assert matrix.yy == builder.VariableFloat(2.0) - assert matrix.dx == builder.VariableFloat(1.0) - assert matrix.dy == builder.VariableFloat(-3.0) + checkBuildAffine2x3(ot.Affine2x3, lambda v: v) -def test_buildPaintLinearGradient(): - layerBuilder = LayerV1ListBuilder() - color_stops = [ - builder.buildColorStop(0.0, 0), - builder.buildColorStop(0.5, 1), - builder.buildColorStop(1.0, 2, alpha=0.8), +def test_buildVarAffine2x3(): + checkBuildAffine2x3(ot.VarAffine2x3, builder.VariableFloat) + + +def _sample_stops(cls): + return [ + _build(cls, (0.0, 0)), + _build(cls, (0.5, 1)), + _build(cls, (1.0, (2, 0.8))), ] - color_line = builder.buildColorLine(color_stops, extend=builder.ExtendMode.REPEAT) - p0 = (builder.VariableInt(100), builder.VariableInt(200)) - p1 = (builder.VariableInt(150), builder.VariableInt(250)) - 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 = layerBuilder.buildPaintLinearGradient({"stops": color_stops}, p0, p1) +def _is_var(fmt): + return fmt.name.startswith("PaintVar") + + +def checkBuildPaintLinearGradient(fmt): + if _is_var(fmt): + inputMapFn = builder.VariableInt + outputMapFn = lambda v: v.value + color_stops = _sample_stops(ot.VarColorStop) + else: + inputMapFn = outputMapFn = lambda v: v + color_stops = _sample_stops(ot.ColorStop) + + x0, y0, x1, y1, x2, y2 = tuple(inputMapFn(v) for v in (1, 2, 3, 4, 5, 6)) + gradient = _buildPaint( + { + "Format": fmt, + "ColorLine": {"ColorStop": color_stops}, + "x0": x0, + "y0": y0, + "x1": x1, + "y1": y1, + "x2": x2, + "y2": y2, + }, + ) assert gradient.ColorLine.Extend == builder.ExtendMode.PAD assert gradient.ColorLine.ColorStop == color_stops - 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) + gradient = _buildPaint(gradient) + assert (outputMapFn(gradient.x0), outputMapFn(gradient.y0)) == (1, 2) + assert (outputMapFn(gradient.x1), outputMapFn(gradient.y1)) == (3, 4) + assert (outputMapFn(gradient.x2), outputMapFn(gradient.y2)) == (5, 6) -def test_buildPaintRadialGradient(): - layerBuilder = LayerV1ListBuilder() - color_stops = [ - builder.buildColorStop(0.0, 0), - builder.buildColorStop(0.5, 1), - builder.buildColorStop(1.0, 2, alpha=0.8), - ] - color_line = builder.buildColorLine(color_stops, extend=builder.ExtendMode.REPEAT) - c0 = (builder.VariableInt(100), builder.VariableInt(200)) - c1 = (builder.VariableInt(150), builder.VariableInt(250)) - r0 = builder.VariableInt(10) - r1 = builder.VariableInt(5) - - gradient = layerBuilder.buildPaintRadialGradient(color_line, c0, c1, r0, r1) - assert gradient.Format == ot.Paint.Format.PaintRadialGradient +def test_buildPaintLinearGradient(): + assert not _is_var(ot.PaintFormat.PaintLinearGradient) + checkBuildPaintLinearGradient(ot.PaintFormat.PaintLinearGradient) + + +def test_buildVarPaintLinearGradient(): + assert _is_var(ot.PaintFormat.PaintVarLinearGradient) + checkBuildPaintLinearGradient(ot.PaintFormat.PaintVarLinearGradient) + + +def checkBuildPaintRadialGradient(fmt): + if _is_var(fmt): + inputMapFn = builder.VariableInt + outputMapFn = lambda v: v + color_stops = _sample_stops(ot.VarColorStop) + line_cls = ot.VarColorLine + else: + inputMapFn = outputMapFn = lambda v: v + color_stops = _sample_stops(ot.ColorStop) + line_cls = ot.ColorLine + + color_line = _build( + line_cls, {"ColorStop": color_stops, "Extend": builder.ExtendMode.REPEAT} + ) + c0 = (inputMapFn(100), inputMapFn(200)) + c1 = (inputMapFn(150), inputMapFn(250)) + r0 = inputMapFn(10) + r1 = inputMapFn(5) + + gradient = _build(ot.Paint, (fmt, color_line, *c0, r0, *c1, r1)) + assert gradient.Format == fmt assert gradient.ColorLine == color_line - assert (gradient.x0, gradient.y0) == c0 - assert (gradient.x1, gradient.y1) == c1 - assert gradient.r0 == r0 - assert gradient.r1 == r1 + assert (outputMapFn(gradient.x0), outputMapFn(gradient.y0)) == c0 + assert (outputMapFn(gradient.x1), outputMapFn(gradient.y1)) == c1 + assert outputMapFn(gradient.r0) == r0 + assert outputMapFn(gradient.r1) == r1 - gradient = layerBuilder.buildPaintRadialGradient( - {"stops": color_stops}, c0, c1, r0, r1 + gradient = _build( + ot.Paint, + { + "Format": fmt, + "ColorLine": {"ColorStop": color_stops}, + "x0": c0[0], + "y0": c0[1], + "x1": c1[0], + "y1": c1[1], + "r0": r0, + "r1": r1, + }, ) assert gradient.ColorLine.Extend == builder.ExtendMode.PAD assert gradient.ColorLine.ColorStop == color_stops + assert (outputMapFn(gradient.x0), outputMapFn(gradient.y0)) == c0 + assert (outputMapFn(gradient.x1), outputMapFn(gradient.y1)) == c1 + assert outputMapFn(gradient.r0) == r0 + assert outputMapFn(gradient.r1) == r1 + + +def test_buildPaintRadialGradient(): + assert not _is_var(ot.PaintFormat.PaintRadialGradient) + checkBuildPaintRadialGradient(ot.PaintFormat.PaintRadialGradient) + + +def test_buildPaintVarRadialGradient(): + assert _is_var(ot.PaintFormat.PaintVarRadialGradient) + checkBuildPaintRadialGradient(ot.PaintFormat.PaintVarRadialGradient) + + +def checkPaintSweepGradient(fmt): + if _is_var(fmt): + outputMapFn = lambda v: v.value + else: + outputMapFn = lambda v: v + + paint = _buildPaint( + { + "Format": fmt, + "ColorLine": { + "ColorStop": ( + (0.0, 0), + (0.5, 1), + (1.0, (2, 0.8)), + ) + }, + "centerX": 127, + "centerY": 129, + "startAngle": 15, + "endAngle": 42, + } + ) + + assert paint.Format == fmt + assert outputMapFn(paint.centerX) == 127 + assert outputMapFn(paint.centerY) == 129 + assert outputMapFn(paint.startAngle) == 15 + assert outputMapFn(paint.endAngle) == 42 + + +def test_buildPaintSweepGradient(): + assert not _is_var(ot.PaintFormat.PaintSweepGradient) + checkPaintSweepGradient(ot.PaintFormat.PaintSweepGradient) + + +def test_buildPaintVarSweepGradient(): + assert _is_var(ot.PaintFormat.PaintVarSweepGradient) + checkPaintSweepGradient(ot.PaintFormat.PaintVarSweepGradient) def test_buildPaintGlyph_Solid(): - layerBuilder = LayerV1ListBuilder() - layer = layerBuilder.buildPaintGlyph("a", 2) + layer = _build( + ot.Paint, + ( + ot.PaintFormat.PaintGlyph, + ( + ot.PaintFormat.PaintSolid, + 2, + ), + "a", + ), + ) + assert layer.Format == ot.PaintFormat.PaintGlyph assert layer.Glyph == "a" - assert layer.Paint.Format == ot.Paint.Format.PaintSolid + assert layer.Paint.Format == ot.PaintFormat.PaintSolid assert layer.Paint.Color.PaletteIndex == 2 - layer = layerBuilder.buildPaintGlyph("a", layerBuilder.buildPaintSolid(3, 0.9)) - assert layer.Paint.Format == ot.Paint.Format.PaintSolid + layer = _build( + ot.Paint, + ( + ot.PaintFormat.PaintGlyph, + ( + ot.PaintFormat.PaintSolid, + (3, 0.9), + ), + "a", + ), + ) + assert layer.Paint.Format == ot.PaintFormat.PaintSolid assert layer.Paint.Color.PaletteIndex == 3 - assert layer.Paint.Color.Alpha.value == 0.9 + assert layer.Paint.Color.Alpha == 0.9 -def test_buildPaintGlyph_LinearGradient(): - layerBuilder = LayerV1ListBuilder() - layer = layerBuilder.buildPaintGlyph( - "a", - layerBuilder.buildPaintLinearGradient( - {"stops": [(0.0, 3), (1.0, 4)]}, (100, 200), (150, 250) - ), +def test_buildPaintGlyph_VarLinearGradient(): + layer = _build( + ot.Paint, + { + "Format": ot.PaintFormat.PaintGlyph, + "Glyph": "a", + "Paint": { + "Format": ot.PaintFormat.PaintVarLinearGradient, + "ColorLine": {"ColorStop": [(0.0, 3), (1.0, 4)]}, + "x0": 100, + "y0": 200, + "x1": 150, + "y1": 250, + }, + }, ) - assert layer.Paint.Format == ot.Paint.Format.PaintLinearGradient + + assert layer.Format == ot.PaintFormat.PaintGlyph + assert layer.Glyph == "a" + assert layer.Paint.Format == ot.PaintFormat.PaintVarLinearGradient assert layer.Paint.ColorLine.ColorStop[0].StopOffset.value == 0.0 assert layer.Paint.ColorLine.ColorStop[0].Color.PaletteIndex == 3 assert layer.Paint.ColorLine.ColorStop[1].StopOffset.value == 1.0 @@ -418,235 +597,387 @@ def test_buildPaintGlyph_LinearGradient(): def test_buildPaintGlyph_RadialGradient(): - layerBuilder = LayerV1ListBuilder() - layer = layerBuilder.buildPaintGlyph( - "a", - layerBuilder.buildPaintRadialGradient( - { - "stops": [ - (0.0, 5), - {"offset": 0.5, "paletteIndex": 6, "alpha": 0.8}, - (1.0, 7), - ] - }, - (50, 50), - (75, 75), - 30, - 10, + layer = _build( + ot.Paint, + ( + int(ot.PaintFormat.PaintGlyph), + ( + ot.PaintFormat.PaintRadialGradient, + ( + "pad", + [ + (0.0, 5), + {"StopOffset": 0.5, "Color": {"PaletteIndex": 6, "Alpha": 0.8}}, + (1.0, 7), + ], + ), + 50, + 50, + 30, + 75, + 75, + 10, + ), + "a", ), ) - assert layer.Paint.Format == ot.Paint.Format.PaintRadialGradient - assert layer.Paint.ColorLine.ColorStop[0].StopOffset.value == 0.0 + assert layer.Format == ot.PaintFormat.PaintGlyph + assert layer.Paint.Format == ot.PaintFormat.PaintRadialGradient + assert layer.Paint.ColorLine.ColorStop[0].StopOffset == 0.0 assert layer.Paint.ColorLine.ColorStop[0].Color.PaletteIndex == 5 - assert layer.Paint.ColorLine.ColorStop[1].StopOffset.value == 0.5 + assert layer.Paint.ColorLine.ColorStop[1].StopOffset == 0.5 assert layer.Paint.ColorLine.ColorStop[1].Color.PaletteIndex == 6 - assert layer.Paint.ColorLine.ColorStop[1].Color.Alpha.value == 0.8 - assert layer.Paint.ColorLine.ColorStop[2].StopOffset.value == 1.0 + assert layer.Paint.ColorLine.ColorStop[1].Color.Alpha == 0.8 + assert layer.Paint.ColorLine.ColorStop[2].StopOffset == 1.0 assert layer.Paint.ColorLine.ColorStop[2].Color.PaletteIndex == 7 - assert layer.Paint.x0.value == 50 - assert layer.Paint.y0.value == 50 - assert layer.Paint.r0.value == 30 - assert layer.Paint.x1.value == 75 - assert layer.Paint.y1.value == 75 - assert layer.Paint.r1.value == 10 + assert layer.Paint.x0 == 50 + assert layer.Paint.y0 == 50 + assert layer.Paint.r0 == 30 + assert layer.Paint.x1 == 75 + assert layer.Paint.y1 == 75 + assert layer.Paint.r1 == 10 def test_buildPaintGlyph_Dict_Solid(): - layerBuilder = LayerV1ListBuilder() - layer = layerBuilder.buildPaintGlyph("a", {"format": 2, "paletteIndex": 0}) + layer = _build( + ot.Paint, + ( + int(ot.PaintFormat.PaintGlyph), + (int(ot.PaintFormat.PaintSolid), 1), + "a", + ), + ) + assert layer.Format == ot.PaintFormat.PaintGlyph + assert layer.Format == ot.PaintFormat.PaintGlyph assert layer.Glyph == "a" - assert layer.Paint.Format == ot.Paint.Format.PaintSolid - assert layer.Paint.Color.PaletteIndex == 0 + assert layer.Paint.Format == ot.PaintFormat.PaintSolid + assert layer.Paint.Color.PaletteIndex == 1 -def test_buildPaintGlyph_Dict_LinearGradient(): - layerBuilder = LayerV1ListBuilder() - layer = layerBuilder.buildPaintGlyph( - "a", +def test_buildPaintGlyph_Dict_VarLinearGradient(): + layer = _build( + ot.Paint, { - "format": 3, - "colorLine": {"stops": [(0.0, 0), (1.0, 1)]}, - "p0": (0, 0), - "p1": (10, 10), + "Format": ot.PaintFormat.PaintGlyph, + "Glyph": "a", + "Paint": { + "Format": int(ot.PaintFormat.PaintVarLinearGradient), + "ColorLine": {"ColorStop": [(0.0, 0), (1.0, 1)]}, + "x0": 0, + "y0": 0, + "x1": 10, + "y1": 10, + }, }, ) - assert layer.Paint.Format == ot.Paint.Format.PaintLinearGradient + assert layer.Format == ot.PaintFormat.PaintGlyph + assert layer.Glyph == "a" + assert layer.Paint.Format == ot.PaintFormat.PaintVarLinearGradient assert layer.Paint.ColorLine.ColorStop[0].StopOffset.value == 0.0 def test_buildPaintGlyph_Dict_RadialGradient(): - layerBuilder = LayerV1ListBuilder() - layer = layerBuilder.buildPaintGlyph( - "a", + layer = _buildPaint( { - "format": 4, - "colorLine": {"stops": [(0.0, 0), (1.0, 1)]}, - "c0": (0, 0), - "c1": (10, 10), - "r0": 4, - "r1": 0, + "Glyph": "a", + "Paint": { + "Format": int(ot.PaintFormat.PaintRadialGradient), + "ColorLine": {"ColorStop": [(0.0, 0), (1.0, 1)]}, + "x0": 0, + "y0": 0, + "r0": 4, + "x1": 10, + "y1": 10, + "r1": 0, + }, + "Format": int(ot.PaintFormat.PaintGlyph), }, ) - assert layer.Paint.Format == ot.Paint.Format.PaintRadialGradient - assert layer.Paint.r0.value == 4 + assert layer.Paint.Format == ot.PaintFormat.PaintRadialGradient + assert layer.Paint.r0 == 4 def test_buildPaintColrGlyph(): - paint = LayerV1ListBuilder().buildPaintColrGlyph("a") - assert paint.Format == ot.Paint.Format.PaintColrGlyph + paint = _buildPaint((int(ot.PaintFormat.PaintColrGlyph), "a")) + assert paint.Format == ot.PaintFormat.PaintColrGlyph assert paint.Glyph == "a" -def test_buildPaintTransform(): - layerBuilder = LayerV1ListBuilder() - paint = layerBuilder.buildPaintTransform( - transform=builder.buildAffine2x3((1, 2, 3, 4, 5, 6)), - paint=layerBuilder.buildPaintGlyph( - glyph="a", - paint=layerBuilder.buildPaintSolid(paletteIndex=0, alpha=1.0), +def checkBuildPaintTransform(fmt): + if _is_var(fmt): + inputMapFn = builder.VariableFloat + outputMapFn = lambda v: v.value + affine_cls = ot.VarAffine2x3 + else: + inputMapFn = outputMapFn = lambda v: v + affine_cls = ot.Affine2x3 + + paint = _buildPaint( + ( + int(fmt), + (ot.PaintFormat.PaintGlyph, (ot.PaintFormat.PaintSolid, (0, 1.0)), "a"), + _build(affine_cls, (1, 2, 3, 4, 5, 6)), ), ) - 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.Format == fmt + assert paint.Paint.Format == ot.PaintFormat.PaintGlyph + assert paint.Paint.Paint.Format == ot.PaintFormat.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 outputMapFn(paint.Transform.xx) == 1.0 + assert outputMapFn(paint.Transform.yx) == 2.0 + assert outputMapFn(paint.Transform.xy) == 3.0 + assert outputMapFn(paint.Transform.yy) == 4.0 + assert outputMapFn(paint.Transform.dx) == 5.0 + assert outputMapFn(paint.Transform.dy) == 6.0 - paint = layerBuilder.buildPaintTransform( - (1, 0, 0, 0.3333, 10, 10), + paint = _build( + ot.Paint, { - "format": 4, - "colorLine": {"stops": [(0.0, 0), (1.0, 1)]}, - "c0": (100, 100), - "c1": (100, 100), - "r0": 0, - "r1": 50, + "Format": fmt, + "Transform": (1, 2, 3, 0.3333, 10, 10), + "Paint": { + "Format": int(ot.PaintFormat.PaintRadialGradient), + "ColorLine": {"ColorStop": [(0.0, 0), (1.0, 1)]}, + "x0": 100, + "y0": 101, + "x1": 102, + "y1": 103, + "r0": 0, + "r1": 50, + }, }, ) - assert paint.Format == ot.Paint.Format.PaintTransform - assert paint.Transform.xx.value == 1.0 - assert paint.Transform.yx.value == 0.0 - assert paint.Transform.xy.value == 0.0 - assert paint.Transform.yy.value == 0.3333 - assert paint.Transform.dx.value == 10 - assert paint.Transform.dy.value == 10 - assert paint.Paint.Format == ot.Paint.Format.PaintRadialGradient + assert paint.Format == fmt + assert outputMapFn(paint.Transform.xx) == 1.0 + assert outputMapFn(paint.Transform.yx) == 2.0 + assert outputMapFn(paint.Transform.xy) == 3.0 + assert outputMapFn(paint.Transform.yy) == 0.3333 + assert outputMapFn(paint.Transform.dx) == 10 + assert outputMapFn(paint.Transform.dy) == 10 + assert paint.Paint.Format == ot.PaintFormat.PaintRadialGradient + + +def test_buildPaintTransform(): + assert not _is_var(ot.PaintFormat.PaintTransform) + checkBuildPaintTransform(ot.PaintFormat.PaintTransform) + + +def test_buildPaintVarTransform(): + assert _is_var(ot.PaintFormat.PaintVarTransform) + checkBuildPaintTransform(ot.PaintFormat.PaintVarTransform) def test_buildPaintComposite(): - layerBuilder = LayerV1ListBuilder() - composite = layerBuilder.buildPaintComposite( - mode=ot.CompositeMode.SRC_OVER, - source={ - "format": 11, - "mode": "src_over", - "source": {"format": 5, "glyph": "c", "paint": 2}, - "backdrop": {"format": 5, "glyph": "b", "paint": 1}, + composite = _build( + ot.Paint, + { + "Format": int(ot.PaintFormat.PaintComposite), + "CompositeMode": "src_over", + "SourcePaint": { + "Format": ot.PaintFormat.PaintComposite, + "CompositeMode": "src_over", + "SourcePaint": { + "Format": int(ot.PaintFormat.PaintGlyph), + "Glyph": "c", + "Paint": (ot.PaintFormat.PaintSolid, 2), + }, + "BackdropPaint": { + "Format": int(ot.PaintFormat.PaintGlyph), + "Glyph": "b", + "Paint": (ot.PaintFormat.PaintSolid, 1), + }, + }, + "BackdropPaint": { + "Format": ot.PaintFormat.PaintGlyph, + "Glyph": "a", + "Paint": { + "Format": ot.PaintFormat.PaintSolid, + "Color": (0, 1.0), + }, + }, }, - backdrop=layerBuilder.buildPaintGlyph( - "a", layerBuilder.buildPaintSolid(paletteIndex=0, alpha=1.0) - ), ) - assert composite.Format == ot.Paint.Format.PaintComposite - assert composite.SourcePaint.Format == ot.Paint.Format.PaintComposite - assert composite.SourcePaint.SourcePaint.Format == ot.Paint.Format.PaintGlyph + assert composite.Format == ot.PaintFormat.PaintComposite + assert composite.SourcePaint.Format == ot.PaintFormat.PaintComposite + assert composite.SourcePaint.SourcePaint.Format == ot.PaintFormat.PaintGlyph assert composite.SourcePaint.SourcePaint.Glyph == "c" - assert composite.SourcePaint.SourcePaint.Paint.Format == ot.Paint.Format.PaintSolid + assert composite.SourcePaint.SourcePaint.Paint.Format == ot.PaintFormat.PaintSolid assert composite.SourcePaint.SourcePaint.Paint.Color.PaletteIndex == 2 assert composite.SourcePaint.CompositeMode == ot.CompositeMode.SRC_OVER - assert composite.SourcePaint.BackdropPaint.Format == ot.Paint.Format.PaintGlyph + assert composite.SourcePaint.BackdropPaint.Format == ot.PaintFormat.PaintGlyph assert composite.SourcePaint.BackdropPaint.Glyph == "b" - assert ( - composite.SourcePaint.BackdropPaint.Paint.Format == ot.Paint.Format.PaintSolid - ) + assert composite.SourcePaint.BackdropPaint.Paint.Format == ot.PaintFormat.PaintSolid assert composite.SourcePaint.BackdropPaint.Paint.Color.PaletteIndex == 1 assert composite.CompositeMode == ot.CompositeMode.SRC_OVER - assert composite.BackdropPaint.Format == ot.Paint.Format.PaintGlyph + assert composite.BackdropPaint.Format == ot.PaintFormat.PaintGlyph assert composite.BackdropPaint.Glyph == "a" - assert composite.BackdropPaint.Paint.Format == ot.Paint.Format.PaintSolid + assert composite.BackdropPaint.Paint.Format == ot.PaintFormat.PaintSolid assert composite.BackdropPaint.Paint.Color.PaletteIndex == 0 +def checkBuildPaintTranslate(fmt): + if _is_var(fmt): + inputMapFn = builder.VariableInt + outputMapFn = lambda v: v.value + else: + inputMapFn = outputMapFn = lambda v: v + + paint = _build( + ot.Paint, + { + "Format": fmt, + "Paint": ( + ot.PaintFormat.PaintGlyph, + (ot.PaintFormat.PaintSolid, (0, 1.0)), + "a", + ), + "dx": 123, + "dy": -345, + }, + ) + + assert paint.Format == fmt + assert paint.Paint.Format == ot.PaintFormat.PaintGlyph + assert outputMapFn(paint.dx) == 123 + assert outputMapFn(paint.dy) == -345 + + def test_buildPaintTranslate(): - layerBuilder = LayerV1ListBuilder() - paint = layerBuilder.buildPaintTranslate( - paint=layerBuilder.buildPaintGlyph( - "a", layerBuilder.buildPaintSolid(paletteIndex=0, alpha=1.0) - ), - dx=123, - dy=-345, + assert not _is_var(ot.PaintFormat.PaintTranslate) + checkBuildPaintTranslate(ot.PaintFormat.PaintTranslate) + + +def test_buildPaintVarTranslate(): + assert _is_var(ot.PaintFormat.PaintVarTranslate) + checkBuildPaintTranslate(ot.PaintFormat.PaintVarTranslate) + + +def checkBuildPaintRotate(fmt): + if _is_var(fmt): + inputMapFn = builder.VariableInt + outputMapFn = lambda v: v.value + else: + inputMapFn = outputMapFn = lambda v: v + + paint = _build( + ot.Paint, + { + "Format": fmt, + "Paint": ( + ot.PaintFormat.PaintGlyph, + (ot.PaintFormat.PaintSolid, (0, 1.0)), + "a", + ), + "angle": 15, + "centerX": 127, + "centerY": 129, + }, ) - assert paint.Format == ot.Paint.Format.PaintTranslate - assert paint.Paint.Format == ot.Paint.Format.PaintGlyph - assert paint.dx.value == 123 - assert paint.dy.value == -345 + assert paint.Format == fmt + assert paint.Paint.Format == ot.PaintFormat.PaintGlyph + assert outputMapFn(paint.angle) == 15 + assert outputMapFn(paint.centerX) == 127 + assert outputMapFn(paint.centerY) == 129 def test_buildPaintRotate(): - layerBuilder = LayerV1ListBuilder() - paint = layerBuilder.buildPaintRotate( - paint=layerBuilder.buildPaintGlyph( - "a", layerBuilder.buildPaintSolid(paletteIndex=0, alpha=1.0) - ), - angle=15, - centerX=127, - centerY=129, + assert not _is_var(ot.PaintFormat.PaintRotate) + checkBuildPaintRotate(ot.PaintFormat.PaintRotate) + + +def test_buildPaintVarRotate(): + assert _is_var(ot.PaintFormat.PaintVarRotate) + checkBuildPaintRotate(ot.PaintFormat.PaintVarRotate) + + +def checkBuildPaintSkew(fmt): + if _is_var(fmt): + inputMapFn = builder.VariableInt + outputMapFn = lambda v: v.value + else: + inputMapFn = outputMapFn = lambda v: v + + paint = _build( + ot.Paint, + { + "Format": fmt, + "Paint": ( + ot.PaintFormat.PaintGlyph, + (ot.PaintFormat.PaintSolid, (0, 1.0)), + "a", + ), + "xSkewAngle": 15, + "ySkewAngle": 42, + "centerX": 127, + "centerY": 129, + }, ) - assert paint.Format == ot.Paint.Format.PaintRotate - assert paint.Paint.Format == ot.Paint.Format.PaintGlyph - assert paint.angle.value == 15 - assert paint.centerX.value == 127 - assert paint.centerY.value == 129 + assert paint.Format == fmt + assert paint.Paint.Format == ot.PaintFormat.PaintGlyph + assert outputMapFn(paint.xSkewAngle) == 15 + assert outputMapFn(paint.ySkewAngle) == 42 + assert outputMapFn(paint.centerX) == 127 + assert outputMapFn(paint.centerY) == 129 def test_buildPaintSkew(): - layerBuilder = LayerV1ListBuilder() - paint = layerBuilder.buildPaintSkew( - paint=layerBuilder.buildPaintGlyph( - "a", layerBuilder.buildPaintSolid(paletteIndex=0, alpha=1.0) - ), - xSkewAngle=15, - ySkewAngle=42, - centerX=127, - centerY=129, - ) + assert not _is_var(ot.PaintFormat.PaintSkew) + checkBuildPaintSkew(ot.PaintFormat.PaintSkew) + - assert paint.Format == ot.Paint.Format.PaintSkew - assert paint.Paint.Format == ot.Paint.Format.PaintGlyph - assert paint.xSkewAngle.value == 15 - assert paint.ySkewAngle.value == 42 - assert paint.centerX.value == 127 - assert paint.centerY.value == 129 +def test_buildPaintVarSkew(): + assert _is_var(ot.PaintFormat.PaintVarSkew) + checkBuildPaintSkew(ot.PaintFormat.PaintVarSkew) def test_buildColrV1(): colorGlyphs = { - "a": [("b", 0), ("c", 1)], - "d": [ - ("e", {"format": 2, "paletteIndex": 2, "alpha": 0.8}), - ( - "f", - { - "format": 4, - "colorLine": {"stops": [(0.0, 3), (1.0, 4)], "extend": "reflect"}, - "c0": (0, 0), - "c1": (0, 0), - "r0": 10, - "r1": 0, - }, - ), - ], - "g": [("h", 5)], + "a": ( + ot.PaintFormat.PaintColrLayers, + [ + (ot.PaintFormat.PaintGlyph, (ot.PaintFormat.PaintSolid, 0), "b"), + (ot.PaintFormat.PaintGlyph, (ot.PaintFormat.PaintVarSolid, 1), "c"), + ], + ), + "d": ( + ot.PaintFormat.PaintColrLayers, + [ + ( + ot.PaintFormat.PaintGlyph, + { + "Format": int(ot.PaintFormat.PaintSolid), + "Color": {"PaletteIndex": 2, "Alpha": 0.8}, + }, + "e", + ), + ( + ot.PaintFormat.PaintGlyph, + { + "Format": int(ot.PaintFormat.PaintVarRadialGradient), + "ColorLine": { + "ColorStop": [(0.0, 3), (1.0, 4)], + "Extend": "reflect", + }, + "x0": 0, + "y0": 0, + "x1": 0, + "y1": 0, + "r0": 10, + "r1": 0, + }, + "f", + ), + ], + ), + "g": ( + ot.PaintFormat.PaintColrLayers, + [(ot.PaintFormat.PaintGlyph, (ot.PaintFormat.PaintSolid, 5), "h")], + ), } glyphMap = { ".notdef": 0, @@ -677,35 +1008,38 @@ def test_buildColrV1(): def test_buildColrV1_more_than_255_paints(): num_paints = 364 colorGlyphs = { - "a": [ - { - "format": 5, # PaintGlyph - "paint": 0, - "glyph": name, - } - for name in (f"glyph{i}" for i in range(num_paints)) - ], + "a": ( + ot.PaintFormat.PaintColrLayers, + [ + { + "Format": int(ot.PaintFormat.PaintGlyph), + "Paint": (ot.PaintFormat.PaintSolid, 0), + "Glyph": name, + } + for name in (f"glyph{i}" for i in range(num_paints)) + ], + ), } layers, baseGlyphs = builder.buildColrV1(colorGlyphs) paints = layers.Paint assert len(paints) == num_paints + 1 - assert all(paints[i].Format == ot.Paint.Format.PaintGlyph for i in range(255)) + assert all(paints[i].Format == ot.PaintFormat.PaintGlyph for i in range(255)) - assert paints[255].Format == ot.Paint.Format.PaintColrLayers + assert paints[255].Format == ot.PaintFormat.PaintColrLayers assert paints[255].FirstLayerIndex == 0 assert paints[255].NumLayers == 255 assert all( - paints[i].Format == ot.Paint.Format.PaintGlyph + paints[i].Format == ot.PaintFormat.PaintGlyph for i in range(256, num_paints + 1) ) assert baseGlyphs.BaseGlyphCount == len(colorGlyphs) assert baseGlyphs.BaseGlyphV1Record[0].BaseGlyph == "a" assert ( - baseGlyphs.BaseGlyphV1Record[0].Paint.Format == ot.Paint.Format.PaintColrLayers + baseGlyphs.BaseGlyphV1Record[0].Paint.Format == ot.PaintFormat.PaintColrLayers ) assert baseGlyphs.BaseGlyphV1Record[0].Paint.FirstLayerIndex == 255 assert baseGlyphs.BaseGlyphV1Record[0].Paint.NumLayers == num_paints + 1 - 255 @@ -727,9 +1061,7 @@ def test_split_color_glyphs_by_version(): assert colorGlyphsV0 == {"a": [("b", 0), ("c", 1), ("d", 2), ("e", 3)]} assert not colorGlyphsV1 - colorGlyphs = { - "a": [("b", layerBuilder.buildPaintSolid(paletteIndex=0, alpha=0.0))] - } + colorGlyphs = {"a": (ot.PaintFormat.PaintGlyph, 0, "b")} colorGlyphsV0, colorGlyphsV1 = builder._split_color_glyphs_by_version(colorGlyphs) @@ -775,32 +1107,35 @@ def assertNoV0Content(colr): def test_build_layerv1list_empty(): - # Nobody uses PaintColrLayers (format 8), no layerlist + # Nobody uses PaintColrLayers, no layerlist colr = builder.buildCOLR( { - "a": { - "format": 5, # PaintGlyph - "paint": {"format": 2, "paletteIndex": 2, "alpha": 0.8}, - "glyph": "b", - }, - # A list of 1 shouldn't become a PaintColrLayers - "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), + # BaseGlyph, tuple form + "a": ( + int(ot.PaintFormat.PaintGlyph), + (2, (2, 0.8)), + "b", + ), + # BaseGlyph, map form + "b": { + "Format": int(ot.PaintFormat.PaintGlyph), + "Paint": { + "Format": int(ot.PaintFormat.PaintLinearGradient), + "ColorLine": { + "ColorStop": [(0.0, 2), (1.0, 3)], + "Extend": "reflect", }, - "glyph": "bb", - } - ], - } + "x0": 1, + "y0": 2, + "x1": 3, + "y1": 4, + "x2": 2, + "y2": 2, + }, + "Glyph": "bb", + }, + }, + version=1, ) assertIsColrV1(colr) @@ -818,9 +1153,9 @@ def _paint_names(paints) -> List[str]: # semi-readable assertions on a LayerV1List order. result = [] for paint in paints: - if paint.Format == int(ot.Paint.Format.PaintGlyph): + if paint.Format == int(ot.PaintFormat.PaintGlyph): result.append(paint.Glyph) - elif paint.Format == int(ot.Paint.Format.PaintColrLayers): + elif paint.Format == int(ot.PaintFormat.PaintColrLayers): result.append( f"Layers[{paint.FirstLayerIndex}:{paint.FirstLayerIndex+paint.NumLayers}]" ) @@ -830,35 +1165,42 @@ def _paint_names(paints) -> List[str]: 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": 2, "paletteIndex": 2, "alpha": 0.8} + solid_paint = {"Format": 2, "Color": {"PaletteIndex": 2, "Alpha": 0.8}} backdrop = { - "format": 5, # PaintGlyph - "paint": solid_paint, - "glyph": "back", + "Format": int(ot.PaintFormat.PaintGlyph), + "Paint": solid_paint, + "Glyph": "back", } a_foreground = { - "format": 5, # PaintGlyph - "paint": solid_paint, - "glyph": "a_fore", + "Format": int(ot.PaintFormat.PaintGlyph), + "Paint": solid_paint, + "Glyph": "a_fore", } b_foreground = { - "format": 5, # PaintGlyph - "paint": solid_paint, - "glyph": "b_fore", + "Format": int(ot.PaintFormat.PaintGlyph), + "Paint": solid_paint, + "Glyph": "b_fore", } - # list => PaintColrLayers, which means contents should be in LayerV1List + # list => PaintColrLayers, contents should land in LayerV1List colr = builder.buildCOLR( { - "a": [ - backdrop, - a_foreground, - ], - "b": [ - backdrop, - b_foreground, - ], - } + "a": ( + ot.PaintFormat.PaintColrLayers, + [ + backdrop, + a_foreground, + ], + ), + "b": { + "Format": ot.PaintFormat.PaintColrLayers, + "Layers": [ + backdrop, + b_foreground, + ], + }, + }, + version=1, ) assertIsColrV1(colr) @@ -879,47 +1221,51 @@ def test_build_layerv1list_simple(): def test_build_layerv1list_with_sharing(): # Three colr glyphs, each with two layers in common - solid_paint = {"format": 2, "paletteIndex": 2, "alpha": 0.8} + solid_paint = {"Format": 2, "Color": (2, 0.8)} backdrop = [ { - "format": 5, # PaintGlyph - "paint": solid_paint, - "glyph": "back1", + "Format": int(ot.PaintFormat.PaintGlyph), + "Paint": solid_paint, + "Glyph": "back1", }, { - "format": 5, # PaintGlyph - "paint": solid_paint, - "glyph": "back2", + "Format": ot.PaintFormat.PaintGlyph, + "Paint": solid_paint, + "Glyph": "back2", }, ] a_foreground = { - "format": 5, # PaintGlyph - "paint": solid_paint, - "glyph": "a_fore", + "Format": ot.PaintFormat.PaintGlyph, + "Paint": solid_paint, + "Glyph": "a_fore", } b_background = { - "format": 5, # PaintGlyph - "paint": solid_paint, - "glyph": "b_back", + "Format": ot.PaintFormat.PaintGlyph, + "Paint": solid_paint, + "Glyph": "b_back", } b_foreground = { - "format": 5, # PaintGlyph - "paint": solid_paint, - "glyph": "b_fore", + "Format": ot.PaintFormat.PaintGlyph, + "Paint": solid_paint, + "Glyph": "b_fore", } c_background = { - "format": 5, # PaintGlyph - "paint": solid_paint, - "glyph": "c_back", + "Format": ot.PaintFormat.PaintGlyph, + "Paint": solid_paint, + "Glyph": "c_back", } # list => PaintColrLayers, which means contents should be in LayerV1List colr = builder.buildCOLR( { - "a": backdrop + [a_foreground], - "b": [b_background] + backdrop + [b_foreground], - "c": [c_background] + backdrop, - } + "a": (ot.PaintFormat.PaintColrLayers, backdrop + [a_foreground]), + "b": ( + ot.PaintFormat.PaintColrLayers, + [b_background] + backdrop + [b_foreground], + ), + "c": (ot.PaintFormat.PaintColrLayers, [c_background] + backdrop), + }, + version=1, ) assertIsColrV1(colr) @@ -951,9 +1297,12 @@ def test_build_layerv1list_with_sharing(): def test_build_layerv1list_with_overlaps(): paints = [ { - "format": 5, # PaintGlyph - "paint": {"format": 2, "paletteIndex": 2, "alpha": 0.8}, - "glyph": c, + "Format": ot.PaintFormat.PaintGlyph, + "Paint": { + "Format": ot.PaintFormat.PaintSolid, + "Color": {"PaletteIndex": 2, "Alpha": 0.8}, + }, + "Glyph": c, } for c in "abcdefghi" ] @@ -961,10 +1310,11 @@ def test_build_layerv1list_with_overlaps(): # list => PaintColrLayers, which means contents should be in LayerV1List colr = builder.buildCOLR( { - "a": paints[0:4], - "b": paints[0:6], - "c": paints[2:8], - } + "a": (ot.PaintFormat.PaintColrLayers, paints[0:4]), + "b": (ot.PaintFormat.PaintColrLayers, paints[0:6]), + "c": (ot.PaintFormat.PaintColrLayers, paints[2:8]), + }, + version=1, ) assertIsColrV1(colr) @@ -994,6 +1344,26 @@ def test_build_layerv1list_with_overlaps(): assert colr.table.LayerV1List.LayerCount == 11 +def test_explicit_version_1(): + colr = builder.buildCOLR( + { + "a": ( + ot.PaintFormat.PaintColrLayers, + [ + (ot.PaintFormat.PaintGlyph, (ot.PaintFormat.PaintSolid, 0), "b"), + (ot.PaintFormat.PaintGlyph, (ot.PaintFormat.PaintSolid, 1), "c"), + ], + ) + }, + version=1, + ) + assert colr.version == 1 + assert not hasattr(colr, "ColorLayers") + assert hasattr(colr, "table") + assert isinstance(colr.table, ot.COLR) + assert colr.table.VarStore is None + + class BuildCOLRTest(object): def test_automatic_version_all_solid_color_glyphs(self): colr = builder.buildCOLR({"a": [("b", 0), ("c", 1)]}) @@ -1005,38 +1375,55 @@ class BuildCOLRTest(object): def test_automatic_version_no_solid_color_glyphs(self): colr = builder.buildCOLR( { - "a": [ - ( - "b", - { - "format": 4, - "colorLine": { - "stops": [(0.0, 0), (1.0, 1)], - "extend": "repeat", + "a": ( + ot.PaintFormat.PaintColrLayers, + [ + ( + ot.PaintFormat.PaintGlyph, + { + "Format": int(ot.PaintFormat.PaintRadialGradient), + "ColorLine": { + "ColorStop": [(0.0, 0), (1.0, 1)], + "Extend": "repeat", + }, + "x0": 1, + "y0": 0, + "x1": 10, + "y1": 0, + "r0": 4, + "r1": 2, }, - "c0": (1, 0), - "c1": (10, 0), - "r0": 4, - "r1": 2, - }, - ), - ("c", {"format": 2, "paletteIndex": 2, "alpha": 0.8}), - ], - "d": [ - ( - "e", + "b", + ), + ( + ot.PaintFormat.PaintGlyph, + {"Format": 2, "Color": {"PaletteIndex": 2, "Alpha": 0.8}}, + "c", + ), + ], + ), + "d": ( + ot.PaintFormat.PaintColrLayers, + [ { - "format": 3, - "colorLine": { - "stops": [(0.0, 2), (1.0, 3)], - "extend": "reflect", + "Format": ot.PaintFormat.PaintGlyph, + "Glyph": "e", + "Paint": { + "Format": ot.PaintFormat.PaintLinearGradient, + "ColorLine": { + "ColorStop": [(0.0, 2), (1.0, 3)], + "Extend": "reflect", + }, + "x0": 1, + "y0": 2, + "x1": 3, + "y1": 4, + "x2": 2, + "y2": 2, }, - "p0": (1, 2), - "p1": (3, 4), - "p2": (2, 2), - }, - ), - ], + } + ], + ), } ) assertIsColrV1(colr) @@ -1049,19 +1436,30 @@ class BuildCOLRTest(object): colr = builder.buildCOLR( { "a": [("b", 0), ("c", 1)], - "d": [ - ( - "e", - { - "format": 3, - "colorLine": {"stops": [(0.0, 2), (1.0, 3)]}, - "p0": (1, 2), - "p1": (3, 4), - "p2": (2, 2), - }, - ), - ("f", {"format": 2, "paletteIndex": 2, "alpha": 0.8}), - ], + "d": ( + ot.PaintFormat.PaintColrLayers, + [ + ( + ot.PaintFormat.PaintGlyph, + { + "Format": ot.PaintFormat.PaintLinearGradient, + "ColorLine": {"ColorStop": [(0.0, 2), (1.0, 3)]}, + "x0": 1, + "y0": 2, + "x1": 3, + "y1": 4, + "x2": 2, + "y2": 2, + }, + "e", + ), + ( + ot.PaintFormat.PaintGlyph, + (ot.PaintFormat.PaintSolid, (2, 0.8)), + "f", + ), + ], + ), } ) assertIsColrV1(colr) @@ -1087,13 +1485,55 @@ class BuildCOLRTest(object): assert hasattr(colr, "ColorLayers") def test_explicit_version_1(self): - colr = builder.buildCOLR({"a": [("b", 0), ("c", 1)]}, version=1) + colr = builder.buildCOLR( + { + "a": ( + ot.PaintFormat.PaintColrLayers, + [ + ( + ot.PaintFormat.PaintGlyph, + (ot.PaintFormat.PaintSolid, 0), + "b", + ), + ( + ot.PaintFormat.PaintGlyph, + (ot.PaintFormat.PaintSolid, 1), + "c", + ), + ], + ) + }, + version=1, + ) assert colr.version == 1 assert not hasattr(colr, "ColorLayers") assert hasattr(colr, "table") assert isinstance(colr.table, ot.COLR) assert colr.table.VarStore is None + def test_paint_one_colr_layers(self): + # A set of one layers should flip to just that layer + colr = builder.buildCOLR( + { + "a": ( + ot.PaintFormat.PaintColrLayers, + [ + ( + ot.PaintFormat.PaintGlyph, + (ot.PaintFormat.PaintSolid, 0), + "b", + ), + ], + ) + }, + ) + + assert len(colr.table.LayerV1List.Paint) == 0, "PaintColrLayers should be gone" + assert colr.table.BaseGlyphV1List.BaseGlyphCount == 1 + paint = colr.table.BaseGlyphV1List.BaseGlyphV1Record[0].Paint + assert paint.Format == ot.PaintFormat.PaintGlyph + assert paint.Paint.Format == ot.PaintFormat.PaintSolid + class TrickyRadialGradientTest: @staticmethod diff --git a/Tests/colorLib/table_builder_test.py b/Tests/colorLib/table_builder_test.py new file mode 100644 index 00000000..d0a76f5a --- /dev/null +++ b/Tests/colorLib/table_builder_test.py @@ -0,0 +1,15 @@ +from fontTools.ttLib.tables import otTables # trigger setup to occur +from fontTools.ttLib.tables.otConverters import UShort +from fontTools.colorLib.table_builder import TableBuilder +import pytest + + +class WriteMe: + value = None + + +def test_intValue_otRound(): + dest = WriteMe() + converter = UShort("value", None, None) + TableBuilder()._convert(dest, "value", converter, 85.6) + assert dest.value == 86, "Should have used otRound" diff --git a/Tests/colorLib/unbuilder_test.py b/Tests/colorLib/unbuilder_test.py new file mode 100644 index 00000000..81169e03 --- /dev/null +++ b/Tests/colorLib/unbuilder_test.py @@ -0,0 +1,210 @@ +from fontTools.ttLib.tables import otTables as ot +from fontTools.colorLib.builder import buildColrV1 +from fontTools.colorLib.unbuilder import unbuildColrV1 +import pytest + + +TEST_COLOR_GLYPHS = { + "glyph00010": { + "Format": int(ot.PaintFormat.PaintColrLayers), + "Layers": [ + { + "Format": int(ot.PaintFormat.PaintGlyph), + "Paint": { + "Format": int(ot.PaintFormat.PaintSolid), + "Color": {"PaletteIndex": 2, "Alpha": 0.5}, + }, + "Glyph": "glyph00011", + }, + { + "Format": int(ot.PaintFormat.PaintGlyph), + "Paint": { + "Format": int(ot.PaintFormat.PaintVarLinearGradient), + "ColorLine": { + "Extend": "repeat", + "ColorStop": [ + { + "StopOffset": (0.0, 0), + "Color": {"PaletteIndex": 3, "Alpha": (1.0, 0)}, + }, + { + "StopOffset": (0.5, 0), + "Color": {"PaletteIndex": 4, "Alpha": (1.0, 0)}, + }, + { + "StopOffset": (1.0, 0), + "Color": {"PaletteIndex": 5, "Alpha": (1.0, 0)}, + }, + ], + }, + "x0": (1, 0), + "y0": (2, 0), + "x1": (-3, 0), + "y1": (-4, 0), + "x2": (5, 0), + "y2": (6, 0), + }, + "Glyph": "glyph00012", + }, + { + "Format": int(ot.PaintFormat.PaintGlyph), + "Paint": { + "Format": int(ot.PaintFormat.PaintVarTransform), + "Paint": { + "Format": int(ot.PaintFormat.PaintRadialGradient), + "ColorLine": { + "Extend": "pad", + "ColorStop": [ + { + "StopOffset": 0, + "Color": {"PaletteIndex": 6, "Alpha": 1.0}, + }, + { + "StopOffset": 1.0, + "Color": {"PaletteIndex": 7, "Alpha": 0.4}, + }, + ], + }, + "x0": 7, + "y0": 8, + "r0": 9, + "x1": 10, + "y1": 11, + "r1": 12, + }, + "Transform": { + "xx": (-13.0, 0), + "yx": (14.0, 0), + "xy": (15.0, 0), + "yy": (-17.0, 0), + "dx": (18.0, 0), + "dy": (19.0, 0), + }, + }, + "Glyph": "glyph00013", + }, + { + "Format": int(ot.PaintFormat.PaintVarTranslate), + "Paint": { + "Format": int(ot.PaintFormat.PaintRotate), + "Paint": { + "Format": int(ot.PaintFormat.PaintVarSkew), + "Paint": { + "Format": int(ot.PaintFormat.PaintGlyph), + "Paint": { + "Format": int(ot.PaintFormat.PaintSolid), + "Color": {"PaletteIndex": 2, "Alpha": 0.5}, + }, + "Glyph": "glyph00011", + }, + "xSkewAngle": (-11.0, 0), + "ySkewAngle": (5.0, 0), + "centerX": (253.0, 0), + "centerY": (254.0, 0), + }, + "angle": 45.0, + "centerX": 255.0, + "centerY": 256.0, + }, + "dx": (257.0, 0), + "dy": (258.0, 0), + }, + ], + }, + "glyph00014": { + "Format": int(ot.PaintFormat.PaintComposite), + "SourcePaint": { + "Format": int(ot.PaintFormat.PaintColrGlyph), + "Glyph": "glyph00010", + }, + "CompositeMode": "src_over", + "BackdropPaint": { + "Format": int(ot.PaintFormat.PaintTransform), + "Paint": { + "Format": int(ot.PaintFormat.PaintColrGlyph), + "Glyph": "glyph00010", + }, + "Transform": { + "xx": 1.0, + "yx": 0.0, + "xy": 0.0, + "yy": 1.0, + "dx": 300.0, + "dy": 0.0, + }, + }, + }, + "glyph00015": { + "Format": int(ot.PaintFormat.PaintGlyph), + "Paint": { + "Format": int(ot.PaintFormat.PaintSweepGradient), + "ColorLine": { + "Extend": "pad", + "ColorStop": [ + { + "StopOffset": 0.0, + "Color": {"PaletteIndex": 3, "Alpha": 1.0}, + }, + { + "StopOffset": 1.0, + "Color": {"PaletteIndex": 5, "Alpha": 1.0}, + }, + ], + }, + "centerX": 259, + "centerY": 300, + "startAngle": 45.0, + "endAngle": 135.0, + }, + "Glyph": "glyph00011", + }, + "glyph00016": { + "Format": int(ot.PaintFormat.PaintColrLayers), + "Layers": [ + { + "Format": int(ot.PaintFormat.PaintGlyph), + "Paint": { + "Format": int(ot.PaintFormat.PaintVarSolid), + "Color": {"PaletteIndex": 2, "Alpha": (0.5, 0)}, + }, + "Glyph": "glyph00011", + }, + { + "Format": int(ot.PaintFormat.PaintGlyph), + "Paint": { + "Format": int(ot.PaintFormat.PaintVarLinearGradient), + "ColorLine": { + "Extend": "repeat", + "ColorStop": [ + { + "StopOffset": (0.0, 0), + "Color": {"PaletteIndex": 3, "Alpha": (1.0, 0)}, + }, + { + "StopOffset": (0.5, 0), + "Color": {"PaletteIndex": 4, "Alpha": (1.0, 0)}, + }, + { + "StopOffset": (1.0, 0), + "Color": {"PaletteIndex": 5, "Alpha": (1.0, 0)}, + }, + ], + }, + "x0": (1, 0), + "y0": (2, 0), + "x1": (-3, 0), + "y1": (-4, 0), + "x2": (5, 0), + "y2": (6, 0), + }, + "Glyph": "glyph00012", + }, + ], + }, +} + + +def test_unbuildColrV1(): + layersV1, baseGlyphsV1 = buildColrV1(TEST_COLOR_GLYPHS) + colorGlyphs = unbuildColrV1(layersV1, baseGlyphsV1) + assert colorGlyphs == TEST_COLOR_GLYPHS |