summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/doc/fonts/amiri/tools
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2012-12-31 23:15:55 +0000
committerKarl Berry <karl@freefriends.org>2012-12-31 23:15:55 +0000
commit7ebd17dd143b59c12da3114ceb657aaf4517822f (patch)
tree998792078e07d3cfb117ff34e60cc486f87d551f /Master/texmf-dist/doc/fonts/amiri/tools
parent8c8b42aef79f3be1cdeb20f0a4572cc5f3fab036 (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')
-rwxr-xr-xMaster/texmf-dist/doc/fonts/amiri/tools/build.py575
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)