def test_constructs(self): for name in ("Attach enum markClass language_required " "GlyphClassDef LigatureCaretByIndex LigatureCaretByPos " "lookup lookupflag").split(): font = makeTTFont() addOpenTypeFeatures(self.getpath("%s.fea" % name), font) self.expect_ttx(font, self.getpath("%s.ttx" % name))
def main(args=None): parser = argparse.ArgumentParser( description="Use fontTools to compile OpenType feature files (*.fea).") parser.add_argument( "input_fea", metavar="FEATURES", help="Path to the feature file") parser.add_argument( "input_font", metavar="INPUT_FONT", help="Path to the input font") parser.add_argument( "-o", "--output", dest="output_font", metavar="OUTPUT_FONT", help="Path to the output font.") parser.add_argument( "-t", "--tables", metavar="TABLE_TAG", choices=Builder.supportedTables, nargs='+', help="Specify the table(s) to be built.") parser.add_argument( "-v", "--verbose", help="increase the logger verbosity. Multiple -v " "options are allowed.", action="count", default=0) options = parser.parse_args(args) levels = ["WARNING", "INFO", "DEBUG"] configLogger(level=levels[min(len(levels) - 1, options.verbose)]) output_font = options.output_font or makeOutputFileName(options.input_font) log.info("Compiling features to '%s'" % (output_font)) font = TTFont(options.input_font) addOpenTypeFeatures(font, options.input_fea, tables=options.tables) font.save(output_font)
def _layoutEngineOTLTablesRepresentationFactory(layoutEngine): import os import tempfile font = layoutEngine.font gdef = gsub = gpos = None if font.features.text: otf = TTFont() otf.setGlyphOrder(sorted(font.keys())) # XXX hack around fontTools only reading from disk fd, feaPath = tempfile.mkstemp() f = open(feaPath, "w") f.write(font.features.text) f.close() # compile with fontTools try: addOpenTypeFeatures(feaPath, otf) except: import traceback print(traceback.format_exc(5)) finally: os.close(fd) os.remove(feaPath) if "GDEF" in otf: gdef = otf["GDEF"] if "GSUB" in otf: gsub = otf["GSUB"] if "GPOS" in otf: gpos = otf["GPOS"] return gdef, gsub, gpos
def build(self, featureFile): path = self.temp_path(suffix=".fea") with codecs.open(path, "wb", "utf-8") as outfile: outfile.write(featureFile) font = makeTTFont() addOpenTypeFeatures(path, font) return font
def setupFile_featureTables(self): """ Compile and return OpenType feature tables from the source. Raises a FeaLibError if the feature compilation was unsuccessful. **This should not be called externally.** Subclasses may override this method to handle the table compilation in a different way if desired. """ if self.mtiFeaFiles is not None: for tag, feapath in self.mtiFeaFiles.items(): with open(feapath) as feafile: table = mtiLib.build(feafile, self.outline) assert table.tableTag == tag self.outline[tag] = table elif self.features.strip(): if self.font.path is not None: self.features = forceAbsoluteIncludesInFeatures(self.features, self.font.path) fd, fea_path = tempfile.mkstemp() with open(fea_path, "w") as feafile: feafile.write(self.features) addOpenTypeFeatures(fea_path, self.outline) os.close(fd) os.remove(fea_path)
def build(self, featureFile): path = self.temp_path(suffix=".fea") with open(path, "w", encoding="utf-8") as outfile: outfile.write(featureFile) font = makeTTFont() addOpenTypeFeatures(font, path) return font
def build(args): font, features = merge(args) build_encoded(font, features) with tempfile.NamedTemporaryFile(mode="r", suffix=args.out_file) as tmp: font.generate(tmp.name, flags=["round", "opentype"]) ttfont = TTFont(tmp.name) try: builder.addOpenTypeFeatures(ttfont, features) except: with tempfile.NamedTemporaryFile(mode="w+", delete=False) as tmp: tmp.write(features.asFea()) print("Failed! Inspect temporary file: %r" % tmp.name) raise # Filter-out useless Macintosh names ttfont["name"].names = [n for n in ttfont["name"].names if n.platformID != 1] # https://github.com/fontforge/fontforge/pull/3235 # fontDirectionHint is deprecated and must be set to 2 ttfont["head"].fontDirectionHint = 2 # unset bits 6..10 ttfont["head"].flags &= ~0x7e0 # Drop useless table with timestamp if "FFTM" in ttfont: del ttfont["FFTM"] ttfont.save(args.out_file)
def addFeatures(self, fea, clear=True): feaPath = tempfile.mktemp() f = open(feaPath, "w") f.write(fea) f.close() if clear: if "GSUB" in self.source: del self.source["GSUB"] if "GPOS" in self.source: del self.source["GPOS"] try: addOpenTypeFeatures(self.source, feaPath) except Exception: import traceback print(traceback.format_exc(5)) finally: os.remove(feaPath) # XXX hacking into fontTools for tableName in ("GDEF", "GSUB", "GPOS"): if tableName in self.source: table = self.source[tableName] compiled = table.compile(self.source) table.decompile(compiled, self.source) self.loadFeatures()
def compileGSUB(featureFile, glyphOrder): """ Compile and return a GSUB table from `featureFile` (feaLib FeatureFile), using the given `glyphOrder` (list of glyph names). """ font = ttLib.TTFont() font.setGlyphOrder(glyphOrder) addOpenTypeFeatures(font, featureFile, tables={"GSUB"}) return font.get("GSUB")
def check_feature_file(self, name): font = makeTTFont() addOpenTypeFeatures(font, self.getpath("%s.fea" % name)) self.expect_ttx(font, self.getpath("%s.ttx" % name)) # Make sure we can produce binary OpenType tables, not just XML. for tag in ('GDEF', 'GSUB', 'GPOS'): if tag in font: font[tag].compile(font)
def check_feature_file(self, name): font = makeTTFont() feapath = self.getpath("%s.fea" % name) addOpenTypeFeatures(font, feapath) self.expect_ttx(font, self.getpath("%s.ttx" % name)) # Make sure we can produce binary OpenType tables, not just XML. for tag in ('GDEF', 'GSUB', 'GPOS'): if tag in font: font[tag].compile(font) debugttx = self.getpath("%s-debug.ttx" % name) if os.path.exists(debugttx): addOpenTypeFeatures(font, feapath, debug=True) self.expect_ttx(font, debugttx, replace = {"__PATH__": feapath})
def dlig2calt(fontPath, inplace=False): font = ttLib.TTFont(fontPath) unitWidth = font['hmtx']['space'][0] # 600 for most monospace fonts w/ UPM=1000 # make "LIG" glyph font['glyf'].__setitem__('LIG', font['glyf']['space']) font['hmtx'].__setitem__('LIG', font['hmtx']['space']) # update code ligature widths to be single units with left overhang for glyphName in font.getGlyphNames(): if font['hmtx'][glyphName][0] > unitWidth: decomposeAndRemoveOverlap(font, glyphName) # set width to space (e.g. 600), then offset left side to be negative oldWidth = font['hmtx'][glyphName][0] oldLSB = font['hmtx'][glyphName][1] widthDiff = oldWidth - unitWidth newLSB = oldLSB - widthDiff font['hmtx'].__setitem__(glyphName, (unitWidth, newLSB)) # Adjust coordinates in glyf table coords = font['glyf'][glyphName].coordinates phantoms = font['glyf'].getPhantomPoints(glyphName, font) adjustedCoords = [(x-widthDiff, y) for x, y in coords] adjustedPhantoms = [(0,0), (600,0), phantoms[-2], phantoms[-1]] newCoords = adjustedCoords+adjustedPhantoms # print(glyphName, newCoords, font) # DEBUGGING font['glyf'].setCoordinates(glyphName, newCoords, font) # add new feature code, using calt rather than dlig builder.addOpenTypeFeatures(font,"font-data/features/calt-generated--code_fonts_only.fea") # save font if inplace: font.save(fontPath) print("\nCode ligatures are now on by default.\n") else: newPath = fontPath.replace('.ttf','.calt_ligs.ttf') font.save(newPath) print("Saved font with feature 'dlig' changed to 'calt' at ", newPath)
def check_feature_file(self, name): font = makeTTFont() feapath = self.getpath("%s.fea" % name) addOpenTypeFeatures(font, feapath) self.expect_ttx(font, self.getpath("%s.ttx" % name)) # Check that: # 1) tables do compile (only G* tables as long as we have a mock font) # 2) dumping after save-reload yields the same TTX dump as before for tag in ('GDEF', 'GSUB', 'GPOS'): if tag in font: data = font[tag].compile(font) font[tag].decompile(data, font) self.expect_ttx(font, self.getpath("%s.ttx" % name)) # Optionally check a debug dump. debugttx = self.getpath("%s-debug.ttx" % name) if os.path.exists(debugttx): addOpenTypeFeatures(font, feapath, debug=True) self.expect_ttx(font, debugttx, replace={"__PATH__": feapath})
def compileGDEF(featureFile, glyphOrder): """Compile and return a GDEF table from `featureFile` (feaLib FeatureFile), using the given `glyphOrder` (list of glyph names). """ from fontTools.feaLib.ast import TableBlock font = ttLib.TTFont() font.setGlyphOrder(glyphOrder) gdefDefined = False for statement in featureFile.statements: if isinstance(statement, TableBlock) and statement.name == "GDEF": gdefDefined = True if not gdefDefined: addOpenTypeFeatures(font, featureFile, tables={"GDEF", "GPOS", "GSUB"}) else: addOpenTypeFeatures(font, featureFile, tables={"GDEF"}) return font.get("GDEF")
def check_feature_file(self, name): font = makeTTFont() if name.startswith("variable_"): font["name"] = newTable("name") addFvar(font, self.VARFONT_AXES, []) del font["name"] feapath = self.getpath("%s.fea" % name) addOpenTypeFeatures(font, feapath) self.expect_ttx(font, self.getpath("%s.ttx" % name)) # Check that: # 1) tables do compile (only G* tables as long as we have a mock font) # 2) dumping after save-reload yields the same TTX dump as before for tag in ("GDEF", "GSUB", "GPOS"): if tag in font: data = font[tag].compile(font) font[tag].decompile(data, font) self.expect_ttx(font, self.getpath("%s.ttx" % name)) # Optionally check a debug dump. debugttx = self.getpath("%s-debug.ttx" % name) if os.path.exists(debugttx): addOpenTypeFeatures(font, feapath, debug=True) self.expect_ttx(font, debugttx, replace={"__PATH__": feapath})
#!/usr/bin/env python from __future__ import print_function, division, absolute_import from fontTools.misc.py23 import * import fontTools.feaLib.builder as feaLibBuilder from fontTools.ttLib import TTFont import sys if len(sys.argv) != 4: print("usage: apply-feature-file.py features.fea in.ttf out.ttf") sys.exit(1) inputFeaturePath = sys.argv[1] inputFontPath = sys.argv[2] outputFontPath = sys.argv[3] font = TTFont(inputFontPath) feaLibBuilder.addOpenTypeFeatures(inputFeaturePath, font) font.save(outputFontPath)
def test_GSUB(self): for name in "2 3 6 8".split(): font = makeTTFont() addOpenTypeFeatures(self.getpath("GSUB_%s.fea" % name), font) self.expect_ttx(font, self.getpath("GSUB_%s.ttx" % name))
#!/usr/bin/env python from __future__ import print_function, division, absolute_import from fontTools.misc.py23 import * import fontTools.feaLib.builder as feaLibBuilder from fontTools.ttLib import TTFont import sys if len(sys.argv) != 4: print("usage: apply-feature-file.py features.fea in.ttf out.ttf") sys.exit(1) inputFeaturePath = sys.argv[1] inputFontPath = sys.argv[2] outputFontPath = sys.argv[3] font = TTFont(inputFontPath) feaLibBuilder.addOpenTypeFeatures(font, inputFeaturePath) font.save(outputFontPath)
deepcopy_glyph(font, ord('*'), ".cb") # open/close it deepcopy_glyph(font, ord('*'), ".oi") deepcopy_glyph(font, ord('*'), ".ci") # open/close mono deepcopy_glyph(font, ord('`'), ".om") deepcopy_glyph(font, ord('`'), ".cm") # open/close boldit deepcopy_glyph(font, ord('*'), ".obi") deepcopy_glyph(font, ord('*'), ".cbi") # open/close itbold deepcopy_glyph(font, ord('*'), ".oib") deepcopy_glyph(font, ord('*'), ".cib") # open/close all deepcopy_glyph(font, ord('*'), ".oa") deepcopy_glyph(font, ord('*'), ".ca") # hidden deepcopy_glyph(font, ord('\\'), ".h") font.generate('tmp.ttf') font.close() it.close() bold.close() boldit.close() mono.close() ft_font = TTFont('tmp.ttf') addOpenTypeFeatures(ft_font, 'md.fea', tables=['GSUB']) ft_font.save('fonts/output.ttf')
from __future__ import print_function, division, absolute_import from fontTools.misc.py23 import * import fontTools.feaLib.builder as feaLibBuilder from fontTools.ttLib import TTFont from fontTools import configLogger import sys import argparse parser = argparse.ArgumentParser( description="Use fontTools to compile OpenType features.") parser.add_argument("input_fea", metavar="FEATURES", help="Path to the feature file") parser.add_argument("input_font", metavar="INPUT", help="Path to the input font") parser.add_argument("output_font", metavar="OUTPUT", help="Path to the output font") parser.add_argument("-v", "--verbose", help="increase the logger verbosity. " "Multiple -v options are allowed.", action="count", default=0) options = parser.parse_args(sys.argv[1:]) levels = ["WARNING", "INFO", "DEBUG"] configLogger(level=levels[min(len(levels) - 1, options.verbose)]) font = TTFont(options.input_font) feaLibBuilder.addOpenTypeFeatures(font, options.input_fea) font.save(options.output_font)
def test_alternateSubst(self): font = makeTTFont() addOpenTypeFeatures(self.getpath("GSUB_3.fea"), font) self.expect_ttx(font, self.getpath("GSUB_3.ttx"))
def check_feature_file(self, name): font = makeTTFont() addOpenTypeFeatures(self.getpath("%s.fea" % name), font) self.expect_ttx(font, self.getpath("%s.ttx" % name))
def test_alternateSubst(self): font = TTFont() addOpenTypeFeatures(self.getpath("GSUB_2.fea"), font) self.expect_ttx(font, self.getpath("GSUB_2.ttx"))
def test_lookup(self): font = TTFont() addOpenTypeFeatures(self.getpath("lookup.fea"), font) self.expect_ttx(font, self.getpath("lookup.ttx"))
def test_language_required(self): font = TTFont() addOpenTypeFeatures(self.getpath("language_required.fea"), font) self.expect_ttx(font, self.getpath("language_required.ttx"))
def test_spec5d2(self): # OpenType Feature File specification, section 5.d, example 2. font = TTFont() addOpenTypeFeatures(self.getpath("spec5d2.fea"), font) self.expect_ttx(font, self.getpath("spec5d2.ttx"))
def test_spec4h1(self): # OpenType Feature File specification, section 4.h, example 1. font = TTFont() addOpenTypeFeatures(self.getpath("spec4h1.fea"), font) self.expect_ttx(font, self.getpath("spec4h1.ttx"))
def test_spec5fi1(self): # OpenType Feature File specification, section 5.f.i, example 1. font = makeTTFont() addOpenTypeFeatures(self.getpath("spec5fi1.fea"), font) self.expect_ttx(font, self.getpath("spec5fi1.ttx"))
def test_build_pre_parsed_ast_featurefile(self): f = StringIO("feature liga {sub f i by f_i;} liga;") tree = Parser(f).parse() font = makeTTFont() addOpenTypeFeatures(font, tree) assert "GSUB" in font
# -*- coding: utf-8 -*- from fontTools import ttLib from fontTools.feaLib.builder import addOpenTypeFeatures import argparse import sys parser = argparse.ArgumentParser() parser.add_argument('in_font') parser.add_argument('out_font') options = parser.parse_args(sys.argv[1:]) # Apply generated feature file to the unlinked font file ricty = ttLib.TTFont(options.in_font) addOpenTypeFeatures(ricty, 'ligatures.fea') ricty.save(options.out_font)
def test_build_pre_parsed_ast_featurefile(self): f = UnicodeIO("feature liga {sub f i by f_i;} liga;") tree = Parser(f).parse() font = makeTTFont() addOpenTypeFeatures(font, tree) assert "GSUB" in font
def test_multipleSubst(self): font = makeTTFont() addOpenTypeFeatures(self.getpath("GSUB_2.fea"), font) self.expect_ttx(font, self.getpath("GSUB_2.ttx"))
def patch_one_font(font, rename_font, add_underlines, shift_amount, squish, squish_all, add_commas, spaceless_commas, debug_annotate, do_decimals, group, sub_font): font.encoding = 'ISO10646' if group: add_underlines = False shift_amount = 100 squish = 0.85 squish_all = True mod_name = 'N' if add_commas: if spaceless_commas: mod_name += 'onoCommas' else: mod_name += 'ommas' if add_underlines: mod_name += 'umderline' if sub_font is not None: mod_name += 'Sub' # Cleaner name for what I expect to be a common combination if shift_amount == 100 and squish == 0.85 and squish_all: mod_name += 'Group' else: if shift_amount != 0: mod_name += 'Shift{}'.format(shift_amount) if squish != 1.0: squish_s = '{}'.format(squish) mod_name += 'Squish{}'.format(squish_s.replace('.', 'p')) if squish_all: mod_name += 'All' if debug_annotate: mod_name += 'Debug' if not do_decimals: mod_name += 'NoDecimals' # Rename font if rename_font: font.familyname += ' with ' + mod_name font.fullname += ' with ' + mod_name fontname, style = FONT_NAME_RE.match(font.fontname).groups() font.fontname = fontname + 'With' + mod_name if style is not None: font.fontname += style font.appendSFNTName('English (US)', 'Preferred Family', font.familyname) font.appendSFNTName('English (US)', 'Compatible Full', font.fullname) digit_names = [ font[code].glyphname for code in range(ord('0'), ord('9') + 1) ] test_names = [ font[code].glyphname for code in range(ord('A'), ord('J') + 1) ] underscore_name = font[ord('_')].glyphname dot_name = font[ord('.')].glyphname # print(digit_names) if sub_font is not None: sub_font = fontforge.open(sub_font.name) underscore_layer = font[underscore_name].layers[1] # 0xE900 starts an area spanning until 0xF000 that as far as I can tell nothing # popular uses. I checked the Apple glyph browser and Nerd Font. # Uses an array because of python closure capture semantics encoding_alloc = [0xE900] def make_copy(src_font, loc, to_name, add_underscore, add_comma, shift, squish, annotate_with): encoding = encoding_alloc[0] src_font.selection.select(loc) src_font.copy() font.selection.select(encoding) font.paste() glyph = font[encoding] glyph.glyphname = to_name if squish != 1.0: glyph.layers[1] = squish_layer(glyph.layers[1], squish) if shift != 0: glyph.layers[1] = shift_layer(glyph.layers[1], shift) if add_underscore: glyph.layers[1] += underscore_layer if add_comma: add_comma_to(glyph, font[ord(',')], spaceless_commas) if annotate_with is not None: annotate_glyph(glyph, annotate_with) encoding_alloc[0] += 1 for copy_i in range(0, NUM_DIGIT_COPIES): for digit_i in range(0, 10): shift = 0 if copy_i % 3 == 0: shift = -shift_amount elif copy_i % 3 == 2: shift = shift_amount in_alternating_group = (copy_i >= 3 and copy_i < 6) add_underscore = add_underlines and in_alternating_group add_comma = add_commas and (copy_i == 3 or copy_i == 6) annotate_with = font[ digit_names[copy_i]] if debug_annotate else None use_sub_font = (sub_font is not None) and in_alternating_group src_font = sub_font if use_sub_font else font make_copy(src_font, digit_names[digit_i], 'nd{}.{}'.format(copy_i, digit_i), add_underscore, add_comma, shift, squish, annotate_with) if squish_all and squish != 1.0: for digit in digit_names: glyph = font[digit] glyph.layers[1] = squish_layer(glyph.layers[1], squish) gen_feature(digit_names, underscore_name, dot_name, do_decimals) font.generate('out/tmp.ttf') ft_font = TTFont('out/tmp.ttf') addOpenTypeFeatures(ft_font, 'mods.fea', tables=['GSUB']) # replacement to comply with SIL Open Font License out_name = font.fullname.replace('Source ', 'Sauce ') ft_font.save(out_path(out_name)) print("> Created '{}'".format(out_name)) if sub_font is not None: sub_font.close() return out_name
def test_reverseChainingSingleSubst(self): font = makeTTFont() addOpenTypeFeatures(self.getpath("GSUB_8.fea"), font) self.expect_ttx(font, self.getpath("GSUB_8.ttx"))
from fontTools.ttLib import TTFont from fontTools import configLogger import sys import argparse parser = argparse.ArgumentParser( description="Use fontTools to compile OpenType features.") parser.add_argument("input_fea", metavar="FEATURES", help="Path to the feature file") parser.add_argument("input_font", metavar="INPUT", help="Path to the input font") parser.add_argument("output_font", metavar="OUTPUT", help="Path to the output font") parser.add_argument("-v", "--verbose", help="increase the logger verbosity. " "Multiple -v options are allowed.", action="count", default=0) options = parser.parse_args(sys.argv[1:]) levels = ["WARNING", "INFO", "DEBUG"] configLogger(level=levels[min(len(levels) - 1, options.verbose)]) font = TTFont(options.input_font) feaLibBuilder.addOpenTypeFeatures(font, options.input_fea) font.save(options.output_font)
def test_markClass(self): font = makeTTFont() addOpenTypeFeatures(self.getpath("markClass.fea"), font) self.expect_ttx(font, self.getpath("markClass.ttx"))
def test_GPOS(self): for name in "1 2 2b 3 4 5 6 8".split(): font = makeTTFont() addOpenTypeFeatures(self.getpath("GPOS_%s.fea" % name), font) self.expect_ttx(font, self.getpath("GPOS_%s.ttx" % name))
def test_spec(self): for name in ("4h1 5d1 5d2 5fi1 5fi2 5fi3 5fi4 5h1 " "6d2 6e 6f 6h_ii").split(): font = makeTTFont() addOpenTypeFeatures(self.getpath("spec%s.fea" % name), font) self.expect_ttx(font, self.getpath("spec%s.ttx" % name))
def test_GPOS(self): for name in "1 2 3".split(): font = makeTTFont() addOpenTypeFeatures(self.getpath("GPOS_%s.fea" % name), font) self.expect_ttx(font, self.getpath("GPOS_%s.ttx" % name))
def test_GPOS_type1(self): font = makeTTFont() addOpenTypeFeatures(self.getpath("GPOS_1.fea"), font) self.expect_ttx(font, self.getpath("GPOS_1.ttx"))
def make_font(file_paths, out_dir, revision, gsub_path, gpos_path, uvs_lst): cmap, gorder, validated_fpaths = {}, deque(), [] # build glyph order for fpath in file_paths: # derive glyph name from file name gname = os.path.splitext(os.path.basename(fpath))[0] # trim extension # validate glyph name if not glyph_name_is_valid(gname, fpath): continue # skip any duplicates and 'space' if gname in gorder or gname == 'space': log.warning("Skipped file '{}'. The glyph name derived from it " "is either a duplicate or 'space'.".format(fpath)) continue # limit the length of glyph name to 31 chars if len(gname) > 31: num = 0 trimmed_gname = get_trimmed_glyph_name(gname, num) while trimmed_gname in gorder: num += 1 trimmed_gname = get_trimmed_glyph_name(trimmed_gname, num) gorder.append(trimmed_gname) log.warning("Glyph name '{}' was trimmed to 31 characters: " "'{}'".format(gname, trimmed_gname)) else: gorder.append(gname) validated_fpaths.append(fpath) # add to cmap if RE_UNICODE.match(gname): uni_int = int(gname[1:], 16) # trim leading 'u' cmap[uni_int] = gname fb = FontBuilder(UPM, isTTF=False) fb.font['head'].fontRevision = float(revision) fb.font['head'].lowestRecPPEM = 12 cs_dict = {} cs_cache = {} for i, svg_file_path in enumerate(validated_fpaths): svg_file_realpath = os.path.realpath(svg_file_path) if svg_file_realpath not in cs_cache: svg_size = get_svg_size(svg_file_realpath) if svg_size is None: cs_dict[gorder[i]] = SPACE_CHARSTRING continue pen = T2CharStringPen(EMOJI_H_ADV, None) svg = SVGPath(svg_file_realpath, transform=(EMOJI_SIZE / svg_size, 0, 0, -EMOJI_SIZE / svg_size, (EMOJI_H_ADV * .5) - (EMOJI_SIZE * .5), EMOJI_H_ADV * ABOVE_BASELINE)) svg.draw(pen) cs = pen.getCharString() cs_cache[svg_file_realpath] = cs else: cs = cs_cache.get(svg_file_realpath) cs_dict[gorder[i]] = cs # add '.notdef', 'space' and zero-width joiner pen = T2CharStringPen(EMOJI_H_ADV, None) draw_notdef(pen) gorder.extendleft(reversed(['.notdef', 'space', 'ZWJ'])) cs_dict.update({ '.notdef': pen.getCharString(), 'space': SPACE_CHARSTRING, 'ZWJ': SPACE_CHARSTRING, }) cmap.update({ 32: 'space', # U+0020 160: 'space', # U+00A0 8205: 'ZWJ', # U+200D }) # add TAG LATIN LETTER glyphs and mappings for cdpt in TAG_LAT_LETTR: tag_gname = f'u{cdpt}' gorder.append(tag_gname) cs_dict[tag_gname] = SPACE_CHARSTRING cmap[int(cdpt, 16)] = tag_gname fb.setupGlyphOrder(list(gorder)) # parts of FontTools require a list fb.setupCharacterMap(cmap, uvs=uvs_lst) fb.setupCFF( PS_NAME, { 'version': revision, 'Notice': TRADEMARK, 'Copyright': COPYRIGHT, 'FullName': FULL_NAME, 'FamilyName': FAMILY_NAME, 'Weight': STYLE_NAME }, cs_dict, {}) glyphs_bearings = {} for gname, cs in cs_dict.items(): gbbox = cs.calcBounds(None) if gbbox: xmin, ymin, _, ymax = gbbox if ymax > ASCENT: log.warning("Top of glyph '{}' may get clipped. " "Glyph's ymax={}; Font's ascent={}".format( gname, ymax, ASCENT)) if ymin < DESCENT: log.warning("Bottom of glyph '{}' may get clipped. " "Glyph's ymin={}; Font's descent={}".format( gname, ymin, DESCENT)) lsb = xmin tsb = EMOJI_V_ADV - ymax - EMOJI_H_ADV * (1 - ABOVE_BASELINE) glyphs_bearings[gname] = (lsb, tsb) else: glyphs_bearings[gname] = (0, 0) h_metrics = {} v_metrics = {} for gname in gorder: h_metrics[gname] = (EMOJI_H_ADV, glyphs_bearings[gname][0]) v_metrics[gname] = (EMOJI_V_ADV, glyphs_bearings[gname][1]) fb.setupHorizontalMetrics(h_metrics) fb.setupVerticalMetrics(v_metrics) fb.setupHorizontalHeader(ascent=ASCENT, descent=DESCENT) v_ascent = EMOJI_H_ADV // 2 v_descent = EMOJI_H_ADV - v_ascent fb.setupVerticalHeader(ascent=v_ascent, descent=-v_descent, caretSlopeRun=1) VERSION_STRING = 'Version {};{}'.format(revision, VENDOR) UNIQUE_ID = '{};{};{}'.format(revision, VENDOR, PS_NAME) name_strings = dict( copyright=COPYRIGHT, # ID 0 familyName=FAMILY_NAME, # ID 1 styleName=STYLE_NAME, # ID 2 uniqueFontIdentifier=UNIQUE_ID, # ID 3 fullName=FULL_NAME, # ID 4 version=VERSION_STRING, # ID 5 psName=PS_NAME, # ID 6 trademark=TRADEMARK, # ID 7 manufacturer=MANUFACTURER, # ID 8 designer=DESIGNER, # ID 9 vendorURL=VENDOR_URL, # ID 11 designerURL=DESIGNER_URL, # ID 12 licenseDescription=LICENSE, # ID 13 licenseInfoURL=LICENSE_URL, # ID 14 ) fb.setupNameTable(name_strings, mac=False) fb.setupOS2( fsType=FSTYPE, achVendID=VENDOR, fsSelection=0x0040, # REGULAR usWinAscent=ASCENT, usWinDescent=-DESCENT, sTypoAscender=ASCENT, sTypoDescender=DESCENT, sCapHeight=ASCENT, ulCodePageRange1=(1 << 1)) # set 1st CP bit if gsub_path: addOpenTypeFeatures(fb.font, gsub_path, tables=['GSUB']) if gpos_path: addOpenTypeFeatures(fb.font, gpos_path, tables=['GPOS']) fb.setupPost(isFixedPitch=1, underlinePosition=UNDERLINE_POSITION, underlineThickness=UNDERLINE_THICKNESS) fb.setupDummyDSIG() fb.save(os.path.join(out_dir, '{}.otf'.format(PS_NAME)))
def test_spec(self): for name in "4h1 5d1 5d2 5fi1 5h1".split(): font = makeTTFont() addOpenTypeFeatures(self.getpath("spec%s.fea" % name), font) self.expect_ttx(font, self.getpath("spec%s.ttx" % name))
def test_language_required(self): font = makeTTFont() addOpenTypeFeatures(self.getpath("language_required.fea"), font) self.expect_ttx(font, self.getpath("language_required.ttx"))
def test_lookup(self): font = makeTTFont() addOpenTypeFeatures(self.getpath("lookup.fea"), font) self.expect_ttx(font, self.getpath("lookup.ttx"))
def main(args=None): """Add features from a feature file (.fea) into a OTF font""" parser = argparse.ArgumentParser( description="Use fontTools to compile OpenType feature files (*.fea).") parser.add_argument("input_fea", metavar="FEATURES", help="Path to the feature file") parser.add_argument("input_font", metavar="INPUT_FONT", help="Path to the input font") parser.add_argument( "-o", "--output", dest="output_font", metavar="OUTPUT_FONT", help="Path to the output font.", ) parser.add_argument( "-t", "--tables", metavar="TABLE_TAG", choices=Builder.supportedTables, nargs="+", help="Specify the table(s) to be built.", ) parser.add_argument( "-d", "--debug", action="store_true", help="Add source-level debugging information to font.", ) parser.add_argument( "-v", "--verbose", help="increase the logger verbosity. Multiple -v " "options are allowed.", action="count", default=0, ) parser.add_argument("--traceback", help="show traceback for exceptions.", action="store_true") options = parser.parse_args(args) levels = ["WARNING", "INFO", "DEBUG"] configLogger(level=levels[min(len(levels) - 1, options.verbose)]) output_font = options.output_font or makeOutputFileName(options.input_font) log.info("Compiling features to '%s'" % (output_font)) font = TTFont(options.input_font) try: addOpenTypeFeatures(font, options.input_fea, tables=options.tables, debug=options.debug) except FeatureLibError as e: if options.traceback: raise log.error(e) font.save(output_font)
def test_constructs(self): for name in ("enum markClass language_required " "lookup lookupflag").split(): font = makeTTFont() addOpenTypeFeatures(self.getpath("%s.fea" % name), font) self.expect_ttx(font, self.getpath("%s.ttx" % name))