diff options
Diffstat (limited to 'Tests/ttLib/ttGlyphSet_test.py')
-rw-r--r-- | Tests/ttLib/ttGlyphSet_test.py | 658 |
1 files changed, 601 insertions, 57 deletions
diff --git a/Tests/ttLib/ttGlyphSet_test.py b/Tests/ttLib/ttGlyphSet_test.py index bc0bf2ce..56514464 100644 --- a/Tests/ttLib/ttGlyphSet_test.py +++ b/Tests/ttLib/ttGlyphSet_test.py @@ -1,112 +1,656 @@ from fontTools.ttLib import TTFont from fontTools.ttLib import ttGlyphSet -from fontTools.pens.recordingPen import RecordingPen +from fontTools.pens.recordingPen import ( + RecordingPen, + RecordingPointPen, + DecomposingRecordingPen, +) +from fontTools.misc.roundTools import otRound +from fontTools.misc.transform import DecomposedTransform import os import pytest class TTGlyphSetTest(object): - @staticmethod def getpath(testfile): path = os.path.dirname(__file__) return os.path.join(path, "data", testfile) @pytest.mark.parametrize( - "location, expected", + "fontfile, location, expected", [ ( + "I.ttf", None, [ - ('moveTo', ((175, 0),)), - ('lineTo', ((367, 0),)), - ('lineTo', ((367, 1456),)), - ('lineTo', ((175, 1456),)), - ('closePath', ()) - ] + ("moveTo", ((175, 0),)), + ("lineTo", ((367, 0),)), + ("lineTo", ((367, 1456),)), + ("lineTo", ((175, 1456),)), + ("closePath", ()), + ], ), ( + "I.ttf", {}, [ - ('moveTo', ((175, 0),)), - ('lineTo', ((367, 0),)), - ('lineTo', ((367, 1456),)), - ('lineTo', ((175, 1456),)), - ('closePath', ()) - ] + ("moveTo", ((175, 0),)), + ("lineTo", ((367, 0),)), + ("lineTo", ((367, 1456),)), + ("lineTo", ((175, 1456),)), + ("closePath", ()), + ], ), ( - {'wght': 100}, + "I.ttf", + {"wght": 100}, [ - ('moveTo', ((175, 0),)), - ('lineTo', ((271, 0),)), - ('lineTo', ((271, 1456),)), - ('lineTo', ((175, 1456),)), - ('closePath', ()) - ] + ("moveTo", ((175, 0),)), + ("lineTo", ((271, 0),)), + ("lineTo", ((271, 1456),)), + ("lineTo", ((175, 1456),)), + ("closePath", ()), + ], ), ( - {'wght': 1000}, + "I.ttf", + {"wght": 1000}, [ - ('moveTo', ((128, 0),)), - ('lineTo', ((550, 0),)), - ('lineTo', ((550, 1456),)), - ('lineTo', ((128, 1456),)), - ('closePath', ()) - ] + ("moveTo", ((128, 0),)), + ("lineTo", ((550, 0),)), + ("lineTo", ((550, 1456),)), + ("lineTo", ((128, 1456),)), + ("closePath", ()), + ], ), ( - {'wght': 1000, 'wdth': 25}, + "I.ttf", + {"wght": 1000, "wdth": 25}, [ - ('moveTo', ((140, 0),)), - ('lineTo', ((553, 0),)), - ('lineTo', ((553, 1456),)), - ('lineTo', ((140, 1456),)), - ('closePath', ()) - ] + ("moveTo", ((140, 0),)), + ("lineTo", ((553, 0),)), + ("lineTo", ((553, 1456),)), + ("lineTo", ((140, 1456),)), + ("closePath", ()), + ], ), ( - {'wght': 1000, 'wdth': 50}, + "I.ttf", + {"wght": 1000, "wdth": 50}, [ - ('moveTo', ((136, 0),)), - ('lineTo', ((552, 0),)), - ('lineTo', ((552, 1456),)), - ('lineTo', ((136, 1456),)), - ('closePath', ()) - ] + ("moveTo", ((136, 0),)), + ("lineTo", ((552, 0),)), + ("lineTo", ((552, 1456),)), + ("lineTo", ((136, 1456),)), + ("closePath", ()), + ], ), - ] + ( + "I.otf", + {"wght": 1000}, + [ + ("moveTo", ((179, 74),)), + ("lineTo", ((28, 59),)), + ("lineTo", ((28, 0),)), + ("lineTo", ((367, 0),)), + ("lineTo", ((367, 59),)), + ("lineTo", ((212, 74),)), + ("lineTo", ((179, 74),)), + ("closePath", ()), + ("moveTo", ((179, 578),)), + ("lineTo", ((212, 578),)), + ("lineTo", ((367, 593),)), + ("lineTo", ((367, 652),)), + ("lineTo", ((28, 652),)), + ("lineTo", ((28, 593),)), + ("lineTo", ((179, 578),)), + ("closePath", ()), + ("moveTo", ((98, 310),)), + ("curveTo", ((98, 205), (98, 101), (95, 0))), + ("lineTo", ((299, 0),)), + ("curveTo", ((296, 103), (296, 207), (296, 311))), + ("lineTo", ((296, 342),)), + ("curveTo", ((296, 447), (296, 551), (299, 652))), + ("lineTo", ((95, 652),)), + ("curveTo", ((98, 549), (98, 445), (98, 342))), + ("lineTo", ((98, 310),)), + ("closePath", ()), + ], + ), + ( + # In this font, /I has an lsb of 30, but an xMin of 25, so an + # offset of 5 units needs to be applied when drawing the outline. + # See https://github.com/fonttools/fonttools/issues/2824 + "issue2824.ttf", + None, + [ + ("moveTo", ((309, 180),)), + ("qCurveTo", ((274, 151), (187, 136), (104, 166), (74, 201))), + ("qCurveTo", ((45, 236), (30, 323), (59, 407), (95, 436))), + ("qCurveTo", ((130, 466), (217, 480), (301, 451), (330, 415))), + ("qCurveTo", ((360, 380), (374, 293), (345, 210), (309, 180))), + ("closePath", ()), + ], + ), + ], ) - def test_glyphset( - self, location, expected - ): - # TODO: also test loading CFF-flavored fonts - font = TTFont(self.getpath("I.ttf")) + def test_glyphset(self, fontfile, location, expected): + font = TTFont(self.getpath(fontfile)) glyphset = font.getGlyphSet(location=location) assert isinstance(glyphset, ttGlyphSet._TTGlyphSet) - if location: - assert isinstance(glyphset, ttGlyphSet._TTVarGlyphSet) assert list(glyphset.keys()) == [".notdef", "I"] assert "I" in glyphset - assert glyphset.has_key("I") # we should really get rid of this... + with pytest.deprecated_call(): + assert glyphset.has_key("I") # we should really get rid of this... assert len(glyphset) == 2 pen = RecordingPen() - glyph = glyphset['I'] + glyph = glyphset["I"] assert glyphset.get("foobar") is None assert isinstance(glyph, ttGlyphSet._TTGlyph) - if location: - assert isinstance(glyph, ttGlyphSet._TTVarGlyphGlyf) - else: - assert isinstance(glyph, ttGlyphSet._TTGlyphGlyf) + is_glyf = fontfile.endswith(".ttf") + glyphType = ttGlyphSet._TTGlyphGlyf if is_glyf else ttGlyphSet._TTGlyphCFF + assert isinstance(glyph, glyphType) glyph.draw(pen) actual = pen.value assert actual == expected, (location, actual, expected) + + def test_glyphset_varComposite_components(self): + font = TTFont(self.getpath("varc-ac00-ac01.ttf")) + glyphset = font.getGlyphSet() + + pen = RecordingPen() + glyph = glyphset["uniAC00"] + + glyph.draw(pen) + actual = pen.value + + expected = [ + ( + "addVarComponent", + ( + "glyph00003", + DecomposedTransform(460.0, 676.0, 0, 1, 1, 0, 0, 0, 0), + { + "0000": 0.84661865234375, + "0001": 0.98944091796875, + "0002": 0.47283935546875, + "0003": 0.446533203125, + }, + ), + ), + ( + "addVarComponent", + ( + "glyph00004", + DecomposedTransform(932.0, 382.0, 0, 1, 1, 0, 0, 0, 0), + { + "0000": 0.93359375, + "0001": 0.916015625, + "0002": 0.523193359375, + "0003": 0.32806396484375, + "0004": 0.85089111328125, + }, + ), + ), + ] + + assert actual == expected, (actual, expected) + + def test_glyphset_varComposite1(self): + font = TTFont(self.getpath("varc-ac00-ac01.ttf")) + glyphset = font.getGlyphSet(location={"wght": 600}) + + pen = DecomposingRecordingPen(glyphset) + glyph = glyphset["uniAC00"] + + glyph.draw(pen) + actual = pen.value + + expected = [ + ("moveTo", ((432, 678),)), + ("lineTo", ((432, 620),)), + ( + "qCurveTo", + ( + (419, 620), + (374, 621), + (324, 619), + (275, 618), + (237, 617), + (228, 616), + ), + ), + ("qCurveTo", ((218, 616), (188, 612), (160, 605), (149, 601))), + ("qCurveTo", ((127, 611), (83, 639), (67, 654))), + ("qCurveTo", ((64, 657), (63, 662), (64, 666))), + ("lineTo", ((72, 678),)), + ("qCurveTo", ((93, 674), (144, 672), (164, 672))), + ( + "qCurveTo", + ( + (173, 672), + (213, 672), + (266, 673), + (323, 674), + (377, 675), + (421, 678), + (432, 678), + ), + ), + ("closePath", ()), + ("moveTo", ((525, 619),)), + ("lineTo", ((412, 620),)), + ("lineTo", ((429, 678),)), + ("lineTo", ((466, 697),)), + ("qCurveTo", ((470, 698), (482, 698), (486, 697))), + ("qCurveTo", ((494, 693), (515, 682), (536, 670), (541, 667))), + ("qCurveTo", ((545, 663), (545, 656), (543, 652))), + ("lineTo", ((525, 619),)), + ("closePath", ()), + ("moveTo", ((63, 118),)), + ("lineTo", ((47, 135),)), + ("qCurveTo", ((42, 141), (48, 146))), + ("qCurveTo", ((135, 213), (278, 373), (383, 541), (412, 620))), + ("lineTo", ((471, 642),)), + ("lineTo", ((525, 619),)), + ("qCurveTo", ((496, 529), (365, 342), (183, 179), (75, 121))), + ("qCurveTo", ((72, 119), (65, 118), (63, 118))), + ("closePath", ()), + ("moveTo", ((925, 372),)), + ("lineTo", ((739, 368),)), + ("lineTo", ((739, 427),)), + ("lineTo", ((822, 430),)), + ("lineTo", ((854, 451),)), + ("qCurveTo", ((878, 453), (930, 449), (944, 445))), + ("qCurveTo", ((961, 441), (962, 426))), + ("qCurveTo", ((964, 411), (956, 386), (951, 381))), + ("qCurveTo", ((947, 376), (931, 372), (925, 372))), + ("closePath", ()), + ("moveTo", ((729, -113),)), + ("lineTo", ((674, -113),)), + ("qCurveTo", ((671, -98), (669, -42), (666, 22), (665, 83), (665, 102))), + ("lineTo", ((665, 763),)), + ("qCurveTo", ((654, 780), (608, 810), (582, 820))), + ("lineTo", ((593, 850),)), + ("qCurveTo", ((594, 852), (599, 856), (607, 856))), + ("qCurveTo", ((628, 855), (684, 846), (736, 834), (752, 827))), + ("qCurveTo", ((766, 818), (766, 802))), + ("lineTo", ((762, 745),)), + ("lineTo", ((762, 134),)), + ("qCurveTo", ((762, 107), (757, 43), (749, -25), (737, -87), (729, -113))), + ("closePath", ()), + ] + + actual = [ + (op, tuple((otRound(pt[0]), otRound(pt[1])) for pt in args)) + for op, args in actual + ] + + assert actual == expected, (actual, expected) + + # Test that drawing twice works, we accidentally don't change the component + pen = DecomposingRecordingPen(glyphset) + glyph.draw(pen) + actual = pen.value + actual = [ + (op, tuple((otRound(pt[0]), otRound(pt[1])) for pt in args)) + for op, args in actual + ] + assert actual == expected, (actual, expected) + + pen = RecordingPointPen() + glyph.drawPoints(pen) + assert pen.value + + def test_glyphset_varComposite2(self): + # This test font has axis variations + + font = TTFont(self.getpath("varc-6868.ttf")) + glyphset = font.getGlyphSet(location={"wght": 600}) + + pen = DecomposingRecordingPen(glyphset) + glyph = glyphset["uni6868"] + + glyph.draw(pen) + actual = pen.value + + expected = [ + ("moveTo", ((460, 565),)), + ( + "qCurveTo", + ( + (482, 577), + (526, 603), + (568, 632), + (607, 663), + (644, 698), + (678, 735), + (708, 775), + (721, 796), + ), + ), + ("lineTo", ((632, 835),)), + ( + "qCurveTo", + ( + (621, 817), + (595, 784), + (566, 753), + (534, 724), + (499, 698), + (462, 675), + (423, 653), + (403, 644), + ), + ), + ("closePath", ()), + ("moveTo", ((616, 765),)), + ("lineTo", ((590, 682),)), + ("lineTo", ((830, 682),)), + ("lineTo", ((833, 682),)), + ("lineTo", ((828, 693),)), + ( + "qCurveTo", + ( + (817, 671), + (775, 620), + (709, 571), + (615, 525), + (492, 490), + (413, 480), + ), + ), + ("lineTo", ((454, 386),)), + ( + "qCurveTo", + ( + (544, 403), + (687, 455), + (798, 519), + (877, 590), + (926, 655), + (937, 684), + ), + ), + ("lineTo", ((937, 765),)), + ("closePath", ()), + ("moveTo", ((723, 555),)), + ( + "qCurveTo", + ( + (713, 563), + (693, 579), + (672, 595), + (651, 610), + (629, 625), + (606, 638), + (583, 651), + (572, 657), + ), + ), + ("lineTo", ((514, 590),)), + ( + "qCurveTo", + ( + (525, 584), + (547, 572), + (568, 559), + (589, 545), + (609, 531), + (629, 516), + (648, 500), + (657, 492), + ), + ), + ("closePath", ()), + ("moveTo", ((387, 375),)), + ("lineTo", ((387, 830),)), + ("lineTo", ((289, 830),)), + ("lineTo", ((289, 375),)), + ("closePath", ()), + ("moveTo", ((96, 383),)), + ( + "qCurveTo", + ( + (116, 390), + (156, 408), + (194, 427), + (231, 449), + (268, 472), + (302, 497), + (335, 525), + (351, 539), + ), + ), + ("lineTo", ((307, 610),)), + ( + "qCurveTo", + ( + (291, 597), + (257, 572), + (221, 549), + (185, 528), + (147, 509), + (108, 492), + (69, 476), + (48, 469), + ), + ), + ("closePath", ()), + ("moveTo", ((290, 653),)), + ( + "qCurveTo", + ( + (281, 664), + (261, 687), + (240, 708), + (219, 729), + (196, 749), + (173, 768), + (148, 786), + (136, 794), + ), + ), + ("lineTo", ((69, 727),)), + ( + "qCurveTo", + ( + (81, 719), + (105, 702), + (129, 684), + (151, 665), + (173, 645), + (193, 625), + (213, 604), + (222, 593), + ), + ), + ("closePath", ()), + ("moveTo", ((913, -57),)), + ("lineTo", ((953, 30),)), + ( + "qCurveTo", + ( + (919, 41), + (854, 67), + (790, 98), + (729, 134), + (671, 173), + (616, 217), + (564, 264), + (540, 290), + ), + ), + ("lineTo", ((522, 286),)), + ("qCurveTo", ((511, 267), (498, 235), (493, 213), (492, 206))), + ("lineTo", ((515, 209),)), + ("qCurveTo", ((569, 146), (695, 44), (835, -32), (913, -57))), + ("closePath", ()), + ("moveTo", ((474, 274),)), + ("lineTo", ((452, 284),)), + ( + "qCurveTo", + ( + (428, 260), + (377, 214), + (323, 172), + (266, 135), + (206, 101), + (144, 71), + (80, 46), + (47, 36), + ), + ), + ("lineTo", ((89, -53),)), + ("qCurveTo", ((163, -29), (299, 46), (423, 142), (476, 201))), + ("lineTo", ((498, 196),)), + ("qCurveTo", ((498, 203), (494, 225), (482, 255), (474, 274))), + ("closePath", ()), + ("moveTo", ((450, 250),)), + ("lineTo", ((550, 250),)), + ("lineTo", ((550, 379),)), + ("lineTo", ((450, 379),)), + ("closePath", ()), + ("moveTo", ((68, 215),)), + ("lineTo", ((932, 215),)), + ("lineTo", ((932, 305),)), + ("lineTo", ((68, 305),)), + ("closePath", ()), + ("moveTo", ((450, -71),)), + ("lineTo", ((550, -71),)), + ("lineTo", ((550, -71),)), + ("lineTo", ((550, 267),)), + ("lineTo", ((450, 267),)), + ("lineTo", ((450, -71),)), + ("closePath", ()), + ] + + actual = [ + (op, tuple((otRound(pt[0]), otRound(pt[1])) for pt in args)) + for op, args in actual + ] + + assert actual == expected, (actual, expected) + + pen = RecordingPointPen() + glyph.drawPoints(pen) + assert pen.value + + def test_cubic_glyf(self): + font = TTFont(self.getpath("dot-cubic.ttf")) + glyphset = font.getGlyphSet() + + expected = [ + ("moveTo", ((76, 181),)), + ("curveTo", ((103, 181), (125, 158), (125, 131))), + ("curveTo", ((125, 104), (103, 82), (76, 82))), + ("curveTo", ((48, 82), (26, 104), (26, 131))), + ("curveTo", ((26, 158), (48, 181), (76, 181))), + ("closePath", ()), + ] + + pen = RecordingPen() + glyphset["one"].draw(pen) + assert pen.value == expected + + expectedPoints = [ + ("beginPath", (), {}), + ("addPoint", ((76, 181), "curve", False, None), {}), + ("addPoint", ((103, 181), None, False, None), {}), + ("addPoint", ((125, 158), None, False, None), {}), + ("addPoint", ((125, 104), None, False, None), {}), + ("addPoint", ((103, 82), None, False, None), {}), + ("addPoint", ((76, 82), "curve", False, None), {}), + ("addPoint", ((48, 82), None, False, None), {}), + ("addPoint", ((26, 104), None, False, None), {}), + ("addPoint", ((26, 158), None, False, None), {}), + ("addPoint", ((48, 181), None, False, None), {}), + ("endPath", (), {}), + ] + pen = RecordingPointPen() + glyphset["one"].drawPoints(pen) + assert pen.value == expectedPoints + + pen = RecordingPen() + glyphset["two"].draw(pen) + assert pen.value == expected + + expectedPoints = [ + ("beginPath", (), {}), + ("addPoint", ((26, 158), None, False, None), {}), + ("addPoint", ((48, 181), None, False, None), {}), + ("addPoint", ((76, 181), "curve", False, None), {}), + ("addPoint", ((103, 181), None, False, None), {}), + ("addPoint", ((125, 158), None, False, None), {}), + ("addPoint", ((125, 104), None, False, None), {}), + ("addPoint", ((103, 82), None, False, None), {}), + ("addPoint", ((76, 82), "curve", False, None), {}), + ("addPoint", ((48, 82), None, False, None), {}), + ("addPoint", ((26, 104), None, False, None), {}), + ("endPath", (), {}), + ] + pen = RecordingPointPen() + glyphset["two"].drawPoints(pen) + assert pen.value == expectedPoints + + pen = RecordingPen() + glyphset["three"].draw(pen) + assert pen.value == expected + + expectedPoints = [ + ("beginPath", (), {}), + ("addPoint", ((48, 82), None, False, None), {}), + ("addPoint", ((26, 104), None, False, None), {}), + ("addPoint", ((26, 158), None, False, None), {}), + ("addPoint", ((48, 181), None, False, None), {}), + ("addPoint", ((76, 181), "curve", False, None), {}), + ("addPoint", ((103, 181), None, False, None), {}), + ("addPoint", ((125, 158), None, False, None), {}), + ("addPoint", ((125, 104), None, False, None), {}), + ("addPoint", ((103, 82), None, False, None), {}), + ("addPoint", ((76, 82), "curve", False, None), {}), + ("endPath", (), {}), + ] + pen = RecordingPointPen() + glyphset["three"].drawPoints(pen) + assert pen.value == expectedPoints + + pen = RecordingPen() + glyphset["four"].draw(pen) + assert pen.value == [ + ("moveTo", ((75.5, 181),)), + ("curveTo", ((103, 181), (125, 158), (125, 131))), + ("curveTo", ((125, 104), (103, 82), (75.5, 82))), + ("curveTo", ((48, 82), (26, 104), (26, 131))), + ("curveTo", ((26, 158), (48, 181), (75.5, 181))), + ("closePath", ()), + ] + + # Ouch! We can't represent all-cubic-offcurves in pointPen! + # https://github.com/fonttools/fonttools/issues/3191 + expectedPoints = [ + ("beginPath", (), {}), + ("addPoint", ((103, 181), None, False, None), {}), + ("addPoint", ((125, 158), None, False, None), {}), + ("addPoint", ((125, 104), None, False, None), {}), + ("addPoint", ((103, 82), None, False, None), {}), + ("addPoint", ((48, 82), None, False, None), {}), + ("addPoint", ((26, 104), None, False, None), {}), + ("addPoint", ((26, 158), None, False, None), {}), + ("addPoint", ((48, 181), None, False, None), {}), + ("endPath", (), {}), + ] + pen = RecordingPointPen() + glyphset["four"].drawPoints(pen) + print(pen.value) + assert pen.value == expectedPoints |