def main(): filepath = sys.argv[1] tt = TTFont(filepath) buildStatTable(tt, AXES) update_fvar(tt) tt.save(filepath) print(f"Added STAT table to {filepath}")
def test_updatetNameTable_axis_order(varfont): axes = [ dict( tag="wght", name="Weight", values=[ dict(value=400, name="Regular"), ], ), dict( tag="wdth", name="Width", values=[ dict(value=75, name="Condensed"), ], ), ] nametable = varfont["name"] buildStatTable(varfont, axes) instancer.names.updateNameTable(varfont, {"wdth": 75, "wght": 400}) assert nametable.getName(17, 3, 1, 0x409).toUnicode() == "Regular Condensed" # Swap the axes so the names get swapped axes[0], axes[1] = axes[1], axes[0] buildStatTable(varfont, axes) instancer.names.updateNameTable(varfont, {"wdth": 75, "wght": 400}) assert nametable.getName(17, 3, 1, 0x409).toUnicode() == "Condensed Regular"
def build_stat(roman_font, italic_font=None): roman_wght_axis = dict( tag="wght", name="Weight", values=build_axis_values(roman_font), ) roman_axes = [roman_wght_axis] if italic_font: # We need to create a new Italic axis in the Roman font roman_axes.append( dict(tag="ital", name="Italic", values=[ dict( name="Roman", flags=2, value=0.0, linkedValue=1.0, ) ])) italic_wght_axis = dict( tag="wght", name="Weight", values=build_axis_values(italic_font), ) italic_axes = [italic_wght_axis] italic_axes.append( dict(tag="ital", name="Italic", values=[dict( name="Italic", value=1.0, )])) buildStatTable(italic_font, italic_axes) buildStatTable(roman_font, roman_axes)
def main(): font = TTFont(sys.argv[1]) # Drop VOLT table if "TSIV" in font: del font["TSIV"] # Add STAT table os2 = font["OS/2"] italic = bool(os2.fsSelection & (1 << 0)) fvar = font["fvar"] axes = [dict(tag=a.axisTag, name=a.axisNameID) for a in fvar.axes] if italic: value = dict(value=italic, name="Italic") else: value = dict(value=italic, name="Roman", flags=0x0002, linkedValue=1) axes.append(dict(tag="ital", name="Italic", values=[value])) buildStatTable(font, axes) # Prune name table names = [n for n in font["name"].names if n.platformID == 3] # Drop Regular from Roman font names if not italic: for name in names: if name.nameID in (3, 6): name.string = str(name).replace("-Regular", "") if name.nameID == 4: name.string = str(name).replace(" Regular", "") font["name"].names = names font["OS/2"].usMaxContext = maxCtxFont(font) font["DSIG"] = DSIG = newTable("DSIG") DSIG.ulVersion = 1 DSIG.usFlag = 0 DSIG.usNumSigs = 0 DSIG.signatureRecords = [] if "glyf" in font and "prep" not in font: # Google Fonts “smart dropout control” font["prep"] = prep = newTable("prep") prep.program = ttProgram.Program() prep.program.fromAssembly( ["PUSHW[]", "511", "SCANCTRL[]", "PUSHB[]", "4", "SCANTYPE[]"] ) if "MVAR" in font: del font["MVAR"] font.save(sys.argv[1])
def main(): # process upright files filepath = CAS_UPRIGHT tt = TTFont(filepath) buildStatTable(tt, UPRIGHT_AXES) tt.save(filepath) print(f"[STAT TABLE] Added STAT table to {filepath}") # process italics files filepath = CAS_ITALIC tt = TTFont(filepath) buildStatTable(tt, ITALIC_AXES) tt.save(filepath) print(f"[STAT TABLE] Added STAT table to {filepath}")
def main(): # process upright files filepath = UPRIGHT_SRC tt = TTFont(filepath) buildStatTable(tt, UPRIGHT_AXES) update_fvar(tt) tt.save(filepath) print(f"[STAT TABLE] Added STAT table to {filepath}") # process italics files filepath = ITALIC_SRC tt = TTFont(filepath) buildStatTable(tt, ITALIC_AXES) update_fvar(tt) tt.save(filepath) print(f"[STAT TABLE] Added STAT table to {filepath}")
def main(): filepath = sys.argv[1] tt = TTFont(filepath) # remove & replace last item in AXES to work for italic font if "Italic" in filepath: AXES.pop() AXES.append( dict( tag="ital", name="Italic", ordering=4, values=[ dict(nominalValue=1, name="Italic"), ], )) buildStatTable(tt, AXES) update_fvar(tt) tt.save(filepath) print(f"Added STAT table to {filepath}")
def main(): filepath = VR_UPRIGHT tt = TTFont(filepath) buildStatTable(tt, UPRIGHT_AXES) tt.save(filepath) print(f"[STAT TABLE] Added STAT table to {filepath}")
def gen_stat_tables_from_config(stat, varfonts, has_italic=None): """ Generate a stat table for each font in a family from a Python configuration. Args: stat: either a dictionary or list as described below varfonts: a list of variable TTFont instances has_italic: a boolean indicating whether the family contains an italic. If not provided, the stylename of the font files are inspected to determine if any of them contain the word ``Italic``. The ``stat`` parameter should normally be a list of axis dictionaries in the format used by ``buildStatTable``. This list should *not* contain an entry for the ``ital`` axis, as this entry will be generated as appropriate for each font if ``has_italic`` is True. For example:: varfonts = [ "Source-Regular-VF[wdth].ttf", "Source-Italic-VF[wdth].ttf" ] stat = [ { "tag":"wdth", "name": "Width", "values": [ ... ] } ] Alternately, to allow different STAT table entries for each font, the ``stat`` parameter may be a dictionary, whose keys are source IDs (usually source filenames) corresponding to the appropriate entry in the ``varfonts`` dictionary and whose values are the list of axis dictionaries for the font. Note that in this case, the axes list is passed to ``buildStatTable`` with no further manipulation, meaning that if you want an ``ital`` axis, you should specify it manually as part of the dictionary. For example:: stat = { "Font[wght].ttf": [ { "tag":"wdth", "name": "Width", "values": [ ... ] }, { "tag":"ital", "name": "Italic", "values": [ ... ] } ], "Font-Italic[wght].ttf": [ { "tag":"wdth", "name": "Width", "values": [ ... ] }, { "tag":"ital", "name": "Italic", "values": [ ... ] } ] } """ assert all("fvar" in f for f in varfonts) # Check we have no italic if isinstance(stat, list): if has_italic is None: has_italic = any(font_is_italic(f) for f in varfonts) if has_italic: for ax in stat: if ax["name"] == "ital": raise ValueError("ital axis should not appear in stat config") ital_stat_for_roman = { "name": "Italic", "tag": "ital", "values": [dict(value=0, name="Roman", flags=0x2, linkedValue=1)] } ital_stat_for_italic = { "name": "Italic", "tag": "ital", "values": [dict(value=1, name="Italic")] } stat.append({}) # We will switch this entry between Roman and Italic for ttFont in varfonts: filename = os.path.basename(ttFont.reader.file.name) if isinstance(stat, dict): if filename not in stat: raise ValueError("Filename %s not found in stat dictionary" % filename) this_stat = stat[filename] else: if has_italic: if font_is_italic(ttFont): stat[-1] = ital_stat_for_italic else: stat[-1] = ital_stat_for_roman this_stat = stat buildStatTable(ttFont, this_stat)
def gen_stat_tables( ttFonts, axis_order, elided_axis_values=None, axis_reg=axis_registry ): """ Generate a stat table for each font in a family using the Google Fonts Axis Registry. Args: ttFonts: an iterable containing ttFont instances axis_order: a list containing the axis order elided_axis_values: a dict containing axes and their values to elide e.g {"wght": [400], "wdth": [100]} axis_reg: Google Fonts axis registry """ # Heuristic: # 1. Gen a STAT table for each font using their fvar tables only # 2. Collect all the axes which exist in every font's family name and # and style name # 3. Add further Axis Records to each font's stat table for the axes we # found in step 2. Only add them if the stat table doesn't contain them # already. # 4. Add an AxisValue to each of the Axes Records we added in step 3. # For each axis in each font, do the following: # a. If a font's name table contains the axis and it is not in the # fvar, we will create a new Axis Value using the axis registry # fallbacks. # b. If a font's name table doesn't contain the axis, we will create a # new Axis Value based the default values found in the axis registry # # Example: # # Test Case: # Axes in family names: ["wdth", "wght", "ital"] # Font StyleName = "Condensed Bold" # Axes in font fvar = ["wght"] # # a result: # axisValue = {"name": "Condensed", "value": 75.0} # "Condensed" exists in our axis registry as a fallback in the # wdth axis # # b result: # AxisValue = {"name": "Roman", "value": 0.0, flags=0x2} # Since there isn't an ital token in the Font family name or # style name, the AxisValue will be based on the default values # for the axis in our axis registry # # 4. For each stat table, update Axis Values which should be linked # 5. For each stat table, update Axis Values which should be elided based # on the user arg elided_axis_values (optional) # 6. For each stat table, sort axes based on the arg axis_order # 7. Use fontTools to build each stat table for each font validate_family_fvar_tables(ttFonts) stat_tables = [_gen_stat_from_fvar(f) for f in ttFonts] axes_in_family_name_records = _axes_in_family_name_records(ttFonts) stat_tables = [ _append_non_fvar_axes_to_stat(ttFont, stat, axes_in_family_name_records) for ttFont, stat in zip(ttFonts, stat_tables) ] seen_axis_values = _seen_axis_values(stat_tables) stat_tables = [ _add_linked_axis_values_to_stat(s, seen_axis_values) for s in stat_tables ] if elided_axis_values: stat_tables = [ _add_elided_axis_values_to_stat(s, elided_axis_values) for s in stat_tables ] # TODO make axis_order an optional arg. We can only do this once we # have established an axis order in the axis registry validate_axis_order(axis_order, set(seen_axis_values.keys())) assert len(stat_tables) == len(ttFonts) axis_order = [a for a in axis_order if a in seen_axis_values.keys()] for stat_table, ttFont in zip(stat_tables, ttFonts): stat_table = [stat_table[axis] for axis in axis_order] _update_fvar_nametable_records(ttFont, stat_table) buildStatTable(ttFont, stat_table)
import sys from fontTools.otlLib.builder import buildStatTable from fontTools.ttLib import TTFont from make_designspace import (wghts, ESHPs, EGRDs) path = sys.argv[1] font = TTFont(path) wght_axis = dict( tag="wght", name="Weight", values=[dict(value=v, name=n) for v, _, n in wghts if n is not None] ) shape_axis = dict( tag="ESHP", name="Element Shape", values=[dict(value=v, name=n) for v, _, n in ESHPs if n is not None] ) grid_axis = dict( tag="EGRD", name="Element Grid", values=[dict(value=v, name=n) for v, _, n in EGRDs if n is not None] ) buildStatTable(font, [wght_axis, shape_axis, grid_axis], elidedFallbackName=2) font.save(path) print("Added STAT table to %s" % path)
widthDict = dict( tag="wdth", name="Width", values=[ dict(nominalValue=60, name="Condensed", rangeMinValue=60, rangeMaxValue=70), dict(nominalValue=80, name="SemiCondensed", rangeMinValue=70, rangeMaxValue=90), dict(nominalValue=100, name="Normal", flags=0x2, rangeMinValue=90, rangeMaxValue=100), ] ) format2RomanAxes = [ weightDict, widthDict, dict( tag="ital", name="Italic", values=[dict(value=0, name="Roman", linkedValue=1, flags=0x2)] ) ] ttfont = ttLib.TTFont(inRomanFont) builder.buildStatTable(ttfont,format2RomanAxes) # ttfont['name'].setName("JuniusVFRoman", 25, 1, 0, 0) ttfont['name'].setName("JuniusVFRoman", 25, 3, 1, 0x409) for inst in ttfont['fvar'].instances: subfamilyName = ttfont['name'].getName( inst.subfamilyNameID,3,1,0x409).toUnicode().replace(" ","") inst.postscriptNameID = ttfont['name'].addName("JuniusVFRoman" + "-" + subfamilyName, platforms=((3,1,0x409),)) ttfont['name'].removeNames(platformID=1) ttfont.save(outRomanFont)
2, 3, 1, 1033).toUnicode() else "Roman" ps_family_name = f"{family_name.replace(' ', '')}{font_style}" nametable.setName(ps_family_name, 25, 3, 1, 1033) for instance in fvar.instances: instance_style = nametable.getName(instance.subfamilyNameID, 3, 1, 1033).toUnicode() instance_style = instance_style.replace("Italic", "").strip().replace(" ", "") if instance_style == "": instance_style = "Regular" ps_name = f"{ps_family_name}-{instance_style}" instance.postscriptNameID = _addName(nametable, ps_name, 256) buildStatTable(ttFont, axes) updateFvar(ttFont) statTable = ttFont['STAT'].table ## Testing STAT version change # statTable.Version = 0x00010001 # statTable.Version = 0x00010002 ttFont.save(file) print() print() print(basename(file).upper()) print('Added STAT Table version %s.' % (statTable.Version))
def rebuildStatTable(font, designspace): # # Changing this code? See discussion at https://github.com/rsms/inter/issues/308 # if not 'fvar' in font: raise Exception('missing fvar table in font') axes = [dict(tag=a.axisTag, name=a.axisNameID) for a in font['fvar'].axes] # isMultiAxis is true when compiling the multi-axis VF, # false when compiling e.g. Inter-roman.var.ttf isMultiAxis = False if len(axes) > 1: isMultiAxis = True axisTagToName = dict() for axis in designspace.axes: axisTagToName[axis.tag] = axis.name weightAxisName = axisTagToName['wght'] slantAxisName = axisTagToName.get('slnt', 'Slant') regularWeightValueEntry = None weightValues = [] slantValues = [] extremeSlantValue = 0 for instance in designspace.instances: weightValue = instance.location[weightAxisName] slantValue = instance.location.get(slantAxisName, 0) if slantValue != 0: # slanted (we only make one entry: "Italic") if isMultiAxis and weightValue == 400: extremeSlantValue = slantValue slantValues.append({ 'name': instance.styleName, 'value': slantValue, }) else: # upright v = { 'name': instance.styleName, 'value': weightValue, } if weightValue == 400: v['flags'] = OT_ELIDABLE_AXIS_VALUE_NAME v['linkedValue'] = 700 # style link to "Bold" regularWeightValueEntry = v weightValues.append(v) # "Regular" entry for the slant axis, linked with the "Italic" entry if isMultiAxis: slantValues.append({ 'name': regularWeightValueEntry['name'], 'value': 0, 'flags': OT_ELIDABLE_AXIS_VALUE_NAME, 'linkedValue': extremeSlantValue, }) for a in axes: tag = a['tag'] if tag == 'wght': a['values'] = weightValues elif tag == 'slnt' and len(slantValues) > 0: a['values'] = slantValues buildStatTable(font, axes)