xref: /aosp_15_r20/external/fonttools/Tests/subset/subset_test.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1*e1fe3e4aSElliott Hughesimport io
2*e1fe3e4aSElliott Hughesimport fontTools.ttLib.tables.otBase
3*e1fe3e4aSElliott Hughesfrom fontTools.misc.testTools import getXML, stripVariableItemsFromTTX
4*e1fe3e4aSElliott Hughesfrom fontTools.misc.textTools import tobytes, tostr
5*e1fe3e4aSElliott Hughesfrom fontTools import subset
6*e1fe3e4aSElliott Hughesfrom fontTools.fontBuilder import FontBuilder
7*e1fe3e4aSElliott Hughesfrom fontTools.pens.ttGlyphPen import TTGlyphPen
8*e1fe3e4aSElliott Hughesfrom fontTools.ttLib import TTFont, newTable
9*e1fe3e4aSElliott Hughesfrom fontTools.ttLib.tables import otTables as ot
10*e1fe3e4aSElliott Hughesfrom fontTools.misc.loggingTools import CapturingLogHandler
11*e1fe3e4aSElliott Hughesfrom fontTools.subset.svg import etree
12*e1fe3e4aSElliott Hughesimport difflib
13*e1fe3e4aSElliott Hughesimport logging
14*e1fe3e4aSElliott Hughesimport os
15*e1fe3e4aSElliott Hughesimport shutil
16*e1fe3e4aSElliott Hughesimport sys
17*e1fe3e4aSElliott Hughesimport tempfile
18*e1fe3e4aSElliott Hughesimport unittest
19*e1fe3e4aSElliott Hughesimport pathlib
20*e1fe3e4aSElliott Hughesimport pytest
21*e1fe3e4aSElliott Hughes
22*e1fe3e4aSElliott Hughes
23*e1fe3e4aSElliott Hughesclass SubsetTest:
24*e1fe3e4aSElliott Hughes    @classmethod
25*e1fe3e4aSElliott Hughes    def setup_class(cls):
26*e1fe3e4aSElliott Hughes        cls.tempdir = None
27*e1fe3e4aSElliott Hughes        cls.num_tempfiles = 0
28*e1fe3e4aSElliott Hughes
29*e1fe3e4aSElliott Hughes    @classmethod
30*e1fe3e4aSElliott Hughes    def teardown_class(cls):
31*e1fe3e4aSElliott Hughes        if cls.tempdir:
32*e1fe3e4aSElliott Hughes            shutil.rmtree(cls.tempdir, ignore_errors=True)
33*e1fe3e4aSElliott Hughes
34*e1fe3e4aSElliott Hughes    @staticmethod
35*e1fe3e4aSElliott Hughes    def getpath(*testfile):
36*e1fe3e4aSElliott Hughes        path, _ = os.path.split(__file__)
37*e1fe3e4aSElliott Hughes        return os.path.join(path, "data", *testfile)
38*e1fe3e4aSElliott Hughes
39*e1fe3e4aSElliott Hughes    @classmethod
40*e1fe3e4aSElliott Hughes    def temp_path(cls, suffix):
41*e1fe3e4aSElliott Hughes        if not cls.tempdir:
42*e1fe3e4aSElliott Hughes            cls.tempdir = tempfile.mkdtemp()
43*e1fe3e4aSElliott Hughes        cls.num_tempfiles += 1
44*e1fe3e4aSElliott Hughes        return os.path.join(cls.tempdir, "tmp%d%s" % (cls.num_tempfiles, suffix))
45*e1fe3e4aSElliott Hughes
46*e1fe3e4aSElliott Hughes    @staticmethod
47*e1fe3e4aSElliott Hughes    def read_ttx(path):
48*e1fe3e4aSElliott Hughes        with open(path, "r", encoding="utf-8") as f:
49*e1fe3e4aSElliott Hughes            ttx = f.read()
50*e1fe3e4aSElliott Hughes        # don't care whether TTF or OTF, thus strip sfntVersion as well
51*e1fe3e4aSElliott Hughes        return stripVariableItemsFromTTX(ttx, sfntVersion=True).splitlines(True)
52*e1fe3e4aSElliott Hughes
53*e1fe3e4aSElliott Hughes    def expect_ttx(self, font, expected_ttx, tables=None):
54*e1fe3e4aSElliott Hughes        path = self.temp_path(suffix=".ttx")
55*e1fe3e4aSElliott Hughes        font.saveXML(path, tables=tables)
56*e1fe3e4aSElliott Hughes        actual = self.read_ttx(path)
57*e1fe3e4aSElliott Hughes        expected = self.read_ttx(expected_ttx)
58*e1fe3e4aSElliott Hughes        if actual != expected:
59*e1fe3e4aSElliott Hughes            for line in difflib.unified_diff(
60*e1fe3e4aSElliott Hughes                expected, actual, fromfile=expected_ttx, tofile=path
61*e1fe3e4aSElliott Hughes            ):
62*e1fe3e4aSElliott Hughes                sys.stdout.write(line)
63*e1fe3e4aSElliott Hughes            pytest.fail("TTX output is different from expected")
64*e1fe3e4aSElliott Hughes
65*e1fe3e4aSElliott Hughes    def compile_font(self, path, suffix):
66*e1fe3e4aSElliott Hughes        savepath = self.temp_path(suffix=suffix)
67*e1fe3e4aSElliott Hughes        font = TTFont(recalcBBoxes=False, recalcTimestamp=False)
68*e1fe3e4aSElliott Hughes        font.importXML(path)
69*e1fe3e4aSElliott Hughes        font.save(savepath, reorderTables=None)
70*e1fe3e4aSElliott Hughes        return savepath
71*e1fe3e4aSElliott Hughes
72*e1fe3e4aSElliott Hughes    # -----
73*e1fe3e4aSElliott Hughes    # Tests
74*e1fe3e4aSElliott Hughes    # -----
75*e1fe3e4aSElliott Hughes
76*e1fe3e4aSElliott Hughes    def test_layout_scripts(self):
77*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("layout_scripts.ttx"), ".otf")
78*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".otf")
79*e1fe3e4aSElliott Hughes        subset.main(
80*e1fe3e4aSElliott Hughes            [
81*e1fe3e4aSElliott Hughes                fontpath,
82*e1fe3e4aSElliott Hughes                "--glyphs=*",
83*e1fe3e4aSElliott Hughes                "--layout-features=*",
84*e1fe3e4aSElliott Hughes                "--layout-scripts=latn,arab.URD,arab.dflt",
85*e1fe3e4aSElliott Hughes                "--output-file=%s" % subsetpath,
86*e1fe3e4aSElliott Hughes            ]
87*e1fe3e4aSElliott Hughes        )
88*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
89*e1fe3e4aSElliott Hughes        self.expect_ttx(
90*e1fe3e4aSElliott Hughes            subsetfont, self.getpath("expect_layout_scripts.ttx"), ["GPOS", "GSUB"]
91*e1fe3e4aSElliott Hughes        )
92*e1fe3e4aSElliott Hughes
93*e1fe3e4aSElliott Hughes    def test_no_notdef_outline_otf(self):
94*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestOTF-Regular.ttx"), ".otf")
95*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".otf")
96*e1fe3e4aSElliott Hughes        subset.main(
97*e1fe3e4aSElliott Hughes            [
98*e1fe3e4aSElliott Hughes                fontpath,
99*e1fe3e4aSElliott Hughes                "--no-notdef-outline",
100*e1fe3e4aSElliott Hughes                "--gids=0",
101*e1fe3e4aSElliott Hughes                "--output-file=%s" % subsetpath,
102*e1fe3e4aSElliott Hughes            ]
103*e1fe3e4aSElliott Hughes        )
104*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
105*e1fe3e4aSElliott Hughes        self.expect_ttx(
106*e1fe3e4aSElliott Hughes            subsetfont, self.getpath("expect_no_notdef_outline_otf.ttx"), ["CFF "]
107*e1fe3e4aSElliott Hughes        )
108*e1fe3e4aSElliott Hughes
109*e1fe3e4aSElliott Hughes    def test_no_notdef_outline_cid(self):
110*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestCID-Regular.ttx"), ".otf")
111*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".otf")
112*e1fe3e4aSElliott Hughes        subset.main(
113*e1fe3e4aSElliott Hughes            [
114*e1fe3e4aSElliott Hughes                fontpath,
115*e1fe3e4aSElliott Hughes                "--no-notdef-outline",
116*e1fe3e4aSElliott Hughes                "--gids=0",
117*e1fe3e4aSElliott Hughes                "--output-file=%s" % subsetpath,
118*e1fe3e4aSElliott Hughes            ]
119*e1fe3e4aSElliott Hughes        )
120*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
121*e1fe3e4aSElliott Hughes        self.expect_ttx(
122*e1fe3e4aSElliott Hughes            subsetfont, self.getpath("expect_no_notdef_outline_cid.ttx"), ["CFF "]
123*e1fe3e4aSElliott Hughes        )
124*e1fe3e4aSElliott Hughes
125*e1fe3e4aSElliott Hughes    def test_no_notdef_outline_ttf(self):
126*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf")
127*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
128*e1fe3e4aSElliott Hughes        subset.main(
129*e1fe3e4aSElliott Hughes            [
130*e1fe3e4aSElliott Hughes                fontpath,
131*e1fe3e4aSElliott Hughes                "--no-notdef-outline",
132*e1fe3e4aSElliott Hughes                "--gids=0",
133*e1fe3e4aSElliott Hughes                "--output-file=%s" % subsetpath,
134*e1fe3e4aSElliott Hughes            ]
135*e1fe3e4aSElliott Hughes        )
136*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
137*e1fe3e4aSElliott Hughes        self.expect_ttx(
138*e1fe3e4aSElliott Hughes            subsetfont,
139*e1fe3e4aSElliott Hughes            self.getpath("expect_no_notdef_outline_ttf.ttx"),
140*e1fe3e4aSElliott Hughes            ["glyf", "hmtx"],
141*e1fe3e4aSElliott Hughes        )
142*e1fe3e4aSElliott Hughes
143*e1fe3e4aSElliott Hughes    def test_subset_ankr(self):
144*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestANKR.ttx"), ".ttf")
145*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
146*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--glyphs=one", "--output-file=%s" % subsetpath])
147*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
148*e1fe3e4aSElliott Hughes        self.expect_ttx(subsetfont, self.getpath("expect_ankr.ttx"), ["ankr"])
149*e1fe3e4aSElliott Hughes
150*e1fe3e4aSElliott Hughes    def test_subset_ankr_remove(self):
151*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestANKR.ttx"), ".ttf")
152*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
153*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--glyphs=two", "--output-file=%s" % subsetpath])
154*e1fe3e4aSElliott Hughes        assert "ankr" not in TTFont(subsetpath)
155*e1fe3e4aSElliott Hughes
156*e1fe3e4aSElliott Hughes    def test_subset_bsln_format_0(self):
157*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestBSLN-0.ttx"), ".ttf")
158*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
159*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--glyphs=one", "--output-file=%s" % subsetpath])
160*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
161*e1fe3e4aSElliott Hughes        self.expect_ttx(subsetfont, self.getpath("expect_bsln_0.ttx"), ["bsln"])
162*e1fe3e4aSElliott Hughes
163*e1fe3e4aSElliott Hughes    def test_subset_bsln_format_0_from_format_1(self):
164*e1fe3e4aSElliott Hughes        # TestBSLN-1 defines the ideographic baseline to be the font's default,
165*e1fe3e4aSElliott Hughes        # and specifies that glyphs {.notdef, zero, one, two} use the roman
166*e1fe3e4aSElliott Hughes        # baseline instead of the default ideographic baseline. As we request
167*e1fe3e4aSElliott Hughes        # a subsetted font with {zero, one} and the implicit .notdef, all
168*e1fe3e4aSElliott Hughes        # glyphs in the resulting font use the Roman baseline. In this case,
169*e1fe3e4aSElliott Hughes        # we expect a format 0 'bsln' table because it is the most compact.
170*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestBSLN-1.ttx"), ".ttf")
171*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
172*e1fe3e4aSElliott Hughes        subset.main(
173*e1fe3e4aSElliott Hughes            [fontpath, "--unicodes=U+0030-0031", "--output-file=%s" % subsetpath]
174*e1fe3e4aSElliott Hughes        )
175*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
176*e1fe3e4aSElliott Hughes        self.expect_ttx(subsetfont, self.getpath("expect_bsln_0.ttx"), ["bsln"])
177*e1fe3e4aSElliott Hughes
178*e1fe3e4aSElliott Hughes    def test_subset_bsln_format_1(self):
179*e1fe3e4aSElliott Hughes        # TestBSLN-1 defines the ideographic baseline to be the font's default,
180*e1fe3e4aSElliott Hughes        # and specifies that glyphs {.notdef, zero, one, two} use the roman
181*e1fe3e4aSElliott Hughes        # baseline instead of the default ideographic baseline. We request
182*e1fe3e4aSElliott Hughes        # a subset where the majority of glyphs use the roman baseline,
183*e1fe3e4aSElliott Hughes        # but one single glyph (uni2EA2) is ideographic. In the resulting
184*e1fe3e4aSElliott Hughes        # subsetted font, we expect a format 1 'bsln' table whose default
185*e1fe3e4aSElliott Hughes        # is Roman, but with an override that uses the ideographic baseline
186*e1fe3e4aSElliott Hughes        # for uni2EA2.
187*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestBSLN-1.ttx"), ".ttf")
188*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
189*e1fe3e4aSElliott Hughes        subset.main(
190*e1fe3e4aSElliott Hughes            [fontpath, "--unicodes=U+0030-0031,U+2EA2", "--output-file=%s" % subsetpath]
191*e1fe3e4aSElliott Hughes        )
192*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
193*e1fe3e4aSElliott Hughes        self.expect_ttx(subsetfont, self.getpath("expect_bsln_1.ttx"), ["bsln"])
194*e1fe3e4aSElliott Hughes
195*e1fe3e4aSElliott Hughes    def test_subset_bsln_format_2(self):
196*e1fe3e4aSElliott Hughes        # The 'bsln' table in TestBSLN-2 refers to control points in glyph 'P'
197*e1fe3e4aSElliott Hughes        # for defining its baselines. Therefore, the subsetted font should
198*e1fe3e4aSElliott Hughes        # include this glyph even though it is not requested explicitly.
199*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestBSLN-2.ttx"), ".ttf")
200*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
201*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--glyphs=one", "--output-file=%s" % subsetpath])
202*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
203*e1fe3e4aSElliott Hughes        self.expect_ttx(subsetfont, self.getpath("expect_bsln_2.ttx"), ["bsln"])
204*e1fe3e4aSElliott Hughes
205*e1fe3e4aSElliott Hughes    def test_subset_bsln_format_2_from_format_3(self):
206*e1fe3e4aSElliott Hughes        # TestBSLN-3 defines the ideographic baseline to be the font's default,
207*e1fe3e4aSElliott Hughes        # and specifies that glyphs {.notdef, zero, one, two, P} use the roman
208*e1fe3e4aSElliott Hughes        # baseline instead of the default ideographic baseline. As we request
209*e1fe3e4aSElliott Hughes        # a subsetted font with zero and the implicit .notdef and P for
210*e1fe3e4aSElliott Hughes        # baseline measurement, all glyphs in the resulting font use the Roman
211*e1fe3e4aSElliott Hughes        # baseline. In this case, we expect a format 2 'bsln' table because it
212*e1fe3e4aSElliott Hughes        # is the most compact encoding.
213*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestBSLN-3.ttx"), ".ttf")
214*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
215*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--unicodes=U+0030", "--output-file=%s" % subsetpath])
216*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
217*e1fe3e4aSElliott Hughes        self.expect_ttx(subsetfont, self.getpath("expect_bsln_2.ttx"), ["bsln"])
218*e1fe3e4aSElliott Hughes
219*e1fe3e4aSElliott Hughes    def test_subset_bsln_format_3(self):
220*e1fe3e4aSElliott Hughes        # TestBSLN-3 defines the ideographic baseline to be the font's default,
221*e1fe3e4aSElliott Hughes        # and specifies that glyphs {.notdef, zero, one, two} use the roman
222*e1fe3e4aSElliott Hughes        # baseline instead of the default ideographic baseline. We request
223*e1fe3e4aSElliott Hughes        # a subset where the majority of glyphs use the roman baseline,
224*e1fe3e4aSElliott Hughes        # but one single glyph (uni2EA2) is ideographic. In the resulting
225*e1fe3e4aSElliott Hughes        # subsetted font, we expect a format 1 'bsln' table whose default
226*e1fe3e4aSElliott Hughes        # is Roman, but with an override that uses the ideographic baseline
227*e1fe3e4aSElliott Hughes        # for uni2EA2.
228*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestBSLN-3.ttx"), ".ttf")
229*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
230*e1fe3e4aSElliott Hughes        subset.main(
231*e1fe3e4aSElliott Hughes            [fontpath, "--unicodes=U+0030-0031,U+2EA2", "--output-file=%s" % subsetpath]
232*e1fe3e4aSElliott Hughes        )
233*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
234*e1fe3e4aSElliott Hughes        self.expect_ttx(subsetfont, self.getpath("expect_bsln_3.ttx"), ["bsln"])
235*e1fe3e4aSElliott Hughes
236*e1fe3e4aSElliott Hughes    def test_subset_clr(self):
237*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestCLR-Regular.ttx"), ".ttf")
238*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
239*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--glyphs=smileface", "--output-file=%s" % subsetpath])
240*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
241*e1fe3e4aSElliott Hughes        self.expect_ttx(
242*e1fe3e4aSElliott Hughes            subsetfont,
243*e1fe3e4aSElliott Hughes            self.getpath("expect_keep_colr.ttx"),
244*e1fe3e4aSElliott Hughes            ["GlyphOrder", "hmtx", "glyf", "COLR", "CPAL"],
245*e1fe3e4aSElliott Hughes        )
246*e1fe3e4aSElliott Hughes
247*e1fe3e4aSElliott Hughes    def test_subset_gvar(self):
248*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestGVAR.ttx"), ".ttf")
249*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
250*e1fe3e4aSElliott Hughes        subset.main(
251*e1fe3e4aSElliott Hughes            [fontpath, "--unicodes=U+002B,U+2212", "--output-file=%s" % subsetpath]
252*e1fe3e4aSElliott Hughes        )
253*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
254*e1fe3e4aSElliott Hughes        self.expect_ttx(
255*e1fe3e4aSElliott Hughes            subsetfont,
256*e1fe3e4aSElliott Hughes            self.getpath("expect_keep_gvar.ttx"),
257*e1fe3e4aSElliott Hughes            ["GlyphOrder", "avar", "fvar", "gvar", "name"],
258*e1fe3e4aSElliott Hughes        )
259*e1fe3e4aSElliott Hughes
260*e1fe3e4aSElliott Hughes    def test_subset_gvar_notdef_outline(self):
261*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestGVAR.ttx"), ".ttf")
262*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
263*e1fe3e4aSElliott Hughes        subset.main(
264*e1fe3e4aSElliott Hughes            [
265*e1fe3e4aSElliott Hughes                fontpath,
266*e1fe3e4aSElliott Hughes                "--unicodes=U+0030",
267*e1fe3e4aSElliott Hughes                "--notdef_outline",
268*e1fe3e4aSElliott Hughes                "--output-file=%s" % subsetpath,
269*e1fe3e4aSElliott Hughes            ]
270*e1fe3e4aSElliott Hughes        )
271*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
272*e1fe3e4aSElliott Hughes        self.expect_ttx(
273*e1fe3e4aSElliott Hughes            subsetfont,
274*e1fe3e4aSElliott Hughes            self.getpath("expect_keep_gvar_notdef_outline.ttx"),
275*e1fe3e4aSElliott Hughes            ["GlyphOrder", "avar", "fvar", "gvar", "name"],
276*e1fe3e4aSElliott Hughes        )
277*e1fe3e4aSElliott Hughes
278*e1fe3e4aSElliott Hughes    def test_subset_lcar_remove(self):
279*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestLCAR-0.ttx"), ".ttf")
280*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
281*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--glyphs=one", "--output-file=%s" % subsetpath])
282*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
283*e1fe3e4aSElliott Hughes        assert "lcar" not in subsetfont
284*e1fe3e4aSElliott Hughes
285*e1fe3e4aSElliott Hughes    def test_subset_lcar_format_0(self):
286*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestLCAR-0.ttx"), ".ttf")
287*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
288*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--unicodes=U+FB01", "--output-file=%s" % subsetpath])
289*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
290*e1fe3e4aSElliott Hughes        self.expect_ttx(subsetfont, self.getpath("expect_lcar_0.ttx"), ["lcar"])
291*e1fe3e4aSElliott Hughes
292*e1fe3e4aSElliott Hughes    def test_subset_lcar_format_1(self):
293*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestLCAR-1.ttx"), ".ttf")
294*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
295*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--unicodes=U+FB01", "--output-file=%s" % subsetpath])
296*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
297*e1fe3e4aSElliott Hughes        self.expect_ttx(subsetfont, self.getpath("expect_lcar_1.ttx"), ["lcar"])
298*e1fe3e4aSElliott Hughes
299*e1fe3e4aSElliott Hughes    def test_subset_math(self):
300*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestMATH-Regular.ttx"), ".ttf")
301*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
302*e1fe3e4aSElliott Hughes        subset.main(
303*e1fe3e4aSElliott Hughes            [
304*e1fe3e4aSElliott Hughes                fontpath,
305*e1fe3e4aSElliott Hughes                "--unicodes=U+0041,U+0028,U+0302,U+1D400,U+1D435",
306*e1fe3e4aSElliott Hughes                "--output-file=%s" % subsetpath,
307*e1fe3e4aSElliott Hughes            ]
308*e1fe3e4aSElliott Hughes        )
309*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
310*e1fe3e4aSElliott Hughes        self.expect_ttx(
311*e1fe3e4aSElliott Hughes            subsetfont,
312*e1fe3e4aSElliott Hughes            self.getpath("expect_keep_math.ttx"),
313*e1fe3e4aSElliott Hughes            ["GlyphOrder", "CFF ", "MATH", "hmtx"],
314*e1fe3e4aSElliott Hughes        )
315*e1fe3e4aSElliott Hughes
316*e1fe3e4aSElliott Hughes    def test_subset_math_partial(self):
317*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("test_math_partial.ttx"), ".ttf")
318*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
319*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--text=A", "--output-file=%s" % subsetpath])
320*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
321*e1fe3e4aSElliott Hughes        self.expect_ttx(subsetfont, self.getpath("expect_math_partial.ttx"), ["MATH"])
322*e1fe3e4aSElliott Hughes
323*e1fe3e4aSElliott Hughes    def test_subset_opbd_remove(self):
324*e1fe3e4aSElliott Hughes        # In the test font, only the glyphs 'A' and 'zero' have an entry in
325*e1fe3e4aSElliott Hughes        # the Optical Bounds table. When subsetting, we do not request any
326*e1fe3e4aSElliott Hughes        # of those glyphs. Therefore, the produced subsetted font should
327*e1fe3e4aSElliott Hughes        # not contain an 'opbd' table.
328*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestOPBD-0.ttx"), ".ttf")
329*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
330*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--glyphs=one", "--output-file=%s" % subsetpath])
331*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
332*e1fe3e4aSElliott Hughes        assert "opbd" not in subsetfont
333*e1fe3e4aSElliott Hughes
334*e1fe3e4aSElliott Hughes    def test_subset_opbd_format_0(self):
335*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestOPBD-0.ttx"), ".ttf")
336*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
337*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--glyphs=A", "--output-file=%s" % subsetpath])
338*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
339*e1fe3e4aSElliott Hughes        self.expect_ttx(subsetfont, self.getpath("expect_opbd_0.ttx"), ["opbd"])
340*e1fe3e4aSElliott Hughes
341*e1fe3e4aSElliott Hughes    def test_subset_opbd_format_1(self):
342*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestOPBD-1.ttx"), ".ttf")
343*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
344*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--glyphs=A", "--output-file=%s" % subsetpath])
345*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
346*e1fe3e4aSElliott Hughes        self.expect_ttx(subsetfont, self.getpath("expect_opbd_1.ttx"), ["opbd"])
347*e1fe3e4aSElliott Hughes
348*e1fe3e4aSElliott Hughes    def test_subset_prop_remove_default_zero(self):
349*e1fe3e4aSElliott Hughes        # If all glyphs have an AAT glyph property with value 0,
350*e1fe3e4aSElliott Hughes        # the "prop" table should be removed from the subsetted font.
351*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestPROP.ttx"), ".ttf")
352*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
353*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--unicodes=U+0041", "--output-file=%s" % subsetpath])
354*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
355*e1fe3e4aSElliott Hughes        assert "prop" not in subsetfont
356*e1fe3e4aSElliott Hughes
357*e1fe3e4aSElliott Hughes    def test_subset_prop_0(self):
358*e1fe3e4aSElliott Hughes        # If all glyphs share the same AAT glyph properties, the "prop" table
359*e1fe3e4aSElliott Hughes        # in the subsetted font should use format 0.
360*e1fe3e4aSElliott Hughes        #
361*e1fe3e4aSElliott Hughes        # Unless the shared value is zero, in which case the subsetted font
362*e1fe3e4aSElliott Hughes        # should have no "prop" table at all. But that case has already been
363*e1fe3e4aSElliott Hughes        # tested above in test_subset_prop_remove_default_zero().
364*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestPROP.ttx"), ".ttf")
365*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
366*e1fe3e4aSElliott Hughes        subset.main(
367*e1fe3e4aSElliott Hughes            [
368*e1fe3e4aSElliott Hughes                fontpath,
369*e1fe3e4aSElliott Hughes                "--unicodes=U+0030-0032",
370*e1fe3e4aSElliott Hughes                "--no-notdef-glyph",
371*e1fe3e4aSElliott Hughes                "--output-file=%s" % subsetpath,
372*e1fe3e4aSElliott Hughes            ]
373*e1fe3e4aSElliott Hughes        )
374*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
375*e1fe3e4aSElliott Hughes        self.expect_ttx(subsetfont, self.getpath("expect_prop_0.ttx"), ["prop"])
376*e1fe3e4aSElliott Hughes
377*e1fe3e4aSElliott Hughes    def test_subset_prop_1(self):
378*e1fe3e4aSElliott Hughes        # If not all glyphs share the same AAT glyph properties, the subsetted
379*e1fe3e4aSElliott Hughes        # font should contain a "prop" table in format 1. To save space, the
380*e1fe3e4aSElliott Hughes        # DefaultProperties should be set to the most frequent value.
381*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestPROP.ttx"), ".ttf")
382*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
383*e1fe3e4aSElliott Hughes        subset.main(
384*e1fe3e4aSElliott Hughes            [
385*e1fe3e4aSElliott Hughes                fontpath,
386*e1fe3e4aSElliott Hughes                "--unicodes=U+0030-0032",
387*e1fe3e4aSElliott Hughes                "--notdef-outline",
388*e1fe3e4aSElliott Hughes                "--output-file=%s" % subsetpath,
389*e1fe3e4aSElliott Hughes            ]
390*e1fe3e4aSElliott Hughes        )
391*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
392*e1fe3e4aSElliott Hughes        self.expect_ttx(subsetfont, self.getpath("expect_prop_1.ttx"), ["prop"])
393*e1fe3e4aSElliott Hughes
394*e1fe3e4aSElliott Hughes    def test_options(self):
395*e1fe3e4aSElliott Hughes        # https://github.com/fonttools/fonttools/issues/413
396*e1fe3e4aSElliott Hughes        opt1 = subset.Options()
397*e1fe3e4aSElliott Hughes        assert "Xyz-" not in opt1.layout_features
398*e1fe3e4aSElliott Hughes        opt2 = subset.Options()
399*e1fe3e4aSElliott Hughes        opt2.layout_features.append("Xyz-")
400*e1fe3e4aSElliott Hughes        assert "Xyz-" in opt2.layout_features
401*e1fe3e4aSElliott Hughes        assert "Xyz-" not in opt1.layout_features
402*e1fe3e4aSElliott Hughes
403*e1fe3e4aSElliott Hughes    def test_google_color(self):
404*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("google_color.ttx"), ".ttf")
405*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
406*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--gids=0,1", "--output-file=%s" % subsetpath])
407*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
408*e1fe3e4aSElliott Hughes        assert "CBDT" in subsetfont
409*e1fe3e4aSElliott Hughes        assert "CBLC" in subsetfont
410*e1fe3e4aSElliott Hughes        assert "x" in subsetfont["CBDT"].strikeData[0]
411*e1fe3e4aSElliott Hughes        assert "y" not in subsetfont["CBDT"].strikeData[0]
412*e1fe3e4aSElliott Hughes
413*e1fe3e4aSElliott Hughes    def test_google_color_all(self):
414*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("google_color.ttx"), ".ttf")
415*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
416*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--unicodes=*", "--output-file=%s" % subsetpath])
417*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
418*e1fe3e4aSElliott Hughes        assert "x" in subsetfont["CBDT"].strikeData[0]
419*e1fe3e4aSElliott Hughes        assert "y" in subsetfont["CBDT"].strikeData[0]
420*e1fe3e4aSElliott Hughes
421*e1fe3e4aSElliott Hughes    def test_sbix(self):
422*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("sbix.ttx"), ".ttf")
423*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
424*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--gids=0,1", "--output-file=%s" % subsetpath])
425*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
426*e1fe3e4aSElliott Hughes        self.expect_ttx(subsetfont, self.getpath("expect_sbix.ttx"), ["sbix"])
427*e1fe3e4aSElliott Hughes
428*e1fe3e4aSElliott Hughes    def test_varComposite(self):
429*e1fe3e4aSElliott Hughes        fontpath = self.getpath("..", "..", "ttLib", "data", "varc-ac00-ac01.ttf")
430*e1fe3e4aSElliott Hughes        origfont = TTFont(fontpath)
431*e1fe3e4aSElliott Hughes        assert len(origfont.getGlyphOrder()) == 6
432*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
433*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--unicodes=ac00", "--output-file=%s" % subsetpath])
434*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
435*e1fe3e4aSElliott Hughes        assert len(subsetfont.getGlyphOrder()) == 4
436*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--unicodes=ac01", "--output-file=%s" % subsetpath])
437*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
438*e1fe3e4aSElliott Hughes        assert len(subsetfont.getGlyphOrder()) == 5
439*e1fe3e4aSElliott Hughes
440*e1fe3e4aSElliott Hughes    def test_timing_publishes_parts(self):
441*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf")
442*e1fe3e4aSElliott Hughes
443*e1fe3e4aSElliott Hughes        options = subset.Options()
444*e1fe3e4aSElliott Hughes        options.timing = True
445*e1fe3e4aSElliott Hughes        subsetter = subset.Subsetter(options)
446*e1fe3e4aSElliott Hughes        subsetter.populate(text="ABC")
447*e1fe3e4aSElliott Hughes        font = TTFont(fontpath)
448*e1fe3e4aSElliott Hughes        with CapturingLogHandler("fontTools.subset.timer", logging.DEBUG) as captor:
449*e1fe3e4aSElliott Hughes            subsetter.subset(font)
450*e1fe3e4aSElliott Hughes        logs = captor.records
451*e1fe3e4aSElliott Hughes
452*e1fe3e4aSElliott Hughes        assert len(logs) > 5
453*e1fe3e4aSElliott Hughes        assert len(logs) == len(
454*e1fe3e4aSElliott Hughes            [l for l in logs if "msg" in l.args and "time" in l.args]
455*e1fe3e4aSElliott Hughes        )
456*e1fe3e4aSElliott Hughes        # Look for a few things we know should happen
457*e1fe3e4aSElliott Hughes        assert filter(lambda l: l.args["msg"] == "load 'cmap'", logs)
458*e1fe3e4aSElliott Hughes        assert filter(lambda l: l.args["msg"] == "subset 'cmap'", logs)
459*e1fe3e4aSElliott Hughes        assert filter(lambda l: l.args["msg"] == "subset 'glyf'", logs)
460*e1fe3e4aSElliott Hughes
461*e1fe3e4aSElliott Hughes    def test_passthrough_tables(self):
462*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf")
463*e1fe3e4aSElliott Hughes        font = TTFont(fontpath)
464*e1fe3e4aSElliott Hughes        unknown_tag = "ZZZZ"
465*e1fe3e4aSElliott Hughes        unknown_table = newTable(unknown_tag)
466*e1fe3e4aSElliott Hughes        unknown_table.data = b"\0" * 10
467*e1fe3e4aSElliott Hughes        font[unknown_tag] = unknown_table
468*e1fe3e4aSElliott Hughes        font.save(fontpath)
469*e1fe3e4aSElliott Hughes
470*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
471*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--output-file=%s" % subsetpath])
472*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
473*e1fe3e4aSElliott Hughes
474*e1fe3e4aSElliott Hughes        # tables we can't subset are dropped by default
475*e1fe3e4aSElliott Hughes        assert unknown_tag not in subsetfont
476*e1fe3e4aSElliott Hughes
477*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
478*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--passthrough-tables", "--output-file=%s" % subsetpath])
479*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
480*e1fe3e4aSElliott Hughes
481*e1fe3e4aSElliott Hughes        # unknown tables are kept if --passthrough-tables option is passed
482*e1fe3e4aSElliott Hughes        assert unknown_tag in subsetfont
483*e1fe3e4aSElliott Hughes
484*e1fe3e4aSElliott Hughes    def test_non_BMP_text_arg_input(self):
485*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(
486*e1fe3e4aSElliott Hughes            self.getpath("TestTTF-Regular_non_BMP_char.ttx"), ".ttf"
487*e1fe3e4aSElliott Hughes        )
488*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
489*e1fe3e4aSElliott Hughes        text = tostr("A\U0001F6D2", encoding="utf-8")
490*e1fe3e4aSElliott Hughes
491*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--text=%s" % text, "--output-file=%s" % subsetpath])
492*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
493*e1fe3e4aSElliott Hughes
494*e1fe3e4aSElliott Hughes        assert subsetfont["maxp"].numGlyphs == 3
495*e1fe3e4aSElliott Hughes        assert subsetfont.getGlyphOrder() == [".notdef", "A", "u1F6D2"]
496*e1fe3e4aSElliott Hughes
497*e1fe3e4aSElliott Hughes    def test_non_BMP_text_file_input(self):
498*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(
499*e1fe3e4aSElliott Hughes            self.getpath("TestTTF-Regular_non_BMP_char.ttx"), ".ttf"
500*e1fe3e4aSElliott Hughes        )
501*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
502*e1fe3e4aSElliott Hughes        text = tobytes("A\U0001F6D2", encoding="utf-8")
503*e1fe3e4aSElliott Hughes        with tempfile.NamedTemporaryFile(delete=False) as tmp:
504*e1fe3e4aSElliott Hughes            tmp.write(text)
505*e1fe3e4aSElliott Hughes
506*e1fe3e4aSElliott Hughes        try:
507*e1fe3e4aSElliott Hughes            subset.main(
508*e1fe3e4aSElliott Hughes                [fontpath, "--text-file=%s" % tmp.name, "--output-file=%s" % subsetpath]
509*e1fe3e4aSElliott Hughes            )
510*e1fe3e4aSElliott Hughes            subsetfont = TTFont(subsetpath)
511*e1fe3e4aSElliott Hughes        finally:
512*e1fe3e4aSElliott Hughes            os.remove(tmp.name)
513*e1fe3e4aSElliott Hughes
514*e1fe3e4aSElliott Hughes        assert subsetfont["maxp"].numGlyphs == 3
515*e1fe3e4aSElliott Hughes        assert subsetfont.getGlyphOrder() == [".notdef", "A", "u1F6D2"]
516*e1fe3e4aSElliott Hughes
517*e1fe3e4aSElliott Hughes    def test_no_hinting_CFF(self):
518*e1fe3e4aSElliott Hughes        ttxpath = self.getpath("Lobster.subset.ttx")
519*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(ttxpath, ".otf")
520*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".otf")
521*e1fe3e4aSElliott Hughes        subset.main(
522*e1fe3e4aSElliott Hughes            [
523*e1fe3e4aSElliott Hughes                fontpath,
524*e1fe3e4aSElliott Hughes                "--no-hinting",
525*e1fe3e4aSElliott Hughes                "--notdef-outline",
526*e1fe3e4aSElliott Hughes                "--output-file=%s" % subsetpath,
527*e1fe3e4aSElliott Hughes                "*",
528*e1fe3e4aSElliott Hughes            ]
529*e1fe3e4aSElliott Hughes        )
530*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
531*e1fe3e4aSElliott Hughes        self.expect_ttx(subsetfont, self.getpath("expect_no_hinting_CFF.ttx"), ["CFF "])
532*e1fe3e4aSElliott Hughes
533*e1fe3e4aSElliott Hughes    def test_desubroutinize_CFF(self):
534*e1fe3e4aSElliott Hughes        ttxpath = self.getpath("Lobster.subset.ttx")
535*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(ttxpath, ".otf")
536*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".otf")
537*e1fe3e4aSElliott Hughes        subset.main(
538*e1fe3e4aSElliott Hughes            [
539*e1fe3e4aSElliott Hughes                fontpath,
540*e1fe3e4aSElliott Hughes                "--desubroutinize",
541*e1fe3e4aSElliott Hughes                "--notdef-outline",
542*e1fe3e4aSElliott Hughes                "--output-file=%s" % subsetpath,
543*e1fe3e4aSElliott Hughes                "*",
544*e1fe3e4aSElliott Hughes            ]
545*e1fe3e4aSElliott Hughes        )
546*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
547*e1fe3e4aSElliott Hughes        self.expect_ttx(
548*e1fe3e4aSElliott Hughes            subsetfont, self.getpath("expect_desubroutinize_CFF.ttx"), ["CFF "]
549*e1fe3e4aSElliott Hughes        )
550*e1fe3e4aSElliott Hughes
551*e1fe3e4aSElliott Hughes    def test_desubroutinize_hinted_subrs_CFF(self):
552*e1fe3e4aSElliott Hughes        ttxpath = self.getpath("test_hinted_subrs_CFF.ttx")
553*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(ttxpath, ".otf")
554*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".otf")
555*e1fe3e4aSElliott Hughes        subset.main(
556*e1fe3e4aSElliott Hughes            [
557*e1fe3e4aSElliott Hughes                fontpath,
558*e1fe3e4aSElliott Hughes                "--desubroutinize",
559*e1fe3e4aSElliott Hughes                "--notdef-outline",
560*e1fe3e4aSElliott Hughes                "--output-file=%s" % subsetpath,
561*e1fe3e4aSElliott Hughes                "*",
562*e1fe3e4aSElliott Hughes            ]
563*e1fe3e4aSElliott Hughes        )
564*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
565*e1fe3e4aSElliott Hughes        self.expect_ttx(
566*e1fe3e4aSElliott Hughes            subsetfont, self.getpath("test_hinted_subrs_CFF.desub.ttx"), ["CFF "]
567*e1fe3e4aSElliott Hughes        )
568*e1fe3e4aSElliott Hughes
569*e1fe3e4aSElliott Hughes    def test_desubroutinize_cntrmask_CFF(self):
570*e1fe3e4aSElliott Hughes        ttxpath = self.getpath("test_cntrmask_CFF.ttx")
571*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(ttxpath, ".otf")
572*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".otf")
573*e1fe3e4aSElliott Hughes        subset.main(
574*e1fe3e4aSElliott Hughes            [
575*e1fe3e4aSElliott Hughes                fontpath,
576*e1fe3e4aSElliott Hughes                "--desubroutinize",
577*e1fe3e4aSElliott Hughes                "--notdef-outline",
578*e1fe3e4aSElliott Hughes                "--output-file=%s" % subsetpath,
579*e1fe3e4aSElliott Hughes                "*",
580*e1fe3e4aSElliott Hughes            ]
581*e1fe3e4aSElliott Hughes        )
582*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
583*e1fe3e4aSElliott Hughes        self.expect_ttx(
584*e1fe3e4aSElliott Hughes            subsetfont, self.getpath("test_cntrmask_CFF.desub.ttx"), ["CFF "]
585*e1fe3e4aSElliott Hughes        )
586*e1fe3e4aSElliott Hughes
587*e1fe3e4aSElliott Hughes    def test_no_hinting_desubroutinize_CFF(self):
588*e1fe3e4aSElliott Hughes        ttxpath = self.getpath("test_hinted_subrs_CFF.ttx")
589*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(ttxpath, ".otf")
590*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".otf")
591*e1fe3e4aSElliott Hughes        subset.main(
592*e1fe3e4aSElliott Hughes            [
593*e1fe3e4aSElliott Hughes                fontpath,
594*e1fe3e4aSElliott Hughes                "--no-hinting",
595*e1fe3e4aSElliott Hughes                "--desubroutinize",
596*e1fe3e4aSElliott Hughes                "--notdef-outline",
597*e1fe3e4aSElliott Hughes                "--output-file=%s" % subsetpath,
598*e1fe3e4aSElliott Hughes                "*",
599*e1fe3e4aSElliott Hughes            ]
600*e1fe3e4aSElliott Hughes        )
601*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
602*e1fe3e4aSElliott Hughes        self.expect_ttx(
603*e1fe3e4aSElliott Hughes            subsetfont,
604*e1fe3e4aSElliott Hughes            self.getpath("expect_no_hinting_desubroutinize_CFF.ttx"),
605*e1fe3e4aSElliott Hughes            ["CFF "],
606*e1fe3e4aSElliott Hughes        )
607*e1fe3e4aSElliott Hughes
608*e1fe3e4aSElliott Hughes    def test_no_hinting_TTF(self):
609*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf")
610*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
611*e1fe3e4aSElliott Hughes        subset.main(
612*e1fe3e4aSElliott Hughes            [
613*e1fe3e4aSElliott Hughes                fontpath,
614*e1fe3e4aSElliott Hughes                "--no-hinting",
615*e1fe3e4aSElliott Hughes                "--notdef-outline",
616*e1fe3e4aSElliott Hughes                "--output-file=%s" % subsetpath,
617*e1fe3e4aSElliott Hughes                "*",
618*e1fe3e4aSElliott Hughes            ]
619*e1fe3e4aSElliott Hughes        )
620*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
621*e1fe3e4aSElliott Hughes        self.expect_ttx(
622*e1fe3e4aSElliott Hughes            subsetfont, self.getpath("expect_no_hinting_TTF.ttx"), ["glyf", "maxp"]
623*e1fe3e4aSElliott Hughes        )
624*e1fe3e4aSElliott Hughes        for tag in subset.Options().hinting_tables:
625*e1fe3e4aSElliott Hughes            assert tag not in subsetfont
626*e1fe3e4aSElliott Hughes
627*e1fe3e4aSElliott Hughes    def test_notdef_width_cid(self):
628*e1fe3e4aSElliott Hughes        # https://github.com/fonttools/fonttools/pull/845
629*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("NotdefWidthCID-Regular.ttx"), ".otf")
630*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".otf")
631*e1fe3e4aSElliott Hughes        subset.main(
632*e1fe3e4aSElliott Hughes            [
633*e1fe3e4aSElliott Hughes                fontpath,
634*e1fe3e4aSElliott Hughes                "--no-notdef-outline",
635*e1fe3e4aSElliott Hughes                "--gids=0,1",
636*e1fe3e4aSElliott Hughes                "--output-file=%s" % subsetpath,
637*e1fe3e4aSElliott Hughes            ]
638*e1fe3e4aSElliott Hughes        )
639*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
640*e1fe3e4aSElliott Hughes        self.expect_ttx(
641*e1fe3e4aSElliott Hughes            subsetfont, self.getpath("expect_notdef_width_cid.ttx"), ["CFF "]
642*e1fe3e4aSElliott Hughes        )
643*e1fe3e4aSElliott Hughes
644*e1fe3e4aSElliott Hughes    def test_recalc_bounds_ttf(self):
645*e1fe3e4aSElliott Hughes        ttxpath = self.getpath("TestTTF-Regular.ttx")
646*e1fe3e4aSElliott Hughes        font = TTFont()
647*e1fe3e4aSElliott Hughes        font.importXML(ttxpath)
648*e1fe3e4aSElliott Hughes        head = font["head"]
649*e1fe3e4aSElliott Hughes        bounds = [head.xMin, head.yMin, head.xMax, head.yMax]
650*e1fe3e4aSElliott Hughes
651*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(ttxpath, ".ttf")
652*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
653*e1fe3e4aSElliott Hughes
654*e1fe3e4aSElliott Hughes        # by default, the subsetter does not recalculate the bounding box
655*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--output-file=%s" % subsetpath, "*"])
656*e1fe3e4aSElliott Hughes        head = TTFont(subsetpath)["head"]
657*e1fe3e4aSElliott Hughes        assert bounds == [head.xMin, head.yMin, head.xMax, head.yMax]
658*e1fe3e4aSElliott Hughes
659*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--recalc-bounds", "--output-file=%s" % subsetpath, "*"])
660*e1fe3e4aSElliott Hughes        head = TTFont(subsetpath)["head"]
661*e1fe3e4aSElliott Hughes        bounds = [132, 304, 365, 567]
662*e1fe3e4aSElliott Hughes        assert bounds == [head.xMin, head.yMin, head.xMax, head.yMax]
663*e1fe3e4aSElliott Hughes
664*e1fe3e4aSElliott Hughes    def test_recalc_bounds_otf(self):
665*e1fe3e4aSElliott Hughes        ttxpath = self.getpath("TestOTF-Regular.ttx")
666*e1fe3e4aSElliott Hughes        font = TTFont()
667*e1fe3e4aSElliott Hughes        font.importXML(ttxpath)
668*e1fe3e4aSElliott Hughes        head = font["head"]
669*e1fe3e4aSElliott Hughes        bounds = [head.xMin, head.yMin, head.xMax, head.yMax]
670*e1fe3e4aSElliott Hughes
671*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(ttxpath, ".otf")
672*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".otf")
673*e1fe3e4aSElliott Hughes
674*e1fe3e4aSElliott Hughes        # by default, the subsetter does not recalculate the bounding box
675*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--output-file=%s" % subsetpath, "*"])
676*e1fe3e4aSElliott Hughes        head = TTFont(subsetpath)["head"]
677*e1fe3e4aSElliott Hughes        assert bounds == [head.xMin, head.yMin, head.xMax, head.yMax]
678*e1fe3e4aSElliott Hughes
679*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--recalc-bounds", "--output-file=%s" % subsetpath, "*"])
680*e1fe3e4aSElliott Hughes        head = TTFont(subsetpath)["head"]
681*e1fe3e4aSElliott Hughes        bounds = [132, 304, 365, 567]
682*e1fe3e4aSElliott Hughes        assert bounds == [head.xMin, head.yMin, head.xMax, head.yMax]
683*e1fe3e4aSElliott Hughes
684*e1fe3e4aSElliott Hughes    def test_recalc_timestamp_ttf(self):
685*e1fe3e4aSElliott Hughes        ttxpath = self.getpath("TestTTF-Regular.ttx")
686*e1fe3e4aSElliott Hughes        font = TTFont()
687*e1fe3e4aSElliott Hughes        font.importXML(ttxpath)
688*e1fe3e4aSElliott Hughes        modified = font["head"].modified
689*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(ttxpath, ".ttf")
690*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
691*e1fe3e4aSElliott Hughes
692*e1fe3e4aSElliott Hughes        # by default, the subsetter does not recalculate the modified timestamp
693*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--output-file=%s" % subsetpath, "*"])
694*e1fe3e4aSElliott Hughes        assert modified == TTFont(subsetpath)["head"].modified
695*e1fe3e4aSElliott Hughes
696*e1fe3e4aSElliott Hughes        subset.main(
697*e1fe3e4aSElliott Hughes            [fontpath, "--recalc-timestamp", "--output-file=%s" % subsetpath, "*"]
698*e1fe3e4aSElliott Hughes        )
699*e1fe3e4aSElliott Hughes        assert modified < TTFont(subsetpath)["head"].modified
700*e1fe3e4aSElliott Hughes
701*e1fe3e4aSElliott Hughes    def test_recalc_timestamp_otf(self):
702*e1fe3e4aSElliott Hughes        ttxpath = self.getpath("TestOTF-Regular.ttx")
703*e1fe3e4aSElliott Hughes        font = TTFont()
704*e1fe3e4aSElliott Hughes        font.importXML(ttxpath)
705*e1fe3e4aSElliott Hughes        modified = font["head"].modified
706*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(ttxpath, ".otf")
707*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".otf")
708*e1fe3e4aSElliott Hughes
709*e1fe3e4aSElliott Hughes        # by default, the subsetter does not recalculate the modified timestamp
710*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--output-file=%s" % subsetpath, "*"])
711*e1fe3e4aSElliott Hughes        assert modified == TTFont(subsetpath)["head"].modified
712*e1fe3e4aSElliott Hughes
713*e1fe3e4aSElliott Hughes        subset.main(
714*e1fe3e4aSElliott Hughes            [fontpath, "--recalc-timestamp", "--output-file=%s" % subsetpath, "*"]
715*e1fe3e4aSElliott Hughes        )
716*e1fe3e4aSElliott Hughes        assert modified < TTFont(subsetpath)["head"].modified
717*e1fe3e4aSElliott Hughes
718*e1fe3e4aSElliott Hughes    def test_recalc_max_context(self):
719*e1fe3e4aSElliott Hughes        ttxpath = self.getpath("Lobster.subset.ttx")
720*e1fe3e4aSElliott Hughes        font = TTFont()
721*e1fe3e4aSElliott Hughes        font.importXML(ttxpath)
722*e1fe3e4aSElliott Hughes        max_context = font["OS/2"].usMaxContext
723*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(ttxpath, ".otf")
724*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".otf")
725*e1fe3e4aSElliott Hughes
726*e1fe3e4aSElliott Hughes        # by default, the subsetter does not recalculate the usMaxContext
727*e1fe3e4aSElliott Hughes        subset.main(
728*e1fe3e4aSElliott Hughes            [fontpath, "--drop-tables+=GSUB,GPOS", "--output-file=%s" % subsetpath]
729*e1fe3e4aSElliott Hughes        )
730*e1fe3e4aSElliott Hughes        assert max_context == TTFont(subsetpath)["OS/2"].usMaxContext
731*e1fe3e4aSElliott Hughes
732*e1fe3e4aSElliott Hughes        subset.main(
733*e1fe3e4aSElliott Hughes            [
734*e1fe3e4aSElliott Hughes                fontpath,
735*e1fe3e4aSElliott Hughes                "--recalc-max-context",
736*e1fe3e4aSElliott Hughes                "--drop-tables+=GSUB,GPOS",
737*e1fe3e4aSElliott Hughes                "--output-file=%s" % subsetpath,
738*e1fe3e4aSElliott Hughes            ]
739*e1fe3e4aSElliott Hughes        )
740*e1fe3e4aSElliott Hughes        assert 0 == TTFont(subsetpath)["OS/2"].usMaxContext
741*e1fe3e4aSElliott Hughes
742*e1fe3e4aSElliott Hughes    def test_retain_gids_ttf(self):
743*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf")
744*e1fe3e4aSElliott Hughes        font = TTFont(fontpath)
745*e1fe3e4aSElliott Hughes
746*e1fe3e4aSElliott Hughes        assert font["hmtx"]["A"] == (500, 132)
747*e1fe3e4aSElliott Hughes        assert font["hmtx"]["B"] == (400, 132)
748*e1fe3e4aSElliott Hughes
749*e1fe3e4aSElliott Hughes        assert font["glyf"]["A"].numberOfContours > 0
750*e1fe3e4aSElliott Hughes        assert font["glyf"]["B"].numberOfContours > 0
751*e1fe3e4aSElliott Hughes
752*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
753*e1fe3e4aSElliott Hughes        subset.main(
754*e1fe3e4aSElliott Hughes            [
755*e1fe3e4aSElliott Hughes                fontpath,
756*e1fe3e4aSElliott Hughes                "--retain-gids",
757*e1fe3e4aSElliott Hughes                "--output-file=%s" % subsetpath,
758*e1fe3e4aSElliott Hughes                "--glyph-names",
759*e1fe3e4aSElliott Hughes                "B",
760*e1fe3e4aSElliott Hughes            ]
761*e1fe3e4aSElliott Hughes        )
762*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
763*e1fe3e4aSElliott Hughes
764*e1fe3e4aSElliott Hughes        assert subsetfont.getGlyphOrder() == font.getGlyphOrder()[0:3]
765*e1fe3e4aSElliott Hughes
766*e1fe3e4aSElliott Hughes        hmtx = subsetfont["hmtx"]
767*e1fe3e4aSElliott Hughes        assert hmtx["A"] == (0, 0)
768*e1fe3e4aSElliott Hughes        assert hmtx["B"] == (400, 132)
769*e1fe3e4aSElliott Hughes
770*e1fe3e4aSElliott Hughes        glyf = subsetfont["glyf"]
771*e1fe3e4aSElliott Hughes        assert glyf["A"].numberOfContours == 0
772*e1fe3e4aSElliott Hughes        assert glyf["B"].numberOfContours > 0
773*e1fe3e4aSElliott Hughes
774*e1fe3e4aSElliott Hughes    def test_retain_gids_cff(self):
775*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestOTF-Regular.ttx"), ".otf")
776*e1fe3e4aSElliott Hughes        font = TTFont(fontpath)
777*e1fe3e4aSElliott Hughes
778*e1fe3e4aSElliott Hughes        assert font["hmtx"]["A"] == (500, 132)
779*e1fe3e4aSElliott Hughes        assert font["hmtx"]["B"] == (400, 132)
780*e1fe3e4aSElliott Hughes        assert font["hmtx"]["C"] == (500, 0)
781*e1fe3e4aSElliott Hughes
782*e1fe3e4aSElliott Hughes        font["CFF "].cff[0].decompileAllCharStrings()
783*e1fe3e4aSElliott Hughes        cs = font["CFF "].cff[0].CharStrings
784*e1fe3e4aSElliott Hughes        assert len(cs["A"].program) > 0
785*e1fe3e4aSElliott Hughes        assert len(cs["B"].program) > 0
786*e1fe3e4aSElliott Hughes        assert len(cs["C"].program) > 0
787*e1fe3e4aSElliott Hughes
788*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".otf")
789*e1fe3e4aSElliott Hughes        subset.main(
790*e1fe3e4aSElliott Hughes            [
791*e1fe3e4aSElliott Hughes                fontpath,
792*e1fe3e4aSElliott Hughes                "--retain-gids",
793*e1fe3e4aSElliott Hughes                "--output-file=%s" % subsetpath,
794*e1fe3e4aSElliott Hughes                "--glyph-names",
795*e1fe3e4aSElliott Hughes                "B",
796*e1fe3e4aSElliott Hughes            ]
797*e1fe3e4aSElliott Hughes        )
798*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
799*e1fe3e4aSElliott Hughes
800*e1fe3e4aSElliott Hughes        assert subsetfont.getGlyphOrder() == font.getGlyphOrder()[0:3]
801*e1fe3e4aSElliott Hughes
802*e1fe3e4aSElliott Hughes        hmtx = subsetfont["hmtx"]
803*e1fe3e4aSElliott Hughes        assert hmtx["A"] == (0, 0)
804*e1fe3e4aSElliott Hughes        assert hmtx["B"] == (400, 132)
805*e1fe3e4aSElliott Hughes
806*e1fe3e4aSElliott Hughes        subsetfont["CFF "].cff[0].decompileAllCharStrings()
807*e1fe3e4aSElliott Hughes        cs = subsetfont["CFF "].cff[0].CharStrings
808*e1fe3e4aSElliott Hughes
809*e1fe3e4aSElliott Hughes        assert cs["A"].program == ["endchar"]
810*e1fe3e4aSElliott Hughes        assert len(cs["B"].program) > 0
811*e1fe3e4aSElliott Hughes
812*e1fe3e4aSElliott Hughes    def test_retain_gids_cff2(self):
813*e1fe3e4aSElliott Hughes        ttx_path = self.getpath(
814*e1fe3e4aSElliott Hughes            "../../varLib/data/master_ttx_varfont_otf/TestCFF2VF.ttx"
815*e1fe3e4aSElliott Hughes        )
816*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(ttx_path, ".otf")
817*e1fe3e4aSElliott Hughes        font = TTFont(fontpath)
818*e1fe3e4aSElliott Hughes
819*e1fe3e4aSElliott Hughes        assert font["hmtx"]["A"] == (600, 31)
820*e1fe3e4aSElliott Hughes        assert font["hmtx"]["T"] == (600, 41)
821*e1fe3e4aSElliott Hughes
822*e1fe3e4aSElliott Hughes        font["CFF2"].cff[0].decompileAllCharStrings()
823*e1fe3e4aSElliott Hughes        cs = font["CFF2"].cff[0].CharStrings
824*e1fe3e4aSElliott Hughes        assert len(cs["A"].program) > 0
825*e1fe3e4aSElliott Hughes        assert len(cs["T"].program) > 0
826*e1fe3e4aSElliott Hughes
827*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".otf")
828*e1fe3e4aSElliott Hughes        subset.main(
829*e1fe3e4aSElliott Hughes            [
830*e1fe3e4aSElliott Hughes                fontpath,
831*e1fe3e4aSElliott Hughes                "--retain-gids",
832*e1fe3e4aSElliott Hughes                "--output-file=%s" % subsetpath,
833*e1fe3e4aSElliott Hughes                "T",
834*e1fe3e4aSElliott Hughes            ]
835*e1fe3e4aSElliott Hughes        )
836*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
837*e1fe3e4aSElliott Hughes
838*e1fe3e4aSElliott Hughes        assert len(subsetfont.getGlyphOrder()) == len(font.getGlyphOrder()[0:3])
839*e1fe3e4aSElliott Hughes
840*e1fe3e4aSElliott Hughes        hmtx = subsetfont["hmtx"]
841*e1fe3e4aSElliott Hughes        assert hmtx["glyph00001"] == (0, 0)
842*e1fe3e4aSElliott Hughes        assert hmtx["T"] == (600, 41)
843*e1fe3e4aSElliott Hughes
844*e1fe3e4aSElliott Hughes        subsetfont["CFF2"].cff[0].decompileAllCharStrings()
845*e1fe3e4aSElliott Hughes        cs = subsetfont["CFF2"].cff[0].CharStrings
846*e1fe3e4aSElliott Hughes        assert cs["glyph00001"].program == []
847*e1fe3e4aSElliott Hughes        assert len(cs["T"].program) > 0
848*e1fe3e4aSElliott Hughes
849*e1fe3e4aSElliott Hughes    def test_HVAR_VVAR(self):
850*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestHVVAR.ttx"), ".ttf")
851*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
852*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--text=BD", "--output-file=%s" % subsetpath])
853*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
854*e1fe3e4aSElliott Hughes        self.expect_ttx(
855*e1fe3e4aSElliott Hughes            subsetfont,
856*e1fe3e4aSElliott Hughes            self.getpath("expect_HVVAR.ttx"),
857*e1fe3e4aSElliott Hughes            ["GlyphOrder", "HVAR", "VVAR", "avar", "fvar"],
858*e1fe3e4aSElliott Hughes        )
859*e1fe3e4aSElliott Hughes
860*e1fe3e4aSElliott Hughes    def test_HVAR_VVAR_retain_gids(self):
861*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestHVVAR.ttx"), ".ttf")
862*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
863*e1fe3e4aSElliott Hughes        subset.main(
864*e1fe3e4aSElliott Hughes            [fontpath, "--text=BD", "--retain-gids", "--output-file=%s" % subsetpath]
865*e1fe3e4aSElliott Hughes        )
866*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
867*e1fe3e4aSElliott Hughes        self.expect_ttx(
868*e1fe3e4aSElliott Hughes            subsetfont,
869*e1fe3e4aSElliott Hughes            self.getpath("expect_HVVAR_retain_gids.ttx"),
870*e1fe3e4aSElliott Hughes            ["GlyphOrder", "HVAR", "VVAR", "avar", "fvar"],
871*e1fe3e4aSElliott Hughes        )
872*e1fe3e4aSElliott Hughes
873*e1fe3e4aSElliott Hughes    def test_subset_flavor_woff(self):
874*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf")
875*e1fe3e4aSElliott Hughes        woff_path = self.temp_path(".woff")
876*e1fe3e4aSElliott Hughes
877*e1fe3e4aSElliott Hughes        subset.main(
878*e1fe3e4aSElliott Hughes            [
879*e1fe3e4aSElliott Hughes                fontpath,
880*e1fe3e4aSElliott Hughes                "*",
881*e1fe3e4aSElliott Hughes                "--flavor=woff",
882*e1fe3e4aSElliott Hughes                "--output-file=%s" % woff_path,
883*e1fe3e4aSElliott Hughes            ]
884*e1fe3e4aSElliott Hughes        )
885*e1fe3e4aSElliott Hughes        woff = TTFont(woff_path)
886*e1fe3e4aSElliott Hughes
887*e1fe3e4aSElliott Hughes        assert woff.flavor == "woff"
888*e1fe3e4aSElliott Hughes
889*e1fe3e4aSElliott Hughes    def test_subset_flavor_woff2(self):
890*e1fe3e4aSElliott Hughes        # skip if brotli is not importable, required for woff2
891*e1fe3e4aSElliott Hughes        pytest.importorskip("brotli")
892*e1fe3e4aSElliott Hughes
893*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf")
894*e1fe3e4aSElliott Hughes        woff2_path = self.temp_path(".woff2")
895*e1fe3e4aSElliott Hughes
896*e1fe3e4aSElliott Hughes        subset.main(
897*e1fe3e4aSElliott Hughes            [
898*e1fe3e4aSElliott Hughes                fontpath,
899*e1fe3e4aSElliott Hughes                "*",
900*e1fe3e4aSElliott Hughes                "--flavor=woff2",
901*e1fe3e4aSElliott Hughes                "--output-file=%s" % woff2_path,
902*e1fe3e4aSElliott Hughes            ]
903*e1fe3e4aSElliott Hughes        )
904*e1fe3e4aSElliott Hughes        woff2 = TTFont(woff2_path)
905*e1fe3e4aSElliott Hughes
906*e1fe3e4aSElliott Hughes        assert woff2.flavor == "woff2"
907*e1fe3e4aSElliott Hughes
908*e1fe3e4aSElliott Hughes    def test_subset_flavor_none(self):
909*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf")
910*e1fe3e4aSElliott Hughes        ttf_path = self.temp_path(".ttf")
911*e1fe3e4aSElliott Hughes
912*e1fe3e4aSElliott Hughes        subset.main(
913*e1fe3e4aSElliott Hughes            [
914*e1fe3e4aSElliott Hughes                fontpath,
915*e1fe3e4aSElliott Hughes                "*",
916*e1fe3e4aSElliott Hughes                "--output-file=%s" % ttf_path,
917*e1fe3e4aSElliott Hughes            ]
918*e1fe3e4aSElliott Hughes        )
919*e1fe3e4aSElliott Hughes        ttf = TTFont(ttf_path)
920*e1fe3e4aSElliott Hughes
921*e1fe3e4aSElliott Hughes        assert ttf.flavor is None
922*e1fe3e4aSElliott Hughes
923*e1fe3e4aSElliott Hughes    def test_subset_context_subst_format_3(self):
924*e1fe3e4aSElliott Hughes        # https://github.com/fonttools/fonttools/issues/1879
925*e1fe3e4aSElliott Hughes        # Test font contains 'calt' feature with Format 3 ContextSubst lookup subtables
926*e1fe3e4aSElliott Hughes        ttx = self.getpath("TestContextSubstFormat3.ttx")
927*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(ttx, ".ttf")
928*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
929*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--unicodes=*", "--output-file=%s" % subsetpath])
930*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
931*e1fe3e4aSElliott Hughes        # check all glyphs are kept via GSUB closure, no changes expected
932*e1fe3e4aSElliott Hughes        self.expect_ttx(subsetfont, ttx)
933*e1fe3e4aSElliott Hughes
934*e1fe3e4aSElliott Hughes    def test_cmap_prune_format12(self):
935*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("CmapSubsetTest.ttx"), ".ttf")
936*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
937*e1fe3e4aSElliott Hughes        subset.main([fontpath, "--glyphs=a", "--output-file=%s" % subsetpath])
938*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
939*e1fe3e4aSElliott Hughes        self.expect_ttx(subsetfont, self.getpath("CmapSubsetTest.subset.ttx"), ["cmap"])
940*e1fe3e4aSElliott Hughes
941*e1fe3e4aSElliott Hughes    @pytest.mark.parametrize("text, n", [("!", 1), ("#", 2)])
942*e1fe3e4aSElliott Hughes    def test_GPOS_PairPos_Format2_useClass0(self, text, n):
943*e1fe3e4aSElliott Hughes        # Check two things related to class 0 ('every other glyph'):
944*e1fe3e4aSElliott Hughes        # 1) that it's reused for ClassDef1 when it becomes empty as the subset glyphset
945*e1fe3e4aSElliott Hughes        #    is intersected with the table's Coverage
946*e1fe3e4aSElliott Hughes        # 2) that it is never reused for ClassDef2 even when it happens to become empty
947*e1fe3e4aSElliott Hughes        #    because of the subset glyphset. In this case, we don't keep a PairPosClass2
948*e1fe3e4aSElliott Hughes        #    subtable if only ClassDef2's class0 survived subsetting.
949*e1fe3e4aSElliott Hughes        # The test font (from Harfbuzz test suite) is constructed to trigger these two
950*e1fe3e4aSElliott Hughes        # situations depending on the input subset --text.
951*e1fe3e4aSElliott Hughes        # https://github.com/fonttools/fonttools/pull/2221
952*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(
953*e1fe3e4aSElliott Hughes            self.getpath("GPOS_PairPos_Format2_PR_2221.ttx"), ".ttf"
954*e1fe3e4aSElliott Hughes        )
955*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
956*e1fe3e4aSElliott Hughes
957*e1fe3e4aSElliott Hughes        expected_ttx = self.getpath(
958*e1fe3e4aSElliott Hughes            f"GPOS_PairPos_Format2_ClassDef{n}_useClass0.subset.ttx"
959*e1fe3e4aSElliott Hughes        )
960*e1fe3e4aSElliott Hughes        subset.main(
961*e1fe3e4aSElliott Hughes            [
962*e1fe3e4aSElliott Hughes                fontpath,
963*e1fe3e4aSElliott Hughes                f"--text='{text}'",
964*e1fe3e4aSElliott Hughes                "--layout-features+=test",
965*e1fe3e4aSElliott Hughes                "--output-file=%s" % subsetpath,
966*e1fe3e4aSElliott Hughes            ]
967*e1fe3e4aSElliott Hughes        )
968*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
969*e1fe3e4aSElliott Hughes        self.expect_ttx(subsetfont, expected_ttx, ["GPOS"])
970*e1fe3e4aSElliott Hughes
971*e1fe3e4aSElliott Hughes    def test_GPOS_SinglePos_prune_post_subset_no_value(self):
972*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(
973*e1fe3e4aSElliott Hughes            self.getpath("GPOS_SinglePos_no_value_issue_2312.ttx"), ".ttf"
974*e1fe3e4aSElliott Hughes        )
975*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".ttf")
976*e1fe3e4aSElliott Hughes        subset.main([fontpath, "*", "--glyph-names", "--output-file=%s" % subsetpath])
977*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
978*e1fe3e4aSElliott Hughes        self.expect_ttx(
979*e1fe3e4aSElliott Hughes            subsetfont,
980*e1fe3e4aSElliott Hughes            self.getpath("GPOS_SinglePos_no_value_issue_2312.subset.ttx"),
981*e1fe3e4aSElliott Hughes            ["GlyphOrder", "GPOS"],
982*e1fe3e4aSElliott Hughes        )
983*e1fe3e4aSElliott Hughes
984*e1fe3e4aSElliott Hughes    @pytest.mark.parametrize(
985*e1fe3e4aSElliott Hughes        "installed, enabled, ok",
986*e1fe3e4aSElliott Hughes        [
987*e1fe3e4aSElliott Hughes            pytest.param(True, None, True, id="installed-auto-ok"),
988*e1fe3e4aSElliott Hughes            pytest.param(True, None, False, id="installed-auto-fail"),
989*e1fe3e4aSElliott Hughes            pytest.param(True, True, True, id="installed-enabled-ok"),
990*e1fe3e4aSElliott Hughes            pytest.param(True, True, False, id="installed-enabled-fail"),
991*e1fe3e4aSElliott Hughes            pytest.param(True, False, True, id="installed-disabled"),
992*e1fe3e4aSElliott Hughes            pytest.param(False, True, True, id="not_installed-enabled"),
993*e1fe3e4aSElliott Hughes            pytest.param(False, False, True, id="not_installed-disabled"),
994*e1fe3e4aSElliott Hughes        ],
995*e1fe3e4aSElliott Hughes    )
996*e1fe3e4aSElliott Hughes    def test_harfbuzz_repacker(self, caplog, monkeypatch, installed, enabled, ok):
997*e1fe3e4aSElliott Hughes        # Use a mock to test the pure-python serializer is used when uharfbuzz
998*e1fe3e4aSElliott Hughes        # returns an error or is not installed
999*e1fe3e4aSElliott Hughes        have_uharfbuzz = fontTools.ttLib.tables.otBase.have_uharfbuzz
1000*e1fe3e4aSElliott Hughes        if installed:
1001*e1fe3e4aSElliott Hughes            if not have_uharfbuzz:
1002*e1fe3e4aSElliott Hughes                pytest.skip("uharfbuzz is not installed")
1003*e1fe3e4aSElliott Hughes            if not ok:
1004*e1fe3e4aSElliott Hughes                # pretend hb.repack/repack_with_tag return an error
1005*e1fe3e4aSElliott Hughes                import uharfbuzz as hb
1006*e1fe3e4aSElliott Hughes
1007*e1fe3e4aSElliott Hughes                def mock_repack(data, obj_list):
1008*e1fe3e4aSElliott Hughes                    raise hb.RepackerError("mocking")
1009*e1fe3e4aSElliott Hughes
1010*e1fe3e4aSElliott Hughes                monkeypatch.setattr(hb, "repack", mock_repack)
1011*e1fe3e4aSElliott Hughes
1012*e1fe3e4aSElliott Hughes                if hasattr(hb, "repack_with_tag"):  # uharfbuzz >= 0.30.0
1013*e1fe3e4aSElliott Hughes
1014*e1fe3e4aSElliott Hughes                    def mock_repack_with_tag(tag, data, obj_list):
1015*e1fe3e4aSElliott Hughes                        raise hb.RepackerError("mocking")
1016*e1fe3e4aSElliott Hughes
1017*e1fe3e4aSElliott Hughes                    monkeypatch.setattr(hb, "repack_with_tag", mock_repack_with_tag)
1018*e1fe3e4aSElliott Hughes        else:
1019*e1fe3e4aSElliott Hughes            if have_uharfbuzz:
1020*e1fe3e4aSElliott Hughes                # pretend uharfbuzz is not installed
1021*e1fe3e4aSElliott Hughes                monkeypatch.setattr(
1022*e1fe3e4aSElliott Hughes                    fontTools.ttLib.tables.otBase, "have_uharfbuzz", False
1023*e1fe3e4aSElliott Hughes                )
1024*e1fe3e4aSElliott Hughes
1025*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(self.getpath("harfbuzz_repacker.ttx"), ".otf")
1026*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".otf")
1027*e1fe3e4aSElliott Hughes        args = [
1028*e1fe3e4aSElliott Hughes            fontpath,
1029*e1fe3e4aSElliott Hughes            "--unicodes=0x53a9",
1030*e1fe3e4aSElliott Hughes            "--layout-features=*",
1031*e1fe3e4aSElliott Hughes            f"--output-file={subsetpath}",
1032*e1fe3e4aSElliott Hughes        ]
1033*e1fe3e4aSElliott Hughes        if enabled is True:
1034*e1fe3e4aSElliott Hughes            args.append("--harfbuzz-repacker")
1035*e1fe3e4aSElliott Hughes        elif enabled is False:
1036*e1fe3e4aSElliott Hughes            args.append("--no-harfbuzz-repacker")
1037*e1fe3e4aSElliott Hughes        # elif enabled is None: ... is the default
1038*e1fe3e4aSElliott Hughes
1039*e1fe3e4aSElliott Hughes        if enabled is True and not installed:
1040*e1fe3e4aSElliott Hughes            # raise if enabled but not installed
1041*e1fe3e4aSElliott Hughes            with pytest.raises(ImportError, match="uharfbuzz"):
1042*e1fe3e4aSElliott Hughes                subset.main(args)
1043*e1fe3e4aSElliott Hughes            return
1044*e1fe3e4aSElliott Hughes
1045*e1fe3e4aSElliott Hughes        with caplog.at_level(logging.DEBUG, "fontTools.ttLib.tables.otBase"):
1046*e1fe3e4aSElliott Hughes            subset.main(args)
1047*e1fe3e4aSElliott Hughes
1048*e1fe3e4aSElliott Hughes        subsetfont = TTFont(subsetpath)
1049*e1fe3e4aSElliott Hughes        # both hb.repack and pure-python serializer compile to the same ttx
1050*e1fe3e4aSElliott Hughes        self.expect_ttx(
1051*e1fe3e4aSElliott Hughes            subsetfont, self.getpath("expect_harfbuzz_repacker.ttx"), ["GSUB"]
1052*e1fe3e4aSElliott Hughes        )
1053*e1fe3e4aSElliott Hughes
1054*e1fe3e4aSElliott Hughes        if enabled or enabled is None:
1055*e1fe3e4aSElliott Hughes            if installed:
1056*e1fe3e4aSElliott Hughes                assert "serializing 'GSUB' with hb.repack" in caplog.text
1057*e1fe3e4aSElliott Hughes
1058*e1fe3e4aSElliott Hughes        if enabled is None and not installed:
1059*e1fe3e4aSElliott Hughes            assert (
1060*e1fe3e4aSElliott Hughes                "uharfbuzz not found, compiling 'GSUB' with pure-python serializer"
1061*e1fe3e4aSElliott Hughes            ) in caplog.text
1062*e1fe3e4aSElliott Hughes
1063*e1fe3e4aSElliott Hughes        if enabled is False:
1064*e1fe3e4aSElliott Hughes            assert (
1065*e1fe3e4aSElliott Hughes                "hb.repack disabled, compiling 'GSUB' with pure-python serializer"
1066*e1fe3e4aSElliott Hughes            ) in caplog.text
1067*e1fe3e4aSElliott Hughes
1068*e1fe3e4aSElliott Hughes        # test we emit a log.error if hb.repack fails (and we don't if successful)
1069*e1fe3e4aSElliott Hughes        assert (
1070*e1fe3e4aSElliott Hughes            (
1071*e1fe3e4aSElliott Hughes                "hb.repack failed to serialize 'GSUB', attempting fonttools resolutions "
1072*e1fe3e4aSElliott Hughes                "; the error message was: RepackerError: mocking"
1073*e1fe3e4aSElliott Hughes            )
1074*e1fe3e4aSElliott Hughes            in caplog.text
1075*e1fe3e4aSElliott Hughes        ) ^ ok
1076*e1fe3e4aSElliott Hughes
1077*e1fe3e4aSElliott Hughes    def test_retain_east_asian_spacing_features(self):
1078*e1fe3e4aSElliott Hughes        # This test font contains halt and vhal features, check that
1079*e1fe3e4aSElliott Hughes        # they are retained by default after subsetting.
1080*e1fe3e4aSElliott Hughes        ttx_path = self.getpath("NotoSansCJKjp-Regular.subset.ttx")
1081*e1fe3e4aSElliott Hughes        ttx = pathlib.Path(ttx_path).read_text()
1082*e1fe3e4aSElliott Hughes        assert 'FeatureTag value="halt"' in ttx
1083*e1fe3e4aSElliott Hughes        assert 'FeatureTag value="vhal"' in ttx
1084*e1fe3e4aSElliott Hughes
1085*e1fe3e4aSElliott Hughes        fontpath = self.compile_font(ttx_path, ".otf")
1086*e1fe3e4aSElliott Hughes        subsetpath = self.temp_path(".otf")
1087*e1fe3e4aSElliott Hughes        subset.main(
1088*e1fe3e4aSElliott Hughes            [
1089*e1fe3e4aSElliott Hughes                fontpath,
1090*e1fe3e4aSElliott Hughes                "--unicodes=*",
1091*e1fe3e4aSElliott Hughes                "--output-file=%s" % subsetpath,
1092*e1fe3e4aSElliott Hughes            ]
1093*e1fe3e4aSElliott Hughes        )
1094*e1fe3e4aSElliott Hughes        # subset output is the same as the input
1095*e1fe3e4aSElliott Hughes        self.expect_ttx(TTFont(subsetpath), ttx_path)
1096*e1fe3e4aSElliott Hughes
1097*e1fe3e4aSElliott Hughes
1098*e1fe3e4aSElliott Hughes@pytest.fixture
1099*e1fe3e4aSElliott Hughesdef featureVarsTestFont():
1100*e1fe3e4aSElliott Hughes    fb = FontBuilder(unitsPerEm=100)
1101*e1fe3e4aSElliott Hughes    fb.setupGlyphOrder([".notdef", "f", "f_f", "dollar", "dollar.rvrn"])
1102*e1fe3e4aSElliott Hughes    fb.setupCharacterMap({ord("f"): "f", ord("$"): "dollar"})
1103*e1fe3e4aSElliott Hughes    fb.setupNameTable({"familyName": "TestFeatureVars", "styleName": "Regular"})
1104*e1fe3e4aSElliott Hughes    fb.setupPost()
1105*e1fe3e4aSElliott Hughes    fb.setupFvar(axes=[("wght", 100, 400, 900, "Weight")], instances=[])
1106*e1fe3e4aSElliott Hughes    fb.addOpenTypeFeatures(
1107*e1fe3e4aSElliott Hughes        """\
1108*e1fe3e4aSElliott Hughes        feature dlig {
1109*e1fe3e4aSElliott Hughes            sub f f by f_f;
1110*e1fe3e4aSElliott Hughes        } dlig;
1111*e1fe3e4aSElliott Hughes    """
1112*e1fe3e4aSElliott Hughes    )
1113*e1fe3e4aSElliott Hughes    fb.addFeatureVariations(
1114*e1fe3e4aSElliott Hughes        [([{"wght": (0.20886, 1.0)}], {"dollar": "dollar.rvrn"})], featureTag="rvrn"
1115*e1fe3e4aSElliott Hughes    )
1116*e1fe3e4aSElliott Hughes    buf = io.BytesIO()
1117*e1fe3e4aSElliott Hughes    fb.save(buf)
1118*e1fe3e4aSElliott Hughes    buf.seek(0)
1119*e1fe3e4aSElliott Hughes
1120*e1fe3e4aSElliott Hughes    return TTFont(buf)
1121*e1fe3e4aSElliott Hughes
1122*e1fe3e4aSElliott Hughes
1123*e1fe3e4aSElliott Hughesdef test_subset_feature_variations_keep_all(featureVarsTestFont):
1124*e1fe3e4aSElliott Hughes    font = featureVarsTestFont
1125*e1fe3e4aSElliott Hughes
1126*e1fe3e4aSElliott Hughes    options = subset.Options()
1127*e1fe3e4aSElliott Hughes    subsetter = subset.Subsetter(options)
1128*e1fe3e4aSElliott Hughes    subsetter.populate(unicodes=[ord("f"), ord("$")])
1129*e1fe3e4aSElliott Hughes    subsetter.subset(font)
1130*e1fe3e4aSElliott Hughes
1131*e1fe3e4aSElliott Hughes    featureTags = {r.FeatureTag for r in font["GSUB"].table.FeatureList.FeatureRecord}
1132*e1fe3e4aSElliott Hughes    # 'dlig' is discretionary so it is dropped by default
1133*e1fe3e4aSElliott Hughes    assert "dlig" not in featureTags
1134*e1fe3e4aSElliott Hughes    assert "f_f" not in font.getGlyphOrder()
1135*e1fe3e4aSElliott Hughes    # 'rvrn' is required so it is kept by default
1136*e1fe3e4aSElliott Hughes    assert "rvrn" in featureTags
1137*e1fe3e4aSElliott Hughes    assert "dollar.rvrn" in font.getGlyphOrder()
1138*e1fe3e4aSElliott Hughes
1139*e1fe3e4aSElliott Hughes
1140*e1fe3e4aSElliott Hughesdef test_subset_feature_variations_drop_all(featureVarsTestFont):
1141*e1fe3e4aSElliott Hughes    font = featureVarsTestFont
1142*e1fe3e4aSElliott Hughes
1143*e1fe3e4aSElliott Hughes    options = subset.Options()
1144*e1fe3e4aSElliott Hughes    options.layout_features.remove("rvrn")  # drop 'rvrn'
1145*e1fe3e4aSElliott Hughes    subsetter = subset.Subsetter(options)
1146*e1fe3e4aSElliott Hughes    subsetter.populate(unicodes=[ord("f"), ord("$")])
1147*e1fe3e4aSElliott Hughes    subsetter.subset(font)
1148*e1fe3e4aSElliott Hughes
1149*e1fe3e4aSElliott Hughes    featureTags = {r.FeatureTag for r in font["GSUB"].table.FeatureList.FeatureRecord}
1150*e1fe3e4aSElliott Hughes    glyphs = set(font.getGlyphOrder())
1151*e1fe3e4aSElliott Hughes
1152*e1fe3e4aSElliott Hughes    assert "rvrn" not in featureTags
1153*e1fe3e4aSElliott Hughes    assert glyphs == {".notdef", "f", "dollar"}
1154*e1fe3e4aSElliott Hughes    # all FeatureVariationRecords were dropped
1155*e1fe3e4aSElliott Hughes    assert font["GSUB"].table.FeatureVariations is None
1156*e1fe3e4aSElliott Hughes    assert font["GSUB"].table.Version == 0x00010000
1157*e1fe3e4aSElliott Hughes
1158*e1fe3e4aSElliott Hughes
1159*e1fe3e4aSElliott Hughes# TODO test_subset_feature_variations_drop_from_end_empty_records
1160*e1fe3e4aSElliott Hughes# https://github.com/fonttools/fonttools/issues/1881#issuecomment-619415044
1161*e1fe3e4aSElliott Hughes
1162*e1fe3e4aSElliott Hughes
1163*e1fe3e4aSElliott Hughes@pytest.fixture
1164*e1fe3e4aSElliott Hughesdef singlepos2_font():
1165*e1fe3e4aSElliott Hughes    fb = FontBuilder(unitsPerEm=1000)
1166*e1fe3e4aSElliott Hughes    fb.setupGlyphOrder([".notdef", "a", "b", "c"])
1167*e1fe3e4aSElliott Hughes    fb.setupCharacterMap({ord("a"): "a", ord("b"): "b", ord("c"): "c"})
1168*e1fe3e4aSElliott Hughes    fb.setupNameTable({"familyName": "TestSingePosFormat", "styleName": "Regular"})
1169*e1fe3e4aSElliott Hughes    fb.setupPost()
1170*e1fe3e4aSElliott Hughes    fb.addOpenTypeFeatures(
1171*e1fe3e4aSElliott Hughes        """
1172*e1fe3e4aSElliott Hughes        feature kern {
1173*e1fe3e4aSElliott Hughes            pos a -50;
1174*e1fe3e4aSElliott Hughes            pos b -40;
1175*e1fe3e4aSElliott Hughes            pos c -50;
1176*e1fe3e4aSElliott Hughes        } kern;
1177*e1fe3e4aSElliott Hughes    """
1178*e1fe3e4aSElliott Hughes    )
1179*e1fe3e4aSElliott Hughes
1180*e1fe3e4aSElliott Hughes    buf = io.BytesIO()
1181*e1fe3e4aSElliott Hughes    fb.save(buf)
1182*e1fe3e4aSElliott Hughes    buf.seek(0)
1183*e1fe3e4aSElliott Hughes
1184*e1fe3e4aSElliott Hughes    return TTFont(buf)
1185*e1fe3e4aSElliott Hughes
1186*e1fe3e4aSElliott Hughes
1187*e1fe3e4aSElliott Hughesdef test_subset_single_pos_format(singlepos2_font):
1188*e1fe3e4aSElliott Hughes    font = singlepos2_font
1189*e1fe3e4aSElliott Hughes    # The input font has a SinglePos Format 2 subtable where each glyph has
1190*e1fe3e4aSElliott Hughes    # different ValueRecords
1191*e1fe3e4aSElliott Hughes    assert getXML(font["GPOS"].table.LookupList.Lookup[0].toXML, font) == [
1192*e1fe3e4aSElliott Hughes        "<Lookup>",
1193*e1fe3e4aSElliott Hughes        '  <LookupType value="1"/>',
1194*e1fe3e4aSElliott Hughes        '  <LookupFlag value="0"/>',
1195*e1fe3e4aSElliott Hughes        "  <!-- SubTableCount=1 -->",
1196*e1fe3e4aSElliott Hughes        '  <SinglePos index="0" Format="2">',
1197*e1fe3e4aSElliott Hughes        "    <Coverage>",
1198*e1fe3e4aSElliott Hughes        '      <Glyph value="a"/>',
1199*e1fe3e4aSElliott Hughes        '      <Glyph value="b"/>',
1200*e1fe3e4aSElliott Hughes        '      <Glyph value="c"/>',
1201*e1fe3e4aSElliott Hughes        "    </Coverage>",
1202*e1fe3e4aSElliott Hughes        '    <ValueFormat value="4"/>',
1203*e1fe3e4aSElliott Hughes        "    <!-- ValueCount=3 -->",
1204*e1fe3e4aSElliott Hughes        '    <Value index="0" XAdvance="-50"/>',
1205*e1fe3e4aSElliott Hughes        '    <Value index="1" XAdvance="-40"/>',
1206*e1fe3e4aSElliott Hughes        '    <Value index="2" XAdvance="-50"/>',
1207*e1fe3e4aSElliott Hughes        "  </SinglePos>",
1208*e1fe3e4aSElliott Hughes        "</Lookup>",
1209*e1fe3e4aSElliott Hughes    ]
1210*e1fe3e4aSElliott Hughes
1211*e1fe3e4aSElliott Hughes    options = subset.Options()
1212*e1fe3e4aSElliott Hughes    subsetter = subset.Subsetter(options)
1213*e1fe3e4aSElliott Hughes    subsetter.populate(unicodes=[ord("a"), ord("c")])
1214*e1fe3e4aSElliott Hughes    subsetter.subset(font)
1215*e1fe3e4aSElliott Hughes
1216*e1fe3e4aSElliott Hughes    # All the subsetted glyphs from the original SinglePos Format2 subtable
1217*e1fe3e4aSElliott Hughes    # now have the same ValueRecord, so we use a more compact Format 1 subtable.
1218*e1fe3e4aSElliott Hughes    assert getXML(font["GPOS"].table.LookupList.Lookup[0].toXML, font) == [
1219*e1fe3e4aSElliott Hughes        "<Lookup>",
1220*e1fe3e4aSElliott Hughes        '  <LookupType value="1"/>',
1221*e1fe3e4aSElliott Hughes        '  <LookupFlag value="0"/>',
1222*e1fe3e4aSElliott Hughes        "  <!-- SubTableCount=1 -->",
1223*e1fe3e4aSElliott Hughes        '  <SinglePos index="0" Format="1">',
1224*e1fe3e4aSElliott Hughes        "    <Coverage>",
1225*e1fe3e4aSElliott Hughes        '      <Glyph value="a"/>',
1226*e1fe3e4aSElliott Hughes        '      <Glyph value="c"/>',
1227*e1fe3e4aSElliott Hughes        "    </Coverage>",
1228*e1fe3e4aSElliott Hughes        '    <ValueFormat value="4"/>',
1229*e1fe3e4aSElliott Hughes        '    <Value XAdvance="-50"/>',
1230*e1fe3e4aSElliott Hughes        "  </SinglePos>",
1231*e1fe3e4aSElliott Hughes        "</Lookup>",
1232*e1fe3e4aSElliott Hughes    ]
1233*e1fe3e4aSElliott Hughes
1234*e1fe3e4aSElliott Hughes
1235*e1fe3e4aSElliott Hughesdef test_subset_single_pos_format2_all_None(singlepos2_font):
1236*e1fe3e4aSElliott Hughes    # https://github.com/fonttools/fonttools/issues/2602
1237*e1fe3e4aSElliott Hughes    font = singlepos2_font
1238*e1fe3e4aSElliott Hughes    gpos = font["GPOS"].table
1239*e1fe3e4aSElliott Hughes    subtable = gpos.LookupList.Lookup[0].SubTable[0]
1240*e1fe3e4aSElliott Hughes    assert subtable.Format == 2
1241*e1fe3e4aSElliott Hughes    # Hack a SinglePosFormat2 with ValueFormat = 0; our own buildSinglePos
1242*e1fe3e4aSElliott Hughes    # never makes these as a SinglePosFormat1 is more compact, but they can
1243*e1fe3e4aSElliott Hughes    # be found in the wild.
1244*e1fe3e4aSElliott Hughes    subtable.Value = [None] * subtable.ValueCount
1245*e1fe3e4aSElliott Hughes    subtable.ValueFormat = 0
1246*e1fe3e4aSElliott Hughes
1247*e1fe3e4aSElliott Hughes    assert getXML(subtable.toXML, font) == [
1248*e1fe3e4aSElliott Hughes        '<SinglePos Format="2">',
1249*e1fe3e4aSElliott Hughes        "  <Coverage>",
1250*e1fe3e4aSElliott Hughes        '    <Glyph value="a"/>',
1251*e1fe3e4aSElliott Hughes        '    <Glyph value="b"/>',
1252*e1fe3e4aSElliott Hughes        '    <Glyph value="c"/>',
1253*e1fe3e4aSElliott Hughes        "  </Coverage>",
1254*e1fe3e4aSElliott Hughes        '  <ValueFormat value="0"/>',
1255*e1fe3e4aSElliott Hughes        "  <!-- ValueCount=3 -->",
1256*e1fe3e4aSElliott Hughes        "</SinglePos>",
1257*e1fe3e4aSElliott Hughes    ]
1258*e1fe3e4aSElliott Hughes
1259*e1fe3e4aSElliott Hughes    options = subset.Options()
1260*e1fe3e4aSElliott Hughes    subsetter = subset.Subsetter(options)
1261*e1fe3e4aSElliott Hughes    subsetter.populate(unicodes=[ord("a"), ord("c")])
1262*e1fe3e4aSElliott Hughes    subsetter.subset(font)
1263*e1fe3e4aSElliott Hughes
1264*e1fe3e4aSElliott Hughes    # Check it was downgraded to Format1 after subsetting
1265*e1fe3e4aSElliott Hughes    assert getXML(font["GPOS"].table.LookupList.Lookup[0].SubTable[0].toXML, font) == [
1266*e1fe3e4aSElliott Hughes        '<SinglePos Format="1">',
1267*e1fe3e4aSElliott Hughes        "  <Coverage>",
1268*e1fe3e4aSElliott Hughes        '    <Glyph value="a"/>',
1269*e1fe3e4aSElliott Hughes        '    <Glyph value="c"/>',
1270*e1fe3e4aSElliott Hughes        "  </Coverage>",
1271*e1fe3e4aSElliott Hughes        '  <ValueFormat value="0"/>',
1272*e1fe3e4aSElliott Hughes        "</SinglePos>",
1273*e1fe3e4aSElliott Hughes    ]
1274*e1fe3e4aSElliott Hughes
1275*e1fe3e4aSElliott Hughes
1276*e1fe3e4aSElliott Hughes@pytest.fixture
1277*e1fe3e4aSElliott Hughesdef ttf_path(tmp_path):
1278*e1fe3e4aSElliott Hughes    # $(dirname $0)/../ttLib/data
1279*e1fe3e4aSElliott Hughes    ttLib_data = pathlib.Path(__file__).parent.parent / "ttLib" / "data"
1280*e1fe3e4aSElliott Hughes    font = TTFont()
1281*e1fe3e4aSElliott Hughes    font.importXML(ttLib_data / "TestTTF-Regular.ttx")
1282*e1fe3e4aSElliott Hughes    font_path = tmp_path / "TestTTF-Regular.ttf"
1283*e1fe3e4aSElliott Hughes    font.save(font_path)
1284*e1fe3e4aSElliott Hughes    return font_path
1285*e1fe3e4aSElliott Hughes
1286*e1fe3e4aSElliott Hughes
1287*e1fe3e4aSElliott Hughesdef test_subset_empty_glyf(tmp_path, ttf_path):
1288*e1fe3e4aSElliott Hughes    subset_path = tmp_path / (ttf_path.name + ".subset")
1289*e1fe3e4aSElliott Hughes    # only keep empty .notdef and space glyph, resulting in an empty glyf table
1290*e1fe3e4aSElliott Hughes    subset.main(
1291*e1fe3e4aSElliott Hughes        [
1292*e1fe3e4aSElliott Hughes            str(ttf_path),
1293*e1fe3e4aSElliott Hughes            "--no-notdef-outline",
1294*e1fe3e4aSElliott Hughes            "--glyph-names",
1295*e1fe3e4aSElliott Hughes            f"--output-file={subset_path}",
1296*e1fe3e4aSElliott Hughes            "--glyphs=.notdef space",
1297*e1fe3e4aSElliott Hughes        ]
1298*e1fe3e4aSElliott Hughes    )
1299*e1fe3e4aSElliott Hughes    subset_font = TTFont(subset_path)
1300*e1fe3e4aSElliott Hughes
1301*e1fe3e4aSElliott Hughes    assert subset_font.getGlyphOrder() == [".notdef", "space"]
1302*e1fe3e4aSElliott Hughes    assert subset_font.reader["glyf"] == b"\x00"
1303*e1fe3e4aSElliott Hughes
1304*e1fe3e4aSElliott Hughes    glyf = subset_font["glyf"]
1305*e1fe3e4aSElliott Hughes    assert all(glyf[g].numberOfContours == 0 for g in subset_font.getGlyphOrder())
1306*e1fe3e4aSElliott Hughes
1307*e1fe3e4aSElliott Hughes    loca = subset_font["loca"]
1308*e1fe3e4aSElliott Hughes    assert all(loc == 0 for loc in loca)
1309*e1fe3e4aSElliott Hughes
1310*e1fe3e4aSElliott Hughes
1311*e1fe3e4aSElliott Hughes@pytest.fixture
1312*e1fe3e4aSElliott Hughesdef colrv1_path(tmp_path):
1313*e1fe3e4aSElliott Hughes    base_glyph_names = ["uni%04X" % i for i in range(0xE000, 0xE000 + 10)]
1314*e1fe3e4aSElliott Hughes    layer_glyph_names = ["glyph%05d" % i for i in range(10, 20)]
1315*e1fe3e4aSElliott Hughes    glyph_order = [".notdef"] + base_glyph_names + layer_glyph_names
1316*e1fe3e4aSElliott Hughes
1317*e1fe3e4aSElliott Hughes    pen = TTGlyphPen(glyphSet=None)
1318*e1fe3e4aSElliott Hughes    pen.moveTo((0, 0))
1319*e1fe3e4aSElliott Hughes    pen.lineTo((0, 500))
1320*e1fe3e4aSElliott Hughes    pen.lineTo((500, 500))
1321*e1fe3e4aSElliott Hughes    pen.lineTo((500, 0))
1322*e1fe3e4aSElliott Hughes    pen.closePath()
1323*e1fe3e4aSElliott Hughes    glyph = pen.glyph()
1324*e1fe3e4aSElliott Hughes    glyphs = {g: glyph for g in glyph_order}
1325*e1fe3e4aSElliott Hughes
1326*e1fe3e4aSElliott Hughes    fb = FontBuilder(unitsPerEm=1024, isTTF=True)
1327*e1fe3e4aSElliott Hughes    fb.setupGlyphOrder(glyph_order)
1328*e1fe3e4aSElliott Hughes    fb.setupCharacterMap({int(name[3:], 16): name for name in base_glyph_names})
1329*e1fe3e4aSElliott Hughes    fb.setupGlyf(glyphs)
1330*e1fe3e4aSElliott Hughes    fb.setupHorizontalMetrics({g: (500, 0) for g in glyph_order})
1331*e1fe3e4aSElliott Hughes    fb.setupHorizontalHeader()
1332*e1fe3e4aSElliott Hughes    fb.setupOS2()
1333*e1fe3e4aSElliott Hughes    fb.setupPost()
1334*e1fe3e4aSElliott Hughes    fb.setupNameTable({"familyName": "TestCOLRv1", "styleName": "Regular"})
1335*e1fe3e4aSElliott Hughes
1336*e1fe3e4aSElliott Hughes    fb.setupCOLR(
1337*e1fe3e4aSElliott Hughes        {
1338*e1fe3e4aSElliott Hughes            "uniE000": (
1339*e1fe3e4aSElliott Hughes                ot.PaintFormat.PaintColrLayers,
1340*e1fe3e4aSElliott Hughes                [
1341*e1fe3e4aSElliott Hughes                    {
1342*e1fe3e4aSElliott Hughes                        "Format": ot.PaintFormat.PaintGlyph,
1343*e1fe3e4aSElliott Hughes                        "Paint": (ot.PaintFormat.PaintSolid, 0),
1344*e1fe3e4aSElliott Hughes                        "Glyph": "glyph00010",
1345*e1fe3e4aSElliott Hughes                    },
1346*e1fe3e4aSElliott Hughes                    {
1347*e1fe3e4aSElliott Hughes                        "Format": ot.PaintFormat.PaintGlyph,
1348*e1fe3e4aSElliott Hughes                        "Paint": (ot.PaintFormat.PaintSolid, 2, 0.3),
1349*e1fe3e4aSElliott Hughes                        "Glyph": "glyph00011",
1350*e1fe3e4aSElliott Hughes                    },
1351*e1fe3e4aSElliott Hughes                ],
1352*e1fe3e4aSElliott Hughes            ),
1353*e1fe3e4aSElliott Hughes            "uniE001": (
1354*e1fe3e4aSElliott Hughes                ot.PaintFormat.PaintColrLayers,
1355*e1fe3e4aSElliott Hughes                [
1356*e1fe3e4aSElliott Hughes                    {
1357*e1fe3e4aSElliott Hughes                        "Format": ot.PaintFormat.PaintTransform,
1358*e1fe3e4aSElliott Hughes                        "Paint": {
1359*e1fe3e4aSElliott Hughes                            "Format": ot.PaintFormat.PaintGlyph,
1360*e1fe3e4aSElliott Hughes                            "Paint": {
1361*e1fe3e4aSElliott Hughes                                "Format": ot.PaintFormat.PaintRadialGradient,
1362*e1fe3e4aSElliott Hughes                                "x0": 250,
1363*e1fe3e4aSElliott Hughes                                "y0": 250,
1364*e1fe3e4aSElliott Hughes                                "r0": 250,
1365*e1fe3e4aSElliott Hughes                                "x1": 200,
1366*e1fe3e4aSElliott Hughes                                "y1": 200,
1367*e1fe3e4aSElliott Hughes                                "r1": 0,
1368*e1fe3e4aSElliott Hughes                                "ColorLine": {
1369*e1fe3e4aSElliott Hughes                                    "ColorStop": [(0.0, 1), (1.0, 2)],
1370*e1fe3e4aSElliott Hughes                                    "Extend": "repeat",
1371*e1fe3e4aSElliott Hughes                                },
1372*e1fe3e4aSElliott Hughes                            },
1373*e1fe3e4aSElliott Hughes                            "Glyph": "glyph00012",
1374*e1fe3e4aSElliott Hughes                        },
1375*e1fe3e4aSElliott Hughes                        "Transform": (0.7071, 0.7071, -0.7071, 0.7071, 0, 0),
1376*e1fe3e4aSElliott Hughes                    },
1377*e1fe3e4aSElliott Hughes                    {
1378*e1fe3e4aSElliott Hughes                        "Format": ot.PaintFormat.PaintGlyph,
1379*e1fe3e4aSElliott Hughes                        "Paint": (ot.PaintFormat.PaintSolid, 1, 0.5),
1380*e1fe3e4aSElliott Hughes                        "Glyph": "glyph00013",
1381*e1fe3e4aSElliott Hughes                    },
1382*e1fe3e4aSElliott Hughes                ],
1383*e1fe3e4aSElliott Hughes            ),
1384*e1fe3e4aSElliott Hughes            "uniE002": (
1385*e1fe3e4aSElliott Hughes                ot.PaintFormat.PaintColrLayers,
1386*e1fe3e4aSElliott Hughes                [
1387*e1fe3e4aSElliott Hughes                    {
1388*e1fe3e4aSElliott Hughes                        "Format": ot.PaintFormat.PaintGlyph,
1389*e1fe3e4aSElliott Hughes                        "Paint": {
1390*e1fe3e4aSElliott Hughes                            "Format": ot.PaintFormat.PaintLinearGradient,
1391*e1fe3e4aSElliott Hughes                            "x0": 0,
1392*e1fe3e4aSElliott Hughes                            "y0": 0,
1393*e1fe3e4aSElliott Hughes                            "x1": 500,
1394*e1fe3e4aSElliott Hughes                            "y1": 500,
1395*e1fe3e4aSElliott Hughes                            "x2": -500,
1396*e1fe3e4aSElliott Hughes                            "y2": 500,
1397*e1fe3e4aSElliott Hughes                            "ColorLine": {"ColorStop": [(0.0, 1), (1.0, 2)]},
1398*e1fe3e4aSElliott Hughes                        },
1399*e1fe3e4aSElliott Hughes                        "Glyph": "glyph00014",
1400*e1fe3e4aSElliott Hughes                    },
1401*e1fe3e4aSElliott Hughes                    {
1402*e1fe3e4aSElliott Hughes                        "Format": ot.PaintFormat.PaintTransform,
1403*e1fe3e4aSElliott Hughes                        "Paint": {
1404*e1fe3e4aSElliott Hughes                            "Format": ot.PaintFormat.PaintGlyph,
1405*e1fe3e4aSElliott Hughes                            "Paint": (ot.PaintFormat.PaintSolid, 1),
1406*e1fe3e4aSElliott Hughes                            "Glyph": "glyph00015",
1407*e1fe3e4aSElliott Hughes                        },
1408*e1fe3e4aSElliott Hughes                        "Transform": (1, 0, 0, 1, 400, 400),
1409*e1fe3e4aSElliott Hughes                    },
1410*e1fe3e4aSElliott Hughes                ],
1411*e1fe3e4aSElliott Hughes            ),
1412*e1fe3e4aSElliott Hughes            "uniE003": {
1413*e1fe3e4aSElliott Hughes                "Format": ot.PaintFormat.PaintRotateAroundCenter,
1414*e1fe3e4aSElliott Hughes                "Paint": {
1415*e1fe3e4aSElliott Hughes                    "Format": ot.PaintFormat.PaintColrGlyph,
1416*e1fe3e4aSElliott Hughes                    "Glyph": "uniE001",
1417*e1fe3e4aSElliott Hughes                },
1418*e1fe3e4aSElliott Hughes                "angle": 45,
1419*e1fe3e4aSElliott Hughes                "centerX": 250,
1420*e1fe3e4aSElliott Hughes                "centerY": 250,
1421*e1fe3e4aSElliott Hughes            },
1422*e1fe3e4aSElliott Hughes            "uniE004": [
1423*e1fe3e4aSElliott Hughes                ("glyph00016", 1),
1424*e1fe3e4aSElliott Hughes                ("glyph00017", 0xFFFF),  # special palette index for foreground text
1425*e1fe3e4aSElliott Hughes                ("glyph00018", 2),
1426*e1fe3e4aSElliott Hughes            ],
1427*e1fe3e4aSElliott Hughes        },
1428*e1fe3e4aSElliott Hughes        clipBoxes={
1429*e1fe3e4aSElliott Hughes            "uniE000": (0, 0, 200, 300),
1430*e1fe3e4aSElliott Hughes            "uniE001": (0, 0, 500, 500),
1431*e1fe3e4aSElliott Hughes            "uniE002": (-50, -50, 400, 400),
1432*e1fe3e4aSElliott Hughes            "uniE003": (-50, -50, 400, 400),
1433*e1fe3e4aSElliott Hughes        },
1434*e1fe3e4aSElliott Hughes    )
1435*e1fe3e4aSElliott Hughes    fb.setupCPAL(
1436*e1fe3e4aSElliott Hughes        [
1437*e1fe3e4aSElliott Hughes            [
1438*e1fe3e4aSElliott Hughes                (1.0, 0.0, 0.0, 1.0),  # red
1439*e1fe3e4aSElliott Hughes                (0.0, 1.0, 0.0, 1.0),  # green
1440*e1fe3e4aSElliott Hughes                (0.0, 0.0, 1.0, 1.0),  # blue
1441*e1fe3e4aSElliott Hughes            ],
1442*e1fe3e4aSElliott Hughes        ],
1443*e1fe3e4aSElliott Hughes    )
1444*e1fe3e4aSElliott Hughes
1445*e1fe3e4aSElliott Hughes    output_path = tmp_path / "TestCOLRv1.ttf"
1446*e1fe3e4aSElliott Hughes    fb.save(output_path)
1447*e1fe3e4aSElliott Hughes
1448*e1fe3e4aSElliott Hughes    return output_path
1449*e1fe3e4aSElliott Hughes
1450*e1fe3e4aSElliott Hughes
1451*e1fe3e4aSElliott Hughes@pytest.fixture
1452*e1fe3e4aSElliott Hughesdef colrv1_cpalv1_path(colrv1_path):
1453*e1fe3e4aSElliott Hughes    # upgrade CPAL from v0 to v1 by adding labels
1454*e1fe3e4aSElliott Hughes    font = TTFont(colrv1_path)
1455*e1fe3e4aSElliott Hughes    fb = FontBuilder(font=font)
1456*e1fe3e4aSElliott Hughes    fb.setupCPAL(
1457*e1fe3e4aSElliott Hughes        [
1458*e1fe3e4aSElliott Hughes            [
1459*e1fe3e4aSElliott Hughes                (1.0, 0.0, 0.0, 1.0),  # red
1460*e1fe3e4aSElliott Hughes                (0.0, 1.0, 0.0, 1.0),  # green
1461*e1fe3e4aSElliott Hughes                (0.0, 0.0, 1.0, 1.0),  # blue
1462*e1fe3e4aSElliott Hughes            ],
1463*e1fe3e4aSElliott Hughes        ],
1464*e1fe3e4aSElliott Hughes        paletteLabels=["test palette"],
1465*e1fe3e4aSElliott Hughes        paletteEntryLabels=["first color", "second color", "third color"],
1466*e1fe3e4aSElliott Hughes    )
1467*e1fe3e4aSElliott Hughes
1468*e1fe3e4aSElliott Hughes    output_path = colrv1_path.parent / "TestCOLRv1CPALv1.ttf"
1469*e1fe3e4aSElliott Hughes    fb.save(output_path)
1470*e1fe3e4aSElliott Hughes
1471*e1fe3e4aSElliott Hughes    return output_path
1472*e1fe3e4aSElliott Hughes
1473*e1fe3e4aSElliott Hughes
1474*e1fe3e4aSElliott Hughes@pytest.fixture
1475*e1fe3e4aSElliott Hughesdef colrv1_cpalv1_share_nameID_path(colrv1_path):
1476*e1fe3e4aSElliott Hughes    font = TTFont(colrv1_path)
1477*e1fe3e4aSElliott Hughes    fb = FontBuilder(font=font)
1478*e1fe3e4aSElliott Hughes    fb.setupCPAL(
1479*e1fe3e4aSElliott Hughes        [
1480*e1fe3e4aSElliott Hughes            [
1481*e1fe3e4aSElliott Hughes                (1.0, 0.0, 0.0, 1.0),  # red
1482*e1fe3e4aSElliott Hughes                (0.0, 1.0, 0.0, 1.0),  # green
1483*e1fe3e4aSElliott Hughes                (0.0, 0.0, 1.0, 1.0),  # blue
1484*e1fe3e4aSElliott Hughes            ],
1485*e1fe3e4aSElliott Hughes        ],
1486*e1fe3e4aSElliott Hughes        paletteLabels=["test palette"],
1487*e1fe3e4aSElliott Hughes        paletteEntryLabels=["first color", "second color", "third color"],
1488*e1fe3e4aSElliott Hughes    )
1489*e1fe3e4aSElliott Hughes
1490*e1fe3e4aSElliott Hughes    # Set the name ID of the first color to use nameID 1 = familyName = "TestCOLRv1"
1491*e1fe3e4aSElliott Hughes    fb.font["CPAL"].paletteEntryLabels[0] = 1
1492*e1fe3e4aSElliott Hughes
1493*e1fe3e4aSElliott Hughes    output_path = colrv1_path.parent / "TestCOLRv1CPALv1.ttf"
1494*e1fe3e4aSElliott Hughes    fb.save(output_path)
1495*e1fe3e4aSElliott Hughes
1496*e1fe3e4aSElliott Hughes    return output_path
1497*e1fe3e4aSElliott Hughes
1498*e1fe3e4aSElliott Hughes
1499*e1fe3e4aSElliott Hughesdef test_subset_COLRv1_and_CPAL(colrv1_path):
1500*e1fe3e4aSElliott Hughes    subset_path = colrv1_path.parent / (colrv1_path.name + ".subset")
1501*e1fe3e4aSElliott Hughes
1502*e1fe3e4aSElliott Hughes    subset.main(
1503*e1fe3e4aSElliott Hughes        [
1504*e1fe3e4aSElliott Hughes            str(colrv1_path),
1505*e1fe3e4aSElliott Hughes            "--glyph-names",
1506*e1fe3e4aSElliott Hughes            f"--output-file={subset_path}",
1507*e1fe3e4aSElliott Hughes            "--unicodes=E002,E003,E004",
1508*e1fe3e4aSElliott Hughes        ]
1509*e1fe3e4aSElliott Hughes    )
1510*e1fe3e4aSElliott Hughes    subset_font = TTFont(subset_path)
1511*e1fe3e4aSElliott Hughes
1512*e1fe3e4aSElliott Hughes    glyph_set = set(subset_font.getGlyphOrder())
1513*e1fe3e4aSElliott Hughes
1514*e1fe3e4aSElliott Hughes    # uniE000 and its children are excluded from subset
1515*e1fe3e4aSElliott Hughes    assert "uniE000" not in glyph_set
1516*e1fe3e4aSElliott Hughes    assert "glyph00010" not in glyph_set
1517*e1fe3e4aSElliott Hughes    assert "glyph00011" not in glyph_set
1518*e1fe3e4aSElliott Hughes
1519*e1fe3e4aSElliott Hughes    # uniE001 and children are pulled in indirectly as PaintColrGlyph by uniE003
1520*e1fe3e4aSElliott Hughes    assert "uniE001" in glyph_set
1521*e1fe3e4aSElliott Hughes    assert "glyph00012" in glyph_set
1522*e1fe3e4aSElliott Hughes    assert "glyph00013" in glyph_set
1523*e1fe3e4aSElliott Hughes
1524*e1fe3e4aSElliott Hughes    assert "uniE002" in glyph_set
1525*e1fe3e4aSElliott Hughes    assert "glyph00014" in glyph_set
1526*e1fe3e4aSElliott Hughes    assert "glyph00015" in glyph_set
1527*e1fe3e4aSElliott Hughes
1528*e1fe3e4aSElliott Hughes    assert "uniE003" in glyph_set
1529*e1fe3e4aSElliott Hughes
1530*e1fe3e4aSElliott Hughes    assert "uniE004" in glyph_set
1531*e1fe3e4aSElliott Hughes    assert "glyph00016" in glyph_set
1532*e1fe3e4aSElliott Hughes    assert "glyph00017" in glyph_set
1533*e1fe3e4aSElliott Hughes    assert "glyph00018" in glyph_set
1534*e1fe3e4aSElliott Hughes
1535*e1fe3e4aSElliott Hughes    assert "COLR" in subset_font
1536*e1fe3e4aSElliott Hughes    colr = subset_font["COLR"].table
1537*e1fe3e4aSElliott Hughes    assert colr.Version == 1
1538*e1fe3e4aSElliott Hughes    assert len(colr.BaseGlyphRecordArray.BaseGlyphRecord) == 1
1539*e1fe3e4aSElliott Hughes    assert len(colr.BaseGlyphList.BaseGlyphPaintRecord) == 3  # was 4
1540*e1fe3e4aSElliott Hughes
1541*e1fe3e4aSElliott Hughes    base = colr.BaseGlyphList.BaseGlyphPaintRecord[0]
1542*e1fe3e4aSElliott Hughes    assert base.BaseGlyph == "uniE001"
1543*e1fe3e4aSElliott Hughes    layers = colr.LayerList.Paint[
1544*e1fe3e4aSElliott Hughes        base.Paint.FirstLayerIndex : base.Paint.FirstLayerIndex + base.Paint.NumLayers
1545*e1fe3e4aSElliott Hughes    ]
1546*e1fe3e4aSElliott Hughes    assert len(layers) == 2
1547*e1fe3e4aSElliott Hughes    # check v1 palette indices were remapped
1548*e1fe3e4aSElliott Hughes    assert layers[0].Paint.Paint.ColorLine.ColorStop[0].PaletteIndex == 0
1549*e1fe3e4aSElliott Hughes    assert layers[0].Paint.Paint.ColorLine.ColorStop[1].PaletteIndex == 1
1550*e1fe3e4aSElliott Hughes    assert layers[1].Paint.PaletteIndex == 0
1551*e1fe3e4aSElliott Hughes
1552*e1fe3e4aSElliott Hughes    baseRecV0 = colr.BaseGlyphRecordArray.BaseGlyphRecord[0]
1553*e1fe3e4aSElliott Hughes    assert baseRecV0.BaseGlyph == "uniE004"
1554*e1fe3e4aSElliott Hughes    layersV0 = colr.LayerRecordArray.LayerRecord
1555*e1fe3e4aSElliott Hughes    assert len(layersV0) == 3
1556*e1fe3e4aSElliott Hughes    # check v0 palette indices were remapped (except for 0xFFFF)
1557*e1fe3e4aSElliott Hughes    assert layersV0[0].PaletteIndex == 0
1558*e1fe3e4aSElliott Hughes    assert layersV0[1].PaletteIndex == 0xFFFF
1559*e1fe3e4aSElliott Hughes    assert layersV0[2].PaletteIndex == 1
1560*e1fe3e4aSElliott Hughes
1561*e1fe3e4aSElliott Hughes    clipBoxes = colr.ClipList.clips
1562*e1fe3e4aSElliott Hughes    assert {"uniE001", "uniE002", "uniE003"} == set(clipBoxes)
1563*e1fe3e4aSElliott Hughes    assert clipBoxes["uniE002"] == clipBoxes["uniE003"]
1564*e1fe3e4aSElliott Hughes
1565*e1fe3e4aSElliott Hughes    assert "CPAL" in subset_font
1566*e1fe3e4aSElliott Hughes    cpal = subset_font["CPAL"]
1567*e1fe3e4aSElliott Hughes    assert [
1568*e1fe3e4aSElliott Hughes        tuple(v / 255 for v in (c.red, c.green, c.blue, c.alpha))
1569*e1fe3e4aSElliott Hughes        for c in cpal.palettes[0]
1570*e1fe3e4aSElliott Hughes    ] == [
1571*e1fe3e4aSElliott Hughes        # the first color 'red' was pruned
1572*e1fe3e4aSElliott Hughes        (0.0, 1.0, 0.0, 1.0),  # green
1573*e1fe3e4aSElliott Hughes        (0.0, 0.0, 1.0, 1.0),  # blue
1574*e1fe3e4aSElliott Hughes    ]
1575*e1fe3e4aSElliott Hughes
1576*e1fe3e4aSElliott Hughes
1577*e1fe3e4aSElliott Hughesdef test_subset_COLRv1_and_CPALv1(colrv1_cpalv1_path):
1578*e1fe3e4aSElliott Hughes    subset_path = colrv1_cpalv1_path.parent / (colrv1_cpalv1_path.name + ".subset")
1579*e1fe3e4aSElliott Hughes
1580*e1fe3e4aSElliott Hughes    subset.main(
1581*e1fe3e4aSElliott Hughes        [
1582*e1fe3e4aSElliott Hughes            str(colrv1_cpalv1_path),
1583*e1fe3e4aSElliott Hughes            "--glyph-names",
1584*e1fe3e4aSElliott Hughes            f"--output-file={subset_path}",
1585*e1fe3e4aSElliott Hughes            "--unicodes=E002,E003,E004",
1586*e1fe3e4aSElliott Hughes        ]
1587*e1fe3e4aSElliott Hughes    )
1588*e1fe3e4aSElliott Hughes    subset_font = TTFont(subset_path)
1589*e1fe3e4aSElliott Hughes
1590*e1fe3e4aSElliott Hughes    assert "CPAL" in subset_font
1591*e1fe3e4aSElliott Hughes    cpal = subset_font["CPAL"]
1592*e1fe3e4aSElliott Hughes    name_table = subset_font["name"]
1593*e1fe3e4aSElliott Hughes    assert [
1594*e1fe3e4aSElliott Hughes        name_table.getDebugName(name_id) for name_id in cpal.paletteEntryLabels
1595*e1fe3e4aSElliott Hughes    ] == [
1596*e1fe3e4aSElliott Hughes        # "first color",  # The first color was pruned
1597*e1fe3e4aSElliott Hughes        "second color",
1598*e1fe3e4aSElliott Hughes        "third color",
1599*e1fe3e4aSElliott Hughes    ]
1600*e1fe3e4aSElliott Hughes    # check that the "first color" name is dropped from name table
1601*e1fe3e4aSElliott Hughes    font = TTFont(colrv1_cpalv1_path)
1602*e1fe3e4aSElliott Hughes
1603*e1fe3e4aSElliott Hughes    first_color_nameID = None
1604*e1fe3e4aSElliott Hughes    for n in font["name"].names:
1605*e1fe3e4aSElliott Hughes        if n.toUnicode() == "first color":
1606*e1fe3e4aSElliott Hughes            first_color_nameID = n.nameID
1607*e1fe3e4aSElliott Hughes            break
1608*e1fe3e4aSElliott Hughes    assert first_color_nameID is not None
1609*e1fe3e4aSElliott Hughes    assert all(n.nameID != first_color_nameID for n in name_table.names)
1610*e1fe3e4aSElliott Hughes
1611*e1fe3e4aSElliott Hughes
1612*e1fe3e4aSElliott Hughesdef test_subset_COLRv1_and_CPALv1_keep_nameID(colrv1_cpalv1_path):
1613*e1fe3e4aSElliott Hughes    subset_path = colrv1_cpalv1_path.parent / (colrv1_cpalv1_path.name + ".subset")
1614*e1fe3e4aSElliott Hughes
1615*e1fe3e4aSElliott Hughes    # figure out the name ID of first color so we can keep it
1616*e1fe3e4aSElliott Hughes    font = TTFont(colrv1_cpalv1_path)
1617*e1fe3e4aSElliott Hughes
1618*e1fe3e4aSElliott Hughes    first_color_nameID = None
1619*e1fe3e4aSElliott Hughes    for n in font["name"].names:
1620*e1fe3e4aSElliott Hughes        if n.toUnicode() == "first color":
1621*e1fe3e4aSElliott Hughes            first_color_nameID = n.nameID
1622*e1fe3e4aSElliott Hughes            break
1623*e1fe3e4aSElliott Hughes    assert first_color_nameID is not None
1624*e1fe3e4aSElliott Hughes
1625*e1fe3e4aSElliott Hughes    subset.main(
1626*e1fe3e4aSElliott Hughes        [
1627*e1fe3e4aSElliott Hughes            str(colrv1_cpalv1_path),
1628*e1fe3e4aSElliott Hughes            "--glyph-names",
1629*e1fe3e4aSElliott Hughes            f"--output-file={subset_path}",
1630*e1fe3e4aSElliott Hughes            "--unicodes=E002,E003,E004",
1631*e1fe3e4aSElliott Hughes            f"--name-IDs={first_color_nameID}",
1632*e1fe3e4aSElliott Hughes        ]
1633*e1fe3e4aSElliott Hughes    )
1634*e1fe3e4aSElliott Hughes    subset_font = TTFont(subset_path)
1635*e1fe3e4aSElliott Hughes
1636*e1fe3e4aSElliott Hughes    assert "CPAL" in subset_font
1637*e1fe3e4aSElliott Hughes    cpal = subset_font["CPAL"]
1638*e1fe3e4aSElliott Hughes    name_table = subset_font["name"]
1639*e1fe3e4aSElliott Hughes    assert [
1640*e1fe3e4aSElliott Hughes        name_table.getDebugName(name_id) for name_id in cpal.paletteEntryLabels
1641*e1fe3e4aSElliott Hughes    ] == [
1642*e1fe3e4aSElliott Hughes        # "first color",  # The first color was pruned
1643*e1fe3e4aSElliott Hughes        "second color",
1644*e1fe3e4aSElliott Hughes        "third color",
1645*e1fe3e4aSElliott Hughes    ]
1646*e1fe3e4aSElliott Hughes
1647*e1fe3e4aSElliott Hughes    # Check that the name ID is kept
1648*e1fe3e4aSElliott Hughes    assert any(n.nameID == first_color_nameID for n in name_table.names)
1649*e1fe3e4aSElliott Hughes
1650*e1fe3e4aSElliott Hughes
1651*e1fe3e4aSElliott Hughesdef test_subset_COLRv1_and_CPALv1_share_nameID(colrv1_cpalv1_share_nameID_path):
1652*e1fe3e4aSElliott Hughes    subset_path = colrv1_cpalv1_share_nameID_path.parent / (
1653*e1fe3e4aSElliott Hughes        colrv1_cpalv1_share_nameID_path.name + ".subset"
1654*e1fe3e4aSElliott Hughes    )
1655*e1fe3e4aSElliott Hughes
1656*e1fe3e4aSElliott Hughes    subset.main(
1657*e1fe3e4aSElliott Hughes        [
1658*e1fe3e4aSElliott Hughes            str(colrv1_cpalv1_share_nameID_path),
1659*e1fe3e4aSElliott Hughes            "--glyph-names",
1660*e1fe3e4aSElliott Hughes            f"--output-file={subset_path}",
1661*e1fe3e4aSElliott Hughes            "--unicodes=E002,E003,E004",
1662*e1fe3e4aSElliott Hughes        ]
1663*e1fe3e4aSElliott Hughes    )
1664*e1fe3e4aSElliott Hughes    subset_font = TTFont(subset_path)
1665*e1fe3e4aSElliott Hughes
1666*e1fe3e4aSElliott Hughes    assert "CPAL" in subset_font
1667*e1fe3e4aSElliott Hughes    cpal = subset_font["CPAL"]
1668*e1fe3e4aSElliott Hughes    name_table = subset_font["name"]
1669*e1fe3e4aSElliott Hughes    assert [
1670*e1fe3e4aSElliott Hughes        name_table.getDebugName(name_id) for name_id in cpal.paletteEntryLabels
1671*e1fe3e4aSElliott Hughes    ] == [
1672*e1fe3e4aSElliott Hughes        # "first color",  # The first color was pruned
1673*e1fe3e4aSElliott Hughes        "second color",
1674*e1fe3e4aSElliott Hughes        "third color",
1675*e1fe3e4aSElliott Hughes    ]
1676*e1fe3e4aSElliott Hughes
1677*e1fe3e4aSElliott Hughes    # Check that the name ID 1 is kept
1678*e1fe3e4aSElliott Hughes    assert any(n.nameID == 1 for n in name_table.names)
1679*e1fe3e4aSElliott Hughes
1680*e1fe3e4aSElliott Hughes
1681*e1fe3e4aSElliott Hughesdef test_subset_COLRv1_and_CPAL_drop_empty(colrv1_path):
1682*e1fe3e4aSElliott Hughes    subset_path = colrv1_path.parent / (colrv1_path.name + ".subset")
1683*e1fe3e4aSElliott Hughes
1684*e1fe3e4aSElliott Hughes    subset.main(
1685*e1fe3e4aSElliott Hughes        [
1686*e1fe3e4aSElliott Hughes            str(colrv1_path),
1687*e1fe3e4aSElliott Hughes            "--glyph-names",
1688*e1fe3e4aSElliott Hughes            f"--output-file={subset_path}",
1689*e1fe3e4aSElliott Hughes            "--glyphs=glyph00010",
1690*e1fe3e4aSElliott Hughes        ]
1691*e1fe3e4aSElliott Hughes    )
1692*e1fe3e4aSElliott Hughes    subset_font = TTFont(subset_path)
1693*e1fe3e4aSElliott Hughes
1694*e1fe3e4aSElliott Hughes    glyph_set = set(subset_font.getGlyphOrder())
1695*e1fe3e4aSElliott Hughes
1696*e1fe3e4aSElliott Hughes    assert "glyph00010" in glyph_set
1697*e1fe3e4aSElliott Hughes    assert "uniE000" not in glyph_set
1698*e1fe3e4aSElliott Hughes
1699*e1fe3e4aSElliott Hughes    assert "COLR" not in subset_font
1700*e1fe3e4aSElliott Hughes    assert "CPAL" not in subset_font
1701*e1fe3e4aSElliott Hughes
1702*e1fe3e4aSElliott Hughes
1703*e1fe3e4aSElliott Hughesdef test_subset_COLRv1_downgrade_version(colrv1_path):
1704*e1fe3e4aSElliott Hughes    subset_path = colrv1_path.parent / (colrv1_path.name + ".subset")
1705*e1fe3e4aSElliott Hughes
1706*e1fe3e4aSElliott Hughes    subset.main(
1707*e1fe3e4aSElliott Hughes        [
1708*e1fe3e4aSElliott Hughes            str(colrv1_path),
1709*e1fe3e4aSElliott Hughes            "--glyph-names",
1710*e1fe3e4aSElliott Hughes            f"--output-file={subset_path}",
1711*e1fe3e4aSElliott Hughes            "--unicodes=E004",
1712*e1fe3e4aSElliott Hughes        ]
1713*e1fe3e4aSElliott Hughes    )
1714*e1fe3e4aSElliott Hughes    subset_font = TTFont(subset_path)
1715*e1fe3e4aSElliott Hughes
1716*e1fe3e4aSElliott Hughes    assert set(subset_font.getGlyphOrder()) == {
1717*e1fe3e4aSElliott Hughes        ".notdef",
1718*e1fe3e4aSElliott Hughes        "uniE004",
1719*e1fe3e4aSElliott Hughes        "glyph00016",
1720*e1fe3e4aSElliott Hughes        "glyph00017",
1721*e1fe3e4aSElliott Hughes        "glyph00018",
1722*e1fe3e4aSElliott Hughes    }
1723*e1fe3e4aSElliott Hughes
1724*e1fe3e4aSElliott Hughes    assert "COLR" in subset_font
1725*e1fe3e4aSElliott Hughes    assert subset_font["COLR"].version == 0
1726*e1fe3e4aSElliott Hughes
1727*e1fe3e4aSElliott Hughes
1728*e1fe3e4aSElliott Hughesdef test_subset_COLRv1_drop_all_v0_glyphs(colrv1_path):
1729*e1fe3e4aSElliott Hughes    subset_path = colrv1_path.parent / (colrv1_path.name + ".subset")
1730*e1fe3e4aSElliott Hughes
1731*e1fe3e4aSElliott Hughes    subset.main(
1732*e1fe3e4aSElliott Hughes        [
1733*e1fe3e4aSElliott Hughes            str(colrv1_path),
1734*e1fe3e4aSElliott Hughes            "--glyph-names",
1735*e1fe3e4aSElliott Hughes            f"--output-file={subset_path}",
1736*e1fe3e4aSElliott Hughes            "--unicodes=E003",
1737*e1fe3e4aSElliott Hughes        ]
1738*e1fe3e4aSElliott Hughes    )
1739*e1fe3e4aSElliott Hughes    subset_font = TTFont(subset_path)
1740*e1fe3e4aSElliott Hughes
1741*e1fe3e4aSElliott Hughes    assert set(subset_font.getGlyphOrder()) == {
1742*e1fe3e4aSElliott Hughes        ".notdef",
1743*e1fe3e4aSElliott Hughes        "uniE001",
1744*e1fe3e4aSElliott Hughes        "uniE003",
1745*e1fe3e4aSElliott Hughes        "glyph00012",
1746*e1fe3e4aSElliott Hughes        "glyph00013",
1747*e1fe3e4aSElliott Hughes    }
1748*e1fe3e4aSElliott Hughes
1749*e1fe3e4aSElliott Hughes    assert "COLR" in subset_font
1750*e1fe3e4aSElliott Hughes    colr = subset_font["COLR"]
1751*e1fe3e4aSElliott Hughes    assert colr.version == 1
1752*e1fe3e4aSElliott Hughes    assert colr.table.BaseGlyphRecordCount == 0
1753*e1fe3e4aSElliott Hughes    assert colr.table.BaseGlyphRecordArray is None
1754*e1fe3e4aSElliott Hughes    assert colr.table.LayerRecordArray is None
1755*e1fe3e4aSElliott Hughes    assert colr.table.LayerRecordCount is 0
1756*e1fe3e4aSElliott Hughes
1757*e1fe3e4aSElliott Hughes
1758*e1fe3e4aSElliott Hughesdef test_subset_COLRv1_no_ClipList(colrv1_path):
1759*e1fe3e4aSElliott Hughes    font = TTFont(colrv1_path)
1760*e1fe3e4aSElliott Hughes    font["COLR"].table.ClipList = None  # empty ClipList
1761*e1fe3e4aSElliott Hughes    font.save(colrv1_path)
1762*e1fe3e4aSElliott Hughes
1763*e1fe3e4aSElliott Hughes    subset_path = colrv1_path.parent / (colrv1_path.name + ".subset")
1764*e1fe3e4aSElliott Hughes    subset.main(
1765*e1fe3e4aSElliott Hughes        [
1766*e1fe3e4aSElliott Hughes            str(colrv1_path),
1767*e1fe3e4aSElliott Hughes            f"--output-file={subset_path}",
1768*e1fe3e4aSElliott Hughes            "--unicodes=*",
1769*e1fe3e4aSElliott Hughes        ]
1770*e1fe3e4aSElliott Hughes    )
1771*e1fe3e4aSElliott Hughes    subset_font = TTFont(subset_path)
1772*e1fe3e4aSElliott Hughes    assert subset_font["COLR"].table.ClipList is None
1773*e1fe3e4aSElliott Hughes
1774*e1fe3e4aSElliott Hughes
1775*e1fe3e4aSElliott Hughesdef test_subset_keep_size_drop_empty_stylistic_set():
1776*e1fe3e4aSElliott Hughes    fb = FontBuilder(unitsPerEm=1000, isTTF=True)
1777*e1fe3e4aSElliott Hughes    glyph_order = [".notdef", "a", "b", "b.ss01"]
1778*e1fe3e4aSElliott Hughes    fb.setupGlyphOrder(glyph_order)
1779*e1fe3e4aSElliott Hughes    fb.setupGlyf({g: TTGlyphPen(None).glyph() for g in glyph_order})
1780*e1fe3e4aSElliott Hughes    fb.setupCharacterMap({ord("a"): "a", ord("b"): "b"})
1781*e1fe3e4aSElliott Hughes    fb.setupHorizontalMetrics({g: (500, 0) for g in glyph_order})
1782*e1fe3e4aSElliott Hughes    fb.setupHorizontalHeader()
1783*e1fe3e4aSElliott Hughes    fb.setupOS2()
1784*e1fe3e4aSElliott Hughes    fb.setupPost()
1785*e1fe3e4aSElliott Hughes    fb.setupNameTable({"familyName": "TestKeepSizeFeature", "styleName": "Regular"})
1786*e1fe3e4aSElliott Hughes    fb.addOpenTypeFeatures(
1787*e1fe3e4aSElliott Hughes        """
1788*e1fe3e4aSElliott Hughes        feature size {
1789*e1fe3e4aSElliott Hughes          parameters 10.0 0;
1790*e1fe3e4aSElliott Hughes        } size;
1791*e1fe3e4aSElliott Hughes        feature ss01 {
1792*e1fe3e4aSElliott Hughes          featureNames {
1793*e1fe3e4aSElliott Hughes            name "Alternate b";
1794*e1fe3e4aSElliott Hughes          };
1795*e1fe3e4aSElliott Hughes          sub b by b.ss01;
1796*e1fe3e4aSElliott Hughes        } ss01;
1797*e1fe3e4aSElliott Hughes    """
1798*e1fe3e4aSElliott Hughes    )
1799*e1fe3e4aSElliott Hughes
1800*e1fe3e4aSElliott Hughes    buf = io.BytesIO()
1801*e1fe3e4aSElliott Hughes    fb.save(buf)
1802*e1fe3e4aSElliott Hughes    buf.seek(0)
1803*e1fe3e4aSElliott Hughes
1804*e1fe3e4aSElliott Hughes    font = TTFont(buf)
1805*e1fe3e4aSElliott Hughes
1806*e1fe3e4aSElliott Hughes    gpos_features = font["GPOS"].table.FeatureList.FeatureRecord
1807*e1fe3e4aSElliott Hughes    assert gpos_features[0].FeatureTag == "size"
1808*e1fe3e4aSElliott Hughes    assert isinstance(gpos_features[0].Feature.FeatureParams, ot.FeatureParamsSize)
1809*e1fe3e4aSElliott Hughes    assert gpos_features[0].Feature.LookupCount == 0
1810*e1fe3e4aSElliott Hughes    gsub_features = font["GSUB"].table.FeatureList.FeatureRecord
1811*e1fe3e4aSElliott Hughes    assert gsub_features[0].FeatureTag == "ss01"
1812*e1fe3e4aSElliott Hughes    assert isinstance(
1813*e1fe3e4aSElliott Hughes        gsub_features[0].Feature.FeatureParams, ot.FeatureParamsStylisticSet
1814*e1fe3e4aSElliott Hughes    )
1815*e1fe3e4aSElliott Hughes
1816*e1fe3e4aSElliott Hughes    options = subset.Options(layout_features=["*"])
1817*e1fe3e4aSElliott Hughes    subsetter = subset.Subsetter(options)
1818*e1fe3e4aSElliott Hughes    subsetter.populate(unicodes=[ord("a")])
1819*e1fe3e4aSElliott Hughes    subsetter.subset(font)
1820*e1fe3e4aSElliott Hughes
1821*e1fe3e4aSElliott Hughes    # empty size feature was kept
1822*e1fe3e4aSElliott Hughes    gpos_features = font["GPOS"].table.FeatureList.FeatureRecord
1823*e1fe3e4aSElliott Hughes    assert gpos_features[0].FeatureTag == "size"
1824*e1fe3e4aSElliott Hughes    assert isinstance(gpos_features[0].Feature.FeatureParams, ot.FeatureParamsSize)
1825*e1fe3e4aSElliott Hughes    assert gpos_features[0].Feature.LookupCount == 0
1826*e1fe3e4aSElliott Hughes    # empty ss01 feature was dropped
1827*e1fe3e4aSElliott Hughes    assert font["GSUB"].table.FeatureList.FeatureCount == 0
1828*e1fe3e4aSElliott Hughes
1829*e1fe3e4aSElliott Hughes
1830*e1fe3e4aSElliott Hughes@pytest.mark.skipif(etree is not None, reason="lxml is installed")
1831*e1fe3e4aSElliott Hughesdef test_subset_svg_missing_lxml(ttf_path):
1832*e1fe3e4aSElliott Hughes    # add dummy SVG table and confirm we raise ImportError upon trying to subset it
1833*e1fe3e4aSElliott Hughes    font = TTFont(ttf_path)
1834*e1fe3e4aSElliott Hughes    font["SVG "] = newTable("SVG ")
1835*e1fe3e4aSElliott Hughes    font["SVG "].docList = [('<svg><g id="glyph1"/></svg>', 1, 1)]
1836*e1fe3e4aSElliott Hughes    font.save(ttf_path)
1837*e1fe3e4aSElliott Hughes
1838*e1fe3e4aSElliott Hughes    with pytest.raises(ImportError):
1839*e1fe3e4aSElliott Hughes        subset.main([str(ttf_path), "--gids=0,1"])
1840*e1fe3e4aSElliott Hughes
1841*e1fe3e4aSElliott Hughes
1842*e1fe3e4aSElliott Hughesdef test_subset_COLR_glyph_closure(tmp_path):
1843*e1fe3e4aSElliott Hughes    # https://github.com/fonttools/fonttools/issues/2461
1844*e1fe3e4aSElliott Hughes    font = TTFont()
1845*e1fe3e4aSElliott Hughes    ttx = pathlib.Path(__file__).parent / "data" / "BungeeColor-Regular.ttx"
1846*e1fe3e4aSElliott Hughes    font.importXML(ttx)
1847*e1fe3e4aSElliott Hughes
1848*e1fe3e4aSElliott Hughes    color_layers = font["COLR"].ColorLayers
1849*e1fe3e4aSElliott Hughes    assert ".notdef" in color_layers
1850*e1fe3e4aSElliott Hughes    assert "Agrave" in color_layers
1851*e1fe3e4aSElliott Hughes    assert "grave" in color_layers
1852*e1fe3e4aSElliott Hughes
1853*e1fe3e4aSElliott Hughes    font_path = tmp_path / "BungeeColor-Regular.ttf"
1854*e1fe3e4aSElliott Hughes    subset_path = font_path.with_suffix(".subset.ttf)")
1855*e1fe3e4aSElliott Hughes    font.save(font_path)
1856*e1fe3e4aSElliott Hughes
1857*e1fe3e4aSElliott Hughes    subset.main(
1858*e1fe3e4aSElliott Hughes        [
1859*e1fe3e4aSElliott Hughes            str(font_path),
1860*e1fe3e4aSElliott Hughes            "--glyph-names",
1861*e1fe3e4aSElliott Hughes            f"--output-file={subset_path}",
1862*e1fe3e4aSElliott Hughes            "--glyphs=Agrave",
1863*e1fe3e4aSElliott Hughes        ]
1864*e1fe3e4aSElliott Hughes    )
1865*e1fe3e4aSElliott Hughes    subset_font = TTFont(subset_path)
1866*e1fe3e4aSElliott Hughes
1867*e1fe3e4aSElliott Hughes    glyph_order = subset_font.getGlyphOrder()
1868*e1fe3e4aSElliott Hughes
1869*e1fe3e4aSElliott Hughes    assert glyph_order == [
1870*e1fe3e4aSElliott Hughes        ".notdef",  # '.notdef' is always included automatically
1871*e1fe3e4aSElliott Hughes        "A",
1872*e1fe3e4aSElliott Hughes        "grave",
1873*e1fe3e4aSElliott Hughes        "Agrave",
1874*e1fe3e4aSElliott Hughes        ".notdef.alt001",
1875*e1fe3e4aSElliott Hughes        ".notdef.alt002",
1876*e1fe3e4aSElliott Hughes        "A.alt002",
1877*e1fe3e4aSElliott Hughes        "Agrave.alt001",
1878*e1fe3e4aSElliott Hughes        "Agrave.alt002",
1879*e1fe3e4aSElliott Hughes        "grave.alt002",
1880*e1fe3e4aSElliott Hughes    ]
1881*e1fe3e4aSElliott Hughes
1882*e1fe3e4aSElliott Hughes    color_layers = subset_font["COLR"].ColorLayers
1883*e1fe3e4aSElliott Hughes    assert ".notdef" in color_layers
1884*e1fe3e4aSElliott Hughes    assert "Agrave" in color_layers
1885*e1fe3e4aSElliott Hughes    # Agrave 'glyf' uses grave. It should be retained in 'glyf' but NOT in
1886*e1fe3e4aSElliott Hughes    # COLR when we subset down to Agrave.
1887*e1fe3e4aSElliott Hughes    assert "grave" not in color_layers
1888*e1fe3e4aSElliott Hughes
1889*e1fe3e4aSElliott Hughes
1890*e1fe3e4aSElliott Hughesdef test_subset_recalc_xAvgCharWidth(ttf_path):
1891*e1fe3e4aSElliott Hughes    # Note that the font in in the *ttLib*/data/TestTTF-Regular.ttx file,
1892*e1fe3e4aSElliott Hughes    # not this subset/data folder.
1893*e1fe3e4aSElliott Hughes    font = TTFont(ttf_path)
1894*e1fe3e4aSElliott Hughes    xAvgCharWidth_before = font["OS/2"].xAvgCharWidth
1895*e1fe3e4aSElliott Hughes
1896*e1fe3e4aSElliott Hughes    subset_path = ttf_path.with_suffix(".subset.ttf")
1897*e1fe3e4aSElliott Hughes    subset.main(
1898*e1fe3e4aSElliott Hughes        [
1899*e1fe3e4aSElliott Hughes            str(ttf_path),
1900*e1fe3e4aSElliott Hughes            f"--output-file={subset_path}",
1901*e1fe3e4aSElliott Hughes            # Keep only the ellipsis, which is very wide, that ought to bump up the average
1902*e1fe3e4aSElliott Hughes            "--glyphs=ellipsis",
1903*e1fe3e4aSElliott Hughes            "--recalc-average-width",
1904*e1fe3e4aSElliott Hughes            "--no-prune-unicode-ranges",
1905*e1fe3e4aSElliott Hughes        ]
1906*e1fe3e4aSElliott Hughes    )
1907*e1fe3e4aSElliott Hughes    subset_font = TTFont(subset_path)
1908*e1fe3e4aSElliott Hughes    xAvgCharWidth_after = subset_font["OS/2"].xAvgCharWidth
1909*e1fe3e4aSElliott Hughes
1910*e1fe3e4aSElliott Hughes    # Check that the value gets updated
1911*e1fe3e4aSElliott Hughes    assert xAvgCharWidth_after != xAvgCharWidth_before
1912*e1fe3e4aSElliott Hughes
1913*e1fe3e4aSElliott Hughes    # Check that the value gets updated to the actual new value
1914*e1fe3e4aSElliott Hughes    subset_font["OS/2"].recalcAvgCharWidth(subset_font)
1915*e1fe3e4aSElliott Hughes    assert xAvgCharWidth_after == subset_font["OS/2"].xAvgCharWidth
1916*e1fe3e4aSElliott Hughes
1917*e1fe3e4aSElliott Hughes
1918*e1fe3e4aSElliott Hughesif __name__ == "__main__":
1919*e1fe3e4aSElliott Hughes    sys.exit(unittest.main())
1920*e1fe3e4aSElliott Hughes
1921*e1fe3e4aSElliott Hughes
1922*e1fe3e4aSElliott Hughesdef test_subset_prune_gdef_markglyphsetsdef():
1923*e1fe3e4aSElliott Hughes    # GDEF_MarkGlyphSetsDef
1924*e1fe3e4aSElliott Hughes    fb = FontBuilder(unitsPerEm=1000, isTTF=True)
1925*e1fe3e4aSElliott Hughes    glyph_order = [
1926*e1fe3e4aSElliott Hughes        ".notdef",
1927*e1fe3e4aSElliott Hughes        "A",
1928*e1fe3e4aSElliott Hughes        "Aacute",
1929*e1fe3e4aSElliott Hughes        "Acircumflex",
1930*e1fe3e4aSElliott Hughes        "Adieresis",
1931*e1fe3e4aSElliott Hughes        "a",
1932*e1fe3e4aSElliott Hughes        "aacute",
1933*e1fe3e4aSElliott Hughes        "acircumflex",
1934*e1fe3e4aSElliott Hughes        "adieresis",
1935*e1fe3e4aSElliott Hughes        "dieresiscomb",
1936*e1fe3e4aSElliott Hughes        "acutecomb",
1937*e1fe3e4aSElliott Hughes        "circumflexcomb",
1938*e1fe3e4aSElliott Hughes    ]
1939*e1fe3e4aSElliott Hughes    fb.setupGlyphOrder(glyph_order)
1940*e1fe3e4aSElliott Hughes    fb.setupGlyf({g: TTGlyphPen(None).glyph() for g in glyph_order})
1941*e1fe3e4aSElliott Hughes    fb.setupHorizontalMetrics({g: (500, 0) for g in glyph_order})
1942*e1fe3e4aSElliott Hughes    fb.setupHorizontalHeader()
1943*e1fe3e4aSElliott Hughes    fb.setupPost()
1944*e1fe3e4aSElliott Hughes    fb.setupNameTable(
1945*e1fe3e4aSElliott Hughes        {"familyName": "TestGDEFMarkGlyphSetsDef", "styleName": "Regular"}
1946*e1fe3e4aSElliott Hughes    )
1947*e1fe3e4aSElliott Hughes    fb.addOpenTypeFeatures(
1948*e1fe3e4aSElliott Hughes        """
1949*e1fe3e4aSElliott Hughes        feature ccmp {
1950*e1fe3e4aSElliott Hughes            lookup ccmp_1 {
1951*e1fe3e4aSElliott Hughes                lookupflag UseMarkFilteringSet [acutecomb];
1952*e1fe3e4aSElliott Hughes                sub a acutecomb by aacute;
1953*e1fe3e4aSElliott Hughes                sub A acutecomb by Aacute;
1954*e1fe3e4aSElliott Hughes            } ccmp_1;
1955*e1fe3e4aSElliott Hughes            lookup ccmp_2 {
1956*e1fe3e4aSElliott Hughes                lookupflag UseMarkFilteringSet [circumflexcomb];
1957*e1fe3e4aSElliott Hughes                sub a circumflexcomb by acircumflex;
1958*e1fe3e4aSElliott Hughes                sub A circumflexcomb by Acircumflex;
1959*e1fe3e4aSElliott Hughes            } ccmp_2;
1960*e1fe3e4aSElliott Hughes            lookup ccmp_3 {
1961*e1fe3e4aSElliott Hughes                lookupflag UseMarkFilteringSet [dieresiscomb];
1962*e1fe3e4aSElliott Hughes                sub a dieresiscomb by adieresis;
1963*e1fe3e4aSElliott Hughes                sub A dieresiscomb by Adieresis;
1964*e1fe3e4aSElliott Hughes                sub A acutecomb by Aacute;
1965*e1fe3e4aSElliott Hughes            } ccmp_3;
1966*e1fe3e4aSElliott Hughes        } ccmp;
1967*e1fe3e4aSElliott Hughes    """
1968*e1fe3e4aSElliott Hughes    )
1969*e1fe3e4aSElliott Hughes
1970*e1fe3e4aSElliott Hughes    buf = io.BytesIO()
1971*e1fe3e4aSElliott Hughes    fb.save(buf)
1972*e1fe3e4aSElliott Hughes    buf.seek(0)
1973*e1fe3e4aSElliott Hughes
1974*e1fe3e4aSElliott Hughes    font = TTFont(buf)
1975*e1fe3e4aSElliott Hughes
1976*e1fe3e4aSElliott Hughes    features = font["GSUB"].table.FeatureList.FeatureRecord
1977*e1fe3e4aSElliott Hughes    assert features[0].FeatureTag == "ccmp"
1978*e1fe3e4aSElliott Hughes    lookups = font["GSUB"].table.LookupList.Lookup
1979*e1fe3e4aSElliott Hughes    assert lookups[0].LookupFlag == 16
1980*e1fe3e4aSElliott Hughes    assert lookups[0].MarkFilteringSet == 0
1981*e1fe3e4aSElliott Hughes    assert lookups[1].LookupFlag == 16
1982*e1fe3e4aSElliott Hughes    assert lookups[1].MarkFilteringSet == 1
1983*e1fe3e4aSElliott Hughes    assert lookups[2].LookupFlag == 16
1984*e1fe3e4aSElliott Hughes    assert lookups[2].MarkFilteringSet == 2
1985*e1fe3e4aSElliott Hughes    marksets = font["GDEF"].table.MarkGlyphSetsDef.Coverage
1986*e1fe3e4aSElliott Hughes    assert marksets[0].glyphs == ["acutecomb"]
1987*e1fe3e4aSElliott Hughes    assert marksets[1].glyphs == ["circumflexcomb"]
1988*e1fe3e4aSElliott Hughes    assert marksets[2].glyphs == ["dieresiscomb"]
1989*e1fe3e4aSElliott Hughes
1990*e1fe3e4aSElliott Hughes    options = subset.Options(layout_features=["*"])
1991*e1fe3e4aSElliott Hughes    subsetter = subset.Subsetter(options)
1992*e1fe3e4aSElliott Hughes    subsetter.populate(glyphs=["A", "a", "acutecomb", "dieresiscomb"])
1993*e1fe3e4aSElliott Hughes    subsetter.subset(font)
1994*e1fe3e4aSElliott Hughes
1995*e1fe3e4aSElliott Hughes    features = font["GSUB"].table.FeatureList.FeatureRecord
1996*e1fe3e4aSElliott Hughes    assert features[0].FeatureTag == "ccmp"
1997*e1fe3e4aSElliott Hughes    lookups = font["GSUB"].table.LookupList.Lookup
1998*e1fe3e4aSElliott Hughes    assert lookups[0].LookupFlag == 16
1999*e1fe3e4aSElliott Hughes    assert lookups[0].MarkFilteringSet == 0
2000*e1fe3e4aSElliott Hughes    assert lookups[1].LookupFlag == 16
2001*e1fe3e4aSElliott Hughes    assert lookups[1].MarkFilteringSet == 1
2002*e1fe3e4aSElliott Hughes    marksets = font["GDEF"].table.MarkGlyphSetsDef.Coverage
2003*e1fe3e4aSElliott Hughes    assert marksets[0].glyphs == ["acutecomb"]
2004*e1fe3e4aSElliott Hughes    assert marksets[1].glyphs == ["dieresiscomb"]
2005*e1fe3e4aSElliott Hughes
2006*e1fe3e4aSElliott Hughes    buf = io.BytesIO()
2007*e1fe3e4aSElliott Hughes    fb.save(buf)
2008*e1fe3e4aSElliott Hughes    buf.seek(0)
2009*e1fe3e4aSElliott Hughes
2010*e1fe3e4aSElliott Hughes    font = TTFont(buf)
2011*e1fe3e4aSElliott Hughes
2012*e1fe3e4aSElliott Hughes    options = subset.Options(layout_features=["*"], layout_closure=False)
2013*e1fe3e4aSElliott Hughes    subsetter = subset.Subsetter(options)
2014*e1fe3e4aSElliott Hughes    subsetter.populate(glyphs=["A", "acutecomb", "Aacute"])
2015*e1fe3e4aSElliott Hughes    subsetter.subset(font)
2016*e1fe3e4aSElliott Hughes
2017*e1fe3e4aSElliott Hughes    features = font["GSUB"].table.FeatureList.FeatureRecord
2018*e1fe3e4aSElliott Hughes    assert features[0].FeatureTag == "ccmp"
2019*e1fe3e4aSElliott Hughes    lookups = font["GSUB"].table.LookupList.Lookup
2020*e1fe3e4aSElliott Hughes    assert lookups[0].LookupFlag == 16
2021*e1fe3e4aSElliott Hughes    assert lookups[0].MarkFilteringSet == 0
2022*e1fe3e4aSElliott Hughes    assert lookups[1].LookupFlag == 0
2023*e1fe3e4aSElliott Hughes    assert lookups[1].MarkFilteringSet == None
2024*e1fe3e4aSElliott Hughes    marksets = font["GDEF"].table.MarkGlyphSetsDef.Coverage
2025*e1fe3e4aSElliott Hughes    assert marksets[0].glyphs == ["acutecomb"]
2026