1
0
Fork 0

Adding upstream version 1.8.0.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-10 05:17:32 +01:00
parent c48d95b7fa
commit e40b3259c1
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
2403 changed files with 153656 additions and 0 deletions

20
examples/gdl/__init__.py Normal file
View file

@ -0,0 +1,20 @@
# Copyright 2012, SIL International
# All rights reserved.
#
# This library is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation; either version 2.1 of License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should also have received a copy of the GNU Lesser General Public
# License along with this library in the file named "LICENSE".
# If not, write to the Free Software Foundation, 51 Franklin Street,
# suite 500, Boston, MA 02110-1335, USA or visit their web page on the
# internet at https://www.fsf.org/licenses/lgpl.html.
__all__ = ['makegdl', 'psnames']

394
examples/gdl/font.py Normal file
View file

@ -0,0 +1,394 @@
#!/usr/bin/env python
'The main font object for GDL creation. Depends on fonttools'
__url__ = 'https://github.com/silnrsi/pysilfont'
__copyright__ = 'Copyright (c) 2012 SIL International (https://www.sil.org)'
__license__ = 'Released under the MIT License (https://opensource.org/licenses/MIT)'
import os, re, traceback
from silfont.gdl.glyph import Glyph
from silfont.gdl.psnames import Name
from xml.etree.cElementTree import ElementTree, parse, Element
from fontTools.ttLib import TTFont
# A collection of glyphs that have a given attachment point defined
class PointClass(object) :
def __init__(self, name) :
self.name = name
self.glyphs = []
self.dias = []
def addBaseGlyph(self, g) :
self.glyphs.append(g)
def addDiaGlyph(self, g) :
self.dias.append(g)
g.isDia = True
def hasDias(self) :
if len(self.dias) and len(self.glyphs) :
return True
else :
return False
def classGlyphs(self, isDia = False) :
if isDia :
return self.dias
else :
return self.glyphs
def isNotInClass(self, g, isDia = False) :
if not g : return False
if not g.isDia : return False
if isDia :
return g not in self.dias
else :
return g not in self.dias and g not in self.glyphs
class FontClass(object) :
def __init__(self, elements = None, fname = None, lineno = None, generated = False, editable = False) :
self.elements = elements or []
self.fname = fname
self.lineno = lineno
self.generated = generated
self.editable = editable
def append(self, element) :
self.elements.append(element)
class Font(object) :
def __init__(self, fontfile) :
self.glyphs = []
self.psnames = {}
self.canons = {}
self.gdls = {}
self.anchors = {}
self.ligs = {}
self.subclasses = {}
self.points = {}
self.classes = {}
self.aliases = {}
self.rules = {}
self.posRules = {}
if fontfile :
self.font = TTFont(fontfile)
for i, n in enumerate(self.font.getGlyphOrder()) :
self.addGlyph(i, n)
else :
self.font = None
def __len__(self) :
return len(self.glyphs)
# [] syntax returns the indicated element of the glyphs array.
def __getitem__(self, y) :
try :
return self.glyphs[y]
except IndexError :
return None
def glyph(self, name) :
return self.psnames.get(name, None)
def alias(self, s) :
return self.aliases.get(s, s)
def emunits(self) :
return 0
def initGlyphs(self, nGlyphs) :
#print "Font::initGlyphs",nGlyphs
self.glyphs = [None] * nGlyphs
self.numRealGlyphs = nGlyphs # does not include pseudo-glyphs
self.psnames = {}
self.canons = {}
self.gdls = {}
self.classes = {}
def addGlyph(self, index = None, psName = None, gdlName = None, factory = Glyph) :
#print "Font::addGlyph",index,psName,gdlName
if psName in self.psnames :
return self.psnames[psName]
if index is not None and index < len(self.glyphs) and self.glyphs[index] :
g = self.glyphs[index]
return g
g = factory(psName, index) # create a new glyph of the given class
self.renameGlyph(g, psName, gdlName)
if index is None : # give it the next available index
index = len(self.glyphs)
self.glyphs.append(g)
elif index >= len(self.glyphs) :
self.glyphs.extend([None] * (len(self.glyphs) - index + 1))
self.glyphs[index] = g
return g
def renameGlyph(self, g, name, gdlName = None) :
if g.psname != name :
for n in g.parseNames() :
del self.psnames[n.psname]
del self.canons[n.canonical()]
if gdlName :
self.setGDL(g, gdlName)
else :
self.setGDL(g, g.GDLName())
for n in g.parseNames() :
if n is None : break
self.psnames[n.psname] = g
self.canons[n.canonical()] = (n, g)
def setGDL(self, glyph, name) :
if not glyph : return
n = glyph.GDLName()
if n != name and n in self.gdls : del self.gdls[n]
if name and name in self.gdls and self.gdls[name] is not glyph :
count = 1
index = -2
name = name + "_1"
while name in self.gdls :
if self.gdls[name] is glyph : break
count = count + 1
name = name[0:index] + "_" + str(count)
if count == 10 : index = -3
if count == 100 : index = -4
self.gdls[name] = glyph
glyph.setGDL(name)
def addClass(self, name, elements, fname = None, lineno = 0, generated = False, editable = False) :
if name :
self.classes[name] = FontClass(elements, fname, lineno, generated, editable)
def addGlyphClass(self, name, gid, editable = False) :
if name not in self.classes :
self.classes[name] = FontClass()
if gid not in self.classes[name].elements :
self.classes[name].append(gid)
def addRules(self, rules, index) :
self.rules[index] = rules
def addPosRules(self, rules, index) :
self.posRules[index] = rules
def classUpdated(self, name, value) :
c = []
if name in self.classes :
for gid in self.classes[name].elements :
g = self[gid]
if g : g.removeClass(name)
if value is None and name in classes :
del self.classes[name]
return
for n in value.split() :
g = self.gdls.get(n, None)
if g :
c.append(g.gid)
g.addClass(name)
if name in self.classes :
self.classes[name].elements = c
else :
self.classes[name] = FontClass(c)
# Return the list of classes that should be updated in the AP XML file.
# This does not include classes that are auto-generated or defined in the hand-crafted GDL code.
def filterAutoClasses(self, names, autoGdlFile) :
res = []
for n in names :
c = self.classes[n]
if not c.generated and (not c.fname or c.fname == autoGdlFile) : res.append(n)
return res
def loadAlias(self, fname) :
with open(fname) as f :
for l in f.readlines() :
l = l.strip()
l = re.sub(ur'#.*$', '', l).strip()
if not len(l) : continue
try :
k, v = re.split(ur'\s*[,;\s]\s*', l, 1)
except ValueError :
k = l
v = ''
self.aliases[k] = v
# TODO: move this method to GraideFont, or refactor
def loadAP(self, apFileName) :
if not os.path.exists(apFileName) : return False
etree = parse(apFileName)
self.initGlyphs(len(etree.getroot())) # guess each child is a glyph
i = 0
for e in etree.getroot().iterfind("glyph") :
g = self.addGlyph(i, e.get('PSName'))
g.readAP(e, self)
i += 1
return True
def saveAP(self, apFileName, autoGdlFile) :
root = Element('font')
root.set('upem', str(self.emunits()))
root.set('producer', 'graide 1.0')
root.text = "\n\n"
for g in self.glyphs :
if g : g.createAP(root, self, autoGdlFile)
ElementTree(root).write(apFileName, encoding="utf-8", xml_declaration=True)
def createClasses(self) :
self.subclasses = {}
for k, v in self.canons.items() :
if v[0].ext :
h = v[0].head()
o = self.canons.get(h.canonical(), None)
if o :
if v[0].ext not in self.subclasses : self.subclasses[v[0].ext] = {}
self.subclasses[v[0].ext][o[1].GDLName()] = v[1].GDLName()
# for g in self.glyphs :
# if not g : continue
# for c in g.classes :
# if c not in self.classes :
# self.classes[c] = []
# self.classes[c].append(g.gid)
def calculatePointClasses(self) :
self.points = {}
for g in self.glyphs :
if not g : continue
for apName in g.anchors.keys() :
genericName = apName[:-1] # without the M or S
if genericName not in self.points :
self.points[genericName] = PointClass(genericName)
if apName.endswith('S') :
self.points[genericName].addBaseGlyph(g)
else :
self.points[genericName].addDiaGlyph(g)
def calculateOTLookups(self) :
if self.font :
for t in ('GSUB', 'GPOS') :
if t in self.font :
self.font[t].table.LookupList.process(self)
def getPointClasses(self) :
if len(self.points) == 0 :
self.calculatePointClasses()
return self.points
def ligClasses(self) :
self.ligs = {}
for g in self.glyphs :
if not g or not g.name : continue
(h, t) = g.name.split_last()
if t :
o = self.canons.get(h.canonical(), None)
if o and o[0].ext == t.ext :
t.ext = None
t.cname = None
tn = t.canonical(noprefix = True)
if tn in self.ligs :
self.ligs[tn].append((g.GDLName(), o[0].GDL()))
else :
self.ligs[tn] = [(g.GDLName(), o[0].GDL())]
def outGDL(self, fh, args) :
munits = self.emunits()
fh.write('table(glyph) {MUnits = ' + str(munits) + '};\n')
nglyphs = 0
for g in self.glyphs :
if not g or not g.psname : continue
if g.psname == '.notdef' :
fh.write(g.GDLName() + ' = glyphid(0)')
else :
fh.write(g.GDLName() + ' = postscript("' + g.psname + '")')
outs = []
if len(g.anchors) :
for a in g.anchors.keys() :
v = g.anchors[a]
outs.append(a + "=point(" + str(int(v[0])) + "m, " + str(int(v[1])) + "m)")
for (p, v) in g.gdl_properties.items() :
outs.append("%s=%s" % (p, v))
if len(outs) : fh.write(" {" + "; ".join(outs) + "}")
fh.write(";\n")
nglyphs += 1
fh.write("\n")
fh.write("\n/* Point Classes */\n")
for p in sorted(self.points.values(), key=lambda x: x.name) :
if not p.hasDias() : continue
n = p.name + "Dia"
self.outclass(fh, "c" + n, p.classGlyphs(True))
self.outclass(fh, "cTakes" + n, p.classGlyphs(False))
self.outclass(fh, 'cn' + n, filter(lambda x : p.isNotInClass(x, True), self.glyphs))
self.outclass(fh, 'cnTakes' + n, filter(lambda x : p.isNotInClass(x, False), self.glyphs))
fh.write("\n/* Classes */\n")
for c in sorted(self.classes.keys()) : # c = class name, l = class object
if c not in self.subclasses and not self.classes[c].generated : # don't output the class to the AP file if it was autogenerated
self.outclass(fh, c, self.classes[c].elements)
for p in self.subclasses.keys() :
ins = []
outs = []
for k, v in self.subclasses[p].items() :
ins.append(k)
outs.append(v)
n = p.replace('.', '_')
self.outclass(fh, 'cno_' + n, ins)
self.outclass(fh, 'c' + n, outs)
fh.write("/* Ligature Classes */\n")
for k in sorted(self.ligs.keys()) :
self.outclass(fh, "clig" + k, map(lambda x: self.gdls[x[0]], self.ligs[k]))
self.outclass(fh, "cligno_" + k, map(lambda x: self.gdls[x[1]], self.ligs[k]))
fh.write("\nendtable;\n")
fh.write("/* Substitution Rules */\n")
for k, v in sorted(self.rules.items(), key=lambda x:map(int,x[0].split('_'))) :
fh.write('\n// lookup ' + k + '\n')
fh.write('// ' + "\n// ".join(v) + "\n")
fh.write("\n/* Positioning Rules */\n")
for k, v in sorted(self.posRules.items(), key=lambda x:map(int,x[0].split('_'))) :
fh.write('\n// lookup ' + k + '\n')
fh.write('// ' + "\n// ".join(v) + "\n")
fh.write("\n\n#define MAXGLYPH %d\n\n" % (nglyphs - 1))
if args.include :
fh.write("#include \"%s\"\n" % args.include)
def outPosRules(self, fh, num) :
fh.write("""
#ifndef opt2
#define opt(x) [x]?
#define opt2(x) [opt(x) x]?
#define opt3(x) [opt2(x) x]?
#define opt4(x) [opt3(x) x]?
#endif
#define posrule(x) c##x##Dia {attach{to=@1; at=x##S; with=x##M}} / cTakes##x##Dia opt4(cnTakes##x##Dia) _;
table(positioning);
pass(%d);
""" % num)
for p in self.points.values() :
if p.hasDias() :
fh.write("posrule(%s);\n" % p.name)
fh.write("endpass;\nendtable;\n")
def outclass(self, fh, name, glyphs) :
fh.write(name + " = (")
count = 1
sep = ""
for g in glyphs :
if not g : continue
if isinstance(g, basestring) :
fh.write(sep + g)
else :
if g.GDLName() is None :
print "Can't output " + str(g.gid) + " to class " + name
else :
fh.write(sep + g.GDLName())
if count % 8 == 0 :
sep = ',\n '
else :
sep = ', '
count += 1
fh.write(');\n\n')

174
examples/gdl/glyph.py Normal file
View file

@ -0,0 +1,174 @@
#!/usr/bin/env python
'Corresponds to a glyph, for analysis purposes, for GDL generation'
__url__ = 'https://github.com/silnrsi/pysilfont'
__copyright__ = 'Copyright (c) 2012 SIL International (https://www.sil.org)'
__license__ = 'Released under the MIT License (https://opensource.org/licenses/MIT)'
import re, traceback
from silfont.gdl.psnames import Name
from xml.etree.cElementTree import SubElement
# Convert from Graphite AP name to the standard name, eg upperM -> _upper
def gr_ap(txt) :
if txt.endswith('M') :
return "_" + txt[:-1]
elif txt.endswith('S') :
return txt[:-1]
else :
return txt
# Convert from standard AP name to the Graphite name, eg _upper -> upperM
def ap_gr(txt) :
if txt.startswith('_') :
return txt[1:] + 'M'
else :
return txt + 'S'
class Glyph(object) :
isDia = False
def __init__(self, name, gid = 0) :
self.clear()
self.setName(name)
self.gdl = None
self.gid = gid
self.uid = "" # this is a string!
self.comment = ""
self.isDia = False
def clear(self) :
self.anchors = {}
self.classes = set()
self.gdl_properties = {}
self.properties = {}
def setName(self, name) :
self.psname = name
self.name = next(self.parseNames())
def setAnchor(self, name, x, y, t = None) :
send = True
if name in self.anchors :
if x is None and y is None :
del self.anchors[name]
return True
if x is None : x = self.anchors[name][0]
if y is None : y = self.anchors[name][1]
send = self.anchors[name] != (x, y)
self.anchors[name] = (x, y)
return send
# if not name.startswith("_") and t != 'basemark' :
# self.isBase = True
def parseNames(self) :
if self.psname :
for name in self.psname.split("/") :
res = Name(name)
yield res
else :
yield None
def GDLName(self) :
if self.gdl :
return self.gdl
elif self.name :
return self.name.GDL()
else :
return None
def setGDL(self, name) :
self.gdl = name
def readAP(self, elem, font) :
self.uid = elem.get('UID', None)
for p in elem.iterfind('property') :
n = p.get('name')
if n == 'GDLName' :
self.setGDL(p.get('value'))
elif n.startswith('GDL_') :
self.gdl_properties[n[4:]] = p.get('value')
else :
self.properties[n] = p.get('value')
for p in elem.iterfind('point') :
l = p.find('location')
self.setAnchor(ap_gr(p.get('type')), int(l.get('x', 0)), int(l.get('y', 0)))
p = elem.find('note')
if p is not None and p.text :
self.comment = p.text
if 'classes' in self.properties :
for c in self.properties['classes'].split() :
if c not in self.classes :
self.classes.add(c)
font.addGlyphClass(c, self, editable = True)
def createAP(self, elem, font, autoGdlFile) :
e = SubElement(elem, 'glyph')
if self.psname : e.set('PSName', self.psname)
if self.uid : e.set('UID', self.uid)
if self.gid is not None : e.set('GID', str(self.gid))
ce = None
if 'classes' in self.properties and self.properties['classes'].strip() :
tempClasses = self.properties['classes']
self.properties['classes'] = " ".join(font.filterAutoClasses(self.properties['classes'].split(), autoGdlFile))
for k in sorted(self.anchors.keys()) :
v = self.anchors[k]
p = SubElement(e, 'point')
p.set('type', gr_ap(k))
p.text = "\n "
l = SubElement(p, 'location')
l.set('x', str(v[0]))
l.set('y', str(v[1]))
l.tail = "\n "
if ce is not None : ce.tail = "\n "
ce = p
for k in sorted(self.gdl_properties.keys()) :
if k == "*skipPasses*" : continue # not set in GDL
v = self.gdl_properties[k]
if v :
p = SubElement(e, 'property')
p.set('name', 'GDL_' + k)
p.set('value', v)
if ce is not None : ce.tail = "\n "
ce = p
if self.gdl and (not self.name or self.gdl != self.name.GDL()) :
p = SubElement(e, 'property')
p.set('name', 'GDLName')
p.set('value', self.GDLName())
if ce is not None : ce.tail = "\n "
ce = p
for k in sorted(self.properties.keys()) :
v = self.properties[k]
if v :
p = SubElement(e, 'property')
p.set('name', k)
p.set('value', v)
if ce is not None : ce.tail = "\n "
ce = p
if self.comment :
p = SubElement(e, 'note')
p.text = self.comment
if ce is not None : ce.tail = "\n "
ce = p
if 'classes' in self.properties and self.properties['classes'].strip() :
self.properties['classes'] = tempClasses
if ce is not None :
ce.tail = "\n"
e.text = "\n "
e.tail = "\n"
return e
def isMakeGDLSpecialClass(name) :
# if re.match(r'^cn?(Takes)?.*?Dia$', name) : return True
# if name.startswith('clig') : return True
# if name.startswith('cno_') : return True
if re.match(r'^\*GC\d+\*$', name) : return True # auto-pseudo glyph with name = *GCXXXX*
return False

31
examples/gdl/makeGdl.py Executable file
View file

@ -0,0 +1,31 @@
#!/usr/bin/env python
'Analyse a font and generate GDL to help with the creation of graphite fonts'
__url__ = 'https://github.com/silnrsi/pysilfont'
__copyright__ = 'Copyright (c) 2015 SIL International (https://www.sil.org)'
__license__ = 'Released under the MIT License (https://opensource.org/licenses/MIT)'
from gdl.font import Font
import gdl.ot
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument('infont')
parser.add_argument('outgdl')
parser.add_argument('-a','--ap')
parser.add_argument('-i','--include')
parser.add_argument('-y','--alias')
args = parser.parse_args()
f = Font(args.infont)
if args.alias : f.loadAlias(args.alias)
if args.ap : f.loadAP(args.ap)
f.createClasses()
f.calculateOTLookups()
f.calculatePointClasses()
f.ligClasses()
outf = open(args.outgdl, "w")
f.outGDL(outf, args)
outf.close()

448
examples/gdl/ot.py Normal file
View file

@ -0,0 +1,448 @@
#!/usr/bin/env python
'OpenType analysis for GDL conversion'
__url__ = 'https://github.com/silnrsi/pysilfont'
__copyright__ = 'Copyright (c) 2012 SIL International (https://www.sil.org)'
__license__ = 'Released under the MIT License (https://opensource.org/licenses/MIT)'
import re, traceback, logging
from fontTools.ttLib.tables import otTables
def compress_strings(strings) :
'''If we replace one column in the string with different lists, can we reduce the number
of strings? Each string is a tuple of the string and a single value that will be put into
a class as well when list compression occurs'''
maxlen = max(map(lambda x: len(x[0]), strings))
scores = []
for r in range(maxlen) :
allchars = {}
count = 0
for s in strings :
if r >= len(s[0]) : continue
c = tuple(s[0][0:r] + (s[0][r+1:] if r < len(s[0]) - 1 else []))
if c in allchars :
allchars[c] += 1
else :
allchars[c] = 0
count += 1
scores.append((max(allchars.values()), len(allchars), count))
best = maxlen
bestr = 0
for r in range(maxlen) :
score = maxlen - (scores[r][2] - scores[r][1])
if score < best :
best = score
bestr = r
numstrings = len(strings)
i = 0
allchars = {}
while i < len(strings) :
s = strings[i][0]
if bestr >= len(s) :
i += 1
continue
c = tuple(s[0:bestr] + (s[bestr+1:] if bestr < len(s) - 1 else []))
if c in allchars :
allchars[c][1].append(s[bestr])
allchars[c][2].append(strings[i][1])
strings.pop(i)
else :
allchars[c] = [i, [s[bestr]], [strings[i][1]]]
i += 1
for v in allchars.values() :
if len(set(v[1])) != 1 : # if all values in the list identical, don't output list
strings[v[0]][0][bestr] = v[1]
if len(v[2]) > 1 : # don't need a list if length 1
strings[v[0]][1] = v[2]
return strings
def make_rule(left, right = None, before = None, after = None) :
res = " ".join(map(lambda x: x or "_", left))
if right :
res += " > " + " ".join(map(lambda x: x or "_", right))
if before or after :
res += " / "
if before : res += " ".join(map(lambda x: x or 'ANY', before))
res += " " + "_ " * len(left) + " "
if after : res += " ".join(map(lambda x: x or 'ANY', after))
res += ";"
return res
def add_class_classes(font, name, ctable) :
vals = {}
for k, v in ctable.classDefs.items() :
if v not in vals : vals[v] = []
vals[v].append(k)
numk = max(vals.keys())
res = [None] * (numk + 1)
for k, v in vals.items() :
if len(v) > 1 :
res[k] = font.alias(name+"{}".format(k))
font.addClass(res[k], map(font.glyph, v))
else :
res[k] = font.glyph(v[0]).GDLName()
return res
vrgdlmap = {
'XPlacement' : 'shift.x',
'YPlacement' : 'shift.y',
'XAdvance' : 'advance'
}
def valuerectogdl(vr) :
res = "{"
for k, v in vrgdlmap.items() :
if hasattr(vr, k) :
res += "{}={}; ".format(v, getattr(vr, k))
res = res[:-1] + "}"
if len(res) == 1 : return ""
return res
def _add_method(*clazzes):
"""Returns a decorator function that adds a new method to one or
more classes."""
def wrapper(method):
for c in clazzes:
assert c.__name__ != 'DefaultTable', \
'Oops, table class not found.'
assert not hasattr(c, method.__name__), \
"Oops, class '%s' has method '%s'." % (c.__name__,
method.__name__)
setattr(c, method.__name__, method)
return None
return wrapper
@_add_method(otTables.Lookup)
def process(self, font, index) :
for i, s in enumerate(self.SubTable) :
if hasattr(s, 'process') :
s.process(font, index + "_{}".format(i))
else :
logging.warning("No processing of {} {}_{}".format(str(s), index, i))
@_add_method(otTables.LookupList)
def process(self, font) :
for i, s in enumerate(self.Lookup) :
s.process(font, str(i))
@_add_method(otTables.ExtensionSubst, otTables.ExtensionPos)
def process(self, font, index) :
x = self.ExtSubTable
if hasattr(x, 'process') :
x.process(font, index)
else :
logging.warning("No processing of {} {}".format(str(x), index))
@_add_method(otTables.SingleSubst)
def process(self, font, index) :
cname = "cot_s{}".format(index)
if not len(font.alias(cname)) : return
lists = zip(*self.mapping.items())
font.addClass(font.alias(cname+"l"), map(font.glyph, lists[0]))
font.addClass(font.alias(cname+"r"), map(font.glyph, lists[1]))
@_add_method(otTables.MultipleSubst)
def process(self, font, index) :
cname = "cot_m{}".format(index)
if not len(font.alias(cname)) : return
nums = len(self.Coverage.glyphs)
strings = []
for i in range(nums) :
strings.append([self.Sequence[i].Substitute, self.Coverage.glyphs[i]])
res = compress_strings(strings)
count = 0
rules = []
for r in res :
if hasattr(r[1], '__iter__') :
lname = font.alias(cname+"l{}".format(count))
font.addClass(lname, map(font.glyph, r[1]))
rule = lname
else :
rule = font.glyph(r[1]).GDLName()
rule += " _" * (len(r[0]) - 1) + " >"
for c in r[0] :
if hasattr(c, '__iter__') :
rname = font.alias(cname+"r{}".format(count))
font.addClass(rname, map(font.glyph, c))
rule += " " + rname
count += 1
else :
rule += " " + font.glyph(c).GDLName()
rule += ';'
rules.append(rule)
font.addRules(rules, index)
@_add_method(otTables.LigatureSubst)
def process(self, font, index) :
cname = "cot_l{}".format(index)
if not len(font.alias(cname)) : return
strings = []
for lg, ls in self.ligatures.items() :
for l in ls :
strings.append([[lg] + l.Component, l.LigGlyph])
res = compress_strings(strings)
count = 0
rules = []
for r in res :
rule = ""
besti = 0
for i, c in enumerate(r[0]) :
if hasattr(c, '__iter__') :
lname = font.alias(cname+"l{}".format(count))
font.addClass(lname, map(font.glyph, c))
rule += lname + " "
besti = i
else :
rule += font.glyph(c).GDLName() + " "
rule += "> " + "_ " * besti
if hasattr(r[1], '__iter__') :
rname = font.alias(cname+"r{}".format(count))
font.addClass(rname, map(font.glyph, r[1]))
rule += rname
count += 1
else :
rule += font.glyph(r[1]).GDLName()
rule += " _" * (len(r[0]) - 1 - besti) + ";"
rules.append(rule)
font.addRules(rules, index)
@_add_method(otTables.ChainContextSubst)
def process(self, font, index) :
def procsubst(rule, action) :
for s in rule.SubstLookupRecord :
action[s.SequenceIndex] += "/*{}*/".format(s.LookupListIndex)
def procCover(cs, name) :
res = []
for i, c in enumerate(cs) :
if len(c.glyphs) > 1 :
n = font.alias(name+"{}".format(i))
font.addClass(n, map(font.glyph, c.glyphs))
res.append(n)
else :
res.append(font.glyph(c.glyphs[0]).GDLName())
return res
cname = "cot_c{}".format(index)
if not len(font.alias(cname)) : return
rules = []
if self.Format == 1 :
for i in range(len(self.ChainSubRuleSet)) :
for r in self.ChainSubRuleSet[i].ChainSubRule :
action = [self.Coverage.glyphs[i]] + r.Input
procsubst(r, action)
rules.append(make_rule(action, None, r.Backtrack, r.LookAhead))
elif self.Format == 2 :
ilist = add_class_classes(font, cname+"i", self.InputClassDef)
if self.BacktrackClassDef :
blist = add_class_classes(font, cname+"b", self.BacktrackClassDef)
if self.LookAheadClassDef :
alist = add_class_classes(font, cname+"a", self.LookAheadClassDef)
for i, s in enumerate(self.ChainSubClassSet) :
if s is None : continue
for r in s.ChainSubClassRule :
action = map(lambda x:ilist[x], [i]+r.Input)
procsubst(r, action)
rules.append(make_rule(action, None,
map(lambda x:blist[x], r.Backtrack or []),
map(lambda x:alist[x], r.LookAhead or [])))
elif self.Format == 3 :
backs = procCover(self.BacktrackCoverage, cname+"b")
aheads = procCover(self.LookAheadCoverage, cname+"a")
actions = procCover(self.InputCoverage, cname+"i")
procsubst(self, actions)
rules.append(make_rule(actions, None, backs, aheads))
font.addRules(rules, index)
@_add_method(otTables.SinglePos)
def process(self, font, index) :
cname = "cot_p{}".format(index)
if self.Format == 1 :
font.addClass(font.alias(cname), map(font.glyph, self.Coverage.glyphs))
rule = cname + " " + valuerectogdl(self.Value)
font.addPosRules([rule], index)
elif self.Format == 2 :
rules = []
for i, g in enumerage(map(font.glyph, self.Coverage.glyphs)) :
rule = font.glyph(g).GDLName()
rule += " " + valuerectogdl(self.Value[i])
rules.append(rule)
font.addPosRules(rules, index)
@_add_method(otTables.PairPos)
def process(self, font, index) :
pass
@_add_method(otTables.CursivePos)
def process(self, font, index) :
apname = "P{}".format(index)
if not len(font.alias(apname)) : return
if self.Format == 1 :
mark_names = self.Coverage.glyphs
for i, g in enumerate(map(font.glyph, mark_names)) :
rec = self.EntryExitRecord[i]
if rec.EntryAnchor is not None :
g.setAnchor(font.alias(apname+"_{}M".format(rec.EntryAnchor)),
rec.EntryAnchor.XCoordinate, rec.EntryAnchor.YCoordinate)
if rec.ExitAnchor is not None :
g.setAnchor(font.alias(apname+"_{}S".format(rec.ExitAnchor)),
rec.ExitAnchor.XCoordinate, rec.ExitAnchor.YCoordinate)
@_add_method(otTables.MarkBasePos)
def process(self, font, index) :
apname = "P{}".format(index)
if not len(font.alias(apname)) : return
if self.Format == 1 :
mark_names = self.MarkCoverage.glyphs
for i, g in enumerate(map(font.glyph, mark_names)) :
rec = self.MarkArray.MarkRecord[i]
g.setAnchor(font.alias(apname+"_{}M".format(rec.Class)),
rec.MarkAnchor.XCoordinate, rec.MarkAnchor.YCoordinate)
base_names = self.BaseCoverage.glyphs
for i, g in enumerate(map(font.glyph, base_names)) :
for j,b in enumerate(self.BaseArray.BaseRecord[i].BaseAnchor) :
if b : g.setAnchor(font.alias(apname+"_{}S".format(j)),
b.XCoordinate, b.YCoordinate)
@_add_method(otTables.MarkMarkPos)
def process(self, font, index) :
apname = "P{}".format(index)
if not len(font.alias(apname)) : return
if self.Format == 1 :
mark_names = self.Mark1Coverage.glyphs
for i, g in enumerate(map(font.glyph, mark_names)) :
rec = self.Mark1Array.MarkRecord[i]
g.setAnchor(font.alias(apname+"_{}M".format(rec.Class)),
rec.MarkAnchor.XCoordinate, rec.MarkAnchor.YCoordinate)
base_names = self.Mark2Coverage.glyphs
for i, g in enumerate(map(font.glyph, base_names)) :
for j,b in enumerate(self.Mark2Array.Mark2Record[i].Mark2Anchor) :
if b : g.setAnchor(font.alias(apname+"_{}S".format(j)),
b.XCoordinate, b.YCoordinate)
@_add_method(otTables.ContextSubst)
def process(self, font, index) :
def procsubst(rule, action) :
for s in rule.SubstLookupRecord :
action[s.SequenceIndex] += "/*{}*/".format(s.LookupListIndex)
def procCover(cs, name) :
res = []
for i, c in enumerate(cs) :
if len(c.glyphs) > 1 :
n = font.alias(name+"{}".format(i))
font.addClass(n, map(font.glyph, c.glyphs))
res.append(n)
else :
res.append(font.glyph(c.glyphs[0]).GDLName())
return res
cname = "cot_cs{}".format(index)
if not len(font.alias(cname)) : return
rules = []
if self.Format == 1 :
for i in range(len(self.SubRuleSet)) :
for r in self.SubRuleSet[i].SubRule :
action = [self.Coverage.glyphs[i]] + r.Input
procsubst(r, action)
rules.append(make_rule(action, None, None, None))
elif self.Format == 2 :
ilist = add_class_classes(font, cname+"i", self.ClassDef)
for i, s in enumerate(self.SubClassSet) :
if s is None : continue
for r in s.SubClassRule :
action = map(lambda x:ilist[x], [i]+r.Class)
procsubst(r, action)
rules.append(make_rule(action, None, None, None))
elif self.Format == 3 :
actions = procCover(self.Coverage, cname+"i")
procsubst(self, actions)
rules.append(make_rule(actions, None, None, None))
font.addRules(rules, index)
@_add_method(otTables.ContextPos)
def process(self, font, index) :
def procsubst(rule, action) :
for s in rule.PosLookupRecord :
action[s.SequenceIndex] += "/*{}*/".format(s.LookupListIndex)
def procCover(cs, name) :
res = []
for i, c in enumerate(cs) :
if len(c.glyphs) > 1 :
n = font.alias(name+"{}".format(i))
font.addClass(n, map(font.glyph, c.glyphs))
res.append(n)
else :
res.append(font.glyph(c.glyphs[0]).GDLName())
return res
cname = "cot_cp{}".format(index)
if not len(font.alias(cname)) : return
rules = []
if self.Format == 1 :
for i in range(len(self.PosRuleSet)) :
for r in self.PosRuleSet[i] :
action = [self.Coverage.glyphs[i]] + r.Input
procsubst(r, action)
rules.append(make_rule(action, None, None, None))
elif self.Format == 2 :
ilist = add_class_classes(font, cname+"i", self.ClassDef)
for i, s in enumerate(self.PosClassSet) :
if s is None : continue
for r in s.PosClassRule :
action = map(lambda x:ilist[x], [i]+r.Class)
procsubst(r, action)
rules.append(make_rule(action, None, None, None))
elif self.Format == 3 :
actions = procCover(self.Coverage, cname+"i")
procsubst(self, actions)
rules.append(make_rule(actions, None, None, None))
font.addPosRules(rules, index)
@_add_method(otTables.ChainContextPos)
def process(self, font, index) :
def procsubst(rule, action) :
for s in rule.PosLookupRecord :
action[s.SequenceIndex] += "/*{}*/".format(s.LookupListIndex)
def procCover(cs, name) :
res = []
for i, c in enumerate(cs) :
if len(c.glyphs) > 1 :
n = font.alias(name+"{}".format(i))
font.addClass(n, map(font.glyph, c.glyphs))
res.append(n)
else :
res.append(font.glyph(c.glyphs[0]).GDLName())
return res
cname = "cot_c{}".format(index)
if not len(font.alias(cname)) : return
rules = []
if self.Format == 1 :
for i in range(len(self.ChainPosRuleSet)) :
for r in self.ChainPosRuleSet[i].ChainPosRule :
action = [self.Coverage.glyphs[i]] + r.Input
procsubst(r, action)
rules.append(make_rule(action, None, r.Backtrack, r.LookAhead))
elif self.Format == 2 :
ilist = add_class_classes(font, cname+"i", self.InputClassDef)
if self.BacktrackClassDef :
blist = add_class_classes(font, cname+"b", self.BacktrackClassDef)
if self.LookAheadClassDef :
alist = add_class_classes(font, cname+"a", self.LookAheadClassDef)
for i, s in enumerate(self.ChainPosClassSet) :
if s is None : continue
for r in s.ChainPosClassRule :
action = map(lambda x:ilist[x], [i]+r.Input)
procsubst(r, action)
rules.append(make_rule(action, None,
map(lambda x:blist[x], r.Backtrack or []),
map(lambda x:alist[x], r.LookAhead or [])))
elif self.Format == 3 :
backs = procCover(self.BacktrackCoverage, cname+"b")
aheads = procCover(self.LookAheadCoverage, cname+"a")
actions = procCover(self.InputCoverage, cname+"i")
procsubst(self, actions)
rules.append(make_rule(actions, None, backs, aheads))
font.addPosRules(rules, index)

4506
examples/gdl/psnames.py Normal file

File diff suppressed because it is too large Load diff