layout/reftests/fonts/gsubtest/makegsubfonts.py

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

     2 import os
     3 import textwrap
     4 from xml.etree import ElementTree
     5 from fontTools.ttLib import TTFont, newTable
     6 from fontTools.misc.psCharStrings import T2CharString
     7 from fontTools.ttLib.tables.otTables import GSUB,\
     8     ScriptList, ScriptRecord, Script, DefaultLangSys,\
     9     FeatureList, FeatureRecord, Feature,\
    10     LookupList, Lookup, AlternateSubst, SingleSubst
    12 # paths
    13 directory = os.path.dirname(__file__)
    14 shellSourcePath = os.path.join(directory, "gsubtest-shell.ttx")
    15 shellTempPath = os.path.join(directory, "gsubtest-shell.otf")
    16 featureList = os.path.join(directory, "gsubtest-features.txt")
    17 javascriptData = os.path.join(directory, "gsubtest-features.js")
    18 outputPath = os.path.join(os.path.dirname(directory), "gsubtest-lookup%d")
    20 baseCodepoint = 0xe000
    22 # -------
    23 # Features
    24 # -------
    26 f = open(featureList, "rb")
    27 text = f.read()
    28 f.close()
    29 mapping = []
    30 for line in text.splitlines():
    31     line = line.strip()
    32     if not line:
    33         continue
    34     if line.startswith("#"):
    35         continue
    36     # parse
    37     values = line.split("\t")
    38     tag = values.pop(0)
    39     mapping.append(tag);
    41 # --------
    42 # Outlines
    43 # --------
    45 def addGlyphToCFF(glyphName=None, program=None, private=None, globalSubrs=None, charStringsIndex=None, topDict=None, charStrings=None):
    46     charString = T2CharString(program=program, private=private, globalSubrs=globalSubrs)
    47     charStringsIndex.append(charString)
    48     glyphID = len(topDict.charset)
    49     charStrings.charStrings[glyphName] = glyphID
    50     topDict.charset.append(glyphName)
    52 def makeLookup1():
    53     # make a variation of the shell TTX data
    54     f = open(shellSourcePath)
    55     ttxData = f.read()
    56     f.close()
    57     ttxData = ttxData.replace("__familyName__", "gsubtest-lookup1")
    58     tempShellSourcePath = shellSourcePath + ".temp"
    59     f = open(tempShellSourcePath, "wb")
    60     f.write(ttxData)
    61     f.close()
    63     # compile the shell
    64     shell = TTFont(sfntVersion="OTTO")
    65     shell.importXML(tempShellSourcePath)
    66     shell.save(shellTempPath)
    67     os.remove(tempShellSourcePath)
    69     # load the shell
    70     shell = TTFont(shellTempPath)
    72     # grab the PASS and FAIL data
    73     hmtx = shell["hmtx"]
    74     glyphSet = shell.getGlyphSet()
    76     failGlyph = glyphSet["F"]
    77     failGlyph.decompile()
    78     failGlyphProgram = list(failGlyph.program)
    79     failGlyphMetrics = hmtx["F"]
    81     passGlyph = glyphSet["P"]
    82     passGlyph.decompile()
    83     passGlyphProgram = list(passGlyph.program)
    84     passGlyphMetrics = hmtx["P"]
    86     # grab some tables
    87     hmtx = shell["hmtx"]
    88     cmap = shell["cmap"]
    90     # start the glyph order
    91     existingGlyphs = [".notdef", "space", "F", "P"]
    92     glyphOrder = list(existingGlyphs)
    94     # start the CFF
    95     cff = shell["CFF "].cff
    96     globalSubrs = cff.GlobalSubrs
    97     topDict = cff.topDictIndex[0]
    98     topDict.charset = existingGlyphs
    99     private = topDict.Private
   100     charStrings = topDict.CharStrings
   101     charStringsIndex = charStrings.charStringsIndex
   103     features = sorted(mapping)
   105     # build the outline, hmtx and cmap data
   106     cp = baseCodepoint
   107     for index, tag in enumerate(features):
   109     	# tag.pass
   110     	glyphName = "%s.pass" % tag
   111     	glyphOrder.append(glyphName)
   112     	addGlyphToCFF(
   113     		glyphName=glyphName,
   114     		program=passGlyphProgram,
   115     		private=private,
   116     		globalSubrs=globalSubrs,
   117     		charStringsIndex=charStringsIndex,
   118     		topDict=topDict,
   119             charStrings=charStrings
   120     	)
   121     	hmtx[glyphName] = passGlyphMetrics
   123     	for table in cmap.tables:
   124     		if table.format == 4:
   125     			table.cmap[cp] = glyphName
   126     		else:
   127     			raise NotImplementedError, "Unsupported cmap table format: %d" % table.format
   128     	cp += 1
   130     	# tag.fail
   131     	glyphName = "%s.fail" % tag
   132     	glyphOrder.append(glyphName)
   133     	addGlyphToCFF(
   134     		glyphName=glyphName,
   135     		program=failGlyphProgram,
   136     		private=private,
   137     		globalSubrs=globalSubrs,
   138     		charStringsIndex=charStringsIndex,
   139     		topDict=topDict,
   140             charStrings=charStrings
   141     	)
   142     	hmtx[glyphName] = failGlyphMetrics
   144     	for table in cmap.tables:
   145     		if table.format == 4:
   146     			table.cmap[cp] = glyphName
   147     		else:
   148     			raise NotImplementedError, "Unsupported cmap table format: %d" % table.format
   150         # bump this up so that the sequence is the same as the lookup 3 font
   151     	cp += 3
   153     # set the glyph order
   154     shell.setGlyphOrder(glyphOrder)
   156     # start the GSUB
   157     shell["GSUB"] = newTable("GSUB")
   158     gsub = shell["GSUB"].table = GSUB()
   159     gsub.Version = 1.0
   161     # make a list of all the features we will make
   162     featureCount = len(features)
   164     # set up the script list
   165     scriptList = gsub.ScriptList = ScriptList()
   166     scriptList.ScriptCount = 1
   167     scriptList.ScriptRecord = []
   168     scriptRecord = ScriptRecord()
   169     scriptList.ScriptRecord.append(scriptRecord)
   170     scriptRecord.ScriptTag = "DFLT"
   171     script = scriptRecord.Script = Script()
   172     defaultLangSys = script.DefaultLangSys = DefaultLangSys()
   173     defaultLangSys.FeatureCount = featureCount
   174     defaultLangSys.FeatureIndex = range(defaultLangSys.FeatureCount)
   175     defaultLangSys.ReqFeatureIndex = 65535
   176     defaultLangSys.LookupOrder = None
   177     script.LangSysCount = 0
   178     script.LangSysRecord = []
   180     # set up the feature list
   181     featureList = gsub.FeatureList = FeatureList()
   182     featureList.FeatureCount = featureCount
   183     featureList.FeatureRecord = []
   184     for index, tag in enumerate(features):
   185         # feature record
   186         featureRecord = FeatureRecord()
   187         featureRecord.FeatureTag = tag
   188         feature = featureRecord.Feature = Feature()
   189         featureList.FeatureRecord.append(featureRecord)
   190         # feature
   191         feature.FeatureParams = None
   192         feature.LookupCount = 1
   193         feature.LookupListIndex = [index]
   195     # write the lookups
   196     lookupList = gsub.LookupList = LookupList()
   197     lookupList.LookupCount = featureCount
   198     lookupList.Lookup = []
   199     for tag in features:
   200         # lookup
   201         lookup = Lookup()
   202         lookup.LookupType = 1
   203         lookup.LookupFlag = 0
   204         lookup.SubTableCount = 1
   205         lookup.SubTable = []
   206         lookupList.Lookup.append(lookup)
   207         # subtable
   208         subtable = SingleSubst()
   209         subtable.Format = 2
   210         subtable.LookupType = 1
   211         subtable.mapping = {
   212             "%s.pass" % tag : "%s.fail" % tag,
   213             "%s.fail" % tag : "%s.pass" % tag,
   214         }
   215         lookup.SubTable.append(subtable)
   217     path = outputPath % 1 + ".otf"
   218     if os.path.exists(path):
   219     	os.remove(path)
   220     shell.save(path)
   222     # get rid of the shell
   223     if os.path.exists(shellTempPath):
   224         os.remove(shellTempPath)
   226 def makeLookup3():
   227     # make a variation of the shell TTX data
   228     f = open(shellSourcePath)
   229     ttxData = f.read()
   230     f.close()
   231     ttxData = ttxData.replace("__familyName__", "gsubtest-lookup3")
   232     tempShellSourcePath = shellSourcePath + ".temp"
   233     f = open(tempShellSourcePath, "wb")
   234     f.write(ttxData)
   235     f.close()
   237     # compile the shell
   238     shell = TTFont(sfntVersion="OTTO")
   239     shell.importXML(tempShellSourcePath)
   240     shell.save(shellTempPath)
   241     os.remove(tempShellSourcePath)
   243     # load the shell
   244     shell = TTFont(shellTempPath)
   246     # grab the PASS and FAIL data
   247     hmtx = shell["hmtx"]
   248     glyphSet = shell.getGlyphSet()
   250     failGlyph = glyphSet["F"]
   251     failGlyph.decompile()
   252     failGlyphProgram = list(failGlyph.program)
   253     failGlyphMetrics = hmtx["F"]
   255     passGlyph = glyphSet["P"]
   256     passGlyph.decompile()
   257     passGlyphProgram = list(passGlyph.program)
   258     passGlyphMetrics = hmtx["P"]
   260     # grab some tables
   261     hmtx = shell["hmtx"]
   262     cmap = shell["cmap"]
   264     # start the glyph order
   265     existingGlyphs = [".notdef", "space", "F", "P"]
   266     glyphOrder = list(existingGlyphs)
   268     # start the CFF
   269     cff = shell["CFF "].cff
   270     globalSubrs = cff.GlobalSubrs
   271     topDict = cff.topDictIndex[0]
   272     topDict.charset = existingGlyphs
   273     private = topDict.Private
   274     charStrings = topDict.CharStrings
   275     charStringsIndex = charStrings.charStringsIndex
   277     features = sorted(mapping)
   279     # build the outline, hmtx and cmap data
   280     cp = baseCodepoint
   281     for index, tag in enumerate(features):
   283     	# tag.pass
   284     	glyphName = "%s.pass" % tag
   285     	glyphOrder.append(glyphName)
   286     	addGlyphToCFF(
   287     		glyphName=glyphName,
   288     		program=passGlyphProgram,
   289     		private=private,
   290     		globalSubrs=globalSubrs,
   291     		charStringsIndex=charStringsIndex,
   292     		topDict=topDict,
   293             charStrings=charStrings
   294     	)
   295     	hmtx[glyphName] = passGlyphMetrics
   297     	# tag.fail
   298     	glyphName = "%s.fail" % tag
   299     	glyphOrder.append(glyphName)
   300     	addGlyphToCFF(
   301     		glyphName=glyphName,
   302     		program=failGlyphProgram,
   303     		private=private,
   304     		globalSubrs=globalSubrs,
   305     		charStringsIndex=charStringsIndex,
   306     		topDict=topDict,
   307             charStrings=charStrings
   308     	)
   309     	hmtx[glyphName] = failGlyphMetrics
   311     	# tag.default
   312     	glyphName = "%s.default" % tag
   313     	glyphOrder.append(glyphName)
   314     	addGlyphToCFF(
   315     		glyphName=glyphName,
   316     		program=passGlyphProgram,
   317     		private=private,
   318     		globalSubrs=globalSubrs,
   319     		charStringsIndex=charStringsIndex,
   320     		topDict=topDict,
   321             charStrings=charStrings
   322     	)
   323     	hmtx[glyphName] = passGlyphMetrics
   325     	for table in cmap.tables:
   326     		if table.format == 4:
   327     			table.cmap[cp] = glyphName
   328     		else:
   329     			raise NotImplementedError, "Unsupported cmap table format: %d" % table.format
   330     	cp += 1
   332     	# tag.alt1,2,3
   333     	for i in range(1,4):
   334     		glyphName = "%s.alt%d" % (tag, i)
   335     		glyphOrder.append(glyphName)
   336     		addGlyphToCFF(
   337     			glyphName=glyphName,
   338     			program=failGlyphProgram,
   339     			private=private,
   340     			globalSubrs=globalSubrs,
   341     			charStringsIndex=charStringsIndex,
   342     			topDict=topDict,
   343                 charStrings=charStrings
   344     		)
   345     		hmtx[glyphName] = failGlyphMetrics
   346     		for table in cmap.tables:
   347     			if table.format == 4:
   348     				table.cmap[cp] = glyphName
   349     			else:
   350     				raise NotImplementedError, "Unsupported cmap table format: %d" % table.format
   351     		cp += 1
   353     # set the glyph order
   354     shell.setGlyphOrder(glyphOrder)
   356     # start the GSUB
   357     shell["GSUB"] = newTable("GSUB")
   358     gsub = shell["GSUB"].table = GSUB()
   359     gsub.Version = 1.0
   361     # make a list of all the features we will make
   362     featureCount = len(features)
   364     # set up the script list
   365     scriptList = gsub.ScriptList = ScriptList()
   366     scriptList.ScriptCount = 1
   367     scriptList.ScriptRecord = []
   368     scriptRecord = ScriptRecord()
   369     scriptList.ScriptRecord.append(scriptRecord)
   370     scriptRecord.ScriptTag = "DFLT"
   371     script = scriptRecord.Script = Script()
   372     defaultLangSys = script.DefaultLangSys = DefaultLangSys()
   373     defaultLangSys.FeatureCount = featureCount
   374     defaultLangSys.FeatureIndex = range(defaultLangSys.FeatureCount)
   375     defaultLangSys.ReqFeatureIndex = 65535
   376     defaultLangSys.LookupOrder = None
   377     script.LangSysCount = 0
   378     script.LangSysRecord = []
   380     # set up the feature list
   381     featureList = gsub.FeatureList = FeatureList()
   382     featureList.FeatureCount = featureCount
   383     featureList.FeatureRecord = []
   384     for index, tag in enumerate(features):
   385     	# feature record
   386     	featureRecord = FeatureRecord()
   387     	featureRecord.FeatureTag = tag
   388     	feature = featureRecord.Feature = Feature()
   389     	featureList.FeatureRecord.append(featureRecord)
   390     	# feature
   391     	feature.FeatureParams = None
   392     	feature.LookupCount = 1
   393     	feature.LookupListIndex = [index]
   395     # write the lookups
   396     lookupList = gsub.LookupList = LookupList()
   397     lookupList.LookupCount = featureCount
   398     lookupList.Lookup = []
   399     for tag in features:
   400     	# lookup
   401     	lookup = Lookup()
   402     	lookup.LookupType = 3
   403     	lookup.LookupFlag = 0
   404     	lookup.SubTableCount = 1
   405     	lookup.SubTable = []
   406     	lookupList.Lookup.append(lookup)
   407     	# subtable
   408     	subtable = AlternateSubst()
   409     	subtable.Format = 1
   410     	subtable.LookupType = 3
   411     	subtable.alternates = {
   412     		"%s.default" % tag : ["%s.fail" % tag, "%s.fail" % tag, "%s.fail" % tag],
   413     		"%s.alt1" % tag    : ["%s.pass" % tag, "%s.fail" % tag, "%s.fail" % tag],
   414     		"%s.alt2" % tag    : ["%s.fail" % tag, "%s.pass" % tag, "%s.fail" % tag],
   415     		"%s.alt3" % tag    : ["%s.fail" % tag, "%s.fail" % tag, "%s.pass" % tag]
   416     	}
   417     	lookup.SubTable.append(subtable)
   419     path = outputPath % 3 + ".otf"
   420     if os.path.exists(path):
   421     	os.remove(path)
   422     shell.save(path)
   424     # get rid of the shell
   425     if os.path.exists(shellTempPath):
   426         os.remove(shellTempPath)
   428 def makeJavascriptData():
   429     features = sorted(mapping)
   430     outStr = []
   432     outStr.append("")
   433     outStr.append("/* This file is autogenerated by makegsubfonts.py */")
   434     outStr.append("")
   435     outStr.append("/* ")
   436     outStr.append("  Features defined in gsubtest fonts with associated base")
   437     outStr.append("  codepoints for each feature:")
   438     outStr.append("")
   439     outStr.append("    cp = codepoint for feature featX")
   440     outStr.append("")
   441     outStr.append("    cp   default   PASS")
   442     outStr.append("    cp   featX=1   FAIL")
   443     outStr.append("    cp   featX=2   FAIL")
   444     outStr.append("")
   445     outStr.append("    cp+1 default   FAIL")
   446     outStr.append("    cp+1 featX=1   PASS")
   447     outStr.append("    cp+1 featX=2   FAIL")
   448     outStr.append("")
   449     outStr.append("    cp+2 default   FAIL")
   450     outStr.append("    cp+2 featX=1   FAIL")
   451     outStr.append("    cp+2 featX=2   PASS")
   452     outStr.append("")
   453     outStr.append("*/")
   454     outStr.append("")
   455     outStr.append("var gFeatures = {");
   456     cp = baseCodepoint
   458     taglist = []
   459     for tag in features:
   460         taglist.append("\"%s\": 0x%x" % (tag, cp))
   461         cp += 4
   463     outStr.append(textwrap.fill(", ".join(taglist), initial_indent="  ", subsequent_indent="  "))
   464     outStr.append("};");
   465     outStr.append("");
   467     if os.path.exists(javascriptData):
   468     	os.remove(javascriptData)
   470     f = open(javascriptData, "wb")
   471     f.write("\n".join(outStr))
   472     f.close()
   475 # build fonts
   477 print "Making lookup type 1 font..."
   478 makeLookup1()
   480 print "Making lookup type 3 font..."
   481 makeLookup3()
   483 # output javascript data
   485 print "Making javascript data file..."
   486 makeJavascriptData()

mercurial