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.

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

mercurial