def merge(args): arabic = fontforge.open(args.arabicfile) arabic.encoding = "Unicode" with open(args.feature_file) as feature_file: features = string.Template(feature_file.read()) gpos = arabic.generateFeatureString() for lookup in arabic.gpos_lookups: arabic.removeLookup(lookup) arabic.mergeFeatureString(features.substitute(GPOS=gpos)) build_encoded(arabic) latin = fontforge.open(args.latinfile) latin.encoding = "Unicode" latin.em = arabic.em latin_locl = "" for glyph in latin.glyphs(): if glyph.color == 0xff0000: latin.removeGlyph(glyph) else: if glyph.glyphname in arabic: name = glyph.glyphname glyph.unicode = -1 glyph.glyphname = name + ".latin" if not latin_locl: latin_locl = "feature locl {lookupflag IgnoreMarks; script latn;" latin_locl += "sub %s by %s;" % (name, glyph.glyphname) arabic.mergeFonts(latin) if latin_locl: latin_locl += "} locl;" arabic.mergeFeatureString(latin_locl) # Set metadata arabic.version = args.version copyright = 'Copyright © 2015-%s The Aref Ruqaa Project Authors, with Reserved Font Name "EURM10".' % datetime.now( ).year arabic.copyright = copyright.replace("©", "(c)") en = "English (US)" arabic.appendSFNTName(en, "Copyright", copyright) arabic.appendSFNTName(en, "Designer", "Aboullah Aref") arabic.appendSFNTName(en, "License URL", "http://scripts.sil.org/OFL") arabic.appendSFNTName( en, "License", 'This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is available with a FAQ at: http://scripts.sil.org/OFL' ) arabic.appendSFNTName( en, "Descriptor", "Aref Ruqaa is an Arabic typeface that aspires to capture the essence of \ the classical Ruqaa calligraphic style.") arabic.appendSFNTName(en, "Sample Text", "الخط هندسة روحانية ظهرت بآلة جسمانية") return arabic
def merge(args): arabic = fontforge.open(args.arabicfile) arabic.encoding = "Unicode" arabic.mergeFeature(args.feature_file) latin = fontforge.open(args.latinfile) latin.encoding = "Unicode" scale = arabic["arAlef.isol"].width / latin["space"].width latin.em = int(math.ceil(scale * latin.em)) latin_locl = "" for glyph in latin.glyphs(): if glyph.glyphclass == "mark": glyph.width = latin["A"].width if glyph.color == 0xff0000: latin.removeGlyph(glyph) else: if glyph.glyphname in arabic: name = glyph.glyphname glyph.unicode = -1 glyph.glyphname = name + ".latin" if not latin_locl: latin_locl = "feature locl {lookupflag IgnoreMarks; script latn;" latin_locl += "sub %s by %s;" % (name, glyph.glyphname) arabic.mergeFonts(latin) if latin_locl: latin_locl += "} locl;" arabic.mergeFeatureString(latin_locl) zeromarks(arabic) # Set metadata arabic.version = args.version copyright = 'Copyright © 2015-%s The Amiri Typewriter Project Authors, with Reserved Font Name "Fira".' % datetime.now( ).year arabic.copyright = copyright.replace("©", "(c)") en = "English (US)" arabic.appendSFNTName(en, "Copyright", copyright) arabic.appendSFNTName(en, "Designer", "Khaled Hosny") arabic.appendSFNTName(en, "License URL", "http://scripts.sil.org/OFL") arabic.appendSFNTName( en, "License", 'This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is available with a FAQ at: http://scripts.sil.org/OFL' ) arabic.appendSFNTName( en, "Descriptor", "Amiri Typewriter is an Arabic monospaced font family inspired by the type of mechanical Arabic typewriters." ) arabic.appendSFNTName(en, "Sample Text", "الخط هندسة روحانية ظهرت بآلة جسمانية") return arabic
def merge(args): arabic = fontforge.open(args.arabicfile) arabic.encoding = "Unicode" with open(args.feature_file) as feature_file: features = string.Template(feature_file.read()) gpos = arabic.generateFeatureString() for lookup in arabic.gpos_lookups: arabic.removeLookup(lookup) arabic.mergeFeatureString(features.substitute(GPOS=gpos)) build_encoded(arabic) latin = fontforge.open(args.latinfile) latin.encoding = "Unicode" latin.em = arabic.em latin_locl = "" for glyph in latin.glyphs(): if glyph.color == 0xff0000: latin.removeGlyph(glyph) else: if glyph.glyphname in arabic: name = glyph.glyphname glyph.unicode = -1 glyph.glyphname = name + ".latin" if not latin_locl: latin_locl = "feature locl {lookupflag IgnoreMarks; script latn;" latin_locl += "sub %s by %s;" % (name, glyph.glyphname) arabic.mergeFonts(latin) if latin_locl: latin_locl += "} locl;" arabic.mergeFeatureString(latin_locl) # Set metadata arabic.version = args.version years = datetime.now().year == 2015 and 2015 or "2015-%s" % datetime.now().year arabic.copyright = ". ".join(["Portions copyright © %s, Khaled Hosny (<*****@*****.**>)" % years, "Portions " + latin.copyright[0].lower() + latin.copyright[1:].replace("(c)", "©")]) en = "English (US)" arabic.appendSFNTName(en, "Designer", "Abdoulla Aref") arabic.appendSFNTName(en, "License URL", "http://scripts.sil.org/OFL") arabic.appendSFNTName(en, "License", 'This Font Software is licensed under the SIL Open Font License, Version 1.1. \ This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR \ CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License \ for the specific language, permissions and limitations governing your use of \ this Font Software.') arabic.appendSFNTName(en, "Descriptor", "Aref Ruqaa is an Arabic typeface that aspires to capture the essence of \ the classical Ruqaa calligraphic style.") arabic.appendSFNTName(en, "Sample Text", "الخط هندسة روحانية ظهرت بآلة جسمانية") return arabic
def merge(args): arabic = fontforge.open(args.arabicfile) arabic.encoding = "Unicode" with open(args.feature_file) as feature_file: fea = feature_file.read() fea += generate_anchors(arabic) arabic.mergeFeatureString(fea) build_encoded(arabic) auto_hint(arabic) latin = fontforge.open(args.latinfile) latin.encoding = "Unicode" latin.em = arabic.em latin_locl = "" for glyph in latin.glyphs(): if glyph.color == 0xff0000: latin.removeGlyph(glyph) else: if glyph.glyphname in arabic: name = glyph.glyphname glyph.unicode = -1 glyph.glyphname = name + ".latin" if not latin_locl: latin_locl = "feature locl {lookupflag IgnoreMarks; script latn;" latin_locl += "sub %s by %s;" % (name, glyph.glyphname) arabic.mergeFonts(latin) if latin_locl: latin_locl += "} locl;" arabic.mergeFeatureString(latin_locl) # Set metadata arabic.version = args.version years = datetime.now().year == 2015 and 2015 or "2015-%s" % datetime.now().year arabic.copyright = ". ".join(["Portions copyright © %s, Khaled Hosny (<*****@*****.**>)" % years, "Portions " + latin.copyright.replace("(c)", "©").replace("Digitized data ", "")]) en = "English (US)" arabic.appendSFNTName(en, "Designer", "Khaled Hosny") arabic.appendSFNTName(en, "License URL", "http://scripts.sil.org/OFL") arabic.appendSFNTName(en, "License", 'This Font Software is licensed under the SIL Open Font License, Version 1.1. \ This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR \ CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License \ for the specific language, permissions and limitations governing your use of \ this Font Software.') arabic.appendSFNTName(en, "Descriptor", "Reem Kufi is a Fatimid-style decorative Kufic typeface as seen in the historical mosques of Cairo. Reem Kufi is based on the Kufic designs of the great Arabic calligrapher Mohammed Abdul Qadir who revived this art in the 20th century and formalised its rules.") arabic.appendSFNTName(en, "Sample Text", "ريم على القاع بين البان و العلم أحل سفك دمي في الأشهر الحرم") return arabic
def merge(args): arabic = fontforge.open(args.arabicfile) arabic.encoding = "Unicode" arabic.mergeFeature(args.feature_file) latin = fontforge.open(args.latinfile) latin.encoding = "Unicode" scale = arabic["arAlef.isol"].width / latin["space"].width latin.em = int(math.ceil(scale * latin.em)) latin_locl = "" for glyph in latin.glyphs(): if glyph.glyphclass == "mark": glyph.width = latin["A"].width if glyph.color == 0xff0000: latin.removeGlyph(glyph) else: if glyph.glyphname in arabic: name = glyph.glyphname glyph.unicode = -1 glyph.glyphname = name + ".latin" if not latin_locl: latin_locl = "feature locl {lookupflag IgnoreMarks; script latn;" latin_locl += "sub %s by %s;" % (name, glyph.glyphname) arabic.mergeFonts(latin) if latin_locl: latin_locl += "} locl;" arabic.mergeFeatureString(latin_locl) zeromarks(arabic) # Set metadata arabic.version = args.version copyright = 'Copyright © 2015-%s The Amiri Typewriter Project Authors, with Reserved Font Name "Fira".' % datetime.now().year arabic.copyright = copyright.replace("©", "(c)") en = "English (US)" arabic.appendSFNTName(en, "Copyright", copyright) arabic.appendSFNTName(en, "Designer", "Khaled Hosny") arabic.appendSFNTName(en, "License URL", "http://scripts.sil.org/OFL") arabic.appendSFNTName(en, "License", 'This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is available with a FAQ at: http://scripts.sil.org/OFL') arabic.appendSFNTName(en, "Descriptor", "Amiri Typewriter is an Arabic monospaced font family inspired by the type of mechanical Arabic typewriters.") arabic.appendSFNTName(en, "Sample Text", "الخط هندسة روحانية ظهرت بآلة جسمانية") return arabic
def main_cleanClass(classname, fontLocation, featureFileList): font = fontforge.open(fontLocation) featureFiles = {name: readLines(name) for name in featureFileList} classFeatureFile = featureFiles['sources/classes.fea'] new_klass, _, removed = _cleanClass(classname, font, classFeatureFile, featureFiles) print('removed', removed) print('{name} = [ {classes} ];'.format(name=classname,classes=' '.join(new_klass)))
def makeDesktop(infile, outfile, feafile, version, latin=True, generate=True): font = fontforge.open(infile) font.encoding = "UnicodeFull" # avoid a crash if compact was set updateInfo(font, version) # remove anchors that are not needed in the production font cleanAnchors(font) #makeOverUnderline(font) # sample text to be used by font viewers sample = 'صِفْ خَلْقَ خَوْدٍ كَمِثْلِ ٱلشَّمْسِ إِذْ بَزَغَتْ يَحْظَىٰ ٱلضَّجِيعُ بِهَا نَجْلَاءَ مِعْطَارِ.' for lang in ('Arabic (Egypt)', 'English (US)'): font.appendSFNTName(lang, 'Sample Text', sample) if latin: mergeLatin(font, feafile) makeNumerators(font) # we want to merge features after merging the latin font because many # referenced glyphs are in the latin font mergeFeatures(font, feafile) if generate: generateFont(font, outfile) else: return font
def makeDesktop(infile, outfile, feafile, version, latin=True, generate=True): font = fontforge.open(infile) font.encoding = "UnicodeBmp" # avoid a crash if compact was set if version: setVersion(font, version) # remove anchors that are not needed in the production font cleanAnchors(font) # makeOverUnderline(font) # sample text to be used by font viewers sample = "صِفْ خَلْقَ خَوْدٍ كَمِثْلِ ٱلشَّمْسِ إِذْ بَزَغَتْ يَحْظَىٰ ٱلضَّجِيعُ بِهَا نَجْلَاءَ مِعْطَارِ." for lang in ("Arabic (Egypt)", "English (US)"): font.appendSFNTName(lang, "Sample Text", sample) if latin: mergeLatin(font, feafile) # we want to merge features after merging the latin font because many # referenced glyphs are in the latin font mergeFeatures(font, feafile) if generate: generateFont(font, outfile) else: return font
def main_cleanClass(classname, fontLocation, featureFileList): font = fontforge.open(fontLocation) featureFiles = {name: readLines(name) for name in featureFileList} classFeatureFile = featureFiles['sources/classes.fea'] new_klass, _, removed = _cleanClass(classname, font, classFeatureFile, featureFiles) print('removed', removed) print('{name} = [ {classes} ];'.format(name=classname, classes=' '.join(new_klass)))
def main_features(fontLocation, featureFileList): font = fontforge.open(fontLocation) featureFiles = {name: readLines(name) for name in featureFileList} marked = getMarkedGlyphs(font) table = sorted([(countNameInFeatures(glyph.glyphname, featureFiles), glyph.glyphname) for glyph in marked], reverse=True) for (total, perFile, _), name in table: if not total: continue print('-' * 20) print(name, 'total:', total) for filename, count in perFile: if not count: continue print(str(count).ljust(5), ' | ', filename)
def makeCss(infiles, outfile): """Builds a CSS file for the entire font family.""" css = "" for f in infiles.split(): base = os.path.splitext(os.path.basename(f))[0] font = fontforge.open(f) font.encoding = "UnicodeBmp" # avoid a crash if compact was set css += genCSS(font, base) font.close() out = open(outfile, "w") out.write(css) out.close()
def main(fontLocation, featureFileList): font = fontforge.open(fontLocation) featureFiles = {name: readLines(name) for name in featureFileList} state = _State(font, featureFiles) removals = state.findUnaccessibleGlyphs() info('to be removed:', len(removals), 'of', len(font), 'new length: ', len(font) - len(removals)) if not removals: info('nothing to do') return; for name in removals: font.removeGlyph(name) font.save()
def main(fontLocation, featureFileList): font = fontforge.open(fontLocation) featureFiles = {name: readLines(name) for name in featureFileList} state = _State(font, featureFiles) removals = state.findUnaccessibleGlyphs() info('to be removed:', len(removals), 'of', len(font), 'new length: ', len(font) - len(removals)) if not removals: info('nothing to do') return for name in removals: font.removeGlyph(name) font.save()
def main_blockers(fontLocation, featureFileList): font = fontforge.open(fontLocation) featureFiles = {name: readLines(name) for name in featureFileList} marked = getMarkedGlyphs(font) dependents = getDependents(marked, font) table = sorted([getTableRow(glyph, dependents, featureFiles) for glyph in marked], reverse=True) headers = ['unicode', 'is exception', '1-hard deps', 'n-hard deps', 'soft deps', 'in fea', 'name'] headerSizes = [len(name) for name in headers] print(' | '.join(headers)) for row in table: # skip rows that would be removed by removeInaccessibleGlyphs.py if tuple(row[:-1]) == (False, False, 0, 0, 0,False): continue index = iter(xrange(len(headers))) print(' | '.join([str(item).rjust(headerSizes[index.next()]) for item in row]))
def makeWeb(infile, outfile): """If we are building a web version then try to minimise file size""" # "short-post" generates a post table without glyph names to save some KBs # since glyph names are only needed for PDF's as readers use them to # "guess" characters when copying text, which is of little use in web fonts. flags = ("opentype", "short-post", "omit-instructions") fontforge.setPrefs("PreserveTables", "COLR,CPAL") font = fontforge.open(infile) font.encoding = "UnicodeBmp" # avoid a crash if compact was set # removed compatibility glyphs that of little use on the web compat_ranges = ( (0xfb50, 0xfbb1), (0xfbd3, 0xfd3d), (0xfd50, 0xfdf9), (0xfdfc, 0xfdfc), (0xfe70, 0xfefc), ) for glyph in font.glyphs(): for i in compat_ranges: start = i[0] end = i[1] if start <= glyph.unicode <= end: font.removeGlyph(glyph) break tmpfile = mkstemp(suffix=os.path.basename(outfile))[1] font.generate(tmpfile, flags=flags) font.close() # now open in fontTools from fontTools.ttLib import TTFont ftfont = TTFont(tmpfile) # force compiling tables by fontTools, saves few tens of KBs for tag in ftfont.keys(): if hasattr(ftfont[tag], "compile"): ftfont[tag].compile(ftfont) ftfont.save(outfile) ftfont.close() os.remove(tmpfile)
def main_features(fontLocation, featureFileList): font = fontforge.open(fontLocation) featureFiles = {name: readLines(name) for name in featureFileList} marked = getMarkedGlyphs(font) table = sorted( [(countNameInFeatures(glyph.glyphname, featureFiles), glyph.glyphname) for glyph in marked], reverse=True) for (total, perFile, _), name in table: if not total: continue print('-' * 20) print(name, 'total:', total) for filename, count in perFile: if not count: continue print(str(count).ljust(5), ' | ', filename)
def main_cleanable(fontLocation, featureFileList): font = fontforge.open(fontLocation) featureFiles = {name: readLines(name) for name in featureFileList} classFeatureFile = featureFiles['sources/classes.fea'] classes = [] for number, line in enumerate(classFeatureFile): index = line.find('=') if index == -1: index = line.find(' ') if index <= 0: continue classname = line[:index-1].strip(); if not isClassName(classname): continue new_klass, _, removed = _cleanClass(classname, font, classFeatureFile, featureFiles) if removed: print (classname, 'removed:', removed, 'empty:', not len(new_klass))
def main_cleanable(fontLocation, featureFileList): font = fontforge.open(fontLocation) featureFiles = {name: readLines(name) for name in featureFileList} classFeatureFile = featureFiles['sources/classes.fea'] classes = [] for number, line in enumerate(classFeatureFile): index = line.find('=') if index == -1: index = line.find(' ') if index <= 0: continue classname = line[:index - 1].strip() if not isClassName(classname): continue new_klass, _, removed = _cleanClass(classname, font, classFeatureFile, featureFiles) if removed: print(classname, 'removed:', removed, 'empty:', not len(new_klass))
def main_replaceReferences(fontLocation, oldName, newName): font = fontforge.open(fontLocation) # just to check if the names exist glyph = font[oldName] replacement = font[newName] total, perGlyph = replaceReference(font, oldName, newName) print('Details:') print('-'*50) for name, number in perGlyph: print(str(number).rjust(5), '|', name) print('Replaced references to "{oldName}" with "{newName}"'.format(oldName=oldName,newName=newName)) print('Changed:', len(perGlyph),'glyphs') print('Total:', total, 'references replaced') if total != 0: font.save();
def main_replaceReferences(fontLocation, oldName, newName): font = fontforge.open(fontLocation) # just to check if the names exist glyph = font[oldName] replacement = font[newName] total, perGlyph = replaceReference(font, oldName, newName) print('Details:') print('-' * 50) for name, number in perGlyph: print(str(number).rjust(5), '|', name) print('Replaced references to "{oldName}" with "{newName}"'.format( oldName=oldName, newName=newName)) print('Changed:', len(perGlyph), 'glyphs') print('Total:', total, 'references replaced') if total != 0: font.save()
def main_blockers(fontLocation, featureFileList): font = fontforge.open(fontLocation) featureFiles = {name: readLines(name) for name in featureFileList} marked = getMarkedGlyphs(font) dependents = getDependents(marked, font) table = sorted( [getTableRow(glyph, dependents, featureFiles) for glyph in marked], reverse=True) headers = [ 'unicode', 'is exception', '1-hard deps', 'n-hard deps', 'soft deps', 'in fea', 'name' ] headerSizes = [len(name) for name in headers] print(' | '.join(headers)) for row in table: # skip rows that would be removed by removeInaccessibleGlyphs.py if tuple(row[:-1]) == (False, False, 0, 0, 0, False): continue index = iter(xrange(len(headers))) print(' | '.join( [str(item).rjust(headerSizes[index.next()]) for item in row]))
def makeDesktop(infile, outfile, latinfile, feafile, version, generate=True): font = fontforge.open(infile) font.encoding = "UnicodeFull" # avoid a crash if compact was set if version: setVersion(font, version) # remove anchors that are not needed in the production font cleanAnchors(font) mergeLatin(font, latinfile) #simpleFontMerge(font, latinfile) makeNumerators(font) # we want to merge features after merging the latin font because many # referenced glyphs are in the latin font prepareFeatures(font, feafile) if generate: generateFont(font, outfile) else: return font
def mergeLatin(font, feafile, italic=False, glyphs=None, quran=False): styles = {"Regular": "Roman", "Slanted": "Italic", "Bold": "Bold", "BoldSlanted": "BoldItalic"} style = styles[font.fontname.split("-")[1]] latinfile = "Crimson-%s.sfd" % style tmpfont = mkstemp(suffix=os.path.basename(latinfile))[1] latinfont = fontforge.open("sources/crimson/%s" % latinfile) validateGlyphs(latinfont) # to flatten nested refs mainly if glyphs: latinglyphs = list(glyphs) else: # collect latin glyphs we want to keep latinglyphs = [] # we want all glyphs in latin0-9 encodings for i in range(0, 9): latinfont.encoding = "latin%d" % i for glyph in latinfont.glyphs("encoding"): if glyph.encoding <= 255: if glyph.glyphname not in latinglyphs: latinglyphs.append(glyph.glyphname) elif glyph.unicode != -1 and glyph.unicode <= 0x017F: # keep also Unicode Latin Extended-A block if glyph.glyphname not in latinglyphs: latinglyphs.append(glyph.glyphname) elif glyph.unicode == -1 and ".prop" in glyph.glyphname: # proportional digits latinglyphs.append(glyph.glyphname) # keep ligatures too ligatures = ( "f_b", "f_f_b", "f_h", "f_f_h", "f_i", "f_f_i", "f_j", "f_f_j", "f_k", "f_f_k", "f_l", "f_f_l", "f_f", ) # and Arabic romanisation characters romanisation = ( "uni02BC", "uni02BE", "uni02BE", "amacron", "uni02BE", "amacron", "eacute", "uni1E6F", "ccedilla", "uni1E6F", "gcaron", "ycircumflex", "uni1E29", "uni1E25", "uni1E2B", "uni1E96", "uni1E0F", "dcroat", "scaron", "scedilla", "uni1E63", "uni1E11", "uni1E0D", "uni1E6D", "uni1E93", "dcroat", "uni02BB", "uni02BF", "rcaron", "grave", "gdotaccent", "gbreve", "umacron", "imacron", "amacron", "amacron", "uni02BE", "amacron", "uni02BE", "acircumflex", "amacron", "uni1E97", "tbar", "aacute", "amacron", "ygrave", "agrave", "uni02BE", "aacute", "Amacron", "Amacron", "Eacute", "uni1E6E", "Ccedilla", "uni1E6E", "Gcaron", "Ycircumflex", "uni1E28", "uni1E24", "uni1E2A", "uni1E0E", "Dcroat", "Scaron", "Scedilla", "uni1E62", "uni1E10", "uni1E0C", "uni1E6C", "uni1E92", "Dcroat", "Rcaron", "Gdotaccent", "Gbreve", "Umacron", "Imacron", "Amacron", "Amacron", "Amacron", "Acircumflex", "Amacron", "Tbar", "Aacute", "Amacron", "Ygrave", "Agrave", "Aacute", ) # and some typographic characters typographic = ( "uni2010", "uni2011", "figuredash", "endash", "emdash", "uni2015", "quoteleft", "quoteright", "quotesinglbase", "quotereversed", "quotedblleft", "quotedblright", "quotedblbase", "uni201F", "dagger", "daggerdbl", "bullet", "onedotenleader", "ellipsis", "uni202F", "perthousand", "minute", "second", "uni2038", "guilsinglleft", "guilsinglright", "uni203E", "fraction", "i.TRK", "minus", "uni2213", "radical", "uni2042", ) for l in (ligatures, romanisation, typographic): for name in l: if name not in latinglyphs: latinglyphs.append(name) if not quran: # we want our ring above and below in Quran font only for name in ("uni030A", "uni0325"): font[name].clear() latinglyphs += buildComposition(latinfont, latinglyphs) subsetFont(latinfont, latinglyphs) digits = ("zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine") # common characters that can be used in Arabic and Latin need to be handled # carefully in the slanted font so that right leaning italic is used with # Latin, and left leaning slanted is used with Arabic, using ltra and rtla # features respectively, for less OpenType savvy apps we make the default # upright so it works reasonably with bot scripts if italic: if "Bold" in style: upright = fontforge.open("sources/crimson/Crimson-Bold.sfd") else: upright = fontforge.open("sources/crimson/Crimson-Roman.sfd") shared = ( "exclam", "quotedbl", "numbersign", "dollar", "percent", "quotesingle", "asterisk", "plus", "colon", "semicolon", "less", "equal", "greater", "question", "at", "asciicircum", "exclamdown", "section", "copyright", "logicalnot", "registered", "plusminus", "uni00B2", "uni00B3", "paragraph", "uni00B9", "ordmasculine", "onequarter", "onehalf", "threequarters", "questiondown", "quoteleft", "quoteright", "quotesinglbase", "quotereversed", "quotedblleft", "quotedblright", "quotedblbase", "uni201F", "dagger", "daggerdbl", "perthousand", "minute", "second", "guilsinglleft", "guilsinglright", "fraction", "uni2213", ) for name in shared: glyph = latinfont[name] glyph.clear() upright.selection.select(name) upright.copy() latinfont.createChar(upright[name].encoding, name) latinfont.selection.select(name) latinfont.paste() for name in digits: glyph = latinfont[name] glyph.glyphname += ".ltr" glyph.unicode = -1 upright.selection.select(name) upright.copy() latinfont.createChar(upright[name].encoding, name) latinfont.selection.select(name) latinfont.paste() rtl = latinfont.createChar(-1, name + ".rtl") rtl.addReference(name, italic) rtl.useRefsMetrics(name) for name in digits: pname = name + ".prop" upright.selection.select(pname) upright.copy() latinfont.createChar(-1, pname) latinfont.selection.select(pname) latinfont.paste() for name in digits: pname = name + ".prop" rtl = latinfont.createChar(-1, name + ".rtl" + ".prop") rtl.addReference(pname, italic) rtl.useRefsMetrics(pname) # copy kerning classes kern_lookups = {} if not quran: for lookup in latinfont.gpos_lookups: kern_lookups[lookup] = {} kern_lookups[lookup]["subtables"] = [] kern_lookups[lookup]["type"], kern_lookups[lookup]["flags"] = latinfont.getLookupInfo(lookup)[:2] for subtable in latinfont.getLookupSubtables(lookup): if latinfont.isKerningClass(subtable): kern_lookups[lookup]["subtables"].append((subtable, latinfont.getKerningClass(subtable))) for lookup in latinfont.gpos_lookups: latinfont.removeLookup(lookup) for lookup in latinfont.gsub_lookups: latinfont.removeLookup(lookup) latinfont.save(tmpfont) latinfont.close() font.mergeFonts(tmpfont) os.remove(tmpfont) if not quran: buildComposition(font, latinglyphs) # add Latin small and medium digits for name in digits: if italic: # they are only used in Arabic contexts, so always reference the # italic rtl variant refname = name + ".rtl" else: refname = name small = font.createChar(-1, name + ".small") small.clear() small.addReference(refname, psMat.scale(0.6)) small.transform(psMat.translate(0, -40)) small.width = 600 centerGlyph(small) medium = font.createChar(-1, name + ".medium") medium.clear() medium.addReference(refname, psMat.scale(0.8)) medium.transform(psMat.translate(0, 50)) medium.width = 900 centerGlyph(medium) for lookup in kern_lookups: font.addLookup(lookup, kern_lookups[lookup]["type"], kern_lookups[lookup]["flags"], (("kern", script_lang),)) for subtable in kern_lookups[lookup]["subtables"]: first = [] second = [] offsets = subtable[1][2] # drop non-existing glyphs for new_klasses, klasses in ((first, subtable[1][0]), (second, subtable[1][1])): for klass in klasses: new_klass = [] if klass: for name in klass: if name in font: new_klass.append(name) new_klasses.append(new_klass) # if either of the classes is empty, don’t bother with the subtable if any(first) and any(second): font.addKerningClass(lookup, subtable[0], first, second, offsets)
except ImportError: print >> sys.stderr, "Failed to import sortsmill, failing back to fontforge" import fontforge import argparse from datetime import date from os import path parser = argparse.ArgumentParser() parser.add_argument("-i", "--input", required=True) parser.add_argument("-o", "--output", required=True) parser.add_argument("-v", "--version", required=True) parser.add_argument("-f", "--feature-file", required=False) args = parser.parse_args() font = fontforge.open(args.input) if args.feature_file and path.isfile(args.feature_file): font.mergeFeature(args.feature_file) font.version = args.version font.copyright = "Copyright 2003-2012 by Philipp H. Poll. Copyright 2012-%s Khaled Hosny." % date.today().year font.appendSFNTName("English (US)", "Manufacturer", "Khaled Hosny") font.appendSFNTName("English (US)", "Vendor URL", "https://github.com/khaledhosny/libertinus") font.appendSFNTName("English (US)", "License URL", "http://scripts.sil.org/OFL") font.appendSFNTName("English (US)", "License", '\ This Font Software is licensed under the SIL Open Font License, Version 1.1. \ This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR \ CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License \ for the specific language, permissions and limitations governing your use of \
def makeWeb(infile, outfile): """If we are building a web version then try to minimise file size""" # "short-post" generates a post table without glyph names to save some KBs # since glyph names are only needed for PDF's as readers use them to # "guess" characters when copying text, which is of little use in web fonts. flags = ("opentype", "short-post", "omit-instructions") fontforge.setPrefs("PreserveTables", "COLR,CPAL") font = fontforge.open(infile) font.encoding = "UnicodeBmp" # avoid a crash if compact was set # removed compatibility glyphs that of little use on the web compat_ranges = ( (0xfb50, 0xfbb1), (0xfbd3, 0xfd3d), (0xfd50, 0xfdf9), (0xfdfc, 0xfdfc), (0xfe70, 0xfefc), ) for glyph in font.glyphs(): for i in compat_ranges: start = i[0] end = i[1] if start <= glyph.unicode <= end: font.removeGlyph(glyph) break tmpfile = mkstemp(suffix=os.path.basename(outfile))[1] font.generate(tmpfile, flags=flags) font.close() # now open in fontTools from fontTools.ttLib import TTFont ftfont = TTFont(tmpfile) # our 'name' table is a bit bulky, and of almost no use in for web fonts, # so we strip all unnecessary entries. name = ftfont['name'] names = [] for record in name.names: platID = record.platformID langID = record.langID nameID = record.nameID # we keep only en_US entries in Windows and Mac platform id, every # thing else is dropped if (platID == 1 and langID == 0) or (platID == 3 and langID == 1033): if nameID == 13: # the full OFL text is too much, replace it with a simple # string if platID == 3: # MS strings are UTF-16 encoded text = 'OFL v1.1'.encode('utf_16_be') else: text = 'OFL v1.1' record.string = text names.append(record) # keep every thing else except Descriptor, Sample Text elif nameID not in (10, 19): names.append(record) name.names = names # force compiling tables by fontTools, saves few tens of KBs for tag in ftfont.keys(): if hasattr(ftfont[tag], "compile"): ftfont[tag].compile(ftfont) ftfont.save(outfile) ftfont.close() os.remove(tmpfile)
if len(g.references) != 0: g.references = follow_references(g) f.generate(temp_ttf.name, flags = flags) print('Compiling Graphite: ' + gdl_file + ' ' + temp_ttf.name + ' -> ' + ttf_file) compile_graphite(gdl_file, temp_ttf.name, ttf_file) temp_ttf.close() else: print('Generating ' + ttf_file) f.generate(ttf_file, flags = flags) print('Validating ' + ttf_file) subprocess.call(['sortsmill-lint', ttf_file]) #-------------------------------------------------------------------------- # If someone runs some script by using "fontforge -script", then name # may be "__main__" and fontforge won't have a user interface, but # also the |sys| module will not yet have an |argv| attribute. Weed # out that case. Perhaps fontforge should handle the situation # better. (5 Nov 2009) if __name__ == '__main__' and not fontforge.hasUserInterface() and hasattr(sys, 'argv'): for font_file in sys.argv[1:]: f = fontforge.open(font_file) generate_tt_font(f, name_modifier) f.close() #--------------------------------------------------------------------------
p.wait(); git_revison = p.stdout.read(); fontlog = '\n'.join(( datetime.datetime.now().isoformat(' ') , 'This file was generated from the sources of Amiri Font by Khaled Hosny,' , 'to serve as a blueprint for the font engineering of Jomhuria font.' , 'http://www.amirifont.org/' , 'Amiri git: {amiriGit}' , 'git revison: {git_revison}' )).format(amiriGit=amiriGit, git_revison=git_revison) # just hijack what Khaled wrote, to make a subsetted, clean latin font import imp build = imp.load_source('build', os.path.join(amiriDir, 'tools', 'build.py')) amiriFont = fontforge.open(os.path.join(amiriDir, 'sources', 'amiri-regular.sfdir')) newFont = fontforge.font(); # < needed for build.mergeLatin, it looks for the -Regular" newFont.fontname = 'JomhuriaLatin-Regular'; # take the dimensions from amiri newFont.ascent = amiriFont.ascent newFont.descent = amiriFont.descent newFont.em = amiriFont.em newFont.fontlog = fontlog; # the source is quadratic, I want to keep that information in the background # the foreground will be cubic newFont.layers['Fore'].is_quadratic = True; newFont.layers['Back'].is_quadratic = True;
def copyAnchors(sourceFont, targetFont): count = 0 for name in sourceFont: if name not in targetFont: continue glyph = targetFont[name] sourceAnchors = sourceFont[name].anchorPoints targetAnchors = {p[0]:p for p in glyph.anchorPoints} for p in sourceAnchors: if p[0] in targetAnchors: continue print glyph, 'adding', p glyph.addAnchorPoint(*p) count += 1 return count if __name__ == '__main__': # cd ./.build pairs = ( ('amiri-font/sources/amiri-regular.sfdir', 'sources/jomhuria.sfdir') , ('amiri-font/sources/latin/amirilatin-regular.sfdir', 'sources/jomhuria-latin.sfdir') ) for source, target in pairs: sourceFont = fontforge.open(source) targetFont = fontforge.open(target) if copyAnchors(sourceFont, targetFont): targetFont.save()
except ImportError: print >> sys.stderr, "Failed to import sortsmill, failing back to fontforge" import fontforge import argparse from datetime import date from os import path parser = argparse.ArgumentParser() parser.add_argument("-i", "--input", required=True) parser.add_argument("-o", "--output", required=True) parser.add_argument("-v", "--version", required=True) parser.add_argument("-f", "--feature-file", required=False) args = parser.parse_args() font = fontforge.open(args.input) if args.feature_file and path.isfile(args.feature_file): font.mergeFeature(args.feature_file) font.version = args.version font.copyright = u"Copyright © 2012-%s The Libertinus Project Authors." % date.today( ).year font.appendSFNTName("English (US)", "Manufacturer", "Khaled Hosny") font.appendSFNTName("English (US)", "Vendor URL", "https://github.com/khaledhosny/libertinus") font.appendSFNTName("English (US)", "License URL", "http://scripts.sil.org/OFL") font.appendSFNTName( "English (US)", "License",
def mergeLatin(font, latinfile, glyphs=None): tmpfont = mkstemp( suffix=os.path.basename(latinfile).replace("ufo", "sfd"))[1] latinfont = fontforge.open(latinfile) validateGlyphs(latinfont) # to flatten nested refs mainly latinglyphs = [name for name in latinfont] # The Latin font is already subsetted to only contain glyphs that take # precedence over glyphs in the Arabic if there are overlapping glyphs. # To give the glyphs of the latin precedence, we need to remove the # existing glyphs from the Arabic. overlapping = {name for name in font} & {name for name in latinfont} for name in overlapping: # Maybe font.removeGlyph(name) is the better strategy than font[name].clear() # As it seems that the newly merged glyphs don't set all properties # of the glyph new on font.mergeFonts(latin) # If there are no references, removeGlyph should be better (there # were none when writing this) font.removeGlyph(name) compositions = buildComposition(latinfont, latinglyphs) latinglyphs += compositions subsetFont(latinfont, latinglyphs) digits = ("zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine") # copy kerning classes kern_lookups = {} for lookup in latinfont.gpos_lookups: kern_lookups[lookup] = {} kern_lookups[lookup]["subtables"] = [] kern_lookups[lookup]["type"], kern_lookups[lookup][ "flags"] = latinfont.getLookupInfo(lookup)[:2] for subtable in latinfont.getLookupSubtables(lookup): if latinfont.isKerningClass(subtable): kern_lookups[lookup]["subtables"].append( (subtable, latinfont.getKerningClass(subtable))) for lookup in latinfont.gpos_lookups: latinfont.removeLookup(lookup) for lookup in latinfont.gsub_lookups: latinfont.removeLookup(lookup) latinfont.save(tmpfont) latinfont.close() font.mergeFonts(tmpfont) os.remove(tmpfont) copyAnchors(font, latinfile) buildComposition(font, latinglyphs) # add Latin small and medium digits for name in digits: refname = name small = font.createChar(-1, name + ".small") #if not small.isWorthOutputting(): small.clear() small.addReference(refname, psMat.scale(0.6)) small.transform(psMat.translate(0, 180)) small.width = 650 centerGlyph(small) medium = font.createChar(-1, name + ".medium") #if not medium.isWorthOutputting(): medium.clear() medium.addReference(refname, psMat.scale(0.8)) medium.transform(psMat.translate(0, 200)) medium.width = 900 centerGlyph(medium) for lookup in kern_lookups: font.addLookup(lookup, kern_lookups[lookup]["type"], kern_lookups[lookup]["flags"], (('kern', script_lang), )) for subtable in kern_lookups[lookup]["subtables"]: first = [] second = [] offsets = subtable[1][2] # drop non-existing glyphs for new_klasses, klasses in ((first, subtable[1][0]), (second, subtable[1][1])): for klass in klasses: new_klass = [] if klass: for name in klass: if name in font: new_klass.append(name) new_klasses.append(new_klass) # if either of the classes is empty, don’t bother with the subtable if any(first) and any(second): font.addKerningClass(lookup, subtable[0], first, second, offsets)
def mergeLatin(font, feafile, italic=False, glyphs=None, quran=False): styles = { "Regular": "regular", "Slanted": "italic", "Bold": "bold", "BoldSlanted": "bolditalic" } style = styles[font.fontname.split("-")[1]] latinfile = "amirilatin-%s.sfdir" % style from tempfile import mkstemp tmpfont = mkstemp( suffix=os.path.basename(latinfile).replace("sfdir", "sfd"))[1] latinfont = fontforge.open("sources/latin/%s" % latinfile) validateGlyphs(latinfont) # to flatten nested refs mainly if glyphs: latinglyphs = list(glyphs) else: # collect latin glyphs we want to keep latinglyphs = [] # we want all glyphs in latin0-9 encodings for i in range(0, 9): latinfont.encoding = 'latin%d' % i for glyph in latinfont.glyphs("encoding"): if glyph.encoding <= 255: if glyph.glyphname not in latinglyphs: latinglyphs.append(glyph.glyphname) elif glyph.unicode != -1 and glyph.unicode <= 0x017F: # keep also Unicode Latin Extended-A block if glyph.glyphname not in latinglyphs: latinglyphs.append(glyph.glyphname) elif glyph.unicode == -1 and '.prop' in glyph.glyphname: # proportional digits latinglyphs.append(glyph.glyphname) # keep ligatures too ligatures = ("f_b", "f_f_b", "f_h", "f_f_h", "f_i", "f_f_i", "f_j", "f_f_j", "f_k", "f_f_k", "f_l", "f_f_l", "f_f") # and Arabic romanisation characters romanisation = ("uni02BC", "uni02BE", "uni02BE", "amacron", "uni02BE", "amacron", "eacute", "uni1E6F", "ccedilla", "uni1E6F", "gcaron", "ycircumflex", "uni1E29", "uni1E25", "uni1E2B", "uni1E96", "uni1E0F", "dcroat", "scaron", "scedilla", "uni1E63", "uni1E11", "uni1E0D", "uni1E6D", "uni1E93", "dcroat", "uni02BB", "uni02BF", "rcaron", "grave", "gdotaccent", "gbreve", "umacron", "imacron", "amacron", "amacron", "uni02BE", "amacron", "uni02BE", "acircumflex", "amacron", "uni1E97", "tbar", "aacute", "amacron", "ygrave", "agrave", "uni02BE", "aacute", "Amacron", "Amacron", "Eacute", "uni1E6E", "Ccedilla", "uni1E6E", "Gcaron", "Ycircumflex", "uni1E28", "uni1E24", "uni1E2A", "uni1E0E", "Dcroat", "Scaron", "Scedilla", "uni1E62", "uni1E10", "uni1E0C", "uni1E6C", "uni1E92", "Dcroat", "Rcaron", "Gdotaccent", "Gbreve", "Umacron", "Imacron", "Amacron", "Amacron", "Amacron", "Acircumflex", "Amacron", "Tbar", "Aacute", "Amacron", "Ygrave", "Agrave", "Aacute") # and some typographic characters typographic = ("uni2010", "uni2011", "figuredash", "endash", "emdash", "uni2015", "quoteleft", "quoteright", "quotesinglbase", "quotereversed", "quotedblleft", "quotedblright", "quotedblbase", "uni201F", "dagger", "daggerdbl", "bullet", "onedotenleader", "ellipsis", "uni202F", "perthousand", "minute", "second", "uni2038", "guilsinglleft", "guilsinglright", "uni203E", "fraction", "i.TRK", "minus", "uni2213", "radical", "uni2042") for l in (ligatures, romanisation, typographic): for name in l: if name not in latinglyphs: latinglyphs.append(name) if not quran: # we want our ring above and below in Quran font only for name in ("uni030A", "uni0325"): font[name].clear() latinglyphs += buildComposition(latinfont, latinglyphs) subsetFont(latinfont, latinglyphs) digits = ("zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine") # common characters that can be used in Arabic and Latin need to be handled # carefully in the slanted font so that right leaning italic is used with # Latin, and left leaning slanted is used with Arabic, using ltra and rtla # features respectively, for less OpenType savvy apps we make the default # upright so it works reasonably with bot scripts if italic: if "bold" in style: upright = fontforge.open("sources/latin/amirilatin-bold.sfdir") else: upright = fontforge.open("sources/latin/amirilatin-regular.sfdir") shared = ("exclam", "quotedbl", "numbersign", "dollar", "percent", "quotesingle", "asterisk", "plus", "colon", "semicolon", "less", "equal", "greater", "question", "at", "asciicircum", "exclamdown", "section", "copyright", "logicalnot", "registered", "plusminus", "uni00B2", "uni00B3", "paragraph", "uni00B9", "ordmasculine", "onequarter", "onehalf", "threequarters", "questiondown", "quoteleft", "quoteright", "quotesinglbase", "quotereversed", "quotedblleft", "quotedblright", "quotedblbase", "uni201F", "dagger", "daggerdbl", "perthousand", "minute", "second", "guilsinglleft", "guilsinglright", "fraction", "uni2213") for name in shared: glyph = latinfont[name] glyph.clear() upright.selection.select(name) upright.copy() latinfont.createChar(upright[name].encoding, name) latinfont.selection.select(name) latinfont.paste() for name in digits: glyph = latinfont[name] glyph.glyphname += '.ltr' glyph.unicode = -1 upright.selection.select(name) upright.copy() latinfont.createChar(upright[name].encoding, name) latinfont.selection.select(name) latinfont.paste() rtl = latinfont.createChar(-1, name + ".rtl") rtl.addReference(name, italic) rtl.useRefsMetrics(name) for name in digits: pname = name + ".prop" glyph = latinfont[pname] glyph.glyphname = name + '.ltr.prop' glyph.unicode = -1 upright.selection.select(pname) upright.copy() latinfont.createChar(-1, pname) latinfont.selection.select(pname) latinfont.paste() rtl = latinfont.createChar(-1, name + ".rtl" + ".prop") rtl.addReference(pname, italic) rtl.useRefsMetrics(pname) # copy kerning classes kern_lookups = {} if not quran: for lookup in latinfont.gpos_lookups: kern_lookups[lookup] = {} kern_lookups[lookup]["subtables"] = [] kern_lookups[lookup]["type"], kern_lookups[lookup][ "flags"] = latinfont.getLookupInfo(lookup)[:2] for subtable in latinfont.getLookupSubtables(lookup): if latinfont.isKerningClass(subtable): kern_lookups[lookup]["subtables"].append( (subtable, latinfont.getKerningClass(subtable))) for lookup in latinfont.gpos_lookups: latinfont.removeLookup(lookup) for lookup in latinfont.gsub_lookups: latinfont.removeLookup(lookup) latinfont.save(tmpfont) latinfont.close() font.mergeFonts(tmpfont) os.remove(tmpfont) if not quran: buildComposition(font, latinglyphs) # add Latin small and medium digits for name in digits: if italic: # they are only used in Arabic contexts, so always reference the # italic rtl variant refname = name + ".rtl" else: refname = name small = font.createChar(-1, name + ".small") if not small.isWorthOutputting(): small.clear() small.addReference(refname, psMat.scale(0.6)) small.transform(psMat.translate(0, -40)) small.width = 600 centerGlyph(small) medium = font.createChar(-1, name + ".medium") if not medium.isWorthOutputting(): medium.clear() medium.addReference(refname, psMat.scale(0.8)) medium.transform(psMat.translate(0, 50)) medium.width = 900 centerGlyph(medium) for lookup in kern_lookups: font.addLookup(lookup, kern_lookups[lookup]["type"], kern_lookups[lookup]["flags"], (('kern', script_lang), )) for subtable in kern_lookups[lookup]["subtables"]: first = [] second = [] offsets = subtable[1][2] # drop non-existing glyphs for new_klasses, klasses in ((first, subtable[1][0]), (second, subtable[1][1])): for klass in klasses: new_klass = [] if klass: for name in klass: if name in font: new_klass.append(name) new_klasses.append(new_klass) # if either of the classes is empty, don’t bother with the subtable if any(first) and any(second): font.addKerningClass(lookup, subtable[0], first, second, offsets)
def merge(args): arabic = fontforge.open(args.arabicfile) arabic.encoding = "Unicode" arabic.mergeFeature(args.feature_file) latin = fontforge.open(args.latinfile) latin.encoding = "Unicode" latin.em = arabic.em latin_locl = "" for glyph in latin.glyphs(): if glyph.color == 0xff0000: latin.removeGlyph(glyph) else: if glyph.glyphname in arabic: name = glyph.glyphname glyph.unicode = -1 glyph.glyphname = name + ".latin" if not latin_locl: latin_locl = "feature locl {lookupflag IgnoreMarks; script latn;" latin_locl += "sub %s by %s;" % (name, glyph.glyphname) arabic.mergeFonts(latin) if latin_locl: latin_locl += "} locl;" arabic.mergeFeatureString(latin_locl) for ch in [(ord(u'،'), "comma"), (ord(u'؛'), "semicolon")]: ar = arabic.createChar(ch[0], fontforge.nameFromUnicode(ch[0])) en = arabic[ch[1]] colon = arabic["colon"] ar.addReference(en.glyphname, psMat.rotate(math.radians(180))) delta = colon.boundingBox()[1] - ar.boundingBox()[1] ar.transform(psMat.translate(0, delta)) ar.left_side_bearing = en.right_side_bearing ar.right_side_bearing = en.left_side_bearing question_ar = arabic.createChar(ord(u'؟'), "uni061F") question_ar.addReference("question", psMat.scale(-1, 1)) question_ar.left_side_bearing = arabic["question"].right_side_bearing question_ar.right_side_bearing = arabic["question"].left_side_bearing # Set metadata arabic.version = args.version years = datetime.now().year == 2015 and 2015 or "2015-%s" % datetime.now().year arabic.copyright = ". ".join(["Portions copyright © %s, Khaled Hosny (<*****@*****.**>)", "Portions " + latin.copyright[0].lower() + latin.copyright[1:].replace("(c)", "©")]) arabic.copyright = arabic.copyright % years handle_cloned_glyphs(arabic) en = "English (US)" arabic.appendSFNTName(en, "Designer", "Khaled Hosny") arabic.appendSFNTName(en, "License URL", "http://scripts.sil.org/OFL") arabic.appendSFNTName(en, "License", 'This Font Software is licensed under the SIL Open Font License, Version 1.1. \ This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR \ CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License \ for the specific language, permissions and limitations governing your use of \ this Font Software.') arabic.appendSFNTName(en, "Descriptor", "Mada is a geometric, unmodulted Arabic display typeface inspired by Cairo road signage.") arabic.appendSFNTName(en, "Sample Text", "صف خلق خود كمثل ٱلشمس إذ بزغت يحظى ٱلضجيع بها نجلاء معطار.") return arabic
def makeWeb(infile, outfile): """If we are building a web version then try to minimise file size""" # "short-post" generates a post table without glyph names to save some KBs # since glyph names are only needed for PDF's as readers use them to # "guess" characters when copying text, which is of little use in web fonts. flags = ("opentype", "short-post", "omit-instructions") font = fontforge.open(infile) font.encoding = "UnicodeBmp" # avoid a crash if compact was set # removed compatibility glyphs that of little use on the web compat_ranges = ((0xFB50, 0xFBB1), (0xFBD3, 0xFD3D), (0xFD50, 0xFDF9), (0xFDFC, 0xFDFC), (0xFE70, 0xFEFC)) for glyph in font.glyphs(): for i in compat_ranges: start = i[0] end = i[1] if start <= glyph.unicode <= end: font.removeGlyph(glyph) break tmpfont = mkstemp(suffix=os.path.basename(outfile))[1] font.generate(tmpfont, flags=flags) font.close() # now open in fontTools from fontTools.ttLib import TTFont font = TTFont(tmpfont, recalcBBoxes=0) # our 'name' table is a bit bulky, and of almost no use in for web fonts, # so we strip all unnecessary entries. name = font["name"] names = [] for record in name.names: platID = record.platformID langID = record.langID nameID = record.nameID # we keep only en_US entries in Windows and Mac platform id, every # thing else is dropped if (platID == 1 and langID == 0) or (platID == 3 and langID == 1033): if nameID == 13: # the full OFL text is too much, replace it with a simple # string if platID == 3: # MS strings are UTF-16 encoded text = "OFL v1.1".encode("utf_16_be") else: text = "OFL v1.1" record.string = text names.append(record) # keep every thing else except Descriptor, Sample Text elif nameID not in (10, 19): names.append(record) name.names = names # FFTM is FontForge specific, remove it del (font["FFTM"]) # force compiling GPOS/GSUB tables by fontTools, saves few tens of KBs for tag in ("GPOS", "GSUB"): font[tag].compile(font) font.save(outfile) font.close() os.remove(tmpfont)
import sys from sortsmill import ffcompat as fontforge amiri = fontforge.open(sys.argv[1]) xits = fontforge.open(sys.argv[2]) amiri.em = 1000 amiri.layers[1].is_quadratic = 0 amiri.selection.all() amiri.unlinkReferences() names = [] alts = [] for aglyph in amiri.glyphs(): u = aglyph.unicode if (u in range(0x1EE00, 0x1EF00) or u in range(0x0660, 0x066E) or u in range(0x06F0, 0x06FA) or u in range(0x0608, 0x060B)): names.append(aglyph.name) for aglyph in amiri.glyphs(): for name in names: if aglyph.name != name and aglyph.name.startswith(name + ".alt"): alts.append(aglyph.name) for name in names + alts: aglyph = amiri[name] if aglyph.name not in xits: xits.createChar(aglyph.unicode, aglyph.name) xglyph = xits[aglyph.name] aglyph.draw(xglyph.glyphPen()) xglyph.width = aglyph.width
from sortsmill import ffcompat as fontforge import sys font = fontforge.open(sys.argv[1]) if len(sys.argv) > 4: font.mergeFeature(sys.argv[4]) font.version = sys.argv[3] # no need for this on the web, saves few KBs font.appendSFNTName ("English (US)", "Descriptor", "") font.generate(sys.argv[2], flags=("round", "opentype", "short-post"))
def makeWeb(infile, outfile): """If we are building a web version then try to minimise file size""" # "short-post" generates a post table without glyph names to save some KBs # since glyph names are only needed for PDF's as readers use them to # "guess" characters when copying text, which is of little use in web fonts. flags = ("opentype", "short-post", "omit-instructions") font = fontforge.open(infile) font.encoding = "UnicodeBmp" # avoid a crash if compact was set # removed compatibility glyphs that of little use on the web compat_ranges = ( (0xfb50, 0xfbb1), (0xfbd3, 0xfd3d), (0xfd50, 0xfdf9), (0xfdfc, 0xfdfc), (0xfe70, 0xfefc), ) for glyph in font.glyphs(): for i in compat_ranges: start = i[0] end = i[1] if start <= glyph.unicode <= end: font.removeGlyph(glyph) break tmpfile = mkstemp(suffix=os.path.basename(outfile))[1] font.generate(tmpfile, flags=flags) font.close() # now open in fontTools from fontTools.ttLib import TTFont ftfont = TTFont(tmpfile) # our 'name' table is a bit bulky, and of almost no use in for web fonts, # so we strip all unnecessary entries. name = ftfont['name'] names = [] for record in name.names: platID = record.platformID langID = record.langID nameID = record.nameID # we keep only en_US entries in Windows and Mac platform id, every # thing else is dropped if (platID == 1 and langID == 0) or (platID == 3 and langID == 1033): if nameID == 13: # the full OFL text is too much, replace it with a simple # string if platID == 3: # MS strings are UTF-16 encoded text = 'OFL v1.1'.encode('utf_16_be') else: text = 'OFL v1.1' record.string = text names.append(record) # keep every thing else except Descriptor, Sample Text elif nameID not in (10, 19): names.append(record) name.names = names # force compiling tables by fontTools, saves few tens of KBs for tag in ftfont.keys(): if hasattr(ftfont[tag], "compile"): ftfont[tag].compile(ftfont) ftfont.save(outfile) ftfont.close() os.remove(tmpfile)