mirror of
https://github.com/weiweihuanghuang/Work-Sans.git
synced 2024-11-22 21:33:10 +03:00
Cleanup repo
This commit is contained in:
parent
2d60d9aab2
commit
261ea8b355
40
sources/build.sh
Executable file
40
sources/build.sh
Executable file
@ -0,0 +1,40 @@
|
||||
cp WorkSans-Upright.glyphs WorkSans-UprightBuild.glyphs
|
||||
|
||||
# Add bracket layers to build version
|
||||
# python2 tools/fixBrackets.py WorkSans-UprightBuild.glyphs
|
||||
|
||||
fontmake -o variable -g WorkSans-UprightBuild.glyphs
|
||||
|
||||
mv variable_ttf/WorkSans-VF.ttf WorkSans-VF.ttf
|
||||
|
||||
rm -rf WorkSans-UprightBuild.glyphs
|
||||
rm -rf master_ufo
|
||||
rm -rf variable_ttf
|
||||
|
||||
python ./addItalics.py WorkSans-VF.ttf
|
||||
|
||||
gftools fix-nonhinting WorkSans-VF.ttf WorkSans-VF.ttf
|
||||
gftools fix-dsig --autofix WorkSans-VF.ttf
|
||||
gftools fix-gasp WorkSans-VF.ttf
|
||||
ttfautohint WorkSans-VF.ttf WorkSans-VF-Hinted.ttf --verbose
|
||||
|
||||
python ./gftools-fix-vf-meta.py WorkSans-VF-Hinted.ttf
|
||||
|
||||
## move font into folder of dist/, with timestamp, then fontbake the font
|
||||
python3 ./distdate-and-fontbake.py WorkSans-VF-Hinted.ttf
|
||||
rm -rf variable_ttf
|
||||
|
||||
# ttx WorkSans-VF.ttf
|
||||
|
||||
# rm -rf WorkSans-VF.ttf
|
||||
# rm -rf WorkSans-Upright-VF-backup-fonttools-prep-gasp.ttf
|
||||
|
||||
# cat WorkSans-Upright-VF.ttx | tr '\n' '\r' | sed -e "s,<STAT>.*<\/STAT>,$(cat tools/patch1.xml | tr '\n' '\r')," | tr '\r' '\n' > WorkSans-Upright-VF-STAT.ttx
|
||||
|
||||
# rm -rf WorkSans-Upright-VF.ttx
|
||||
|
||||
# ttx WorkSans-Upright-VF-STAT.ttx
|
||||
|
||||
# rm -rf WorkSans-Upright-VF-STAT.ttx
|
||||
|
||||
# mv WorkSans-Upright-VF-STAT.ttf WorkSans-UprightBeta-VF.ttf
|
26
sources/tools/addItalics.py
Executable file
26
sources/tools/addItalics.py
Executable file
@ -0,0 +1,26 @@
|
||||
import os
|
||||
import sys
|
||||
import fontTools
|
||||
from fontTools.ttLib import TTFont
|
||||
from fontTools.varLib.featureVars import addFeatureVariations
|
||||
from fontTools.ttx import makeOutputFileName
|
||||
|
||||
|
||||
inputTTF = sys.argv[1]
|
||||
|
||||
# f = TTFont(fontPath)
|
||||
f = TTFont(inputTTF)
|
||||
|
||||
condSubst = [
|
||||
# A list of (Region, Substitution) tuples.
|
||||
([{"wght": (0.158, 0.564)}], {"a": "a.swsh"}),
|
||||
([{"wght": (0.868, 1.0)}], {"dollar": "dollar.201_230"}),
|
||||
]
|
||||
|
||||
addFeatureVariations(f, condSubst)
|
||||
|
||||
# newFontPath = fontPath.split(".")[0] + "-italic.ttf"
|
||||
# f.save(fontPath)
|
||||
extension = os.path.splitext(inputTTF)[1]
|
||||
outputTTF = makeOutputFileName(inputTTF, '', extension)
|
||||
f.save(outputTTF)
|
330
sources/tools/gftools-fix-vf-meta.py
Executable file
330
sources/tools/gftools-fix-vf-meta.py
Executable file
@ -0,0 +1,330 @@
|
||||
"""
|
||||
Fontmake can only generate a single variable font. It cannot generate a
|
||||
family of variable fonts, that are related to one another.
|
||||
|
||||
This script will update the nametables and STAT tables so a family
|
||||
which has more than one variable font will work correctly in desktop
|
||||
applications.
|
||||
|
||||
It will also work on single font VF families by creating a better STAT
|
||||
table.
|
||||
|
||||
TODO make script work on VFs which have multiple axises. We'll need to
|
||||
change the axis array format to v4 (we're using v1),
|
||||
https://docs.microsoft.com/en-us/typography/opentype/spec/stat#axis-value-table-format-4
|
||||
|
||||
Atm, the script will work well for single axis fonts and families which
|
||||
have a single vf for Roman and another for Italic/Condensed, both using the wght
|
||||
axis (covers 95% of GF cases).
|
||||
"""
|
||||
from argparse import ArgumentParser
|
||||
from fontTools.ttLib import TTFont, newTable
|
||||
from fontTools.ttLib.tables import otTables
|
||||
|
||||
|
||||
OS_2_WEIGHT_CLASS = {
|
||||
'Thin': 250,
|
||||
'ExtraLight': 275,
|
||||
'Light': 300,
|
||||
'Regular': 400,
|
||||
'': 400,
|
||||
'Medium': 500,
|
||||
'SemiBold': 600,
|
||||
'Bold': 700,
|
||||
'ExtraBold': 800,
|
||||
'Black': 900,
|
||||
}
|
||||
|
||||
|
||||
def _parse_styles(stylename):
|
||||
bold, italic = False, False
|
||||
if 'Italic' in stylename:
|
||||
italic = True
|
||||
bold = False
|
||||
if 'Bold' == stylename or 'Bold Italic' == stylename:
|
||||
bold = True
|
||||
return bold, italic
|
||||
|
||||
|
||||
def set_fsselection(style, fsselection,):
|
||||
bold, italic = _parse_styles(style)
|
||||
|
||||
mask = 0b1100001
|
||||
fsselection = (fsselection | mask) ^ mask
|
||||
|
||||
if bold:
|
||||
fsselection |= 0b100000
|
||||
else:
|
||||
fsselection |= 0b1000000
|
||||
if italic:
|
||||
# unset Reg bit
|
||||
fsselection = (fsselection | 0b1000000) ^ 0b1000000
|
||||
fsselection |= 0b1
|
||||
return fsselection
|
||||
|
||||
|
||||
def set_mac_style(stylename, macstyle):
|
||||
bold, italic = _parse_styles(stylename)
|
||||
|
||||
mask = ~0b11
|
||||
bold_bit = 0b1 if bold else 0b0
|
||||
italic_bit = 0b10 if italic else 0b0
|
||||
|
||||
macstyle = (macstyle | mask) ^ mask
|
||||
macstyle |= (bold_bit + italic_bit)
|
||||
return macstyle
|
||||
|
||||
|
||||
def set_weight_class(stylename):
|
||||
weight = stylename.replace('Italic', '').replace(' ', '')
|
||||
return OS_2_WEIGHT_CLASS[weight]
|
||||
|
||||
|
||||
def fonts_are_same_family(ttfonts):
|
||||
"""Check fonts have the same preferred family name or family name"""
|
||||
family_names = []
|
||||
for ttfont in ttfonts:
|
||||
pref_family_name = ttfont['name'].getName(16, 3, 1, 1033)
|
||||
family_name = ttfont['name'].getName(1, 3, 1, 1033)
|
||||
name = pref_family_name if pref_family_name else family_name
|
||||
family_names.append(name.toUnicode())
|
||||
if len(set(family_names)) != 1:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def fix_nametable(ttfont):
|
||||
"""Cleanup nametable issues caused by fontmake"""
|
||||
table = ttfont['name']
|
||||
family_name = table.getName(1, 3, 1, 1033).toUnicode()
|
||||
|
||||
# Remove style from family name. Often happens for Italic fonts
|
||||
# Family Name Light --> Family Name
|
||||
for style in (' Regular', ' Light', ' Bold'):
|
||||
if style in family_name:
|
||||
table.setName(family_name.replace(style, ''), 1, 3, 1, 1033)
|
||||
# Remove preferred family name and prefered style
|
||||
idx = 0
|
||||
while idx < len(table.names) - 1:
|
||||
if table.names[idx].nameID in [16, 17]:
|
||||
table.names.pop(idx)
|
||||
idx = 0
|
||||
else:
|
||||
idx += 1
|
||||
|
||||
# Update existing nameids based on the varfont's default style
|
||||
default_style = _get_vf_default_style(ttfont)
|
||||
family_name = table.getName(1, 3, 1, 1033).toUnicode()
|
||||
table.setName(default_style, 2, 3, 1, 1033)
|
||||
|
||||
fullname = '{} {}'.format(family_name, default_style)
|
||||
table.setName(unicode(fullname), 4, 3, 1, 1033)
|
||||
|
||||
psname = '{}-{}'.format(family_name, default_style.replace(' ', ''))
|
||||
table.setName(unicode(psname), 6, 3, 1, 1033)
|
||||
|
||||
# uniqueid basedon fontmake output version;vendorid;psname
|
||||
font_version = format(ttfont['head'].fontRevision, '.3f')
|
||||
vendor = ttfont['OS/2'].achVendID
|
||||
uniqueid = '{};{};{}'.format(font_version, vendor, psname)
|
||||
table.setName(unicode(uniqueid), 3, 3, 1, 1033)
|
||||
|
||||
|
||||
def fix_bits(ttfont):
|
||||
"""Set fsSelection, macStyle and usWeightClass to correct values.
|
||||
|
||||
The values must be derived from the default style. By default, the
|
||||
Regular instance's values are used"""
|
||||
dflt_style = _get_vf_default_style(ttfont)
|
||||
ttfont['OS/2'].fsSelection = set_fsselection(
|
||||
dflt_style, ttfont['OS/2'].fsSelection
|
||||
)
|
||||
ttfont['OS/2'].usWeightClass = set_weight_class(dflt_style)
|
||||
ttfont['head'].macStyle = set_mac_style(
|
||||
dflt_style, ttfont['head'].macStyle
|
||||
)
|
||||
|
||||
|
||||
def create_stat_table(ttfont):
|
||||
"""Atm, Fontmake is only able to produce a basic stat table. Because of
|
||||
this, we'll create a STAT using the font's fvar table."""
|
||||
stat = newTable('STAT')
|
||||
stat.table = otTables.STAT()
|
||||
stat.table.Version = 0x00010002
|
||||
|
||||
# # Build DesignAxisRecords from fvar
|
||||
stat.table.DesignAxisRecord = otTables.AxisRecordArray()
|
||||
stat.table.DesignAxisRecord.Axis = []
|
||||
|
||||
stat_axises = stat.table.DesignAxisRecord.Axis
|
||||
|
||||
# TODO (M Foley) add support for fonts which have multiple
|
||||
# axises e.g Barlow
|
||||
if len(ttfont['fvar'].axes) > 1:
|
||||
raise Exception('VFs with more than one axis are currently '
|
||||
'not supported.')
|
||||
|
||||
for idx, axis in enumerate(ttfont['fvar'].axes):
|
||||
append_stat_axis(stat, axis.axisTag, axis.axisNameID)
|
||||
|
||||
# Build AxisValueArrays for each namedInstance from fvar namedInstances
|
||||
stat.table.AxisValueArray = otTables.AxisValueArray()
|
||||
stat.table.AxisValueArray.AxisValue = []
|
||||
|
||||
for idx, instance in enumerate(ttfont['fvar'].instances):
|
||||
append_stat_record(stat, 0, instance.coordinates.values()[0], instance.subfamilyNameID)
|
||||
|
||||
# Set ElidedFallbackNameID
|
||||
stat.table.ElidedFallbackNameID = 2
|
||||
ttfont['STAT'] = stat
|
||||
|
||||
|
||||
def _get_vf_types(ttfonts):
|
||||
styles = []
|
||||
for ttfont in ttfonts:
|
||||
styles.append(_get_vf_type(ttfont))
|
||||
return styles
|
||||
|
||||
|
||||
def _get_vf_type(ttfont):
|
||||
style = ttfont['name'].getName(2, 3, 1, 1033).toUnicode()
|
||||
return 'Italic' if 'Italic' in style else 'Roman'
|
||||
|
||||
|
||||
def _get_vf_default_style(ttfont):
|
||||
"""Return the name record string of the default style"""
|
||||
default_fvar_val = ttfont['fvar'].axes[0].defaultValue
|
||||
|
||||
name_id = None
|
||||
for inst in ttfont['fvar'].instances:
|
||||
if inst.coordinates['wght'] == default_fvar_val:
|
||||
name_id = inst.subfamilyNameID
|
||||
return ttfont['name'].getName(name_id, 1, 0, 0).toUnicode()
|
||||
|
||||
|
||||
def add_other_vf_styles_to_nametable(ttfont, text_records):
|
||||
"""Each nametable in a font must reference every font in the family.
|
||||
Since fontmake doesn't append the other families to the nametable,
|
||||
we'll do this ourselves. Skip this step if these records already
|
||||
exist."""
|
||||
found = set()
|
||||
for name in ttfont['name'].names[:-len(text_records)-1:-1]:
|
||||
found.add(name.toUnicode())
|
||||
leftover = set(text_records) - found
|
||||
|
||||
if leftover:
|
||||
nameid = ttfont['name'].names[-1].nameID + 1
|
||||
for record in leftover:
|
||||
ttfont['name'].setName(unicode(record), nameid, 3, 1, 1033)
|
||||
nameid += 1
|
||||
|
||||
|
||||
def get_custom_name_record(ttfont, text):
|
||||
"""Return a name record by text. Record ID must be greater than 255"""
|
||||
for record in ttfont['name'].names[::-1]:
|
||||
if record.nameID > 255:
|
||||
rec_text = record.toUnicode()
|
||||
if rec_text == text:
|
||||
return record
|
||||
return None
|
||||
|
||||
|
||||
def append_stat_axis(stat, tag, namerecord_id):
|
||||
"""Add a STAT axis if the tag does not exist already."""
|
||||
has_tags = []
|
||||
axises = stat.table.DesignAxisRecord.Axis
|
||||
for axis in axises:
|
||||
has_tags.append(axis.AxisTag)
|
||||
|
||||
if tag in has_tags:
|
||||
raise Exception('{} has already been declared in the STAT table')
|
||||
|
||||
axis_record = otTables.AxisRecord()
|
||||
axis_record.AxisTag = tag
|
||||
axis_record.AxisNameID = namerecord_id
|
||||
axis_record.AxisOrdering = len(axises)
|
||||
axises.append(axis_record)
|
||||
|
||||
|
||||
def append_stat_record(stat, axis_index, value, namerecord_id, linked_value=None):
|
||||
records = stat.table.AxisValueArray.AxisValue
|
||||
axis_record = otTables.AxisValue()
|
||||
axis_record.Format = 1
|
||||
axis_record.ValueNameID = namerecord_id
|
||||
axis_record.Value = value
|
||||
axis_record.AxisIndex = axis_index
|
||||
|
||||
axis_record.Flags = 0
|
||||
if linked_value:
|
||||
axis_record.Format = 3
|
||||
axis_record.LinkedValue = linked_value
|
||||
records.append(axis_record)
|
||||
|
||||
|
||||
def get_stat_axis_index(ttfont, axis_name):
|
||||
axises = ttfont['STAT'].table.DesignAxisRecord.Axis
|
||||
available_axises = [a.AxisTag for a in axises]
|
||||
for idx, axis in enumerate(axises):
|
||||
if axis.AxisTag == axis_name:
|
||||
return idx
|
||||
raise Exception('{} is not a valid axis. Font has [{}] axises'.format(
|
||||
axis_name, available_axises)
|
||||
)
|
||||
|
||||
|
||||
def set_stat_for_font_in_family(ttfont, family_styles):
|
||||
"""Based on examples from:
|
||||
https://docs.microsoft.com/en-us/typography/opentype/spec/stat"""
|
||||
font_type = _get_vf_type(ttfont)
|
||||
# See example 5
|
||||
if font_type == 'Roman' and 'Italic' in family_styles:
|
||||
name_record = get_custom_name_record(ttfont, 'Italic')
|
||||
append_stat_axis(ttfont['STAT'], 'ital', name_record.nameID)
|
||||
|
||||
name_record = get_custom_name_record(ttfont, 'Roman')
|
||||
axis_idx = get_stat_axis_index(ttfont, 'ital')
|
||||
append_stat_record(ttfont['STAT'], axis_idx, 0, name_record.nameID, linked_value=1.0)
|
||||
|
||||
elif font_type == 'Italic' and 'Roman' in family_styles:
|
||||
name_record = get_custom_name_record(ttfont, 'Italic')
|
||||
append_stat_axis(ttfont['STAT'], 'ital', name_record.nameID)
|
||||
|
||||
name_record = get_custom_name_record(ttfont, 'Italic')
|
||||
axis_idx = get_stat_axis_index(ttfont, 'ital')
|
||||
append_stat_record(ttfont['STAT'], axis_idx, 1.0, name_record.nameID)
|
||||
|
||||
|
||||
def harmonize_vf_families(ttfonts):
|
||||
"""Make sure the fonts which are part of a vf family reference each other
|
||||
in both the nametable and STAT table. For examples see:
|
||||
https://docs.microsoft.com/en-us/typography/opentype/spec/stat
|
||||
|
||||
"""
|
||||
family_styles = _get_vf_types(ttfonts)
|
||||
for ttfont in ttfonts:
|
||||
add_other_vf_styles_to_nametable(ttfont, family_styles)
|
||||
set_stat_for_font_in_family(ttfont, family_styles)
|
||||
|
||||
|
||||
def main():
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument('fonts', nargs='+',
|
||||
help='All fonts within a font family must be included')
|
||||
args = parser.parse_args()
|
||||
font_paths = args.fonts
|
||||
ttfonts = [TTFont(p) for p in font_paths]
|
||||
if not fonts_are_same_family(ttfonts):
|
||||
raise Exception('Fonts have different family_names: [{}]'.format(
|
||||
', '.join(map(os.path.basename, ttfonts))
|
||||
))
|
||||
|
||||
map(fix_nametable, ttfonts)
|
||||
map(fix_bits, ttfonts)
|
||||
map(create_stat_table, ttfonts)
|
||||
harmonize_vf_families(ttfonts)
|
||||
|
||||
for path, ttfont in zip(font_paths, ttfonts):
|
||||
ttfont.save(path + '.fix')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
Loading…
Reference in New Issue
Block a user