diff --git a/Lib/fontTools/colorLib/builder.py b/Lib/fontTools/colorLib/builder.py index 5e7d8c6eb..d5084f456 100644 --- a/Lib/fontTools/colorLib/builder.py +++ b/Lib/fontTools/colorLib/builder.py @@ -542,6 +542,38 @@ class LayerV1ListBuilder: ot_paint.Paint = self.buildPaint(paint) return ot_paint + def buildPaintRotate( + self, + paint: _PaintInput, + angle: _ScalarInput, + centerX: _ScalarInput, + centerY: _ScalarInput, + ) -> ot.Paint: + ot_paint = ot.Paint() + ot_paint.Format = int(ot.Paint.Format.PaintRotate) + ot_paint.Paint = self.buildPaint(paint) + ot_paint.angle = _to_variable_f16dot16_float(angle) + ot_paint.centerX = _to_variable_f16dot16_float(centerX) + ot_paint.centerY = _to_variable_f16dot16_float(centerY) + return ot_paint + + def buildPaintSkew( + self, + paint: _PaintInput, + xSkewAngle: _ScalarInput, + ySkewAngle: _ScalarInput, + centerX: _ScalarInput, + centerY: _ScalarInput, + ) -> ot.Paint: + ot_paint = ot.Paint() + ot_paint.Format = int(ot.Paint.Format.PaintSkew) + ot_paint.Paint = self.buildPaint(paint) + ot_paint.xSkewAngle = _to_variable_f16dot16_float(xSkewAngle) + ot_paint.ySkewAngle = _to_variable_f16dot16_float(ySkewAngle) + ot_paint.centerX = _to_variable_f16dot16_float(centerX) + ot_paint.centerY = _to_variable_f16dot16_float(centerY) + return ot_paint + def buildPaintComposite( self, mode: _CompositeInput, diff --git a/Lib/fontTools/ttLib/tables/otData.py b/Lib/fontTools/ttLib/tables/otData.py index f260a542a..776cf75ba 100755 --- a/Lib/fontTools/ttLib/tables/otData.py +++ b/Lib/fontTools/ttLib/tables/otData.py @@ -1664,6 +1664,23 @@ otData = [ ]), ('PaintFormat8', [ + ('uint8', 'PaintFormat', None, None, 'Format identifier-format = 8'), + ('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintRotate table) to Paint subtable.'), + ('VarFixed', 'angle', None, None, ''), + ('VarFixed', 'centerX', None, None, ''), + ('VarFixed', 'centerY', None, None, ''), + ]), + + ('PaintFormat9', [ + ('uint8', 'PaintFormat', None, None, 'Format identifier-format = 9'), + ('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintRotate table) to Paint subtable.'), + ('VarFixed', 'xSkewAngle', None, None, ''), + ('VarFixed', 'ySkewAngle', None, None, ''), + ('VarFixed', 'centerX', None, None, ''), + ('VarFixed', 'centerY', None, None, ''), + ]), + + ('PaintFormat10', [ ('uint8', 'PaintFormat', None, None, 'Format identifier-format = 8'), ('LOffset24To(Paint)', 'SourcePaint', None, None, 'Offset (from beginning of PaintComposite table) to source Paint subtable.'), ('CompositeMode', 'CompositeMode', None, None, 'A CompositeMode enumeration value.'), diff --git a/Lib/fontTools/ttLib/tables/otTables.py b/Lib/fontTools/ttLib/tables/otTables.py index 7a04d5aa1..c4208a55f 100644 --- a/Lib/fontTools/ttLib/tables/otTables.py +++ b/Lib/fontTools/ttLib/tables/otTables.py @@ -1334,7 +1334,9 @@ class Paint(getFormatSwitchingBaseTableClass("uint8")): PaintGlyph = 5 PaintColrGlyph = 6 PaintTransform = 7 - PaintComposite = 8 + PaintRotate = 8 + PaintSkew = 9 + PaintComposite = 10 def getFormatName(self): try: diff --git a/Tests/colorLib/builder_test.py b/Tests/colorLib/builder_test.py index 86b5f9e92..152e16e08 100644 --- a/Tests/colorLib/builder_test.py +++ b/Tests/colorLib/builder_test.py @@ -544,7 +544,7 @@ def test_buildPaintComposite(): composite = layerBuilder.buildPaintComposite( mode=ot.CompositeMode.SRC_OVER, source={ - "format": 8, + "format": 10, "mode": "src_over", "source": {"format": 5, "glyph": "c", "paint": 2}, "backdrop": {"format": 5, "glyph": "b", "paint": 1}, @@ -574,6 +574,44 @@ def test_buildPaintComposite(): assert composite.BackdropPaint.Paint.Color.PaletteIndex == 0 +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 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 + + +def test_buildPaintRotate(): + 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 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_buildColrV1(): colorGlyphs = { "a": [("b", 0), ("c", 1)], diff --git a/Tests/ttLib/tables/C_O_L_R_test.py b/Tests/ttLib/tables/C_O_L_R_test.py index 0a1d9df9d..76e9e61a1 100644 --- a/Tests/ttLib/tables/C_O_L_R_test.py +++ b/Tests/ttLib/tables/C_O_L_R_test.py @@ -2,26 +2,32 @@ from fontTools import ttLib from fontTools.misc.testTools import getXML, parseXML from fontTools.ttLib.tables.C_O_L_R_ import table_C_O_L_R_ +import binascii import pytest -COLR_V0_DATA = ( - b"\x00\x00" # Version (0) - b"\x00\x01" # BaseGlyphRecordCount (1) - b"\x00\x00\x00\x0e" # Offset to BaseGlyphRecordArray from beginning of table (14) - b"\x00\x00\x00\x14" # Offset to LayerRecordArray from beginning of table (20) - b"\x00\x03" # LayerRecordCount (3) - b"\x00\x06" # BaseGlyphRecord[0].BaseGlyph (6) - b"\x00\x00" # BaseGlyphRecord[0].FirstLayerIndex (0) - b"\x00\x03" # BaseGlyphRecord[0].NumLayers (3) - b"\x00\x07" # LayerRecord[0].LayerGlyph (7) - b"\x00\x00" # LayerRecord[0].PaletteIndex (0) - b"\x00\x08" # LayerRecord[1].LayerGlyph (8) - b"\x00\x01" # LayerRecord[1].PaletteIndex (1) - b"\x00\t" # LayerRecord[2].LayerGlyph (9) - b"\x00\x02" # LayerRecord[3].PaletteIndex (2) +COLR_V0_SAMPLE = ( + (b"\x00\x00", "Version (0)"), + (b"\x00\x01", "BaseGlyphRecordCount (1)"), + ( + b"\x00\x00\x00\x0e", + "Offset to BaseGlyphRecordArray from beginning of table (14)", + ), + (b"\x00\x00\x00\x14", "Offset to LayerRecordArray from beginning of table (20)"), + (b"\x00\x03", "LayerRecordCount (3)"), + (b"\x00\x06", "BaseGlyphRecord[0].BaseGlyph (6)"), + (b"\x00\x00", "BaseGlyphRecord[0].FirstLayerIndex (0)"), + (b"\x00\x03", "BaseGlyphRecord[0].NumLayers (3)"), + (b"\x00\x07", "LayerRecord[0].LayerGlyph (7)"), + (b"\x00\x00", "LayerRecord[0].PaletteIndex (0)"), + (b"\x00\x08", "LayerRecord[1].LayerGlyph (8)"), + (b"\x00\x01", "LayerRecord[1].PaletteIndex (1)"), + (b"\x00\t", "LayerRecord[2].LayerGlyph (9)"), + (b"\x00\x02", "LayerRecord[3].PaletteIndex (2)"), ) +COLR_V0_DATA = b"".join(t[0] for t in COLR_V0_SAMPLE) + COLR_V0_XML = [ '', @@ -37,6 +43,21 @@ def dump(table, ttFont=None): print("\n".join(getXML(table.toXML, ttFont))) +def diff_binary_fragments(font_bytes, expected_fragments): + pos = 0 + prev_desc = "" + for expected_bytes, description in expected_fragments: + actual_bytes = font_bytes[pos : pos + len(expected_bytes)] + assert ( + actual_bytes == expected_bytes + ), f'{description} (previous "{prev_desc}", bytes: {str(font_bytes[pos:pos+16])}' + pos += len(expected_bytes) + prev_desc = description + assert pos == len( + font_bytes + ), f"Leftover font bytes, used {pos} of {len(font_bytes)}" + + @pytest.fixture def font(): font = ttLib.TTFont() @@ -48,7 +69,7 @@ class COLR_V0_Test(object): def test_decompile_and_compile(self, font): colr = table_C_O_L_R_() colr.decompile(COLR_V0_DATA, font) - assert colr.compile(font) == COLR_V0_DATA + diff_binary_fragments(colr.compile(font), COLR_V0_SAMPLE) def test_decompile_and_dump_xml(self, font): colr = table_C_O_L_R_() @@ -62,145 +83,177 @@ class COLR_V0_Test(object): for name, attrs, content in parseXML(COLR_V0_XML): colr.fromXML(name, attrs, content, font) - assert colr.compile(font) == COLR_V0_DATA + diff_binary_fragments(colr.compile(font), COLR_V0_SAMPLE) + + def test_round_trip_xml(self, font): + colr = table_C_O_L_R_() + for name, attrs, content in parseXML(COLR_V0_XML): + colr.fromXML(name, attrs, content, font) + compiled = colr.compile(font) + + colr = table_C_O_L_R_() + colr.decompile(compiled, font) + assert getXML(colr.toXML, font) == COLR_V0_XML -COLR_V1_DATA = ( - b"\x00\x01" # Version (1) - b"\x00\x01" # BaseGlyphRecordCount (1) - b"\x00\x00\x00\x1a" # Offset to BaseGlyphRecordArray from beginning of table (26) - b"\x00\x00\x00 " # Offset to LayerRecordArray from beginning of table (32) - b"\x00\x03" # LayerRecordCount (3) - b"\x00\x00\x00," # Offset to BaseGlyphV1List from beginning of table (44) - b"\x00\x00\x00\x81" # Offset to LayerV1List from beginning of table (129) - b"\x00\x00\x00\x00" # Offset to VarStore (NULL) - b"\x00\x06" # BaseGlyphRecord[0].BaseGlyph (6) - b"\x00\x00" # BaseGlyphRecord[0].FirstLayerIndex (0) - b"\x00\x03" # BaseGlyphRecord[0].NumLayers (3) - b"\x00\x07" # LayerRecord[0].LayerGlyph (7) - b"\x00\x00" # LayerRecord[0].PaletteIndex (0) - b"\x00\x08" # LayerRecord[1].LayerGlyph (8) - b"\x00\x01" # LayerRecord[1].PaletteIndex (1) - b"\x00\t" # LayerRecord[2].LayerGlyph (9) - b"\x00\x02" # LayerRecord[2].PaletteIndex (2) - b"\x00\x00\x00\x02" # BaseGlyphV1List.BaseGlyphCount (2) - b"\x00\n" # BaseGlyphV1List.BaseGlyphV1Record[0].BaseGlyph (10) - b"\x00\x00\x00\x10" # Offset to Paint table from beginning of BaseGlyphV1List (16) - b"\x00\x0e" # BaseGlyphV1List.BaseGlyphV1Record[1].BaseGlyph (14) - b"\x00\x00\x00\x16" # Offset to Paint table from beginning of BaseGlyphV1List (22) - b"\x01" # BaseGlyphV1Record[0].Paint.Format (1) - b"\x03" # BaseGlyphV1Record[0].Paint.NumLayers (3) - b"\x00\x00\x00\x00" # BaseGlyphV1Record[0].Paint.FirstLayerIndex (0) - b"\x08" # BaseGlyphV1Record[1].Paint.Format (8) - b"\x00\x00<" # Offset to SourcePaint from beginning of PaintComposite (60) - b"\x03" # BaseGlyphV1Record[1].Paint.CompositeMode [SRC_OVER] (3) - b"\x00\x00\x08" # Offset to BackdropPaint from beginning of PaintComposite (8) - b"\x07" # BaseGlyphV1Record[1].Paint.BackdropPaint.Format (7) - b"\x00\x004" # Offset to Paint from beginning of PaintTransform (52) - b"\x00\x01\x00\x00" # Affine2x3.xx.value (1.0) - b"\x00\x00\x00\x00" - b"\x00\x00\x00\x00" # Affine2x3.xy.value (0.0) - b"\x00\x00\x00\x00" - b"\x00\x00\x00\x00" # Affine2x3.yx.value (0.0) - b"\x00\x00\x00\x00" - b"\x00\x01\x00\x00" # Affine2x3.yy.value (1.0) - b"\x00\x00\x00\x00" - b"\x01,\x00\x00" # Affine2x3.dx.value (300.0) - b"\x00\x00\x00\x00" - b"\x00\x00\x00\x00" # Affine2x3.dy.value (0.0) - b"\x00\x00\x00\x00" - b"\x06" # BaseGlyphV1Record[1].Paint.SourcePaint.Format (6) - b"\x00\n" # BaseGlyphV1Record[1].Paint.SourcePaint.Glyph (10) - b"\x00\x00\x00\x03" # LayerV1List.LayerCount (3) - b"\x00\x00\x00\x10" # Offset to Paint table from beginning of LayerV1List (16) - b"\x00\x00\x00\x1f" # Offset to Paint table from beginning of LayerV1List (31) - b"\x00\x00\x00z" # Offset to Paint table from beginning of LayerV1List (122) - b"\x05" # LayerV1List.Paint[0].Format (5) - b"\x00\x00\x06" # Offset to Paint subtable from beginning of PaintGlyph (6) - b"\x00\x0b" # LayerV1List.Paint[0].Glyph (11) - b"\x02" # LayerV1List.Paint[0].Paint.Format (2) - b"\x00\x02" # Paint.Color.PaletteIndex (2) - b" \x00" # Paint.Color.Alpha.value (0.5) - b"\x00\x00\x00\x00" # Paint.Color.Alpha.varIdx (0) - b"\x05" # LayerV1List.Paint[1].Format (5) - b"\x00\x00\x06" # Offset to Paint subtable from beginning of PaintGlyph (6) - b"\x00\x0c" # LayerV1List.Paint[1].Glyph (12) - b"\x03" # LayerV1List.Paint[1].Paint.Format (3) - b"\x00\x00(" # Offset to ColorLine from beginning of PaintLinearGradient (40) - b"\x00\x01" # Paint.x0.value (1) - b"\x00\x00\x00\x00" # Paint.x0.varIdx (0) - b"\x00\x02" # Paint.y0.value (2) - b"\x00\x00\x00\x00" # Paint.y0.varIdx (0) - b"\xff\xfd" # Paint.x1.value (-3) - b"\x00\x00\x00\x00" # Paint.x1.varIdx (0) - b"\xff\xfc" # Paint.y1.value (-4) - b"\x00\x00\x00\x00" # Paint.y1.varIdx (0) - b"\x00\x05" # Paint.x2.value (5) - b"\x00\x00\x00\x00" # Paint.x2.varIdx (0) - b"\x00\x06" # Paint.y2.value (6) - b"\x00\x00\x00\x00" # Paint.y2.varIdx (0) - b"\x01" # ColorLine.Extend (1 or "repeat") - b"\x00\x03" # ColorLine.StopCount (3) - b"\x00\x00" # ColorLine.ColorStop[0].StopOffset.value (0.0) - b"\x00\x00\x00\x00" # ColorLine.ColorStop[0].StopOffset.varIdx (0) - b"\x00\x03" # ColorLine.ColorStop[0].Color.PaletteIndex (3) - b"@\x00" # ColorLine.ColorStop[0].Color.Alpha.value (1.0) - b"\x00\x00\x00\x00" # ColorLine.ColorStop[0].Color.Alpha.varIdx (0) - b" \x00" # ColorLine.ColorStop[1].StopOffset.value (0.5) - b"\x00\x00\x00\x00" # ColorLine.ColorStop[1].StopOffset.varIdx (0) - b"\x00\x04" # ColorLine.ColorStop[1].Color.PaletteIndex (4) - b"@\x00" # ColorLine.ColorStop[1].Color.Alpha.value (1.0) - b"\x00\x00\x00\x00" # ColorLine.ColorStop[1].Color.Alpha.varIdx (0) - b"@\x00" # ColorLine.ColorStop[2].StopOffset.value (1.0) - b"\x00\x00\x00\x00" # ColorLine.ColorStop[2].StopOffset.varIdx (0) - b"\x00\x05" # ColorLine.ColorStop[2].Color.PaletteIndex (5) - b"@\x00" # ColorLine.ColorStop[2].Color.Alpha.value (1.0) - b"\x00\x00\x00\x00" # ColorLine.ColorStop[2].Color.Alpha.varIdx (0) - b"\x05" # LayerV1List.Paint[2].Format (5) - b"\x00\x00\x06" # Offset to Paint subtable from beginning of PaintGlyph (6) - b"\x00\r" # LayerV1List.Paint[2].Glyph (13) - b"\x07" # LayerV1List.Paint[2].Paint.Format (5) - b"\x00\x004" # Offset to Paint subtable from beginning of PaintTransform (52) - b"\xff\xf3\x00\x00" # Affine2x3.xx.value (-13) - b"\x00\x00\x00\x00" - b"\x00\x0e\x00\x00" # Affine2x3.xy.value (14) - b"\x00\x00\x00\x00" - b"\x00\x0f\x00\x00" # Affine2x3.yx.value (15) - b"\x00\x00\x00\x00" - b"\xff\xef\x00\x00" # Affine2x3.yy.value (-17) - b"\x00\x00\x00\x00" - b"\x00\x12\x00\x00" # Affine2x3.yy.value (18) - b"\x00\x00\x00\x00" - b"\x00\x13\x00\x00" # Affine2x3.yy.value (19) - b"\x00\x00\x00\x00" - b"\x04" # LayerV1List.Paint[2].Paint.Paint.Format (4) - b"\x00\x00(" # Offset to ColorLine from beginning of PaintRadialGradient (40) - b"\x00\x07" # Paint.x0.value (7) - b"\x00\x00\x00\x00" - b"\x00\x08" # Paint.y0.value (8) - b"\x00\x00\x00\x00" - b"\x00\t" # Paint.r0.value (9) - b"\x00\x00\x00\x00" - b"\x00\n" # Paint.x1.value (10) - b"\x00\x00\x00\x00" - b"\x00\x0b" # Paint.y1.value (11) - b"\x00\x00\x00\x00" - b"\x00\x0c" # Paint.r1.value (12) - b"\x00\x00\x00\x00" - b"\x00" # ColorLine.Extend (0 or "pad") - b"\x00\x02" # ColorLine.StopCount (2) - b"\x00\x00" # ColorLine.ColorStop[0].StopOffset.value (0.0) - b"\x00\x00\x00\x00" - b"\x00\x06" # ColorLine.ColorStop[0].Color.PaletteIndex (6) - b"@\x00" # ColorLine.ColorStop[0].Color.Alpha.value (1.0) - b"\x00\x00\x00\x00" - b"@\x00" # ColorLine.ColorStop[1].StopOffset.value (1.0) - b"\x00\x00\x00\x00" - b"\x00\x07" # ColorLine.ColorStop[1].Color.PaletteIndex (7) - b"\x19\x9a" # ColorLine.ColorStop[1].Color.Alpha.value (0.4) - b"\x00\x00\x00\x00" +COLR_V1_SAMPLE = ( + (b"\x00\x01", "Version (1)"), + (b"\x00\x01", "BaseGlyphRecordCount (1)"), + ( + b"\x00\x00\x00\x1a", + "Offset to BaseGlyphRecordArray from beginning of table (26)", + ), + (b"\x00\x00\x00 ", "Offset to LayerRecordArray from beginning of table (32)"), + (b"\x00\x03", "LayerRecordCount (3)"), + (b"\x00\x00\x00,", "Offset to BaseGlyphV1List from beginning of table (44)"), + (b"\x00\x00\x00\x81", "Offset to LayerV1List from beginning of table (129)"), + (b"\x00\x00\x00\x00", "Offset to VarStore (NULL)"), + (b"\x00\x06", "BaseGlyphRecord[0].BaseGlyph (6)"), + (b"\x00\x00", "BaseGlyphRecord[0].FirstLayerIndex (0)"), + (b"\x00\x04", "BaseGlyphRecord[0].NumLayers (4)"), + (b"\x00\x07", "LayerRecord[0].LayerGlyph (7)"), + (b"\x00\x00", "LayerRecord[0].PaletteIndex (0)"), + (b"\x00\x08", "LayerRecord[1].LayerGlyph (8)"), + (b"\x00\x01", "LayerRecord[1].PaletteIndex (1)"), + (b"\x00\t", "LayerRecord[2].LayerGlyph (9)"), + (b"\x00\x02", "LayerRecord[2].PaletteIndex (2)"), + (b"\x00\x00\x00\x02", "BaseGlyphV1List.BaseGlyphCount (2)"), + (b"\x00\n", "BaseGlyphV1List.BaseGlyphV1Record[0].BaseGlyph (10)"), + ( + b"\x00\x00\x00\x10", + "Offset to Paint table from beginning of BaseGlyphV1List (16)", + ), + (b"\x00\x0e", "BaseGlyphV1List.BaseGlyphV1Record[1].BaseGlyph (14)"), + ( + b"\x00\x00\x00\x16", + "Offset to Paint table from beginning of BaseGlyphV1List (22)", + ), + (b"\x01", "BaseGlyphV1Record[0].Paint.Format (1)"), + (b"\x04", "BaseGlyphV1Record[0].Paint.NumLayers (4)"), + (b"\x00\x00\x00\x00", "BaseGlyphV1Record[0].Paint.FirstLayerIndex (0)"), + (b"\x0A", "BaseGlyphV1Record[1].Paint.Format (10)"), + (b"\x00\x00<", "Offset to SourcePaint from beginning of PaintComposite (60)"), + (b"\x03", "BaseGlyphV1Record[1].Paint.CompositeMode [SRC_OVER] (3)"), + (b"\x00\x00\x08", "Offset to BackdropPaint from beginning of PaintComposite (8)"), + (b"\x07", "BaseGlyphV1Record[1].Paint.BackdropPaint.Format (7)"), + (b"\x00\x00\x34", "Offset to Paint from beginning of PaintTransform (52)"), + (b"\x00\x01\x00\x00\x00\x00\x00\x00", "Affine2x3.xx.value (1.0)"), + (b"\x00\x00\x00\x00\x00\x00\x00\x00", "Affine2x3.xy.value (0.0)"), + (b"\x00\x00\x00\x00\x00\x00\x00\x00", "Affine2x3.yx.value (0.0)"), + (b"\x00\x01\x00\x00\x00\x00\x00\x00", "Affine2x3.yy.value (1.0)"), + (b"\x01\x2c\x00\x00\x00\x00\x00\x00", "Affine2x3.dx.value (300.0)"), + (b"\x00\x00\x00\x00\x00\x00\x00\x00", "Affine2x3.dy.value (0.0)"), + (b"\x06", "BaseGlyphV1Record[1].Paint.SourcePaint.Format (6)"), + (b"\x00\n", "BaseGlyphV1Record[1].Paint.SourcePaint.Glyph (10)"), + (b"\x00\x00\x00\x04", "LayerV1List.LayerCount (4)"), + ( + b"\x00\x00\x00\x14", + "First Offset to Paint table from beginning of LayerV1List (20)", + ), + ( + b"\x00\x00\x00\x1a", + "Second Offset to Paint table from beginning of LayerV1List (26)", + ), + ( + b"\x00\x00\x00u", + "Third Offset to Paint table from beginning of LayerV1List (117)", + ), + ( + b"\x00\x00\x00\xf6", + "Fourth Offset to Paint table from beginning of LayerV1List (246)", + ), + # PaintGlyph glyph00011 + (b"\x05", "LayerV1List.Paint[0].Format (5)"), + (b"\x00\x01\x28", "Offset24 to Paint subtable from beginning of PaintGlyph (296)"), + (b"\x00\x0b", "LayerV1List.Paint[0].Glyph (glyph00011)"), + # PaintGlyph glyph00012 + (b"\x05", "LayerV1List.Paint[1].Format (5)"), + (b"\x00\x00\x06", "Offset to Paint subtable from beginning of PaintGlyph (6)"), + (b"\x00\x0c", "LayerV1List.Paint[1].Glyph (glyph00012)"), + (b"\x03", "LayerV1List.Paint[1].Paint.Format (3)"), + (b"\x00\x00(", "Offset to ColorLine from beginning of PaintLinearGradient (40)"), + (b"\x00\x01", "Paint.x0.value (1)"), + (b"\x00\x00\x00\x00", "Paint.x0.varIdx (0)"), + (b"\x00\x02", "Paint.y0.value (2)"), + (b"\x00\x00\x00\x00", "Paint.y0.varIdx (0)"), + (b"\xff\xfd", "Paint.x1.value (-3)"), + (b"\x00\x00\x00\x00", "Paint.x1.varIdx (0)"), + (b"\xff\xfc", "Paint.y1.value (-4)"), + (b"\x00\x00\x00\x00", "Paint.y1.varIdx (0)"), + (b"\x00\x05", "Paint.x2.value (5)"), + (b"\x00\x00\x00\x00", "Paint.x2.varIdx (0)"), + (b"\x00\x06", "Paint.y2.value (6)"), + (b"\x00\x00\x00\x00", "Paint.y2.varIdx (0)"), + (b"\x01", "ColorLine.Extend (1; repeat)"), + (b"\x00\x03", "ColorLine.StopCount (3)"), + (b"\x00\x00", "ColorLine.ColorStop[0].StopOffset.value (0.0)"), + (b"\x00\x00\x00\x00", "ColorLine.ColorStop[0].StopOffset.varIdx (0)"), + (b"\x00\x03", "ColorLine.ColorStop[0].Color.PaletteIndex (3)"), + (b"@\x00", "ColorLine.ColorStop[0].Color.Alpha.value (1.0)"), + (b"\x00\x00\x00\x00", "ColorLine.ColorStop[0].Color.Alpha.varIdx (0)"), + (b" \x00", "ColorLine.ColorStop[1].StopOffset.value (0.5)"), + (b"\x00\x00\x00\x00", "ColorLine.ColorStop[1].StopOffset.varIdx (0)"), + (b"\x00\x04", "ColorLine.ColorStop[1].Color.PaletteIndex (4)"), + (b"@\x00", "ColorLine.ColorStop[1].Color.Alpha.value (1.0)"), + (b"\x00\x00\x00\x00", "ColorLine.ColorStop[1].Color.Alpha.varIdx (0)"), + (b"@\x00", "ColorLine.ColorStop[2].StopOffset.value (1.0)"), + (b"\x00\x00\x00\x00", "ColorLine.ColorStop[2].StopOffset.varIdx (0)"), + (b"\x00\x05", "ColorLine.ColorStop[2].Color.PaletteIndex (5)"), + (b"@\x00", "ColorLine.ColorStop[2].Color.Alpha.value (1.0)"), + (b"\x00\x00\x00\x00", "ColorLine.ColorStop[2].Color.Alpha.varIdx (0)"), + # PaintGlyph glyph00013 + (b"\x05", "LayerV1List.Paint[2].Format (5)"), + (b"\x00\x00\x06", "Offset to Paint subtable from beginning of PaintGlyph (6)"), + (b"\x00\r", "LayerV1List.Paint[2].Glyph (13)"), + (b"\x07", "LayerV1List.Paint[2].Paint.Format (5)"), + (b"\x00\x00\x34", "Offset to Paint subtable from beginning of PaintTransform (52)"), + (b"\xff\xf3\x00\x00\x00\x00\x00\x00", "Affine2x3.xx.value (-13)"), + (b"\x00\x0e\x00\x00\x00\x00\x00\x00", "Affine2x3.xy.value (14)"), + (b"\x00\x0f\x00\x00\x00\x00\x00\x00", "Affine2x3.yx.value (15)"), + (b"\xff\xef\x00\x00\x00\x00\x00\x00", "Affine2x3.yy.value (-17)"), + (b"\x00\x12\x00\x00\x00\x00\x00\x00", "Affine2x3.yy.value (18)"), + (b"\x00\x13\x00\x00\x00\x00\x00\x00", "Affine2x3.yy.value (19)"), + (b"\x04", "LayerV1List.Paint[2].Paint.Paint.Format (4)"), + (b"\x00\x00(", "Offset to ColorLine from beginning of PaintRadialGradient (40)"), + (b"\x00\x07\x00\x00\x00\x00", "Paint.x0.value (7)"), + (b"\x00\x08\x00\x00\x00\x00", "Paint.y0.value (8)"), + (b"\x00\t\x00\x00\x00\x00", "Paint.r0.value (9)"), + (b"\x00\n\x00\x00\x00\x00", "Paint.x1.value (10)"), + (b"\x00\x0b\x00\x00\x00\x00", "Paint.y1.value (11)"), + (b"\x00\x0c\x00\x00\x00\x00", "Paint.r1.value (12)"), + (b"\x00", "ColorLine.Extend (0; pad)"), + (b"\x00\x02", "ColorLine.StopCount (2)"), + (b"\x00\x00\x00\x00\x00\x00", "ColorLine.ColorStop[0].StopOffset.value (0.0)"), + (b"\x00\x06", "ColorLine.ColorStop[0].Color.PaletteIndex (6)"), + (b"@\x00\x00\x00\x00\x00", "ColorLine.ColorStop[0].Color.Alpha.value (1.0)"), + (b"@\x00\x00\x00\x00\x00", "ColorLine.ColorStop[1].StopOffset.value (1.0)"), + (b"\x00\x07", "ColorLine.ColorStop[1].Color.PaletteIndex (7)"), + (b"\x19\x9a\x00\x00\x00\x00", "ColorLine.ColorStop[1].Color.Alpha.value (0.4)"), + # PaintRotate + (b"\x08", "LayerV1List.Paint[3].Format (8)"), + (b"\x00\x00\x1c", "Offset to Paint subtable from beginning of PaintRotate (28)"), + (b"\x00\x2d\x00\x00\x00\x00\x00\x00", "angle.value (45)"), + (b"\x00\xff\x00\x00\x00\x00\x00\x00", "centerX.value (255)"), + (b"\x01\x00\x00\x00\x00\x00\x00\x00", "centerY.value (256)"), + # PaintSkew + (b"\x09", "LayerV1List.Paint[3].Format (9)"), + (b"\x00\x00\x24", "Offset to Paint subtable from beginning of PaintSkew (36)"), + (b"\xff\xf5\x00\x00\x00\x00\x00\x00", "xSkewAngle (-11)"), + (b"\x00\x05\x00\x00\x00\x00\x00\x00", "ySkewAngle (5)"), + (b"\x00\xfd\x00\x00\x00\x00\x00\x00", "centerX.value (253)"), + (b"\x00\xfe\x00\x00\x00\x00\x00\x00", "centerY.value (254)"), + # PaintGlyph + (b"\x05", "LayerV1List.Paint[2].Format (5)"), + (b"\x00\x00\x06", "Offset to Paint subtable from beginning of PaintGlyph (6)"), + (b"\x00\x0b", "LayerV1List.Paint[2].Glyph (11)"), + # PaintSolid + (b"\x02", "LayerV1List.Paint[0].Paint.Format (2)"), + (b"\x00\x02", "Paint.Color.PaletteIndex (2)"), + (b" \x00", "Paint.Color.Alpha.value (0.5)"), + (b"\x00\x00\x00\x00", "Paint.Color.Alpha.varIdx (0)"), ) +COLR_V1_DATA = b"".join(t[0] for t in COLR_V1_SAMPLE) COLR_V1_XML = [ '', @@ -209,7 +262,7 @@ COLR_V1_XML = [ ' ', ' ', ' ', - ' ', + ' ', " ", "", "", @@ -232,13 +285,13 @@ COLR_V1_XML = [ ' ', ' ', ' ', - ' ', + ' ', ' ', " ", " ", ' ', ' ', - ' ', + ' ', ' ', ' ', " ", @@ -260,7 +313,7 @@ COLR_V1_XML = [ " ", "", "", - " ", + " ", ' ', ' ', " ", @@ -345,6 +398,26 @@ COLR_V1_XML = [ " ", ' ', " ", + ' ', + ' ', + ' ', + ' ', + " ", + ' ', + ' ', + " ", + " ", + ' ', + " ", + ' ', + ' ', + ' ', + ' ', + " ", + ' ', + ' ', + ' ', + " ", "", ] @@ -353,7 +426,7 @@ class COLR_V1_Test(object): def test_decompile_and_compile(self, font): colr = table_C_O_L_R_() colr.decompile(COLR_V1_DATA, font) - assert colr.compile(font) == COLR_V1_DATA + diff_binary_fragments(colr.compile(font), COLR_V1_SAMPLE) def test_decompile_and_dump_xml(self, font): colr = table_C_O_L_R_() @@ -366,5 +439,14 @@ class COLR_V1_Test(object): colr = table_C_O_L_R_() for name, attrs, content in parseXML(COLR_V1_XML): colr.fromXML(name, attrs, content, font) + diff_binary_fragments(colr.compile(font), COLR_V1_SAMPLE) - assert colr.compile(font) == COLR_V1_DATA + def test_round_trip_xml(self, font): + colr = table_C_O_L_R_() + for name, attrs, content in parseXML(COLR_V1_XML): + colr.fromXML(name, attrs, content, font) + compiled = colr.compile(font) + + colr = table_C_O_L_R_() + colr.decompile(compiled, font) + assert getXML(colr.toXML, font) == COLR_V1_XML