diff --git a/Lib/fontTools/varLib/__init__.py b/Lib/fontTools/varLib/__init__.py index 2dd5718e8..6ec4113ff 100644 --- a/Lib/fontTools/varLib/__init__.py +++ b/Lib/fontTools/varLib/__init__.py @@ -521,7 +521,11 @@ def _add_MVAR(font, masterModel, master_ttfs, axisTags): # the minimum FWord (int16) value, was chosen for its unlikelyhood to appear # in real-world underline position/thickness values. specialTags = {"unds": -0x8000, "undo": -0x8000} + for tag, (tableTag, itemName) in sorted(MVAR_ENTRIES.items(), key=lambda kv: kv[1]): + # For each tag, fetch the associated table from all fonts (or not when we are + # still looking at a tag from the same tables) and set up the variation model + # for them. if tableTag != lastTableTag: tables = fontTable = None if tableTag in font: @@ -535,15 +539,15 @@ def _add_MVAR(font, masterModel, master_ttfs, axisTags): tables.append(None) else: tables.append(master[tableTag]) + model, tables = masterModel.getSubModel(tables) + store_builder.setModel(model) lastTableTag = tableTag - if tables is None: + + if tables is None: # Tag not applicable to the master font. continue # TODO support gasp entries - model, tables = masterModel.getSubModel(tables) - store_builder.setModel(model) - master_values = [getattr(table, itemName) for table in tables] if models.allEqual(master_values): base, varIdx = master_values[0], None @@ -886,7 +890,7 @@ def load_masters(designspace, master_finder=lambda s: s): # 2. A SourceDescriptor's path might point an OpenType binary, a # TTX file, or another source file (e.g. UFO), in which case we # resolve the path using 'master_finder' function - font = _open_font(master.path, master_finder) + master.font = font = _open_font(master.path, master_finder) master_fonts.append(font) return master_fonts diff --git a/Tests/varLib/varLib_test.py b/Tests/varLib/varLib_test.py index 831d8b832..32e7ab5e2 100644 --- a/Tests/varLib/varLib_test.py +++ b/Tests/varLib/varLib_test.py @@ -1,6 +1,6 @@ from __future__ import print_function, division, absolute_import from fontTools.misc.py23 import * -from fontTools.ttLib import TTFont +from fontTools.ttLib import TTFont, newTable from fontTools.varLib import build from fontTools.varLib import main as varLib_main, load_masters from fontTools.designspaceLib import ( @@ -361,6 +361,87 @@ class BuildTest(unittest.TestCase): tables = [table_tag for table_tag in varfont.keys() if table_tag != "head"] self.expect_ttx(varfont, expected_ttx_path, tables) + def test_varlib_build_sparse_masters_MVAR(self): + import fontTools.varLib.mvar + + ds_path = self.get_test_input("SparseMasters.designspace") + ds = DesignSpaceDocument.fromfile(ds_path) + load_masters(ds) + + # Trigger MVAR generation so varLib is forced to create deltas with a + # sparse master inbetween. + font_0_os2 = ds.sources[0].font["OS/2"] + font_0_os2.sTypoAscender = 1 + font_0_os2.sTypoDescender = 1 + font_0_os2.sTypoLineGap = 1 + font_0_os2.usWinAscent = 1 + font_0_os2.usWinDescent = 1 + font_0_os2.sxHeight = 1 + font_0_os2.sCapHeight = 1 + font_0_os2.ySubscriptXSize = 1 + font_0_os2.ySubscriptYSize = 1 + font_0_os2.ySubscriptXOffset = 1 + font_0_os2.ySubscriptYOffset = 1 + font_0_os2.ySuperscriptXSize = 1 + font_0_os2.ySuperscriptYSize = 1 + font_0_os2.ySuperscriptXOffset = 1 + font_0_os2.ySuperscriptYOffset = 1 + font_0_os2.yStrikeoutSize = 1 + font_0_os2.yStrikeoutPosition = 1 + font_0_vhea = newTable("vhea") + font_0_vhea.ascent = 1 + font_0_vhea.descent = 1 + font_0_vhea.lineGap = 1 + font_0_vhea.caretSlopeRise = 1 + font_0_vhea.caretSlopeRun = 1 + font_0_vhea.caretOffset = 1 + ds.sources[0].font["vhea"] = font_0_vhea + font_0_hhea = ds.sources[0].font["hhea"] + font_0_hhea.caretSlopeRise = 1 + font_0_hhea.caretSlopeRun = 1 + font_0_hhea.caretOffset = 1 + font_0_post = ds.sources[0].font["post"] + font_0_post.underlineThickness = 1 + font_0_post.underlinePosition = 1 + + font_2_os2 = ds.sources[2].font["OS/2"] + font_2_os2.sTypoAscender = 800 + font_2_os2.sTypoDescender = 800 + font_2_os2.sTypoLineGap = 800 + font_2_os2.usWinAscent = 800 + font_2_os2.usWinDescent = 800 + font_2_os2.sxHeight = 800 + font_2_os2.sCapHeight = 800 + font_2_os2.ySubscriptXSize = 800 + font_2_os2.ySubscriptYSize = 800 + font_2_os2.ySubscriptXOffset = 800 + font_2_os2.ySubscriptYOffset = 800 + font_2_os2.ySuperscriptXSize = 800 + font_2_os2.ySuperscriptYSize = 800 + font_2_os2.ySuperscriptXOffset = 800 + font_2_os2.ySuperscriptYOffset = 800 + font_2_os2.yStrikeoutSize = 800 + font_2_os2.yStrikeoutPosition = 800 + font_2_vhea = newTable("vhea") + font_2_vhea.ascent = 800 + font_2_vhea.descent = 800 + font_2_vhea.lineGap = 800 + font_2_vhea.caretSlopeRise = 800 + font_2_vhea.caretSlopeRun = 800 + font_2_vhea.caretOffset = 800 + ds.sources[2].font["vhea"] = font_2_vhea + font_2_hhea = ds.sources[2].font["hhea"] + font_2_hhea.caretSlopeRise = 800 + font_2_hhea.caretSlopeRun = 800 + font_2_hhea.caretOffset = 800 + font_2_post = ds.sources[2].font["post"] + font_2_post.underlineThickness = 800 + font_2_post.underlinePosition = 800 + + varfont, _, _ = build(ds) + mvar_tags = [vr.ValueTag for vr in varfont["MVAR"].table.ValueRecord] + assert all(tag in mvar_tags for tag in fontTools.varLib.mvar.MVAR_ENTRIES) + def test_load_masters_layerName_without_required_font(): ds = DesignSpaceDocument()