diff options
Diffstat (limited to 'Master/texmf-dist/doc/fonts/amiri/tools')
-rwxr-xr-x | Master/texmf-dist/doc/fonts/amiri/tools/build.py | 324 | ||||
-rwxr-xr-x | Master/texmf-dist/doc/fonts/amiri/tools/runtest.py | 79 |
2 files changed, 403 insertions, 0 deletions
diff --git a/Master/texmf-dist/doc/fonts/amiri/tools/build.py b/Master/texmf-dist/doc/fonts/amiri/tools/build.py new file mode 100755 index 00000000000..c72eb81c706 --- /dev/null +++ b/Master/texmf-dist/doc/fonts/amiri/tools/build.py @@ -0,0 +1,324 @@ +#!/usr/bin/python +# coding=utf-8 +# +# build.py - Amiri font build utility +# +# Written in 2010-2011 by Khaled Hosny <khaledhosny@eglug.org> +# +# To the extent possible under law, the author have dedicated all copyright +# and related and neighboring rights to this software to the public domain +# worldwide. This software is distributed without any warranty. +# +# You should have received a copy of the CC0 Public Domain Dedication along +# with this software. If not, see +# <http://creativecommons.org/publicdomain/zero/1.0/>. + +import fontforge +import sys +import os +import getopt +import tempfile + +def genCSS(font, base): + style = ("slanted" in font.fullname.lower()) and "oblique" or "normal" + weight = font.os2_weight + family = font.familyname + "Web" + + css = """ +@font-face { + font-family: %(family)s; + font-style: %(style)s; + font-weight: %(weight)s; + src: url('%(base)s.eot?') format('eot'), + url('%(base)s.woff') format('woff'), + url('%(base)s.ttf') format('truetype'); +} +""" %{"style":style, "weight":weight, "family":family, "base":base} + + return css + +def cleanAnchors(font): + klasses = ( + "Dash", + "DigitAbove", + "DigitBelow", + "DotAbove", + "DotAlt", + "DotBelow", + "DotBelowAlt", + "DotHmaza", + "HighHamza", + "MarkDotAbove", + "MarkDotBelow", + "RingBelow", + "RingDash", + "Stroke", + "TaaAbove", + "TaaBelow", + "Tail", + "TashkilAboveDot", + "TashkilBelowDot", + "TwoDotsAbove", + "TwoDotsBelow", + "TwoDotsBelowAlt" + ) + + for klass in klasses: + subtable = font.getSubtableOfAnchor(klass) + lookup = font.getLookupOfSubtable(subtable) + font.removeLookup(lookup) + +def cleanUnused(font): + for glyph in font.glyphs(): + # glyphs colored yellow are pending removal, so we remove them from the + # final font. + if glyph.color == 0xffff00: + font.removeGlyph(glyph) + +def validateGlyphs(font): + flipped_ref = 0x10 + wrong_dir = 0x8 + missing_extrema = 0x20 + for glyph in font.glyphs(): + state = glyph.validate(True) + if state & flipped_ref: + glyph.unlinkRef() + glyph.correctDirection() + if state & wrong_dir: + glyph.correctDirection() + if state & missing_extrema: + glyph.addExtrema("all") + +def setVersion(font, version): + font.version = "%07.3f" %float(version) + font.appendSFNTName("Arabic (Egypt)", "Version", "إصدارة %s" %font.version.replace(".", ",")) + +def mergeFeatures(font, feafile): + oldfea = tempfile.mkstemp(suffix='.fea')[1] + font.generateFeatureFile(oldfea) + + for lookup in font.gpos_lookups: + font.removeLookup(lookup) + + font.mergeFeature(feafile) + font.mergeFeature(oldfea) + os.remove(oldfea) + +def stripLocalName(font): + for name in font.sfnt_names: + if name[0] != "English (US)" and name[1] in ("Family", "Fullname"): + font.appendSFNTName(name[0], name[1], None) + +def makeCss(infile, outfile): + text = "" + + for f in infile.split(): + base = os.path.splitext(os.path.basename(f))[0] + font = fontforge.open(f) + text += genCSS(font, base) + font.close() + + out = open(outfile, "w") + out.write(text) + out.close() + +def generateFont(font, outfile, hack=False): + flags = ("opentype", "dummy-dsig", "round") + + if hack: + # ff takes long to write the file, so generate to tmp file then rename + # it to keep fontview happy + import subprocess + tmpout = tempfile.mkstemp(dir=".", suffix=os.path.basename(outfile))[1] + font.generate(tmpout, flags=flags) + #os.rename(tmpout, outfile) # file monitor will not see this, why? + p = subprocess.Popen("cat %s > %s" %(tmpout, outfile), shell=True) + p.wait() + os.remove(tmpout) + else: + font.generate(outfile, flags=flags) + font.close() + +def makeWeb(infile, outfile): + """If we are building a web version then try to minimise file size""" + from fontTools.ttLib import TTFont + + # suppress noisy DeprecationWarnings in fontTools + import warnings + warnings.filterwarnings("ignore",category=DeprecationWarning) + + font = TTFont(infile, recalcBBoxes=0) + + # internal glyph names are useless on the web, so force a format 3 post + # table + post = font['post'] + post.formatType = 3.0 + post.glyphOrder = None + del(post.extraNames) + del(post.mapping) + + # '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 + + # dummy DSIG is useless here too + del(font['DSIG']) + + # FFTM is FontForge specific, remove too + 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() + +def makeSlanted(infile, outfile, slant): + import psMat + import math + + font = fontforge.open(infile) + + # compute amout of skew, magic formula copied from fontforge sources + skew = psMat.skew(-slant * math.pi/180.0) + + font.selection.all() + font.transform(skew) + + # fix metadata + font.italicangle = slant + font.fontname = font.fontname.replace("Regular", "Slanted") + font.fullname += " Slanted" + font.appendSFNTName("Arabic (Egypt)", "SubFamily", "مائل") + +def makeSfd(infile, outfile, version): + font = fontforge.open(infile) + if version: + setVersion(font, version) + font.save(outfile) + font.close() + +def makeDesktop(infile, outfile, feafile, version, nolocalname): + font = fontforge.open(infile) + + if version: + setVersion(font, version) + + # remove anchors that are not needed in the production font + cleanAnchors(font) + + # fix some common font issues + validateGlyphs(font) + + # remove unused glyphs + cleanUnused(font) + + if nolocalname: + stripLocalName(font) + + if feafile: + mergeFeatures(font, feafile) + + generateFont(font, outfile, True) + +def usage(code): + message = """Usage: %s OPTIONS... + +Options: + --input=FILE file name of input font + --output=FILE file name of output font + --version=VALUE set font version to VALUE + --feature-file=FILE optional feature file + --slant=VALUE autoslant + --css output is a CSS file + --sfd output is a SFD file + --web output is web version + --desktop output is desktop version + --no-localised-name strip out localised font name + + -h, --help print this message and exit +""" % os.path.basename(sys.argv[0]) + + print message + sys.exit(code) + +if __name__ == "__main__": + try: + opts, args = getopt.gnu_getopt(sys.argv[1:], + "h", + ["help","input=","output=", "feature-file=", "version=", "slant=", "css", "web", "desktop", "sfd", "no-localised-name"]) + except getopt.GetoptError, err: + print str(err) + usage(-1) + + infile = None + outfile = None + feafile = None + version = None + slant = False + css = False + web = False + desktop = False + sfd = False + nolocalname = False + + for opt, arg in opts: + if opt in ("-h", "--help"): + usage(0) + elif opt == "--input": infile = arg + elif opt == "--output": outfile = arg + elif opt == "--feature-file": feafile = arg + elif opt == "--version": version = arg + elif opt == "--slant": slant = float(arg) + elif opt == "--css": css = True + elif opt == "--web": web = True + elif opt == "--desktop": desktop = True + elif opt == "--sfd": sfd = True + elif opt == "--no-localised-name": nolocalname = True + + if not infile: + print "No input file" + usage(-1) + if not outfile: + print "No output file" + usage(-1) + + if css: + makeCss(infile, outfile) + + if sfd: + makeSfd(infile, outfile, version) + + if slant: + makeSlanted(infile, outfile, slant) + + if web: + makeWeb(infile, outfile) + + if desktop: + makeDesktop(infile, outfile, feafile, version, nolocalname) diff --git a/Master/texmf-dist/doc/fonts/amiri/tools/runtest.py b/Master/texmf-dist/doc/fonts/amiri/tools/runtest.py new file mode 100755 index 00000000000..fe14f279766 --- /dev/null +++ b/Master/texmf-dist/doc/fonts/amiri/tools/runtest.py @@ -0,0 +1,79 @@ +#!/usr/bin/python + +import sys +import os +import csv +import subprocess + +def runHB(row, font): + args = ["hb-shape", "--no-clusters", "--no-positions", + "--font-file=%s" %font, + "--direction=%s" %row[0], + "--script=%s" %row[1], + "--language=%s" %row[2], + "--features=%s" %row[3], + "--text=%s" %row[4]] + process = subprocess.Popen(args, stdout=subprocess.PIPE) + return process.communicate()[0].strip() + +def runTest(reader, font): + count = 0 + failed = {} + passed = [] + for row in reader: + count += 1 + row[4] = ('\\' in row[4]) and row[4].decode('unicode-escape') or row[4] + text = row[4] + reference = row[5] + result = runHB(row, font) + if reference == result: + passed.append(count) + else: + failed[count] = (text, reference, result) + + return passed, failed + +def initTest(reader, font): + out = "" + for row in reader: + result = runHB(row, font) + out += "%s;%s\n" %(";".join(row), result) + + return out + +if __name__ == '__main__': + init = False + args = sys.argv[1:] + + if len (sys.argv) > 2 and sys.argv[1] == "-i": + init = True + args = sys.argv[2:] + + for arg in args: + testname = arg + + testfd = open(testname, 'r') + fontname = testfd.readline().strip("# \n") + reader = csv.reader(testfd, delimiter=';') + + if init: + outname = testname+".test" + outfd = open(outname, "w") + outfd.write("# %s\n" %fontname) + outfd.write(initTest(reader, fontname)) + outfd.close() + sys.exit(0) + + passed, failed = runTest(reader, fontname) + message = "%s: %d passed, %d failed" %(os.path.basename(testname), len(passed), len(failed)) + + if failed: + print message + for test in failed: + print test + print "string: \t", failed[test][0] + print "reference:\t", failed[test][1] + print "result: \t", failed[test][2] + sys.exit(1) + else: + print message |