diff options
Diffstat (limited to 'Lib/fontTools/varLib/__init__.py')
-rw-r--r-- | Lib/fontTools/varLib/__init__.py | 177 |
1 files changed, 119 insertions, 58 deletions
diff --git a/Lib/fontTools/varLib/__init__.py b/Lib/fontTools/varLib/__init__.py index 0543ee37..db567062 100644 --- a/Lib/fontTools/varLib/__init__.py +++ b/Lib/fontTools/varLib/__init__.py @@ -463,46 +463,120 @@ def _merge_TTHinting(font, masterModel, master_ttfs, tolerance=0.5): var = TupleVariation(support, delta) cvar.variations.append(var) +MetricsFields = namedtuple('MetricsFields', + ['tableTag', 'metricsTag', 'sb1', 'sb2', 'advMapping', 'vOrigMapping']) + +hvarFields = MetricsFields(tableTag='HVAR', metricsTag='hmtx', sb1='LsbMap', + sb2='RsbMap', advMapping='AdvWidthMap', vOrigMapping=None) + +vvarFields = MetricsFields(tableTag='VVAR', metricsTag='vmtx', sb1='TsbMap', + sb2='BsbMap', advMapping='AdvHeightMap', vOrigMapping='VOrgMap') + def _add_HVAR(font, masterModel, master_ttfs, axisTags): + _add_VHVAR(font, masterModel, master_ttfs, axisTags, hvarFields) + +def _add_VVAR(font, masterModel, master_ttfs, axisTags): + _add_VHVAR(font, masterModel, master_ttfs, axisTags, vvarFields) - log.info("Generating HVAR") +def _add_VHVAR(font, masterModel, master_ttfs, axisTags, tableFields): + + tableTag = tableFields.tableTag + assert tableTag not in font + log.info("Generating " + tableTag) + VHVAR = newTable(tableTag) + tableClass = getattr(ot, tableTag) + vhvar = VHVAR.table = tableClass() + vhvar.Version = 0x00010000 glyphOrder = font.getGlyphOrder() - hAdvanceDeltasAndSupports = {} - metricses = [m["hmtx"].metrics for m in master_ttfs] + # Build list of source font advance widths for each glyph + metricsTag = tableFields.metricsTag + advMetricses = [m[metricsTag].metrics for m in master_ttfs] + + # Build list of source font vertical origin coords for each glyph + if tableTag == 'VVAR' and 'VORG' in master_ttfs[0]: + vOrigMetricses = [m['VORG'].VOriginRecords for m in master_ttfs] + defaultYOrigs = [m['VORG'].defaultVertOriginY for m in master_ttfs] + vOrigMetricses = list(zip(vOrigMetricses, defaultYOrigs)) + else: + vOrigMetricses = None + + metricsStore, advanceMapping, vOrigMapping = _get_advance_metrics(font, + masterModel, master_ttfs, axisTags, glyphOrder, advMetricses, + vOrigMetricses) + + vhvar.VarStore = metricsStore + if advanceMapping is None: + setattr(vhvar, tableFields.advMapping, None) + else: + setattr(vhvar, tableFields.advMapping, advanceMapping) + if vOrigMapping is not None: + setattr(vhvar, tableFields.vOrigMapping, vOrigMapping) + setattr(vhvar, tableFields.sb1, None) + setattr(vhvar, tableFields.sb2, None) + + font[tableTag] = VHVAR + return + +def _get_advance_metrics(font, masterModel, master_ttfs, + axisTags, glyphOrder, advMetricses, vOrigMetricses=None): + + vhAdvanceDeltasAndSupports = {} + vOrigDeltasAndSupports = {} for glyph in glyphOrder: - hAdvances = [metrics[glyph][0] if glyph in metrics else None for metrics in metricses] - hAdvanceDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(hAdvances) + vhAdvances = [metrics[glyph][0] if glyph in metrics else None for metrics in advMetricses] + vhAdvanceDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(vhAdvances) - singleModel = models.allEqual(id(v[1]) for v in hAdvanceDeltasAndSupports.values()) + singleModel = models.allEqual(id(v[1]) for v in vhAdvanceDeltasAndSupports.values()) + + if vOrigMetricses: + singleModel = False + for glyph in glyphOrder: + # We need to supply a vOrigs tuple with non-None default values + # for each glyph. vOrigMetricses contains values only for those + # glyphs which have a non-default vOrig. + vOrigs = [metrics[glyph] if glyph in metrics else defaultVOrig + for metrics, defaultVOrig in vOrigMetricses] + vOrigDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(vOrigs) directStore = None if singleModel: # Build direct mapping - - supports = next(iter(hAdvanceDeltasAndSupports.values()))[1][1:] + supports = next(iter(vhAdvanceDeltasAndSupports.values()))[1][1:] varTupleList = builder.buildVarRegionList(supports, axisTags) varTupleIndexes = list(range(len(supports))) varData = builder.buildVarData(varTupleIndexes, [], optimize=False) for glyphName in glyphOrder: - varData.addItem(hAdvanceDeltasAndSupports[glyphName][0]) + varData.addItem(vhAdvanceDeltasAndSupports[glyphName][0]) varData.optimize() directStore = builder.buildVarStore(varTupleList, [varData]) # Build optimized indirect mapping storeBuilder = varStore.OnlineVarStoreBuilder(axisTags) - mapping = {} + advMapping = {} for glyphName in glyphOrder: - deltas,supports = hAdvanceDeltasAndSupports[glyphName] + deltas, supports = vhAdvanceDeltasAndSupports[glyphName] storeBuilder.setSupports(supports) - mapping[glyphName] = storeBuilder.storeDeltas(deltas) + advMapping[glyphName] = storeBuilder.storeDeltas(deltas) + + if vOrigMetricses: + vOrigMap = {} + for glyphName in glyphOrder: + deltas, supports = vOrigDeltasAndSupports[glyphName] + storeBuilder.setSupports(supports) + vOrigMap[glyphName] = storeBuilder.storeDeltas(deltas) + indirectStore = storeBuilder.finish() mapping2 = indirectStore.optimize() - mapping = [mapping2[mapping[g]] for g in glyphOrder] - advanceMapping = builder.buildVarIdxMap(mapping, glyphOrder) + advMapping = [mapping2[advMapping[g]] for g in glyphOrder] + advanceMapping = builder.buildVarIdxMap(advMapping, glyphOrder) + + if vOrigMetricses: + vOrigMap = [mapping2[vOrigMap[g]] for g in glyphOrder] - use_direct = False + useDirect = False + vOrigMapping = None if directStore: # Compile both, see which is more compact @@ -515,20 +589,17 @@ def _add_HVAR(font, masterModel, master_ttfs, axisTags): advanceMapping.compile(writer, font) indirectSize = len(writer.getAllData()) - use_direct = directSize < indirectSize - - # Done; put it all together. - assert "HVAR" not in font - HVAR = font["HVAR"] = newTable('HVAR') - hvar = HVAR.table = ot.HVAR() - hvar.Version = 0x00010000 - hvar.LsbMap = hvar.RsbMap = None - if use_direct: - hvar.VarStore = directStore - hvar.AdvWidthMap = None + useDirect = directSize < indirectSize + + if useDirect: + metricsStore = directStore + advanceMapping = None else: - hvar.VarStore = indirectStore - hvar.AdvWidthMap = advanceMapping + metricsStore = indirectStore + if vOrigMetricses: + vOrigMapping = builder.buildVarIdxMap(vOrigMap, glyphOrder) + + return metricsStore, advanceMapping, vOrigMapping def _add_MVAR(font, masterModel, master_ttfs, axisTags): @@ -688,12 +759,11 @@ _DesignSpaceData = namedtuple( def _add_CFF2(varFont, model, master_fonts): - from .cff import (convertCFFtoCFF2, addCFFVarStore, merge_region_fonts) + from .cff import (convertCFFtoCFF2, merge_region_fonts) glyphOrder = varFont.getGlyphOrder() convertCFFtoCFF2(varFont) ordered_fonts_list = model.reorderMasters(master_fonts, model.reverseMapping) # re-ordering the master list simplifies building the CFF2 data item lists. - addCFFVarStore(varFont, model) # Add VarStore to the CFF2 font. merge_region_fonts(varFont, model, ordered_fonts_list, glyphOrder) @@ -840,6 +910,8 @@ def build(designspace, master_finder=lambda s:s, exclude=[], optimize=True): _add_MVAR(vf, model, master_fonts, axisTags) if 'HVAR' not in exclude: _add_HVAR(vf, model, master_fonts, axisTags) + if 'VVAR' not in exclude and 'vmtx' in vf: + _add_VVAR(vf, model, master_fonts, axisTags) if 'GDEF' not in exclude or 'GPOS' not in exclude: _merge_OTL(vf, model, master_fonts, axisTags) if 'gvar' not in exclude and 'glyf' in vf: @@ -850,6 +922,13 @@ def build(designspace, master_finder=lambda s:s, exclude=[], optimize=True): _add_GSUB_feature_variations(vf, ds.axes, ds.internal_axis_supports, ds.rules) if 'CFF2' not in exclude and 'CFF ' in vf: _add_CFF2(vf, model, master_fonts) + if "post" in vf: + # set 'post' to format 2 to keep the glyph names dropped from CFF2 + post = vf["post"] + if post.formatType != 2.0: + post.formatType = 2.0 + post.extraNames = [] + post.mapping = {} for tag in exclude: if tag in vf: @@ -859,7 +938,7 @@ def build(designspace, master_finder=lambda s:s, exclude=[], optimize=True): return vf, model, master_ttfs -def _open_font(path, master_finder): +def _open_font(path, master_finder=lambda s: s): # load TTFont masters from given 'path': this can be either a .TTX or an # OpenType binary font; or if neither of these, try use the 'master_finder' # callable to resolve the path to a valid .TTX or OpenType font binary. @@ -893,35 +972,17 @@ def load_masters(designspace, master_finder=lambda s: s): Return list of master TTFont objects in the same order they are listed in the DesignSpaceDocument. """ - master_fonts = [] - for master in designspace.sources: - # 1. If the caller already supplies a TTFont for a source, just take it. - if master.font: - font = master.font - master_fonts.append(font) - else: - # If a SourceDescriptor has a layer name, demand that the compiled TTFont - # be supplied by the caller. This spares us from modifying MasterFinder. - if master.layerName: - raise AttributeError( - "Designspace source '%s' specified a layer name but lacks the " - "required TTFont object in the 'font' attribute." - % (master.name or "<Unknown>") + # If a SourceDescriptor has a layer name, demand that the compiled TTFont + # be supplied by the caller. This spares us from modifying MasterFinder. + if master.layerName and master.font is None: + raise AttributeError( + "Designspace source '%s' specified a layer name but lacks the " + "required TTFont object in the 'font' attribute." + % (master.name or "<Unknown>") ) - else: - if master.path is None: - raise AttributeError( - "Designspace source '%s' has neither 'font' nor 'path' " - "attributes" % (master.name or "<Unknown>") - ) - # 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 - master.font = font = _open_font(master.path, master_finder) - master_fonts.append(font) - - return master_fonts + + return designspace.loadSourceFonts(_open_font, master_finder=master_finder) class MasterFinder(object): |