334 lines
11 KiB
Python
334 lines
11 KiB
Python
from __future__ import print_function, division, absolute_import
|
|
from __future__ import unicode_literals
|
|
import itertools
|
|
|
|
|
|
def deviceToString(device):
|
|
if device is None:
|
|
return "<device NULL>"
|
|
else:
|
|
return "<device %s>" % ", ".join(["%d %d" % t for t in device])
|
|
|
|
|
|
class Statement(object):
|
|
def __init__(self, location):
|
|
self.location = location
|
|
|
|
def build(self, builder):
|
|
pass
|
|
|
|
|
|
class Expression(object):
|
|
def __init__(self, location):
|
|
self.location = location
|
|
|
|
def build(self, builder):
|
|
pass
|
|
|
|
|
|
class Block(Statement):
|
|
def __init__(self, location):
|
|
Statement.__init__(self, location)
|
|
self.statements = []
|
|
|
|
def build(self, builder):
|
|
for s in self.statements:
|
|
s.build(builder)
|
|
|
|
|
|
class FeatureFile(Block):
|
|
def __init__(self):
|
|
Block.__init__(self, location=None)
|
|
self.markClasses = {} # name --> ast.MarkClassDefinition
|
|
|
|
|
|
class FeatureBlock(Block):
|
|
def __init__(self, location, name, use_extension):
|
|
Block.__init__(self, location)
|
|
self.name, self.use_extension = name, use_extension
|
|
|
|
def build(self, builder):
|
|
# TODO(sascha): Handle use_extension.
|
|
builder.start_feature(self.location, self.name)
|
|
Block.build(self, builder)
|
|
builder.end_feature()
|
|
|
|
|
|
class LookupBlock(Block):
|
|
def __init__(self, location, name, use_extension):
|
|
Block.__init__(self, location)
|
|
self.name, self.use_extension = name, use_extension
|
|
|
|
def build(self, builder):
|
|
# TODO(sascha): Handle use_extension.
|
|
builder.start_lookup_block(self.location, self.name)
|
|
Block.build(self, builder)
|
|
builder.end_lookup_block()
|
|
|
|
|
|
class GlyphClassDefinition(Statement):
|
|
def __init__(self, location, name, glyphs):
|
|
Statement.__init__(self, location)
|
|
self.name = name
|
|
self.glyphs = glyphs
|
|
|
|
|
|
class MarkClassDefinition(object):
|
|
def __init__(self, location, name):
|
|
self.location, self.name = location, name
|
|
self.anchors = {} # glyph --> ast.Anchor
|
|
self.glyphLocations = {} # glyph --> (filepath, line, column)
|
|
|
|
|
|
class AlternateSubstStatement(Statement):
|
|
def __init__(self, location, glyph, from_class):
|
|
Statement.__init__(self, location)
|
|
self.glyph, self.from_class = (glyph, from_class)
|
|
|
|
def build(self, builder):
|
|
builder.add_alternate_subst(self.location, self.glyph, self.from_class)
|
|
|
|
|
|
class Anchor(Expression):
|
|
def __init__(self, location, x, y, contourpoint,
|
|
xDeviceTable, yDeviceTable):
|
|
Expression.__init__(self, location)
|
|
self.x, self.y, self.contourpoint = x, y, contourpoint
|
|
self.xDeviceTable, self.yDeviceTable = xDeviceTable, yDeviceTable
|
|
|
|
|
|
class AnchorDefinition(Statement):
|
|
def __init__(self, location, name, x, y, contourpoint):
|
|
Statement.__init__(self, location)
|
|
self.name, self.x, self.y, self.contourpoint = name, x, y, contourpoint
|
|
|
|
|
|
class ChainContextSubstStatement(Statement):
|
|
def __init__(self, location, old_prefix, old, old_suffix, lookups):
|
|
Statement.__init__(self, location)
|
|
self.old, self.lookups = old, lookups
|
|
self.old_prefix, self.old_suffix = old_prefix, old_suffix
|
|
|
|
def build(self, builder):
|
|
builder.add_chain_context_subst(
|
|
self.location, self.old_prefix, self.old, self.old_suffix,
|
|
self.lookups)
|
|
|
|
|
|
class CursivePosStatement(Statement):
|
|
def __init__(self, location, glyphclass, entryAnchor, exitAnchor):
|
|
Statement.__init__(self, location)
|
|
self.glyphclass = glyphclass
|
|
self.entryAnchor, self.exitAnchor = entryAnchor, exitAnchor
|
|
|
|
def build(self, builder):
|
|
builder.add_cursive_pos(
|
|
self.location, self.glyphclass, self.entryAnchor, self.exitAnchor)
|
|
|
|
|
|
class LanguageStatement(Statement):
|
|
def __init__(self, location, language, include_default, required):
|
|
Statement.__init__(self, location)
|
|
assert(len(language) == 4)
|
|
self.language = language
|
|
self.include_default = include_default
|
|
self.required = required
|
|
|
|
def build(self, builder):
|
|
builder.set_language(location=self.location, language=self.language,
|
|
include_default=self.include_default,
|
|
required=self.required)
|
|
|
|
|
|
class LanguageSystemStatement(Statement):
|
|
def __init__(self, location, script, language):
|
|
Statement.__init__(self, location)
|
|
self.script, self.language = (script, language)
|
|
|
|
def build(self, builder):
|
|
builder.add_language_system(self.location, self.script, self.language)
|
|
|
|
|
|
class IgnoreSubstitutionRule(Statement):
|
|
def __init__(self, location, prefix, glyphs, suffix):
|
|
Statement.__init__(self, location)
|
|
self.prefix, self.glyphs, self.suffix = (prefix, glyphs, suffix)
|
|
|
|
|
|
class LigatureSubstStatement(Statement):
|
|
def __init__(self, location, glyphs, replacement):
|
|
Statement.__init__(self, location)
|
|
self.glyphs, self.replacement = (glyphs, replacement)
|
|
|
|
def build(self, builder):
|
|
# OpenType feature file syntax, section 5.d, "Ligature substitution":
|
|
# "Since the OpenType specification does not allow ligature
|
|
# substitutions to be specified on target sequences that contain
|
|
# glyph classes, the implementation software will enumerate
|
|
# all specific glyph sequences if glyph classes are detected"
|
|
for glyphs in sorted(itertools.product(*self.glyphs)):
|
|
builder.add_ligature_subst(self.location, glyphs, self.replacement)
|
|
|
|
|
|
class LookupReferenceStatement(Statement):
|
|
def __init__(self, location, lookup):
|
|
Statement.__init__(self, location)
|
|
self.location, self.lookup = (location, lookup)
|
|
|
|
def build(self, builder):
|
|
for s in self.lookup.statements:
|
|
s.build(builder)
|
|
|
|
|
|
class MarkBasePosStatement(Statement):
|
|
def __init__(self, location, base, marks):
|
|
Statement.__init__(self, location)
|
|
self.base, self.marks = base, marks
|
|
|
|
def build(self, builder):
|
|
builder.add_mark_base_pos(self.location, self.base, self.marks)
|
|
|
|
|
|
class MarkLigPosStatement(Statement):
|
|
def __init__(self, location, ligatures, marks):
|
|
Statement.__init__(self, location)
|
|
self.ligatures, self.marks = ligatures, marks
|
|
|
|
def build(self, builder):
|
|
builder.add_mark_lig_pos(self.location, self.ligatures, self.marks)
|
|
|
|
|
|
class MarkMarkPosStatement(Statement):
|
|
def __init__(self, location, baseMarks, marks):
|
|
Statement.__init__(self, location)
|
|
self.baseMarks, self.marks = baseMarks, marks
|
|
|
|
def build(self, builder):
|
|
builder.add_mark_mark_pos(self.location, self.baseMarks, self.marks)
|
|
pass
|
|
|
|
|
|
class MultipleSubstStatement(Statement):
|
|
def __init__(self, location, glyph, replacement):
|
|
Statement.__init__(self, location)
|
|
self.glyph, self.replacement = glyph, replacement
|
|
|
|
def build(self, builder):
|
|
builder.add_multiple_subst(self.location, self.glyph, self.replacement)
|
|
|
|
|
|
class PairPosStatement(Statement):
|
|
def __init__(self, location, enumerated,
|
|
glyphclass1, valuerecord1, glyphclass2, valuerecord2):
|
|
Statement.__init__(self, location)
|
|
self.enumerated = enumerated
|
|
self.glyphclass1, self.valuerecord1 = glyphclass1, valuerecord1
|
|
self.glyphclass2, self.valuerecord2 = glyphclass2, valuerecord2
|
|
|
|
def build(self, builder):
|
|
builder.add_pair_pos(self.location, self.enumerated,
|
|
self.glyphclass1, self.valuerecord1,
|
|
self.glyphclass2, self.valuerecord2)
|
|
|
|
|
|
class ReverseChainSingleSubstStatement(Statement):
|
|
def __init__(self, location, old_prefix, old_suffix, mapping):
|
|
Statement.__init__(self, location)
|
|
self.old_prefix, self.old_suffix = old_prefix, old_suffix
|
|
self.mapping = mapping
|
|
|
|
def build(self, builder):
|
|
builder.add_reverse_chain_single_subst(
|
|
self.location, self.old_prefix, self.old_suffix, self.mapping)
|
|
|
|
|
|
class SingleSubstStatement(Statement):
|
|
def __init__(self, location, mapping):
|
|
Statement.__init__(self, location)
|
|
self.mapping = mapping
|
|
|
|
def build(self, builder):
|
|
builder.add_single_subst(self.location, self.mapping)
|
|
|
|
|
|
class ScriptStatement(Statement):
|
|
def __init__(self, location, script):
|
|
Statement.__init__(self, location)
|
|
self.script = script
|
|
|
|
def build(self, builder):
|
|
builder.set_script(self.location, self.script)
|
|
|
|
|
|
class SinglePosStatement(Statement):
|
|
def __init__(self, location, glyphclass, valuerecord):
|
|
Statement.__init__(self, location)
|
|
self.glyphclass, self.valuerecord = glyphclass, valuerecord
|
|
|
|
def build(self, builder):
|
|
for glyph in self.glyphclass:
|
|
builder.add_single_pos(self.location, glyph, self.valuerecord)
|
|
|
|
|
|
class SubtableStatement(Statement):
|
|
def __init__(self, location):
|
|
Statement.__init__(self, location)
|
|
|
|
|
|
class ValueRecord(Statement):
|
|
def __init__(self, location, xPlacement, yPlacement, xAdvance, yAdvance,
|
|
xPlaDevice, yPlaDevice, xAdvDevice, yAdvDevice):
|
|
Statement.__init__(self, location)
|
|
self.xPlacement, self.yPlacement = (xPlacement, yPlacement)
|
|
self.xAdvance, self.yAdvance = (xAdvance, yAdvance)
|
|
self.xPlaDevice, self.yPlaDevice = (xPlaDevice, yPlaDevice)
|
|
self.xAdvDevice, self.yAdvDevice = (xAdvDevice, yAdvDevice)
|
|
|
|
def __eq__(self, other):
|
|
return (self.xPlacement == other.xPlacement and
|
|
self.yPlacement == other.yPlacement and
|
|
self.xAdvance == other.xAdvance and
|
|
self.yAdvance == other.yAdvance and
|
|
self.xPlaDevice == other.xPlaDevice and
|
|
self.xAdvDevice == other.xAdvDevice)
|
|
|
|
def __ne__(self, other):
|
|
return not self.__eq__(other)
|
|
|
|
def __hash__(self):
|
|
return (hash(self.xPlacement) ^ hash(self.yPlacement) ^
|
|
hash(self.xAdvance) ^ hash(self.yAdvance) ^
|
|
hash(self.xPlaDevice) ^ hash(self.yPlaDevice) ^
|
|
hash(self.xAdvDevice) ^ hash(self.yAdvDevice))
|
|
|
|
def makeString(self, vertical):
|
|
x, y = self.xPlacement, self.yPlacement
|
|
xAdvance, yAdvance = self.xAdvance, self.yAdvance
|
|
xPlaDevice, yPlaDevice = self.xPlaDevice, self.yPlaDevice
|
|
xAdvDevice, yAdvDevice = self.xAdvDevice, self.yAdvDevice
|
|
|
|
# Try format A, if possible.
|
|
if x == 0 and y == 0:
|
|
if xAdvance == 0 and vertical:
|
|
return str(yAdvance)
|
|
elif yAdvance == 0 and not vertical:
|
|
return str(xAdvance)
|
|
|
|
# Try format B, if possible.
|
|
if (xPlaDevice is None and yPlaDevice is None and
|
|
xAdvDevice is None and yAdvDevice is None):
|
|
return "<%s %s %s %s>" % (x, y, xAdvance, yAdvance)
|
|
|
|
# Last resort is format C.
|
|
return "<%s %s %s %s %s %s %s %s %s %s>" % (
|
|
x, y, xAdvance, yAdvance,
|
|
deviceToString(xPlaDevice), deviceToString(yPlaDevice),
|
|
deviceToString(xAdvDevice), deviceToString(yAdvDevice))
|
|
|
|
|
|
class ValueRecordDefinition(Statement):
|
|
def __init__(self, location, name, value):
|
|
Statement.__init__(self, location)
|
|
self.name = name
|
|
self.value = value
|