Merge pull request #3164 from fonttools/volto

Add voltLib.voltToFea module and command line tool
This commit is contained in:
خالد حسني (Khaled Hosny) 2023-06-16 23:55:46 +03:00 committed by GitHub
commit 060b5f36bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 2833 additions and 4 deletions

View File

@ -56,6 +56,7 @@ This last utility takes a subcommand, which could be one of:
- ``varLib.models``: Normalize locations on a given designspace - ``varLib.models``: Normalize locations on a given designspace
- ``varLib.mutator``: Instantiate a variation font - ``varLib.mutator``: Instantiate a variation font
- ``varLib.varStore``: Optimize a font's GDEF variation store - ``varLib.varStore``: Optimize a font's GDEF variation store
- ``voltLib.voltToFea``: Convert MS VOLT to AFDKO feature files.
Libraries Libraries
--------- ---------
@ -141,7 +142,7 @@ Table of Contents
unicode unicode
unicodedata/index unicodedata/index
varLib/index varLib/index
voltLib voltLib/index
.. |Travis Build Status| image:: https://travis-ci.org/fonttools/fonttools.svg .. |Travis Build Status| image:: https://travis-ci.org/fonttools/fonttools.svg

View File

@ -1,6 +1,11 @@
####### ####################################
voltLib voltLib: Read/write MS VOLT projects
####### ####################################
.. toctree::
:maxdepth: 2
voltToFea
.. automodule:: fontTools.voltLib .. automodule:: fontTools.voltLib

View File

@ -0,0 +1,8 @@
#################################################
voltToFea: Convert MS VOLT to AFDKO feature files
#################################################
.. automodule:: fontTools.voltLib.voltToFea
:inherited-members:
:members:
:undoc-members:

View File

@ -0,0 +1,726 @@
"""\
MS VOLT ``.vtp`` to AFDKO ``.fea`` OpenType Layout converter.
Usage
-----
To convert a VTP project file:
$ fonttools voltLib.voltToFea input.vtp output.fea
It is also possible convert font files with `TSIV` table (as saved from Volt),
in this case the glyph names used in the Volt project will be mapped to the
actual glyph names in the font files when written to the feature file:
$ fonttools voltLib.voltToFea input.ttf output.fea
The ``--quiet`` option can be used to suppress warnings.
The ``--traceback`` can be used to get Python traceback in case of exceptions,
instead of suppressing the traceback.
Limitations
-----------
* Not all VOLT features are supported, the script will error if it it
encounters something it does not understand. Please report an issue if this
happens.
* AFDKO feature file syntax for mark positioning is awkward and does not allow
setting the mark coverage. It also defines mark anchors globally, as a result
some mark positioning lookups might cover many marks than what was in the VOLT
file. This should not be an issue in practice, but if it is then the only way
is to modify the VOLT file or the generated feature file manually to use unique
mark anchors for each lookup.
* VOLT allows subtable breaks in any lookup type, but AFDKO feature file
implementations vary in their support; currently AFDKOs makeOTF supports
subtable breaks in pair positioning lookups only, while FontTools feaLib
support it for most substitution lookups and only some positioning lookups.
"""
import logging
import re
from io import StringIO
from fontTools.feaLib import ast
from fontTools.ttLib import TTFont, TTLibError
from fontTools.voltLib import ast as VAst
from fontTools.voltLib.parser import Parser as VoltParser
log = logging.getLogger("fontTools.voltLib.voltToFea")
TABLES = ["GDEF", "GSUB", "GPOS"]
class MarkClassDefinition(ast.MarkClassDefinition):
def asFea(self, indent=""):
res = ""
if not getattr(self, "used", False):
res += "#"
res += ast.MarkClassDefinition.asFea(self, indent)
return res
# For sorting voltLib.ast.GlyphDefinition, see its use below.
class Group:
def __init__(self, group):
self.name = group.name.lower()
self.groups = [
x.group.lower() for x in group.enum.enum if isinstance(x, VAst.GroupName)
]
def __lt__(self, other):
if self.name in other.groups:
return True
if other.name in self.groups:
return False
if self.groups and not other.groups:
return False
if not self.groups and other.groups:
return True
class VoltToFea:
_NOT_LOOKUP_NAME_RE = re.compile(r"[^A-Za-z_0-9.]")
_NOT_CLASS_NAME_RE = re.compile(r"[^A-Za-z_0-9.\-]")
def __init__(self, file_or_path, font=None):
self._file_or_path = file_or_path
self._font = font
self._glyph_map = {}
self._glyph_order = None
self._gdef = {}
self._glyphclasses = {}
self._features = {}
self._lookups = {}
self._marks = set()
self._ligatures = {}
self._markclasses = {}
self._anchors = {}
self._settings = {}
self._lookup_names = {}
self._class_names = {}
def _lookupName(self, name):
if name not in self._lookup_names:
res = self._NOT_LOOKUP_NAME_RE.sub("_", name)
while res in self._lookup_names.values():
res += "_"
self._lookup_names[name] = res
return self._lookup_names[name]
def _className(self, name):
if name not in self._class_names:
res = self._NOT_CLASS_NAME_RE.sub("_", name)
while res in self._class_names.values():
res += "_"
self._class_names[name] = res
return self._class_names[name]
def _collectStatements(self, doc, tables):
# Collect and sort group definitions first, to make sure a group
# definition that references other groups comes after them since VOLT
# does not enforce such ordering, and feature file require it.
groups = [s for s in doc.statements if isinstance(s, VAst.GroupDefinition)]
for statement in sorted(groups, key=lambda x: Group(x)):
self._groupDefinition(statement)
for statement in doc.statements:
if isinstance(statement, VAst.GlyphDefinition):
self._glyphDefinition(statement)
elif isinstance(statement, VAst.AnchorDefinition):
if "GPOS" in tables:
self._anchorDefinition(statement)
elif isinstance(statement, VAst.SettingDefinition):
self._settingDefinition(statement)
elif isinstance(statement, VAst.GroupDefinition):
pass # Handled above
elif isinstance(statement, VAst.ScriptDefinition):
self._scriptDefinition(statement)
elif not isinstance(statement, VAst.LookupDefinition):
raise NotImplementedError(statement)
# Lookup definitions need to be handled last as they reference glyph
# and mark classes that might be defined after them.
for statement in doc.statements:
if isinstance(statement, VAst.LookupDefinition):
if statement.pos and "GPOS" not in tables:
continue
if statement.sub and "GSUB" not in tables:
continue
self._lookupDefinition(statement)
def _buildFeatureFile(self, tables):
doc = ast.FeatureFile()
statements = doc.statements
if self._glyphclasses:
statements.append(ast.Comment("# Glyph classes"))
statements.extend(self._glyphclasses.values())
if self._markclasses:
statements.append(ast.Comment("\n# Mark classes"))
statements.extend(c[1] for c in sorted(self._markclasses.items()))
if self._lookups:
statements.append(ast.Comment("\n# Lookups"))
for lookup in self._lookups.values():
statements.extend(getattr(lookup, "targets", []))
statements.append(lookup)
# Prune features
features = self._features.copy()
for ftag in features:
scripts = features[ftag]
for stag in scripts:
langs = scripts[stag]
for ltag in langs:
langs[ltag] = [l for l in langs[ltag] if l.lower() in self._lookups]
scripts[stag] = {t: l for t, l in langs.items() if l}
features[ftag] = {t: s for t, s in scripts.items() if s}
features = {t: f for t, f in features.items() if f}
if features:
statements.append(ast.Comment("# Features"))
for ftag, scripts in features.items():
feature = ast.FeatureBlock(ftag)
stags = sorted(scripts, key=lambda k: 0 if k == "DFLT" else 1)
for stag in stags:
feature.statements.append(ast.ScriptStatement(stag))
ltags = sorted(scripts[stag], key=lambda k: 0 if k == "dflt" else 1)
for ltag in ltags:
include_default = True if ltag == "dflt" else False
feature.statements.append(
ast.LanguageStatement(ltag, include_default=include_default)
)
for name in scripts[stag][ltag]:
lookup = self._lookups[name.lower()]
lookupref = ast.LookupReferenceStatement(lookup)
feature.statements.append(lookupref)
statements.append(feature)
if self._gdef and "GDEF" in tables:
classes = []
for name in ("BASE", "MARK", "LIGATURE", "COMPONENT"):
if name in self._gdef:
classname = "GDEF_" + name.lower()
glyphclass = ast.GlyphClassDefinition(classname, self._gdef[name])
statements.append(glyphclass)
classes.append(ast.GlyphClassName(glyphclass))
else:
classes.append(None)
gdef = ast.TableBlock("GDEF")
gdef.statements.append(ast.GlyphClassDefStatement(*classes))
statements.append(gdef)
return doc
def convert(self, tables=None):
doc = VoltParser(self._file_or_path).parse()
if tables is None:
tables = TABLES
if self._font is not None:
self._glyph_order = self._font.getGlyphOrder()
self._collectStatements(doc, tables)
fea = self._buildFeatureFile(tables)
return fea.asFea()
def _glyphName(self, glyph):
try:
name = glyph.glyph
except AttributeError:
name = glyph
return ast.GlyphName(self._glyph_map.get(name, name))
def _groupName(self, group):
try:
name = group.group
except AttributeError:
name = group
return ast.GlyphClassName(self._glyphclasses[name.lower()])
def _coverage(self, coverage):
items = []
for item in coverage:
if isinstance(item, VAst.GlyphName):
items.append(self._glyphName(item))
elif isinstance(item, VAst.GroupName):
items.append(self._groupName(item))
elif isinstance(item, VAst.Enum):
items.append(self._enum(item))
elif isinstance(item, VAst.Range):
items.append((item.start, item.end))
else:
raise NotImplementedError(item)
return items
def _enum(self, enum):
return ast.GlyphClass(self._coverage(enum.enum))
def _context(self, context):
out = []
for item in context:
coverage = self._coverage(item)
if not isinstance(coverage, (tuple, list)):
coverage = [coverage]
out.extend(coverage)
return out
def _groupDefinition(self, group):
name = self._className(group.name)
glyphs = self._enum(group.enum)
glyphclass = ast.GlyphClassDefinition(name, glyphs)
self._glyphclasses[group.name.lower()] = glyphclass
def _glyphDefinition(self, glyph):
try:
self._glyph_map[glyph.name] = self._glyph_order[glyph.id]
except TypeError:
pass
if glyph.type in ("BASE", "MARK", "LIGATURE", "COMPONENT"):
if glyph.type not in self._gdef:
self._gdef[glyph.type] = ast.GlyphClass()
self._gdef[glyph.type].glyphs.append(self._glyphName(glyph.name))
if glyph.type == "MARK":
self._marks.add(glyph.name)
elif glyph.type == "LIGATURE":
self._ligatures[glyph.name] = glyph.components
def _scriptDefinition(self, script):
stag = script.tag
for lang in script.langs:
ltag = lang.tag
for feature in lang.features:
lookups = {l.split("\\")[0]: True for l in feature.lookups}
ftag = feature.tag
if ftag not in self._features:
self._features[ftag] = {}
if stag not in self._features[ftag]:
self._features[ftag][stag] = {}
assert ltag not in self._features[ftag][stag]
self._features[ftag][stag][ltag] = lookups.keys()
def _settingDefinition(self, setting):
if setting.name.startswith("COMPILER_"):
self._settings[setting.name] = setting.value
else:
log.warning(f"Unsupported setting ignored: {setting.name}")
def _adjustment(self, adjustment):
adv, dx, dy, adv_adjust_by, dx_adjust_by, dy_adjust_by = adjustment
adv_device = adv_adjust_by and adv_adjust_by.items() or None
dx_device = dx_adjust_by and dx_adjust_by.items() or None
dy_device = dy_adjust_by and dy_adjust_by.items() or None
return ast.ValueRecord(
xPlacement=dx,
yPlacement=dy,
xAdvance=adv,
xPlaDevice=dx_device,
yPlaDevice=dy_device,
xAdvDevice=adv_device,
)
def _anchor(self, adjustment):
adv, dx, dy, adv_adjust_by, dx_adjust_by, dy_adjust_by = adjustment
assert not adv_adjust_by
dx_device = dx_adjust_by and dx_adjust_by.items() or None
dy_device = dy_adjust_by and dy_adjust_by.items() or None
return ast.Anchor(
dx or 0,
dy or 0,
xDeviceTable=dx_device or None,
yDeviceTable=dy_device or None,
)
def _anchorDefinition(self, anchordef):
anchorname = anchordef.name
glyphname = anchordef.glyph_name
anchor = self._anchor(anchordef.pos)
if anchorname.startswith("MARK_"):
name = "_".join(anchorname.split("_")[1:])
markclass = ast.MarkClass(self._className(name))
glyph = self._glyphName(glyphname)
markdef = MarkClassDefinition(markclass, anchor, glyph)
self._markclasses[(glyphname, anchorname)] = markdef
else:
if glyphname not in self._anchors:
self._anchors[glyphname] = {}
if anchorname not in self._anchors[glyphname]:
self._anchors[glyphname][anchorname] = {}
self._anchors[glyphname][anchorname][anchordef.component] = anchor
def _gposLookup(self, lookup, fealookup):
statements = fealookup.statements
pos = lookup.pos
if isinstance(pos, VAst.PositionAdjustPairDefinition):
for (idx1, idx2), (pos1, pos2) in pos.adjust_pair.items():
coverage_1 = pos.coverages_1[idx1 - 1]
coverage_2 = pos.coverages_2[idx2 - 1]
# If not both are groups, use “enum pos” otherwise makeotf will
# fail.
enumerated = False
for item in coverage_1 + coverage_2:
if not isinstance(item, VAst.GroupName):
enumerated = True
glyphs1 = self._coverage(coverage_1)
glyphs2 = self._coverage(coverage_2)
record1 = self._adjustment(pos1)
record2 = self._adjustment(pos2)
assert len(glyphs1) == 1
assert len(glyphs2) == 1
statements.append(
ast.PairPosStatement(
glyphs1[0], record1, glyphs2[0], record2, enumerated=enumerated
)
)
elif isinstance(pos, VAst.PositionAdjustSingleDefinition):
for a, b in pos.adjust_single:
glyphs = self._coverage(a)
record = self._adjustment(b)
assert len(glyphs) == 1
statements.append(
ast.SinglePosStatement([(glyphs[0], record)], [], [], False)
)
elif isinstance(pos, VAst.PositionAttachDefinition):
anchors = {}
for marks, classname in pos.coverage_to:
for mark in marks:
# Set actually used mark classes. Basically a hack to get
# around the feature file syntax limitation of making mark
# classes global and not allowing mark positioning to
# specify mark coverage.
for name in mark.glyphSet():
key = (name, "MARK_" + classname)
self._markclasses[key].used = True
markclass = ast.MarkClass(self._className(classname))
for base in pos.coverage:
for name in base.glyphSet():
if name not in anchors:
anchors[name] = []
if classname not in anchors[name]:
anchors[name].append(classname)
for name in anchors:
components = 1
if name in self._ligatures:
components = self._ligatures[name]
marks = []
for mark in anchors[name]:
markclass = ast.MarkClass(self._className(mark))
for component in range(1, components + 1):
if len(marks) < component:
marks.append([])
anchor = None
if component in self._anchors[name][mark]:
anchor = self._anchors[name][mark][component]
marks[component - 1].append((anchor, markclass))
base = self._glyphName(name)
if name in self._marks:
mark = ast.MarkMarkPosStatement(base, marks[0])
elif name in self._ligatures:
mark = ast.MarkLigPosStatement(base, marks)
else:
mark = ast.MarkBasePosStatement(base, marks[0])
statements.append(mark)
elif isinstance(pos, VAst.PositionAttachCursiveDefinition):
# Collect enter and exit glyphs
enter_coverage = []
for coverage in pos.coverages_enter:
for base in coverage:
for name in base.glyphSet():
enter_coverage.append(name)
exit_coverage = []
for coverage in pos.coverages_exit:
for base in coverage:
for name in base.glyphSet():
exit_coverage.append(name)
# Write enter anchors, also check if the glyph has exit anchor and
# write it, too.
for name in enter_coverage:
glyph = self._glyphName(name)
entry = self._anchors[name]["entry"][1]
exit = None
if name in exit_coverage:
exit = self._anchors[name]["exit"][1]
exit_coverage.pop(exit_coverage.index(name))
statements.append(ast.CursivePosStatement(glyph, entry, exit))
# Write any remaining exit anchors.
for name in exit_coverage:
glyph = self._glyphName(name)
exit = self._anchors[name]["exit"][1]
statements.append(ast.CursivePosStatement(glyph, None, exit))
else:
raise NotImplementedError(pos)
def _gposContextLookup(
self, lookup, prefix, suffix, ignore, fealookup, targetlookup
):
statements = fealookup.statements
assert not lookup.reversal
pos = lookup.pos
if isinstance(pos, VAst.PositionAdjustPairDefinition):
for (idx1, idx2), (pos1, pos2) in pos.adjust_pair.items():
glyphs1 = self._coverage(pos.coverages_1[idx1 - 1])
glyphs2 = self._coverage(pos.coverages_2[idx2 - 1])
assert len(glyphs1) == 1
assert len(glyphs2) == 1
glyphs = (glyphs1[0], glyphs2[0])
if ignore:
statement = ast.IgnorePosStatement([(prefix, glyphs, suffix)])
else:
lookups = (targetlookup, targetlookup)
statement = ast.ChainContextPosStatement(
prefix, glyphs, suffix, lookups
)
statements.append(statement)
elif isinstance(pos, VAst.PositionAdjustSingleDefinition):
glyphs = [ast.GlyphClass()]
for a, b in pos.adjust_single:
glyph = self._coverage(a)
glyphs[0].extend(glyph)
if ignore:
statement = ast.IgnorePosStatement([(prefix, glyphs, suffix)])
else:
statement = ast.ChainContextPosStatement(
prefix, glyphs, suffix, [targetlookup]
)
statements.append(statement)
elif isinstance(pos, VAst.PositionAttachDefinition):
glyphs = [ast.GlyphClass()]
for coverage, _ in pos.coverage_to:
glyphs[0].extend(self._coverage(coverage))
if ignore:
statement = ast.IgnorePosStatement([(prefix, glyphs, suffix)])
else:
statement = ast.ChainContextPosStatement(
prefix, glyphs, suffix, [targetlookup]
)
statements.append(statement)
else:
raise NotImplementedError(pos)
def _gsubLookup(self, lookup, prefix, suffix, ignore, chain, fealookup):
statements = fealookup.statements
sub = lookup.sub
for key, val in sub.mapping.items():
if not key or not val:
path, line, column = sub.location
log.warning(f"{path}:{line}:{column}: Ignoring empty substitution")
continue
statement = None
glyphs = self._coverage(key)
replacements = self._coverage(val)
if ignore:
chain_context = (prefix, glyphs, suffix)
statement = ast.IgnoreSubstStatement([chain_context])
elif isinstance(sub, VAst.SubstitutionSingleDefinition):
assert len(glyphs) == 1
assert len(replacements) == 1
statement = ast.SingleSubstStatement(
glyphs, replacements, prefix, suffix, chain
)
elif isinstance(sub, VAst.SubstitutionReverseChainingSingleDefinition):
assert len(glyphs) == 1
assert len(replacements) == 1
statement = ast.ReverseChainSingleSubstStatement(
prefix, suffix, glyphs, replacements
)
elif isinstance(sub, VAst.SubstitutionMultipleDefinition):
assert len(glyphs) == 1
statement = ast.MultipleSubstStatement(
prefix, glyphs[0], suffix, replacements, chain
)
elif isinstance(sub, VAst.SubstitutionLigatureDefinition):
assert len(replacements) == 1
statement = ast.LigatureSubstStatement(
prefix, glyphs, suffix, replacements[0], chain
)
else:
raise NotImplementedError(sub)
statements.append(statement)
def _lookupDefinition(self, lookup):
mark_attachement = None
mark_filtering = None
flags = 0
if lookup.direction == "RTL":
flags |= 1
if not lookup.process_base:
flags |= 2
# FIXME: Does VOLT support this?
# if not lookup.process_ligatures:
# flags |= 4
if not lookup.process_marks:
flags |= 8
elif isinstance(lookup.process_marks, str):
mark_attachement = self._groupName(lookup.process_marks)
elif lookup.mark_glyph_set is not None:
mark_filtering = self._groupName(lookup.mark_glyph_set)
lookupflags = None
if flags or mark_attachement is not None or mark_filtering is not None:
lookupflags = ast.LookupFlagStatement(
flags, mark_attachement, mark_filtering
)
if "\\" in lookup.name:
# Merge sub lookups as subtables (lookups named “base\sub”),
# makeotf/feaLib will issue a warning and ignore the subtable
# statement if it is not a pairpos lookup, though.
name = lookup.name.split("\\")[0]
if name.lower() not in self._lookups:
fealookup = ast.LookupBlock(self._lookupName(name))
if lookupflags is not None:
fealookup.statements.append(lookupflags)
fealookup.statements.append(ast.Comment("# " + lookup.name))
else:
fealookup = self._lookups[name.lower()]
fealookup.statements.append(ast.SubtableStatement())
fealookup.statements.append(ast.Comment("# " + lookup.name))
self._lookups[name.lower()] = fealookup
else:
fealookup = ast.LookupBlock(self._lookupName(lookup.name))
if lookupflags is not None:
fealookup.statements.append(lookupflags)
self._lookups[lookup.name.lower()] = fealookup
if lookup.comments is not None:
fealookup.statements.append(ast.Comment("# " + lookup.comments))
contexts = []
if lookup.context:
for context in lookup.context:
prefix = self._context(context.left)
suffix = self._context(context.right)
ignore = context.ex_or_in == "EXCEPT_CONTEXT"
contexts.append([prefix, suffix, ignore, False])
# It seems that VOLT will create contextual substitution using
# only the input if there is no other contexts in this lookup.
if ignore and len(lookup.context) == 1:
contexts.append([[], [], False, True])
else:
contexts.append([[], [], False, False])
targetlookup = None
for prefix, suffix, ignore, chain in contexts:
if lookup.sub is not None:
self._gsubLookup(lookup, prefix, suffix, ignore, chain, fealookup)
if lookup.pos is not None:
if self._settings.get("COMPILER_USEEXTENSIONLOOKUPS"):
fealookup.use_extension = True
if prefix or suffix or chain or ignore:
if not ignore and targetlookup is None:
targetname = self._lookupName(lookup.name + " target")
targetlookup = ast.LookupBlock(targetname)
fealookup.targets = getattr(fealookup, "targets", [])
fealookup.targets.append(targetlookup)
self._gposLookup(lookup, targetlookup)
self._gposContextLookup(
lookup, prefix, suffix, ignore, fealookup, targetlookup
)
else:
self._gposLookup(lookup, fealookup)
def main(args=None):
"""Convert MS VOLT to AFDKO feature files."""
import argparse
from pathlib import Path
from fontTools import configLogger
parser = argparse.ArgumentParser(
"fonttools voltLib.voltToFea", description=main.__doc__
)
parser.add_argument(
"input", metavar="INPUT", type=Path, help="input font/VTP file to process"
)
parser.add_argument(
"featurefile", metavar="OUTPUT", type=Path, help="output feature file"
)
parser.add_argument(
"-t",
"--table",
action="append",
choices=TABLES,
dest="tables",
help="List of tables to write, by default all tables are written",
)
parser.add_argument(
"-q", "--quiet", action="store_true", help="Suppress non-error messages"
)
parser.add_argument(
"--traceback", action="store_true", help="Dont catch exceptions"
)
options = parser.parse_args(args)
configLogger(level=("ERROR" if options.quiet else "INFO"))
file_or_path = options.input
font = None
try:
font = TTFont(file_or_path)
if "TSIV" in font:
file_or_path = StringIO(font["TSIV"].data.decode("utf-8"))
else:
log.error('"TSIV" table is missing, font was not saved from VOLT?')
return 1
except TTLibError:
pass
converter = VoltToFea(file_or_path, font)
try:
fea = converter.convert(options.tables)
except NotImplementedError as e:
if options.traceback:
raise
location = getattr(e.args[0], "location", None)
message = f'"{e}" is not supported'
if location:
path, line, column = location
log.error(f"{path}:{line}:{column}: {message}")
else:
log.error(message)
return 1
with open(options.featurefile, "w") as feafile:
feafile.write(fea)
if __name__ == "__main__":
import sys
sys.exit(main())

Binary file not shown.

View File

@ -0,0 +1,506 @@
# Glyph classes
@Cons = [uni1901 uni1902 uni1903 uni1904 uni1905 uni1906 uni1907 uni1908 uni1909 uni190A uni190B uni190C uni190D uni190E uni190F uni1910 uni1911 uni1912 uni1913 uni1914 uni1915 uni1916 uni1917 uni1918 uni1919 uni191A uni191B uni191C uni1940];
@ConsRaU = [uni1901192A1922 uni1902192A1922 uni1903192A1922 uni1904192A1922 uni1905192A1922 uni1906192A1922 uni1907192A1922 uni1908192A1922 uni1909192A1922 uni190A192A1922 uni190B192A1922 uni190C192A1922 uni190D192A1922 uni190192AE1922 uni190F192A1922 uni1910192A1922 uni1911192A1922 uni1912192A1922 uni1913192A1922 uni1914192A1922 uni1915192A1922 uni1916192A1922 uni1917192A1922 uni1918192A1922 uni1919192A1922 uni1919192A1922 uni191A192A1922 uni191B192A1922 uni191C192A1922 uni1940192A1922];
@ConsU = [uni19011922 uni19021922 uni19031922 uni19041922 uni19051922 uni19061922 uni19071922 uni19081922 uni19091922 uni190A1922 uni190B1922 uni190C1922 uni190D1922 uni190E1922 uni190F1922 uni19101922 uni19111922 uni19121922 uni19131922 uni19141922 uni19151922 uni19161922 uni19171922 uni19181922 uni19191922 uni191A1922 uni191B1922 uni191C1922 uni19401922];
@Ikar = [uni1921 uni1921193A];
@Vowels = [uni1920 uni1927 uni1928];
@YaWa = [uni1929 uni192B];
@AllCons = [@Cons @ConsU @ConsRaU];
@VowelsKem = [@Vowels uni193A];
# Mark classes
markClass uni1920 <anchor -500 1050> @Aabove;
markClass uni1922 <anchor -150 -15> @U;
markClass uni1927 <anchor -300 1050> @eo;
markClass uni1928 <anchor -190 1050> @eo;
markClass uni193A <anchor -260 1250> @K;
markClass uni193A <anchor -260 1250> @VK;
# Lookups
lookup EEAIDecomp {
sub uni1925 by uni1920 uni1923;
sub uni1926 by uni1920 uni1924;
} EEAIDecomp;
lookup OoAuKComp {
sub uni1923 uni193A by uni1923193A;
sub uni1924 uni193A by uni1924193A;
} OoAuKComp;
lookup OoAuKDecomp {
# The OoAuDecomp substitution rule replaces the OO and AU vowels with their visually constitutent components A plus EE or AI respectively. This is so that the 'A' portion can be positioned independently over the consonant when a Glide occurs between the consonant and the vowel.
sub uni1923193A by uni193A uni1923;
sub uni1924193A by uni193A uni1924;
} OoAuKDecomp;
lookup GlideVowelComp {
sub uni1929 uni1920 uni193A by uni19291920193A;
sub uni1929 uni1922 uni193A by uni19291922193A;
sub uni1929 uni1927 uni193A by uni19291927193A;
sub uni1929 uni1928 uni193A by uni19291928193A;
sub uni1929 uni193A by uni1929193A;
sub uni1929 uni1920 by uni19291920;
sub uni1929 uni1922 by uni19291922;
sub uni1929 uni1927 by uni19291927;
sub uni1929 uni1928 by uni19291928;
sub uni192B uni1920 uni193A by uni192B1920193A;
sub uni192B uni1922 uni193A by uni192B1922193A;
sub uni192B uni1927 uni193A by uni192B1927193A;
sub uni192B uni1928 uni193A by uni192B1928193A;
sub uni192B uni193A by uni192B193A;
sub uni192B uni1920 by uni192B1920;
sub uni192B uni1922 by uni192B1922;
sub uni192B uni1927 by uni192B1927;
sub uni192B uni1928 by uni192B1928;
} GlideVowelComp;
lookup GlideVowelDecomp {
sub uni19291920193A by uni1920 uni193A uni1929;
sub uni19291922193A by uni1922 uni193A uni1929;
sub uni19291927193A by uni1927 uni193A uni1929;
sub uni19291928193A by uni1928 uni193A uni1929;
sub uni1929193A by uni193A uni1929;
sub uni19291920 by uni1920 uni1929;
sub uni19291922 by uni1922 uni1929;
sub uni19291927 by uni1927 uni1929;
sub uni19291928 by uni1928 uni1929;
sub uni192B1920193A by uni1920 uni193A uni192B;
sub uni192B1922193A by uni1922 uni193A uni192B;
sub uni192B1927193A by uni1927 uni193A uni192B;
sub uni192B1928193A by uni1928 uni193A uni192B;
sub uni192B193A by uni193A uni192B;
sub uni192B1920 by uni1920 uni192B;
sub uni192B1922 by uni1922 uni192B;
sub uni192B1927 by uni1927 uni192B;
sub uni192B1928 by uni1928 uni192B;
} GlideVowelDecomp;
lookup RaUkar {
# The RaUkar substitution rule replaces Consonant, Ra, Ukar with a ligature.
sub @Cons uni192A uni1922 by @ConsRaU;
} RaUkar;
lookup Ukar {
# The Ukar substitution rule replaces Consonant + Ukar with a ligature. It also applies to the Vowel-Carrier, which has its own ligature with ukar.
sub @Cons uni1922 by @ConsU;
sub uni1900 uni1922 by uni19001922;
} Ukar;
lookup IkarK {
# The IkarK substitution rule replaces Ikar + Kemphreng with a ligature. The ligature is then positioned properly on the base consonant via the positioning rule IEO.
sub uni1921 uni193A by uni1921193A;
} IkarK;
lookup GlideIkar_target {
pos @YaWa -475;
} GlideIkar_target;
lookup GlideIkar {
pos [@YaWa]' lookup GlideIkar_target @Ikar;
} GlideIkar;
lookup IkarKWid_target {
pos uni1921193A 110;
} IkarKWid_target;
lookup IkarKWid {
# The IkarKWid lookup, applied to the Kern feature, adds 110 units of width to the IkarKemphreng ligature when followed by a consonant with akar on it. This prevents the akar from overprinting the rightmost dot of the kemphreng. (The dot overhangs to the right slightly, which is OK unless the following character has akar on it).
pos [uni1921193A]' lookup IkarKWid_target @Cons uni1920;
} IkarKWid;
lookup Akar {
# The Akar positioning rule positions the Akar on all consonants.
pos base uni1901
<anchor 487 1050> mark @Aabove;
pos base uni1902
<anchor 622 1050> mark @Aabove;
pos base uni1903
<anchor 475 1050> mark @Aabove;
pos base uni1904
<anchor 460 1050> mark @Aabove;
pos base uni1905
<anchor 590 1050> mark @Aabove;
pos base uni1906
<anchor 519 1050> mark @Aabove;
pos base uni1907
<anchor 570 1050> mark @Aabove;
pos base uni1908
<anchor 564 1050> mark @Aabove;
pos base uni1909
<anchor 430 1050> mark @Aabove;
pos base uni190A
<anchor 575 1050> mark @Aabove;
pos base uni190B
<anchor 450 1050> mark @Aabove;
pos base uni190C
<anchor 556 1050> mark @Aabove;
pos base uni190D
<anchor 515 1050> mark @Aabove;
pos base uni190E
<anchor 510 1050> mark @Aabove;
pos base uni190F
<anchor 497 1050> mark @Aabove;
pos base uni1910
<anchor 657 1050> mark @Aabove;
pos base uni1911
<anchor 690 1050> mark @Aabove;
pos base uni1912
<anchor 538 1050> mark @Aabove;
pos base uni1913
<anchor 571 1050> mark @Aabove;
pos base uni1914
<anchor 538 1050> mark @Aabove;
pos base uni1915
<anchor 470 1050> mark @Aabove;
pos base uni1916
<anchor 503 1050> mark @Aabove;
pos base uni1917
<anchor 548 1050> mark @Aabove;
pos base uni1918
<anchor 511 1050> mark @Aabove;
pos base uni1919
<anchor 560 1050> mark @Aabove;
pos base uni191A
<anchor 420 1050> mark @Aabove;
pos base uni191B
<anchor 580 1050> mark @Aabove;
pos base uni191C
<anchor 540 1050> mark @Aabove;
pos base uni1940
<anchor 480 1050> mark @Aabove;
} Akar;
lookup Kemphreng {
# The Kemphreng positioning rule positions the Kemphreng on all consonants, including the vowel carrier.
pos base uni1901
<anchor 500 1050> mark @K;
pos base uni1902
<anchor 680 1050> mark @K;
pos base uni1903
<anchor 540 1050> mark @K;
pos base uni1904
<anchor 500 1050> mark @K;
pos base uni1905
<anchor 590 1050> mark @K;
pos base uni1906
<anchor 540 1050> mark @K;
pos base uni1907
<anchor 620 1050> mark @K;
pos base uni1908
<anchor 580 1050> mark @K;
pos base uni1909
<anchor 450 1050> mark @K;
pos base uni190A
<anchor 580 1050> mark @K;
pos base uni190B
<anchor 450 1050> mark @K;
pos base uni190C
<anchor 656 1050> mark @K;
pos base uni190D
<anchor 570 1050> mark @K;
pos base uni190E
<anchor 530 1050> mark @K;
pos base uni190F
<anchor 515 1050> mark @K;
pos base uni1910
<anchor 680 1050> mark @K;
pos base uni1911
<anchor 720 1050> mark @K;
pos base uni1912
<anchor 580 1050> mark @K;
pos base uni1913
<anchor 600 1050> mark @K;
pos base uni1914
<anchor 560 1050> mark @K;
pos base uni1915
<anchor 480 1050> mark @K;
pos base uni1916
<anchor 520 1050> mark @K;
pos base uni1917
<anchor 585 1050> mark @K;
pos base uni1918
<anchor 610 1050> mark @K;
pos base uni1919
<anchor 520 1050> mark @K;
pos base uni191A
<anchor 440 1050> mark @K;
pos base uni191B
<anchor 600 1050> mark @K;
pos base uni191C
<anchor 600 1050> mark @K;
pos base uni1940
<anchor 490 1050> mark @K;
pos base uni19011922
<anchor 500 1050> mark @K;
pos base uni19021922
<anchor 680 1050> mark @K;
pos base uni19031922
<anchor 540 1050> mark @K;
pos base uni19041922
<anchor 500 1050> mark @K;
pos base uni19051922
<anchor 590 1050> mark @K;
pos base uni19061922
<anchor 540 1050> mark @K;
pos base uni19071922
<anchor 620 1050> mark @K;
pos base uni19081922
<anchor 580 1050> mark @K;
pos base uni19091922
<anchor 450 1050> mark @K;
pos base uni190A1922
<anchor 580 1050> mark @K;
pos base uni190B1922
<anchor 450 1050> mark @K;
pos base uni190C1922
<anchor 656 1050> mark @K;
pos base uni190D1922
<anchor 570 1050> mark @K;
pos base uni190E1922
<anchor 530 1050> mark @K;
pos base uni190F1922
<anchor 515 1050> mark @K;
pos base uni19101922
<anchor 680 1050> mark @K;
pos base uni19111922
<anchor 720 1050> mark @K;
pos base uni19121922
<anchor 580 1050> mark @K;
pos base uni19131922
<anchor 600 1050> mark @K;
pos base uni19141922
<anchor 560 1050> mark @K;
pos base uni19151922
<anchor 480 1050> mark @K;
pos base uni19161922
<anchor 520 1050> mark @K;
pos base uni19171922
<anchor 585 1050> mark @K;
pos base uni19181922
<anchor 610 1050> mark @K;
pos base uni19191922
<anchor 520 1050> mark @K;
pos base uni191A1922
<anchor 440 1050> mark @K;
pos base uni191B1922
<anchor 600 1050> mark @K;
pos base uni191C1922
<anchor 600 1050> mark @K;
pos base uni19401922
<anchor 490 1050> mark @K;
pos base uni1901192A1922
<anchor 500 1050> mark @K;
pos base uni1902192A1922
<anchor 680 1050> mark @K;
pos base uni1903192A1922
<anchor 540 1050> mark @K;
pos base uni1904192A1922
<anchor 500 1050> mark @K;
pos base uni1905192A1922
<anchor 590 1050> mark @K;
pos base uni1906192A1922
<anchor 540 1050> mark @K;
pos base uni1907192A1922
<anchor 620 1050> mark @K;
pos base uni1908192A1922
<anchor 580 1050> mark @K;
pos base uni1909192A1922
<anchor 450 1050> mark @K;
pos base uni190A192A1922
<anchor 580 1050> mark @K;
pos base uni190B192A1922
<anchor 450 1050> mark @K;
pos base uni190C192A1922
<anchor 656 1050> mark @K;
pos base uni190D192A1922
<anchor 570 1050> mark @K;
pos base uni190192AE1922
<anchor 530 1050> mark @K;
pos base uni190F192A1922
<anchor 515 1050> mark @K;
pos base uni1910192A1922
<anchor 680 1050> mark @K;
pos base uni1911192A1922
<anchor 720 1050> mark @K;
pos base uni1912192A1922
<anchor 580 1050> mark @K;
pos base uni1913192A1922
<anchor 600 1050> mark @K;
pos base uni1914192A1922
<anchor 560 1050> mark @K;
pos base uni1915192A1922
<anchor 480 1050> mark @K;
pos base uni1916192A1922
<anchor 520 1050> mark @K;
pos base uni1917192A1922
<anchor 585 1050> mark @K;
pos base uni1918192A1922
<anchor 610 1050> mark @K;
pos base uni1919192A1922
<anchor 520 1050> mark @K;
pos base uni191A192A1922
<anchor 440 1050> mark @K;
pos base uni191B192A1922
<anchor 600 1050> mark @K;
pos base uni191C192A1922
<anchor 600 1050> mark @K;
pos base uni1940192A1922
<anchor 490 1050> mark @K;
pos base uni1900
<anchor 525 1050> mark @K;
} Kemphreng;
lookup EO {
# The IEO positioning rule positions ikar (including the ligature with kemphreng), e and o on all consonants plus the vowel carrier.
pos base uni1901
<anchor 755 1050> mark @eo;
pos base uni1902
<anchor 943 1050> mark @eo;
pos base uni1903
<anchor 790 1050> mark @eo;
pos base uni1904
<anchor 780 1050> mark @eo;
pos base uni1905
<anchor 790 1050> mark @eo;
pos base uni1906
<anchor 878 1050> mark @eo;
pos base uni1907
<anchor 825 1050> mark @eo;
pos base uni1908
<anchor 968 1050> mark @eo;
pos base uni1909
<anchor 660 1050> mark @eo;
pos base uni190A
<anchor 569 1050> mark @eo;
pos base uni190B
<anchor 690 1050> mark @eo;
pos base uni190C
<anchor 649 1050> mark @eo;
pos base uni190D
<anchor 682 1050> mark @eo;
pos base uni190E
<anchor 680 1050> mark @eo;
pos base uni190F
<anchor 778 1050> mark @eo;
pos base uni1910
<anchor 920 1050> mark @eo;
pos base uni1911
<anchor 894 1050> mark @eo;
pos base uni1912
<anchor 782 1050> mark @eo;
pos base uni1913
<anchor 982 1050> mark @eo;
pos base uni1914
<anchor 917 1050> mark @eo;
pos base uni1915
<anchor 730 1050> mark @eo;
pos base uni1916
<anchor 767 1050> mark @eo;
pos base uni1917
<anchor 937 1050> mark @eo;
pos base uni1918
<anchor 862 1050> mark @eo;
pos base uni1919
<anchor 670 1050> mark @eo;
pos base uni191A
<anchor 682 1050> mark @eo;
pos base uni191B
<anchor 921 1050> mark @eo;
pos base uni191C
<anchor 870 1050> mark @eo;
pos base uni1940
<anchor 650 1050> mark @eo;
pos base uni1900
<anchor 810 1050> mark @eo;
} EO;
lookup VKem {
lookupflag MarkAttachmentType @VowelsKem;
# The VKem positioning rule positions the kemphreng on all upper vowels (except ikar, which has its own ligature). The vowel itself is positioned on the consonant with the Akar or IEO positioning rule.
pos mark uni1920
<anchor -260 1250> mark @VK;
pos mark uni1927
<anchor -300 1250> mark @VK;
pos mark uni1928
<anchor -150 1455> mark @VK;
} VKem;
lookup GlideU {
# The GlideU positioning rule positions the ukar on the glides Ya and Wa. (There is already a ligature for each consonant with the Ra+Ukar combination).
pos base uni1929
<anchor -135 -40> mark @U;
pos base uni192B
<anchor -135 -40> mark @U;
} GlideU;
# Features
feature ccmp {
script latn;
language dflt;
lookup EEAIDecomp;
lookup OoAuKComp;
lookup OoAuKDecomp;
lookup GlideVowelComp;
lookup GlideVowelDecomp;
script limb;
language dflt;
lookup EEAIDecomp;
lookup OoAuKComp;
lookup OoAuKDecomp;
lookup GlideVowelComp;
lookup GlideVowelDecomp;
} ccmp;
feature kern {
script latn;
language dflt;
lookup GlideIkar;
lookup IkarKWid;
script limb;
language dflt;
lookup GlideIkar;
lookup IkarKWid;
} kern;
feature mark {
script latn;
language dflt;
lookup Akar;
lookup Kemphreng;
lookup EO;
script limb;
language dflt;
lookup Akar;
lookup Kemphreng;
lookup EO;
} mark;
feature mkmk {
script latn;
language dflt;
lookup VKem;
lookup GlideU;
script limb;
language dflt;
lookup VKem;
lookup GlideU;
} mkmk;
feature liga {
script latn;
language dflt;
lookup RaUkar;
lookup Ukar;
lookup IkarK;
script limb;
language dflt;
lookup RaUkar;
lookup Ukar;
lookup IkarK;
} liga;
@GDEF_base = [glyph0 .null CR space exclam quotedbl numbersign dollar percent quotesingle parenleft parenright asterisk plus comma hyphen period slash zero one two three four five six seven eight nine colon semicolon less equal greater question at A B C D E F G H I J K L M N O P Q R S T U V W X Y Z bracketleft backslash bracketright asciicircum underscore grave a b c d e f g h i j k l m n o p q r s t u v w x y z braceleft bar braceright asciitilde uni0965 uni1900 uni19001922 uni1901 uni19011922 uni1901192A1922 uni1902 uni19021922 uni1902192A1922 uni1903 uni19031922 uni1903192A1922 uni1904 uni19041922 uni1904192A1922 uni1905 uni19051922 uni1905192A1922 uni1906 uni19061922 uni1906192A1922 uni1907 uni19071922 uni1907192A1922 uni1908 uni19081922 uni1908192A1922 uni1909 uni19091922 uni1909192A1922 uni190A uni190A1922 uni190A192A1922 uni190B uni190B1922 uni190B192A1922 uni190C uni190C1922 uni190C192A1922 uni190D uni190D1922 uni190D192A1922 uni190E uni190E1922 uni190192AE1922 uni190F uni190F1922 uni190F192A1922 uni1910 uni19101922 uni1910192A1922 uni1911 uni19111922 uni1911192A1922 uni1912 uni19121922 uni1912192A1922 uni1913 uni19131922 uni1913192A1922 uni1914 uni19141922 uni1914192A1922 uni1915 uni19151922 uni1915192A1922 uni1916 uni19161922 uni1916192A1922 uni1917 uni19171922 uni1917192A1922 uni1918 uni19181922 uni1918192A1922 uni1919 uni19191922 uni1919192A1922 uni191A uni191A1922 uni191A192A1922 uni191B uni191B1922 uni191B192A1922 uni191C uni191C1922 uni191C192A1922 uni1921 uni1923 uni1924 uni1929 uni192B uni1930 uni1931 uni1932 uni1933 uni1934 uni1935 uni1936 uni1937 uni1938 uni1939 uni1940 uni19401922 uni1940192A1922 uni1944 uni1945 uni1946 uni1947 uni1948 uni1949 uni194A uni194B uni194C uni194D uni194E uni194F quoteleft quoteright quotedblleft quotedblright uni1921193A ampersand uni2009 endash emdash uni202F uni1923193A uni1924193A uni19291920 uni19291922 uni19291927 uni19291928 uni1929193A uni19291920193A uni19291922193A uni19291927193A uni19291928193A uni192B1920 uni192B1922 uni192B1927 uni192B1928 uni192B193A uni192B1920193A uni192B1922193A uni192B1927193A uni192B1928193A uni25CC uni191E uni191E1922 uni191E192A1922 uni191D uni191D1922 uni191D192A1922];
@GDEF_mark = [uni1920 uni1920.widC uni1920.widD uni1922 uni1922.altA uni1922.altB uni1922.altC uni1925 uni1926 uni1927 uni1928 uni192A uni193A uni193A.widC uni193B uni193B.widA uni193B.widB uni193B.widC uni192A1922];
table GDEF {
GlyphClassDef @GDEF_base, , @GDEF_mark, ;
} GDEF;

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,328 @@
# Glyph classes
@dnom = [zero.dnom one.dnom two.dnom three.dnom four.dnom five.dnom six.dnom seven.dnom eight.dnom nine.dnom];
@numerals = [zero one two three four five six seven eight nine];
@numr = [zero.numr one.numr two.numr three.numr four.numr five.numr six.numr seven.numr eight.numr nine.numr];
@slash = [slash fraction];
# Mark classes
markClass eight.numr <anchor 0 0> @INIT.1.10;
markClass eight.numr <anchor 0 0> @INIT.2.10;
markClass eight.numr <anchor 0 0> @INIT.3.10;
markClass eight.numr <anchor 0 0> @INIT.4.10;
markClass eight.numr <anchor 0 0> @INIT.5.10;
markClass eight.numr <anchor 0 0> @INIT.6.10;
markClass eight.numr <anchor 0 0> @INIT.7.10;
markClass eight.numr <anchor 0 0> @INIT.8.10;
markClass eight.numr <anchor 0 0> @INIT.9.10;
markClass eight.numr <anchor 0 0> @NUMRNUMR;
markClass five.numr <anchor 0 0> @INIT.1.10;
markClass five.numr <anchor 0 0> @INIT.2.10;
markClass five.numr <anchor 0 0> @INIT.3.10;
markClass five.numr <anchor 0 0> @INIT.4.10;
markClass five.numr <anchor 0 0> @INIT.5.10;
markClass five.numr <anchor 0 0> @INIT.6.10;
markClass five.numr <anchor 0 0> @INIT.7.10;
markClass five.numr <anchor 0 0> @INIT.8.10;
markClass five.numr <anchor 0 0> @INIT.9.10;
markClass five.numr <anchor 0 0> @NUMRNUMR;
markClass four.numr <anchor 0 0> @INIT.1.10;
markClass four.numr <anchor 0 0> @INIT.2.10;
markClass four.numr <anchor 0 0> @INIT.3.10;
markClass four.numr <anchor 0 0> @INIT.4.10;
markClass four.numr <anchor 0 0> @INIT.5.10;
markClass four.numr <anchor 0 0> @INIT.6.10;
markClass four.numr <anchor 0 0> @INIT.7.10;
markClass four.numr <anchor 0 0> @INIT.8.10;
markClass four.numr <anchor 0 0> @INIT.9.10;
markClass four.numr <anchor 0 0> @NUMRNUMR;
markClass nine.numr <anchor 0 0> @INIT.1.10;
markClass nine.numr <anchor 0 0> @INIT.2.10;
markClass nine.numr <anchor 0 0> @INIT.3.10;
markClass nine.numr <anchor 0 0> @INIT.4.10;
markClass nine.numr <anchor 0 0> @INIT.5.10;
markClass nine.numr <anchor 0 0> @INIT.6.10;
markClass nine.numr <anchor 0 0> @INIT.7.10;
markClass nine.numr <anchor 0 0> @INIT.8.10;
markClass nine.numr <anchor 0 0> @INIT.9.10;
markClass nine.numr <anchor 0 0> @NUMRNUMR;
markClass one.numr <anchor 0 0> @INIT.1.10;
markClass one.numr <anchor 0 0> @INIT.2.10;
markClass one.numr <anchor 0 0> @INIT.3.10;
markClass one.numr <anchor 0 0> @INIT.4.10;
markClass one.numr <anchor 0 0> @INIT.5.10;
markClass one.numr <anchor 0 0> @INIT.6.10;
markClass one.numr <anchor 0 0> @INIT.7.10;
markClass one.numr <anchor 0 0> @INIT.8.10;
markClass one.numr <anchor 0 0> @INIT.9.10;
markClass one.numr <anchor 0 0> @NUMRNUMR;
markClass seven.numr <anchor 0 0> @INIT.1.10;
markClass seven.numr <anchor 0 0> @INIT.2.10;
markClass seven.numr <anchor 0 0> @INIT.3.10;
markClass seven.numr <anchor 0 0> @INIT.4.10;
markClass seven.numr <anchor 0 0> @INIT.5.10;
markClass seven.numr <anchor 0 0> @INIT.6.10;
markClass seven.numr <anchor 0 0> @INIT.7.10;
markClass seven.numr <anchor 0 0> @INIT.8.10;
markClass seven.numr <anchor 0 0> @INIT.9.10;
markClass seven.numr <anchor 0 0> @NUMRNUMR;
markClass six.numr <anchor 0 0> @INIT.1.10;
markClass six.numr <anchor 0 0> @INIT.2.10;
markClass six.numr <anchor 0 0> @INIT.3.10;
markClass six.numr <anchor 0 0> @INIT.4.10;
markClass six.numr <anchor 0 0> @INIT.5.10;
markClass six.numr <anchor 0 0> @INIT.6.10;
markClass six.numr <anchor 0 0> @INIT.7.10;
markClass six.numr <anchor 0 0> @INIT.8.10;
markClass six.numr <anchor 0 0> @INIT.9.10;
markClass six.numr <anchor 0 0> @NUMRNUMR;
markClass three.numr <anchor 0 0> @INIT.1.10;
markClass three.numr <anchor 0 0> @INIT.2.10;
markClass three.numr <anchor 0 0> @INIT.3.10;
markClass three.numr <anchor 0 0> @INIT.4.10;
markClass three.numr <anchor 0 0> @INIT.5.10;
markClass three.numr <anchor 0 0> @INIT.6.10;
markClass three.numr <anchor 0 0> @INIT.7.10;
markClass three.numr <anchor 0 0> @INIT.8.10;
markClass three.numr <anchor 0 0> @INIT.9.10;
markClass three.numr <anchor 0 0> @NUMRNUMR;
markClass two.numr <anchor 0 0> @INIT.1.10;
markClass two.numr <anchor 0 0> @INIT.2.10;
markClass two.numr <anchor 0 0> @INIT.3.10;
markClass two.numr <anchor 0 0> @INIT.4.10;
markClass two.numr <anchor 0 0> @INIT.5.10;
markClass two.numr <anchor 0 0> @INIT.6.10;
markClass two.numr <anchor 0 0> @INIT.7.10;
markClass two.numr <anchor 0 0> @INIT.8.10;
markClass two.numr <anchor 0 0> @INIT.9.10;
markClass two.numr <anchor 0 0> @NUMRNUMR;
markClass zero.numr <anchor 0 0> @INIT.1.10;
markClass zero.numr <anchor 0 0> @INIT.2.10;
markClass zero.numr <anchor 0 0> @INIT.3.10;
markClass zero.numr <anchor 0 0> @INIT.4.10;
markClass zero.numr <anchor 0 0> @INIT.5.10;
markClass zero.numr <anchor 0 0> @INIT.6.10;
markClass zero.numr <anchor 0 0> @INIT.7.10;
markClass zero.numr <anchor 0 0> @INIT.8.10;
markClass zero.numr <anchor 0 0> @INIT.9.10;
markClass zero.numr <anchor 0 0> @NUMRNUMR;
# Lookups
lookup frac.numr {
sub @numerals by @numr;
} frac.numr;
lookup frac.dnom {
sub [@slash @dnom] @numr' by @dnom;
} frac.dnom;
lookup frac.noslash {
sub @numr slash by @numr;
sub @numr fraction by @numr;
} frac.noslash;
lookup frac.fracinit {
ignore sub @numr @numr';
sub @numr' by fracinit @numr;
} frac.fracinit;
lookup kern.numeral_to_fraction {
enum pos @numerals fracinit 140;
pos @dnom @numerals 140;
} kern.numeral_to_fraction;
lookup fracmark.init_1.10_target {
pos base fracinit
<anchor 3150 0> mark @INIT.1.10;
} fracmark.init_1.10_target;
lookup fracmark.init_2.10_target {
pos base fracinit
<anchor 2800 0> mark @INIT.2.10;
} fracmark.init_2.10_target;
lookup fracmark.init_3.10_target {
pos base fracinit
<anchor 2450 0> mark @INIT.3.10;
} fracmark.init_3.10_target;
lookup fracmark.init_4.10_target {
pos base fracinit
<anchor 2100 0> mark @INIT.4.10;
} fracmark.init_4.10_target;
lookup fracmark.init_5.10_target {
pos base fracinit
<anchor 1750 0> mark @INIT.5.10;
} fracmark.init_5.10_target;
lookup fracmark.init_6.10_target {
pos base fracinit
<anchor 1400 0> mark @INIT.6.10;
} fracmark.init_6.10_target;
lookup fracmark.init_7.10_target {
pos base fracinit
<anchor 1050 0> mark @INIT.7.10;
} fracmark.init_7.10_target;
lookup fracmark.init_8.10_target {
pos base fracinit
<anchor 700 0> mark @INIT.8.10;
} fracmark.init_8.10_target;
lookup fracmark.init_9.10_target {
pos base fracinit
<anchor 350 0> mark @INIT.9.10;
} fracmark.init_9.10_target;
lookup fracmark.init {
# fracmark.init\1.10
pos [@numr]' lookup fracmark.init_1.10_target @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
subtable;
# fracmark.init\2.10
pos [@numr]' lookup fracmark.init_2.10_target @numr @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_2.10_target @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
subtable;
# fracmark.init\3.10
pos [@numr]' lookup fracmark.init_3.10_target @numr @numr @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_3.10_target @numr @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_3.10_target @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
subtable;
# fracmark.init\4.10
pos [@numr]' lookup fracmark.init_4.10_target @numr @numr @numr @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_4.10_target @numr @numr @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_4.10_target @numr @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_4.10_target @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
subtable;
# fracmark.init\5.10
pos [@numr]' lookup fracmark.init_5.10_target @numr @numr @numr @numr @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_5.10_target @numr @numr @numr @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_5.10_target @numr @numr @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_5.10_target @numr @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_5.10_target @dnom @dnom @dnom @dnom @dnom @dnom;
subtable;
# fracmark.init\6.10
pos [@numr]' lookup fracmark.init_6.10_target @numr @numr @numr @numr @numr @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_6.10_target @numr @numr @numr @numr @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_6.10_target @numr @numr @numr @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_6.10_target @numr @numr @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_6.10_target @numr @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_6.10_target @dnom @dnom @dnom @dnom @dnom;
subtable;
# fracmark.init\7.10
pos [@numr]' lookup fracmark.init_7.10_target @numr @numr @numr @numr @numr @numr @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_7.10_target @numr @numr @numr @numr @numr @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_7.10_target @numr @numr @numr @numr @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_7.10_target @numr @numr @numr @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_7.10_target @numr @numr @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_7.10_target @numr @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_7.10_target @dnom @dnom @dnom @dnom;
subtable;
# fracmark.init\8.10
pos [@numr]' lookup fracmark.init_8.10_target @numr @numr @numr @numr @numr @numr @numr @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_8.10_target @numr @numr @numr @numr @numr @numr @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_8.10_target @numr @numr @numr @numr @numr @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_8.10_target @numr @numr @numr @numr @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_8.10_target @numr @numr @numr @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_8.10_target @numr @numr @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_8.10_target @numr @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_8.10_target @dnom @dnom @dnom;
subtable;
# fracmark.init\9.10
pos [@numr]' lookup fracmark.init_9.10_target @numr @numr @numr @numr @numr @numr @numr @numr @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_9.10_target @numr @numr @numr @numr @numr @numr @numr @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_9.10_target @numr @numr @numr @numr @numr @numr @dnom @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_9.10_target @numr @numr @numr @numr @numr @dnom @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_9.10_target @numr @numr @numr @numr @dnom @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_9.10_target @numr @numr @numr @dnom @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_9.10_target @numr @numr @dnom @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_9.10_target @numr @dnom @dnom @dnom;
pos [@numr]' lookup fracmark.init_9.10_target @dnom @dnom;
} fracmark.init;
lookup fracmkmk.numrspacing {
pos mark zero.numr
<anchor 700 0> mark @NUMRNUMR;
pos mark one.numr
<anchor 700 0> mark @NUMRNUMR;
pos mark two.numr
<anchor 700 0> mark @NUMRNUMR;
pos mark three.numr
<anchor 700 0> mark @NUMRNUMR;
pos mark four.numr
<anchor 700 0> mark @NUMRNUMR;
pos mark five.numr
<anchor 700 0> mark @NUMRNUMR;
pos mark six.numr
<anchor 700 0> mark @NUMRNUMR;
pos mark seven.numr
<anchor 700 0> mark @NUMRNUMR;
pos mark eight.numr
<anchor 700 0> mark @NUMRNUMR;
pos mark nine.numr
<anchor 700 0> mark @NUMRNUMR;
} fracmkmk.numrspacing;
# Features
feature afrc {
script DFLT;
language dflt;
lookup frac.numr;
lookup frac.dnom;
lookup frac.noslash;
lookup frac.fracinit;
script latn;
language dflt;
lookup frac.numr;
lookup frac.dnom;
lookup frac.noslash;
lookup frac.fracinit;
} afrc;
feature frac {
script DFLT;
language dflt;
lookup frac.numr;
lookup frac.dnom;
lookup frac.noslash;
lookup frac.fracinit;
script latn;
language dflt;
lookup frac.numr;
lookup frac.dnom;
lookup frac.noslash;
lookup frac.fracinit;
} frac;
feature kern {
script DFLT;
language dflt;
lookup kern.numeral_to_fraction;
script latn;
language dflt;
lookup kern.numeral_to_fraction;
} kern;
feature mark {
script DFLT;
language dflt;
lookup fracmark.init;
script latn;
language dflt;
lookup fracmark.init;
} mark;
feature mkmk {
script DFLT;
language dflt;
lookup fracmkmk.numrspacing;
script latn;
language dflt;
lookup fracmkmk.numrspacing;
} mkmk;
@GDEF_base = [glyph0 \NULL CR space zero one two three four five six seven eight nine slash fraction fracinit zero.dnom one.dnom two.dnom three.dnom four.dnom five.dnom six.dnom seven.dnom eight.dnom nine.dnom];
@GDEF_mark = [zero.numr one.numr two.numr three.numr four.numr five.numr six.numr seven.numr eight.numr nine.numr];
table GDEF {
GlyphClassDef @GDEF_base, , @GDEF_mark, ;
} GDEF;

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff