1*e1fe3e4aSElliott Hughesfrom fontTools.colorLib.builder import buildCOLR 2*e1fe3e4aSElliott Hughesfrom fontTools.ttLib import TTFont, newTable 3*e1fe3e4aSElliott Hughesfrom fontTools.ttLib.tables import otTables as ot 4*e1fe3e4aSElliott Hughesfrom fontTools.varLib import ( 5*e1fe3e4aSElliott Hughes build, 6*e1fe3e4aSElliott Hughes build_many, 7*e1fe3e4aSElliott Hughes load_designspace, 8*e1fe3e4aSElliott Hughes _add_COLR, 9*e1fe3e4aSElliott Hughes addGSUBFeatureVariations, 10*e1fe3e4aSElliott Hughes) 11*e1fe3e4aSElliott Hughesfrom fontTools.varLib.errors import VarLibValidationError 12*e1fe3e4aSElliott Hughesimport fontTools.varLib.errors as varLibErrors 13*e1fe3e4aSElliott Hughesfrom fontTools.varLib.models import VariationModel 14*e1fe3e4aSElliott Hughesfrom fontTools.varLib.mutator import instantiateVariableFont 15*e1fe3e4aSElliott Hughesfrom fontTools.varLib import main as varLib_main, load_masters 16*e1fe3e4aSElliott Hughesfrom fontTools.varLib import set_default_weight_width_slant 17*e1fe3e4aSElliott Hughesfrom fontTools.designspaceLib import ( 18*e1fe3e4aSElliott Hughes DesignSpaceDocumentError, 19*e1fe3e4aSElliott Hughes DesignSpaceDocument, 20*e1fe3e4aSElliott Hughes SourceDescriptor, 21*e1fe3e4aSElliott Hughes) 22*e1fe3e4aSElliott Hughesfrom fontTools.feaLib.builder import addOpenTypeFeaturesFromString 23*e1fe3e4aSElliott Hughesimport difflib 24*e1fe3e4aSElliott Hughesfrom copy import deepcopy 25*e1fe3e4aSElliott Hughesfrom io import BytesIO 26*e1fe3e4aSElliott Hughesimport os 27*e1fe3e4aSElliott Hughesimport shutil 28*e1fe3e4aSElliott Hughesimport sys 29*e1fe3e4aSElliott Hughesimport tempfile 30*e1fe3e4aSElliott Hughesimport unittest 31*e1fe3e4aSElliott Hughesimport pytest 32*e1fe3e4aSElliott Hughes 33*e1fe3e4aSElliott Hughes 34*e1fe3e4aSElliott Hughesdef reload_font(font): 35*e1fe3e4aSElliott Hughes """(De)serialize to get final binary layout.""" 36*e1fe3e4aSElliott Hughes buf = BytesIO() 37*e1fe3e4aSElliott Hughes font.save(buf) 38*e1fe3e4aSElliott Hughes # Close the font to release filesystem resources so that on Windows the tearDown 39*e1fe3e4aSElliott Hughes # method can successfully remove the temporary directory created during setUp. 40*e1fe3e4aSElliott Hughes font.close() 41*e1fe3e4aSElliott Hughes buf.seek(0) 42*e1fe3e4aSElliott Hughes return TTFont(buf) 43*e1fe3e4aSElliott Hughes 44*e1fe3e4aSElliott Hughes 45*e1fe3e4aSElliott Hughesclass BuildTest(unittest.TestCase): 46*e1fe3e4aSElliott Hughes def __init__(self, methodName): 47*e1fe3e4aSElliott Hughes unittest.TestCase.__init__(self, methodName) 48*e1fe3e4aSElliott Hughes # Python 3 renamed assertRaisesRegexp to assertRaisesRegex, 49*e1fe3e4aSElliott Hughes # and fires deprecation warnings if a program uses the old name. 50*e1fe3e4aSElliott Hughes if not hasattr(self, "assertRaisesRegex"): 51*e1fe3e4aSElliott Hughes self.assertRaisesRegex = self.assertRaisesRegexp 52*e1fe3e4aSElliott Hughes 53*e1fe3e4aSElliott Hughes def setUp(self): 54*e1fe3e4aSElliott Hughes self.tempdir = None 55*e1fe3e4aSElliott Hughes self.num_tempfiles = 0 56*e1fe3e4aSElliott Hughes 57*e1fe3e4aSElliott Hughes def tearDown(self): 58*e1fe3e4aSElliott Hughes if self.tempdir: 59*e1fe3e4aSElliott Hughes shutil.rmtree(self.tempdir) 60*e1fe3e4aSElliott Hughes 61*e1fe3e4aSElliott Hughes def get_test_input(self, test_file_or_folder, copy=False): 62*e1fe3e4aSElliott Hughes parent_dir = os.path.dirname(__file__) 63*e1fe3e4aSElliott Hughes path = os.path.join(parent_dir, "data", test_file_or_folder) 64*e1fe3e4aSElliott Hughes if copy: 65*e1fe3e4aSElliott Hughes copied_path = os.path.join(self.tempdir, test_file_or_folder) 66*e1fe3e4aSElliott Hughes shutil.copy2(path, copied_path) 67*e1fe3e4aSElliott Hughes return copied_path 68*e1fe3e4aSElliott Hughes else: 69*e1fe3e4aSElliott Hughes return path 70*e1fe3e4aSElliott Hughes 71*e1fe3e4aSElliott Hughes @staticmethod 72*e1fe3e4aSElliott Hughes def get_test_output(test_file_or_folder): 73*e1fe3e4aSElliott Hughes path, _ = os.path.split(__file__) 74*e1fe3e4aSElliott Hughes return os.path.join(path, "data", "test_results", test_file_or_folder) 75*e1fe3e4aSElliott Hughes 76*e1fe3e4aSElliott Hughes @staticmethod 77*e1fe3e4aSElliott Hughes def get_file_list(folder, suffix, prefix=""): 78*e1fe3e4aSElliott Hughes all_files = os.listdir(folder) 79*e1fe3e4aSElliott Hughes file_list = [] 80*e1fe3e4aSElliott Hughes for p in all_files: 81*e1fe3e4aSElliott Hughes if p.startswith(prefix) and p.endswith(suffix): 82*e1fe3e4aSElliott Hughes file_list.append(os.path.abspath(os.path.join(folder, p))) 83*e1fe3e4aSElliott Hughes return file_list 84*e1fe3e4aSElliott Hughes 85*e1fe3e4aSElliott Hughes def temp_path(self, suffix): 86*e1fe3e4aSElliott Hughes self.temp_dir() 87*e1fe3e4aSElliott Hughes self.num_tempfiles += 1 88*e1fe3e4aSElliott Hughes return os.path.join(self.tempdir, "tmp%d%s" % (self.num_tempfiles, suffix)) 89*e1fe3e4aSElliott Hughes 90*e1fe3e4aSElliott Hughes def temp_dir(self): 91*e1fe3e4aSElliott Hughes if not self.tempdir: 92*e1fe3e4aSElliott Hughes self.tempdir = tempfile.mkdtemp() 93*e1fe3e4aSElliott Hughes 94*e1fe3e4aSElliott Hughes def read_ttx(self, path): 95*e1fe3e4aSElliott Hughes lines = [] 96*e1fe3e4aSElliott Hughes with open(path, "r", encoding="utf-8") as ttx: 97*e1fe3e4aSElliott Hughes for line in ttx.readlines(): 98*e1fe3e4aSElliott Hughes # Elide ttFont attributes because ttLibVersion may change. 99*e1fe3e4aSElliott Hughes if line.startswith("<ttFont "): 100*e1fe3e4aSElliott Hughes lines.append("<ttFont>\n") 101*e1fe3e4aSElliott Hughes else: 102*e1fe3e4aSElliott Hughes lines.append(line.rstrip() + "\n") 103*e1fe3e4aSElliott Hughes return lines 104*e1fe3e4aSElliott Hughes 105*e1fe3e4aSElliott Hughes def expect_ttx(self, font, expected_ttx, tables): 106*e1fe3e4aSElliott Hughes path = self.temp_path(suffix=".ttx") 107*e1fe3e4aSElliott Hughes font.saveXML(path, tables=tables) 108*e1fe3e4aSElliott Hughes actual = self.read_ttx(path) 109*e1fe3e4aSElliott Hughes expected = self.read_ttx(expected_ttx) 110*e1fe3e4aSElliott Hughes if actual != expected: 111*e1fe3e4aSElliott Hughes for line in difflib.unified_diff( 112*e1fe3e4aSElliott Hughes expected, actual, fromfile=expected_ttx, tofile=path 113*e1fe3e4aSElliott Hughes ): 114*e1fe3e4aSElliott Hughes sys.stdout.write(line) 115*e1fe3e4aSElliott Hughes self.fail("TTX output is different from expected") 116*e1fe3e4aSElliott Hughes 117*e1fe3e4aSElliott Hughes def check_ttx_dump(self, font, expected_ttx, tables, suffix): 118*e1fe3e4aSElliott Hughes """Ensure the TTX dump is the same after saving and reloading the font.""" 119*e1fe3e4aSElliott Hughes path = self.temp_path(suffix=suffix) 120*e1fe3e4aSElliott Hughes font.save(path) 121*e1fe3e4aSElliott Hughes self.expect_ttx(TTFont(path), expected_ttx, tables) 122*e1fe3e4aSElliott Hughes 123*e1fe3e4aSElliott Hughes def compile_font(self, path, suffix, temp_dir): 124*e1fe3e4aSElliott Hughes ttx_filename = os.path.basename(path) 125*e1fe3e4aSElliott Hughes savepath = os.path.join(temp_dir, ttx_filename.replace(".ttx", suffix)) 126*e1fe3e4aSElliott Hughes font = TTFont(recalcBBoxes=False, recalcTimestamp=False) 127*e1fe3e4aSElliott Hughes font.importXML(path) 128*e1fe3e4aSElliott Hughes font.save(savepath, reorderTables=None) 129*e1fe3e4aSElliott Hughes return font, savepath 130*e1fe3e4aSElliott Hughes 131*e1fe3e4aSElliott Hughes def _run_varlib_build_test( 132*e1fe3e4aSElliott Hughes self, 133*e1fe3e4aSElliott Hughes designspace_name, 134*e1fe3e4aSElliott Hughes font_name, 135*e1fe3e4aSElliott Hughes tables, 136*e1fe3e4aSElliott Hughes expected_ttx_name, 137*e1fe3e4aSElliott Hughes save_before_dump=False, 138*e1fe3e4aSElliott Hughes post_process_master=None, 139*e1fe3e4aSElliott Hughes ): 140*e1fe3e4aSElliott Hughes suffix = ".ttf" 141*e1fe3e4aSElliott Hughes ds_path = self.get_test_input(designspace_name + ".designspace") 142*e1fe3e4aSElliott Hughes ufo_dir = self.get_test_input("master_ufo") 143*e1fe3e4aSElliott Hughes ttx_dir = self.get_test_input("master_ttx_interpolatable_ttf") 144*e1fe3e4aSElliott Hughes 145*e1fe3e4aSElliott Hughes self.temp_dir() 146*e1fe3e4aSElliott Hughes ttx_paths = self.get_file_list(ttx_dir, ".ttx", font_name + "-") 147*e1fe3e4aSElliott Hughes for path in ttx_paths: 148*e1fe3e4aSElliott Hughes font, savepath = self.compile_font(path, suffix, self.tempdir) 149*e1fe3e4aSElliott Hughes if post_process_master is not None: 150*e1fe3e4aSElliott Hughes post_process_master(font, savepath) 151*e1fe3e4aSElliott Hughes 152*e1fe3e4aSElliott Hughes finder = lambda s: s.replace(ufo_dir, self.tempdir).replace(".ufo", suffix) 153*e1fe3e4aSElliott Hughes varfont, model, _ = build(ds_path, finder) 154*e1fe3e4aSElliott Hughes 155*e1fe3e4aSElliott Hughes if save_before_dump: 156*e1fe3e4aSElliott Hughes # some data (e.g. counts printed in TTX inline comments) is only 157*e1fe3e4aSElliott Hughes # calculated at compile time, so before we can compare the TTX 158*e1fe3e4aSElliott Hughes # dumps we need to save to a temporary stream, and realod the font 159*e1fe3e4aSElliott Hughes varfont = reload_font(varfont) 160*e1fe3e4aSElliott Hughes 161*e1fe3e4aSElliott Hughes expected_ttx_path = self.get_test_output(expected_ttx_name + ".ttx") 162*e1fe3e4aSElliott Hughes self.expect_ttx(varfont, expected_ttx_path, tables) 163*e1fe3e4aSElliott Hughes self.check_ttx_dump(varfont, expected_ttx_path, tables, suffix) 164*e1fe3e4aSElliott Hughes 165*e1fe3e4aSElliott Hughes # ----- 166*e1fe3e4aSElliott Hughes # Tests 167*e1fe3e4aSElliott Hughes # ----- 168*e1fe3e4aSElliott Hughes 169*e1fe3e4aSElliott Hughes def test_varlib_build_ttf(self): 170*e1fe3e4aSElliott Hughes """Designspace file contains <axes> element.""" 171*e1fe3e4aSElliott Hughes self._run_varlib_build_test( 172*e1fe3e4aSElliott Hughes designspace_name="Build", 173*e1fe3e4aSElliott Hughes font_name="TestFamily", 174*e1fe3e4aSElliott Hughes tables=["GDEF", "HVAR", "MVAR", "fvar", "gvar"], 175*e1fe3e4aSElliott Hughes expected_ttx_name="Build", 176*e1fe3e4aSElliott Hughes ) 177*e1fe3e4aSElliott Hughes 178*e1fe3e4aSElliott Hughes def test_varlib_build_no_axes_ttf(self): 179*e1fe3e4aSElliott Hughes """Designspace file does not contain an <axes> element.""" 180*e1fe3e4aSElliott Hughes ds_path = self.get_test_input("InterpolateLayout3.designspace") 181*e1fe3e4aSElliott Hughes with self.assertRaisesRegex(DesignSpaceDocumentError, "No axes defined"): 182*e1fe3e4aSElliott Hughes build(ds_path) 183*e1fe3e4aSElliott Hughes 184*e1fe3e4aSElliott Hughes def test_varlib_avar_single_axis(self): 185*e1fe3e4aSElliott Hughes """Designspace file contains a 'weight' axis with <map> elements 186*e1fe3e4aSElliott Hughes modifying the normalization mapping. An 'avar' table is generated. 187*e1fe3e4aSElliott Hughes """ 188*e1fe3e4aSElliott Hughes test_name = "BuildAvarSingleAxis" 189*e1fe3e4aSElliott Hughes self._run_varlib_build_test( 190*e1fe3e4aSElliott Hughes designspace_name=test_name, 191*e1fe3e4aSElliott Hughes font_name="TestFamily3", 192*e1fe3e4aSElliott Hughes tables=["avar"], 193*e1fe3e4aSElliott Hughes expected_ttx_name=test_name, 194*e1fe3e4aSElliott Hughes ) 195*e1fe3e4aSElliott Hughes 196*e1fe3e4aSElliott Hughes def test_varlib_avar_with_identity_maps(self): 197*e1fe3e4aSElliott Hughes """Designspace file contains two 'weight' and 'width' axes both with 198*e1fe3e4aSElliott Hughes <map> elements. 199*e1fe3e4aSElliott Hughes 200*e1fe3e4aSElliott Hughes The 'width' axis only contains identity mappings, however the resulting 201*e1fe3e4aSElliott Hughes avar segment will not be empty but will contain the default axis value 202*e1fe3e4aSElliott Hughes maps: {-1.0: -1.0, 0.0: 0.0, 1.0: 1.0}. 203*e1fe3e4aSElliott Hughes 204*e1fe3e4aSElliott Hughes This is to work around an issue with some rasterizers: 205*e1fe3e4aSElliott Hughes https://github.com/googlei18n/fontmake/issues/295 206*e1fe3e4aSElliott Hughes https://github.com/fonttools/fonttools/issues/1011 207*e1fe3e4aSElliott Hughes """ 208*e1fe3e4aSElliott Hughes test_name = "BuildAvarIdentityMaps" 209*e1fe3e4aSElliott Hughes self._run_varlib_build_test( 210*e1fe3e4aSElliott Hughes designspace_name=test_name, 211*e1fe3e4aSElliott Hughes font_name="TestFamily3", 212*e1fe3e4aSElliott Hughes tables=["avar"], 213*e1fe3e4aSElliott Hughes expected_ttx_name=test_name, 214*e1fe3e4aSElliott Hughes ) 215*e1fe3e4aSElliott Hughes 216*e1fe3e4aSElliott Hughes def test_varlib_avar_empty_axis(self): 217*e1fe3e4aSElliott Hughes """Designspace file contains two 'weight' and 'width' axes, but 218*e1fe3e4aSElliott Hughes only one axis ('weight') has some <map> elements. 219*e1fe3e4aSElliott Hughes 220*e1fe3e4aSElliott Hughes Even if no <map> elements are defined for the 'width' axis, the 221*e1fe3e4aSElliott Hughes resulting avar segment still contains the default axis value maps: 222*e1fe3e4aSElliott Hughes {-1.0: -1.0, 0.0: 0.0, 1.0: 1.0}. 223*e1fe3e4aSElliott Hughes 224*e1fe3e4aSElliott Hughes This is again to work around an issue with some rasterizers: 225*e1fe3e4aSElliott Hughes https://github.com/googlei18n/fontmake/issues/295 226*e1fe3e4aSElliott Hughes https://github.com/fonttools/fonttools/issues/1011 227*e1fe3e4aSElliott Hughes """ 228*e1fe3e4aSElliott Hughes test_name = "BuildAvarEmptyAxis" 229*e1fe3e4aSElliott Hughes self._run_varlib_build_test( 230*e1fe3e4aSElliott Hughes designspace_name=test_name, 231*e1fe3e4aSElliott Hughes font_name="TestFamily3", 232*e1fe3e4aSElliott Hughes tables=["avar"], 233*e1fe3e4aSElliott Hughes expected_ttx_name=test_name, 234*e1fe3e4aSElliott Hughes ) 235*e1fe3e4aSElliott Hughes 236*e1fe3e4aSElliott Hughes def test_varlib_avar2(self): 237*e1fe3e4aSElliott Hughes """Designspace file contains a 'weight' axis with <map> elements 238*e1fe3e4aSElliott Hughes modifying the normalization mapping as well as <mappings> element 239*e1fe3e4aSElliott Hughes modifying it post-normalization. An 'avar' table is generated. 240*e1fe3e4aSElliott Hughes """ 241*e1fe3e4aSElliott Hughes test_name = "BuildAvar2" 242*e1fe3e4aSElliott Hughes self._run_varlib_build_test( 243*e1fe3e4aSElliott Hughes designspace_name=test_name, 244*e1fe3e4aSElliott Hughes font_name="TestFamily3", 245*e1fe3e4aSElliott Hughes tables=["avar"], 246*e1fe3e4aSElliott Hughes expected_ttx_name=test_name, 247*e1fe3e4aSElliott Hughes ) 248*e1fe3e4aSElliott Hughes 249*e1fe3e4aSElliott Hughes def test_varlib_build_feature_variations(self): 250*e1fe3e4aSElliott Hughes """Designspace file contains <rules> element, used to build 251*e1fe3e4aSElliott Hughes GSUB FeatureVariations table. 252*e1fe3e4aSElliott Hughes """ 253*e1fe3e4aSElliott Hughes self._run_varlib_build_test( 254*e1fe3e4aSElliott Hughes designspace_name="FeatureVars", 255*e1fe3e4aSElliott Hughes font_name="TestFamily", 256*e1fe3e4aSElliott Hughes tables=["fvar", "GSUB"], 257*e1fe3e4aSElliott Hughes expected_ttx_name="FeatureVars", 258*e1fe3e4aSElliott Hughes save_before_dump=True, 259*e1fe3e4aSElliott Hughes ) 260*e1fe3e4aSElliott Hughes 261*e1fe3e4aSElliott Hughes def test_varlib_build_feature_variations_custom_tag(self): 262*e1fe3e4aSElliott Hughes """Designspace file contains <rules> element, used to build 263*e1fe3e4aSElliott Hughes GSUB FeatureVariations table. 264*e1fe3e4aSElliott Hughes """ 265*e1fe3e4aSElliott Hughes self._run_varlib_build_test( 266*e1fe3e4aSElliott Hughes designspace_name="FeatureVarsCustomTag", 267*e1fe3e4aSElliott Hughes font_name="TestFamily", 268*e1fe3e4aSElliott Hughes tables=["fvar", "GSUB"], 269*e1fe3e4aSElliott Hughes expected_ttx_name="FeatureVarsCustomTag", 270*e1fe3e4aSElliott Hughes save_before_dump=True, 271*e1fe3e4aSElliott Hughes ) 272*e1fe3e4aSElliott Hughes 273*e1fe3e4aSElliott Hughes def test_varlib_build_feature_variations_whole_range(self): 274*e1fe3e4aSElliott Hughes """Designspace file contains <rules> element specifying the entire design 275*e1fe3e4aSElliott Hughes space, used to build GSUB FeatureVariations table. 276*e1fe3e4aSElliott Hughes """ 277*e1fe3e4aSElliott Hughes self._run_varlib_build_test( 278*e1fe3e4aSElliott Hughes designspace_name="FeatureVarsWholeRange", 279*e1fe3e4aSElliott Hughes font_name="TestFamily", 280*e1fe3e4aSElliott Hughes tables=["fvar", "GSUB"], 281*e1fe3e4aSElliott Hughes expected_ttx_name="FeatureVarsWholeRange", 282*e1fe3e4aSElliott Hughes save_before_dump=True, 283*e1fe3e4aSElliott Hughes ) 284*e1fe3e4aSElliott Hughes 285*e1fe3e4aSElliott Hughes def test_varlib_build_feature_variations_whole_range_empty(self): 286*e1fe3e4aSElliott Hughes """Designspace file contains <rules> element without a condition, specifying 287*e1fe3e4aSElliott Hughes the entire design space, used to build GSUB FeatureVariations table. 288*e1fe3e4aSElliott Hughes """ 289*e1fe3e4aSElliott Hughes self._run_varlib_build_test( 290*e1fe3e4aSElliott Hughes designspace_name="FeatureVarsWholeRangeEmpty", 291*e1fe3e4aSElliott Hughes font_name="TestFamily", 292*e1fe3e4aSElliott Hughes tables=["fvar", "GSUB"], 293*e1fe3e4aSElliott Hughes expected_ttx_name="FeatureVarsWholeRange", 294*e1fe3e4aSElliott Hughes save_before_dump=True, 295*e1fe3e4aSElliott Hughes ) 296*e1fe3e4aSElliott Hughes 297*e1fe3e4aSElliott Hughes def test_varlib_build_feature_variations_with_existing_rclt(self): 298*e1fe3e4aSElliott Hughes """Designspace file contains <rules> element, used to build GSUB 299*e1fe3e4aSElliott Hughes FeatureVariations table. <rules> is specified to do its OT processing 300*e1fe3e4aSElliott Hughes "last", so a 'rclt' feature will be used or created. This test covers 301*e1fe3e4aSElliott Hughes the case when a 'rclt' already exists in the masters. 302*e1fe3e4aSElliott Hughes 303*e1fe3e4aSElliott Hughes We dynamically add a 'rclt' feature to an existing set of test 304*e1fe3e4aSElliott Hughes masters, to avoid adding more test data. 305*e1fe3e4aSElliott Hughes 306*e1fe3e4aSElliott Hughes The multiple languages are done to verify whether multiple existing 307*e1fe3e4aSElliott Hughes 'rclt' features are updated correctly. 308*e1fe3e4aSElliott Hughes """ 309*e1fe3e4aSElliott Hughes 310*e1fe3e4aSElliott Hughes def add_rclt(font, savepath): 311*e1fe3e4aSElliott Hughes features = """ 312*e1fe3e4aSElliott Hughes languagesystem DFLT dflt; 313*e1fe3e4aSElliott Hughes languagesystem latn dflt; 314*e1fe3e4aSElliott Hughes languagesystem latn NLD; 315*e1fe3e4aSElliott Hughes 316*e1fe3e4aSElliott Hughes feature rclt { 317*e1fe3e4aSElliott Hughes script latn; 318*e1fe3e4aSElliott Hughes language NLD; 319*e1fe3e4aSElliott Hughes lookup A { 320*e1fe3e4aSElliott Hughes sub uni0041 by uni0061; 321*e1fe3e4aSElliott Hughes } A; 322*e1fe3e4aSElliott Hughes language dflt; 323*e1fe3e4aSElliott Hughes lookup B { 324*e1fe3e4aSElliott Hughes sub uni0041 by uni0061; 325*e1fe3e4aSElliott Hughes } B; 326*e1fe3e4aSElliott Hughes } rclt; 327*e1fe3e4aSElliott Hughes """ 328*e1fe3e4aSElliott Hughes addOpenTypeFeaturesFromString(font, features) 329*e1fe3e4aSElliott Hughes font.save(savepath) 330*e1fe3e4aSElliott Hughes 331*e1fe3e4aSElliott Hughes self._run_varlib_build_test( 332*e1fe3e4aSElliott Hughes designspace_name="FeatureVars", 333*e1fe3e4aSElliott Hughes font_name="TestFamily", 334*e1fe3e4aSElliott Hughes tables=["fvar", "GSUB"], 335*e1fe3e4aSElliott Hughes expected_ttx_name="FeatureVars_rclt", 336*e1fe3e4aSElliott Hughes save_before_dump=True, 337*e1fe3e4aSElliott Hughes post_process_master=add_rclt, 338*e1fe3e4aSElliott Hughes ) 339*e1fe3e4aSElliott Hughes 340*e1fe3e4aSElliott Hughes def test_varlib_gvar_explicit_delta(self): 341*e1fe3e4aSElliott Hughes """The variable font contains a composite glyph odieresis which does not 342*e1fe3e4aSElliott Hughes need a gvar entry, because all its deltas are 0, but it must be added 343*e1fe3e4aSElliott Hughes anyway to work around an issue with macOS 10.14. 344*e1fe3e4aSElliott Hughes 345*e1fe3e4aSElliott Hughes https://github.com/fonttools/fonttools/issues/1381 346*e1fe3e4aSElliott Hughes """ 347*e1fe3e4aSElliott Hughes test_name = "BuildGvarCompositeExplicitDelta" 348*e1fe3e4aSElliott Hughes self._run_varlib_build_test( 349*e1fe3e4aSElliott Hughes designspace_name=test_name, 350*e1fe3e4aSElliott Hughes font_name="TestFamily4", 351*e1fe3e4aSElliott Hughes tables=["gvar"], 352*e1fe3e4aSElliott Hughes expected_ttx_name=test_name, 353*e1fe3e4aSElliott Hughes ) 354*e1fe3e4aSElliott Hughes 355*e1fe3e4aSElliott Hughes def test_varlib_nonmarking_CFF2(self): 356*e1fe3e4aSElliott Hughes self.temp_dir() 357*e1fe3e4aSElliott Hughes 358*e1fe3e4aSElliott Hughes ds_path = self.get_test_input("TestNonMarkingCFF2.designspace", copy=True) 359*e1fe3e4aSElliott Hughes ttx_dir = self.get_test_input("master_non_marking_cff2") 360*e1fe3e4aSElliott Hughes expected_ttx_path = self.get_test_output("TestNonMarkingCFF2.ttx") 361*e1fe3e4aSElliott Hughes 362*e1fe3e4aSElliott Hughes for path in self.get_file_list(ttx_dir, ".ttx", "TestNonMarkingCFF2_"): 363*e1fe3e4aSElliott Hughes self.compile_font(path, ".otf", self.tempdir) 364*e1fe3e4aSElliott Hughes 365*e1fe3e4aSElliott Hughes ds = DesignSpaceDocument.fromfile(ds_path) 366*e1fe3e4aSElliott Hughes for source in ds.sources: 367*e1fe3e4aSElliott Hughes source.path = os.path.join( 368*e1fe3e4aSElliott Hughes self.tempdir, os.path.basename(source.filename).replace(".ufo", ".otf") 369*e1fe3e4aSElliott Hughes ) 370*e1fe3e4aSElliott Hughes ds.updatePaths() 371*e1fe3e4aSElliott Hughes 372*e1fe3e4aSElliott Hughes varfont, _, _ = build(ds) 373*e1fe3e4aSElliott Hughes varfont = reload_font(varfont) 374*e1fe3e4aSElliott Hughes 375*e1fe3e4aSElliott Hughes tables = ["CFF2"] 376*e1fe3e4aSElliott Hughes self.expect_ttx(varfont, expected_ttx_path, tables) 377*e1fe3e4aSElliott Hughes 378*e1fe3e4aSElliott Hughes def test_varlib_build_CFF2(self): 379*e1fe3e4aSElliott Hughes self.temp_dir() 380*e1fe3e4aSElliott Hughes 381*e1fe3e4aSElliott Hughes ds_path = self.get_test_input("TestCFF2.designspace", copy=True) 382*e1fe3e4aSElliott Hughes ttx_dir = self.get_test_input("master_cff2") 383*e1fe3e4aSElliott Hughes expected_ttx_path = self.get_test_output("BuildTestCFF2.ttx") 384*e1fe3e4aSElliott Hughes 385*e1fe3e4aSElliott Hughes for path in self.get_file_list(ttx_dir, ".ttx", "TestCFF2_"): 386*e1fe3e4aSElliott Hughes self.compile_font(path, ".otf", self.tempdir) 387*e1fe3e4aSElliott Hughes 388*e1fe3e4aSElliott Hughes ds = DesignSpaceDocument.fromfile(ds_path) 389*e1fe3e4aSElliott Hughes for source in ds.sources: 390*e1fe3e4aSElliott Hughes source.path = os.path.join( 391*e1fe3e4aSElliott Hughes self.tempdir, os.path.basename(source.filename).replace(".ufo", ".otf") 392*e1fe3e4aSElliott Hughes ) 393*e1fe3e4aSElliott Hughes ds.updatePaths() 394*e1fe3e4aSElliott Hughes 395*e1fe3e4aSElliott Hughes varfont, _, _ = build(ds) 396*e1fe3e4aSElliott Hughes varfont = reload_font(varfont) 397*e1fe3e4aSElliott Hughes 398*e1fe3e4aSElliott Hughes tables = ["fvar", "CFF2"] 399*e1fe3e4aSElliott Hughes self.expect_ttx(varfont, expected_ttx_path, tables) 400*e1fe3e4aSElliott Hughes 401*e1fe3e4aSElliott Hughes def test_varlib_build_CFF2_from_CFF2(self): 402*e1fe3e4aSElliott Hughes self.temp_dir() 403*e1fe3e4aSElliott Hughes 404*e1fe3e4aSElliott Hughes ds_path = self.get_test_input("TestCFF2Input.designspace", copy=True) 405*e1fe3e4aSElliott Hughes ttx_dir = self.get_test_input("master_cff2_input") 406*e1fe3e4aSElliott Hughes expected_ttx_path = self.get_test_output("BuildTestCFF2.ttx") 407*e1fe3e4aSElliott Hughes 408*e1fe3e4aSElliott Hughes for path in self.get_file_list(ttx_dir, ".ttx", "TestCFF2_"): 409*e1fe3e4aSElliott Hughes self.compile_font(path, ".otf", self.tempdir) 410*e1fe3e4aSElliott Hughes 411*e1fe3e4aSElliott Hughes ds = DesignSpaceDocument.fromfile(ds_path) 412*e1fe3e4aSElliott Hughes for source in ds.sources: 413*e1fe3e4aSElliott Hughes source.path = os.path.join( 414*e1fe3e4aSElliott Hughes self.tempdir, os.path.basename(source.filename).replace(".ufo", ".otf") 415*e1fe3e4aSElliott Hughes ) 416*e1fe3e4aSElliott Hughes ds.updatePaths() 417*e1fe3e4aSElliott Hughes 418*e1fe3e4aSElliott Hughes varfont, _, _ = build(ds) 419*e1fe3e4aSElliott Hughes varfont = reload_font(varfont) 420*e1fe3e4aSElliott Hughes 421*e1fe3e4aSElliott Hughes tables = ["fvar", "CFF2"] 422*e1fe3e4aSElliott Hughes self.expect_ttx(varfont, expected_ttx_path, tables) 423*e1fe3e4aSElliott Hughes 424*e1fe3e4aSElliott Hughes def test_varlib_build_sparse_CFF2(self): 425*e1fe3e4aSElliott Hughes self.temp_dir() 426*e1fe3e4aSElliott Hughes 427*e1fe3e4aSElliott Hughes ds_path = self.get_test_input("TestSparseCFF2VF.designspace", copy=True) 428*e1fe3e4aSElliott Hughes ttx_dir = self.get_test_input("master_sparse_cff2") 429*e1fe3e4aSElliott Hughes expected_ttx_path = self.get_test_output("TestSparseCFF2VF.ttx") 430*e1fe3e4aSElliott Hughes 431*e1fe3e4aSElliott Hughes for path in self.get_file_list(ttx_dir, ".ttx", "MasterSet_Kanji-"): 432*e1fe3e4aSElliott Hughes self.compile_font(path, ".otf", self.tempdir) 433*e1fe3e4aSElliott Hughes 434*e1fe3e4aSElliott Hughes ds = DesignSpaceDocument.fromfile(ds_path) 435*e1fe3e4aSElliott Hughes for source in ds.sources: 436*e1fe3e4aSElliott Hughes source.path = os.path.join( 437*e1fe3e4aSElliott Hughes self.tempdir, os.path.basename(source.filename).replace(".ufo", ".otf") 438*e1fe3e4aSElliott Hughes ) 439*e1fe3e4aSElliott Hughes ds.updatePaths() 440*e1fe3e4aSElliott Hughes 441*e1fe3e4aSElliott Hughes varfont, _, _ = build(ds) 442*e1fe3e4aSElliott Hughes varfont = reload_font(varfont) 443*e1fe3e4aSElliott Hughes 444*e1fe3e4aSElliott Hughes tables = ["fvar", "CFF2"] 445*e1fe3e4aSElliott Hughes self.expect_ttx(varfont, expected_ttx_path, tables) 446*e1fe3e4aSElliott Hughes 447*e1fe3e4aSElliott Hughes def test_varlib_build_vpal(self): 448*e1fe3e4aSElliott Hughes self.temp_dir() 449*e1fe3e4aSElliott Hughes 450*e1fe3e4aSElliott Hughes ds_path = self.get_test_input("test_vpal.designspace", copy=True) 451*e1fe3e4aSElliott Hughes ttx_dir = self.get_test_input("master_vpal_test") 452*e1fe3e4aSElliott Hughes expected_ttx_path = self.get_test_output("test_vpal.ttx") 453*e1fe3e4aSElliott Hughes 454*e1fe3e4aSElliott Hughes for path in self.get_file_list(ttx_dir, ".ttx", "master_vpal_test_"): 455*e1fe3e4aSElliott Hughes self.compile_font(path, ".otf", self.tempdir) 456*e1fe3e4aSElliott Hughes 457*e1fe3e4aSElliott Hughes ds = DesignSpaceDocument.fromfile(ds_path) 458*e1fe3e4aSElliott Hughes for source in ds.sources: 459*e1fe3e4aSElliott Hughes source.path = os.path.join( 460*e1fe3e4aSElliott Hughes self.tempdir, os.path.basename(source.filename).replace(".ufo", ".otf") 461*e1fe3e4aSElliott Hughes ) 462*e1fe3e4aSElliott Hughes ds.updatePaths() 463*e1fe3e4aSElliott Hughes 464*e1fe3e4aSElliott Hughes varfont, _, _ = build(ds) 465*e1fe3e4aSElliott Hughes varfont = reload_font(varfont) 466*e1fe3e4aSElliott Hughes 467*e1fe3e4aSElliott Hughes tables = ["GPOS"] 468*e1fe3e4aSElliott Hughes self.expect_ttx(varfont, expected_ttx_path, tables) 469*e1fe3e4aSElliott Hughes 470*e1fe3e4aSElliott Hughes def test_varlib_main_ttf(self): 471*e1fe3e4aSElliott Hughes """Mostly for testing varLib.main()""" 472*e1fe3e4aSElliott Hughes suffix = ".ttf" 473*e1fe3e4aSElliott Hughes ds_path = self.get_test_input("Build.designspace") 474*e1fe3e4aSElliott Hughes ttx_dir = self.get_test_input("master_ttx_interpolatable_ttf") 475*e1fe3e4aSElliott Hughes 476*e1fe3e4aSElliott Hughes self.temp_dir() 477*e1fe3e4aSElliott Hughes ttf_dir = os.path.join(self.tempdir, "master_ttf_interpolatable") 478*e1fe3e4aSElliott Hughes os.makedirs(ttf_dir) 479*e1fe3e4aSElliott Hughes ttx_paths = self.get_file_list(ttx_dir, ".ttx", "TestFamily-") 480*e1fe3e4aSElliott Hughes for path in ttx_paths: 481*e1fe3e4aSElliott Hughes self.compile_font(path, suffix, ttf_dir) 482*e1fe3e4aSElliott Hughes 483*e1fe3e4aSElliott Hughes ds_copy = os.path.join(self.tempdir, "BuildMain.designspace") 484*e1fe3e4aSElliott Hughes shutil.copy2(ds_path, ds_copy) 485*e1fe3e4aSElliott Hughes 486*e1fe3e4aSElliott Hughes # by default, varLib.main finds master TTFs inside a 487*e1fe3e4aSElliott Hughes # 'master_ttf_interpolatable' subfolder in current working dir 488*e1fe3e4aSElliott Hughes cwd = os.getcwd() 489*e1fe3e4aSElliott Hughes os.chdir(self.tempdir) 490*e1fe3e4aSElliott Hughes try: 491*e1fe3e4aSElliott Hughes varLib_main([ds_copy]) 492*e1fe3e4aSElliott Hughes finally: 493*e1fe3e4aSElliott Hughes os.chdir(cwd) 494*e1fe3e4aSElliott Hughes 495*e1fe3e4aSElliott Hughes varfont_path = os.path.splitext(ds_copy)[0] + "-VF" + suffix 496*e1fe3e4aSElliott Hughes self.assertTrue(os.path.exists(varfont_path)) 497*e1fe3e4aSElliott Hughes 498*e1fe3e4aSElliott Hughes # try again passing an explicit --master-finder 499*e1fe3e4aSElliott Hughes os.remove(varfont_path) 500*e1fe3e4aSElliott Hughes finder = "%s/master_ttf_interpolatable/{stem}.ttf" % self.tempdir 501*e1fe3e4aSElliott Hughes varLib_main([ds_copy, "--master-finder", finder]) 502*e1fe3e4aSElliott Hughes self.assertTrue(os.path.exists(varfont_path)) 503*e1fe3e4aSElliott Hughes 504*e1fe3e4aSElliott Hughes # and also with explicit -o output option 505*e1fe3e4aSElliott Hughes os.remove(varfont_path) 506*e1fe3e4aSElliott Hughes varfont_path = os.path.splitext(varfont_path)[0] + "-o" + suffix 507*e1fe3e4aSElliott Hughes varLib_main([ds_copy, "-o", varfont_path, "--master-finder", finder]) 508*e1fe3e4aSElliott Hughes self.assertTrue(os.path.exists(varfont_path)) 509*e1fe3e4aSElliott Hughes 510*e1fe3e4aSElliott Hughes varfont = TTFont(varfont_path) 511*e1fe3e4aSElliott Hughes tables = [table_tag for table_tag in varfont.keys() if table_tag != "head"] 512*e1fe3e4aSElliott Hughes expected_ttx_path = self.get_test_output("BuildMain.ttx") 513*e1fe3e4aSElliott Hughes self.expect_ttx(varfont, expected_ttx_path, tables) 514*e1fe3e4aSElliott Hughes 515*e1fe3e4aSElliott Hughes def test_varLib_main_output_dir(self): 516*e1fe3e4aSElliott Hughes self.temp_dir() 517*e1fe3e4aSElliott Hughes outdir = os.path.join(self.tempdir, "output_dir_test") 518*e1fe3e4aSElliott Hughes self.assertFalse(os.path.exists(outdir)) 519*e1fe3e4aSElliott Hughes 520*e1fe3e4aSElliott Hughes ds_path = os.path.join(self.tempdir, "BuildMain.designspace") 521*e1fe3e4aSElliott Hughes shutil.copy2(self.get_test_input("Build.designspace"), ds_path) 522*e1fe3e4aSElliott Hughes 523*e1fe3e4aSElliott Hughes shutil.copytree( 524*e1fe3e4aSElliott Hughes self.get_test_input("master_ttx_interpolatable_ttf"), 525*e1fe3e4aSElliott Hughes os.path.join(outdir, "master_ttx"), 526*e1fe3e4aSElliott Hughes ) 527*e1fe3e4aSElliott Hughes 528*e1fe3e4aSElliott Hughes finder = "%s/output_dir_test/master_ttx/{stem}.ttx" % self.tempdir 529*e1fe3e4aSElliott Hughes 530*e1fe3e4aSElliott Hughes varLib_main([ds_path, "--output-dir", outdir, "--master-finder", finder]) 531*e1fe3e4aSElliott Hughes 532*e1fe3e4aSElliott Hughes self.assertTrue(os.path.isdir(outdir)) 533*e1fe3e4aSElliott Hughes self.assertTrue(os.path.exists(os.path.join(outdir, "BuildMain-VF.ttf"))) 534*e1fe3e4aSElliott Hughes 535*e1fe3e4aSElliott Hughes def test_varLib_main_filter_variable_fonts(self): 536*e1fe3e4aSElliott Hughes self.temp_dir() 537*e1fe3e4aSElliott Hughes outdir = os.path.join(self.tempdir, "filter_variable_fonts_test") 538*e1fe3e4aSElliott Hughes self.assertFalse(os.path.exists(outdir)) 539*e1fe3e4aSElliott Hughes 540*e1fe3e4aSElliott Hughes ds_path = os.path.join(self.tempdir, "BuildMain.designspace") 541*e1fe3e4aSElliott Hughes shutil.copy2(self.get_test_input("Build.designspace"), ds_path) 542*e1fe3e4aSElliott Hughes 543*e1fe3e4aSElliott Hughes shutil.copytree( 544*e1fe3e4aSElliott Hughes self.get_test_input("master_ttx_interpolatable_ttf"), 545*e1fe3e4aSElliott Hughes os.path.join(outdir, "master_ttx"), 546*e1fe3e4aSElliott Hughes ) 547*e1fe3e4aSElliott Hughes 548*e1fe3e4aSElliott Hughes finder = "%s/filter_variable_fonts_test/master_ttx/{stem}.ttx" % self.tempdir 549*e1fe3e4aSElliott Hughes 550*e1fe3e4aSElliott Hughes cmd = [ds_path, "--output-dir", outdir, "--master-finder", finder] 551*e1fe3e4aSElliott Hughes 552*e1fe3e4aSElliott Hughes with pytest.raises(SystemExit): 553*e1fe3e4aSElliott Hughes varLib_main(cmd + ["--variable-fonts", "FooBar"]) # no font matches 554*e1fe3e4aSElliott Hughes 555*e1fe3e4aSElliott Hughes varLib_main(cmd + ["--variable-fonts", "Build.*"]) # this does match 556*e1fe3e4aSElliott Hughes 557*e1fe3e4aSElliott Hughes self.assertTrue(os.path.isdir(outdir)) 558*e1fe3e4aSElliott Hughes self.assertTrue(os.path.exists(os.path.join(outdir, "BuildMain-VF.ttf"))) 559*e1fe3e4aSElliott Hughes 560*e1fe3e4aSElliott Hughes def test_varLib_main_drop_implied_oncurves(self): 561*e1fe3e4aSElliott Hughes self.temp_dir() 562*e1fe3e4aSElliott Hughes outdir = os.path.join(self.tempdir, "drop_implied_oncurves_test") 563*e1fe3e4aSElliott Hughes self.assertFalse(os.path.exists(outdir)) 564*e1fe3e4aSElliott Hughes 565*e1fe3e4aSElliott Hughes ttf_dir = os.path.join(outdir, "master_ttf_interpolatable") 566*e1fe3e4aSElliott Hughes os.makedirs(ttf_dir) 567*e1fe3e4aSElliott Hughes ttx_dir = self.get_test_input("master_ttx_drop_oncurves") 568*e1fe3e4aSElliott Hughes ttx_paths = self.get_file_list(ttx_dir, ".ttx", "TestFamily-") 569*e1fe3e4aSElliott Hughes for path in ttx_paths: 570*e1fe3e4aSElliott Hughes self.compile_font(path, ".ttf", ttf_dir) 571*e1fe3e4aSElliott Hughes 572*e1fe3e4aSElliott Hughes ds_copy = os.path.join(outdir, "DropOnCurves.designspace") 573*e1fe3e4aSElliott Hughes ds_path = self.get_test_input("DropOnCurves.designspace") 574*e1fe3e4aSElliott Hughes shutil.copy2(ds_path, ds_copy) 575*e1fe3e4aSElliott Hughes 576*e1fe3e4aSElliott Hughes finder = "%s/master_ttf_interpolatable/{stem}.ttf" % outdir 577*e1fe3e4aSElliott Hughes varLib_main([ds_copy, "--master-finder", finder, "--drop-implied-oncurves"]) 578*e1fe3e4aSElliott Hughes 579*e1fe3e4aSElliott Hughes vf_path = os.path.join(outdir, "DropOnCurves-VF.ttf") 580*e1fe3e4aSElliott Hughes varfont = TTFont(vf_path) 581*e1fe3e4aSElliott Hughes tables = [table_tag for table_tag in varfont.keys() if table_tag != "head"] 582*e1fe3e4aSElliott Hughes expected_ttx_path = self.get_test_output("DropOnCurves.ttx") 583*e1fe3e4aSElliott Hughes self.expect_ttx(varfont, expected_ttx_path, tables) 584*e1fe3e4aSElliott Hughes 585*e1fe3e4aSElliott Hughes def test_varLib_build_many_no_overwrite_STAT(self): 586*e1fe3e4aSElliott Hughes # Ensure that varLib.build_many doesn't overwrite a pre-existing STAT table, 587*e1fe3e4aSElliott Hughes # e.g. one built by feaLib from features.fea; the VF simply should inherit the 588*e1fe3e4aSElliott Hughes # STAT from the base master: https://github.com/googlefonts/fontmake/issues/985 589*e1fe3e4aSElliott Hughes base_master = TTFont() 590*e1fe3e4aSElliott Hughes base_master.importXML( 591*e1fe3e4aSElliott Hughes self.get_test_input("master_no_overwrite_stat/Test-CondensedThin.ttx") 592*e1fe3e4aSElliott Hughes ) 593*e1fe3e4aSElliott Hughes assert "STAT" in base_master 594*e1fe3e4aSElliott Hughes 595*e1fe3e4aSElliott Hughes vf = next( 596*e1fe3e4aSElliott Hughes iter( 597*e1fe3e4aSElliott Hughes build_many( 598*e1fe3e4aSElliott Hughes DesignSpaceDocument.fromfile( 599*e1fe3e4aSElliott Hughes self.get_test_input("TestNoOverwriteSTAT.designspace") 600*e1fe3e4aSElliott Hughes ) 601*e1fe3e4aSElliott Hughes ).values() 602*e1fe3e4aSElliott Hughes ) 603*e1fe3e4aSElliott Hughes ) 604*e1fe3e4aSElliott Hughes assert "STAT" in vf 605*e1fe3e4aSElliott Hughes 606*e1fe3e4aSElliott Hughes assert vf["STAT"].table == base_master["STAT"].table 607*e1fe3e4aSElliott Hughes 608*e1fe3e4aSElliott Hughes def test_varlib_build_from_ds_object_in_memory_ttfonts(self): 609*e1fe3e4aSElliott Hughes ds_path = self.get_test_input("Build.designspace") 610*e1fe3e4aSElliott Hughes ttx_dir = self.get_test_input("master_ttx_interpolatable_ttf") 611*e1fe3e4aSElliott Hughes expected_ttx_path = self.get_test_output("BuildMain.ttx") 612*e1fe3e4aSElliott Hughes 613*e1fe3e4aSElliott Hughes self.temp_dir() 614*e1fe3e4aSElliott Hughes for path in self.get_file_list(ttx_dir, ".ttx", "TestFamily-"): 615*e1fe3e4aSElliott Hughes self.compile_font(path, ".ttf", self.tempdir) 616*e1fe3e4aSElliott Hughes 617*e1fe3e4aSElliott Hughes ds = DesignSpaceDocument.fromfile(ds_path) 618*e1fe3e4aSElliott Hughes for source in ds.sources: 619*e1fe3e4aSElliott Hughes filename = os.path.join( 620*e1fe3e4aSElliott Hughes self.tempdir, os.path.basename(source.filename).replace(".ufo", ".ttf") 621*e1fe3e4aSElliott Hughes ) 622*e1fe3e4aSElliott Hughes source.font = TTFont( 623*e1fe3e4aSElliott Hughes filename, recalcBBoxes=False, recalcTimestamp=False, lazy=True 624*e1fe3e4aSElliott Hughes ) 625*e1fe3e4aSElliott Hughes source.filename = None # Make sure no file path gets into build() 626*e1fe3e4aSElliott Hughes 627*e1fe3e4aSElliott Hughes varfont, _, _ = build(ds) 628*e1fe3e4aSElliott Hughes varfont = reload_font(varfont) 629*e1fe3e4aSElliott Hughes tables = [table_tag for table_tag in varfont.keys() if table_tag != "head"] 630*e1fe3e4aSElliott Hughes self.expect_ttx(varfont, expected_ttx_path, tables) 631*e1fe3e4aSElliott Hughes 632*e1fe3e4aSElliott Hughes def test_varlib_build_from_ttf_paths(self): 633*e1fe3e4aSElliott Hughes self.temp_dir() 634*e1fe3e4aSElliott Hughes 635*e1fe3e4aSElliott Hughes ds_path = self.get_test_input("Build.designspace", copy=True) 636*e1fe3e4aSElliott Hughes ttx_dir = self.get_test_input("master_ttx_interpolatable_ttf") 637*e1fe3e4aSElliott Hughes expected_ttx_path = self.get_test_output("BuildMain.ttx") 638*e1fe3e4aSElliott Hughes 639*e1fe3e4aSElliott Hughes for path in self.get_file_list(ttx_dir, ".ttx", "TestFamily-"): 640*e1fe3e4aSElliott Hughes self.compile_font(path, ".ttf", self.tempdir) 641*e1fe3e4aSElliott Hughes 642*e1fe3e4aSElliott Hughes ds = DesignSpaceDocument.fromfile(ds_path) 643*e1fe3e4aSElliott Hughes for source in ds.sources: 644*e1fe3e4aSElliott Hughes source.path = os.path.join( 645*e1fe3e4aSElliott Hughes self.tempdir, os.path.basename(source.filename).replace(".ufo", ".ttf") 646*e1fe3e4aSElliott Hughes ) 647*e1fe3e4aSElliott Hughes ds.updatePaths() 648*e1fe3e4aSElliott Hughes 649*e1fe3e4aSElliott Hughes varfont, _, _ = build(ds) 650*e1fe3e4aSElliott Hughes varfont = reload_font(varfont) 651*e1fe3e4aSElliott Hughes tables = [table_tag for table_tag in varfont.keys() if table_tag != "head"] 652*e1fe3e4aSElliott Hughes self.expect_ttx(varfont, expected_ttx_path, tables) 653*e1fe3e4aSElliott Hughes 654*e1fe3e4aSElliott Hughes def test_varlib_build_from_ttx_paths(self): 655*e1fe3e4aSElliott Hughes ds_path = self.get_test_input("Build.designspace") 656*e1fe3e4aSElliott Hughes ttx_dir = self.get_test_input("master_ttx_interpolatable_ttf") 657*e1fe3e4aSElliott Hughes expected_ttx_path = self.get_test_output("BuildMain.ttx") 658*e1fe3e4aSElliott Hughes 659*e1fe3e4aSElliott Hughes ds = DesignSpaceDocument.fromfile(ds_path) 660*e1fe3e4aSElliott Hughes for source in ds.sources: 661*e1fe3e4aSElliott Hughes source.path = os.path.join( 662*e1fe3e4aSElliott Hughes ttx_dir, os.path.basename(source.filename).replace(".ufo", ".ttx") 663*e1fe3e4aSElliott Hughes ) 664*e1fe3e4aSElliott Hughes ds.updatePaths() 665*e1fe3e4aSElliott Hughes 666*e1fe3e4aSElliott Hughes varfont, _, _ = build(ds) 667*e1fe3e4aSElliott Hughes varfont = reload_font(varfont) 668*e1fe3e4aSElliott Hughes tables = [table_tag for table_tag in varfont.keys() if table_tag != "head"] 669*e1fe3e4aSElliott Hughes self.expect_ttx(varfont, expected_ttx_path, tables) 670*e1fe3e4aSElliott Hughes 671*e1fe3e4aSElliott Hughes def test_varlib_build_sparse_masters(self): 672*e1fe3e4aSElliott Hughes ds_path = self.get_test_input("SparseMasters.designspace") 673*e1fe3e4aSElliott Hughes expected_ttx_path = self.get_test_output("SparseMasters.ttx") 674*e1fe3e4aSElliott Hughes 675*e1fe3e4aSElliott Hughes varfont, _, _ = build(ds_path) 676*e1fe3e4aSElliott Hughes varfont = reload_font(varfont) 677*e1fe3e4aSElliott Hughes tables = [table_tag for table_tag in varfont.keys() if table_tag != "head"] 678*e1fe3e4aSElliott Hughes self.expect_ttx(varfont, expected_ttx_path, tables) 679*e1fe3e4aSElliott Hughes 680*e1fe3e4aSElliott Hughes def test_varlib_build_lazy_masters(self): 681*e1fe3e4aSElliott Hughes # See https://github.com/fonttools/fonttools/issues/1808 682*e1fe3e4aSElliott Hughes ds_path = self.get_test_input("SparseMasters.designspace") 683*e1fe3e4aSElliott Hughes expected_ttx_path = self.get_test_output("SparseMasters.ttx") 684*e1fe3e4aSElliott Hughes 685*e1fe3e4aSElliott Hughes def _open_font(master_path, master_finder=lambda s: s): 686*e1fe3e4aSElliott Hughes font = TTFont() 687*e1fe3e4aSElliott Hughes font.importXML(master_path) 688*e1fe3e4aSElliott Hughes buf = BytesIO() 689*e1fe3e4aSElliott Hughes font.save(buf, reorderTables=False) 690*e1fe3e4aSElliott Hughes buf.seek(0) 691*e1fe3e4aSElliott Hughes font = TTFont(buf, lazy=True) # reopen in lazy mode, to reproduce #1808 692*e1fe3e4aSElliott Hughes return font 693*e1fe3e4aSElliott Hughes 694*e1fe3e4aSElliott Hughes ds = DesignSpaceDocument.fromfile(ds_path) 695*e1fe3e4aSElliott Hughes ds.loadSourceFonts(_open_font) 696*e1fe3e4aSElliott Hughes varfont, _, _ = build(ds) 697*e1fe3e4aSElliott Hughes varfont = reload_font(varfont) 698*e1fe3e4aSElliott Hughes tables = [table_tag for table_tag in varfont.keys() if table_tag != "head"] 699*e1fe3e4aSElliott Hughes self.expect_ttx(varfont, expected_ttx_path, tables) 700*e1fe3e4aSElliott Hughes 701*e1fe3e4aSElliott Hughes def test_varlib_build_sparse_masters_MVAR(self): 702*e1fe3e4aSElliott Hughes import fontTools.varLib.mvar 703*e1fe3e4aSElliott Hughes 704*e1fe3e4aSElliott Hughes ds_path = self.get_test_input("SparseMasters.designspace") 705*e1fe3e4aSElliott Hughes ds = DesignSpaceDocument.fromfile(ds_path) 706*e1fe3e4aSElliott Hughes load_masters(ds) 707*e1fe3e4aSElliott Hughes 708*e1fe3e4aSElliott Hughes # Trigger MVAR generation so varLib is forced to create deltas with a 709*e1fe3e4aSElliott Hughes # sparse master inbetween. 710*e1fe3e4aSElliott Hughes font_0_os2 = ds.sources[0].font["OS/2"] 711*e1fe3e4aSElliott Hughes font_0_os2.sTypoAscender = 1 712*e1fe3e4aSElliott Hughes font_0_os2.sTypoDescender = 1 713*e1fe3e4aSElliott Hughes font_0_os2.sTypoLineGap = 1 714*e1fe3e4aSElliott Hughes font_0_os2.usWinAscent = 1 715*e1fe3e4aSElliott Hughes font_0_os2.usWinDescent = 1 716*e1fe3e4aSElliott Hughes font_0_os2.sxHeight = 1 717*e1fe3e4aSElliott Hughes font_0_os2.sCapHeight = 1 718*e1fe3e4aSElliott Hughes font_0_os2.ySubscriptXSize = 1 719*e1fe3e4aSElliott Hughes font_0_os2.ySubscriptYSize = 1 720*e1fe3e4aSElliott Hughes font_0_os2.ySubscriptXOffset = 1 721*e1fe3e4aSElliott Hughes font_0_os2.ySubscriptYOffset = 1 722*e1fe3e4aSElliott Hughes font_0_os2.ySuperscriptXSize = 1 723*e1fe3e4aSElliott Hughes font_0_os2.ySuperscriptYSize = 1 724*e1fe3e4aSElliott Hughes font_0_os2.ySuperscriptXOffset = 1 725*e1fe3e4aSElliott Hughes font_0_os2.ySuperscriptYOffset = 1 726*e1fe3e4aSElliott Hughes font_0_os2.yStrikeoutSize = 1 727*e1fe3e4aSElliott Hughes font_0_os2.yStrikeoutPosition = 1 728*e1fe3e4aSElliott Hughes font_0_vhea = newTable("vhea") 729*e1fe3e4aSElliott Hughes font_0_vhea.ascent = 1 730*e1fe3e4aSElliott Hughes font_0_vhea.descent = 1 731*e1fe3e4aSElliott Hughes font_0_vhea.lineGap = 1 732*e1fe3e4aSElliott Hughes font_0_vhea.caretSlopeRise = 1 733*e1fe3e4aSElliott Hughes font_0_vhea.caretSlopeRun = 1 734*e1fe3e4aSElliott Hughes font_0_vhea.caretOffset = 1 735*e1fe3e4aSElliott Hughes ds.sources[0].font["vhea"] = font_0_vhea 736*e1fe3e4aSElliott Hughes font_0_hhea = ds.sources[0].font["hhea"] 737*e1fe3e4aSElliott Hughes font_0_hhea.caretSlopeRise = 1 738*e1fe3e4aSElliott Hughes font_0_hhea.caretSlopeRun = 1 739*e1fe3e4aSElliott Hughes font_0_hhea.caretOffset = 1 740*e1fe3e4aSElliott Hughes font_0_post = ds.sources[0].font["post"] 741*e1fe3e4aSElliott Hughes font_0_post.underlineThickness = 1 742*e1fe3e4aSElliott Hughes font_0_post.underlinePosition = 1 743*e1fe3e4aSElliott Hughes 744*e1fe3e4aSElliott Hughes font_2_os2 = ds.sources[2].font["OS/2"] 745*e1fe3e4aSElliott Hughes font_2_os2.sTypoAscender = 800 746*e1fe3e4aSElliott Hughes font_2_os2.sTypoDescender = 800 747*e1fe3e4aSElliott Hughes font_2_os2.sTypoLineGap = 800 748*e1fe3e4aSElliott Hughes font_2_os2.usWinAscent = 800 749*e1fe3e4aSElliott Hughes font_2_os2.usWinDescent = 800 750*e1fe3e4aSElliott Hughes font_2_os2.sxHeight = 800 751*e1fe3e4aSElliott Hughes font_2_os2.sCapHeight = 800 752*e1fe3e4aSElliott Hughes font_2_os2.ySubscriptXSize = 800 753*e1fe3e4aSElliott Hughes font_2_os2.ySubscriptYSize = 800 754*e1fe3e4aSElliott Hughes font_2_os2.ySubscriptXOffset = 800 755*e1fe3e4aSElliott Hughes font_2_os2.ySubscriptYOffset = 800 756*e1fe3e4aSElliott Hughes font_2_os2.ySuperscriptXSize = 800 757*e1fe3e4aSElliott Hughes font_2_os2.ySuperscriptYSize = 800 758*e1fe3e4aSElliott Hughes font_2_os2.ySuperscriptXOffset = 800 759*e1fe3e4aSElliott Hughes font_2_os2.ySuperscriptYOffset = 800 760*e1fe3e4aSElliott Hughes font_2_os2.yStrikeoutSize = 800 761*e1fe3e4aSElliott Hughes font_2_os2.yStrikeoutPosition = 800 762*e1fe3e4aSElliott Hughes font_2_vhea = newTable("vhea") 763*e1fe3e4aSElliott Hughes font_2_vhea.ascent = 800 764*e1fe3e4aSElliott Hughes font_2_vhea.descent = 800 765*e1fe3e4aSElliott Hughes font_2_vhea.lineGap = 800 766*e1fe3e4aSElliott Hughes font_2_vhea.caretSlopeRise = 800 767*e1fe3e4aSElliott Hughes font_2_vhea.caretSlopeRun = 800 768*e1fe3e4aSElliott Hughes font_2_vhea.caretOffset = 800 769*e1fe3e4aSElliott Hughes ds.sources[2].font["vhea"] = font_2_vhea 770*e1fe3e4aSElliott Hughes font_2_hhea = ds.sources[2].font["hhea"] 771*e1fe3e4aSElliott Hughes font_2_hhea.caretSlopeRise = 800 772*e1fe3e4aSElliott Hughes font_2_hhea.caretSlopeRun = 800 773*e1fe3e4aSElliott Hughes font_2_hhea.caretOffset = 800 774*e1fe3e4aSElliott Hughes font_2_post = ds.sources[2].font["post"] 775*e1fe3e4aSElliott Hughes font_2_post.underlineThickness = 800 776*e1fe3e4aSElliott Hughes font_2_post.underlinePosition = 800 777*e1fe3e4aSElliott Hughes 778*e1fe3e4aSElliott Hughes varfont, _, _ = build(ds) 779*e1fe3e4aSElliott Hughes mvar_tags = [vr.ValueTag for vr in varfont["MVAR"].table.ValueRecord] 780*e1fe3e4aSElliott Hughes assert all(tag in mvar_tags for tag in fontTools.varLib.mvar.MVAR_ENTRIES) 781*e1fe3e4aSElliott Hughes 782*e1fe3e4aSElliott Hughes def test_varlib_build_VVAR_CFF2(self): 783*e1fe3e4aSElliott Hughes self.temp_dir() 784*e1fe3e4aSElliott Hughes 785*e1fe3e4aSElliott Hughes ds_path = self.get_test_input("TestVVAR.designspace", copy=True) 786*e1fe3e4aSElliott Hughes ttx_dir = self.get_test_input("master_vvar_cff2") 787*e1fe3e4aSElliott Hughes expected_ttx_name = "TestVVAR" 788*e1fe3e4aSElliott Hughes suffix = ".otf" 789*e1fe3e4aSElliott Hughes 790*e1fe3e4aSElliott Hughes for path in self.get_file_list(ttx_dir, ".ttx", "TestVVAR"): 791*e1fe3e4aSElliott Hughes font, savepath = self.compile_font(path, suffix, self.tempdir) 792*e1fe3e4aSElliott Hughes 793*e1fe3e4aSElliott Hughes ds = DesignSpaceDocument.fromfile(ds_path) 794*e1fe3e4aSElliott Hughes for source in ds.sources: 795*e1fe3e4aSElliott Hughes source.path = os.path.join( 796*e1fe3e4aSElliott Hughes self.tempdir, os.path.basename(source.filename).replace(".ufo", suffix) 797*e1fe3e4aSElliott Hughes ) 798*e1fe3e4aSElliott Hughes ds.updatePaths() 799*e1fe3e4aSElliott Hughes 800*e1fe3e4aSElliott Hughes varfont, _, _ = build(ds) 801*e1fe3e4aSElliott Hughes varfont = reload_font(varfont) 802*e1fe3e4aSElliott Hughes 803*e1fe3e4aSElliott Hughes expected_ttx_path = self.get_test_output(expected_ttx_name + ".ttx") 804*e1fe3e4aSElliott Hughes tables = ["VVAR"] 805*e1fe3e4aSElliott Hughes self.expect_ttx(varfont, expected_ttx_path, tables) 806*e1fe3e4aSElliott Hughes self.check_ttx_dump(varfont, expected_ttx_path, tables, suffix) 807*e1fe3e4aSElliott Hughes 808*e1fe3e4aSElliott Hughes def test_varlib_build_BASE(self): 809*e1fe3e4aSElliott Hughes self.temp_dir() 810*e1fe3e4aSElliott Hughes 811*e1fe3e4aSElliott Hughes ds_path = self.get_test_input("TestBASE.designspace", copy=True) 812*e1fe3e4aSElliott Hughes ttx_dir = self.get_test_input("master_base_test") 813*e1fe3e4aSElliott Hughes expected_ttx_name = "TestBASE" 814*e1fe3e4aSElliott Hughes suffix = ".otf" 815*e1fe3e4aSElliott Hughes 816*e1fe3e4aSElliott Hughes for path in self.get_file_list(ttx_dir, ".ttx", "TestBASE"): 817*e1fe3e4aSElliott Hughes font, savepath = self.compile_font(path, suffix, self.tempdir) 818*e1fe3e4aSElliott Hughes 819*e1fe3e4aSElliott Hughes ds = DesignSpaceDocument.fromfile(ds_path) 820*e1fe3e4aSElliott Hughes for source in ds.sources: 821*e1fe3e4aSElliott Hughes source.path = os.path.join( 822*e1fe3e4aSElliott Hughes self.tempdir, os.path.basename(source.filename).replace(".ufo", suffix) 823*e1fe3e4aSElliott Hughes ) 824*e1fe3e4aSElliott Hughes ds.updatePaths() 825*e1fe3e4aSElliott Hughes 826*e1fe3e4aSElliott Hughes varfont, _, _ = build(ds) 827*e1fe3e4aSElliott Hughes varfont = reload_font(varfont) 828*e1fe3e4aSElliott Hughes 829*e1fe3e4aSElliott Hughes expected_ttx_path = self.get_test_output(expected_ttx_name + ".ttx") 830*e1fe3e4aSElliott Hughes tables = ["BASE"] 831*e1fe3e4aSElliott Hughes self.expect_ttx(varfont, expected_ttx_path, tables) 832*e1fe3e4aSElliott Hughes self.check_ttx_dump(varfont, expected_ttx_path, tables, suffix) 833*e1fe3e4aSElliott Hughes 834*e1fe3e4aSElliott Hughes def test_varlib_build_single_master(self): 835*e1fe3e4aSElliott Hughes self._run_varlib_build_test( 836*e1fe3e4aSElliott Hughes designspace_name="SingleMaster", 837*e1fe3e4aSElliott Hughes font_name="TestFamily", 838*e1fe3e4aSElliott Hughes tables=["GDEF", "HVAR", "MVAR", "STAT", "fvar", "cvar", "gvar", "name"], 839*e1fe3e4aSElliott Hughes expected_ttx_name="SingleMaster", 840*e1fe3e4aSElliott Hughes save_before_dump=True, 841*e1fe3e4aSElliott Hughes ) 842*e1fe3e4aSElliott Hughes 843*e1fe3e4aSElliott Hughes def test_kerning_merging(self): 844*e1fe3e4aSElliott Hughes """Test the correct merging of class-based pair kerning. 845*e1fe3e4aSElliott Hughes 846*e1fe3e4aSElliott Hughes Problem description at https://github.com/fonttools/fonttools/pull/1638. 847*e1fe3e4aSElliott Hughes Test font and Designspace generated by 848*e1fe3e4aSElliott Hughes https://gist.github.com/madig/183d0440c9f7d05f04bd1280b9664bd1. 849*e1fe3e4aSElliott Hughes """ 850*e1fe3e4aSElliott Hughes ds_path = self.get_test_input("KerningMerging.designspace") 851*e1fe3e4aSElliott Hughes ttx_dir = self.get_test_input("master_kerning_merging") 852*e1fe3e4aSElliott Hughes 853*e1fe3e4aSElliott Hughes ds = DesignSpaceDocument.fromfile(ds_path) 854*e1fe3e4aSElliott Hughes for source in ds.sources: 855*e1fe3e4aSElliott Hughes ttx_dump = TTFont() 856*e1fe3e4aSElliott Hughes ttx_dump.importXML( 857*e1fe3e4aSElliott Hughes os.path.join( 858*e1fe3e4aSElliott Hughes ttx_dir, os.path.basename(source.filename).replace(".ttf", ".ttx") 859*e1fe3e4aSElliott Hughes ) 860*e1fe3e4aSElliott Hughes ) 861*e1fe3e4aSElliott Hughes source.font = reload_font(ttx_dump) 862*e1fe3e4aSElliott Hughes 863*e1fe3e4aSElliott Hughes varfont, _, _ = build(ds) 864*e1fe3e4aSElliott Hughes varfont = reload_font(varfont) 865*e1fe3e4aSElliott Hughes 866*e1fe3e4aSElliott Hughes class_kerning_tables = [ 867*e1fe3e4aSElliott Hughes t 868*e1fe3e4aSElliott Hughes for l in varfont["GPOS"].table.LookupList.Lookup 869*e1fe3e4aSElliott Hughes for t in l.SubTable 870*e1fe3e4aSElliott Hughes if t.Format == 2 871*e1fe3e4aSElliott Hughes ] 872*e1fe3e4aSElliott Hughes assert len(class_kerning_tables) == 1 873*e1fe3e4aSElliott Hughes class_kerning_table = class_kerning_tables[0] 874*e1fe3e4aSElliott Hughes 875*e1fe3e4aSElliott Hughes # Test that no class kerned against class zero (containing all glyphs not 876*e1fe3e4aSElliott Hughes # classed) has a `XAdvDevice` table attached, which in the variable font 877*e1fe3e4aSElliott Hughes # context is a "VariationIndex" table and points to kerning deltas in the GDEF 878*e1fe3e4aSElliott Hughes # table. Variation deltas of any kerning class against class zero should 879*e1fe3e4aSElliott Hughes # probably never exist. 880*e1fe3e4aSElliott Hughes for class1_record in class_kerning_table.Class1Record: 881*e1fe3e4aSElliott Hughes class2_zero = class1_record.Class2Record[0] 882*e1fe3e4aSElliott Hughes assert getattr(class2_zero.Value1, "XAdvDevice", None) is None 883*e1fe3e4aSElliott Hughes 884*e1fe3e4aSElliott Hughes # Assert the variable font's kerning table (without deltas) is equal to the 885*e1fe3e4aSElliott Hughes # default font's kerning table. The bug fixed in 886*e1fe3e4aSElliott Hughes # https://github.com/fonttools/fonttools/pull/1638 caused rogue kerning 887*e1fe3e4aSElliott Hughes # values to be written to the variable font. 888*e1fe3e4aSElliott Hughes assert _extract_flat_kerning(varfont, class_kerning_table) == { 889*e1fe3e4aSElliott Hughes ("A", ".notdef"): 0, 890*e1fe3e4aSElliott Hughes ("A", "A"): 0, 891*e1fe3e4aSElliott Hughes ("A", "B"): -20, 892*e1fe3e4aSElliott Hughes ("A", "C"): 0, 893*e1fe3e4aSElliott Hughes ("A", "D"): -20, 894*e1fe3e4aSElliott Hughes ("B", ".notdef"): 0, 895*e1fe3e4aSElliott Hughes ("B", "A"): 0, 896*e1fe3e4aSElliott Hughes ("B", "B"): 0, 897*e1fe3e4aSElliott Hughes ("B", "C"): 0, 898*e1fe3e4aSElliott Hughes ("B", "D"): 0, 899*e1fe3e4aSElliott Hughes } 900*e1fe3e4aSElliott Hughes 901*e1fe3e4aSElliott Hughes instance_thin = instantiateVariableFont(varfont, {"wght": 100}) 902*e1fe3e4aSElliott Hughes instance_thin_kerning_table = ( 903*e1fe3e4aSElliott Hughes instance_thin["GPOS"].table.LookupList.Lookup[0].SubTable[0] 904*e1fe3e4aSElliott Hughes ) 905*e1fe3e4aSElliott Hughes assert _extract_flat_kerning(instance_thin, instance_thin_kerning_table) == { 906*e1fe3e4aSElliott Hughes ("A", ".notdef"): 0, 907*e1fe3e4aSElliott Hughes ("A", "A"): 0, 908*e1fe3e4aSElliott Hughes ("A", "B"): 0, 909*e1fe3e4aSElliott Hughes ("A", "C"): 10, 910*e1fe3e4aSElliott Hughes ("A", "D"): 0, 911*e1fe3e4aSElliott Hughes ("B", ".notdef"): 0, 912*e1fe3e4aSElliott Hughes ("B", "A"): 0, 913*e1fe3e4aSElliott Hughes ("B", "B"): 0, 914*e1fe3e4aSElliott Hughes ("B", "C"): 10, 915*e1fe3e4aSElliott Hughes ("B", "D"): 0, 916*e1fe3e4aSElliott Hughes } 917*e1fe3e4aSElliott Hughes 918*e1fe3e4aSElliott Hughes instance_black = instantiateVariableFont(varfont, {"wght": 900}) 919*e1fe3e4aSElliott Hughes instance_black_kerning_table = ( 920*e1fe3e4aSElliott Hughes instance_black["GPOS"].table.LookupList.Lookup[0].SubTable[0] 921*e1fe3e4aSElliott Hughes ) 922*e1fe3e4aSElliott Hughes assert _extract_flat_kerning(instance_black, instance_black_kerning_table) == { 923*e1fe3e4aSElliott Hughes ("A", ".notdef"): 0, 924*e1fe3e4aSElliott Hughes ("A", "A"): 0, 925*e1fe3e4aSElliott Hughes ("A", "B"): 0, 926*e1fe3e4aSElliott Hughes ("A", "C"): 0, 927*e1fe3e4aSElliott Hughes ("A", "D"): 40, 928*e1fe3e4aSElliott Hughes ("B", ".notdef"): 0, 929*e1fe3e4aSElliott Hughes ("B", "A"): 0, 930*e1fe3e4aSElliott Hughes ("B", "B"): 0, 931*e1fe3e4aSElliott Hughes ("B", "C"): 0, 932*e1fe3e4aSElliott Hughes ("B", "D"): 40, 933*e1fe3e4aSElliott Hughes } 934*e1fe3e4aSElliott Hughes 935*e1fe3e4aSElliott Hughes def test_designspace_fill_in_location(self): 936*e1fe3e4aSElliott Hughes ds_path = self.get_test_input("VarLibLocationTest.designspace") 937*e1fe3e4aSElliott Hughes ds = DesignSpaceDocument.fromfile(ds_path) 938*e1fe3e4aSElliott Hughes ds_loaded = load_designspace(ds) 939*e1fe3e4aSElliott Hughes 940*e1fe3e4aSElliott Hughes assert ds_loaded.instances[0].location == {"weight": 0, "width": 50} 941*e1fe3e4aSElliott Hughes 942*e1fe3e4aSElliott Hughes def test_varlib_build_incompatible_features(self): 943*e1fe3e4aSElliott Hughes with pytest.raises( 944*e1fe3e4aSElliott Hughes varLibErrors.ShouldBeConstant, 945*e1fe3e4aSElliott Hughes match=""" 946*e1fe3e4aSElliott Hughes 947*e1fe3e4aSElliott HughesCouldn't merge the fonts, because some values were different, but should have 948*e1fe3e4aSElliott Hughesbeen the same. This happened while performing the following operation: 949*e1fe3e4aSElliott HughesGPOS.table.FeatureList.FeatureCount 950*e1fe3e4aSElliott Hughes 951*e1fe3e4aSElliott HughesThe problem is likely to be in Simple Two Axis Bold: 952*e1fe3e4aSElliott HughesExpected to see .FeatureCount==2, instead saw 1 953*e1fe3e4aSElliott Hughes 954*e1fe3e4aSElliott HughesIncompatible features between masters. 955*e1fe3e4aSElliott HughesExpected: kern, mark. 956*e1fe3e4aSElliott HughesGot: kern. 957*e1fe3e4aSElliott Hughes""", 958*e1fe3e4aSElliott Hughes ): 959*e1fe3e4aSElliott Hughes self._run_varlib_build_test( 960*e1fe3e4aSElliott Hughes designspace_name="IncompatibleFeatures", 961*e1fe3e4aSElliott Hughes font_name="IncompatibleFeatures", 962*e1fe3e4aSElliott Hughes tables=["GPOS"], 963*e1fe3e4aSElliott Hughes expected_ttx_name="IncompatibleFeatures", 964*e1fe3e4aSElliott Hughes save_before_dump=True, 965*e1fe3e4aSElliott Hughes ) 966*e1fe3e4aSElliott Hughes 967*e1fe3e4aSElliott Hughes def test_varlib_build_incompatible_lookup_types(self): 968*e1fe3e4aSElliott Hughes with pytest.raises( 969*e1fe3e4aSElliott Hughes varLibErrors.MismatchedTypes, match=r"'MarkBasePos', instead saw 'PairPos'" 970*e1fe3e4aSElliott Hughes ): 971*e1fe3e4aSElliott Hughes self._run_varlib_build_test( 972*e1fe3e4aSElliott Hughes designspace_name="IncompatibleLookupTypes", 973*e1fe3e4aSElliott Hughes font_name="IncompatibleLookupTypes", 974*e1fe3e4aSElliott Hughes tables=["GPOS"], 975*e1fe3e4aSElliott Hughes expected_ttx_name="IncompatibleLookupTypes", 976*e1fe3e4aSElliott Hughes save_before_dump=True, 977*e1fe3e4aSElliott Hughes ) 978*e1fe3e4aSElliott Hughes 979*e1fe3e4aSElliott Hughes def test_varlib_build_incompatible_arrays(self): 980*e1fe3e4aSElliott Hughes with pytest.raises( 981*e1fe3e4aSElliott Hughes varLibErrors.ShouldBeConstant, 982*e1fe3e4aSElliott Hughes match=""" 983*e1fe3e4aSElliott Hughes 984*e1fe3e4aSElliott HughesCouldn't merge the fonts, because some values were different, but should have 985*e1fe3e4aSElliott Hughesbeen the same. This happened while performing the following operation: 986*e1fe3e4aSElliott HughesGPOS.table.ScriptList.ScriptCount 987*e1fe3e4aSElliott Hughes 988*e1fe3e4aSElliott HughesThe problem is likely to be in Simple Two Axis Bold: 989*e1fe3e4aSElliott HughesExpected to see .ScriptCount==1, instead saw 0""", 990*e1fe3e4aSElliott Hughes ): 991*e1fe3e4aSElliott Hughes self._run_varlib_build_test( 992*e1fe3e4aSElliott Hughes designspace_name="IncompatibleArrays", 993*e1fe3e4aSElliott Hughes font_name="IncompatibleArrays", 994*e1fe3e4aSElliott Hughes tables=["GPOS"], 995*e1fe3e4aSElliott Hughes expected_ttx_name="IncompatibleArrays", 996*e1fe3e4aSElliott Hughes save_before_dump=True, 997*e1fe3e4aSElliott Hughes ) 998*e1fe3e4aSElliott Hughes 999*e1fe3e4aSElliott Hughes def test_varlib_build_variable_colr(self): 1000*e1fe3e4aSElliott Hughes self._run_varlib_build_test( 1001*e1fe3e4aSElliott Hughes designspace_name="TestVariableCOLR", 1002*e1fe3e4aSElliott Hughes font_name="TestVariableCOLR", 1003*e1fe3e4aSElliott Hughes tables=["GlyphOrder", "fvar", "glyf", "COLR", "CPAL"], 1004*e1fe3e4aSElliott Hughes expected_ttx_name="TestVariableCOLR-VF", 1005*e1fe3e4aSElliott Hughes save_before_dump=True, 1006*e1fe3e4aSElliott Hughes ) 1007*e1fe3e4aSElliott Hughes 1008*e1fe3e4aSElliott Hughes def test_varlib_build_variable_cff2_with_empty_sparse_glyph(self): 1009*e1fe3e4aSElliott Hughes # https://github.com/fonttools/fonttools/issues/3233 1010*e1fe3e4aSElliott Hughes self._run_varlib_build_test( 1011*e1fe3e4aSElliott Hughes designspace_name="SparseCFF2", 1012*e1fe3e4aSElliott Hughes font_name="SparseCFF2", 1013*e1fe3e4aSElliott Hughes tables=["GlyphOrder", "CFF2", "fvar", "hmtx", "HVAR"], 1014*e1fe3e4aSElliott Hughes expected_ttx_name="SparseCFF2-VF", 1015*e1fe3e4aSElliott Hughes save_before_dump=True, 1016*e1fe3e4aSElliott Hughes ) 1017*e1fe3e4aSElliott Hughes 1018*e1fe3e4aSElliott Hughes def test_varlib_addGSUBFeatureVariations(self): 1019*e1fe3e4aSElliott Hughes ttx_dir = self.get_test_input("master_ttx_interpolatable_ttf") 1020*e1fe3e4aSElliott Hughes 1021*e1fe3e4aSElliott Hughes ds = DesignSpaceDocument.fromfile( 1022*e1fe3e4aSElliott Hughes self.get_test_input("FeatureVars.designspace") 1023*e1fe3e4aSElliott Hughes ) 1024*e1fe3e4aSElliott Hughes for source in ds.sources: 1025*e1fe3e4aSElliott Hughes ttx_dump = TTFont() 1026*e1fe3e4aSElliott Hughes ttx_dump.importXML( 1027*e1fe3e4aSElliott Hughes os.path.join( 1028*e1fe3e4aSElliott Hughes ttx_dir, os.path.basename(source.filename).replace(".ufo", ".ttx") 1029*e1fe3e4aSElliott Hughes ) 1030*e1fe3e4aSElliott Hughes ) 1031*e1fe3e4aSElliott Hughes source.font = ttx_dump 1032*e1fe3e4aSElliott Hughes 1033*e1fe3e4aSElliott Hughes varfont, _, _ = build(ds, exclude=["GSUB"]) 1034*e1fe3e4aSElliott Hughes assert "GSUB" not in varfont 1035*e1fe3e4aSElliott Hughes 1036*e1fe3e4aSElliott Hughes addGSUBFeatureVariations(varfont, ds) 1037*e1fe3e4aSElliott Hughes assert "GSUB" in varfont 1038*e1fe3e4aSElliott Hughes 1039*e1fe3e4aSElliott Hughes tables = ["fvar", "GSUB"] 1040*e1fe3e4aSElliott Hughes expected_ttx_path = self.get_test_output("FeatureVars.ttx") 1041*e1fe3e4aSElliott Hughes self.expect_ttx(varfont, expected_ttx_path, tables) 1042*e1fe3e4aSElliott Hughes self.check_ttx_dump(varfont, expected_ttx_path, tables, ".ttf") 1043*e1fe3e4aSElliott Hughes 1044*e1fe3e4aSElliott Hughes 1045*e1fe3e4aSElliott Hughesdef test_load_masters_layerName_without_required_font(): 1046*e1fe3e4aSElliott Hughes ds = DesignSpaceDocument() 1047*e1fe3e4aSElliott Hughes s = SourceDescriptor() 1048*e1fe3e4aSElliott Hughes s.font = None 1049*e1fe3e4aSElliott Hughes s.layerName = "Medium" 1050*e1fe3e4aSElliott Hughes ds.addSource(s) 1051*e1fe3e4aSElliott Hughes 1052*e1fe3e4aSElliott Hughes with pytest.raises( 1053*e1fe3e4aSElliott Hughes VarLibValidationError, 1054*e1fe3e4aSElliott Hughes match="specified a layer name but lacks the required TTFont object", 1055*e1fe3e4aSElliott Hughes ): 1056*e1fe3e4aSElliott Hughes load_masters(ds) 1057*e1fe3e4aSElliott Hughes 1058*e1fe3e4aSElliott Hughes 1059*e1fe3e4aSElliott Hughesdef _extract_flat_kerning(font, pairpos_table): 1060*e1fe3e4aSElliott Hughes extracted_kerning = {} 1061*e1fe3e4aSElliott Hughes for glyph_name_1 in pairpos_table.Coverage.glyphs: 1062*e1fe3e4aSElliott Hughes class_def_1 = pairpos_table.ClassDef1.classDefs.get(glyph_name_1, 0) 1063*e1fe3e4aSElliott Hughes for glyph_name_2 in font.getGlyphOrder(): 1064*e1fe3e4aSElliott Hughes class_def_2 = pairpos_table.ClassDef2.classDefs.get(glyph_name_2, 0) 1065*e1fe3e4aSElliott Hughes kern_value = ( 1066*e1fe3e4aSElliott Hughes pairpos_table.Class1Record[class_def_1] 1067*e1fe3e4aSElliott Hughes .Class2Record[class_def_2] 1068*e1fe3e4aSElliott Hughes .Value1.XAdvance 1069*e1fe3e4aSElliott Hughes ) 1070*e1fe3e4aSElliott Hughes extracted_kerning[(glyph_name_1, glyph_name_2)] = kern_value 1071*e1fe3e4aSElliott Hughes return extracted_kerning 1072*e1fe3e4aSElliott Hughes 1073*e1fe3e4aSElliott Hughes 1074*e1fe3e4aSElliott Hughes@pytest.fixture 1075*e1fe3e4aSElliott Hughesdef ttFont(): 1076*e1fe3e4aSElliott Hughes f = TTFont() 1077*e1fe3e4aSElliott Hughes f["OS/2"] = newTable("OS/2") 1078*e1fe3e4aSElliott Hughes f["OS/2"].usWeightClass = 400 1079*e1fe3e4aSElliott Hughes f["OS/2"].usWidthClass = 100 1080*e1fe3e4aSElliott Hughes f["post"] = newTable("post") 1081*e1fe3e4aSElliott Hughes f["post"].italicAngle = 0 1082*e1fe3e4aSElliott Hughes return f 1083*e1fe3e4aSElliott Hughes 1084*e1fe3e4aSElliott Hughes 1085*e1fe3e4aSElliott Hughesclass SetDefaultWeightWidthSlantTest(object): 1086*e1fe3e4aSElliott Hughes @pytest.mark.parametrize( 1087*e1fe3e4aSElliott Hughes "location, expected", 1088*e1fe3e4aSElliott Hughes [ 1089*e1fe3e4aSElliott Hughes ({"wght": 0}, 1), 1090*e1fe3e4aSElliott Hughes ({"wght": 1}, 1), 1091*e1fe3e4aSElliott Hughes ({"wght": 100}, 100), 1092*e1fe3e4aSElliott Hughes ({"wght": 1000}, 1000), 1093*e1fe3e4aSElliott Hughes ({"wght": 1001}, 1000), 1094*e1fe3e4aSElliott Hughes ], 1095*e1fe3e4aSElliott Hughes ) 1096*e1fe3e4aSElliott Hughes def test_wght(self, ttFont, location, expected): 1097*e1fe3e4aSElliott Hughes set_default_weight_width_slant(ttFont, location) 1098*e1fe3e4aSElliott Hughes 1099*e1fe3e4aSElliott Hughes assert ttFont["OS/2"].usWeightClass == expected 1100*e1fe3e4aSElliott Hughes 1101*e1fe3e4aSElliott Hughes @pytest.mark.parametrize( 1102*e1fe3e4aSElliott Hughes "location, expected", 1103*e1fe3e4aSElliott Hughes [ 1104*e1fe3e4aSElliott Hughes ({"wdth": 0}, 1), 1105*e1fe3e4aSElliott Hughes ({"wdth": 56}, 1), 1106*e1fe3e4aSElliott Hughes ({"wdth": 57}, 2), 1107*e1fe3e4aSElliott Hughes ({"wdth": 62.5}, 2), 1108*e1fe3e4aSElliott Hughes ({"wdth": 75}, 3), 1109*e1fe3e4aSElliott Hughes ({"wdth": 87.5}, 4), 1110*e1fe3e4aSElliott Hughes ({"wdth": 100}, 5), 1111*e1fe3e4aSElliott Hughes ({"wdth": 112.5}, 6), 1112*e1fe3e4aSElliott Hughes ({"wdth": 125}, 7), 1113*e1fe3e4aSElliott Hughes ({"wdth": 150}, 8), 1114*e1fe3e4aSElliott Hughes ({"wdth": 200}, 9), 1115*e1fe3e4aSElliott Hughes ({"wdth": 201}, 9), 1116*e1fe3e4aSElliott Hughes ({"wdth": 1000}, 9), 1117*e1fe3e4aSElliott Hughes ], 1118*e1fe3e4aSElliott Hughes ) 1119*e1fe3e4aSElliott Hughes def test_wdth(self, ttFont, location, expected): 1120*e1fe3e4aSElliott Hughes set_default_weight_width_slant(ttFont, location) 1121*e1fe3e4aSElliott Hughes 1122*e1fe3e4aSElliott Hughes assert ttFont["OS/2"].usWidthClass == expected 1123*e1fe3e4aSElliott Hughes 1124*e1fe3e4aSElliott Hughes @pytest.mark.parametrize( 1125*e1fe3e4aSElliott Hughes "location, expected", 1126*e1fe3e4aSElliott Hughes [ 1127*e1fe3e4aSElliott Hughes ({"slnt": -91}, -90), 1128*e1fe3e4aSElliott Hughes ({"slnt": -90}, -90), 1129*e1fe3e4aSElliott Hughes ({"slnt": 0}, 0), 1130*e1fe3e4aSElliott Hughes ({"slnt": 11.5}, 11.5), 1131*e1fe3e4aSElliott Hughes ({"slnt": 90}, 90), 1132*e1fe3e4aSElliott Hughes ({"slnt": 91}, 90), 1133*e1fe3e4aSElliott Hughes ], 1134*e1fe3e4aSElliott Hughes ) 1135*e1fe3e4aSElliott Hughes def test_slnt(self, ttFont, location, expected): 1136*e1fe3e4aSElliott Hughes set_default_weight_width_slant(ttFont, location) 1137*e1fe3e4aSElliott Hughes 1138*e1fe3e4aSElliott Hughes assert ttFont["post"].italicAngle == expected 1139*e1fe3e4aSElliott Hughes 1140*e1fe3e4aSElliott Hughes def test_all(self, ttFont): 1141*e1fe3e4aSElliott Hughes set_default_weight_width_slant( 1142*e1fe3e4aSElliott Hughes ttFont, {"wght": 500, "wdth": 150, "slnt": -12.0} 1143*e1fe3e4aSElliott Hughes ) 1144*e1fe3e4aSElliott Hughes 1145*e1fe3e4aSElliott Hughes assert ttFont["OS/2"].usWeightClass == 500 1146*e1fe3e4aSElliott Hughes assert ttFont["OS/2"].usWidthClass == 8 1147*e1fe3e4aSElliott Hughes assert ttFont["post"].italicAngle == -12.0 1148*e1fe3e4aSElliott Hughes 1149*e1fe3e4aSElliott Hughes 1150*e1fe3e4aSElliott Hughesdef test_variable_COLR_without_VarIndexMap(): 1151*e1fe3e4aSElliott Hughes # test we don't add a no-op VarIndexMap to variable COLR when not needed 1152*e1fe3e4aSElliott Hughes # https://github.com/fonttools/fonttools/issues/2800 1153*e1fe3e4aSElliott Hughes 1154*e1fe3e4aSElliott Hughes font1 = TTFont() 1155*e1fe3e4aSElliott Hughes font1.setGlyphOrder([".notdef", "A"]) 1156*e1fe3e4aSElliott Hughes font1["COLR"] = buildCOLR({"A": (ot.PaintFormat.PaintSolid, 0, 1.0)}) 1157*e1fe3e4aSElliott Hughes # font2 == font1 except for PaintSolid.Alpha 1158*e1fe3e4aSElliott Hughes font2 = deepcopy(font1) 1159*e1fe3e4aSElliott Hughes font2["COLR"].table.BaseGlyphList.BaseGlyphPaintRecord[0].Paint.Alpha = 0.0 1160*e1fe3e4aSElliott Hughes master_fonts = [font1, font2] 1161*e1fe3e4aSElliott Hughes 1162*e1fe3e4aSElliott Hughes varfont = deepcopy(font1) 1163*e1fe3e4aSElliott Hughes axis_order = ["XXXX"] 1164*e1fe3e4aSElliott Hughes model = VariationModel([{}, {"XXXX": 1.0}], axis_order) 1165*e1fe3e4aSElliott Hughes 1166*e1fe3e4aSElliott Hughes _add_COLR(varfont, model, master_fonts, axis_order) 1167*e1fe3e4aSElliott Hughes 1168*e1fe3e4aSElliott Hughes colr = varfont["COLR"].table 1169*e1fe3e4aSElliott Hughes 1170*e1fe3e4aSElliott Hughes assert len(colr.BaseGlyphList.BaseGlyphPaintRecord) == 1 1171*e1fe3e4aSElliott Hughes baserec = colr.BaseGlyphList.BaseGlyphPaintRecord[0] 1172*e1fe3e4aSElliott Hughes assert baserec.Paint.Format == ot.PaintFormat.PaintVarSolid 1173*e1fe3e4aSElliott Hughes assert baserec.Paint.VarIndexBase == 0 1174*e1fe3e4aSElliott Hughes 1175*e1fe3e4aSElliott Hughes assert colr.VarStore is not None 1176*e1fe3e4aSElliott Hughes assert len(colr.VarStore.VarData) == 1 1177*e1fe3e4aSElliott Hughes assert len(colr.VarStore.VarData[0].Item) == 1 1178*e1fe3e4aSElliott Hughes assert colr.VarStore.VarData[0].Item[0] == [-16384] 1179*e1fe3e4aSElliott Hughes 1180*e1fe3e4aSElliott Hughes assert colr.VarIndexMap is None 1181*e1fe3e4aSElliott Hughes 1182*e1fe3e4aSElliott Hughes 1183*e1fe3e4aSElliott Hughesif __name__ == "__main__": 1184*e1fe3e4aSElliott Hughes sys.exit(unittest.main()) 1185