diff options
author | Karl Berry <karl@freefriends.org> | 2012-12-31 23:15:55 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2012-12-31 23:15:55 +0000 |
commit | 7ebd17dd143b59c12da3114ceb657aaf4517822f (patch) | |
tree | 998792078e07d3cfb117ff34e60cc486f87d551f /Master/texmf-dist/doc/fonts/amiri/tools/build.py | |
parent | 8c8b42aef79f3be1cdeb20f0a4572cc5f3fab036 (diff) |
amiri (31dec12)
git-svn-id: svn://tug.org/texlive/trunk@28674 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/doc/fonts/amiri/tools/build.py')
-rwxr-xr-x | Master/texmf-dist/doc/fonts/amiri/tools/build.py | 575 |
1 files changed, 378 insertions, 197 deletions
diff --git a/Master/texmf-dist/doc/fonts/amiri/tools/build.py b/Master/texmf-dist/doc/fonts/amiri/tools/build.py index a2573752d8c..0b46ef547db 100755 --- a/Master/texmf-dist/doc/fonts/amiri/tools/build.py +++ b/Master/texmf-dist/doc/fonts/amiri/tools/build.py @@ -16,12 +16,15 @@ # minimum required FontForge version min_ff_version = "20120702" +script_lang = (('latn', ('dflt', 'TRK ')), ('arab', ('dflt', 'ARA ', 'URD ', 'SND ')), ('DFLT', ('dflt',))) + import fontforge import psMat import sys import os import getopt import math +import unicodedata from tempfile import mkstemp from fontTools.ttLib import TTFont @@ -59,7 +62,6 @@ def cleanAnchors(font): "DotBelow", "DotBelowAlt", "DotHmaza", - "HamzaBelow", "HighHamza", "MarkDotAbove", "MarkDotBelow", @@ -147,10 +149,27 @@ def mergeFeatures(font, feafile): for lookup in font.gpos_lookups: font.removeLookup(lookup) - font.mergeFeature(feafile) - font.mergeFeature(oldfea) + for lookup in font.gsub_lookups: + font.removeLookup(lookup) + + # open feature file and insert the generated GPOS features in place of the + # placeholder text + fea = open(feafile) + old = open(oldfea) + fea_text = fea.read() + fea_text = fea_text.replace("{%anchors%}", old.read()) + fea.close() + old.close() os.remove(oldfea) + # write new feature text back + fea = open(feafile, "w") + fea.write(fea_text) + fea.close() + + # now merge it into the font + font.mergeFeature(feafile) + def makeCss(infiles, outfile): """Builds a CSS file for the entire font family.""" @@ -159,6 +178,7 @@ def makeCss(infiles, outfile): 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() @@ -190,7 +210,11 @@ def generateFont(font, outfile, hack=False): font.generate(outfile, flags=flags) font.close() -def drawOverUnderline(glyph, pos, thickness, width): +def drawOverUnderline(font, name, uni, glyphclass, pos, thickness, width): + glyph = font.createChar(uni, name) + glyph.width = 0 + glyph.glyphclass = glyphclass + pen = glyph.glyphPen() pen.moveTo((-50, pos)) @@ -199,68 +223,68 @@ def drawOverUnderline(glyph, pos, thickness, width): pen.lineTo((width + 50, pos)) pen.closePath() -def makeOverUnderline(font): + return glyph + +def makeOverUnderline(font, over=True, under=True, o_pos=None, u_pos=None): # test string: # صِ̅فْ̅ ̅خَ̅ل̅قَ̅ ̅بًّ̅ صِ̲فْ̲ ̲خَ̲ل̲قَ̲ ̲بِ̲ thickness = font.uwidth # underline width (thickness) - o_pos = font.os2_typoascent - u_pos = font.upos - thickness # underline pos minwidth = 100.0 - widths = {} + if not o_pos: + o_pos = font.os2_typoascent + if not u_pos: + u_pos = font.upos - thickness # underline pos + + # collect glyphs grouped by their widths rounded by 100 units, we will use + # them to decide the widths of over/underline glyphs we will draw + widths = {} for glyph in font.glyphs(): - if glyph.glyphclass == 'baseglyph': + if glyph.glyphclass == 'baseglyph' and glyph.unicode != 0xFDFD: width = round(glyph.width/100) * 100 width = width > minwidth and width or minwidth if not width in widths: widths[width] = [] widths[width].append(glyph.glyphname) - o_encoded = font.createChar(0x0305, 'uni0305') - u_encoded = font.createChar(0x0332, 'uni0332') - o_encoded.width = u_encoded.width = 0 - o_encoded.glyphclass = u_encoded.glyphclass = 'mark' - drawOverUnderline(o_encoded, o_pos, thickness, 500) - drawOverUnderline(u_encoded, u_pos, thickness, 500) + if over: + o_base = drawOverUnderline(font, 'uni0305', 0x0305, 'mark', o_pos, thickness, 500) - o_base = font.createChar(-1, 'uni0305.0') - u_base = font.createChar(-1, 'uni0332.0') - o_base.width = u_base.width = 0 - o_base.glyphclass = u_base.glyphclass = 'baseglyph' - drawOverUnderline(o_base, o_pos, thickness, 500) - drawOverUnderline(u_base, u_pos, thickness, 500) + if under: + u_base = drawOverUnderline(font, 'uni0332', 0x0332, 'mark', u_pos, thickness, 500) - font.addLookup('mark hack', 'gsub_single', (), (("mark",(("arab",("dflt")),)),), font.gsub_lookups[-1]) - font.addLookupSubtable('mark hack', 'mark hack 1') + markset = "%s %s" %(over and o_base.glyphname or "", under and u_base.glyphname or "") - o_encoded.addPosSub('mark hack 1', o_base.glyphname) - u_encoded.addPosSub('mark hack 1', u_base.glyphname) + font.addMarkSet("OverUnderSet", markset) context_lookup_name = 'OverUnderLine' - font.addLookup(context_lookup_name, 'gsub_contextchain', ('ignore_marks'), (("mark",(("arab",("dflt")),)),), font.gsub_lookups[-1]) + font.addLookup(context_lookup_name, 'gsub_contextchain', ('OverUnderSet'), (('mark', script_lang),), font.gsub_lookups[-1]) for width in sorted(widths.keys()): - o_name = 'uni0305.%d' % width - u_name = 'uni0332.%d' % width - o_glyph = font.createChar(-1, o_name) - u_glyph = font.createChar(-1, u_name) - - o_glyph.glyphclass = u_glyph.glyphclass = 'mark' - - drawOverUnderline(o_glyph, o_pos, thickness, width) - drawOverUnderline(u_glyph, u_pos, thickness, width) + # for each width group we create an over/underline glyph with the same + # width, and add a contextual substitution lookup to use it when an + # over/underline follows any glyph in this group single_lookup_name = str(width) font.addLookup(single_lookup_name, 'gsub_single', (), (), font.gsub_lookups[-1]) font.addLookupSubtable(single_lookup_name, single_lookup_name + '1') - o_base.addPosSub(single_lookup_name + '1', o_name) - u_base.addPosSub(single_lookup_name + '1', u_name) + if over: + o_name = 'uni0305.%d' % width + o_glyph = drawOverUnderline(font, o_name, -1, 'mark', o_pos, thickness, width) + o_base.addPosSub(single_lookup_name + '1', o_name) + + if under: + u_name = 'uni0332.%d' % width + u_glyph = drawOverUnderline(font, u_name, -1, 'mark', u_pos, thickness, width) + u_base.addPosSub(single_lookup_name + '1', u_name) - rule = '| [%s] [%s %s] @<%s> | ' %(" ".join(widths[width]), o_base.glyphname, u_base.glyphname, single_lookup_name) + context = "%s %s" %(over and o_base.glyphname or "", under and u_base.glyphname or "") + + rule = '| [%s] [%s] @<%s> | ' %(" ".join(widths[width]), context, single_lookup_name) font.addContextualSubtable(context_lookup_name, context_lookup_name + str(width), 'coverage', rule) @@ -269,41 +293,71 @@ def centerGlyph(glyph): glyph.right_side_bearing = glyph.left_side_bearing = (glyph.right_side_bearing + glyph.left_side_bearing)/2 glyph.width = width -def buildLatinExtras(font, italic): - for ltr, rtl in (("question", "uni061F"), ("radical", "radical.rtlm")): - font[rtl].clear() - font[rtl].addReference(ltr, psMat.scale(-1, 1)) - font[rtl].left_side_bearing = font[ltr].right_side_bearing - font[rtl].right_side_bearing = font[ltr].left_side_bearing +def subsetFont(font, glyphnames, similar=False): + # keep any glyph with the same base name + reported = [] - # slanted arabic question mark - if italic: - question = font.createChar(-1, "uni061F.rtl") - question.addReference("uni061F", italic) - question.useRefsMetrics("uni061F") + if similar: + for name in glyphnames: + for glyph in font.glyphs(): + if "." in glyph.glyphname and glyph.glyphname.split(".")[0] == name: + glyphnames.append(glyph.glyphname) - for name in ("zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"): - if italic: - # they are only used in Arabic contexts, so always reference the - # italic rtl variant - refname = name +".rtl" + # keep any glyph referenced requested glyphs + for name in glyphnames: + if name in font: + glyph = font[name] + for ref in glyph.references: + glyphnames.append(ref[0]) 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) + if name not in reported: + print 'Font ‘%s’ is missing glyph: %s' %(font.fontname, name) + reported.append(name) - 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) - -def mergeLatin(font, feafile, italic=False): + # remove everything else + for glyph in font.glyphs(): + if glyph.glyphname not in glyphnames: + font.removeGlyph(glyph) + +def buildComposition(font, glyphnames): + newnames = [] + + font.addLookup("Latin composition", 'gsub_ligature', (), (('ccmp', script_lang),)) + font.addLookupSubtable("Latin composition", "Latin composition subtable") + + for name in glyphnames: + u = fontforge.unicodeFromName(name) + if 0 < u < 0xfb00: + decomp = unicodedata.decomposition(unichr(u)) + if decomp: + base = decomp.split()[0] + mark = decomp.split()[1] + if not '<' in base: + nmark = None + nbase = None + + for g in font.glyphs(): + if g.unicode == int(base, 16): + nbase = g.glyphname + if g.unicode == int(mark, 16): + nmark = g.glyphname + + if not nbase: + nbase = "uni%04X" % int(base, 16) + if not nmark: + nmark = "uni%04X" % int(mark, 16) + + if nbase in font and nmark in font: + font[name].addPosSub("Latin composition subtable", (nbase, nmark)) + + if base not in glyphnames: + newnames.append(nbase) + if mark not in glyphnames: + newnames.append(nmark) + + return newnames + +def mergeLatin(font, feafile, italic=False, glyphs=None, quran=False): styles = {"Regular": "Roman", "Slanted": "Italic", "Bold": "Bold", @@ -314,88 +368,82 @@ def mergeLatin(font, feafile, italic=False): latinfile = "Crimson-%s.sfd" %style tmpfont = mkstemp(suffix=os.path.basename(latinfile))[1] - latinfont = fontforge.open("sources/crimson/sources/%s" %latinfile) - latinfont.em = 2048 - - # convert to quadratic splines then simplify - latinfont.is_quadratic = True - for glyph in latinfont.glyphs(): - glyph.simplify() + latinfont = fontforge.open("sources/crimson/%s" %latinfile) validateGlyphs(latinfont) # to flatten nested refs mainly - # 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: + 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) - 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 = ("afii57929", "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", - "afii00208", "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) - - # keep any glyph referenced by previous glyphs - for name in latinglyphs: - if name in latinfont: - glyph = latinfont[name] - for ref in glyph.references: - latinglyphs.append(ref[0]) - else: - print 'Font ‘%s’ is missing glyph: %s' %(font.fontname, name) - # remove everything else - for glyph in latinfont.glyphs(): - if glyph.glyphname not in latinglyphs: - latinfont.removeGlyph(glyph) + # 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 @@ -404,43 +452,31 @@ def mergeLatin(font, feafile, italic=False): # upright so it works reasonably with bot scripts if italic: if "Bold" in style: - upright = fontforge.open("sources/crimson/sources/Crimson-Bold.sfd") + upright = fontforge.open("sources/crimson/Crimson-Bold.sfd") else: - upright = fontforge.open("sources/crimson/sources/Crimson-Roman.sfd") - upright.em = 2048 + upright = fontforge.open("sources/crimson/Crimson-Roman.sfd") shared = ("exclam", "quotedbl", "numbersign", "dollar", "percent", - "quotesingle", "parenleft", "parenright", "asterisk", "plus", - "slash", "colon", "semicolon", "less", "equal", "greater", - "question", "at", "bracketleft", "backslash", "bracketright", - "asciicircum", "braceleft", "bar", "braceright", "brokenbar", - "section", "copyright", "guillemotleft", "logicalnot", - "registered", "plusminus", "uni00B2", "uni00B3", "paragraph", - "uni00B9", "ordmasculine", "guillemotright", "onequarter", - "onehalf", "threequarters", "questiondown", "quoteleft", - "quoteright", "quotesinglbase", "quotereversed", - "quotedblleft", "quotedblright", "quotedblbase", "uni201F", - "dagger", "daggerdbl", "perthousand", "minute", "second", - "guilsinglleft", "guilsinglright", "fraction", "uni2213") - - digits = ("zero", "one", "two", "three", "four", "five", "six", - "seven", "eight", "nine") + "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.glyphname += '.ltr' - glyph.unicode = -1 + glyph.clear() upright.selection.select(name) upright.copy() latinfont.createChar(upright[name].encoding, name) latinfont.selection.select(name) latinfont.paste() - if not name + ".ara" in font: - rtl = latinfont.createChar(-1, name + ".rtl") - rtl.addReference(name, italic) - rtl.useRefsMetrics(name) - for name in digits: glyph = latinfont[name] glyph.glyphname += '.ltr' @@ -471,13 +507,14 @@ def mergeLatin(font, feafile, italic=False): # 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"], dummy = latinfont.getLookupInfo(lookup) - for subtable in latinfont.getLookupSubtables(lookup): - if latinfont.isKerningClass(subtable): - kern_lookups[lookup]["subtables"].append((subtable, latinfont.getKerningClass(subtable))) + 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) @@ -491,33 +528,56 @@ def mergeLatin(font, feafile, italic=False): font.mergeFonts(tmpfont) os.remove(tmpfont) - buildLatinExtras(font, italic) - - # we want to merge features after merging the latin font because many - # referenced glyphs are in the latin font - - if italic: - font.mergeFeature("sources/italic_ltra.fea") - font.mergeFeature("sources/italic_rtla.fea") + if not quran: + buildComposition(font, latinglyphs) - mergeFeatures(font, feafile) + # 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) - font.mergeFeature("sources/latin_gsub.fea") + 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', - ( - ('DFLT', ('dflt',)), - ('latn', ('dflt', 'TRK ')), - ) - ),) + (('kern', script_lang),) ) for subtable in kern_lookups[lookup]["subtables"]: - font.addKerningClass(lookup, subtable[0], subtable[1][0], subtable[1][1], subtable[1][2]) + 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 makeWeb(infile, outfile): """If we are building a web version then try to minimise file size""" @@ -528,6 +588,7 @@ def makeWeb(infile, outfile): 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 = ( @@ -601,6 +662,13 @@ def makeSlanted(infile, outfile, feafile, version, slant): skew = psMat.skew(-slant * math.pi/180.0) font.selection.all() + punct = ("period", "guillemotleft", "guillemotright", "braceleft", "bar", + "braceright", "bracketleft", "bracketright", "parenleft", + "parenright", "slash", "backslash", "brokenbar", "uni061F") + + for name in punct: + font.selection.select(("less", None), name) + font.transform(skew) # fix metadata @@ -614,12 +682,117 @@ def makeSlanted(infile, outfile, feafile, version, slant): font.fontname = font.fontname.replace("Regular", "Slanted") font.appendSFNTName("Arabic (Egypt)", "SubFamily", "مائل") - mergeLatin(font, feafile, skew) + mergeLatin(font, feafile, italic=skew) + + # we want to merge features after merging the latin font because many + # referenced glyphs are in the latin font + mergeFeatures(font, feafile) + + generateFont(font, outfile) + +def makeQuran(infile, outfile, feafile, version): + font = makeDesktop(infile, outfile, feafile, version, False, False) + + # fix metadata + font.fontname = font.fontname.replace("-Regular", "Quran-Regular") + font.familyname += " Quran" + font.fullname += " Quran" + + digits = ("zero", "one", "two", "three", "four", "five", "six", + "seven", "eight", "nine") + + mergeLatin(font, feafile, glyphs=digits, quran=True) + + punct = ("period", "guillemotleft", "guillemotright", "braceleft", "bar", + "braceright", "bracketleft", "bracketright", "parenleft", + "parenright", "slash", "backslash") + + for name in punct: + if name+".ara" in font: + glyph = font[name+".ara"] + glyph.glyphname = name + glyph.unicode = fontforge.unicodeFromName(name) + + # abuse U+065C as a below form of U+06EC, for Qaloon + dotabove = font["uni06EC"] + dotbelow = font["uni065C"] + delta = dotbelow.boundingBox()[-1] - dotabove.boundingBox()[-1] + dotbelow.references = [] + dotbelow.addReference(dotabove.glyphname, psMat.translate(0, delta)) + dotbelow.addAnchorPoint("TashkilTashkilBelow", "basemark", 220, dotbelow.boundingBox()[1] - 100) + + # scale some vowel marks and dots down a bit + font["uni0651"].transform(psMat.scale(0.8)) + for mark in ("uni064B", "uni064C", "uni064E", "uni064F", "uni06E1"): + font[mark].transform(psMat.scale(0.9)) + + for dot in ("TwoDots.a", "ThreeDots.a", "vTwoDots.a"): + font[dot].transform(psMat.scale(0.9)) + + quran_glyphs = [] + + # create dummy glyphs used for some coding hacks + for i in range(1, 11): + dummy = font.createChar(-1, "dummy%s" %i) + dummy.width = 0 + quran_glyphs.append(dummy.glyphname) + + mergeFeatures(font, feafile) + + quran_glyphs += digits + quran_glyphs += punct + quran_glyphs += ("space", + "uni060C", "uni0615", "uni0617", "uni0618", "uni0619", "uni061A", + "uni061B", "uni061E", "uni061F", "uni0621", "uni0622", "uni0623", + "uni0624", "uni0625", "uni0626", "uni0627", "uni0628", "uni0629", + "uni062A", "uni062B", "uni062C", "uni062D", "uni062E", "uni062F", + "uni0630", "uni0631", "uni0632", "uni0633", "uni0634", "uni0635", + "uni0636", "uni0637", "uni0638", "uni0639", "uni063A", "uni0640", + "uni0641", "uni0642", "uni0643", "uni0644", "uni0645", "uni0646", + "uni0647", "uni0648", "uni0649", "uni064A", "uni064B", "uni064C", + "uni064D", "uni064E", "uni064F", "uni0650", "uni0651", "uni0652", + "uni0653", "uni0654", "uni0655", "uni0656", "uni0657", "uni0658", + "uni065C", "uni0660", "uni0661", "uni0662", "uni0663", "uni0664", + "uni0665", "uni0666", "uni0667", "uni0668", "uni0669", "uni066E", + "uni066F", "uni06A1", "uni06BA", "uni0670", "uni0671", "uni067A", + "uni06CC", "uni06D6", "uni06D7", "uni06D8", "uni06D9", "uni06DA", + "uni06DB", "uni06DC", "uni06DD", "uni06DE", "uni06DF", "uni06E0", + "uni06E1", "uni06E2", "uni06E3", "uni06E4", "uni06E5", "uni06E6", + "uni06E7", "uni06E8", "uni06E9", "uni06EA", "uni06EB", "uni06EC", + "uni06ED", "uni06F0", "uni06F1", "uni06F2", "uni06F3", "uni06F4", + "uni06F5", "uni06F6", "uni06F7", "uni06F8", "uni06F9", "uni08F0", + "uni08F1", "uni08F2", "uni2000", "uni2001", "uni2002", "uni2003", + "uni2004", "uni2005", "uni2006", "uni2007", "uni2008", "uni2009", + "uni200A", "uni200B", "uni200C", "uni200D", "uni200E", "uni200F", + "uni2028", "uni2029", "uni202A", "uni202B", "uni202C", "uni202D", + "uni202E", "uni202F", "uni25CC", "uniFDFA", "uniFDFD") + quran_glyphs += ("uni030A", "uni0325") # ring above and below + + subsetFont(font, quran_glyphs, True) + + # set font ascent to the highest glyph in the font so that waqf marks don't + # get truncated + # we could have set os2_typoascent_add and hhea_ascent_add, but ff makes + # the offset relative to em-size in the former and font bounds in the + # later, but we want both to be relative to font bounds + ymax = 0 + for glyph in font.glyphs(): + bb = glyph.boundingBox() + if bb[-1] > ymax: + ymax = bb[-1] + + font.os2_typoascent = font.hhea_ascent = ymax + + # create overline glyph to be used for sajda line, it is positioned + # vertically at the level of the base of waqf marks + overline_pos = font[0x06D7].boundingBox()[1] + makeOverUnderline(font, under=False, o_pos=overline_pos) generateFont(font, outfile) 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) @@ -638,6 +811,10 @@ def makeDesktop(infile, outfile, feafile, version, latin=True, generate=True): 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, True) else: @@ -672,7 +849,7 @@ if __name__ == "__main__": try: opts, args = getopt.gnu_getopt(sys.argv[1:], "h", - ["help", "input=", "output=", "features=", "version=", "slant=", "css", "web"]) + ["help", "input=", "output=", "features=", "version=", "slant=", "css", "web", "quran"]) except getopt.GetoptError, err: usage(str(err), -1) @@ -683,6 +860,7 @@ if __name__ == "__main__": slant = False css = False web = False + quran = False for opt, arg in opts: if opt in ("-h", "--help"): @@ -694,6 +872,7 @@ if __name__ == "__main__": elif opt == "--slant": slant = float(arg) elif opt == "--css": css = True elif opt == "--web": web = True + elif opt == "--quran": quran = True if not infile: usage("No input file specified", -1) @@ -712,5 +891,7 @@ if __name__ == "__main__": if slant: makeSlanted(infile, outfile, feafile, version, slant) + elif quran: + makeQuran(infile, outfile, feafile, version) else: makeDesktop(infile, outfile, feafile, version) |