Upgrade fonttools from 2.4 to 3.28.0
am: 8b3c57bdcb

Change-Id: I71a2a90fe698a781d2d1f0ecab3ee9715afb0e95
diff --git a/.appveyor.yml b/.appveyor.yml
new file mode 100644
index 0000000..f04db29
--- /dev/null
+++ b/.appveyor.yml
@@ -0,0 +1,62 @@
+environment:
+  matrix:
+    - JOB: "2.7 32-bit"
+      PYTHON_HOME: "C:\\Python27"
+      TOXENV: "py27-cov"
+      TOXPYTHON: "C:\\Python27\\python.exe"
+
+    - JOB: "3.6 32-bit"
+      PYTHON_HOME: "C:\\Python36"
+      TOXENV: "py36-cov"
+      TOXPYTHON: "C:\\Python36\\python.exe"
+
+    - JOB: "2.7 64-bit"
+      PYTHON_HOME: "C:\\Python27-x64"
+      TOXENV: "py27-cov"
+      TOXPYTHON: "C:\\Python27-x64\\python.exe"
+
+    - JOB: "3.6 64-bit"
+      PYTHON_HOME: "C:\\Python36-x64"
+      TOXENV: "py36-cov"
+      TOXPYTHON: "C:\\Python36-x64\\python.exe"
+
+install:
+  # If there is a newer build queued for the same PR, cancel this one.
+  # The AppVeyor 'rollout builds' option is supposed to serve the same
+  # purpose but it is problematic because it tends to cancel builds pushed
+  # directly to master instead of just PR builds (or the converse).
+  # credits: JuliaLang developers.
+  - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod `
+        https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | `
+        Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { `
+          throw "There are newer queued builds for this pull request, failing early." }
+
+  # Prepend Python to the PATH of this build
+  - "SET PATH=%PYTHON_HOME%;%PYTHON_HOME%\\Scripts;%PATH%"
+
+  # check that we have the expected version and architecture for Python
+  - "python --version"
+  - "python -c \"import struct; print(struct.calcsize('P') * 8)\""
+
+  # upgrade pip and setuptools to avoid out-of-date warnings
+  - "python -m pip install --disable-pip-version-check --user --upgrade pip setuptools"
+
+  # install the dependencies to run the tests
+  - "python -m pip install tox"
+
+
+build: false
+
+test_script:
+  - "tox"
+
+after_test:
+  - "tox -e codecov"
+
+notifications:
+  - provider: Email
+    to:
+      - fonttools-dev@googlegroups.com
+    on_build_success: false
+    on_build_failure: true
+    on_build_status_changed: true
diff --git a/.codecov.yml b/.codecov.yml
new file mode 100644
index 0000000..5e7474d
--- /dev/null
+++ b/.codecov.yml
@@ -0,0 +1,5 @@
+comment: false
+coverage:
+    status:
+        project: off
+        patch: off
diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000..16b5756
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,34 @@
+[run]
+# measure 'branch' coverage in addition to 'statement' coverage
+# See: http://coverage.readthedocs.org/en/coverage-4.0.3/branch.html#branch
+branch = True
+
+# list of directories or packages to measure
+source = fontTools
+
+# these are treated as equivalent when combining data
+[paths]
+source =
+    Lib/fontTools
+    .tox/*/lib/python*/site-packages/fontTools
+    .tox/pypy*/site-packages/fontTools
+
+[report]
+# Regexes for lines to exclude from consideration
+exclude_lines =
+    # keywords to use in inline comments to skip coverage
+    pragma: no cover
+
+    # don't complain if tests don't hit defensive assertion code
+    raise AssertionError
+    raise NotImplementedError
+
+    # don't complain if non-runnable code isn't run
+    if 0:
+    if __name__ == .__main__.:
+
+# ignore source code that can’t be found
+ignore_errors = True
+
+# when running a summary report, show missing lines
+show_missing = True
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..a1cd77d
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,104 @@
+sudo: false
+
+language: python
+python: 3.5
+
+# empty "env:" is needed for 'allow_failures'
+# https://docs.travis-ci.com/user/customizing-the-build/#Rows-that-are-Allowed-to-Fail
+env:
+
+matrix:
+  fast_finish: true
+  exclude:
+    # Exclude the default Python 3.5 build
+    - python: 3.5
+  include:
+    - python: 2.7
+      env: TOXENV=py27-cov
+    - python: 3.4
+      env: TOXENV=py34-cov
+    - python: 3.5
+      env: TOXENV=py35-cov
+    - python: 3.6
+      env:
+        - TOXENV=py36-cov
+        - BUILD_DIST=true
+    - python: pypy2.7-5.8.0
+      # disable coverage.py on pypy because of performance problems
+      env: TOXENV=pypy-nocov
+    - language: generic
+      os: osx
+      env: TOXENV=py27-cov
+    - language: generic
+      os: osx
+      env:
+        - TOXENV=py36-cov
+        - HOMEBREW_NO_AUTO_UPDATE=1
+    - env:
+        - TOXENV=py27-nocov
+        - PYENV_VERSION='2.7.6'
+        - PYENV_VERSION_STRING='Python 2.7.6'
+        - PYENV_ROOT=$HOME/.travis-pyenv
+        - TRAVIS_PYENV_VERSION='0.4.0'
+  allow_failures:
+    # We use fast_finish + allow_failures because OSX builds take forever
+    # https://blog.travis-ci.com/2013-11-27-fast-finishing-builds
+    - language: generic
+      os: osx
+      env: TOXENV=py27-cov
+    - language: generic
+      os: osx
+      env:
+        - TOXENV=py36-cov
+        - HOMEBREW_NO_AUTO_UPDATE=1
+
+cache:
+  - pip
+  - directories:
+    - $HOME/.pyenv_cache
+
+before_install:
+  - source ./.travis/before_install.sh
+
+install:
+  - ./.travis/install.sh
+
+script:
+  - ./.travis/run.sh
+
+after_success:
+  - ./.travis/after_success.sh
+
+before_deploy:
+  - ./.travis/before_deploy.sh
+
+notifications:
+  irc: "irc.freenode.org##fonts"
+  email: fonttools-dev@googlegroups.com
+
+deploy:
+  # deploy to Github Releases on tags
+  - provider: releases
+    api_key:
+      secure: KEcWhJxMcnKay7wmWJCpg2W5GWHTQ+LaRbqGM11IKGcQuEOFxWuG7W1xjGpVdKPj/MQ+cG0b9hGUFpls1hwseOA1HANMv4xjCgYkuvT1OdpX/KOcZ7gfe/qaovzVxHyP9xwohnHSJMb790t37fmDfFUSROx3iEexIX09LLoDjO8=
+    skip_cleanup: true
+    file_glob: true
+    file: "dist/*"
+    on:
+      tags: true
+      repo: fonttools/fonttools
+      all_branches: true
+      condition: "$BUILD_DIST == true"
+  # deploy to PyPI on tags
+  - provider: pypi
+    server: https://upload.pypi.org/legacy/
+    user: anthrotype
+    password:
+      secure: Dz3x8kh4ergBV6qZUgcGVDOEzjoCEFzzQiO5WVw4Zfi04DD8+d1ghmMz2BY4UvoVKSsFrfKDuEB3MCWyqewJsf/zoZQczk/vnWVFjERROieyO1Ckzpz/WkCvbjtniIE0lxzB7zorSV+kGI9VigGAaRlXJyU7mCFojeAFqD6cjS4=
+    skip_cleanup: true
+    distributions: pass
+    on:
+      tags: true
+      repo: fonttools/fonttools
+      all_branches: true
+      condition: "$BUILD_DIST == true"
diff --git a/.travis/after_success.sh b/.travis/after_success.sh
new file mode 100755
index 0000000..d113fe7
--- /dev/null
+++ b/.travis/after_success.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+set -e
+set -x
+
+if [ "$TRAVIS_OS_NAME" == "osx" ]; then
+    source .venv/bin/activate
+fi
+
+# upload coverage data to Codecov.io
+[[ ${TOXENV} == *"-cov"* ]] && tox -e codecov
diff --git a/.travis/before_deploy.sh b/.travis/before_deploy.sh
new file mode 100755
index 0000000..1ded8f0
--- /dev/null
+++ b/.travis/before_deploy.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+set -e
+set -x
+
+# build sdist and wheel distribution packages in ./dist folder.
+# Travis runs the `before_deploy` stage before each deployment, but
+# we only want to build them once, as we want to use the same
+# files for both Github and PyPI
+if $(ls ./dist/fonttools*.zip > /dev/null 2>&1) && \
+		$(ls ./dist/fonttools*.whl > /dev/null 2>&1); then
+	echo "Distribution packages already exists; skipping"
+else
+	tox -e bdist
+fi
diff --git a/.travis/before_install.sh b/.travis/before_install.sh
new file mode 100755
index 0000000..8cc4edb
--- /dev/null
+++ b/.travis/before_install.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+if [[ -n "$PYENV_VERSION" ]]; then
+    wget https://github.com/praekeltfoundation/travis-pyenv/releases/download/${TRAVIS_PYENV_VERSION}/setup-pyenv.sh
+    source setup-pyenv.sh
+fi
diff --git a/.travis/install.sh b/.travis/install.sh
new file mode 100755
index 0000000..03cc0b3
--- /dev/null
+++ b/.travis/install.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+set -e
+set -x
+
+ci_requirements="pip setuptools tox"
+
+if [ "$TRAVIS_OS_NAME" == "osx" ]; then
+    if [[ ${TOXENV} == *"py27"* ]]; then
+        # install pip on the system python
+        curl -O https://bootstrap.pypa.io/get-pip.py
+        python get-pip.py --user
+        # install virtualenv and create virtual environment
+        python -m pip install --user virtualenv
+        python -m virtualenv .venv/
+    elif [[ ${TOXENV} == *"py3"* ]]; then
+        # install/upgrade current python3 with homebrew
+        if brew list --versions python3 > /dev/null; then
+            brew upgrade python3
+        else
+            brew install python3
+        fi
+        # create virtual environment
+        python3 -m venv .venv/
+    else
+        echo "unsupported $TOXENV: "${TOXENV}
+        exit 1
+    fi
+    # activate virtual environment
+    source .venv/bin/activate
+fi
+
+python -m pip install $ci_requirements
diff --git a/.travis/run.sh b/.travis/run.sh
new file mode 100755
index 0000000..6804f7d
--- /dev/null
+++ b/.travis/run.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+set -e
+set -x
+
+if [ "$TRAVIS_OS_NAME" == "osx" ]; then
+    source .venv/bin/activate
+fi
+
+tox
diff --git a/Doc/Makefile b/Doc/Makefile
new file mode 100644
index 0000000..af18dd8
--- /dev/null
+++ b/Doc/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+SPHINXPROJ    = fontTools
+SOURCEDIR     = source
+BUILDDIR      = build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
\ No newline at end of file
diff --git a/Doc/changes.txt b/Doc/changes.txt
deleted file mode 100644
index 22bcd5b..0000000
--- a/Doc/changes.txt
+++ /dev/null
@@ -1,161 +0,0 @@
-TTX/FontTools Version 2.4
-- Option to write to arbitrary files
-- Better dump format for DSIG
-- Better detection of OTF XML
-- Fix issue with Apple's kern table format
-- Fix mangling of TT glyph programs
-- Fix issues related to mona.ttf
-- Fix Windows Installer instructions
-- Fix some modern MacOS issues
-- Fix minor issues and typos
-
-TTX/FontTools Version 2.3
-
-- TrueType Collection (TTC) support
-- Python 2.6 support
-- Update Unicode data to 5.2.0
-- Couple of bug fixes
-
-TTX/FontTools Version 2.2
-
-- ClearType support
-- cmap format 1 support
-- PFA font support
-- Switched from Numeric to numpy
-- Update Unicode data to 5.1.0
-- Update AGLFN data to 1.6
-- Many bug fixes
-
-TTX/FontTools Version 2.1
-
-- Many years worth of fixes and features
-
-TTX/FontTools Version 2.0 beta 2 (released ??? 2002)
-
-- Be "forgiving" when interpreting the maxp table version field:
-  interpret any value as 1.0 if it's not 0.5. Fixes dumping of these
-  GPL fonts: http://www.freebsd.org/cgi/pds.cgi?ports/chinese/wangttf
-- Fixed ttx -l: it turned out this part of the code didn't work with
-  Python 2.2.1 and earlier. My bad to do most of my testing with a
-  different version than I shipped TTX with :-(
-- Fixed bug in ClassDef format 1 subtable (Andreas Seidel bumped into
-  this one).
-
-TTX/FontTools Version 2.0 beta 1 (released September 10 2002)
-
-- Fixed embarrassing bug: the master checksum in the head table is now
-  calculated correctly even on little-endian platforms (such as Intel).
-- Made the cmap format 4 compiler smarter: the binary data it creates is
-  now more or less as compact as possible. TTX now makes more compact
-  data than in any shipping font I've tested it with.
-- Dump glyph names as a separate "GlyphOrder" pseudo table as opposed to
-  as part of the glyf table (obviously needed for CFF-OTF's).
-- Added proper support for the CFF table.
-- Don't barf on empty tables (questionable, but "there are font out there...")
-- When writing TT glyf data, align glyphs on 4-byte boundaries. This seems
-  to be the current recommendation by MS. Also: don't barf on fonts which
-  are already 4-byte aligned.
-- Windows installer contributed bu Adam Twardoch! Yay!
-- Changed the command line interface again, now by creating one new tool
-  replacing the old ones: ttx
-  It dumps and compiles, depending on input file types. The options have
-  changed somewhat. 
-  - The -d option is back (output dir)
-  - ttcompile's -i options is now called -m (as in "merge"), to avoid clash
-    with dump's -i.
-  - The -s option ("split tables") no longer creates a directory,
-    but instead outputs a small .ttx file containing references to the
-    individual table files. This is not a true link, it's a simple file
-    name, and the referenced file should be in the same directory so
-    ttcompile can find them.
-  - compile no longer accepts a directory as input argument. Instead it
-    can parse the new "mini-ttx" format as output by "ttx -s".
-  - all arguments are input files
-- Renamed the command line programs and moved them to the Tools
-  subdirectory. They are now installed by the setup.py install script.
-- Added OpenType support. BASE, GDEF, GPOS, GSUB and JSTF are (almost)
-  fully supported. The XML output is not yet final, as I'm still
-  considering to output certain subtables in a more human-friendly
-  manner.
-- Fixed 'kern' table to correctly accept subtables it doesn't know about,
-  as well as interpreting Apple's definition of the 'kern' table headers
-  correctly.
-- Fixed bug where glyphnames were not calculated from 'cmap' if it was
-  (one of the) first tables to be decompiled. More specifically: it cmap
-  was the first to ask for a glyphID -> glyphName mapping.
-- Switched XML parsers: use expat instead of xmlproc. Should be faster.
-- Removed my UnicodeString object: I now require Python 2.0 or up, which
-  has unicode support built in.
-- Removed assert in glyf table: redundant data at the end of the table
-  is now ignored instead of raising an error. Should become a warning.
-- Fixed bug in hmtx/vmtx code that only occured if all advances were equal.
-- Fixed subtle bug in TT instruction disassembler.
-- Couple of fixes to the 'post' table.
-- Updated OS/2 table to latest spec.
-
-TTX/FontTools Version 1.0 beta 1 (released August 10 2001)
-
-- Reorganized the command line interface for ttDump.py and ttCompile.py,
-  they now behave more like "normal" command line tool, in that they accept
-  multiple input files for batch processing. 
-- ttDump.py and ttCompile.py don't silently override files anymore, but ask
-  before doing so. Can be overridden by -f.
-- Added -d <destination-directory> option to both ttDump.py and ttCompile.py.
-- Installation is now done with distutils. (Needs work for environments without
-  compilers.)
-- Updated installation instructions.
-- Added some workarounds so as to handle certain buggy fonts more gracefully.
-- Updated Unicode table to Unicode 3.0 (Thanks Antoine!)
-- Included a Python script by Adam Twardoch that adds some useful stuff to the
-  Windows registry.
-- Moved the project to SourceForge.
-
-TTX/FontTools Version 1.0 alpha 6 (released March 15 2000)
-
-- Big reorganization: made ttLib a subpackage of the new fontTools package,
-  changed several module names. Called the entire suite "FontTools"
-- Added several submodules to fontTools, some new, some older.
-- Added experimental CFF/GPOS/GSUB support to ttLib, read-only (but XML dumping
-  of GPOS/GSUB is for now disabled)
-- Fixed hdmx endian bug
-- Added -b option to ttCompile.py, it disables recalculation of bounding boxes,
-  as requested by Werner Lemberg.
-- Renamed tt2xml.pt to ttDump.py and xml2tt.py to ttCompile.py
-- Use ".ttx" as file extension instead of ".xml".
-- TTX is now the name of the XML-based *format* for TT fonts, and not just
-  an application.
-
-Version 1.0 alpha 5 (never released)
-
-- More tables supported: hdmx, vhea, vmtx
-
-Version 1.0 alpha 3 & 4 (never released)
-
-- fixed most portability issues
-- retracted the "Euro_or_currency" change from 1.0a2: it was nonsense!
-
-Version 1.0 alpha 2 (released as binary for MacOS, 2 May 1999)
-
-- genenates full FOND resources: including width table, PS
-  font name info and kern table if applicable. 
-- added cmap format 4 support. Extra: dumps Unicode char names as XML comments! 
-- added cmap format 6 support 
-- now accepts true type files starting with "true"
-  (instead of just 0x00010000 and "OTTO") 
-- 'glyf' table support is now complete: I added support for composite scale, 
-  xy-scale and two-by-two for the 'glyf' table. For now, component offset scale 
-  behaviour defaults to Apple-style. This only affects the (re)calculation of 
-  the glyph bounding box. 
-- changed "Euro" to "Euro_or_currency" in the Standard Apple Glyph order list, 
-  since we cannot tell from the 'post' table which is meant. I should probably 
-  doublecheck with a Unicode encoding if available. (This does not affect the 
-  output!)
-
-Fixed bugs: 
-- 'hhea' table is now recalculated correctly 
-- fixed wrong assumption about sfnt resource names
-
-Version 1.0 alpha 1 (27 Apr 1999)
-
-- initial binary release for MacOS
-
diff --git a/Doc/documentation.html b/Doc/documentation.html
deleted file mode 100644
index e2821fe..0000000
--- a/Doc/documentation.html
+++ /dev/null
@@ -1,104 +0,0 @@
-<HTML>
-<HEAD>
-
-<TITLE>TTX Documentation</TITLE>
-
-
-</HEAD>
-<BODY bgcolor="#FFFFFF">
-
-<H3>TTX -- From OpenType and TrueType to XML and Back</H3>
-
-<A HREF="http://fonttools.sourceforge.net/">TTX</A> is a tool for manipulating TrueType and OpenType fonts. It is written in Python and has a BSD-style, open-source licence -- see LICENSE.txt. Among other things this means you can use it free of charge. It's hosted at <A HREF="http://sourceforge.net/">sourceforge.net</A>.
-
-<P>
-TTX can dump TrueType and OpenType fonts to an XML-based text format, which is also called TTX. TTX files have a .ttx file extension.
-
-<H3>How to use TTX</H3>
-
-The TTX application works can be used in two ways, depending on what platform you run it on:
-
-<ul>
-  <li>As a command line tool (Windows/DOS, Unix, MacOSX)</li>
-  <li>By dropping files onto the application (Windows, MacOS)</li>
-</ul>
-
-<P>
-TTX detects what kind of files it is fed: it will output a .ttx file when it sees a .ttf or .otf, and it will compile a .ttf or .otf when the input file is a .ttx file. By default, the output file is created in the same folder as the input file, and will have the same name as the input file but with a different extension. TTX will <I>never</I> overwrite existing files, but if necessary will append a unique number to the output filename (before the extension), eg.: "Arial#1.ttf".
-
-<P>
-When using TTX from the command line there are a bunch of extra options, these are explained in the help text, as displayed when typing "ttx -h" at the command prompt. These additional options include:
-<ul>
-  <li>specifying the folder where the output files are created</li>
-  <li>specifying which tables to dump or which tables to exclude</li>
-  <li>merging partial .ttx files with existing .ttf or .otf files</li>
-  <li>listing brief table info isntead of dumping to .ttx</li>
-  <li>splitting tables to separate .ttx files</li>
-  <li>disabling TT instruction disassembly</li>
-</ul>
-
-<H3>The TTX file format</H3>
-
-The following tables are currently supported:
-<BLOCKQUOTE><TT>
-<!-- begin table list -->
-BASE, CBDT, CBLC, CFF, COLR, CPAL, DSIG, EBDT, EBLC, FFTM, GDEF, GMAP, GPKG, GPOS, GSUB, JSTF, LTSH, META, OS/2, SING, SVG, TSI0, TSI1, TSI2, TSI3, TSI5, TSIB, TSID, TSIJ, TSIP, TSIS, TSIV, VORG, cmap, cvt, fpgm, gasp, glyf, hdmx, head, hhea, hmtx, kern, loca, maxp, name, post, prep, sbix, vhea and vmtx
-<!-- end table list -->
-</TT></BLOCKQUOTE>
-Other tables are dumped as hexadecimal data.
-
-<P>
-TrueType fonts use glyph indices (GlyphID's) to refer to glyphs in most places.
-While this is fine in binary form, it is really hard to work with for
-humans. Therefore we use names instead.
-
-<P>The glyph names are either extracted from the 'CFF ' table or the 'post' table,
-or are derived from a Unicode 'cmap' table. In the latter case the Adobe Glyph List
-is used to calculate names based on Unicode values. If all of these mthods fail,
-names are invented based on GlyphID (eg. "glyph00142").
-
-<P>It is possible that different glyphs use the same name. If this happens,
-we force the names to be unique by appending "#n" to the name (n being an
-integer number). The original names are being kept, so this has no influence
-on a "round tripped" font.
-
-<P>Because the order in which glyphs are stored inside the TT font is
-important, we maintain an ordered list of glyph names in the font.
-
-
-<H3>Development and feedback</H3>
-
-TTX/FontTools development is ongoing, but often goes in spurts. Feature requests and bug reports are always welcome. The best place for these is currently the fonttools-discussion mailing list at SourceForge. This list is both for discussion TTX from an end-user perspective as well as TTX/FontTools development. Subscription info can be found if you follow the "Mailing Lists" link at the <A HREF="http://sourceforge.net/projects/fonttools/">SourceForge project page</A>. You can also email me directly at <A HREF="mailto:just@letterror.com">just@letterror.com</A>.
-
-<P>
-Let me take this opportunity to mention that if you have special needs (eg. custom font monipulators, dufferent table formats, etc.): I am available for contracting.
-
-<H3>Credits</H3>
-
-Windows setup script: Adam Twardoch
-<BR>Icon: Hannes Famira
-
-<H3>Acknowledgements</H3>
-
-(in alphabetical order) 
-Erik van Blokland, Petr van Blokland, Jelle Bosma, Vincent Connare, 
-Simon Daniels, Hannes Famira, Yannis Haralambous, Greg Hitchcock, John Hudson,
-Jack Jansen, Tom Kacvinsky, Antoine Leca, Werner Lemberg, Tal Leming,
-Peter Lofting, Dave Opstad, Laurence Penney, Read Roberts, Guido van Rossum, Andreas Seidel, Adam Twardoch. 
-
-<H3>Copyrights</H3>
-
-<A HREF="http://fonttools.sourceforge.net/">FontTools/TTX</A>
-<BR>1999-2003 Just van Rossum; LettError (just@letterror.com). See LICENSE.txt for the full license.
-<P>
-<A HREF="http://www.python.org/">Python</A>
-<BR>Copyright (c) 2001-2003 Python Software Foundation. All Rights Reserved.
-<BR>Copyright (c) 2000 BeOpen.com. All Rights Reserved.
-<BR>Copyright (c) 1995-2001 Corporation for National Research Initiatives. All Rights Reserved.
-<BR>Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam. All Rights Reserved.
-<P>
-<A HREF="http://www.pfdubois.com/numpy/">Numeric Python (NumPy)</A>
-<BR>Copyright (c) 1996. The Regents of the University of California. All rights reserved.
-
-</BODY>
-</HTML>
diff --git a/Doc/install.txt b/Doc/install.txt
deleted file mode 100644
index f2f3b0c..0000000
--- a/Doc/install.txt
+++ /dev/null
@@ -1,114 +0,0 @@
-TTX/FontTools
-
-TTX/FontTools is a suite of tools for manipulating fonts. It is written in
-Python and has a BSD-style, open-source licence -- see LICENSE.txt.
-It's hosted at http://sourceforge.net/.
-
-The flagship is TTX, a tool to convert OpenType and TrueType font files to
-an XML-based format (also called TTX), and back. This lets you edit TTF or
-OTF files with any text editor.
-
-The FontTools library currently reads and writes TrueType font files, reads
-PostScript Type 1 fonts and more.
-
-
-Scope
-
-TTX/FontTools' functionality is aimed towards font developers and font tool
-developers. It can of course be used to just access fonts (outlines,
-metrics, etc.) but it is not optimized for that. It will be further
-developed so it can be the core of any font editor. And that's exactly
-what it will be for our upcoming major rewrite of RoboFog, our (commercial)
-PythonPowered font editor for MacOS.
-
-
-Installation
-
-For Windows and MacOS there are easy-to-use TTX installers. The rest if this
-document is meant for people who want to use TTX/FontTools from the source.
-
-You need the following software:
-
-Python
-  The fresh versions as well as older versions (You need 2.0 or higher)
-  can be downloaded from
-     http://www.python.org/download/
-  or here
-     http://sourceforge.net/projects/python/
-  
-  Windows: grab the Windows installer, run the full install.
-  Un*x: follow the build instructions.
-  MacOS: grab the installer, run "Easy Install"
-
-The numpy extension
-  See http://numpy.scipy.org/
-
-Now run the "setup.py" script from the FontTools archive. This will install
-all the modules in the right places, as well as tries to compile the one
-(optional) C extension contained in FontTools. On Unix it also installs the
-"ttx" command line tool. This tool can also be used on Windows, but might
-need some fiddling.
-
-For instructions how to build a standalone Windows installer, see
-Windows/README.TXT. Thanks a LOT to Adam Twardoch for this essential
-contribution.
-
-For TTX usage instructions, see the file "documentation.html".
-
-
-Feedback
-
-Please join the fonttools-discussion mailing list at SourceForge. Subscription
-info can be found if you follow the "Mailing Lists" link at the SourceForge
-project page:
-  http://sourceforge.net/projects/fonttools/
-You can also email me directly at just@letterror.com.
-
-If you want to follow the development of FontTools closely, or would like to
-contribute, you can also subscribe to the fonttools-checkins mailing list.
-
-
-Anonymous VCS access
-
-The FontTools sources are also accessible here:
-  http://sourceforge.net/projects/fonttools/
-Let me know if you'd like to become a co-developer.
-
-
-Developer documentation
-
-Sorry, documentation beyond doc strings in the source code is still on my to-do list... 
-Below follows a brief overview of what's there.
-
-
-The library
-
-  Cross-platform
-     fontTools.t1Lib -- Provides a Type 1 font reader. Writing is a planned feature.
-     fontTools.ttLib -- Extensive TrueType tools. Reads and writes. This is the flagship 
-	 of FontTools, it's by far the most mature component. Contains a completely modular
-	 TTF table converter architecture. See ttLib/tables/table_API_readme.txt.
-     fontTools.afmLib -- And AFM file reader/writer.
-     fontTools.cffLib -- Reads CFF fonts. Writing is a planned feature.
-     fontTools.unicode -- A simple (but large) module that translates 
-	 Unicode values to their descriptive names. Still Unicode 2.0.
-     fontTools.agl -- Interface to the Adobe Glyph List: maps unicode values
-	 to glyph names and back.
-
-  Mac-specific
-     fontTools.fondLib -- A reader/writer class for Mac FOND resources.
-     fontTools.nfntLib -- Reads Mac NFNT bitmap font resources.
-
-
-Thank-you's
-
-(in alphabetical order) 
-Erik van Blokland, Petr van Blokland, Jelle Bosma, Vincent Connare, 
-Simon Daniels, Hannes Famira, Greg Hitchcock, John Hudson, Jack Jansen,
-Antoine Leca, Werner Lemberg, Peter Lofting, Dave Opstad, Laurence Penney, 
-Guido van Rossum, Adam Twardoch. 
-
-Copyrights
-
-FontTools/TTX -- 1999-2002 Just van Rossum; Letterror (just@letterror.com) 
-See LICENCE.txt for the full license.
diff --git a/Doc/make.bat b/Doc/make.bat
new file mode 100644
index 0000000..81a851a
--- /dev/null
+++ b/Doc/make.bat
@@ -0,0 +1,36 @@
+@ECHO OFF

+

+pushd %~dp0

+

+REM Command file for Sphinx documentation

+

+if "%SPHINXBUILD%" == "" (

+	set SPHINXBUILD=sphinx-build

+)

+set SOURCEDIR=source

+set BUILDDIR=build

+set SPHINXPROJ=fontTools

+

+if "%1" == "" goto help

+

+%SPHINXBUILD% >NUL 2>NUL

+if errorlevel 9009 (

+	echo.

+	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx

+	echo.installed, then set the SPHINXBUILD environment variable to point

+	echo.to the full path of the 'sphinx-build' executable. Alternatively you

+	echo.may add the Sphinx directory to PATH.

+	echo.

+	echo.If you don't have Sphinx installed, grab it from

+	echo.http://sphinx-doc.org/

+	exit /b 1

+)

+

+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%

+goto end

+

+:help

+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%

+

+:end

+popd

diff --git a/Doc/man/man1/ttx.1 b/Doc/man/man1/ttx.1
new file mode 100644
index 0000000..bba23b5
--- /dev/null
+++ b/Doc/man/man1/ttx.1
@@ -0,0 +1,225 @@
+.Dd May 18, 2004
+.\" ttx is not specific to any OS, but contrary to what groff_mdoc(7)
+.\" seems to imply, entirely omitting the .Os macro causes 'BSD' to
+.\" be used, so I give a zero-width space as its argument.
+.Os \&
+.\" The "FontTools Manual" argument apparently has no effect in
+.\" groff 1.18.1. I think it is a bug in the -mdoc groff package.
+.Dt TTX 1 "FontTools Manual"
+.Sh NAME
+.Nm ttx
+.Nd tool for manipulating TrueType and OpenType fonts
+.Sh SYNOPSIS
+.Nm
+.Bk
+.Op Ar option ...
+.Ek
+.Bk
+.Ar file ...
+.Ek
+.Sh DESCRIPTION
+.Nm
+is a tool for manipulating TrueType and OpenType fonts.  It can convert
+TrueType and OpenType fonts to and from an
+.Tn XML Ns -based format called
+.Tn TTX .
+.Tn TTX
+files have a
+.Ql .ttx
+extension.
+.Pp
+For each
+.Ar file
+argument it is given,
+.Nm
+detects whether it is a
+.Ql .ttf ,
+.Ql .otf
+or
+.Ql .ttx
+file and acts accordingly: if it is a
+.Ql .ttf
+or
+.Ql .otf
+file, it generates a
+.Ql .ttx
+file; if it is a
+.Ql .ttx
+file, it generates a
+.Ql .ttf
+or
+.Ql .otf
+file.
+.Pp
+By default, every output file is created in the same directory as the
+corresponding input file and with the same name except for the
+extension, which is substituted appropriately.
+.Nm
+never overwrites existing files; if necessary, it appends a suffix to
+the output file name before the extension, as in
+.Pa Arial#1.ttf .
+.Ss "General options"
+.Bl -tag -width ".Fl t Ar table"
+.It Fl h
+Display usage information.
+.It Fl d Ar dir
+Write the output files to directory
+.Ar dir
+instead of writing every output file to the same directory as the
+corresponding input file.
+.It Fl o Ar file
+Write the output to
+.Ar file
+instead of writing it to the same directory as the
+corresponding input file.
+.It Fl v
+Be verbose.  Write more messages to the standard output describing what
+is being done.
+.It Fl a
+Allow virtual glyphs ID's on compile or decompile.
+.El
+.Ss "Dump options"
+The following options control the process of dumping font files
+(TrueType or OpenType) to
+.Tn TTX
+files.
+.Bl -tag -width ".Fl t Ar table"
+.It Fl l
+List table information.  Instead of dumping the font to a
+.Tn TTX
+file, display minimal information about each table.
+.It Fl t Ar table
+Dump table
+.Ar table .
+This option may be given multiple times to dump several tables at
+once.  When not specified, all tables are dumped.
+.It Fl x Ar table
+Exclude table
+.Ar table
+from the list of tables to dump.  This option may be given multiple
+times to exclude several tables from the dump.  The
+.Fl t
+and
+.Fl x
+options are mutually exclusive.
+.It Fl s
+Split tables.  Dump each table to a separate
+.Tn TTX
+file and write (under the name that would have been used for the output
+file if the
+.Fl s
+option had not been given) one small
+.Tn TTX
+file containing references to the individual table dump files.  This
+file can be used as input to
+.Nm
+as long as the referenced files can be found in the same directory.
+.It Fl i
+.\" XXX: I suppose OpenType programs (exist and) are also affected.
+Don't disassemble TrueType instructions.  When this option is specified,
+all TrueType programs (glyph programs, the font program and the
+pre-program) are written to the
+.Tn TTX
+file as hexadecimal data instead of
+assembly.  This saves some time and results in smaller
+.Tn TTX
+files.
+.It Fl y Ar n
+When decompiling a TrueType Collection (TTC) file,
+decompile font number
+.Ar n ,
+starting from 0.
+.El
+.Ss "Compilation options"
+The following options control the process of compiling
+.Tn TTX
+files into font files (TrueType or OpenType):
+.Bl -tag -width ".Fl t Ar table"
+.It Fl m Ar fontfile
+Merge the input
+.Tn TTX
+file
+.Ar file
+with
+.Ar fontfile .
+No more than one
+.Ar file
+argument can be specified when this option is used.
+.It Fl b
+Don't recalculate glyph bounding boxes.  Use the values in the
+.Tn TTX
+file as is.
+.El
+.Sh "THE TTX FILE FORMAT"
+You can find some information about the
+.Tn TTX
+file format in
+.Pa documentation.html .
+In particular, you will find in that file the list of tables understood by
+.Nm
+and the relations between TrueType GlyphIDs and the glyph names used in
+.Tn TTX
+files.
+.Sh EXAMPLES
+In the following examples, all files are read from and written to the
+current directory.  Additionally, the name given for the output file
+assumes in every case that it did not exist before
+.Nm
+was invoked.
+.Pp
+Dump the TrueType font contained in
+.Pa FreeSans.ttf
+to
+.Pa FreeSans.ttx :
+.Pp
+.Dl ttx FreeSans.ttf
+.Pp
+Compile
+.Pa MyFont.ttx
+into a TrueType or OpenType font file:
+.Pp
+.Dl ttx MyFont.ttx
+.Pp
+List the tables in
+.Pa FreeSans.ttf
+along with some information:
+.Pp
+.Dl ttx -l FreeSans.ttf
+.Pp
+Dump the
+.Sq cmap
+table from
+.Pa FreeSans.ttf
+to
+.Pa FreeSans.ttx :
+.Pp
+.Dl ttx -t cmap FreeSans.ttf
+.Sh NOTES
+On MS\-Windows and MacOS,
+.Nm
+is available as a graphical application to which files can be dropped.
+.Sh SEE ALSO
+.Pa documentation.html
+.Pp
+.Xr fontforge 1 ,
+.Xr ftinfo 1 ,
+.Xr gfontview 1 ,
+.Xr xmbdfed 1 ,
+.Xr Font::TTF 3pm
+.Sh AUTHORS
+.Nm
+was written by
+.An -nosplit
+.An "Just van Rossum" Aq just@letterror.com .
+.Pp
+This manual page was written by
+.An "Florent Rougon" Aq f.rougon@free.fr
+for the Debian GNU/Linux system based on the existing FontTools
+documentation.  It may be freely used, modified and distributed without
+restrictions.
+.\" For Emacs:
+.\" Local Variables:
+.\" fill-column: 72
+.\" sentence-end: "[.?!][]\"')}]*\\($\\| $\\|   \\|  \\)[   \n]*"
+.\" sentence-end-double-space: t
+.\" End:
\ No newline at end of file
diff --git a/Doc/source/afmLib.rst b/Doc/source/afmLib.rst
new file mode 100644
index 0000000..f56d3c1
--- /dev/null
+++ b/Doc/source/afmLib.rst
@@ -0,0 +1,7 @@
+######
+afmLib
+######
+
+.. automodule:: fontTools.afmLib
+   :members:
+   :undoc-members:
diff --git a/Doc/source/agl.rst b/Doc/source/agl.rst
new file mode 100644
index 0000000..0ecf14d
--- /dev/null
+++ b/Doc/source/agl.rst
@@ -0,0 +1,7 @@
+###
+agl
+###
+
+.. automodule:: fontTools.agl
+   :members:
+   :undoc-members:
diff --git a/Doc/source/cffLib.rst b/Doc/source/cffLib.rst
new file mode 100644
index 0000000..364824f
--- /dev/null
+++ b/Doc/source/cffLib.rst
@@ -0,0 +1,7 @@
+######
+cffLib
+######
+
+.. automodule:: fontTools.cffLib
+   :members:
+   :undoc-members:
diff --git a/Doc/source/conf.py b/Doc/source/conf.py
new file mode 100644
index 0000000..a3b2be2
--- /dev/null
+++ b/Doc/source/conf.py
@@ -0,0 +1,156 @@
+# -*- coding: utf-8 -*-
+#
+# fontTools documentation build configuration file, created by
+# sphinx-quickstart on Thu Apr 20 11:07:39 2017.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#
+needs_sphinx = '1.3'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode']
+
+autodoc_mock_imports = ['gtk']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'fontTools'
+copyright = u'2017, Just van Rossum, Behdad Esfahbod et al.'
+author = u'Just van Rossum, Behdad Esfahbod et al.'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = u'3.10'
+# The full version, including alpha/beta/rc tags.
+release = u'3.10'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This patterns also effect to html_static_path and html_extra_path
+exclude_patterns = []
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'classic'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#
+# html_theme_options = {}
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+
+# -- Options for HTMLHelp output ------------------------------------------
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'fontToolsDoc'
+
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+    # The paper size ('letterpaper' or 'a4paper').
+    #
+    # 'papersize': 'letterpaper',
+
+    # The font size ('10pt', '11pt' or '12pt').
+    #
+    # 'pointsize': '10pt',
+
+    # Additional stuff for the LaTeX preamble.
+    #
+    # 'preamble': '',
+
+    # Latex figure (float) alignment
+    #
+    # 'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+#  author, documentclass [howto, manual, or own class]).
+latex_documents = [
+    (master_doc, 'fontTools.tex', u'fontTools Documentation',
+     u'Just van Rossum, Behdad Esfahbod et al.', 'manual'),
+]
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    (master_doc, 'fonttools', u'fontTools Documentation',
+     [author], 1)
+]
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+#  dir menu entry, description, category)
+texinfo_documents = [
+    (master_doc, 'fontTools', u'fontTools Documentation',
+     author, 'fontTools', 'A library for manipulating fonts, written in Python.',
+     'Typography'),
+]
+
diff --git a/Doc/source/designspaceLib/index.rst b/Doc/source/designspaceLib/index.rst
new file mode 100644
index 0000000..2fb4e52
--- /dev/null
+++ b/Doc/source/designspaceLib/index.rst
@@ -0,0 +1,17 @@
+##############
+designspaceLib
+##############
+
+MutatorMath started out with its own reader and writer for designspaces.
+Since then the use of designspace has broadened and it would be useful
+to have a reader and writer that are independent of a specific system.
+
+.. toctree::
+   :maxdepth: 1
+
+   readme
+   scripting
+
+.. automodule:: fontTools.designspaceLib
+   :members:
+   :undoc-members:
diff --git a/Doc/source/designspaceLib/readme.rst b/Doc/source/designspaceLib/readme.rst
new file mode 100644
index 0000000..5af5d63
--- /dev/null
+++ b/Doc/source/designspaceLib/readme.rst
@@ -0,0 +1,1052 @@
+#################################
+DesignSpaceDocument Specification
+#################################
+
+An object to read, write and edit interpolation systems for typefaces.
+
+-  the format was originally written for MutatorMath.
+-  the format is now also used in fontTools.varlib.
+-  Define sources, axes and instances.
+-  Not all values might be required by all applications.
+
+A couple of differences between things that use designspaces:
+
+-  Varlib does not support anisotropic interpolations.
+-  MutatorMath and Superpolator will extrapolate over the boundaries of
+   the axes. Varlib can not (at the moment).
+-  Varlib requires much less data to define an instance than
+   MutatorMath.
+-  The goals of Varlib and MutatorMath are different, so not all
+   attributes are always needed.
+-  Need to expand the description of FDK use of designspace files.
+
+The DesignSpaceDocument object can read and write ``.designspace`` data.
+It imports the axes, sources and instances to very basic **descriptor**
+objects that store the data in attributes. Data is added to the document
+by creating such descriptor objects, filling them with data and then
+adding them to the document. This makes it easy to integrate this object
+in different contexts.
+
+The **DesignSpaceDocument** object can be subclassed to work with
+different objects, as long as they have the same attributes.
+
+.. code:: python
+
+    from designSpaceDocument import DesignSpaceDocument
+    doc = DesignSpaceDocument()
+    doc.read("some/path/to/my.designspace")
+    doc.axes
+    doc.sources
+    doc.instances
+
+**********
+Validation
+**********
+
+Some validation is done when reading.
+
+Axes
+====
+
+-  If the ``axes`` element is available in the document then all
+   locations will check their dimensions against the defined axes. If a
+   location uses an axis that is not defined it will be ignored.
+-  If there are no ``axes`` in the document, locations will accept all
+   axis names, so that we can..
+-  Use ``doc.checkAxes()`` to reconstruct axes definitions based on the
+   ``source.location`` values. If you save the document the axes will be
+   there.
+
+Default font
+============
+
+-  The source with the ``copyInfo`` flag indicates this is the default
+   font.
+-  In mutatorMath the default font is selected automatically. A warning
+   is printed if the mutatorMath default selection differs from the one
+   set by ``copyInfo``. But the ``copyInfo`` source will be used.
+-  If no source has a ``copyInfo`` flag, mutatorMath will be used to
+   select one. This source gets its ``copyInfo`` flag set. If you save
+   the document this flag will be set.
+-  Use ``doc.checkDefault()`` to set the default font.
+
+************
+Localisation
+************
+
+Some of the descriptors support localised names. The names are stored in
+dictionaries using the language code as key. That means that there are
+now two places to store names: the old attribute and the new localised
+dictionary, ``obj.stylename`` and ``obj.localisedStyleName['en']``.
+
+*****
+Rules
+*****
+
+Rules describe designspace areas in which one glyph should be replaced by another.
+A rule has a name and a number of conditionsets. The rule also contains a list of
+glyphname pairs: the glyphs that need to be substituted. For a rule to be triggered
+**only one** of the conditionsets needs to be true, ``OR``. Within a conditionset 
+**all** conditions need to be true, ``AND``.
+
+The ``sub`` element contains a pair of glyphnames. The ``name`` attribute is the glyph that should be visible when the rule evaluates to **False**. The ``with`` attribute is the glyph that should be visible when the rule evaluates to **True**.
+
+UFO instances
+=============
+
+-  When making instances as UFOs however, we need to swap the glyphs so
+   that the original shape is still available. For instance, if a rule
+   swaps ``a`` for ``a.alt``, but a glyph that references ``a`` in a
+   component would then show the new ``a.alt``.
+-  But that can lead to unexpected results. So, if there are no rules
+   for ``adieresis`` (assuming it references ``a``) then that glyph
+   **should not change appearance**. That means that when the rule swaps
+   ``a`` and ``a.alt`` it also swaps all components that reference these
+   glyphs so they keep their appearance.
+-  The swap function also needs to take care of swapping the names in
+   kerning data.
+
+**********
+Python API
+**********
+
+SourceDescriptor object
+=======================
+
+Attributes
+----------
+
+-  ``filename``: string. A relative path to the source file, **as it is
+   in the document**. MutatorMath + Varlib.
+-  ``path``: string. Absolute path to the source file, calculated from
+   the document path and the string in the filename attr. MutatorMath +
+   Varlib.
+-  ``layerName``: string. The name of the layer in the source to look for
+   outline data. Default ``None`` which means ``foreground``.
+-  ``font``: Any Python object. Optional. Points to a representation of
+   this source font that is loaded in memory, as a Python object
+   (e.g. a ``defcon.Font`` or a ``fontTools.ttFont.TTFont``). The default
+   document reader will not fill-in this attribute, and the default
+   writer will not use this attribute. It is up to the user of
+   ``designspaceLib`` to either load the resource identified by ``filename``
+   and store it in this field, or write the contents of this field to the
+   disk and make ``filename`` point to that.
+-  ``name``: string. Optional. Unique identifier name for this source,
+   if there is one or more ``instance.glyph`` elements in the document.
+   MutatorMath.
+-  ``location``: dict. Axis values for this source. MutatorMath + Varlib
+-  ``copyLib``: bool. Indicates if the contents of the font.lib need to
+   be copied to the instances. MutatorMath.
+-  ``copyInfo`` bool. Indicates if the non-interpolating font.info needs
+   to be copied to the instances. Also indicates this source is expected
+   to be the default font. MutatorMath + Varlib
+-  ``copyGroups`` bool. Indicates if the groups need to be copied to the
+   instances. MutatorMath.
+-  ``copyFeatures`` bool. Indicates if the feature text needs to be
+   copied to the instances. MutatorMath.
+-  ``muteKerning``: bool. Indicates if the kerning data from this source
+   needs to be muted (i.e. not be part of the calculations).
+   MutatorMath.
+-  ``muteInfo``: bool. Indicated if the interpolating font.info data for
+   this source needs to be muted. MutatorMath.
+-  ``mutedGlyphNames``: list. Glyphnames that need to be muted in the
+   instances. MutatorMath.
+-  ``familyName``: string. Family name of this source. Though this data
+   can be extracted from the font, it can be efficient to have it right
+   here. Varlib.
+-  ``styleName``: string. Style name of this source. Though this data
+   can be extracted from the font, it can be efficient to have it right
+   here. Varlib.
+
+.. code:: python
+
+    doc = DesignSpaceDocument()
+    s1 = SourceDescriptor()
+    s1.path = masterPath1
+    s1.name = "master.ufo1"
+    s1.font = defcon.Font("master.ufo1")
+    s1.copyLib = True
+    s1.copyInfo = True
+    s1.copyFeatures = True
+    s1.location = dict(weight=0)
+    s1.familyName = "MasterFamilyName"
+    s1.styleName = "MasterStyleNameOne"
+    s1.mutedGlyphNames.append("A")
+    s1.mutedGlyphNames.append("Z")
+    doc.addSource(s1)
+
+.. _instance-descriptor-object:
+
+InstanceDescriptor object
+=========================
+
+.. attributes-1:
+
+Attributes
+----------
+
+-  ``filename``: string. Relative path to the instance file, **as it is
+   in the document**. The file may or may not exist. MutatorMath.
+-  ``path``: string. Absolute path to the source file, calculated from
+   the document path and the string in the filename attr. The file may
+   or may not exist. MutatorMath.
+-  ``name``: string. Unique identifier name of the instance, used to
+   identify it if it needs to be referenced from elsewhere in the
+   document.
+-  ``location``: dict. Axis values for this source. MutatorMath +
+   Varlib.
+-  ``familyName``: string. Family name of this instance. MutatorMath +
+   Varlib.
+-  ``localisedFamilyName``: dict. A dictionary of localised family name
+   strings, keyed by language code.
+-  ``styleName``: string. Style name of this source. MutatorMath +
+   Varlib.
+-  ``localisedStyleName``: dict. A dictionary of localised stylename
+   strings, keyed by language code.
+-  ``postScriptFontName``: string. Postscript fontname for this
+   instance. MutatorMath.
+-  ``styleMapFamilyName``: string. StyleMap familyname for this
+   instance. MutatorMath.
+-  ``localisedStyleMapFamilyName``: A dictionary of localised style map
+   familyname strings, keyed by language code.
+-  ``localisedStyleMapStyleName``: A dictionary of localised style map
+   stylename strings, keyed by language code.
+-  ``styleMapStyleName``: string. StyleMap stylename for this instance.
+   MutatorMath.
+-  ``glyphs``: dict for special master definitions for glyphs. If glyphs
+   need special masters (to record the results of executed rules for
+   example). MutatorMath.
+-  ``mutedGlyphNames``: list of glyphnames that should be suppressed in
+   the generation of this instance.
+-  ``kerning``: bool. Indicates if this instance needs its kerning
+   calculated. MutatorMath.
+-  ``info``: bool. Indicated if this instance needs the interpolating
+   font.info calculated.
+-  ``lib``: dict. Custom data associated with this instance.
+
+Methods
+-------
+
+These methods give easier access to the localised names.
+
+-  ``setStyleName(styleName, languageCode="en")``
+-  ``getStyleName(languageCode="en")``
+-  ``setFamilyName(familyName, languageCode="en")``
+-  ``getFamilyName(self, languageCode="en")``
+-  ``setStyleMapStyleName(styleMapStyleName, languageCode="en")``
+-  ``getStyleMapStyleName(languageCode="en")``
+-  ``setStyleMapFamilyName(styleMapFamilyName, languageCode="en")``
+-  ``getStyleMapFamilyName(languageCode="en")``
+
+Example
+-------
+
+.. code:: python
+
+    i2 = InstanceDescriptor()
+    i2.path = instancePath2
+    i2.familyName = "InstanceFamilyName"
+    i2.styleName = "InstanceStyleName"
+    i2.name = "instance.ufo2"
+    # anisotropic location
+    i2.location = dict(weight=500, width=(400,300))
+    i2.postScriptFontName = "InstancePostscriptName"
+    i2.styleMapFamilyName = "InstanceStyleMapFamilyName"
+    i2.styleMapStyleName = "InstanceStyleMapStyleName"
+    glyphMasters = [dict(font="master.ufo1", glyphName="BB", location=dict(width=20,weight=20)), dict(font="master.ufo2", glyphName="CC", location=dict(width=900,weight=900))]
+    glyphData = dict(name="arrow", unicodeValue=1234)
+    glyphData['masters'] = glyphMasters
+    glyphData['note'] = "A note about this glyph"
+    glyphData['instanceLocation'] = dict(width=100, weight=120)
+    i2.glyphs['arrow'] = glyphData
+    i2.glyphs['arrow2'] = dict(mute=False)
+    i2.lib['com.coolDesignspaceApp.specimenText'] = 'Hamburgerwhatever'
+    doc.addInstance(i2)
+
+.. _axis-descriptor-object:
+
+AxisDescriptor object
+=====================
+
+-  ``tag``: string. Four letter tag for this axis. Some might be
+   registered at the `OpenType
+   specification <https://www.microsoft.com/typography/otspec/fvar.htm#VAT>`__.
+   Privately-defined axis tags must begin with an uppercase letter and
+   use only uppercase letters or digits.
+-  ``name``: string. Name of the axis as it is used in the location
+   dicts. MutatorMath + Varlib.
+-  ``labelNames``: dict. When defining a non-registered axis, it will be
+   necessary to define user-facing readable names for the axis. Keyed by
+   xml:lang code. Varlib.
+-  ``minimum``: number. The minimum value for this axis. MutatorMath +
+   Varlib.
+-  ``maximum``: number. The maximum value for this axis. MutatorMath +
+   Varlib.
+-  ``default``: number. The default value for this axis, i.e. when a new
+   location is created, this is the value this axis will get.
+   MutatorMath + Varlib.
+-  ``map``: list of input / output values that can describe a warp of
+   user space to designspace coordinates. If no map values are present,
+   it is assumed it is [(minimum, minimum), (maximum, maximum)]. Varlib.
+
+.. code:: python
+
+    a1 = AxisDescriptor()
+    a1.minimum = 1
+    a1.maximum = 1000
+    a1.default = 400
+    a1.name = "weight"
+    a1.tag = "wght"
+    a1.labelNames[u'fa-IR'] = u"قطر"
+    a1.labelNames[u'en'] = u"Wéíght"
+    a1.map = [(1.0, 10.0), (400.0, 66.0), (1000.0, 990.0)]
+
+RuleDescriptor object
+=====================
+
+-  ``name``: string. Unique name for this rule. Can be used to
+   reference this rule data.
+-  ``conditionSets``: a list of conditionsets
+-  Each conditionset is a list of conditions.
+-  Each condition is a dict with ``name``, ``minimum`` and ``maximum`` keys.
+-  ``subs``: list of substitutions
+-  Each substitution is stored as tuples of glyphnames, e.g. ("a", "a.alt").
+
+.. code:: python
+
+    r1 = RuleDescriptor()
+    r1.name = "unique.rule.name"
+    r1.conditionsSets.append([dict(name="weight", minimum=-10, maximum=10), dict(...)])
+    r1.conditionsSets.append([dict(...), dict(...)])
+    r1.subs.append(("a", "a.alt"))
+
+.. _subclassing-descriptors:
+
+Subclassing descriptors
+=======================
+
+The DesignSpaceDocument can take subclassed Reader and Writer objects.
+This allows you to work with your own descriptors. You could subclass
+the descriptors. But as long as they have the basic attributes the
+descriptor does not need to be a subclass.
+
+.. code:: python
+
+    class MyDocReader(BaseDocReader):
+        ruleDescriptorClass = MyRuleDescriptor
+        axisDescriptorClass = MyAxisDescriptor
+        sourceDescriptorClass = MySourceDescriptor
+        instanceDescriptorClass = MyInstanceDescriptor
+
+    class MyDocWriter(BaseDocWriter):
+        ruleDescriptorClass = MyRuleDescriptor
+        axisDescriptorClass = MyAxisDescriptor
+        sourceDescriptorClass = MySourceDescriptor
+        instanceDescriptorClass = MyInstanceDescriptor
+
+    myDoc = DesignSpaceDocument(KeyedDocReader, KeyedDocWriter)
+
+**********************
+Document xml structure
+**********************
+
+-  The ``axes`` element contains one or more ``axis`` elements.
+-  The ``sources`` element contains one or more ``source`` elements.
+-  The ``instances`` element contains one or more ``instance`` elements.
+-  The ``rules`` element contains one or more ``rule`` elements.
+-  The ``lib`` element contains arbitrary data.
+
+.. code:: xml
+
+    <?xml version='1.0' encoding='utf-8'?>
+    <designspace format="3">
+        <axes>
+            <!-- define axes here -->
+            <axis../>
+        </axes>
+        <sources>
+            <!-- define masters here -->
+            <source../>
+        </sources>
+        <instances>
+            <!-- define instances here -->
+            <instance../>
+        </instances>
+        <rules>
+            <!-- define rules here -->
+            <rule../>
+        </rules>
+        <lib>
+            <dict>
+                <!-- store custom data here -->
+            </dict>
+        </lib>
+    </designspace>
+
+.. 1-axis-element:
+
+1. axis element
+===============
+
+-  Define a single axis
+-  Child element of ``axes``
+
+.. attributes-2:
+
+Attributes
+----------
+
+-  ``name``: required, string. Name of the axis that is used in the
+   location elements.
+-  ``tag``: required, string, 4 letters. Some axis tags are registered
+   in the OpenType Specification.
+-  ``minimum``: required, number. The minimum value for this axis.
+-  ``maximum``: required, number. The maximum value for this axis.
+-  ``default``: required, number. The default value for this axis.
+-  ``hidden``: optional, 0 or 1. Records whether this axis needs to be
+   hidden in interfaces.
+
+.. code:: xml
+
+    <axis name="weight" tag="wght" minimum="1" maximum="1000" default="400">
+
+.. 11-labelname-element:
+
+1.1 labelname element
+=====================
+
+-  Defines a human readable name for UI use.
+-  Optional for non-registered axis names.
+-  Can be localised with ``xml:lang``
+-  Child element of ``axis``
+
+.. attributes-3:
+
+Attributes
+----------
+
+-  ``xml:lang``: required, string. `XML language
+   definition <https://www.w3.org/International/questions/qa-when-xmllang.en>`__
+
+Value
+-----
+
+-  The natural language name of this axis.
+
+.. example-1:
+
+Example
+-------
+
+.. code:: xml
+
+    <labelname xml:lang="fa-IR">قطر</labelname>
+    <labelname xml:lang="en">Wéíght</labelname>
+
+.. 12-map-element:
+
+1.2 map element
+===============
+
+-  Defines a single node in a series of input value / output value
+   pairs.
+-  Together these values transform the designspace.
+-  Child of ``axis`` element.
+
+.. example-2:
+
+Example
+-------
+
+.. code:: xml
+
+    <map input="1.0" output="10.0" />
+    <map input="400.0" output="66.0" />
+    <map input="1000.0" output="990.0" />
+
+Example of all axis elements together:
+--------------------------------------
+
+.. code:: xml
+
+        <axes>
+            <axis default="1" maximum="1000" minimum="0" name="weight" tag="wght">
+                <labelname xml:lang="fa-IR">قطر</labelname>
+                <labelname xml:lang="en">Wéíght</labelname>
+            </axis>
+            <axis default="100" maximum="200" minimum="50" name="width" tag="wdth">
+                <map input="50.0" output="10.0" />
+                <map input="100.0" output="66.0" />
+                <map input="200.0" output="990.0" />
+            </axis>
+        </axes>
+
+.. 2-location-element:
+
+2. location element
+===================
+
+-  Defines a coordinate in the design space.
+-  Dictionary of axisname: axisvalue
+-  Used in ``source``, ``instance`` and ``glyph`` elements.
+
+.. 21-dimension-element:
+
+2.1 dimension element
+=====================
+
+-  Child element of ``location``
+
+.. attributes-4:
+
+Attributes
+----------
+
+-  ``name``: required, string. Name of the axis.
+-  ``xvalue``: required, number. The value on this axis.
+-  ``yvalue``: optional, number. Separate value for anisotropic
+   interpolations.
+
+.. example-3:
+
+Example
+-------
+
+.. code:: xml
+
+    <location>
+        <dimension name="width" xvalue="0.000000" />
+        <dimension name="weight" xvalue="0.000000" yvalue="0.003" />
+    </location>
+
+.. 3-source-element:
+
+3. source element
+=================
+
+-  Defines a single font that contributes to the designspace.
+-  Child element of ``sources``
+
+.. attributes-5:
+
+Attributes
+----------
+
+-  ``familyname``: optional, string. The family name of the source font.
+   While this could be extracted from the font data itself, it can be
+   more efficient to add it here.
+-  ``stylename``: optional, string. The style name of the source font.
+-  ``name``: required, string. A unique name that can be used to
+   identify this font if it needs to be referenced elsewhere.
+-  ``filename``: required, string. A path to the source file, relative
+   to the root path of this document. The path can be at the same level
+   as the document or lower.
+-  ``layer``: optional, string. The name of the layer in the source file.
+   If no layer attribute is given assume the foreground layer should be used.
+
+.. 31-lib-element:
+
+3.1 lib element
+===============
+
+There are two meanings for the ``lib`` element:
+
+1. Source lib
+    -  Example: ``<lib copy="1" />``
+    -  Child element of ``source``
+    -  Defines if the instances can inherit the data in the lib of this
+       source.
+    -  MutatorMath only
+
+2. Document and instance lib
+    - Example:
+
+      .. code:: xml
+
+        <lib>
+            <dict>
+                <key>...</key>
+                <string>The contents use the PLIST format.</string>
+            </dict>
+        </lib>
+
+    - Child element of ``designspace`` and ``instance``
+    - Contains arbitrary data about the whole document or about a specific
+      instance.
+    - Items in the dict need to use **reverse domain name notation** <https://en.wikipedia.org/wiki/Reverse_domain_name_notation>__
+
+.. 32-info-element:
+
+3.2 info element
+================
+
+-  ``<info copy="1" />``
+-  Child element of ``source``
+-  Defines if the instances can inherit the non-interpolating font info
+   from this source.
+-  MutatorMath + Varlib
+-  NOTE: **This presence of this element indicates this source is to be
+   the default font.**
+
+.. 33-features-element:
+
+3.3 features element
+====================
+
+-  ``<features copy="1" />``
+-  Defines if the instances can inherit opentype feature text from this
+   source.
+-  Child element of ``source``
+-  MutatorMath only
+
+.. 34-glyph-element:
+
+3.4 glyph element
+=================
+
+-  Can appear in ``source`` as well as in ``instance`` elements.
+-  In a ``source`` element this states if a glyph is to be excluded from
+   the calculation.
+-  MutatorMath only
+
+.. attributes-6:
+
+Attributes
+----------
+
+-  ``mute``: optional attribute, number 1 or 0. Indicate if this glyph
+   should be ignored as a master.
+-  ``<glyph mute="1" name="A"/>``
+-  MutatorMath only
+
+.. 35-kerning-element:
+
+3.5 kerning element
+===================
+
+-  ``<kerning mute="1" />``
+-  Can appear in ``source`` as well as in ``instance`` elements.
+
+.. attributes-7:
+
+Attributes
+----------
+
+-  ``mute``: required attribute, number 1 or 0. Indicate if the kerning
+   data from this source is to be excluded from the calculation.
+-  If the kerning element is not present, assume ``mute=0``, yes,
+   include the kerning of this source in the calculation.
+-  MutatorMath only
+
+.. example-4:
+
+Example
+-------
+
+.. code:: xml
+
+    <source familyname="MasterFamilyName" filename="masters/masterTest1.ufo" name="master.ufo1" stylename="MasterStyleNameOne">
+        <lib copy="1" />
+        <features copy="1" />
+        <info copy="1" />
+        <glyph mute="1" name="A" />
+        <glyph mute="1" name="Z" />
+        <location>
+            <dimension name="width" xvalue="0.000000" />
+            <dimension name="weight" xvalue="0.000000" />
+        </location>
+    </source>
+
+.. 4-instance-element:
+
+4. instance element
+===================
+
+-  Defines a single font that can be calculated with the designspace.
+-  Child element of ``instances``
+-  For use in Varlib the instance element really only needs the names
+   and the location. The ``glyphs`` element is not required.
+-  MutatorMath uses the ``glyphs`` element to describe how certain
+   glyphs need different masters, mainly to describe the effects of
+   conditional rules in Superpolator.
+
+.. attributes-8:
+
+Attributes
+----------
+
+-  ``familyname``: required, string. The family name of the instance
+   font. Corresponds with ``font.info.familyName``
+-  ``stylename``: required, string. The style name of the instance font.
+   Corresponds with ``font.info.styleName``
+-  ``name``: required, string. A unique name that can be used to
+   identify this font if it needs to be referenced elsewhere.
+-  ``filename``: string. Required for MutatorMath. A path to the
+   instance file, relative to the root path of this document. The path
+   can be at the same level as the document or lower.
+-  ``postscriptfontname``: string. Optional for MutatorMath. Corresponds
+   with ``font.info.postscriptFontName``
+-  ``stylemapfamilyname``: string. Optional for MutatorMath. Corresponds
+   with ``styleMapFamilyName``
+-  ``stylemapstylename``: string. Optional for MutatorMath. Corresponds
+   with ``styleMapStyleName``
+
+Example for varlib
+------------------
+
+.. code:: xml
+
+    <instance familyname="InstanceFamilyName" filename="instances/instanceTest2.ufo" name="instance.ufo2" postscriptfontname="InstancePostscriptName" stylemapfamilyname="InstanceStyleMapFamilyName" stylemapstylename="InstanceStyleMapStyleName" stylename="InstanceStyleName">
+    <location>
+        <dimension name="width" xvalue="400" yvalue="300" />
+        <dimension name="weight" xvalue="66" />
+    </location>
+    <kerning />
+    <info />
+    <lib>
+        <dict>
+            <key>com.coolDesignspaceApp.specimenText</key>
+            <string>Hamburgerwhatever</string>
+        </dict>
+    </lib>
+    </instance>
+
+.. 41-glyphs-element:
+
+4.1 glyphs element
+==================
+
+-  Container for ``glyph`` elements.
+-  Optional
+-  MutatorMath only.
+
+.. 42-glyph-element:
+
+4.2 glyph element
+=================
+
+-  Child element of ``glyphs``
+-  May contain a ``location`` element.
+
+.. attributes-9:
+
+Attributes
+----------
+
+-  ``name``: string. The name of the glyph.
+-  ``unicode``: string. Unicode values for this glyph, in hexadecimal.
+   Multiple values should be separated with a space.
+-  ``mute``: optional attribute, number 1 or 0. Indicate if this glyph
+   should be supressed in the output.
+
+.. 421-note-element:
+
+4.2.1 note element
+==================
+
+-  String. The value corresponds to glyph.note in UFO.
+
+.. 422-masters-element:
+
+4.2.2 masters element
+=====================
+
+-  Container for ``master`` elements
+-  These ``master`` elements define an alternative set of glyph masters
+   for this glyph.
+
+.. 4221-master-element:
+
+4.2.2.1 master element
+======================
+
+-  Defines a single alternative master for this glyph.
+
+4.3 Localised names for instances
+=================================
+
+Localised names for instances can be included with these simple elements
+with an ``xml:lang`` attribute:
+`XML language definition <https://www.w3.org/International/questions/qa-when-xmllang.en>`__
+
+-  stylename
+-  familyname
+-  stylemapstylename
+-  stylemapfamilyname
+
+.. example-5:
+
+Example
+-------
+
+.. code:: xml
+
+    <stylename xml:lang="fr">Demigras</stylename>
+    <stylename xml:lang="ja">半ば</stylename>
+    <familyname xml:lang="fr">Montserrat</familyname>
+    <familyname xml:lang="ja">モンセラート</familyname>
+    <stylemapstylename xml:lang="de">Standard</stylemapstylename>
+    <stylemapfamilyname xml:lang="de">Montserrat Halbfett</stylemapfamilyname>
+    <stylemapfamilyname xml:lang="ja">モンセラート SemiBold</stylemapfamilyname>
+
+.. attributes-10:
+
+Attributes
+----------
+
+-  ``glyphname``: the name of the alternate master glyph.
+-  ``source``: the identifier name of the source this master glyph needs
+   to be loaded from
+
+.. example-6:
+
+Example
+-------
+
+.. code:: xml
+
+    <instance familyname="InstanceFamilyName" filename="instances/instanceTest2.ufo" name="instance.ufo2" postscriptfontname="InstancePostscriptName" stylemapfamilyname="InstanceStyleMapFamilyName" stylemapstylename="InstanceStyleMapStyleName" stylename="InstanceStyleName">
+    <location>
+        <dimension name="width" xvalue="400" yvalue="300" />
+        <dimension name="weight" xvalue="66" />
+    </location>
+    <glyphs>
+        <glyph name="arrow2" />
+        <glyph name="arrow" unicode="0x4d2 0x4d3">
+        <location>
+            <dimension name="width" xvalue="100" />
+            <dimension name="weight" xvalue="120" />
+        </location>
+        <note>A note about this glyph</note>
+        <masters>
+            <master glyphname="BB" source="master.ufo1">
+            <location>
+                <dimension name="width" xvalue="20" />
+                <dimension name="weight" xvalue="20" />
+            </location>
+            </master>
+        </masters>
+        </glyph>
+    </glyphs>
+    <kerning />
+    <info />
+    <lib>
+        <dict>
+            <key>com.coolDesignspaceApp.specimenText</key>
+            <string>Hamburgerwhatever</string>
+        </dict>
+    </lib>
+    </instance>
+
+.. 50-rules-element:
+
+5.0 rules element
+=================
+
+-  Container for ``rule`` elements
+-  The rules are evaluated in this order.
+
+.. 51-rule-element:
+
+5.1 rule element
+================
+
+-  Defines a named rule.
+-  Each ``rule`` element contains one or more ``conditionset`` elements.
+-  Only one ``conditionset`` needs to be true to trigger the rule.
+-  All conditions in a ``conditionset`` must be true to make the ``conditionset`` true.
+-  For backwards compatibility a ``rule`` can contain ``condition`` elements outside of a conditionset. These are then understood to be part of a single, implied, ``conditionset``. Note: these conditions should be written wrapped in a conditionset.
+-  A rule element needs to contain one or more ``sub`` elements in order to be compiled to a variable font.
+-  Rules without sub elements should be ignored when compiling a font.
+-  For authoring tools it might be necessary to save designspace files without ``sub`` elements just because the work is incomplete.
+
+.. attributes-11:
+
+Attributes
+----------
+
+-  ``name``: optional, string. A unique name that can be used to
+   identify this rule if it needs to be referenced elsewhere. The name
+   is not important for compiling variable fonts.
+
+5.1.1 conditionset element
+=======================
+
+-  Child element of ``rule``
+-  Contains one or more ``condition`` elements.
+
+.. 512-condition-element:
+
+5.1.2 condition element
+=======================
+
+-  Child element of ``conditionset``
+-  Between the ``minimum`` and ``maximum`` this rule is ``True``.
+-  If ``minimum`` is not available, assume it is ``axis.minimum``.
+-  If ``maximum`` is not available, assume it is ``axis.maximum``.
+-  The condition must contain at least a minimum or maximum or both.
+
+.. attributes-12:
+
+Attributes
+----------
+
+-  ``name``: string, required. Must match one of the defined ``axis``
+   name attributes.
+-  ``minimum``: number, required*. The low value.
+-  ``maximum``: number, required*. The high value.
+
+.. 513-sub-element:
+
+5.1.3 sub element
+=================
+
+-  Child element of ``rule``.
+-  Defines which glyph to replace when the rule evaluates to **True**.
+
+.. attributes-13:
+
+Attributes
+----------
+
+-  ``name``: string, required. The name of the glyph this rule looks
+   for.
+-  ``with``: string, required. The name of the glyph it is replaced
+   with.
+
+.. example-7:
+
+Example
+-------
+
+Example with an implied ``conditionset``. Here the conditions are not
+contained in a conditionset. 
+
+.. code:: xml
+
+    <rules>
+        <rule name="named.rule.1">
+            <condition minimum="250" maximum="750" name="weight" />
+            <condition minimum="50" maximum="100" name="width" />
+            <sub name="dollar" with="dollar.alt"/>
+        </rule>
+    </rules>
+
+Example with ``conditionsets``. All conditions in a conditionset must be true.
+
+.. code:: xml
+
+    <rules>
+        <rule name="named.rule.2">
+            <conditionset>
+                <condition minimum="250" maximum="750" name="weight" />
+                <condition minimum="50" maximum="100" name="width" />
+            </conditionset>
+            <conditionset>
+                <condition ... />
+                <condition ... />
+            </conditionset>
+            <sub name="dollar" with="dollar.alt"/>
+        </rule>
+    </rules>
+
+.. 6-notes:
+
+6 Notes
+=======
+
+Paths and filenames
+-------------------
+
+A designspace file needs to store many references to UFO files.
+
+-  designspace files can be part of versioning systems and appear on
+   different computers. This means it is not possible to store absolute
+   paths.
+-  So, all paths are relative to the designspace document path.
+-  Using relative paths allows designspace files and UFO files to be
+   **near** each other, and that they can be **found** without enforcing
+   one particular structure.
+-  The **filename** attribute in the ``SourceDescriptor`` and
+   ``InstanceDescriptor`` classes stores the preferred relative path.
+-  The **path** attribute in these objects stores the absolute path. It
+   is calculated from the document path and the relative path in the
+   filename attribute when the object is created.
+-  Only the **filename** attribute is written to file.
+-  Both **filename** and **path** must use forward slashes (``/``) as
+   path separators, even on Windows.
+
+Right before we save we need to identify and respond to the following
+situations:
+
+In each descriptor, we have to do the right thing for the filename
+attribute. Before writing to file, the ``documentObject.updatePaths()``
+method prepares the paths as follows:
+
+**Case 1**
+
+::
+
+    descriptor.filename == None
+    descriptor.path == None
+
+**Action**
+
+-  write as is, descriptors will not have a filename attr. Useless, but
+   no reason to interfere.
+
+**Case 2**
+
+::
+
+    descriptor.filename == "../something"
+    descriptor.path == None
+
+**Action**
+
+-  write as is. The filename attr should not be touched.
+
+**Case 3**
+
+::
+
+    descriptor.filename == None
+    descriptor.path == "~/absolute/path/there"
+
+**Action**
+
+-  calculate the relative path for filename. We're not overwriting some
+   other value for filename, it should be fine.
+
+**Case 4**
+
+::
+
+    descriptor.filename == '../somewhere'
+    descriptor.path == "~/absolute/path/there"
+
+**Action**
+
+-  There is a conflict between the given filename, and the path. The
+   difference could have happened for any number of reasons. Assuming
+   the values were not in conflict when the object was created, either
+   could have changed. We can't guess.
+-  Assume the path attribute is more up to date. Calculate a new value
+   for filename based on the path and the document path.
+
+Recommendation for editors
+--------------------------
+
+-  If you want to explicitly set the **filename** attribute, leave the
+   path attribute empty.
+-  If you want to explicitly set the **path** attribute, leave the
+   filename attribute empty. It will be recalculated.
+-  Use ``documentObject.updateFilenameFromPath()`` to explicitly set the
+   **filename** attributes for all instance and source descriptors.
+
+.. 7-this-document:
+
+7 This document
+===============
+
+-  The package is rather new and changes are to be expected.
diff --git a/Doc/source/designspaceLib/scripting.rst b/Doc/source/designspaceLib/scripting.rst
new file mode 100644
index 0000000..2bd4a0a
--- /dev/null
+++ b/Doc/source/designspaceLib/scripting.rst
@@ -0,0 +1,253 @@
+#######################
+Scripting a designspace
+#######################
+
+It can be useful to build a designspace with a script rather than
+construct one with an interface like
+`Superpolator <http://superpolator.com>`__ or
+`DesignSpaceEditor <https://github.com/LettError/designSpaceRoboFontExtension>`__.
+
+`fontTools.designspaceLib` offers a some tools for building designspaces in
+Python. This document shows an example.
+
+********************************
+Filling-in a DesignSpaceDocument
+********************************
+
+So, suppose you installed the `fontTools` package through your favorite
+``git`` client.
+
+The ``DesignSpaceDocument`` object represents the document, whether it
+already exists or not. Make a new one:
+
+.. code:: python
+
+    from fontTools.designspaceLib import (DesignSpaceDocument, AxisDescriptor,
+                                          SourceDescriptor, InstanceDescriptor)
+    doc = DesignSpaceDocument()
+
+We want to create definitions for axes, sources and instances. That
+means there are a lot of attributes to set. The **DesignSpaceDocument
+object** uses objects to describe the axes, sources and instances. These
+are relatively simple objects, think of these as collections of
+attributes.
+
+-  Attributes of the :ref:`source-descriptor-object`
+-  Attributes of the :ref:`instance-descriptor-object`
+-  Attributes of the :ref:`axis-descriptor-object`
+-  Read about :ref:`subclassing-descriptors`
+
+Make an axis object
+===================
+
+Make a descriptor object and add it to the document.
+
+.. code:: python
+
+    a1 = AxisDescriptor()
+    a1.maximum = 1000
+    a1.minimum = 0
+    a1.default = 0
+    a1.name = "weight"
+    a1.tag = "wght"
+    doc.addAxis(a1)
+
+-  You can add as many axes as you need. OpenType has a maximum of
+   around 64K. DesignSpaceEditor has a maximum of 5.
+-  The ``name`` attribute is the name you'll be using as the axis name
+   in the locations.
+-  The ``tag`` attribute is the one of the registered `OpenType
+   Variation Axis
+   Tags <https://www.microsoft.com/typography/otspec/fvar.htm#VAT>`__
+-  The default master is expected at the intersection of all
+   default values of all axes. 
+
+Option: add label names
+-----------------------
+
+The **labelnames** attribute is intended to store localisable, human
+readable names for this axis if this is not an axis that is registered
+by OpenType. Think "The label next to the slider". The attribute is a
+dictionary. The key is the `xml language
+tag <https://www.w3.org/International/articles/language-tags/>`__, the
+value is a utf-8 string with the name. Whether or not this attribute is
+used depends on the font building tool, the operating system and the
+authoring software. This, at least, is the place to record it.
+
+.. code:: python
+
+    a1.labelNames['fa-IR'] = u"قطر"
+    a1.labelNames['en'] = u"Wéíght"
+
+Option: add a map
+-----------------
+
+The **map** attribute is a list of (input, output) mapping values
+intended for `axis variations table of
+OpenType <https://www.microsoft.com/typography/otspec/avar.htm>`__.
+
+.. code:: python
+
+    a1.map = [(0.0, 10.0), (401.0, 66.0), (1000.0, 990.0)]
+
+Make a source object
+====================
+
+A **source** is an object that points to a UFO file. It provides the
+outline geometry, kerning and font.info that we want to work with.
+
+.. code:: python
+
+    s0 = SourceDescriptor()
+    s0.path = "my/path/to/thin.ufo"
+    s0.name = "master.thin"
+    s0.location = dict(weight=0)
+    doc.addSource(s0)
+
+-  You'll need to have at least 2 sources in your document, so go ahead
+   and add another one.
+-  The **location** attribute is a dictionary with the designspace
+   location for this master.
+-  The axis names in the location have to match one of the ``axis.name``
+   values you defined before.
+-  The **path** attribute is the absolute path to an existing UFO.
+-  The **name** attribute is a unique name for this source used to keep
+   track it.
+-  The **layerName** attribute is the name of the UFO3 layer. Default None for ``foreground``.
+
+So go ahead and add another master:
+
+.. code:: python
+
+    s1 = SourceDescriptor()
+    s1.path = "my/path/to/bold.ufo"
+    s1.name = "master.bold"
+    s1.location = dict(weight=1000)
+    doc.addSource(s1)
+    
+
+Option: exclude glyphs
+----------------------
+
+By default all glyphs in a source will be processed. If you want to
+exclude certain glyphs, add their names to the ``mutedGlyphNames`` list.
+
+.. code:: python
+
+    s1.mutedGlyphNames = ["A.test", "A.old"]
+
+Make an instance object
+=======================
+
+An **instance** is description of a UFO that you want to generate with
+the designspace. For an instance you can define more things. If you want
+to generate UFO instances with MutatorMath then you can define different
+names and set flags for if you want to generate kerning and font info
+and so on. You can also set a path where to generate the instance.
+
+.. code:: python
+
+    i0 = InstanceDescriptor()
+    i0.familyName = "MyVariableFontPrototype"
+    i0.styleName = "Medium"
+    i0.path = os.path.join(root, "instances","MyVariableFontPrototype-Medium.ufo")
+    i0.location = dict(weight=500)
+    i0.kerning = True
+    i0.info = True
+    doc.addInstance(i0)
+
+-  The ``path`` attribute needs to be the absolute (real or intended)
+   path for the instance. When the document is saved this path will
+   written as relative to the path of the document.
+-  instance paths should be on the same level as the document, or in a
+   level below.
+-  Instances for MutatorMath will generate to UFO.
+-  Instances for variable fonts become **named instances**.
+
+Option: add more names
+----------------------
+
+If you want you can add a PostScript font name, a stylemap familyName
+and a stylemap styleName.
+
+.. code:: python
+
+    i0.postScriptFontName = "MyVariableFontPrototype-Medium"
+    i0.styleMapFamilyName = "MyVarProtoMedium"
+    i0.styleMapStyleName = "regular"
+
+Option: add glyph specific masters
+----------------------------------
+
+This bit is not supported by OpenType variable fonts, but it is needed
+for some designspaces intended for generating instances with
+MutatorMath. The code becomes a bit verbose, so you're invited to wrap
+this into something clever.
+
+.. code:: python
+
+    # we're making a dict with all sorts of
+    #(optional) settings for a glyph.
+    #In this example: the dollar.
+    glyphData = dict(name="dollar", unicodeValue=0x24)
+
+    # you can specify a different location for a glyph
+    glyphData['instanceLocation'] = dict(weight=500)
+
+    # You can specify different masters
+    # for this specific glyph.
+    # You can also give those masters new
+    # locations. It's a miniature designspace.
+    # Remember the "name" attribute we assigned to the sources?
+    glyphData['masters'] = [
+        dict(font="master.thin",
+            glyphName="dollar.nostroke",
+            location=dict(weight=0)),
+        dict(font="master.bold",
+            glyphName="dollar.nostroke",
+            location=dict(weight=1000)),
+        ]
+
+    # With all of that set up, store it in the instance.
+    i4.glyphs['dollar'] = glyphData
+
+******
+Saving
+******
+
+.. code:: python
+
+    path = "myprototype.designspace"
+    doc.write(path)
+
+************************
+Reading old designspaces
+************************
+
+Old designspace files might not contain ``axes`` definitions. This is
+how you reconstruct the axes from the extremes of the source locations
+
+.. code:: python
+
+    doc.checkAxes()
+
+This is how you check the default font.
+
+.. code:: python
+
+    doc.checkDefault()
+
+***********
+Generating?
+***********
+
+You can generate the UFO's with MutatorMath:
+
+.. code:: python
+
+    from mutatorMath.ufo import build
+    build("whatevs/myprototype.designspace")
+
+-  Assuming the outline data in the masters is compatible.
+
+Or you can use the file in making a **variable font** with varlib.
diff --git a/Doc/source/encodings.rst b/Doc/source/encodings.rst
new file mode 100644
index 0000000..8bcd38a
--- /dev/null
+++ b/Doc/source/encodings.rst
@@ -0,0 +1,14 @@
+#########
+encodings
+#########
+
+.. automodule:: fontTools.encodings
+   :members:
+   :undoc-members:
+
+codecs
+------
+
+.. automodule:: fontTools.encodings.codecs
+   :members:
+   :undoc-members:
diff --git a/Doc/source/feaLib.rst b/Doc/source/feaLib.rst
new file mode 100644
index 0000000..ad69217
--- /dev/null
+++ b/Doc/source/feaLib.rst
@@ -0,0 +1,43 @@
+######
+feaLib
+######
+
+.. automodule:: fontTools.feaLib
+   :members:
+   :undoc-members:
+
+ast
+---
+
+.. automodule:: fontTools.feaLib.ast
+   :members:
+   :undoc-members:
+
+builder
+-------
+
+.. automodule:: fontTools.feaLib.builder
+   :members:
+   :undoc-members:
+
+error
+-----
+
+.. automodule:: fontTools.feaLib.parser
+   :members:
+   :undoc-members:
+
+lexer
+-----
+
+.. automodule:: fontTools.feaLib.lexer
+   :members:
+   :undoc-members:
+
+parser
+------
+
+.. automodule:: fontTools.feaLib.parser
+   :members:
+   :undoc-members:
+
diff --git a/Doc/source/index.rst b/Doc/source/index.rst
new file mode 100644
index 0000000..67ec88e
--- /dev/null
+++ b/Doc/source/index.rst
@@ -0,0 +1,29 @@
+fontTools Docs
+==============
+
+.. toctree::
+   :maxdepth: 1
+
+   afmLib
+   agl
+   cffLib
+   designspaceLib/index
+   inspect
+   encodings
+   feaLib
+   merge
+   misc/index
+   pens/index
+   subset
+   t1Lib
+   ttLib/index
+   ttx
+   varLib/index
+   voltLib
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/Doc/source/inspect.rst b/Doc/source/inspect.rst
new file mode 100644
index 0000000..e0f65c2
--- /dev/null
+++ b/Doc/source/inspect.rst
@@ -0,0 +1,7 @@
+#######
+inspect
+#######
+
+.. automodule:: fontTools.inspect
+   :members:
+   :undoc-members:
diff --git a/Doc/source/merge.rst b/Doc/source/merge.rst
new file mode 100644
index 0000000..74cf9ad
--- /dev/null
+++ b/Doc/source/merge.rst
@@ -0,0 +1,7 @@
+#####
+merge
+#####
+
+.. automodule:: fontTools.merge
+   :members:
+   :undoc-members:
diff --git a/Doc/source/misc/arrayTools.rst b/Doc/source/misc/arrayTools.rst
new file mode 100644
index 0000000..acb2a51
--- /dev/null
+++ b/Doc/source/misc/arrayTools.rst
@@ -0,0 +1,7 @@
+##########
+arrayTools
+##########
+
+.. automodule:: fontTools.misc.arrayTools
+   :members:
+   :undoc-members:
diff --git a/Doc/source/misc/bezierTools.rst b/Doc/source/misc/bezierTools.rst
new file mode 100644
index 0000000..c5b4d2a
--- /dev/null
+++ b/Doc/source/misc/bezierTools.rst
@@ -0,0 +1,7 @@
+###########
+bezierTools
+###########
+
+.. automodule:: fontTools.misc.bezierTools
+   :members:
+   :undoc-members:
diff --git a/Doc/source/misc/classifyTools.rst b/Doc/source/misc/classifyTools.rst
new file mode 100644
index 0000000..b02b350
--- /dev/null
+++ b/Doc/source/misc/classifyTools.rst
@@ -0,0 +1,7 @@
+#############
+classifyTools
+#############
+
+.. automodule:: fontTools.misc.classifyTools
+   :members:
+   :undoc-members:
diff --git a/Doc/source/misc/eexec.rst b/Doc/source/misc/eexec.rst
new file mode 100644
index 0000000..8506f86
--- /dev/null
+++ b/Doc/source/misc/eexec.rst
@@ -0,0 +1,7 @@
+#####
+eexec
+#####
+
+.. automodule:: fontTools.misc.eexec
+   :members:
+   :undoc-members:
diff --git a/Doc/source/misc/encodingTools.rst b/Doc/source/misc/encodingTools.rst
new file mode 100644
index 0000000..ff29f66
--- /dev/null
+++ b/Doc/source/misc/encodingTools.rst
@@ -0,0 +1,7 @@
+#############
+encodingTools
+#############
+
+.. automodule:: fontTools.misc.encodingTools
+   :members:
+   :undoc-members:
diff --git a/Doc/source/misc/fixedTools.rst b/Doc/source/misc/fixedTools.rst
new file mode 100644
index 0000000..30a1f02
--- /dev/null
+++ b/Doc/source/misc/fixedTools.rst
@@ -0,0 +1,7 @@
+##########
+fixedTools
+##########
+
+.. automodule:: fontTools.misc.fixedTools
+   :members:
+   :undoc-members:
diff --git a/Doc/source/misc/index.rst b/Doc/source/misc/index.rst
new file mode 100644
index 0000000..29a7245
--- /dev/null
+++ b/Doc/source/misc/index.rst
@@ -0,0 +1,23 @@
+####
+misc
+####
+
+.. toctree::
+   :maxdepth: 2
+
+   arrayTools
+   bezierTools
+   classifyTools
+   eexec
+   encodingTools
+   fixedTools
+   loggingTools
+   sstruct
+   psCharStrings
+   testTools
+   textTools
+   timeTools
+   transform
+   xmlReader
+   xmlWriter
+
diff --git a/Doc/source/misc/loggingTools.rst b/Doc/source/misc/loggingTools.rst
new file mode 100644
index 0000000..fb8eab5
--- /dev/null
+++ b/Doc/source/misc/loggingTools.rst
@@ -0,0 +1,7 @@
+############
+loggingTools
+############
+
+.. automodule:: fontTools.misc.loggingTools
+   :members:
+   :undoc-members:
diff --git a/Doc/source/misc/psCharStrings.rst b/Doc/source/misc/psCharStrings.rst
new file mode 100644
index 0000000..3dc0dce
--- /dev/null
+++ b/Doc/source/misc/psCharStrings.rst
@@ -0,0 +1,7 @@
+#############
+psCharStrings
+#############
+
+.. automodule:: fontTools.misc.psCharStrings
+   :members:
+   :undoc-members:
diff --git a/Doc/source/misc/sstruct.rst b/Doc/source/misc/sstruct.rst
new file mode 100644
index 0000000..482aa23
--- /dev/null
+++ b/Doc/source/misc/sstruct.rst
@@ -0,0 +1,7 @@
+#######
+sstruct
+#######
+
+.. automodule:: fontTools.misc.sstruct
+   :members:
+   :undoc-members:
diff --git a/Doc/source/misc/testTools.rst b/Doc/source/misc/testTools.rst
new file mode 100644
index 0000000..b3215ac
--- /dev/null
+++ b/Doc/source/misc/testTools.rst
@@ -0,0 +1,7 @@
+#########
+testTools
+#########
+
+.. automodule:: fontTools.misc.testTools
+   :members:
+   :undoc-members:
diff --git a/Doc/source/misc/textTools.rst b/Doc/source/misc/textTools.rst
new file mode 100644
index 0000000..754eb67
--- /dev/null
+++ b/Doc/source/misc/textTools.rst
@@ -0,0 +1,7 @@
+#########
+textTools
+#########
+
+.. automodule:: fontTools.misc.textTools
+   :members:
+   :undoc-members:
diff --git a/Doc/source/misc/timeTools.rst b/Doc/source/misc/timeTools.rst
new file mode 100644
index 0000000..f8d1508
--- /dev/null
+++ b/Doc/source/misc/timeTools.rst
@@ -0,0 +1,7 @@
+#########
+timeTools
+#########
+
+.. automodule:: fontTools.misc.timeTools
+   :members:
+   :undoc-members:
diff --git a/Doc/source/misc/transform.rst b/Doc/source/misc/transform.rst
new file mode 100644
index 0000000..9517fe0
--- /dev/null
+++ b/Doc/source/misc/transform.rst
@@ -0,0 +1,7 @@
+#########
+transform
+#########
+
+.. automodule:: fontTools.misc.transform
+   :members:
+   :undoc-members:
diff --git a/Doc/source/misc/xmlReader.rst b/Doc/source/misc/xmlReader.rst
new file mode 100644
index 0000000..6e09354
--- /dev/null
+++ b/Doc/source/misc/xmlReader.rst
@@ -0,0 +1,7 @@
+#########
+xmlReader
+#########
+
+.. automodule:: fontTools.misc.xmlReader
+   :members:
+   :undoc-members:
diff --git a/Doc/source/misc/xmlWriter.rst b/Doc/source/misc/xmlWriter.rst
new file mode 100644
index 0000000..f488183
--- /dev/null
+++ b/Doc/source/misc/xmlWriter.rst
@@ -0,0 +1,7 @@
+#########
+xmlWriter
+#########
+
+.. automodule:: fontTools.misc.xmlWriter
+   :members:
+   :undoc-members:
diff --git a/Doc/source/pens/areaPen.rst b/Doc/source/pens/areaPen.rst
new file mode 100644
index 0000000..03a751b
--- /dev/null
+++ b/Doc/source/pens/areaPen.rst
@@ -0,0 +1,7 @@
+#######
+areaPen
+#######
+
+.. automodule:: fontTools.pens.areaPen
+   :members:
+   :undoc-members:
diff --git a/Doc/source/pens/basePen.rst b/Doc/source/pens/basePen.rst
new file mode 100644
index 0000000..f5965b1
--- /dev/null
+++ b/Doc/source/pens/basePen.rst
@@ -0,0 +1,7 @@
+#######
+basePen
+#######
+
+.. automodule:: fontTools.pens.basePen
+   :members:
+   :undoc-members:
diff --git a/Doc/source/pens/boundsPen.rst b/Doc/source/pens/boundsPen.rst
new file mode 100644
index 0000000..8de5620
--- /dev/null
+++ b/Doc/source/pens/boundsPen.rst
@@ -0,0 +1,7 @@
+#########
+boundsPen
+#########
+
+.. automodule:: fontTools.pens.boundsPen
+   :members:
+   :undoc-members:
diff --git a/Doc/source/pens/filterPen.rst b/Doc/source/pens/filterPen.rst
new file mode 100644
index 0000000..0b484a4
--- /dev/null
+++ b/Doc/source/pens/filterPen.rst
@@ -0,0 +1,7 @@
+#########
+filterPen
+#########
+
+.. automodule:: fontTools.pens.filterPen
+   :members:
+   :undoc-members:
diff --git a/Doc/source/pens/index.rst b/Doc/source/pens/index.rst
new file mode 100644
index 0000000..7e5a192
--- /dev/null
+++ b/Doc/source/pens/index.rst
@@ -0,0 +1,18 @@
+####
+pens
+####
+
+.. toctree::
+   :maxdepth: 1
+
+   basePen
+   boundsPen
+   pointInsidePen
+   filterPen
+   transformPen
+   t2CharStringPen
+   statisticsPen
+   recordingPen
+   teePen
+   areaPen
+   perimeterPen
diff --git a/Doc/source/pens/perimeterPen.rst b/Doc/source/pens/perimeterPen.rst
new file mode 100644
index 0000000..97fecca
--- /dev/null
+++ b/Doc/source/pens/perimeterPen.rst
@@ -0,0 +1,7 @@
+############
+perimeterPen
+############
+
+.. automodule:: fontTools.pens.perimeterPen
+   :members:
+   :undoc-members:
diff --git a/Doc/source/pens/pointInsidePen.rst b/Doc/source/pens/pointInsidePen.rst
new file mode 100644
index 0000000..9954e47
--- /dev/null
+++ b/Doc/source/pens/pointInsidePen.rst
@@ -0,0 +1,7 @@
+##############
+pointInsidePen
+##############
+
+.. automodule:: fontTools.pens.pointInsidePen
+   :members:
+   :undoc-members:
diff --git a/Doc/source/pens/recordingPen.rst b/Doc/source/pens/recordingPen.rst
new file mode 100644
index 0000000..69e7ceb
--- /dev/null
+++ b/Doc/source/pens/recordingPen.rst
@@ -0,0 +1,7 @@
+############
+recordingPen
+############
+
+.. automodule:: fontTools.pens.recordingPen
+   :members:
+   :undoc-members:
diff --git a/Doc/source/pens/statisticsPen.rst b/Doc/source/pens/statisticsPen.rst
new file mode 100644
index 0000000..efa1608
--- /dev/null
+++ b/Doc/source/pens/statisticsPen.rst
@@ -0,0 +1,7 @@
+#############
+statisticsPen
+#############
+
+.. automodule:: fontTools.pens.statisticsPen
+   :members:
+   :undoc-members:
diff --git a/Doc/source/pens/t2CharStringPen.rst b/Doc/source/pens/t2CharStringPen.rst
new file mode 100644
index 0000000..d58c67a
--- /dev/null
+++ b/Doc/source/pens/t2CharStringPen.rst
@@ -0,0 +1,7 @@
+###############
+t2CharStringPen
+###############
+
+.. automodule:: fontTools.pens.t2CharStringPen
+   :members:
+   :undoc-members:
diff --git a/Doc/source/pens/teePen.rst b/Doc/source/pens/teePen.rst
new file mode 100644
index 0000000..7a0313c
--- /dev/null
+++ b/Doc/source/pens/teePen.rst
@@ -0,0 +1,7 @@
+######
+teePen
+######
+
+.. automodule:: fontTools.pens.teePen
+   :members:
+   :undoc-members:
diff --git a/Doc/source/pens/transformPen.rst b/Doc/source/pens/transformPen.rst
new file mode 100644
index 0000000..5b414f8
--- /dev/null
+++ b/Doc/source/pens/transformPen.rst
@@ -0,0 +1,7 @@
+############
+transformPen
+############
+
+.. automodule:: fontTools.pens.transformPen
+   :members:
+   :undoc-members:
diff --git a/Doc/source/subset.rst b/Doc/source/subset.rst
new file mode 100644
index 0000000..a8fff95
--- /dev/null
+++ b/Doc/source/subset.rst
@@ -0,0 +1,7 @@
+######
+subset
+######
+
+.. automodule:: fontTools.subset
+   :members:
+   :undoc-members:
diff --git a/Doc/source/t1Lib.rst b/Doc/source/t1Lib.rst
new file mode 100644
index 0000000..dcc0438
--- /dev/null
+++ b/Doc/source/t1Lib.rst
@@ -0,0 +1,7 @@
+#####
+t1Lib
+#####
+
+.. automodule:: fontTools.t1Lib
+   :members:
+   :undoc-members:
diff --git a/Doc/source/ttLib/index.rst b/Doc/source/ttLib/index.rst
new file mode 100644
index 0000000..d273934
--- /dev/null
+++ b/Doc/source/ttLib/index.rst
@@ -0,0 +1,15 @@
+#####
+ttLib
+#####
+
+.. toctree::
+   :maxdepth: 1
+
+   macUtils
+   sfnt
+   tables
+   woff2
+
+.. automodule:: fontTools.ttLib
+   :members:
+   :undoc-members:
diff --git a/Doc/source/ttLib/macUtils.rst b/Doc/source/ttLib/macUtils.rst
new file mode 100644
index 0000000..cb014d7
--- /dev/null
+++ b/Doc/source/ttLib/macUtils.rst
@@ -0,0 +1,7 @@
+########
+macUtils
+########
+
+.. automodule:: fontTools.ttLib.macUtils
+   :members:
+   :undoc-members:
diff --git a/Doc/source/ttLib/sfnt.rst b/Doc/source/ttLib/sfnt.rst
new file mode 100644
index 0000000..41e3032
--- /dev/null
+++ b/Doc/source/ttLib/sfnt.rst
@@ -0,0 +1,7 @@
+####
+sfnt
+####
+
+.. automodule:: fontTools.ttLib.sfnt
+   :members:
+   :undoc-members:
diff --git a/Doc/source/ttLib/tables.rst b/Doc/source/ttLib/tables.rst
new file mode 100644
index 0000000..6ae705f
--- /dev/null
+++ b/Doc/source/ttLib/tables.rst
@@ -0,0 +1,506 @@
+######
+tables
+######
+
+.. automodule:: fontTools.ttLib.tables
+   :members:
+   :undoc-members:
+
+_a_v_a_r
+--------
+
+.. automodule:: fontTools.ttLib.tables._a_v_a_r
+   :members:
+   :undoc-members:
+
+_c_m_a_p
+--------
+
+.. automodule:: fontTools.ttLib.tables._c_m_a_p
+   :members:
+   :undoc-members:
+
+_c_v_a_r
+--------
+
+.. automodule:: fontTools.ttLib.tables._c_v_a_r
+   :members:
+   :undoc-members:
+
+_c_v_t
+------
+
+.. automodule:: fontTools.ttLib.tables._c_v_t
+   :members:
+   :undoc-members:
+
+_f_e_a_t
+--------
+
+.. automodule:: fontTools.ttLib.tables._f_e_a_t
+   :members:
+   :undoc-members:
+
+_f_p_g_m
+--------
+
+.. automodule:: fontTools.ttLib.tables._f_p_g_m
+   :members:
+   :undoc-members:
+
+_f_v_a_r
+--------
+
+.. automodule:: fontTools.ttLib.tables._f_v_a_r
+   :members:
+   :undoc-members:
+
+_g_a_s_p
+--------
+
+.. automodule:: fontTools.ttLib.tables._g_a_s_p
+   :members:
+   :undoc-members:
+
+_g_l_y_f
+--------
+
+.. automodule:: fontTools.ttLib.tables._g_l_y_f
+   :members:
+   :undoc-members:
+
+_g_v_a_r
+--------
+
+.. automodule:: fontTools.ttLib.tables._g_v_a_r
+   :members:
+   :undoc-members:
+
+_h_d_m_x
+--------
+
+.. automodule:: fontTools.ttLib.tables._h_d_m_x
+   :members:
+   :undoc-members:
+
+_h_e_a_d
+--------
+
+.. automodule:: fontTools.ttLib.tables._h_e_a_d
+   :members:
+   :undoc-members:
+
+_h_h_e_a
+--------
+
+.. automodule:: fontTools.ttLib.tables._h_h_e_a
+   :members:
+   :undoc-members:
+
+_h_m_t_x
+--------
+
+.. automodule:: fontTools.ttLib.tables._h_m_t_x
+   :members:
+   :undoc-members:
+
+_k_e_r_n
+--------
+
+.. automodule:: fontTools.ttLib.tables._k_e_r_n
+   :members:
+   :undoc-members:
+
+_l_o_c_a
+--------
+
+.. automodule:: fontTools.ttLib.tables._l_o_c_a
+   :members:
+   :undoc-members:
+
+_l_t_a_g
+--------
+
+.. automodule:: fontTools.ttLib.tables._l_t_a_g
+   :members:
+   :undoc-members:
+
+_m_a_x_p
+--------
+
+.. automodule:: fontTools.ttLib.tables._m_a_x_p
+   :members:
+   :undoc-members:
+
+_m_e_t_a
+--------
+
+.. automodule:: fontTools.ttLib.tables._m_e_t_a
+   :members:
+   :undoc-members:
+
+_n_a_m_e
+--------
+
+.. automodule:: fontTools.ttLib.tables._n_a_m_e
+   :members:
+   :undoc-members:
+
+_p_o_s_t
+--------
+
+.. automodule:: fontTools.ttLib.tables._p_o_s_t
+   :members:
+   :undoc-members:
+
+_p_r_e_p
+--------
+
+.. automodule:: fontTools.ttLib.tables._p_r_e_p
+   :members:
+   :undoc-members:
+
+_s_b_i_x
+--------
+
+.. automodule:: fontTools.ttLib.tables._s_b_i_x
+   :members:
+   :undoc-members:
+
+_t_r_a_k
+--------
+
+.. automodule:: fontTools.ttLib.tables._t_r_a_k
+   :members:
+   :undoc-members:
+
+_v_h_e_a
+--------
+
+.. automodule:: fontTools.ttLib.tables._v_h_e_a
+   :members:
+   :undoc-members:
+
+_v_m_t_x
+--------
+
+.. automodule:: fontTools.ttLib.tables._v_m_t_x
+   :members:
+   :undoc-members:
+
+asciiTable
+----------
+
+.. automodule:: fontTools.ttLib.tables.asciiTable
+   :members:
+   :undoc-members:
+
+B_A_S_E_
+--------
+
+.. automodule:: fontTools.ttLib.tables.B_A_S_E_
+   :members:
+   :undoc-members:
+
+BitmapGlyphMetrics
+------------------
+
+.. automodule:: fontTools.ttLib.tables.BitmapGlyphMetrics
+   :members:
+   :undoc-members:
+
+C_B_D_T_
+--------
+
+.. automodule:: fontTools.ttLib.tables.C_B_D_T_
+   :members:
+   :undoc-members:
+
+C_B_L_C_
+--------
+
+.. automodule:: fontTools.ttLib.tables.C_B_L_C_
+   :members:
+   :undoc-members:
+
+C_F_F_
+------
+
+.. automodule:: fontTools.ttLib.tables.C_F_F_
+   :members:
+   :undoc-members:
+
+C_F_F__2
+--------
+
+.. automodule:: fontTools.ttLib.tables.C_F_F__2
+   :members:
+   :undoc-members:
+
+C_O_L_R_
+--------
+
+.. automodule:: fontTools.ttLib.tables.C_O_L_R_
+   :members:
+   :undoc-members:
+
+C_P_A_L_
+--------
+
+.. automodule:: fontTools.ttLib.tables.C_P_A_L_
+   :members:
+   :undoc-members:
+
+D_S_I_G_
+--------
+
+.. automodule:: fontTools.ttLib.tables.D_S_I_G_
+   :members:
+   :undoc-members:
+
+DefaultTable
+------------
+
+.. automodule:: fontTools.ttLib.tables.DefaultTable
+   :members:
+   :undoc-members:
+
+E_B_D_T_
+--------
+
+.. automodule:: fontTools.ttLib.tables.E_B_D_T_
+   :members:
+   :undoc-members:
+
+E_B_L_C_
+--------
+
+.. automodule:: fontTools.ttLib.tables.E_B_L_C_
+   :members:
+   :undoc-members:
+
+F_F_T_M_
+--------
+
+.. automodule:: fontTools.ttLib.tables.F_F_T_M_
+   :members:
+   :undoc-members:
+
+G_D_E_F_
+--------
+
+.. automodule:: fontTools.ttLib.tables.G_D_E_F_
+   :members:
+   :undoc-members:
+
+G_M_A_P_
+--------
+
+.. automodule:: fontTools.ttLib.tables.G_M_A_P_
+   :members:
+   :undoc-members:
+
+G_P_K_G_
+--------
+
+.. automodule:: fontTools.ttLib.tables.G_P_K_G_
+   :members:
+   :undoc-members:
+
+G_P_O_S_
+--------
+
+.. automodule:: fontTools.ttLib.tables.G_P_O_S_
+   :members:
+   :undoc-members:
+
+G_S_U_B_
+--------
+
+.. automodule:: fontTools.ttLib.tables.G_S_U_B_
+   :members:
+   :undoc-members:
+
+H_V_A_R_
+--------
+
+.. automodule:: fontTools.ttLib.tables.H_V_A_R_
+   :members:
+   :undoc-members:
+
+J_S_T_F_
+--------
+
+.. automodule:: fontTools.ttLib.tables.J_S_T_F_
+   :members:
+   :undoc-members:
+
+L_T_S_H_
+--------
+
+.. automodule:: fontTools.ttLib.tables.L_T_S_H_
+   :members:
+   :undoc-members:
+
+M_A_T_H_
+--------
+
+.. automodule:: fontTools.ttLib.tables.M_A_T_H_
+   :members:
+   :undoc-members:
+
+M_E_T_A_
+--------
+
+.. automodule:: fontTools.ttLib.tables.M_E_T_A_
+   :members:
+   :undoc-members:
+
+M_V_A_R_
+--------
+
+.. automodule:: fontTools.ttLib.tables.M_V_A_R_
+   :members:
+   :undoc-members:
+
+O_S_2f_2
+--------
+
+.. automodule:: fontTools.ttLib.tables.O_S_2f_2
+   :members:
+   :undoc-members:
+
+otBase
+------
+
+.. automodule:: fontTools.ttLib.tables.otBase
+   :members:
+   :undoc-members:
+
+otConverters
+------------
+
+.. automodule:: fontTools.ttLib.tables.otConverters
+   :members:
+   :undoc-members:
+
+otData
+------
+
+.. automodule:: fontTools.ttLib.tables.otData
+   :members:
+   :undoc-members:
+
+otTables
+--------
+
+.. automodule:: fontTools.ttLib.tables.otTables
+   :members:
+   :undoc-members:
+
+S_I_N_G_
+--------
+
+.. automodule:: fontTools.ttLib.tables.S_I_N_G_
+   :members:
+   :undoc-members:
+
+S_T_A_T_
+--------
+
+.. automodule:: fontTools.ttLib.tables.S_T_A_T_
+   :members:
+   :undoc-members:
+
+S_V_G_
+------
+
+.. automodule:: fontTools.ttLib.tables.S_V_G_
+   :members:
+   :undoc-members:
+
+sbixGlyph
+---------
+
+.. automodule:: fontTools.ttLib.tables.sbixGlyph
+   :members:
+   :undoc-members:
+
+sbixStrike
+----------
+
+.. automodule:: fontTools.ttLib.tables.sbixStrike
+   :members:
+   :undoc-members:
+
+T_S_I__0
+--------
+
+.. automodule:: fontTools.ttLib.tables.T_S_I__0
+   :members:
+   :undoc-members:
+
+T_S_I__1
+--------
+
+.. automodule:: fontTools.ttLib.tables.T_S_I__1
+   :members:
+   :undoc-members:
+
+T_S_I__2
+--------
+
+.. automodule:: fontTools.ttLib.tables.T_S_I__2
+   :members:
+   :undoc-members:
+
+T_S_I__3
+--------
+
+.. automodule:: fontTools.ttLib.tables.T_S_I__3
+   :members:
+   :undoc-members:
+
+T_S_I__5
+--------
+
+.. automodule:: fontTools.ttLib.tables.T_S_I__5
+   :members:
+   :undoc-members:
+
+ttProgram
+---------
+
+.. automodule:: fontTools.ttLib.tables.ttProgram
+   :members:
+   :undoc-members:
+
+TupleVariation
+--------------
+
+.. automodule:: fontTools.ttLib.tables.TupleVariation
+   :members:
+   :undoc-members:
+
+V_D_M_X_
+--------
+
+.. automodule:: fontTools.ttLib.tables.V_D_M_X_
+   :members:
+   :undoc-members:
+
+V_O_R_G_
+--------
+
+.. automodule:: fontTools.ttLib.tables.V_O_R_G_
+   :members:
+   :undoc-members:
+
+V_V_A_R_
+--------
+
+.. automodule:: fontTools.ttLib.tables.V_V_A_R_
+   :members:
+   :undoc-members:
+
+
diff --git a/Doc/source/ttLib/woff2.rst b/Doc/source/ttLib/woff2.rst
new file mode 100644
index 0000000..0c464be
--- /dev/null
+++ b/Doc/source/ttLib/woff2.rst
@@ -0,0 +1,7 @@
+#####
+woff2
+#####
+
+.. automodule:: fontTools.ttLib.woff2
+   :members:
+   :undoc-members:
diff --git a/Doc/source/ttx.rst b/Doc/source/ttx.rst
new file mode 100644
index 0000000..1c90901
--- /dev/null
+++ b/Doc/source/ttx.rst
@@ -0,0 +1,7 @@
+###
+ttx
+###
+
+.. automodule:: fontTools.ttx
+   :members:
+   :undoc-members:
diff --git a/Doc/source/varLib/designspace.rst b/Doc/source/varLib/designspace.rst
new file mode 100644
index 0000000..e3bbdf7
--- /dev/null
+++ b/Doc/source/varLib/designspace.rst
@@ -0,0 +1,7 @@
+###########
+designspace
+###########
+
+.. automodule:: fontTools.varLib.designspace
+   :members:
+   :undoc-members:
diff --git a/Doc/source/varLib/index.rst b/Doc/source/varLib/index.rst
new file mode 100644
index 0000000..0e9f17b
--- /dev/null
+++ b/Doc/source/varLib/index.rst
@@ -0,0 +1,17 @@
+######
+varLib
+######
+
+.. toctree::
+   :maxdepth: 2
+
+   designspace
+   interpolatable
+   interpolate_layout
+   merger
+   models
+   mutator
+
+.. automodule:: fontTools.varLib
+   :members:
+   :undoc-members:
diff --git a/Doc/source/varLib/interpolatable.rst b/Doc/source/varLib/interpolatable.rst
new file mode 100644
index 0000000..969fb61
--- /dev/null
+++ b/Doc/source/varLib/interpolatable.rst
@@ -0,0 +1,7 @@
+##############
+interpolatable
+##############
+
+.. automodule:: fontTools.varLib.interpolatable
+   :members:
+   :undoc-members:
diff --git a/Doc/source/varLib/interpolate_layout.rst b/Doc/source/varLib/interpolate_layout.rst
new file mode 100644
index 0000000..752f748
--- /dev/null
+++ b/Doc/source/varLib/interpolate_layout.rst
@@ -0,0 +1,7 @@
+##################
+interpolate_layout
+##################
+
+.. automodule:: fontTools.varLib.interpolate_layout
+   :members:
+   :undoc-members:
diff --git a/Doc/source/varLib/merger.rst b/Doc/source/varLib/merger.rst
new file mode 100644
index 0000000..37383aa
--- /dev/null
+++ b/Doc/source/varLib/merger.rst
@@ -0,0 +1,7 @@
+######
+merger
+######
+
+.. automodule:: fontTools.varLib.merger
+   :members:
+   :undoc-members:
diff --git a/Doc/source/varLib/models.rst b/Doc/source/varLib/models.rst
new file mode 100644
index 0000000..e6c7fa8
--- /dev/null
+++ b/Doc/source/varLib/models.rst
@@ -0,0 +1,7 @@
+######
+models
+######
+
+.. automodule:: fontTools.varLib.models
+   :members:
+   :undoc-members:
diff --git a/Doc/source/varLib/mutator.rst b/Doc/source/varLib/mutator.rst
new file mode 100644
index 0000000..e606ab8
--- /dev/null
+++ b/Doc/source/varLib/mutator.rst
@@ -0,0 +1,7 @@
+#######
+mutator
+#######
+
+.. automodule:: fontTools.varLib.mutator
+   :members:
+   :undoc-members:
diff --git a/Doc/source/voltLib.rst b/Doc/source/voltLib.rst
new file mode 100644
index 0000000..5906c4c
--- /dev/null
+++ b/Doc/source/voltLib.rst
@@ -0,0 +1,35 @@
+#######
+voltLib
+#######
+
+.. automodule:: fontTools.voltLib
+   :members:
+   :undoc-members:
+
+ast
+---
+
+.. automodule:: fontTools.voltLib.ast
+   :members:
+   :undoc-members:
+
+error
+-----
+
+.. automodule:: fontTools.voltLib.parser
+   :members:
+   :undoc-members:
+
+lexer
+-----
+
+.. automodule:: fontTools.voltLib.lexer
+   :members:
+   :undoc-members:
+
+parser
+------
+
+.. automodule:: fontTools.voltLib.parser
+   :members:
+   :undoc-members:
diff --git a/Doc/ttx.1 b/Doc/ttx.1
deleted file mode 100644
index 24ce9c1..0000000
--- a/Doc/ttx.1
+++ /dev/null
@@ -1,225 +0,0 @@
-.Dd May 18, 2004
-.\" ttx is not specific to any OS, but contrary to what groff_mdoc(7)
-.\" seems to imply, entirely omitting the .Os macro causes 'BSD' to
-.\" be used, so I give a zero-width space as its argument.
-.Os \&
-.\" The "FontTools Manual" argument apparently has no effect in
-.\" groff 1.18.1. I think it is a bug in the -mdoc groff package.
-.Dt TTX 1 "FontTools Manual"
-.Sh NAME
-.Nm ttx
-.Nd tool for manipulating TrueType and OpenType fonts
-.Sh SYNOPSIS
-.Nm
-.Bk
-.Op Ar option ...
-.Ek
-.Bk
-.Ar file ...
-.Ek
-.Sh DESCRIPTION
-.Nm
-is a tool for manipulating TrueType and OpenType fonts.  It can convert
-TrueType and OpenType fonts to and from an
-.Tn XML Ns -based format called
-.Tn TTX .
-.Tn TTX
-files have a
-.Ql .ttx
-extension.
-.Pp
-For each
-.Ar file
-argument it is given,
-.Nm
-detects whether it is a
-.Ql .ttf ,
-.Ql .otf
-or
-.Ql .ttx
-file and acts accordingly: if it is a
-.Ql .ttf
-or
-.Ql .otf
-file, it generates a
-.Ql .ttx
-file; if it is a
-.Ql .ttx
-file, it generates a
-.Ql .ttf
-or
-.Ql .otf
-file.
-.Pp
-By default, every output file is created in the same directory as the
-corresponding input file and with the same name except for the
-extension, which is substituted appropriately.
-.Nm
-never overwrites existing files; if necessary, it appends a suffix to
-the output file name before the extension, as in
-.Pa Arial#1.ttf .
-.Ss "General options"
-.Bl -tag -width ".Fl t Ar table"
-.It Fl h
-Display usage information.
-.It Fl d Ar dir
-Write the output files to directory
-.Ar dir
-instead of writing every output file to the same directory as the
-corresponding input file.
-.It Fl o Ar file
-Write the output to
-.Ar file
-instead of writing it to the same directory as the
-corresponding input file.
-.It Fl v
-Be verbose.  Write more messages to the standard output describing what
-is being done.
-.It Fl a
-Allow virtual glyphs ID's on compile or decompile.
-.El
-.Ss "Dump options"
-The following options control the process of dumping font files
-(TrueType or OpenType) to
-.Tn TTX
-files.
-.Bl -tag -width ".Fl t Ar table"
-.It Fl l
-List table information.  Instead of dumping the font to a
-.Tn TTX
-file, display minimal information about each table.
-.It Fl t Ar table
-Dump table
-.Ar table .
-This option may be given multiple times to dump several tables at
-once.  When not specified, all tables are dumped.
-.It Fl x Ar table
-Exclude table
-.Ar table
-from the list of tables to dump.  This option may be given multiple
-times to exclude several tables from the dump.  The
-.Fl t
-and
-.Fl x
-options are mutually exclusive.
-.It Fl s
-Split tables.  Dump each table to a separate
-.Tn TTX
-file and write (under the name that would have been used for the output
-file if the
-.Fl s
-option had not been given) one small
-.Tn TTX
-file containing references to the individual table dump files.  This
-file can be used as input to
-.Nm
-as long as the referenced files can be found in the same directory.
-.It Fl i
-.\" XXX: I suppose OpenType programs (exist and) are also affected.
-Don't disassemble TrueType instructions.  When this option is specified,
-all TrueType programs (glyph programs, the font program and the
-pre-program) are written to the
-.Tn TTX
-file as hexadecimal data instead of
-assembly.  This saves some time and results in smaller
-.Tn TTX
-files.
-.It Fl y Ar n
-When decompiling a TrueType Collection (TTC) file,
-decompile font number
-.Ar n ,
-starting from 0.
-.El
-.Ss "Compilation options"
-The following options control the process of compiling
-.Tn TTX
-files into font files (TrueType or OpenType):
-.Bl -tag -width ".Fl t Ar table"
-.It Fl m Ar fontfile
-Merge the input
-.Tn TTX
-file
-.Ar file
-with
-.Ar fontfile .
-No more than one
-.Ar file
-argument can be specified when this option is used.
-.It Fl b
-Don't recalculate glyph bounding boxes.  Use the values in the
-.Tn TTX
-file as is.
-.El
-.Sh "THE TTX FILE FORMAT"
-You can find some information about the
-.Tn TTX
-file format in
-.Pa documentation.html .
-In particular, you will find in that file the list of tables understood by
-.Nm
-and the relations between TrueType GlyphIDs and the glyph names used in
-.Tn TTX
-files.
-.Sh EXAMPLES
-In the following examples, all files are read from and written to the
-current directory.  Additionally, the name given for the output file
-assumes in every case that it did not exist before
-.Nm
-was invoked.
-.Pp
-Dump the TrueType font contained in
-.Pa FreeSans.ttf
-to
-.Pa FreeSans.ttx :
-.Pp
-.Dl ttx FreeSans.ttf
-.Pp
-Compile
-.Pa MyFont.ttx
-into a TrueType or OpenType font file:
-.Pp
-.Dl ttx MyFont.ttx
-.Pp
-List the tables in
-.Pa FreeSans.ttf
-along with some information:
-.Pp
-.Dl ttx -l FreeSans.ttf
-.Pp
-Dump the
-.Sq cmap
-table from
-.Pa FreeSans.ttf
-to
-.Pa FreeSans.ttx :
-.Pp
-.Dl ttx -t cmap FreeSans.ttf
-.Sh NOTES
-On MS\-Windows and MacOS,
-.Nm
-is available as a graphical application to which files can be dropped.
-.Sh SEE ALSO
-.Pa documentation.html
-.Pp
-.Xr fontforge 1 ,
-.Xr ftinfo 1 ,
-.Xr gfontview 1 ,
-.Xr xmbdfed 1 ,
-.Xr Font::TTF 3pm
-.Sh AUTHORS
-.Nm
-was written by
-.An -nosplit
-.An "Just van Rossum" Aq just@letterror.com .
-.Pp
-This manual page was written by
-.An "Florent Rougon" Aq f.rougon@free.fr
-for the Debian GNU/Linux system based on the existing FontTools
-documentation.  It may be freely used, modified and distributed without
-restrictions.
-.\" For Emacs:
-.\" Local Variables:
-.\" fill-column: 72
-.\" sentence-end: "[.?!][]\"')}]*\\($\\| $\\|	\\|  \\)[ 	\n]*"
-.\" sentence-end-double-space: t
-.\" End:
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..cc63390
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Just van Rossum
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/LICENSE.external b/LICENSE.external
new file mode 100644
index 0000000..4abc7d5
--- /dev/null
+++ b/LICENSE.external
@@ -0,0 +1,148 @@
+FontTools includes the following font projects for testing purposes, which are
+under SIL Open Font License, Version 1.1:
+
+Lobster
+  Copyright (c) 2010, Pablo Impallari (www.impallari.com|impallari@gmail.com),
+  with Reserved Font Name Lobster.
+  This Font Software is licensed under the SIL Open Font License, Version 1.1.
+
+Noto Fonts
+  This Font Software is licensed under the SIL Open Font License, Version 1.1.
+
+XITS font project
+  Copyright (c) 2001-2010 by the STI Pub Companies, consisting of the American
+  Institute of Physics, the American Chemical Society, the American
+  Mathematical Society, the American Physical Society, Elsevier, Inc., and The
+  Institute of Electrical and Electronic Engineers, Inc. (www.stixfonts.org),
+  with Reserved Font Name STIX Fonts, STIX Fonts (TM) is a  trademark of The
+  Institute of Electrical and Electronics Engineers, Inc.
+
+  Portions copyright (c) 1998-2003 by MicroPress, Inc.
+  (www.micropress-inc.com), with Reserved Font Name TM Math. To obtain
+  additional mathematical fonts, please contact MicroPress, Inc., 68-30 Harrow
+  Street, Forest Hills, NY 11375, USA, Phone: (718) 575-1816.
+
+  Portions copyright (c) 1990 by Elsevier, Inc.
+
+  This Font Software is licensed under the SIL Open Font License, Version 1.1.
+
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font
+creation efforts of academic and linguistic communities, and to
+provide a free and open framework in which fonts may be shared and
+improved in partnership with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply to
+any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software
+components as distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to,
+deleting, or substituting -- in part or in whole -- any of the
+components of the Original Version, by changing formats or by porting
+the Font Software to a new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed,
+modify, redistribute, and sell modified and unmodified copies of the
+Font Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components, in
+Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the
+corresponding Copyright Holder. This restriction only applies to the
+primary font name as presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created using
+the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
+
+=====
+
+FontTools includes Adobe AGL & AGLFN, which is under 3-clauses BSD license:
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+Neither the name of Adobe Systems Incorporated nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/LICENSE.txt b/LICENSE.txt
deleted file mode 100644
index 2c90134..0000000
--- a/LICENSE.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-Copyright 1999-2004
-by Just van Rossum, Letterror, The Netherlands.
-
-                        All Rights Reserved
-
-Permission to use, copy, modify, and distribute this software and 
-its documentation for any purpose and without fee is hereby granted,
-provided that the above copyright notice appear in all copies and 
-that both that copyright notice and this permission notice appear 
-in supporting documentation, and that the names of Just van Rossum 
-or Letterror not be used in advertising or publicity pertaining to
-distribution of the software without specific, written prior
-permission.
-
-JUST VAN ROSSUM AND LETTERROR DISCLAIM ALL WARRANTIES WITH
-REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL JUST VAN ROSSUM OR 
-LETTERROR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
-DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
-PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
-TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-PERFORMANCE OF THIS SOFTWARE.
-
-
-just@letterror.com
diff --git a/Lib/fontTools/Android.bp b/Lib/fontTools/Android.bp
index 7767914..34ca987 100644
--- a/Lib/fontTools/Android.bp
+++ b/Lib/fontTools/Android.bp
@@ -11,7 +11,6 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-
 python_defaults {
     name: "fonttools_default",
     version: {
@@ -25,15 +24,11 @@
         },
     },
 }
-
 python_library_host {
     name: "fontTools",
     defaults: ["fonttools_default"],
     pkg_path: "fontTools",
     srcs: [
-        "*.py",
-        "misc/*.py",
-        "ttLib/*.py",
-        "ttLib/tables/*.py",
+        "**/*.py",
     ],
 }
diff --git a/Lib/fontTools/__init__.py b/Lib/fontTools/__init__.py
index ea07e8d..fb2b35c 100644
--- a/Lib/fontTools/__init__.py
+++ b/Lib/fontTools/__init__.py
@@ -1 +1,10 @@
-version = "2.4"
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+import logging
+from fontTools.misc.loggingTools import configLogger
+
+log = logging.getLogger(__name__)
+
+version = __version__ = "3.28.0"
+
+__all__ = ["version", "log", "configLogger"]
diff --git a/Lib/fontTools/__main__.py b/Lib/fontTools/__main__.py
new file mode 100644
index 0000000..7d45751
--- /dev/null
+++ b/Lib/fontTools/__main__.py
@@ -0,0 +1,33 @@
+from __future__ import print_function, division, absolute_import
+import sys
+
+
+def main(args=None):
+	if args is None:
+		args = sys.argv[1:]
+
+	# TODO Add help output, --help, etc.
+
+	# TODO Handle library-wide options. Eg.:
+	# --unicodedata
+	# --verbose / other logging stuff
+
+	# TODO Allow a way to run arbitrary modules? Useful for setting
+	# library-wide options and calling another library. Eg.:
+	#
+	#   $ fonttools --unicodedata=... fontmake ...
+	#
+	# This allows for a git-like command where thirdparty commands
+	# can be added.  Should we just try importing the fonttools
+	# module first and try without if it fails?
+
+	mod = 'fontTools.'+sys.argv[1]
+	sys.argv[1] = sys.argv[0] + ' ' + sys.argv[1]
+	del sys.argv[0]
+
+	import runpy
+	runpy.run_module(mod, run_name='__main__')
+
+
+if __name__ == '__main__':
+	sys.exit(main())
diff --git a/Lib/fontTools/afmLib.py b/Lib/fontTools/afmLib.py
index e679770..e0ccafe 100644
--- a/Lib/fontTools/afmLib.py
+++ b/Lib/fontTools/afmLib.py
@@ -14,47 +14,47 @@
 # regular expression to parse char lines
 charRE = re.compile(
 		"(-?\d+)"			# charnum
-		"\s*;\s*WX\s+"		# ; WX 
+		"\s*;\s*WX\s+"			# ; WX
 		"(-?\d+)"			# width
-		"\s*;\s*N\s+"		# ; N 
-		"([.A-Za-z0-9_]+)"	# charname
-		"\s*;\s*B\s+"		# ; B 
+		"\s*;\s*N\s+"			# ; N
+		"([.A-Za-z0-9_]+)"		# charname
+		"\s*;\s*B\s+"			# ; B
 		"(-?\d+)"			# left
-		"\s+"				# 
+		"\s+"
 		"(-?\d+)"			# bottom
-		"\s+"				# 
+		"\s+"
 		"(-?\d+)"			# right
-		"\s+"				# 
+		"\s+"
 		"(-?\d+)"			# top
-		"\s*;\s*"			# ; 
+		"\s*;\s*"			# ;
 		)
 
 # regular expression to parse kerning lines
 kernRE = re.compile(
-		"([.A-Za-z0-9_]+)"	# leftchar
-		"\s+"				# 
-		"([.A-Za-z0-9_]+)"	# rightchar
-		"\s+"				# 
+		"([.A-Za-z0-9_]+)"		# leftchar
+		"\s+"
+		"([.A-Za-z0-9_]+)"		# rightchar
+		"\s+"
 		"(-?\d+)"			# value
-		"\s*"				# 
+		"\s*"
 		)
 
 # regular expressions to parse composite info lines of the form:
 # Aacute 2 ; PCC A 0 0 ; PCC acute 182 211 ;
 compositeRE = re.compile(
-		"([.A-Za-z0-9_]+)"	# char name
-		"\s+"				# 
+		"([.A-Za-z0-9_]+)"		# char name
+		"\s+"
 		"(\d+)"				# number of parts
-		"\s*;\s*"			# 
+		"\s*;\s*"
 		)
 componentRE = re.compile(
 		"PCC\s+"			# PPC
-		"([.A-Za-z0-9_]+)"	# base char name
-		"\s+"				# 
+		"([.A-Za-z0-9_]+)"		# base char name
+		"\s+"
 		"(-?\d+)"			# x offset
-		"\s+"				# 
+		"\s+"
 		"(-?\d+)"			# y offset
-		"\s*;\s*"			# 
+		"\s*;\s*"
 		)
 
 preferredAttributeOrder = [
@@ -77,13 +77,14 @@
 ]
 
 
-class error(Exception): pass
+class error(Exception):
+	pass
 
 
 class AFM(object):
-	
+
 	_attrs = None
-	
+
 	_keywords = ['StartFontMetrics',
 			'EndFontMetrics',
 			'StartCharMetrics',
@@ -95,7 +96,7 @@
 			'StartComposites',
 			'EndComposites',
 			]
-	
+
 	def __init__(self, path=None):
 		self._attrs = {}
 		self._chars = {}
@@ -105,7 +106,7 @@
 		self._composites = {}
 		if path is not None:
 			self.read(path)
-	
+
 	def read(self, path):
 		lines = readlines(path)
 		for line in lines:
@@ -114,7 +115,7 @@
 			m = identifierRE.match(line)
 			if m is None:
 				raise error("syntax error in AFM file: " + repr(line))
-			
+
 			pos = m.regs[1][1]
 			word = line[:pos]
 			rest = line[pos:].strip()
@@ -128,7 +129,7 @@
 				self.parsecomposite(rest)
 			else:
 				self.parseattr(word, rest)
-	
+
 	def parsechar(self, rest):
 		m = charRE.match(rest)
 		if m is None:
@@ -140,7 +141,7 @@
 		del things[2]
 		charnum, width, l, b, r, t = (int(thing) for thing in things)
 		self._chars[charname] = charnum, width, (l, b, r, t)
-	
+
 	def parsekernpair(self, rest):
 		m = kernRE.match(rest)
 		if m is None:
@@ -151,7 +152,7 @@
 		leftchar, rightchar, value = things
 		value = int(value)
 		self._kerning[(leftchar, rightchar)] = value
-	
+
 	def parseattr(self, word, rest):
 		if word == "FontBBox":
 			l, b, r, t = [int(thing) for thing in rest.split()]
@@ -165,7 +166,7 @@
 				self._attrs[word] = rest
 			else:
 				self._attrs[word] = value
-	
+
 	def parsecomposite(self, rest):
 		m = compositeRE.match(rest)
 		if m is None:
@@ -187,19 +188,19 @@
 				break
 		assert len(components) == ncomponents
 		self._composites[charname] = components
-	
+
 	def write(self, path, sep='\r'):
 		import time
 		lines = [	"StartFontMetrics 2.0",
 				"Comment Generated by afmLib; at %s" % (
-						time.strftime("%m/%d/%Y %H:%M:%S", 
+						time.strftime("%m/%d/%Y %H:%M:%S",
 						time.localtime(time.time())))]
-		
+
 		# write comments, assuming (possibly wrongly!) they should
 		# all appear at the top
 		for comment in self._comments:
 			lines.append("Comment " + comment)
-		
+
 		# write attributes, first the ones we know about, in
 		# a preferred order
 		attrs = self._attrs
@@ -216,24 +217,24 @@
 			if attr in preferredAttributeOrder:
 				continue
 			lines.append(attr + " " + str(value))
-		
+
 		# write char metrics
 		lines.append("StartCharMetrics " + repr(len(self._chars)))
 		items = [(charnum, (charname, width, box)) for charname, (charnum, width, box) in self._chars.items()]
-		
+
 		def myKey(a):
-			"""Custom key function to make sure unencoded chars (-1) 
+			"""Custom key function to make sure unencoded chars (-1)
 			end up at the end of the list after sorting."""
 			if a[0] == -1:
 				a = (0xffff,) + a[1:]  # 0xffff is an arbitrary large number
 			return a
 		items.sort(key=myKey)
-		
+
 		for charnum, (charname, width, (l, b, r, t)) in items:
 			lines.append("C %d ; WX %d ; N %s ; B %d %d %d %d ;" %
 					(charnum, width, charname, l, b, r, t))
 		lines.append("EndCharMetrics")
-		
+
 		# write kerning info
 		lines.append("StartKernData")
 		lines.append("StartKernPairs " + repr(len(self._kerning)))
@@ -242,7 +243,7 @@
 			lines.append("KPX %s %s %d" % (leftchar, rightchar, value))
 		lines.append("EndKernPairs")
 		lines.append("EndKernData")
-		
+
 		if self._composites:
 			composites = sorted(self._composites.items())
 			lines.append("StartComposites %s" % len(self._composites))
@@ -252,45 +253,45 @@
 					line = line + " PCC %s %s %s ;" % (basechar, xoffset, yoffset)
 				lines.append(line)
 			lines.append("EndComposites")
-		
+
 		lines.append("EndFontMetrics")
-		
+
 		writelines(path, lines, sep)
-	
+
 	def has_kernpair(self, pair):
 		return pair in self._kerning
-	
+
 	def kernpairs(self):
 		return list(self._kerning.keys())
-	
+
 	def has_char(self, char):
 		return char in self._chars
-	
+
 	def chars(self):
 		return list(self._chars.keys())
-	
+
 	def comments(self):
 		return self._comments
-	
+
 	def addComment(self, comment):
 		self._comments.append(comment)
-	
+
 	def addComposite(self, glyphName, components):
 		self._composites[glyphName] = components
-	
+
 	def __getattr__(self, attr):
 		if attr in self._attrs:
 			return self._attrs[attr]
 		else:
 			raise AttributeError(attr)
-	
+
 	def __setattr__(self, attr, value):
 		# all attrs *not* starting with "_" are consider to be AFM keywords
 		if attr[:1] == "_":
 			self.__dict__[attr] = value
 		else:
 			self._attrs[attr] = value
-	
+
 	def __delattr__(self, attr):
 		# all attrs *not* starting with "_" are consider to be AFM keywords
 		if attr[:1] == "_":
@@ -303,7 +304,7 @@
 				del self._attrs[attr]
 			except KeyError:
 				raise AttributeError(attr)
-	
+
 	def __getitem__(self, key):
 		if isinstance(key, tuple):
 			# key is a tuple, return the kernpair
@@ -311,7 +312,7 @@
 		else:
 			# return the metrics instead
 			return self._chars[key]
-	
+
 	def __setitem__(self, key, value):
 		if isinstance(key, tuple):
 			# key is a tuple, set kernpair
@@ -319,7 +320,7 @@
 		else:
 			# set char metrics
 			self._chars[key] = value
-	
+
 	def __delitem__(self, key):
 		if isinstance(key, tuple):
 			# key is a tuple, del kernpair
@@ -327,7 +328,7 @@
 		else:
 			# del char metrics
 			del self._chars[key]
-	
+
 	def __repr__(self):
 		if hasattr(self, "FullName"):
 			return '<AFM object for %s>' % self.FullName
@@ -336,24 +337,14 @@
 
 
 def readlines(path):
-	f = open(path, 'rb')
-	data = f.read()
-	f.close()
-	# read any text file, regardless whether it's formatted for Mac, Unix or Dos
-	sep = ""
-	if '\r' in data:
-		sep = sep + '\r'	# mac or dos
-	if '\n' in data:
-		sep = sep + '\n'	# unix or dos
-	return data.split(sep)
+	with open(path, "r", encoding="ascii") as f:
+		data = f.read()
+	return data.splitlines()
 
 def writelines(path, lines, sep='\r'):
-	f = open(path, 'wb')
-	for line in lines:
-		f.write(line + sep)
-	f.close()
-	
-	
+	with open(path, "w", encoding="ascii", newline=sep) as f:
+		f.write("\n".join(lines) + "\n")
+
 
 if __name__ == "__main__":
 	import EasyDialogs
@@ -374,4 +365,3 @@
 		#print afm.kernpairs()
 		print(afm)
 		afm.write(path + ".muck")
-
diff --git a/Lib/fontTools/agl.py b/Lib/fontTools/agl.py
index 5f20f51..ec1a1b0 100644
--- a/Lib/fontTools/agl.py
+++ b/Lib/fontTools/agl.py
@@ -1,8 +1,12 @@
+# -*- coding: utf-8 -*-
 # The table below is taken from
 # http://www.adobe.com/devnet/opentype/archives/aglfn.txt
 
-from __future__ import print_function, division, absolute_import
+from __future__ import (print_function, division, absolute_import,
+                        unicode_literals)
 from fontTools.misc.py23 import *
+import re
+
 
 _aglText = """\
 # -----------------------------------------------------------
@@ -705,18 +709,19 @@
 """
 
 
-AGLError = "AGLError"
+class AGLError(Exception):
+	pass
 
 AGL2UV = {}
 UV2AGL = {}
 
 def _builddicts():
 	import re
-	
+
 	lines = _aglText.splitlines()
-	
+
 	parseAGL_RE = re.compile("([0-9A-F]{4});([A-Za-z_0-9.]+);.*?$")
-	
+
 	for line in lines:
 		if not line or line[:1] == '#':
 			continue
@@ -726,12 +731,145 @@
 		unicode = m.group(1)
 		assert len(unicode) == 4
 		unicode = int(unicode, 16)
-		glyphName = m.group(2)
+		glyphName = tostr(m.group(2))
 		if glyphName in AGL2UV:
 			# the above table contains identical duplicates
 			assert AGL2UV[glyphName] == unicode
 		else:
 			AGL2UV[glyphName] = unicode
 		UV2AGL[unicode] = glyphName
-	
+
 _builddicts()
+
+
+def toUnicode(glyph, isZapfDingbats=False):
+	"""Convert glyph names to Unicode, such as 'longs_t.oldstyle' --> u'ſt'
+
+	If isZapfDingbats is True, the implementation recognizes additional
+	glyph names (as required by the AGL specification).
+	"""
+	# https://github.com/adobe-type-tools/agl-specification#2-the-mapping
+	#
+	# 1. Drop all the characters from the glyph name starting with
+	#    the first occurrence of a period (U+002E; FULL STOP), if any.
+	glyph = glyph.split(".", 1)[0]
+
+	# 2. Split the remaining string into a sequence of components,
+	#    using underscore (U+005F; LOW LINE) as the delimiter.
+	components = glyph.split("_")
+
+	# 3. Map each component to a character string according to the
+	#    procedure below, and concatenate those strings; the result
+	#     is the character string to which the glyph name is mapped.
+	result = [_glyphComponentToUnicode(c, isZapfDingbats)
+                  for c in components]
+	return "".join(result)
+
+
+def _glyphComponentToUnicode(component, isZapfDingbats):
+	# If the font is Zapf Dingbats (PostScript FontName: ZapfDingbats),
+	# and the component is in the ITC Zapf Dingbats Glyph List, then
+	# map it to the corresponding character in that list.
+	dingbat = _zapfDingbatsToUnicode(component) if isZapfDingbats else None
+	if dingbat:
+		return dingbat
+
+	# Otherwise, if the component is in AGL, then map it
+	# to the corresponding character in that list.
+	#
+	# TODO: We currently use the AGLFN (Adobe glyph list for new fonts),
+	# although the spec actually mandates the legacy AGL which is
+	# a superset of the AGLFN.
+	# https://github.com/fonttools/fonttools/issues/775
+	uchar = AGL2UV.get(component)
+	if uchar:
+		return unichr(uchar)
+
+	# Otherwise, if the component is of the form "uni" (U+0075,
+	# U+006E, and U+0069) followed by a sequence of uppercase
+	# hexadecimal digits (0–9 and A–F, meaning U+0030 through
+	# U+0039 and U+0041 through U+0046), if the length of that
+	# sequence is a multiple of four, and if each group of four
+	# digits represents a value in the ranges 0000 through D7FF
+	# or E000 through FFFF, then interpret each as a Unicode scalar
+	# value and map the component to the string made of those
+	# scalar values. Note that the range and digit-length
+	# restrictions mean that the "uni" glyph name prefix can be
+	# used only with UVs in the Basic Multilingual Plane (BMP).
+	uni = _uniToUnicode(component)
+	if uni:
+		return uni
+
+	# Otherwise, if the component is of the form "u" (U+0075)
+	# followed by a sequence of four to six uppercase hexadecimal
+	# digits (0–9 and A–F, meaning U+0030 through U+0039 and
+	# U+0041 through U+0046), and those digits represents a value
+	# in the ranges 0000 through D7FF or E000 through 10FFFF, then
+	# interpret it as a Unicode scalar value and map the component
+	# to the string made of this scalar value.
+	uni = _uToUnicode(component)
+	if uni:
+		return uni
+
+	# Otherwise, map the component to an empty string.
+	return ''
+
+
+# https://github.com/adobe-type-tools/agl-aglfn/blob/master/zapfdingbats.txt
+_AGL_ZAPF_DINGBATS = (
+	" ✁✂✄☎✆✝✞✟✠✡☛☞✌✍✎✏✑✒✓✔✕✖✗✘✙✚✛✜✢✣✤✥✦✧★✩✪✫✬✭✮✯✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀"
+	"❁❂❃❄❅❆❇❈❉❊❋●❍■❏❑▲▼◆❖ ◗❘❙❚❯❱❲❳❨❩❬❭❪❫❴❵❛❜❝❞❡❢❣❤✐❥❦❧♠♥♦♣    ✉✈✇"
+	"①②③④⑤⑥⑦⑧⑨⑩❶❷❸❹❺❻❼❽❾❿➀➁➂➃➄➅➆➇➈➉➊➋➌➍➎➏➐➑➒➓➔→➣↔"
+	"↕➙➛➜➝➞➟➠➡➢➤➥➦➧➨➩➫➭➯➲➳➵➸➺➻➼➽➾➚➪➶➹➘➴➷➬➮➱✃❐❒❮❰")
+
+
+def _zapfDingbatsToUnicode(glyph):
+	"""Helper for toUnicode()."""
+	if len(glyph) < 2 or glyph[0] != 'a':
+		return None
+	try:
+		gid = int(glyph[1:])
+	except ValueError:
+		return None
+	if gid < 0 or gid >= len(_AGL_ZAPF_DINGBATS):
+		return None
+	uchar = _AGL_ZAPF_DINGBATS[gid]
+	return uchar if uchar != ' ' else None
+
+
+_re_uni = re.compile("^uni([0-9A-F]+)$")
+
+
+def _uniToUnicode(component):
+	"""Helper for toUnicode() to handle "uniABCD" components."""
+	match = _re_uni.match(component)
+	if match is None:
+		return None
+	digits = match.group(1)
+	if len(digits) % 4 != 0:
+		return None
+	chars = [int(digits[i : i + 4], 16)
+                 for i in range(0, len(digits), 4)]
+	if any(c >= 0xD800 and c <= 0xDFFF for c in chars):
+		# The AGL specification explicitly excluded surrogate pairs.
+		return None
+	return ''.join([unichr(c) for c in chars])
+
+
+_re_u = re.compile("^u([0-9A-F]{4,6})$")
+
+
+def _uToUnicode(component):
+	"""Helper for toUnicode() to handle "u1ABCD" components."""
+	match = _re_u.match(component)
+	if match is None:
+		return None
+	digits = match.group(1)
+	try:
+		value = int(digits, 16)
+	except ValueError:
+		return None
+	if ((value >= 0x0000 and value <= 0xD7FF) or
+	    (value >= 0xE000 and value <= 0x10FFFF)):
+		return unichr(value)
+	return None
diff --git a/Lib/fontTools/cffLib.py b/Lib/fontTools/cffLib.py
deleted file mode 100644
index cd5a92f..0000000
--- a/Lib/fontTools/cffLib.py
+++ /dev/null
@@ -1,1811 +0,0 @@
-"""cffLib.py -- read/write tools for Adobe CFF fonts."""
-
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
-from fontTools.misc import sstruct
-from fontTools.misc import psCharStrings
-from fontTools.misc.textTools import safeEval
-import struct
-
-DEBUG = 0
-
-
-cffHeaderFormat = """
-	major:   B
-	minor:   B
-	hdrSize: B
-	offSize: B
-"""
-
-class CFFFontSet(object):
-	
-	def __init__(self):
-		pass
-	
-	def decompile(self, file, otFont):
-		sstruct.unpack(cffHeaderFormat, file.read(4), self)
-		assert self.major == 1 and self.minor == 0, \
-				"unknown CFF format: %d.%d" % (self.major, self.minor)
-		
-		file.seek(self.hdrSize)
-		self.fontNames = list(Index(file))
-		self.topDictIndex = TopDictIndex(file)
-		self.strings = IndexedStrings(file)
-		self.GlobalSubrs = GlobalSubrsIndex(file)
-		self.topDictIndex.strings = self.strings
-		self.topDictIndex.GlobalSubrs = self.GlobalSubrs
-	
-	def __len__(self):
-		return len(self.fontNames)
-	
-	def keys(self):
-		return list(self.fontNames)
-	
-	def values(self):
-		return self.topDictIndex
-	
-	def __getitem__(self, name):
-		try:
-			index = self.fontNames.index(name)
-		except ValueError:
-			raise KeyError(name)
-		return self.topDictIndex[index]
-	
-	def compile(self, file, otFont):
-		strings = IndexedStrings()
-		writer = CFFWriter()
-		writer.add(sstruct.pack(cffHeaderFormat, self))
-		fontNames = Index()
-		for name in self.fontNames:
-			fontNames.append(name)
-		writer.add(fontNames.getCompiler(strings, None))
-		topCompiler = self.topDictIndex.getCompiler(strings, None)
-		writer.add(topCompiler)
-		writer.add(strings.getCompiler())
-		writer.add(self.GlobalSubrs.getCompiler(strings, None))
-		
-		for topDict in self.topDictIndex:
-			if not hasattr(topDict, "charset") or topDict.charset is None:
-				charset = otFont.getGlyphOrder()
-				topDict.charset = charset
-		
-		for child in topCompiler.getChildren(strings):
-			writer.add(child)
-		
-		writer.toFile(file)
-	
-	def toXML(self, xmlWriter, progress=None):
-		for fontName in self.fontNames:
-			xmlWriter.begintag("CFFFont", name=tostr(fontName))
-			xmlWriter.newline()
-			font = self[fontName]
-			font.toXML(xmlWriter, progress)
-			xmlWriter.endtag("CFFFont")
-			xmlWriter.newline()
-		xmlWriter.newline()
-		xmlWriter.begintag("GlobalSubrs")
-		xmlWriter.newline()
-		self.GlobalSubrs.toXML(xmlWriter, progress)
-		xmlWriter.endtag("GlobalSubrs")
-		xmlWriter.newline()
-	
-	def fromXML(self, name, attrs, content):
-		if not hasattr(self, "GlobalSubrs"):
-			self.GlobalSubrs = GlobalSubrsIndex()
-			self.major = 1
-			self.minor = 0
-			self.hdrSize = 4
-			self.offSize = 4  # XXX ??
-		if name == "CFFFont":
-			if not hasattr(self, "fontNames"):
-				self.fontNames = []
-				self.topDictIndex = TopDictIndex()
-			fontName = attrs["name"]
-			topDict = TopDict(GlobalSubrs=self.GlobalSubrs)
-			topDict.charset = None  # gets filled in later
-			self.fontNames.append(fontName)
-			self.topDictIndex.append(topDict)
-			for element in content:
-				if isinstance(element, basestring):
-					continue
-				name, attrs, content = element
-				topDict.fromXML(name, attrs, content)
-		elif name == "GlobalSubrs":
-			for element in content:
-				if isinstance(element, basestring):
-					continue
-				name, attrs, content = element
-				subr = psCharStrings.T2CharString()
-				subr.fromXML(name, attrs, content)
-				self.GlobalSubrs.append(subr)
-
-
-class CFFWriter(object):
-	
-	def __init__(self):
-		self.data = []
-	
-	def add(self, table):
-		self.data.append(table)
-	
-	def toFile(self, file):
-		lastPosList = None
-		count = 1
-		while True:
-			if DEBUG:
-				print("CFFWriter.toFile() iteration:", count)
-			count = count + 1
-			pos = 0
-			posList = [pos]
-			for item in self.data:
-				if hasattr(item, "getDataLength"):
-					endPos = pos + item.getDataLength()
-				else:
-					endPos = pos + len(item)
-				if hasattr(item, "setPos"):
-					item.setPos(pos, endPos)
-				pos = endPos
-				posList.append(pos)
-			if posList == lastPosList:
-				break
-			lastPosList = posList
-		if DEBUG:
-			print("CFFWriter.toFile() writing to file.")
-		begin = file.tell()
-		posList = [0]
-		for item in self.data:
-			if hasattr(item, "toFile"):
-				item.toFile(file)
-			else:
-				file.write(item)
-			posList.append(file.tell() - begin)
-		assert posList == lastPosList
-
-
-def calcOffSize(largestOffset):
-	if largestOffset < 0x100:
-		offSize = 1
-	elif largestOffset < 0x10000:
-		offSize = 2
-	elif largestOffset < 0x1000000:
-		offSize = 3
-	else:
-		offSize = 4
-	return offSize
-
-
-class IndexCompiler(object):
-	
-	def __init__(self, items, strings, parent):
-		self.items = self.getItems(items, strings)
-		self.parent = parent
-	
-	def getItems(self, items, strings):
-		return items
-	
-	def getOffsets(self):
-		pos = 1
-		offsets = [pos]
-		for item in self.items:
-			if hasattr(item, "getDataLength"):
-				pos = pos + item.getDataLength()
-			else:
-				pos = pos + len(item)
-			offsets.append(pos)
-		return offsets
-	
-	def getDataLength(self):
-		lastOffset = self.getOffsets()[-1]
-		offSize = calcOffSize(lastOffset)
-		dataLength = (
-			2 +                                # count
-			1 +                                # offSize
-			(len(self.items) + 1) * offSize +  # the offsets
-			lastOffset - 1                     # size of object data
-		)
-		return dataLength
-	
-	def toFile(self, file):
-		offsets = self.getOffsets()
-		writeCard16(file, len(self.items))
-		offSize = calcOffSize(offsets[-1])
-		writeCard8(file, offSize)
-		offSize = -offSize
-		pack = struct.pack
-		for offset in offsets:
-			binOffset = pack(">l", offset)[offSize:]
-			assert len(binOffset) == -offSize
-			file.write(binOffset)
-		for item in self.items:
-			if hasattr(item, "toFile"):
-				item.toFile(file)
-			else:
-				file.write(tobytes(item, encoding="latin1"))
-
-
-class IndexedStringsCompiler(IndexCompiler):
-	
-	def getItems(self, items, strings):
-		return items.strings
-
-
-class TopDictIndexCompiler(IndexCompiler):
-	
-	def getItems(self, items, strings):
-		out = []
-		for item in items:
-			out.append(item.getCompiler(strings, self))
-		return out
-	
-	def getChildren(self, strings):
-		children = []
-		for topDict in self.items:
-			children.extend(topDict.getChildren(strings))
-		return children
-
-
-class FDArrayIndexCompiler(IndexCompiler):
-	
-	def getItems(self, items, strings):
-		out = []
-		for item in items:
-			out.append(item.getCompiler(strings, self))
-		return out
-	
-	def getChildren(self, strings):
-		children = []
-		for fontDict in self.items:
-			children.extend(fontDict.getChildren(strings))
-		return children
-
-	def toFile(self, file):
-		offsets = self.getOffsets()
-		writeCard16(file, len(self.items))
-		offSize = calcOffSize(offsets[-1])
-		writeCard8(file, offSize)
-		offSize = -offSize
-		pack = struct.pack
-		for offset in offsets:
-			binOffset = pack(">l", offset)[offSize:]
-			assert len(binOffset) == -offSize
-			file.write(binOffset)
-		for item in self.items:
-			if hasattr(item, "toFile"):
-				item.toFile(file)
-			else:
-				file.write(item)
-
-	def setPos(self, pos, endPos):
-		self.parent.rawDict["FDArray"] = pos
-
-
-class GlobalSubrsCompiler(IndexCompiler):
-	def getItems(self, items, strings):
-		out = []
-		for cs in items:
-			cs.compile()
-			out.append(cs.bytecode)
-		return out
-
-class SubrsCompiler(GlobalSubrsCompiler):
-	def setPos(self, pos, endPos):
-		offset = pos - self.parent.pos
-		self.parent.rawDict["Subrs"] = offset
-
-class CharStringsCompiler(GlobalSubrsCompiler):
-	def setPos(self, pos, endPos):
-		self.parent.rawDict["CharStrings"] = pos
-
-
-class Index(object):
-	
-	"""This class represents what the CFF spec calls an INDEX."""
-	
-	compilerClass = IndexCompiler
-	
-	def __init__(self, file=None):
-		name = self.__class__.__name__
-		if file is None:
-			self.items = []
-			return
-		if DEBUG:
-			print("loading %s at %s" % (name, file.tell()))
-		self.file = file
-		count = readCard16(file)
-		self.count = count
-		self.items = [None] * count
-		if count == 0:
-			self.items = []
-			return
-		offSize = readCard8(file)
-		if DEBUG:
-			print("    index count: %s offSize: %s" % (count, offSize))
-		assert offSize <= 4, "offSize too large: %s" % offSize
-		self.offsets = offsets = []
-		pad = b'\0' * (4 - offSize)
-		for index in range(count+1):
-			chunk = file.read(offSize)
-			chunk = pad + chunk
-			offset, = struct.unpack(">L", chunk)
-			offsets.append(int(offset))
-		self.offsetBase = file.tell() - 1
-		file.seek(self.offsetBase + offsets[-1])  # pretend we've read the whole lot
-		if DEBUG:
-			print("    end of %s at %s" % (name, file.tell()))
-	
-	def __len__(self):
-		return len(self.items)
-	
-	def __getitem__(self, index):
-		item = self.items[index]
-		if item is not None:
-			return item
-		offset = self.offsets[index] + self.offsetBase
-		size = self.offsets[index+1] - self.offsets[index]
-		file = self.file
-		file.seek(offset)
-		data = file.read(size)
-		assert len(data) == size
-		item = self.produceItem(index, data, file, offset, size)
-		self.items[index] = item
-		return item
-	
-	def produceItem(self, index, data, file, offset, size):
-		return data
-	
-	def append(self, item):
-		self.items.append(item)
-	
-	def getCompiler(self, strings, parent):
-		return self.compilerClass(self, strings, parent)
-
-
-class GlobalSubrsIndex(Index):
-	
-	compilerClass = GlobalSubrsCompiler
-	
-	def __init__(self, file=None, globalSubrs=None, private=None, fdSelect=None, fdArray=None):
-		Index.__init__(self, file)
-		self.globalSubrs = globalSubrs
-		self.private = private
-		if fdSelect:
-			self.fdSelect = fdSelect
-		if fdArray:
-			self.fdArray = fdArray
-	
-	def produceItem(self, index, data, file, offset, size):
-		if self.private is not None:
-			private = self.private
-		elif hasattr(self, 'fdArray') and self.fdArray is not None:
-			private = self.fdArray[self.fdSelect[index]].Private
-		else:
-			private = None
-		return psCharStrings.T2CharString(data, private=private, globalSubrs=self.globalSubrs)
-	
-	def toXML(self, xmlWriter, progress):
-		xmlWriter.comment("The 'index' attribute is only for humans; it is ignored when parsed.")
-		xmlWriter.newline()
-		for i in range(len(self)):
-			subr = self[i]
-			if subr.needsDecompilation():
-				xmlWriter.begintag("CharString", index=i, raw=1)
-			else:
-				xmlWriter.begintag("CharString", index=i)
-			xmlWriter.newline()
-			subr.toXML(xmlWriter)
-			xmlWriter.endtag("CharString")
-			xmlWriter.newline()
-	
-	def fromXML(self, name, attrs, content):
-		if name != "CharString":
-			return
-		subr = psCharStrings.T2CharString()
-		subr.fromXML(name, attrs, content)
-		self.append(subr)
-	
-	def getItemAndSelector(self, index):
-		sel = None
-		if hasattr(self, 'fdSelect'):
-			sel = self.fdSelect[index]
-		return self[index], sel
-
-
-class SubrsIndex(GlobalSubrsIndex):
-	compilerClass = SubrsCompiler
-
-
-class TopDictIndex(Index):
-	
-	compilerClass = TopDictIndexCompiler
-	
-	def produceItem(self, index, data, file, offset, size):
-		top = TopDict(self.strings, file, offset, self.GlobalSubrs)
-		top.decompile(data)
-		return top
-	
-	def toXML(self, xmlWriter, progress):
-		for i in range(len(self)):
-			xmlWriter.begintag("FontDict", index=i)
-			xmlWriter.newline()
-			self[i].toXML(xmlWriter, progress)
-			xmlWriter.endtag("FontDict")
-			xmlWriter.newline()
-
-
-class FDArrayIndex(TopDictIndex):
-	
-	compilerClass = FDArrayIndexCompiler
-
-	def fromXML(self, name, attrs, content):
-		if name != "FontDict":
-			return
-		fontDict = FontDict()
-		for element in content:
-			if isinstance(element, basestring):
-				continue
-			name, attrs, content = element
-			fontDict.fromXML(name, attrs, content)
-		self.append(fontDict)
-
-
-class	FDSelect:
-	def __init__(self, file = None, numGlyphs = None, format=None):
-		if file:
-			# read data in from file
-			self.format = readCard8(file)
-			if self.format == 0:
-				from array import array
-				self.gidArray = array("B", file.read(numGlyphs)).tolist()
-			elif self.format == 3:
-				gidArray = [None] * numGlyphs
-				nRanges = readCard16(file)
-				prev = None
-				for i in range(nRanges):
-					first = readCard16(file)
-					if prev is not None:
-						for glyphID in range(prev, first):
-							gidArray[glyphID] = fd
-					prev = first
-					fd = readCard8(file)
-				if prev is not None:
-					first = readCard16(file)
-					for glyphID in range(prev, first):
-						gidArray[glyphID] = fd
-				self.gidArray = gidArray
-			else:
-				assert False, "unsupported FDSelect format: %s" % format
-		else:
-			# reading from XML. Make empty gidArray,, and leave format as passed in.
-			# format is None will result in the smallest representation being used.
-			self.format = format
-			self.gidArray = []
-
-
-	def __len__(self):
-		return len(self.gidArray)
-	
-	def __getitem__(self, index):
-		return self.gidArray[index]
-	
-	def __setitem__(self, index, fdSelectValue):
-		self.gidArray[index] = fdSelectValue
-
-	def append(self, fdSelectValue):
-		self.gidArray.append(fdSelectValue)
-	
-
-class CharStrings(object):
-	
-	def __init__(self, file, charset, globalSubrs, private, fdSelect, fdArray):
-		if file is not None:
-			self.charStringsIndex = SubrsIndex(file, globalSubrs, private, fdSelect, fdArray)
-			self.charStrings = charStrings = {}
-			for i in range(len(charset)):
-				charStrings[charset[i]] = i
-			self.charStringsAreIndexed = 1
-		else:
-			self.charStrings = {}
-			self.charStringsAreIndexed = 0
-			self.globalSubrs = globalSubrs
-			self.private = private
-			if fdSelect is not None:
-				self.fdSelect = fdSelect
-			if fdArray is not None:
-				self.fdArray = fdArray
-	
-	def keys(self):
-		return list(self.charStrings.keys())
-	
-	def values(self):
-		if self.charStringsAreIndexed:
-			return self.charStringsIndex
-		else:
-			return list(self.charStrings.values())
-	
-	def has_key(self, name):
-		return name in self.charStrings
-	
-	def __len__(self):
-		return len(self.charStrings)
-	
-	def __getitem__(self, name):
-		charString = self.charStrings[name]
-		if self.charStringsAreIndexed:
-			charString = self.charStringsIndex[charString]
-		return charString
-	
-	def __setitem__(self, name, charString):
-		if self.charStringsAreIndexed:
-			index = self.charStrings[name]
-			self.charStringsIndex[index] = charString
-		else:
-			self.charStrings[name] = charString
-	
-	def getItemAndSelector(self, name):
-		if self.charStringsAreIndexed:
-			index = self.charStrings[name]
-			return self.charStringsIndex.getItemAndSelector(index)
-		else:
-			if hasattr(self, 'fdSelect'):
-				sel = self.fdSelect[index]  # index is not defined at this point. Read R. ?
-			else:
-				raise KeyError("fdSelect array not yet defined.")
-			return self.charStrings[name], sel
-	
-	def toXML(self, xmlWriter, progress):
-		names = sorted(self.keys())
-		i = 0
-		step = 10
-		numGlyphs = len(names)
-		for name in names:
-			charStr, fdSelectIndex = self.getItemAndSelector(name)
-			if charStr.needsDecompilation():
-				raw = [("raw", 1)]
-			else:
-				raw = []
-			if fdSelectIndex is None:
-				xmlWriter.begintag("CharString", [('name', name)] + raw)
-			else:
-				xmlWriter.begintag("CharString",
-						[('name', name), ('fdSelectIndex', fdSelectIndex)] + raw)
-			xmlWriter.newline()
-			charStr.toXML(xmlWriter)
-			xmlWriter.endtag("CharString")
-			xmlWriter.newline()
-			if not i % step and progress is not None:
-				progress.setLabel("Dumping 'CFF ' table... (%s)" % name)
-				progress.increment(step / numGlyphs)
-			i = i + 1
-	
-	def fromXML(self, name, attrs, content):
-		for element in content:
-			if isinstance(element, basestring):
-				continue
-			name, attrs, content = element
-			if name != "CharString":
-				continue
-			fdID = -1
-			if hasattr(self, "fdArray"):
-				fdID = safeEval(attrs["fdSelectIndex"])
-				private = self.fdArray[fdID].Private
-			else:
-				private = self.private
-				
-			glyphName = attrs["name"]
-			charString = psCharStrings.T2CharString(
-					private=private,
-					globalSubrs=self.globalSubrs)
-			charString.fromXML(name, attrs, content)
-			if fdID >= 0:
-				charString.fdSelectIndex = fdID
-			self[glyphName] = charString
-
-
-def readCard8(file):
-	return byteord(file.read(1))
-
-def readCard16(file):
-	value, = struct.unpack(">H", file.read(2))
-	return value
-
-def writeCard8(file, value):
-	file.write(bytechr(value))
-
-def writeCard16(file, value):
-	file.write(struct.pack(">H", value))
-
-def packCard8(value):
-	return bytechr(value)
-
-def packCard16(value):
-	return struct.pack(">H", value)
-
-def buildOperatorDict(table):
-	d = {}
-	for op, name, arg, default, conv in table:
-		d[op] = (name, arg)
-	return d
-
-def buildOpcodeDict(table):
-	d = {}
-	for op, name, arg, default, conv in table:
-		if isinstance(op, tuple):
-			op = bytechr(op[0]) + bytechr(op[1])
-		else:
-			op = bytechr(op)
-		d[name] = (op, arg)
-	return d
-
-def buildOrder(table):
-	l = []
-	for op, name, arg, default, conv in table:
-		l.append(name)
-	return l
-
-def buildDefaults(table):
-	d = {}
-	for op, name, arg, default, conv in table:
-		if default is not None:
-			d[name] = default
-	return d
-
-def buildConverters(table):
-	d = {}
-	for op, name, arg, default, conv in table:
-		d[name] = conv
-	return d
-
-
-class SimpleConverter(object):
-	def read(self, parent, value):
-		return value
-	def write(self, parent, value):
-		return value
-	def xmlWrite(self, xmlWriter, name, value, progress):
-		xmlWriter.simpletag(name, value=value)
-		xmlWriter.newline()
-	def xmlRead(self, name, attrs, content, parent):
-		return attrs["value"]
-
-class ASCIIConverter(SimpleConverter):
-	def read(self, parent, value):
-		return tostr(value, encoding='ascii')
-	def write(self, parent, value):
-		return tobytes(value, encoding='ascii')
-	def xmlWrite(self, xmlWriter, name, value, progress):
-		xmlWriter.simpletag(name, value=tostr(value, encoding="ascii"))
-		xmlWriter.newline()
-	def xmlRead(self, name, attrs, content, parent):
-		return tobytes(attrs["value"], encoding=("ascii"))
-
-class Latin1Converter(SimpleConverter):
-	def read(self, parent, value):
-		return tostr(value, encoding='latin1')
-	def write(self, parent, value):
-		return tobytes(value, encoding='latin1')
-	def xmlWrite(self, xmlWriter, name, value, progress):
-		xmlWriter.simpletag(name, value=tostr(value, encoding="latin1"))
-		xmlWriter.newline()
-	def xmlRead(self, name, attrs, content, parent):
-		return tobytes(attrs["value"], encoding=("latin1"))
-
-
-def parseNum(s):
-	try:
-		value = int(s)
-	except:
-		value = float(s)
-	return value
-
-class NumberConverter(SimpleConverter):
-	def xmlRead(self, name, attrs, content, parent):
-		return parseNum(attrs["value"])
-
-class ArrayConverter(SimpleConverter):
-	def xmlWrite(self, xmlWriter, name, value, progress):
-		value = " ".join(map(str, value))
-		xmlWriter.simpletag(name, value=value)
-		xmlWriter.newline()
-	def xmlRead(self, name, attrs, content, parent):
-		values = attrs["value"].split()
-		return [parseNum(value) for value in values]
-
-class TableConverter(SimpleConverter):
-	def xmlWrite(self, xmlWriter, name, value, progress):
-		xmlWriter.begintag(name)
-		xmlWriter.newline()
-		value.toXML(xmlWriter, progress)
-		xmlWriter.endtag(name)
-		xmlWriter.newline()
-	def xmlRead(self, name, attrs, content, parent):
-		ob = self.getClass()()
-		for element in content:
-			if isinstance(element, basestring):
-				continue
-			name, attrs, content = element
-			ob.fromXML(name, attrs, content)
-		return ob
-
-class PrivateDictConverter(TableConverter):
-	def getClass(self):
-		return PrivateDict
-	def read(self, parent, value):
-		size, offset = value
-		file = parent.file
-		priv = PrivateDict(parent.strings, file, offset)
-		file.seek(offset)
-		data = file.read(size)
-		assert len(data) == size
-		priv.decompile(data)
-		return priv
-	def write(self, parent, value):
-		return (0, 0)  # dummy value
-
-class SubrsConverter(TableConverter):
-	def getClass(self):
-		return SubrsIndex
-	def read(self, parent, value):
-		file = parent.file
-		file.seek(parent.offset + value)  # Offset(self)
-		return SubrsIndex(file)
-	def write(self, parent, value):
-		return 0  # dummy value
-
-class CharStringsConverter(TableConverter):
-	def read(self, parent, value):
-		file = parent.file
-		charset = parent.charset
-		globalSubrs = parent.GlobalSubrs
-		if hasattr(parent, "ROS"):
-			fdSelect, fdArray = parent.FDSelect, parent.FDArray
-			private = None
-		else:
-			fdSelect, fdArray = None, None
-			private = parent.Private
-		file.seek(value)  # Offset(0)
-		return CharStrings(file, charset, globalSubrs, private, fdSelect, fdArray)
-	def write(self, parent, value):
-		return 0  # dummy value
-	def xmlRead(self, name, attrs, content, parent):
-		if hasattr(parent, "ROS"):
-			# if it is a CID-keyed font, then the private Dict is extracted from the parent.FDArray 
-			private, fdSelect, fdArray = None, parent.FDSelect, parent.FDArray
-		else:
-			# if it is a name-keyed font, then the private dict is in the top dict, and there is no fdArray. 
-			private, fdSelect, fdArray = parent.Private, None, None
-		charStrings = CharStrings(None, None, parent.GlobalSubrs, private, fdSelect, fdArray)
-		charStrings.fromXML(name, attrs, content)
-		return charStrings
-
-class CharsetConverter(object):
-	def read(self, parent, value):
-		isCID = hasattr(parent, "ROS")
-		if value > 2:
-			numGlyphs = parent.numGlyphs
-			file = parent.file
-			file.seek(value)
-			if DEBUG:
-				print("loading charset at %s" % value)
-			format = readCard8(file)
-			if format == 0:
-				charset = parseCharset0(numGlyphs, file, parent.strings, isCID)
-			elif format == 1 or format == 2:
-				charset = parseCharset(numGlyphs, file, parent.strings, isCID, format)
-			else:
-				raise NotImplementedError
-			assert len(charset) == numGlyphs
-			if DEBUG:
-				print("    charset end at %s" % file.tell())
-		else: # offset == 0 -> no charset data.
-			if isCID or "CharStrings" not in parent.rawDict: 
-				assert value == 0 # We get here only when processing fontDicts from the FDArray of CFF-CID fonts. Only the real topDict references the chrset.
-				charset = None
-			elif value == 0:
-				charset = cffISOAdobeStrings
-			elif value == 1:
-				charset = cffIExpertStrings
-			elif value == 2:
-				charset = cffExpertSubsetStrings
-		return charset
-
-	def write(self, parent, value):
-		return 0  # dummy value
-	def xmlWrite(self, xmlWriter, name, value, progress):
-		# XXX only write charset when not in OT/TTX context, where we
-		# dump charset as a separate "GlyphOrder" table.
-		##xmlWriter.simpletag("charset")
-		xmlWriter.comment("charset is dumped separately as the 'GlyphOrder' element")
-		xmlWriter.newline()
-	def xmlRead(self, name, attrs, content, parent):
-		if 0:
-			return safeEval(attrs["value"])
-
-
-class CharsetCompiler(object):
-	
-	def __init__(self, strings, charset, parent):
-		assert charset[0] == '.notdef'
-		isCID = hasattr(parent.dictObj, "ROS")
-		data0 = packCharset0(charset, isCID, strings)
-		data = packCharset(charset, isCID, strings)
-		if len(data) < len(data0):
-			self.data = data
-		else:
-			self.data = data0
-		self.parent = parent
-	
-	def setPos(self, pos, endPos):
-		self.parent.rawDict["charset"] = pos
-	
-	def getDataLength(self):
-		return len(self.data)
-	
-	def toFile(self, file):
-		file.write(self.data)
-
-
-def getCIDfromName(name, strings):
-	return int(name[3:])
-
-def getSIDfromName(name, strings):
-	return strings.getSID(name)
-
-def packCharset0(charset, isCID, strings):
-	fmt = 0
-	data = [packCard8(fmt)]
-	if isCID:
-		getNameID = getCIDfromName
-	else:
-		getNameID = getSIDfromName
-
-	for name in charset[1:]:
-		data.append(packCard16(getNameID(name,strings)))
-	return bytesjoin(data)
-
-
-def packCharset(charset, isCID, strings):
-	fmt = 1
-	ranges = []
-	first = None
-	end = 0
-	if isCID:
-		getNameID = getCIDfromName
-	else:
-		getNameID = getSIDfromName
-	
-	for name in charset[1:]:
-		SID = getNameID(name, strings)
-		if first is None:
-			first = SID
-		elif end + 1 != SID:
-			nLeft = end - first
-			if nLeft > 255:
-				fmt = 2
-			ranges.append((first, nLeft))
-			first = SID
-		end = SID
-	nLeft = end - first
-	if nLeft > 255:
-		fmt = 2
-	ranges.append((first, nLeft))
-	
-	data = [packCard8(fmt)]
-	if fmt == 1:
-		nLeftFunc = packCard8
-	else:
-		nLeftFunc = packCard16
-	for first, nLeft in ranges:
-		data.append(packCard16(first) + nLeftFunc(nLeft))
-	return bytesjoin(data)
-
-def parseCharset0(numGlyphs, file, strings, isCID):
-	charset = [".notdef"]
-	if isCID:
-		for i in range(numGlyphs - 1):
-			CID = readCard16(file)
-			charset.append("cid" + str(CID).zfill(5))
-	else:
-		for i in range(numGlyphs - 1):
-			SID = readCard16(file)
-			charset.append(strings[SID])
-	return charset
-
-def parseCharset(numGlyphs, file, strings, isCID, fmt):
-	charset = ['.notdef']
-	count = 1
-	if fmt == 1:
-		nLeftFunc = readCard8
-	else:
-		nLeftFunc = readCard16
-	while count < numGlyphs:
-		first = readCard16(file)
-		nLeft = nLeftFunc(file)
-		if isCID:
-			for CID in range(first, first+nLeft+1):
-				charset.append("cid" + str(CID).zfill(5))
-		else:
-			for SID in range(first, first+nLeft+1):
-				charset.append(strings[SID])
-		count = count + nLeft + 1
-	return charset
-
-
-class EncodingCompiler(object):
-
-	def __init__(self, strings, encoding, parent):
-		assert not isinstance(encoding, basestring)
-		data0 = packEncoding0(parent.dictObj.charset, encoding, parent.strings)
-		data1 = packEncoding1(parent.dictObj.charset, encoding, parent.strings)
-		if len(data0) < len(data1):
-			self.data = data0
-		else:
-			self.data = data1
-		self.parent = parent
-
-	def setPos(self, pos, endPos):
-		self.parent.rawDict["Encoding"] = pos
-	
-	def getDataLength(self):
-		return len(self.data)
-	
-	def toFile(self, file):
-		file.write(self.data)
-
-
-class EncodingConverter(SimpleConverter):
-
-	def read(self, parent, value):
-		if value == 0:
-			return "StandardEncoding"
-		elif value == 1:
-			return "ExpertEncoding"
-		else:
-			assert value > 1
-			file = parent.file
-			file.seek(value)
-			if DEBUG:
-				print("loading Encoding at %s" % value)
-			fmt = readCard8(file)
-			haveSupplement = fmt & 0x80
-			if haveSupplement:
-				raise NotImplementedError("Encoding supplements are not yet supported")
-			fmt = fmt & 0x7f
-			if fmt == 0:
-				encoding = parseEncoding0(parent.charset, file, haveSupplement,
-						parent.strings)
-			elif fmt == 1:
-				encoding = parseEncoding1(parent.charset, file, haveSupplement,
-						parent.strings)
-			return encoding
-
-	def write(self, parent, value):
-		if value == "StandardEncoding":
-			return 0
-		elif value == "ExpertEncoding":
-			return 1
-		return 0  # dummy value
-
-	def xmlWrite(self, xmlWriter, name, value, progress):
-		if value in ("StandardEncoding", "ExpertEncoding"):
-			xmlWriter.simpletag(name, name=value)
-			xmlWriter.newline()
-			return
-		xmlWriter.begintag(name)
-		xmlWriter.newline()
-		for code in range(len(value)):
-			glyphName = value[code]
-			if glyphName != ".notdef":
-				xmlWriter.simpletag("map", code=hex(code), name=glyphName)
-				xmlWriter.newline()
-		xmlWriter.endtag(name)
-		xmlWriter.newline()
-
-	def xmlRead(self, name, attrs, content, parent):
-		if "name" in attrs:
-			return attrs["name"]
-		encoding = [".notdef"] * 256
-		for element in content:
-			if isinstance(element, basestring):
-				continue
-			name, attrs, content = element
-			code = safeEval(attrs["code"])
-			glyphName = attrs["name"]
-			encoding[code] = glyphName
-		return encoding
-
-
-def parseEncoding0(charset, file, haveSupplement, strings):
-	nCodes = readCard8(file)
-	encoding = [".notdef"] * 256
-	for glyphID in range(1, nCodes + 1):
-		code = readCard8(file)
-		if code != 0:
-			encoding[code] = charset[glyphID]
-	return encoding
-
-def parseEncoding1(charset, file, haveSupplement, strings):
-	nRanges = readCard8(file)
-	encoding = [".notdef"] * 256
-	glyphID = 1
-	for i in range(nRanges):
-		code = readCard8(file)
-		nLeft = readCard8(file)
-		for glyphID in range(glyphID, glyphID + nLeft + 1):
-			encoding[code] = charset[glyphID]
-			code = code + 1
-		glyphID = glyphID + 1
-	return encoding
-
-def packEncoding0(charset, encoding, strings):
-	fmt = 0
-	m = {}
-	for code in range(len(encoding)):
-		name = encoding[code]
-		if name != ".notdef":
-			m[name] = code
-	codes = []
-	for name in charset[1:]:
-		code = m.get(name)
-		codes.append(code)
-	
-	while codes and codes[-1] is None:
-		codes.pop()
-
-	data = [packCard8(fmt), packCard8(len(codes))]
-	for code in codes:
-		if code is None:
-			code = 0
-		data.append(packCard8(code))
-	return bytesjoin(data)
-
-def packEncoding1(charset, encoding, strings):
-	fmt = 1
-	m = {}
-	for code in range(len(encoding)):
-		name = encoding[code]
-		if name != ".notdef":
-			m[name] = code
-	ranges = []
-	first = None
-	end = 0
-	for name in charset[1:]:
-		code = m.get(name, -1)
-		if first is None:
-			first = code
-		elif end + 1 != code:
-			nLeft = end - first
-			ranges.append((first, nLeft))
-			first = code
-		end = code
-	nLeft = end - first
-	ranges.append((first, nLeft))
-	
-	# remove unencoded glyphs at the end.
-	while ranges and ranges[-1][0] == -1:
-		ranges.pop()
-
-	data = [packCard8(fmt), packCard8(len(ranges))]
-	for first, nLeft in ranges:
-		if first == -1:  # unencoded
-			first = 0
-		data.append(packCard8(first) + packCard8(nLeft))
-	return bytesjoin(data)
-
-
-class FDArrayConverter(TableConverter):
-
-	def read(self, parent, value):
-		file = parent.file
-		file.seek(value)
-		fdArray = FDArrayIndex(file)
-		fdArray.strings = parent.strings
-		fdArray.GlobalSubrs = parent.GlobalSubrs
-		return fdArray
-
-	def write(self, parent, value):
-		return 0  # dummy value
-
-	def xmlRead(self, name, attrs, content, parent):
-		fdArray = FDArrayIndex()
-		for element in content:
-			if isinstance(element, basestring):
-				continue
-			name, attrs, content = element
-			fdArray.fromXML(name, attrs, content)
-		return fdArray
-
-
-class FDSelectConverter(object):
-
-	def read(self, parent, value):
-		file = parent.file
-		file.seek(value)
-		fdSelect = FDSelect(file, parent.numGlyphs)
-		return 	fdSelect
-
-	def write(self, parent, value):
-		return 0  # dummy value
-
-	# The FDSelect glyph data is written out to XML in the charstring keys,
-	# so we write out only the format selector
-	def xmlWrite(self, xmlWriter, name, value, progress):
-		xmlWriter.simpletag(name, [('format', value.format)])
-		xmlWriter.newline()
-
-	def xmlRead(self, name, attrs, content, parent):
-		fmt = safeEval(attrs["format"])
-		file = None
-		numGlyphs = None
-		fdSelect = FDSelect(file, numGlyphs, fmt)
-		return fdSelect
-		
-
-def packFDSelect0(fdSelectArray):
-	fmt = 0
-	data = [packCard8(fmt)]
-	for index in fdSelectArray:
-		data.append(packCard8(index))
-	return bytesjoin(data)
-
-
-def packFDSelect3(fdSelectArray):
-	fmt = 3
-	fdRanges = []
-	first = None
-	end = 0
-	lenArray = len(fdSelectArray)
-	lastFDIndex = -1
-	for i in range(lenArray):
-		fdIndex = fdSelectArray[i]
-		if lastFDIndex != fdIndex:
-			fdRanges.append([i, fdIndex])
-			lastFDIndex = fdIndex
-	sentinelGID = i + 1
-		
-	data = [packCard8(fmt)]
-	data.append(packCard16( len(fdRanges) ))
-	for fdRange in fdRanges:
-		data.append(packCard16(fdRange[0]))
-		data.append(packCard8(fdRange[1]))
-	data.append(packCard16(sentinelGID))
-	return bytesjoin(data)
-
-
-class FDSelectCompiler(object):
-	
-	def __init__(self, fdSelect, parent):
-		fmt = fdSelect.format
-		fdSelectArray = fdSelect.gidArray
-		if fmt == 0:
-			self.data = packFDSelect0(fdSelectArray)
-		elif fmt == 3:
-			self.data = packFDSelect3(fdSelectArray)
-		else:
-			# choose smaller of the two formats
-			data0 = packFDSelect0(fdSelectArray)
-			data3 = packFDSelect3(fdSelectArray)
-			if len(data0) < len(data3):
-				self.data = data0
-				fdSelect.format = 0
-			else:
-				self.data = data3
-				fdSelect.format = 3
-
-		self.parent = parent
-	
-	def setPos(self, pos, endPos):
-		self.parent.rawDict["FDSelect"] = pos
-	
-	def getDataLength(self):
-		return len(self.data)
-	
-	def toFile(self, file):
-		file.write(self.data)
-
-
-class ROSConverter(SimpleConverter):
-
-	def xmlWrite(self, xmlWriter, name, value, progress):
-		registry, order, supplement = value
-		xmlWriter.simpletag(name, [('Registry', tostr(registry)), ('Order', tostr(order)),
-			('Supplement', supplement)])
-		xmlWriter.newline()
-
-	def xmlRead(self, name, attrs, content, parent):
-		return (attrs['Registry'], attrs['Order'], safeEval(attrs['Supplement']))
-
-
-
-topDictOperators = [
-#	opcode     name                  argument type   default    converter
-	((12, 30), 'ROS',        ('SID','SID','number'), None,      ROSConverter()),
-	((12, 20), 'SyntheticBase',      'number',       None,      None),
-	(0,        'version',            'SID',          None,      None),
-	(1,        'Notice',             'SID',          None,      Latin1Converter()),
-	((12, 0),  'Copyright',          'SID',          None,      Latin1Converter()),
-	(2,        'FullName',           'SID',          None,      None),
-	((12, 38), 'FontName',           'SID',          None,      None),
-	(3,        'FamilyName',         'SID',          None,      None),
-	(4,        'Weight',             'SID',          None,      None),
-	((12, 1),  'isFixedPitch',       'number',       0,         None),
-	((12, 2),  'ItalicAngle',        'number',       0,         None),
-	((12, 3),  'UnderlinePosition',  'number',       None,      None),
-	((12, 4),  'UnderlineThickness', 'number',       50,        None),
-	((12, 5),  'PaintType',          'number',       0,         None),
-	((12, 6),  'CharstringType',     'number',       2,         None),
-	((12, 7),  'FontMatrix',         'array',  [0.001,0,0,0.001,0,0],  None),
-	(13,       'UniqueID',           'number',       None,      None),
-	(5,        'FontBBox',           'array',  [0,0,0,0],       None),
-	((12, 8),  'StrokeWidth',        'number',       0,         None),
-	(14,       'XUID',               'array',        None,      None),
-	((12, 21), 'PostScript',         'SID',          None,      None),
-	((12, 22), 'BaseFontName',       'SID',          None,      None),
-	((12, 23), 'BaseFontBlend',      'delta',        None,      None),
-	((12, 31), 'CIDFontVersion',     'number',       0,         None),
-	((12, 32), 'CIDFontRevision',    'number',       0,         None),
-	((12, 33), 'CIDFontType',        'number',       0,         None),
-	((12, 34), 'CIDCount',           'number',       8720,      None),
-	(15,       'charset',            'number',       0,         CharsetConverter()),
-	((12, 35), 'UIDBase',            'number',       None,      None),
-	(16,       'Encoding',           'number',       0,         EncodingConverter()),
-	(18,       'Private',       ('number','number'), None,      PrivateDictConverter()),
-	((12, 37), 'FDSelect',           'number',       None,      FDSelectConverter()),
-	((12, 36), 'FDArray',            'number',       None,      FDArrayConverter()),
-	(17,       'CharStrings',        'number',       None,      CharStringsConverter()),
-]
-
-# Note! FDSelect and FDArray must both preceed CharStrings in the output XML build order,
-# in order for the font to compile back from xml.
-
-
-privateDictOperators = [
-#	opcode     name                  argument type   default    converter
-	(6,        'BlueValues',         'delta',        None,      None),
-	(7,        'OtherBlues',         'delta',        None,      None),
-	(8,        'FamilyBlues',        'delta',        None,      None),
-	(9,        'FamilyOtherBlues',   'delta',        None,      None),
-	((12, 9),  'BlueScale',          'number',       0.039625,  None),
-	((12, 10), 'BlueShift',          'number',       7,         None),
-	((12, 11), 'BlueFuzz',           'number',       1,         None),
-	(10,       'StdHW',              'number',       None,      None),
-	(11,       'StdVW',              'number',       None,      None),
-	((12, 12), 'StemSnapH',          'delta',        None,      None),
-	((12, 13), 'StemSnapV',          'delta',        None,      None),
-	((12, 14), 'ForceBold',          'number',       0,         None),
-	((12, 15), 'ForceBoldThreshold', 'number',       None,      None),  # deprecated
-	((12, 16), 'lenIV',              'number',       None,      None),  # deprecated
-	((12, 17), 'LanguageGroup',      'number',       0,         None),
-	((12, 18), 'ExpansionFactor',    'number',       0.06,      None),
-	((12, 19), 'initialRandomSeed',  'number',       0,         None),
-	(20,       'defaultWidthX',      'number',       0,         None),
-	(21,       'nominalWidthX',      'number',       0,         None),
-	(19,       'Subrs',              'number',       None,      SubrsConverter()),
-]
-
-def addConverters(table):
-	for i in range(len(table)):
-		op, name, arg, default, conv = table[i]
-		if conv is not None:
-			continue
-		if arg in ("delta", "array"):
-			conv = ArrayConverter()
-		elif arg == "number":
-			conv = NumberConverter()
-		elif arg == "SID":
-			conv = ASCIIConverter()
-		else:
-			assert False
-		table[i] = op, name, arg, default, conv
-
-addConverters(privateDictOperators)
-addConverters(topDictOperators)
-
-
-class TopDictDecompiler(psCharStrings.DictDecompiler):
-	operators = buildOperatorDict(topDictOperators)
-
-
-class PrivateDictDecompiler(psCharStrings.DictDecompiler):
-	operators = buildOperatorDict(privateDictOperators)
-
-
-class DictCompiler(object):
-	
-	def __init__(self, dictObj, strings, parent):
-		assert isinstance(strings, IndexedStrings)
-		self.dictObj = dictObj
-		self.strings = strings
-		self.parent = parent
-		rawDict = {}
-		for name in dictObj.order:
-			value = getattr(dictObj, name, None)
-			if value is None:
-				continue
-			conv = dictObj.converters[name]
-			value = conv.write(dictObj, value)
-			if value == dictObj.defaults.get(name):
-				continue
-			rawDict[name] = value
-		self.rawDict = rawDict
-	
-	def setPos(self, pos, endPos):
-		pass
-	
-	def getDataLength(self):
-		return len(self.compile("getDataLength"))
-	
-	def compile(self, reason):
-		if DEBUG:
-			print("-- compiling %s for %s" % (self.__class__.__name__, reason))
-			print("in baseDict: ", self)
-		rawDict = self.rawDict
-		data = []
-		for name in self.dictObj.order:
-			value = rawDict.get(name)
-			if value is None:
-				continue
-			op, argType = self.opcodes[name]
-			if isinstance(argType, tuple):
-				l = len(argType)
-				assert len(value) == l, "value doesn't match arg type"
-				for i in range(l):
-					arg = argType[i]
-					v = value[i]
-					arghandler = getattr(self, "arg_" + arg)
-					data.append(arghandler(v))
-			else:
-				arghandler = getattr(self, "arg_" + argType)
-				data.append(arghandler(value))
-			data.append(op)
-		return bytesjoin(data)
-	
-	def toFile(self, file):
-		file.write(self.compile("toFile"))
-	
-	def arg_number(self, num):
-		return encodeNumber(num)
-	def arg_SID(self, s):
-		return psCharStrings.encodeIntCFF(self.strings.getSID(s))
-	def arg_array(self, value):
-		data = []
-		for num in value:
-			data.append(encodeNumber(num))
-		return bytesjoin(data)
-	def arg_delta(self, value):
-		out = []
-		last = 0
-		for v in value:
-			out.append(v - last)
-			last = v
-		data = []
-		for num in out:
-			data.append(encodeNumber(num))
-		return bytesjoin(data)
-
-
-def encodeNumber(num):
-	if isinstance(num, float):
-		return psCharStrings.encodeFloat(num)
-	else:
-		return psCharStrings.encodeIntCFF(num)
-
-
-class TopDictCompiler(DictCompiler):
-	
-	opcodes = buildOpcodeDict(topDictOperators)
-	
-	def getChildren(self, strings):
-		children = []
-		if hasattr(self.dictObj, "charset") and self.dictObj.charset:
-			children.append(CharsetCompiler(strings, self.dictObj.charset, self))
-		if hasattr(self.dictObj, "Encoding"):
-			encoding = self.dictObj.Encoding
-			if not isinstance(encoding, basestring):
-				children.append(EncodingCompiler(strings, encoding, self))
-		if hasattr(self.dictObj, "FDSelect"):
-			# I have not yet supported merging a ttx CFF-CID font, as there are interesting
-			# issues about merging the FDArrays. Here I assume that
-			# either the font was read from XML, and teh FDSelect indices are all
-			# in the charstring data, or the FDSelect array is already fully defined.
-			fdSelect = self.dictObj.FDSelect
-			if len(fdSelect) == 0: # probably read in from XML; assume fdIndex in CharString data
-				charStrings = self.dictObj.CharStrings
-				for name in self.dictObj.charset:
-					fdSelect.append(charStrings[name].fdSelectIndex)
-			fdSelectComp = FDSelectCompiler(fdSelect, self)
-			children.append(fdSelectComp)
-		if hasattr(self.dictObj, "CharStrings"):
-			items = []
-			charStrings = self.dictObj.CharStrings
-			for name in self.dictObj.charset:
-				items.append(charStrings[name])
-			charStringsComp = CharStringsCompiler(items, strings, self)
-			children.append(charStringsComp)
-		if hasattr(self.dictObj, "FDArray"):
-			# I have not yet supported merging a ttx CFF-CID font, as there are interesting
-			# issues about merging the FDArrays. Here I assume that the FDArray info is correct
-			# and complete.
-			fdArrayIndexComp = self.dictObj.FDArray.getCompiler(strings, self)
-			children.append(fdArrayIndexComp)
-			children.extend(fdArrayIndexComp.getChildren(strings))
-		if hasattr(self.dictObj, "Private"):
-			privComp = self.dictObj.Private.getCompiler(strings, self)
-			children.append(privComp)
-			children.extend(privComp.getChildren(strings))
-		return children
-
-
-class FontDictCompiler(DictCompiler):
-	
-	opcodes = buildOpcodeDict(topDictOperators)
-	
-	def getChildren(self, strings):
-		children = []
-		if hasattr(self.dictObj, "Private"):
-			privComp = self.dictObj.Private.getCompiler(strings, self)
-			children.append(privComp)
-			children.extend(privComp.getChildren(strings))
-		return children
-
-
-class PrivateDictCompiler(DictCompiler):
-	
-	opcodes = buildOpcodeDict(privateDictOperators)
-	
-	def setPos(self, pos, endPos):
-		size = endPos - pos
-		self.parent.rawDict["Private"] = size, pos
-		self.pos = pos
-	
-	def getChildren(self, strings):
-		children = []
-		if hasattr(self.dictObj, "Subrs"):
-			children.append(self.dictObj.Subrs.getCompiler(strings, self))
-		return children
-
-
-class BaseDict(object):
-	
-	def __init__(self, strings=None, file=None, offset=None):
-		self.rawDict = {}
-		if DEBUG:
-			print("loading %s at %s" % (self.__class__.__name__, offset))
-		self.file = file
-		self.offset = offset
-		self.strings = strings
-		self.skipNames = []
-	
-	def decompile(self, data):
-		if DEBUG:
-			print("    length %s is %s" % (self.__class__.__name__, len(data)))
-		dec = self.decompilerClass(self.strings)
-		dec.decompile(data)
-		self.rawDict = dec.getDict()
-		self.postDecompile()
-	
-	def postDecompile(self):
-		pass
-	
-	def getCompiler(self, strings, parent):
-		return self.compilerClass(self, strings, parent)
-	
-	def __getattr__(self, name):
-		value = self.rawDict.get(name)
-		if value is None:
-			value = self.defaults.get(name)
-		if value is None:
-			raise AttributeError(name)
-		conv = self.converters[name]
-		value = conv.read(self, value)
-		setattr(self, name, value)
-		return value
-	
-	def toXML(self, xmlWriter, progress):
-		for name in self.order:
-			if name in self.skipNames:
-				continue
-			value = getattr(self, name, None)
-			if value is None:
-				continue
-			conv = self.converters[name]
-			conv.xmlWrite(xmlWriter, name, value, progress)
-	
-	def fromXML(self, name, attrs, content):
-		conv = self.converters[name]
-		value = conv.xmlRead(name, attrs, content, self)
-		setattr(self, name, value)
-
-
-class TopDict(BaseDict):
-	
-	defaults = buildDefaults(topDictOperators)
-	converters = buildConverters(topDictOperators)
-	order = buildOrder(topDictOperators)
-	decompilerClass = TopDictDecompiler
-	compilerClass = TopDictCompiler
-	
-	def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
-		BaseDict.__init__(self, strings, file, offset)
-		self.GlobalSubrs = GlobalSubrs
-	
-	def getGlyphOrder(self):
-		return self.charset
-	
-	def postDecompile(self):
-		offset = self.rawDict.get("CharStrings")
-		if offset is None:
-			return
-		# get the number of glyphs beforehand.
-		self.file.seek(offset)
-		self.numGlyphs = readCard16(self.file)
-	
-	def toXML(self, xmlWriter, progress):
-		if hasattr(self, "CharStrings"):
-			self.decompileAllCharStrings(progress)
-		if hasattr(self, "ROS"):
-			self.skipNames = ['Encoding']
-		if not hasattr(self, "ROS") or not hasattr(self, "CharStrings"):
-			# these values have default values, but I only want them to show up
-			# in CID fonts.
-			self.skipNames = ['CIDFontVersion', 'CIDFontRevision', 'CIDFontType',
-					'CIDCount']
-		BaseDict.toXML(self, xmlWriter, progress)
-	
-	def decompileAllCharStrings(self, progress):
-		# XXX only when doing ttdump -i?
-		i = 0
-		for charString in self.CharStrings.values():
-			try:
-				charString.decompile()
-			except:
-				print("Error in charstring ", i)
-				import sys
-				typ, value = sys.exc_info()[0:2]
-				raise typ(value)
-			if not i % 30 and progress:
-				progress.increment(0)  # update
-			i = i + 1
-
-
-class FontDict(BaseDict):
-	
-	defaults = buildDefaults(topDictOperators)
-	converters = buildConverters(topDictOperators)
-	order = buildOrder(topDictOperators)
-	decompilerClass = None
-	compilerClass = FontDictCompiler
-	
-	def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
-		BaseDict.__init__(self, strings, file, offset)
-		self.GlobalSubrs = GlobalSubrs
-	
-	def getGlyphOrder(self):
-		return self.charset
-	
-	def toXML(self, xmlWriter, progress):
-		self.skipNames = ['Encoding']
-		BaseDict.toXML(self, xmlWriter, progress)
-	
-
-
-class PrivateDict(BaseDict):
-	defaults = buildDefaults(privateDictOperators)
-	converters = buildConverters(privateDictOperators)
-	order = buildOrder(privateDictOperators)
-	decompilerClass = PrivateDictDecompiler
-	compilerClass = PrivateDictCompiler
-
-
-class IndexedStrings(object):
-	
-	"""SID -> string mapping."""
-	
-	def __init__(self, file=None):
-		if file is None:
-			strings = []
-		else:
-			strings = [tostr(s, encoding="latin1") for s in Index(file)]
-		self.strings = strings
-	
-	def getCompiler(self):
-		return IndexedStringsCompiler(self, None, None)
-	
-	def __len__(self):
-		return len(self.strings)
-	
-	def __getitem__(self, SID):
-		if SID < cffStandardStringCount:
-			return cffStandardStrings[SID]
-		else:
-			return self.strings[SID - cffStandardStringCount]
-	
-	def getSID(self, s):
-		if not hasattr(self, "stringMapping"):
-			self.buildStringMapping()
-		if s in cffStandardStringMapping:
-			SID = cffStandardStringMapping[s]
-		elif s in self.stringMapping:
-			SID = self.stringMapping[s]
-		else:
-			SID = len(self.strings) + cffStandardStringCount
-			self.strings.append(s)
-			self.stringMapping[s] = SID
-		return SID
-	
-	def getStrings(self):
-		return self.strings
-	
-	def buildStringMapping(self):
-		self.stringMapping = {}
-		for index in range(len(self.strings)):
-			self.stringMapping[self.strings[index]] = index + cffStandardStringCount
-
-
-# The 391 Standard Strings as used in the CFF format.
-# from Adobe Technical None #5176, version 1.0, 18 March 1998
-
-cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign', 
-		'dollar', 'percent', 'ampersand', 'quoteright', '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', 'quoteleft', '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', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin', 
-		'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft', 
-		'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger', 
-		'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase', 
-		'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand', 
-		'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve', 
-		'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron', 
-		'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae', 
-		'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior', 
-		'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn', 
-		'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters', 
-		'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior', 
-		'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring', 
-		'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave', 
-		'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute', 
-		'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute', 
-		'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron', 
-		'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla', 
-		'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex', 
-		'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis', 
-		'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave', 
-		'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall', 
-		'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall', 
-		'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader', 
-		'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle', 
-		'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 
-		'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior', 
-		'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior', 
-		'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior', 
-		'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior', 
-		'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall', 
-		'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall', 
-		'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall', 
-		'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall', 
-		'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall', 
-		'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall', 
-		'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall', 
-		'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall', 
-		'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 
-		'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior', 
-		'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior', 
-		'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior', 
-		'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior', 
-		'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall', 
-		'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall', 
-		'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall', 
-		'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall', 
-		'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall', 
-		'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall', 
-		'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall', 
-		'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002', 
-		'001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman', 
-		'Semibold'
-]
-
-cffStandardStringCount = 391
-assert len(cffStandardStrings) == cffStandardStringCount
-# build reverse mapping
-cffStandardStringMapping = {}
-for _i in range(cffStandardStringCount):
-	cffStandardStringMapping[cffStandardStrings[_i]] = _i
-
-cffISOAdobeStrings = [".notdef", "space", "exclam", "quotedbl", "numbersign",
-"dollar", "percent", "ampersand", "quoteright", "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", "quoteleft", "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", "exclamdown", "cent",
-"sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle",
-"quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl",
-"endash", "dagger", "daggerdbl", "periodcentered", "paragraph", "bullet",
-"quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis",
-"perthousand", "questiondown", "grave", "acute", "circumflex", "tilde",
-"macron", "breve", "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut",
-"ogonek", "caron", "emdash", "AE", "ordfeminine", "Lslash", "Oslash", "OE",
-"ordmasculine", "ae", "dotlessi", "lslash", "oslash", "oe", "germandbls",
-"onesuperior", "logicalnot", "mu", "trademark", "Eth", "onehalf", "plusminus",
-"Thorn", "onequarter", "divide", "brokenbar", "degree", "thorn",
-"threequarters", "twosuperior", "registered", "minus", "eth", "multiply",
-"threesuperior", "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave",
-"Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex", "Edieresis", "Egrave",
-"Iacute", "Icircumflex", "Idieresis", "Igrave", "Ntilde", "Oacute",
-"Ocircumflex", "Odieresis", "Ograve", "Otilde", "Scaron", "Uacute",
-"Ucircumflex", "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron", "aacute",
-"acircumflex", "adieresis", "agrave", "aring", "atilde", "ccedilla", "eacute",
-"ecircumflex", "edieresis", "egrave", "iacute", "icircumflex", "idieresis",
-"igrave", "ntilde", "oacute", "ocircumflex", "odieresis", "ograve", "otilde",
-"scaron", "uacute", "ucircumflex", "udieresis", "ugrave", "yacute", "ydieresis",
-"zcaron"]
-
-cffISOAdobeStringCount = 229
-assert len(cffISOAdobeStrings) == cffISOAdobeStringCount
-
-cffIExpertStrings = [".notdef", "space", "exclamsmall", "Hungarumlautsmall",
-"dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall",
-"parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader",
-"comma", "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle",
-"twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle",
-"sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon", "semicolon",
-"commasuperior", "threequartersemdash", "periodsuperior", "questionsmall",
-"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
-"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
-"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
-"parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall",
-"Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall",
-"Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall",
-"Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall",
-"Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall",
-"exclamdownsmall", "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall",
-"Dieresissmall", "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall",
-"figuredash", "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall",
-"onequarter", "onehalf", "threequarters", "questiondownsmall", "oneeighth",
-"threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds",
-"zerosuperior", "onesuperior", "twosuperior", "threesuperior", "foursuperior",
-"fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior",
-"zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior",
-"fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior",
-"centinferior", "dollarinferior", "periodinferior", "commainferior",
-"Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall",
-"Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall",
-"Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall",
-"Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall",
-"Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall",
-"Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall",
-"Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall",
-"Ydieresissmall"]
-
-cffExpertStringCount = 166
-assert len(cffIExpertStrings) == cffExpertStringCount
-
-cffExpertSubsetStrings = [".notdef", "space", "dollaroldstyle",
-"dollarsuperior", "parenleftsuperior", "parenrightsuperior", "twodotenleader",
-"onedotenleader", "comma", "hyphen", "period", "fraction", "zerooldstyle",
-"oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle",
-"sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon",
-"semicolon", "commasuperior", "threequartersemdash", "periodsuperior",
-"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
-"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
-"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
-"parenrightinferior", "hyphensuperior", "colonmonetary", "onefitted", "rupiah",
-"centoldstyle", "figuredash", "hypheninferior", "onequarter", "onehalf",
-"threequarters", "oneeighth", "threeeighths", "fiveeighths", "seveneighths",
-"onethird", "twothirds", "zerosuperior", "onesuperior", "twosuperior",
-"threesuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior",
-"eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior",
-"threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior",
-"eightinferior", "nineinferior", "centinferior", "dollarinferior",
-"periodinferior", "commainferior"]
-
-cffExpertSubsetStringCount = 87
-assert len(cffExpertSubsetStrings) == cffExpertSubsetStringCount
diff --git a/Lib/fontTools/cffLib/__init__.py b/Lib/fontTools/cffLib/__init__.py
new file mode 100644
index 0000000..83526ed
--- /dev/null
+++ b/Lib/fontTools/cffLib/__init__.py
@@ -0,0 +1,2658 @@
+"""cffLib.py -- read/write tools for Adobe CFF fonts."""
+
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc import sstruct
+from fontTools.misc import psCharStrings
+from fontTools.misc.arrayTools import unionRect, intRect
+from fontTools.misc.textTools import safeEval
+from fontTools.ttLib import TTFont
+from fontTools.ttLib.tables.otBase import OTTableWriter
+from fontTools.ttLib.tables.otBase import OTTableReader
+from fontTools.ttLib.tables import otTables as ot
+import struct
+import logging
+import re
+
+# mute cffLib debug messages when running ttx in verbose mode
+DEBUG = logging.DEBUG - 1
+log = logging.getLogger(__name__)
+
+cffHeaderFormat = """
+	major:   B
+	minor:   B
+	hdrSize: B
+"""
+
+maxStackLimit = 513
+# maxstack operator has been deprecated. max stack is now always 513.
+
+
+class CFFFontSet(object):
+
+	def decompile(self, file, otFont, isCFF2=None):
+		self.otFont = otFont
+		sstruct.unpack(cffHeaderFormat, file.read(3), self)
+		if isCFF2 is not None:
+			# called from ttLib: assert 'major' as read from file matches the
+			# expected version
+			expected_major = (2 if isCFF2 else 1)
+			if self.major != expected_major:
+				raise ValueError(
+					"Invalid CFF 'major' version: expected %d, found %d" %
+					(expected_major, self.major))
+		else:
+			# use 'major' version from file to determine if isCFF2
+			assert self.major in (1, 2), "Unknown CFF format"
+			isCFF2 = self.major == 2
+		if not isCFF2:
+			self.offSize = struct.unpack("B", file.read(1))[0]
+			file.seek(self.hdrSize)
+			self.fontNames = list(tostr(s) for s in Index(file, isCFF2=isCFF2))
+			self.topDictIndex = TopDictIndex(file, isCFF2=isCFF2)
+			self.strings = IndexedStrings(file)
+		else:  # isCFF2
+			self.topDictSize = struct.unpack(">H", file.read(2))[0]
+			file.seek(self.hdrSize)
+			self.fontNames = ["CFF2Font"]
+			cff2GetGlyphOrder = otFont.getGlyphOrder
+			# in CFF2, offsetSize is the size of the TopDict data.
+			self.topDictIndex = TopDictIndex(
+				file, cff2GetGlyphOrder, self.topDictSize, isCFF2=isCFF2)
+			self.strings = None
+		self.GlobalSubrs = GlobalSubrsIndex(file, isCFF2=isCFF2)
+		self.topDictIndex.strings = self.strings
+		self.topDictIndex.GlobalSubrs = self.GlobalSubrs
+
+	def __len__(self):
+		return len(self.fontNames)
+
+	def keys(self):
+		return list(self.fontNames)
+
+	def values(self):
+		return self.topDictIndex
+
+	def __getitem__(self, nameOrIndex):
+		""" Return TopDict instance identified by name (str) or index (int
+		or any object that implements `__index__`).
+		"""
+		if hasattr(nameOrIndex, "__index__"):
+			index = nameOrIndex.__index__()
+		elif isinstance(nameOrIndex, basestring):
+			name = nameOrIndex
+			try:
+				index = self.fontNames.index(name)
+			except ValueError:
+				raise KeyError(nameOrIndex)
+		else:
+			raise TypeError(nameOrIndex)
+		return self.topDictIndex[index]
+
+	def compile(self, file, otFont, isCFF2=None):
+		self.otFont = otFont
+		if isCFF2 is not None:
+			# called from ttLib: assert 'major' value matches expected version
+			expected_major = (2 if isCFF2 else 1)
+			if self.major != expected_major:
+				raise ValueError(
+					"Invalid CFF 'major' version: expected %d, found %d" %
+					(expected_major, self.major))
+		else:
+			# use current 'major' value to determine output format
+			assert self.major in (1, 2), "Unknown CFF format"
+			isCFF2 = self.major == 2
+
+		if otFont.recalcBBoxes and not isCFF2:
+			for topDict in self.topDictIndex:
+				topDict.recalcFontBBox()
+
+		if not isCFF2:
+			strings = IndexedStrings()
+		else:
+			strings = None
+		writer = CFFWriter(isCFF2)
+		topCompiler = self.topDictIndex.getCompiler(strings, self, isCFF2=isCFF2)
+		if isCFF2:
+			self.hdrSize = 5
+			writer.add(sstruct.pack(cffHeaderFormat, self))
+			# Note: topDictSize will most likely change in CFFWriter.toFile().
+			self.topDictSize = topCompiler.getDataLength()
+			writer.add(struct.pack(">H", self.topDictSize))
+		else:
+			self.hdrSize = 4
+			self.offSize = 4  # will most likely change in CFFWriter.toFile().
+			writer.add(sstruct.pack(cffHeaderFormat, self))
+			writer.add(struct.pack("B", self.offSize))
+		if not isCFF2:
+			fontNames = Index()
+			for name in self.fontNames:
+				fontNames.append(name)
+			writer.add(fontNames.getCompiler(strings, self, isCFF2=isCFF2))
+		writer.add(topCompiler)
+		if not isCFF2:
+			writer.add(strings.getCompiler())
+		writer.add(self.GlobalSubrs.getCompiler(strings, self, isCFF2=isCFF2))
+
+		for topDict in self.topDictIndex:
+			if not hasattr(topDict, "charset") or topDict.charset is None:
+				charset = otFont.getGlyphOrder()
+				topDict.charset = charset
+		children = topCompiler.getChildren(strings)
+		for child in children:
+			writer.add(child)
+
+		writer.toFile(file)
+
+	def toXML(self, xmlWriter):
+		xmlWriter.simpletag("major", value=self.major)
+		xmlWriter.newline()
+		xmlWriter.simpletag("minor", value=self.minor)
+		xmlWriter.newline()
+		for fontName in self.fontNames:
+			xmlWriter.begintag("CFFFont", name=tostr(fontName))
+			xmlWriter.newline()
+			font = self[fontName]
+			font.toXML(xmlWriter)
+			xmlWriter.endtag("CFFFont")
+			xmlWriter.newline()
+		xmlWriter.newline()
+		xmlWriter.begintag("GlobalSubrs")
+		xmlWriter.newline()
+		self.GlobalSubrs.toXML(xmlWriter)
+		xmlWriter.endtag("GlobalSubrs")
+		xmlWriter.newline()
+
+	def fromXML(self, name, attrs, content, otFont=None):
+		self.otFont = otFont
+
+		# set defaults. These will be replaced if there are entries for them
+		# in the XML file.
+		if not hasattr(self, "major"):
+			self.major = 1
+		if not hasattr(self, "minor"):
+			self.minor = 0
+
+		if name == "CFFFont":
+			if self.major == 1:
+				if not hasattr(self, "offSize"):
+					# this will be recalculated when the cff is compiled.
+					self.offSize = 4
+				if not hasattr(self, "hdrSize"):
+					self.hdrSize = 4
+				if not hasattr(self, "GlobalSubrs"):
+					self.GlobalSubrs = GlobalSubrsIndex()
+				if not hasattr(self, "fontNames"):
+					self.fontNames = []
+					self.topDictIndex = TopDictIndex()
+				fontName = attrs["name"]
+				self.fontNames.append(fontName)
+				topDict = TopDict(GlobalSubrs=self.GlobalSubrs)
+				topDict.charset = None  # gets filled in later
+			elif self.major == 2:
+				if not hasattr(self, "hdrSize"):
+					self.hdrSize = 5
+				if not hasattr(self, "GlobalSubrs"):
+					self.GlobalSubrs = GlobalSubrsIndex()
+				if not hasattr(self, "fontNames"):
+					self.fontNames = ["CFF2Font"]
+				cff2GetGlyphOrder = self.otFont.getGlyphOrder
+				topDict = TopDict(
+					GlobalSubrs=self.GlobalSubrs,
+					cff2GetGlyphOrder=cff2GetGlyphOrder)
+				self.topDictIndex = TopDictIndex(None, cff2GetGlyphOrder, None)
+			self.topDictIndex.append(topDict)
+			for element in content:
+				if isinstance(element, basestring):
+					continue
+				name, attrs, content = element
+				topDict.fromXML(name, attrs, content)
+		elif name == "GlobalSubrs":
+			subrCharStringClass = psCharStrings.T2CharString
+			if not hasattr(self, "GlobalSubrs"):
+				self.GlobalSubrs = GlobalSubrsIndex()
+			for element in content:
+				if isinstance(element, basestring):
+					continue
+				name, attrs, content = element
+				subr = subrCharStringClass()
+				subr.fromXML(name, attrs, content)
+				self.GlobalSubrs.append(subr)
+		elif name == "major":
+			self.major = int(attrs['value'])
+		elif name == "minor":
+			self.minor = int(attrs['value'])
+
+	def convertCFFToCFF2(self, otFont):
+		# This assumes a decompiled CFF table.
+		self.major = 2
+		cff2GetGlyphOrder = self.otFont.getGlyphOrder
+		topDictData = TopDictIndex(None, cff2GetGlyphOrder, None)
+		topDictData.items = self.topDictIndex.items
+		self.topDictIndex = topDictData
+		topDict = topDictData[0]
+		if hasattr(topDict, 'Private'):
+			privateDict = topDict.Private
+		else:
+			privateDict = None
+		opOrder = buildOrder(topDictOperators2)
+		topDict.order = opOrder
+		topDict.cff2GetGlyphOrder = cff2GetGlyphOrder
+		for entry in topDictOperators:
+			key = entry[1]
+			if key not in opOrder:
+				if key in topDict.rawDict:
+					del topDict.rawDict[key]
+				if hasattr(topDict, key):
+					delattr(topDict, key)
+
+		if not hasattr(topDict, "FDArray"):
+			fdArray = topDict.FDArray = FDArrayIndex()
+			fdArray.strings = None
+			fdArray.GlobalSubrs = topDict.GlobalSubrs
+			topDict.GlobalSubrs.fdArray = fdArray
+			charStrings = topDict.CharStrings
+			if charStrings.charStringsAreIndexed:
+				charStrings.charStringsIndex.fdArray = fdArray
+			else:
+				charStrings.fdArray = fdArray
+			fontDict = FontDict()
+			fontDict.setCFF2(True)
+			fdArray.append(fontDict)
+			fontDict.Private = privateDict
+			privateOpOrder = buildOrder(privateDictOperators2)
+			for entry in privateDictOperators:
+				key = entry[1]
+				if key not in privateOpOrder:
+					if key in privateDict.rawDict:
+						# print "Removing private dict", key
+						del privateDict.rawDict[key]
+					if hasattr(privateDict, key):
+						delattr(privateDict, key)
+						# print "Removing privateDict attr", key
+		else:
+			# clean up the PrivateDicts in the fdArray
+			fdArray = topDict.FDArray
+			privateOpOrder = buildOrder(privateDictOperators2)
+			for fontDict in fdArray:
+				fontDict.setCFF2(True)
+				for key in fontDict.rawDict.keys():
+					if key not in fontDict.order:
+						del fontDict.rawDict[key]
+						if hasattr(fontDict, key):
+							delattr(fontDict, key)
+
+				privateDict = fontDict.Private
+				for entry in privateDictOperators:
+					key = entry[1]
+					if key not in privateOpOrder:
+						if key in privateDict.rawDict:
+							# print "Removing private dict", key
+							del privateDict.rawDict[key]
+						if hasattr(privateDict, key):
+							delattr(privateDict, key)
+							# print "Removing privateDict attr", key
+		# At this point, the Subrs and Charstrings are all still T2Charstring class
+		# easiest to fix this by compiling, then decompiling again
+		file = BytesIO()
+		self.compile(file, otFont, isCFF2=True)
+		file.seek(0)
+		self.decompile(file, otFont, isCFF2=True)
+
+
+class CFFWriter(object):
+
+	def __init__(self, isCFF2):
+		self.data = []
+		self.isCFF2 = isCFF2
+
+	def add(self, table):
+		self.data.append(table)
+
+	def toFile(self, file):
+		lastPosList = None
+		count = 1
+		while True:
+			log.log(DEBUG, "CFFWriter.toFile() iteration: %d", count)
+			count = count + 1
+			pos = 0
+			posList = [pos]
+			for item in self.data:
+				if hasattr(item, "getDataLength"):
+					endPos = pos + item.getDataLength()
+					if isinstance(item, TopDictIndexCompiler) and item.isCFF2:
+						self.topDictSize = item.getDataLength()
+				else:
+					endPos = pos + len(item)
+				if hasattr(item, "setPos"):
+					item.setPos(pos, endPos)
+				pos = endPos
+				posList.append(pos)
+			if posList == lastPosList:
+				break
+			lastPosList = posList
+		log.log(DEBUG, "CFFWriter.toFile() writing to file.")
+		begin = file.tell()
+		if self.isCFF2:
+			self.data[1] = struct.pack(">H", self.topDictSize)
+		else:
+			self.offSize = calcOffSize(lastPosList[-1])
+			self.data[1] = struct.pack("B", self.offSize)
+		posList = [0]
+		for item in self.data:
+			if hasattr(item, "toFile"):
+				item.toFile(file)
+			else:
+				file.write(item)
+			posList.append(file.tell() - begin)
+		assert posList == lastPosList
+
+
+def calcOffSize(largestOffset):
+	if largestOffset < 0x100:
+		offSize = 1
+	elif largestOffset < 0x10000:
+		offSize = 2
+	elif largestOffset < 0x1000000:
+		offSize = 3
+	else:
+		offSize = 4
+	return offSize
+
+
+class IndexCompiler(object):
+
+	def __init__(self, items, strings, parent, isCFF2=None):
+		if isCFF2 is None and hasattr(parent, "isCFF2"):
+			isCFF2 = parent.isCFF2
+			assert isCFF2 is not None
+		self.isCFF2 = isCFF2
+		self.items = self.getItems(items, strings)
+		self.parent = parent
+
+	def getItems(self, items, strings):
+		return items
+
+	def getOffsets(self):
+		# An empty INDEX contains only the count field.
+		if self.items:
+			pos = 1
+			offsets = [pos]
+			for item in self.items:
+				if hasattr(item, "getDataLength"):
+					pos = pos + item.getDataLength()
+				else:
+					pos = pos + len(item)
+				offsets.append(pos)
+		else:
+			offsets = []
+		return offsets
+
+	def getDataLength(self):
+		if self.isCFF2:
+			countSize = 4
+		else:
+			countSize = 2
+
+		if self.items:
+			lastOffset = self.getOffsets()[-1]
+			offSize = calcOffSize(lastOffset)
+			dataLength = (
+				countSize +                        # count
+				1 +                                # offSize
+				(len(self.items) + 1) * offSize +  # the offsets
+				lastOffset - 1                     # size of object data
+			)
+		else:
+			# count. For empty INDEX tables, this is the only entry.
+			dataLength = countSize
+
+		return dataLength
+
+	def toFile(self, file):
+		offsets = self.getOffsets()
+		if self.isCFF2:
+			writeCard32(file, len(self.items))
+		else:
+			writeCard16(file, len(self.items))
+		# An empty INDEX contains only the count field.
+		if self.items:
+			offSize = calcOffSize(offsets[-1])
+			writeCard8(file, offSize)
+			offSize = -offSize
+			pack = struct.pack
+			for offset in offsets:
+				binOffset = pack(">l", offset)[offSize:]
+				assert len(binOffset) == -offSize
+				file.write(binOffset)
+			for item in self.items:
+				if hasattr(item, "toFile"):
+					item.toFile(file)
+				else:
+					data = tobytes(item, encoding="latin1")
+					file.write(data)
+
+
+class IndexedStringsCompiler(IndexCompiler):
+
+	def getItems(self, items, strings):
+		return items.strings
+
+
+class TopDictIndexCompiler(IndexCompiler):
+
+	def getItems(self, items, strings):
+		out = []
+		for item in items:
+			out.append(item.getCompiler(strings, self))
+		return out
+
+	def getChildren(self, strings):
+		children = []
+		for topDict in self.items:
+			children.extend(topDict.getChildren(strings))
+		return children
+
+	def getOffsets(self):
+		if self.isCFF2:
+			offsets = [0, self.items[0].getDataLength()]
+			return offsets
+		else:
+			return super(TopDictIndexCompiler, self).getOffsets()
+
+	def getDataLength(self):
+		if self.isCFF2:
+			dataLength = self.items[0].getDataLength()
+			return dataLength
+		else:
+			return super(TopDictIndexCompiler, self).getDataLength()
+
+	def toFile(self, file):
+		if self.isCFF2:
+			self.items[0].toFile(file)
+		else:
+			super(TopDictIndexCompiler, self).toFile(file)
+
+
+class FDArrayIndexCompiler(IndexCompiler):
+
+	def getItems(self, items, strings):
+		out = []
+		for item in items:
+			out.append(item.getCompiler(strings, self))
+		return out
+
+	def getChildren(self, strings):
+		children = []
+		for fontDict in self.items:
+			children.extend(fontDict.getChildren(strings))
+		return children
+
+	def toFile(self, file):
+		offsets = self.getOffsets()
+		if self.isCFF2:
+			writeCard32(file, len(self.items))
+		else:
+			writeCard16(file, len(self.items))
+		offSize = calcOffSize(offsets[-1])
+		writeCard8(file, offSize)
+		offSize = -offSize
+		pack = struct.pack
+		for offset in offsets:
+			binOffset = pack(">l", offset)[offSize:]
+			assert len(binOffset) == -offSize
+			file.write(binOffset)
+		for item in self.items:
+			if hasattr(item, "toFile"):
+				item.toFile(file)
+			else:
+				file.write(item)
+
+	def setPos(self, pos, endPos):
+		self.parent.rawDict["FDArray"] = pos
+
+
+class GlobalSubrsCompiler(IndexCompiler):
+
+	def getItems(self, items, strings):
+		out = []
+		for cs in items:
+			cs.compile(self.isCFF2)
+			out.append(cs.bytecode)
+		return out
+
+
+class SubrsCompiler(GlobalSubrsCompiler):
+
+	def setPos(self, pos, endPos):
+		offset = pos - self.parent.pos
+		self.parent.rawDict["Subrs"] = offset
+
+
+class CharStringsCompiler(GlobalSubrsCompiler):
+
+	def getItems(self, items, strings):
+		out = []
+		for cs in items:
+			cs.compile(self.isCFF2)
+			out.append(cs.bytecode)
+		return out
+
+	def setPos(self, pos, endPos):
+		self.parent.rawDict["CharStrings"] = pos
+
+
+class Index(object):
+
+	"""This class represents what the CFF spec calls an INDEX."""
+
+	compilerClass = IndexCompiler
+
+	def __init__(self, file=None, isCFF2=None):
+		assert (isCFF2 is None) == (file is None)
+		self.items = []
+		name = self.__class__.__name__
+		if file is None:
+			return
+		self._isCFF2 = isCFF2
+		log.log(DEBUG, "loading %s at %s", name, file.tell())
+		self.file = file
+		if isCFF2:
+			count = readCard32(file)
+		else:
+			count = readCard16(file)
+		if count == 0:
+			return
+		self.items = [None] * count
+		offSize = readCard8(file)
+		log.log(DEBUG, "    index count: %s offSize: %s", count, offSize)
+		assert offSize <= 4, "offSize too large: %s" % offSize
+		self.offsets = offsets = []
+		pad = b'\0' * (4 - offSize)
+		for index in range(count + 1):
+			chunk = file.read(offSize)
+			chunk = pad + chunk
+			offset, = struct.unpack(">L", chunk)
+			offsets.append(int(offset))
+		self.offsetBase = file.tell() - 1
+		file.seek(self.offsetBase + offsets[-1])  # pretend we've read the whole lot
+		log.log(DEBUG, "    end of %s at %s", name, file.tell())
+
+	def __len__(self):
+		return len(self.items)
+
+	def __getitem__(self, index):
+		item = self.items[index]
+		if item is not None:
+			return item
+		offset = self.offsets[index] + self.offsetBase
+		size = self.offsets[index + 1] - self.offsets[index]
+		file = self.file
+		file.seek(offset)
+		data = file.read(size)
+		assert len(data) == size
+		item = self.produceItem(index, data, file, offset)
+		self.items[index] = item
+		return item
+
+	def __setitem__(self, index, item):
+		self.items[index] = item
+
+	def produceItem(self, index, data, file, offset):
+		return data
+
+	def append(self, item):
+		self.items.append(item)
+
+	def getCompiler(self, strings, parent, isCFF2=None):
+		return self.compilerClass(self, strings, parent, isCFF2=isCFF2)
+
+
+class GlobalSubrsIndex(Index):
+
+	compilerClass = GlobalSubrsCompiler
+	subrClass = psCharStrings.T2CharString
+	charStringClass = psCharStrings.T2CharString
+
+	def __init__(self, file=None, globalSubrs=None, private=None,
+			fdSelect=None, fdArray=None, isCFF2=None):
+		super(GlobalSubrsIndex, self).__init__(file, isCFF2=isCFF2)
+		self.globalSubrs = globalSubrs
+		self.private = private
+		if fdSelect:
+			self.fdSelect = fdSelect
+		if fdArray:
+			self.fdArray = fdArray
+		if isCFF2:
+			# CFF2Subr's can have numeric arguments on the stack after the last operator.
+			self.subrClass = psCharStrings.CFF2Subr
+			self.charStringClass = psCharStrings.CFF2Subr
+			
+
+	def produceItem(self, index, data, file, offset):
+		if self.private is not None:
+			private = self.private
+		elif hasattr(self, 'fdArray') and self.fdArray is not None:
+			if hasattr(self, 'fdSelect') and self.fdSelect is not None:
+				fdIndex = self.fdSelect[index]
+			else:
+				fdIndex = 0
+			private = self.fdArray[fdIndex].Private
+		else:
+			private = None
+		return self.subrClass(data, private=private, globalSubrs=self.globalSubrs)
+
+	def toXML(self, xmlWriter):
+		xmlWriter.comment(
+			"The 'index' attribute is only for humans; "
+			"it is ignored when parsed.")
+		xmlWriter.newline()
+		for i in range(len(self)):
+			subr = self[i]
+			if subr.needsDecompilation():
+				xmlWriter.begintag("CharString", index=i, raw=1)
+			else:
+				xmlWriter.begintag("CharString", index=i)
+			xmlWriter.newline()
+			subr.toXML(xmlWriter)
+			xmlWriter.endtag("CharString")
+			xmlWriter.newline()
+
+	def fromXML(self, name, attrs, content):
+		if name != "CharString":
+			return
+		subr = self.subrClass()
+		subr.fromXML(name, attrs, content)
+		self.append(subr)
+
+	def getItemAndSelector(self, index):
+		sel = None
+		if hasattr(self, 'fdSelect'):
+			sel = self.fdSelect[index]
+		return self[index], sel
+
+
+class SubrsIndex(GlobalSubrsIndex):
+	compilerClass = SubrsCompiler
+
+
+class TopDictIndex(Index):
+
+	compilerClass = TopDictIndexCompiler
+
+	def __init__(self, file=None, cff2GetGlyphOrder=None, topSize=0,
+			isCFF2=None):
+		assert (isCFF2 is None) == (file is None)
+		self.cff2GetGlyphOrder = cff2GetGlyphOrder
+		if file is not None and isCFF2:
+			self._isCFF2 = isCFF2
+			self.items = []
+			name = self.__class__.__name__
+			log.log(DEBUG, "loading %s at %s", name, file.tell())
+			self.file = file
+			count = 1
+			self.items = [None] * count
+			self.offsets = [0, topSize]
+			self.offsetBase = file.tell()
+			# pretend we've read the whole lot
+			file.seek(self.offsetBase + topSize)
+			log.log(DEBUG, "    end of %s at %s", name, file.tell())
+		else:
+			super(TopDictIndex, self).__init__(file, isCFF2=isCFF2)
+
+	def produceItem(self, index, data, file, offset):
+		top = TopDict(
+			self.strings, file, offset, self.GlobalSubrs,
+			self.cff2GetGlyphOrder, isCFF2=self._isCFF2)
+		top.decompile(data)
+		return top
+
+	def toXML(self, xmlWriter):
+		for i in range(len(self)):
+			xmlWriter.begintag("FontDict", index=i)
+			xmlWriter.newline()
+			self[i].toXML(xmlWriter)
+			xmlWriter.endtag("FontDict")
+			xmlWriter.newline()
+
+
+class FDArrayIndex(Index):
+
+	compilerClass = FDArrayIndexCompiler
+
+	def toXML(self, xmlWriter):
+		for i in range(len(self)):
+			xmlWriter.begintag("FontDict", index=i)
+			xmlWriter.newline()
+			self[i].toXML(xmlWriter)
+			xmlWriter.endtag("FontDict")
+			xmlWriter.newline()
+
+	def produceItem(self, index, data, file, offset):
+		fontDict = FontDict(
+			self.strings, file, offset, self.GlobalSubrs, isCFF2=self._isCFF2,
+			vstore=self.vstore)
+		fontDict.decompile(data)
+		return fontDict
+
+	def fromXML(self, name, attrs, content):
+		if name != "FontDict":
+			return
+		fontDict = FontDict()
+		for element in content:
+			if isinstance(element, basestring):
+				continue
+			name, attrs, content = element
+			fontDict.fromXML(name, attrs, content)
+		self.append(fontDict)
+
+
+class VarStoreData(object):
+
+	def __init__(self, file=None, otVarStore=None):
+		self.file = file
+		self.data = None
+		self.otVarStore = otVarStore
+		self.font = TTFont()  # dummy font for the decompile function.
+
+	def decompile(self):
+		if self.file:
+			class GlobalState(object):
+				def __init__(self, tableType, cachingStats):
+					self.tableType = tableType
+					self.cachingStats = cachingStats
+			globalState = GlobalState(tableType="VarStore", cachingStats={})
+			# read data in from file. Assume position is correct.
+			length = readCard16(self.file)
+			self.data = self.file.read(length)
+			globalState = {}
+			reader = OTTableReader(self.data, globalState)
+			self.otVarStore = ot.VarStore()
+			self.otVarStore.decompile(reader, self.font)
+		return self
+
+	def compile(self):
+		writer = OTTableWriter()
+		self.otVarStore.compile(writer, self.font)
+		# Note that this omits the initial Card16 length from the CFF2
+		# VarStore data block
+		self.data = writer.getAllData()
+
+	def writeXML(self, xmlWriter, name):
+		self.otVarStore.toXML(xmlWriter, self.font)
+
+	def xmlRead(self, name, attrs, content, parent):
+		self.otVarStore = ot.VarStore()
+		for element in content:
+			if isinstance(element, tuple):
+				name, attrs, content = element
+				self.otVarStore.fromXML(name, attrs, content, self.font)
+			else:
+				pass
+		return None
+
+	def __len__(self):
+		return len(self.data)
+
+	def getNumRegions(self, vsIndex):
+		varData = self.otVarStore.VarData[vsIndex]
+		numRegions = varData.VarRegionCount
+		return numRegions
+
+
+class FDSelect(object):
+
+	def __init__(self, file=None, numGlyphs=None, format=None):
+		if file:
+			# read data in from file
+			self.format = readCard8(file)
+			if self.format == 0:
+				from array import array
+				self.gidArray = array("B", file.read(numGlyphs)).tolist()
+			elif self.format == 3:
+				gidArray = [None] * numGlyphs
+				nRanges = readCard16(file)
+				fd = None
+				prev = None
+				for i in range(nRanges):
+					first = readCard16(file)
+					if prev is not None:
+						for glyphID in range(prev, first):
+							gidArray[glyphID] = fd
+					prev = first
+					fd = readCard8(file)
+				if prev is not None:
+					first = readCard16(file)
+					for glyphID in range(prev, first):
+						gidArray[glyphID] = fd
+				self.gidArray = gidArray
+			else:
+				assert False, "unsupported FDSelect format: %s" % format
+		else:
+			# reading from XML. Make empty gidArray, and leave format as passed in.
+			# format is None will result in the smallest representation being used.
+			self.format = format
+			self.gidArray = []
+
+	def __len__(self):
+		return len(self.gidArray)
+
+	def __getitem__(self, index):
+		return self.gidArray[index]
+
+	def __setitem__(self, index, fdSelectValue):
+		self.gidArray[index] = fdSelectValue
+
+	def append(self, fdSelectValue):
+		self.gidArray.append(fdSelectValue)
+
+
+class CharStrings(object):
+
+	def __init__(self, file, charset, globalSubrs, private, fdSelect, fdArray,
+			isCFF2=None):
+		self.globalSubrs = globalSubrs
+		if file is not None:
+			self.charStringsIndex = SubrsIndex(
+				file, globalSubrs, private, fdSelect, fdArray, isCFF2=isCFF2)
+			self.charStrings = charStrings = {}
+			for i in range(len(charset)):
+				charStrings[charset[i]] = i
+			# read from OTF file: charStrings.values() are indices into
+			# charStringsIndex.
+			self.charStringsAreIndexed = 1
+		else:
+			self.charStrings = {}
+			# read from ttx file: charStrings.values() are actual charstrings
+			self.charStringsAreIndexed = 0
+			self.private = private
+			if fdSelect is not None:
+				self.fdSelect = fdSelect
+			if fdArray is not None:
+				self.fdArray = fdArray
+
+	def keys(self):
+		return list(self.charStrings.keys())
+
+	def values(self):
+		if self.charStringsAreIndexed:
+			return self.charStringsIndex
+		else:
+			return list(self.charStrings.values())
+
+	def has_key(self, name):
+		return name in self.charStrings
+
+	__contains__ = has_key
+
+	def __len__(self):
+		return len(self.charStrings)
+
+	def __getitem__(self, name):
+		charString = self.charStrings[name]
+		if self.charStringsAreIndexed:
+			charString = self.charStringsIndex[charString]
+		return charString
+
+	def __setitem__(self, name, charString):
+		if self.charStringsAreIndexed:
+			index = self.charStrings[name]
+			self.charStringsIndex[index] = charString
+		else:
+			self.charStrings[name] = charString
+
+	def getItemAndSelector(self, name):
+		if self.charStringsAreIndexed:
+			index = self.charStrings[name]
+			return self.charStringsIndex.getItemAndSelector(index)
+		else:
+			if hasattr(self, 'fdArray'):
+				if hasattr(self, 'fdSelect'):
+					sel = self.charStrings[name].fdSelectIndex
+				else:
+					sel = 0
+			else:
+				sel = None
+			return self.charStrings[name], sel
+
+	def toXML(self, xmlWriter):
+		names = sorted(self.keys())
+		for name in names:
+			charStr, fdSelectIndex = self.getItemAndSelector(name)
+			if charStr.needsDecompilation():
+				raw = [("raw", 1)]
+			else:
+				raw = []
+			if fdSelectIndex is None:
+				xmlWriter.begintag("CharString", [('name', name)] + raw)
+			else:
+				xmlWriter.begintag(
+					"CharString",
+					[('name', name), ('fdSelectIndex', fdSelectIndex)] + raw)
+			xmlWriter.newline()
+			charStr.toXML(xmlWriter)
+			xmlWriter.endtag("CharString")
+			xmlWriter.newline()
+
+	def fromXML(self, name, attrs, content):
+		for element in content:
+			if isinstance(element, basestring):
+				continue
+			name, attrs, content = element
+			if name != "CharString":
+				continue
+			fdID = -1
+			if hasattr(self, "fdArray"):
+				try:
+					fdID = safeEval(attrs["fdSelectIndex"])
+				except KeyError:
+					fdID = 0
+				private = self.fdArray[fdID].Private
+			else:
+				private = self.private
+
+			glyphName = attrs["name"]
+			charStringClass = psCharStrings.T2CharString
+			charString = charStringClass(
+					private=private,
+					globalSubrs=self.globalSubrs)
+			charString.fromXML(name, attrs, content)
+			if fdID >= 0:
+				charString.fdSelectIndex = fdID
+			self[glyphName] = charString
+
+
+def readCard8(file):
+	return byteord(file.read(1))
+
+
+def readCard16(file):
+	value, = struct.unpack(">H", file.read(2))
+	return value
+
+
+def readCard32(file):
+	value, = struct.unpack(">L", file.read(4))
+	return value
+
+
+def writeCard8(file, value):
+	file.write(bytechr(value))
+
+
+def writeCard16(file, value):
+	file.write(struct.pack(">H", value))
+
+
+def writeCard32(file, value):
+	file.write(struct.pack(">L", value))
+
+
+def packCard8(value):
+	return bytechr(value)
+
+
+def packCard16(value):
+	return struct.pack(">H", value)
+
+
+def buildOperatorDict(table):
+	d = {}
+	for op, name, arg, default, conv in table:
+		d[op] = (name, arg)
+	return d
+
+
+def buildOpcodeDict(table):
+	d = {}
+	for op, name, arg, default, conv in table:
+		if isinstance(op, tuple):
+			op = bytechr(op[0]) + bytechr(op[1])
+		else:
+			op = bytechr(op)
+		d[name] = (op, arg)
+	return d
+
+
+def buildOrder(table):
+	l = []
+	for op, name, arg, default, conv in table:
+		l.append(name)
+	return l
+
+
+def buildDefaults(table):
+	d = {}
+	for op, name, arg, default, conv in table:
+		if default is not None:
+			d[name] = default
+	return d
+
+
+def buildConverters(table):
+	d = {}
+	for op, name, arg, default, conv in table:
+		d[name] = conv
+	return d
+
+
+class SimpleConverter(object):
+
+	def read(self, parent, value):
+		if not hasattr(parent, "file"):
+			return self._read(parent, value)
+		file = parent.file
+		pos = file.tell()
+		try:
+			return self._read(parent, value)
+		finally:
+			file.seek(pos)
+
+	def _read(self, parent, value):
+		return value
+
+	def write(self, parent, value):
+		return value
+
+	def xmlWrite(self, xmlWriter, name, value):
+		xmlWriter.simpletag(name, value=value)
+		xmlWriter.newline()
+
+	def xmlRead(self, name, attrs, content, parent):
+		return attrs["value"]
+
+
+class ASCIIConverter(SimpleConverter):
+
+	def _read(self, parent, value):
+		return tostr(value, encoding='ascii')
+
+	def write(self, parent, value):
+		return tobytes(value, encoding='ascii')
+
+	def xmlWrite(self, xmlWriter, name, value):
+		xmlWriter.simpletag(name, value=tounicode(value, encoding="ascii"))
+		xmlWriter.newline()
+
+	def xmlRead(self, name, attrs, content, parent):
+		return tobytes(attrs["value"], encoding=("ascii"))
+
+
+class Latin1Converter(SimpleConverter):
+
+	def _read(self, parent, value):
+		return tostr(value, encoding='latin1')
+
+	def write(self, parent, value):
+		return tobytes(value, encoding='latin1')
+
+	def xmlWrite(self, xmlWriter, name, value):
+		value = tounicode(value, encoding="latin1")
+		if name in ['Notice', 'Copyright']:
+			value = re.sub(r"[\r\n]\s+", " ", value)
+		xmlWriter.simpletag(name, value=value)
+		xmlWriter.newline()
+
+	def xmlRead(self, name, attrs, content, parent):
+		return tobytes(attrs["value"], encoding=("latin1"))
+
+
+def parseNum(s):
+	try:
+		value = int(s)
+	except:
+		value = float(s)
+	return value
+
+
+def parseBlendList(s):
+	valueList = []
+	for element in s:
+		if isinstance(element, basestring):
+			continue
+		name, attrs, content = element
+		blendList = attrs["value"].split()
+		blendList = [eval(val) for val in blendList]
+		valueList.append(blendList)
+	if len(valueList) == 1:
+		valueList = valueList[0]
+	return valueList
+
+
+class NumberConverter(SimpleConverter):
+	def xmlWrite(self, xmlWriter, name, value):
+		if isinstance(value, list):
+			xmlWriter.begintag(name)
+			xmlWriter.newline()
+			xmlWriter.indent()
+			blendValue = " ".join([str(val) for val in value])
+			xmlWriter.simpletag(kBlendDictOpName, value=blendValue)
+			xmlWriter.newline()
+			xmlWriter.dedent()
+			xmlWriter.endtag(name)
+			xmlWriter.newline()
+		else:
+			xmlWriter.simpletag(name, value=value)
+			xmlWriter.newline()
+
+	def xmlRead(self, name, attrs, content, parent):
+		valueString = attrs.get("value", None)
+		if valueString is None:
+			value = parseBlendList(content)
+		else:
+			value = parseNum(attrs["value"])
+		return value
+
+
+class ArrayConverter(SimpleConverter):
+	def xmlWrite(self, xmlWriter, name, value):
+		if value and isinstance(value[0], list):
+			xmlWriter.begintag(name)
+			xmlWriter.newline()
+			xmlWriter.indent()
+			for valueList in value:
+				blendValue = " ".join([str(val) for val in valueList])
+				xmlWriter.simpletag(kBlendDictOpName, value=blendValue)
+				xmlWriter.newline()
+			xmlWriter.dedent()
+			xmlWriter.endtag(name)
+			xmlWriter.newline()
+		else:
+			value = " ".join([str(val) for val in value])
+			xmlWriter.simpletag(name, value=value)
+			xmlWriter.newline()
+
+	def xmlRead(self, name, attrs, content, parent):
+		valueString = attrs.get("value", None)
+		if valueString is None:
+			valueList = parseBlendList(content)
+		else:
+			values = valueString.split()
+			valueList = [parseNum(value) for value in values]
+		return valueList
+
+
+class TableConverter(SimpleConverter):
+
+	def xmlWrite(self, xmlWriter, name, value):
+		xmlWriter.begintag(name)
+		xmlWriter.newline()
+		value.toXML(xmlWriter)
+		xmlWriter.endtag(name)
+		xmlWriter.newline()
+
+	def xmlRead(self, name, attrs, content, parent):
+		ob = self.getClass()()
+		for element in content:
+			if isinstance(element, basestring):
+				continue
+			name, attrs, content = element
+			ob.fromXML(name, attrs, content)
+		return ob
+
+
+class PrivateDictConverter(TableConverter):
+
+	def getClass(self):
+		return PrivateDict
+
+	def _read(self, parent, value):
+		size, offset = value
+		file = parent.file
+		isCFF2 = parent._isCFF2
+		try:
+			vstore = parent.vstore
+		except AttributeError:
+			vstore = None
+		priv = PrivateDict(
+			parent.strings, file, offset, isCFF2=isCFF2, vstore=vstore)
+		file.seek(offset)
+		data = file.read(size)
+		assert len(data) == size
+		priv.decompile(data)
+		return priv
+
+	def write(self, parent, value):
+		return (0, 0)  # dummy value
+
+
+class SubrsConverter(TableConverter):
+
+	def getClass(self):
+		return SubrsIndex
+
+	def _read(self, parent, value):
+		file = parent.file
+		isCFF2 = parent._isCFF2
+		file.seek(parent.offset + value)  # Offset(self)
+		return SubrsIndex(file, isCFF2=isCFF2)
+
+	def write(self, parent, value):
+		return 0  # dummy value
+
+
+class CharStringsConverter(TableConverter):
+
+	def _read(self, parent, value):
+		file = parent.file
+		isCFF2 = parent._isCFF2
+		charset = parent.charset
+		globalSubrs = parent.GlobalSubrs
+		if hasattr(parent, "FDArray"):
+			fdArray = parent.FDArray
+			if hasattr(parent, "FDSelect"):
+				fdSelect = parent.FDSelect
+			else:
+				fdSelect = None
+			private = None
+		else:
+			fdSelect, fdArray = None, None
+			private = parent.Private
+		file.seek(value)  # Offset(0)
+		charStrings = CharStrings(
+			file, charset, globalSubrs, private, fdSelect, fdArray, isCFF2=isCFF2)
+		return charStrings
+
+	def write(self, parent, value):
+		return 0  # dummy value
+
+	def xmlRead(self, name, attrs, content, parent):
+		if hasattr(parent, "FDArray"):
+			# if it is a CID-keyed font, then the private Dict is extracted from the
+			# parent.FDArray
+			fdArray = parent.FDArray
+			if hasattr(parent, "FDSelect"):
+				fdSelect = parent.FDSelect
+			else:
+				fdSelect = None
+			private = None
+		else:
+			# if it is a name-keyed font, then the private dict is in the top dict,
+			# and
+			# there is no fdArray.
+			private, fdSelect, fdArray = parent.Private, None, None
+		charStrings = CharStrings(
+			None, None, parent.GlobalSubrs, private, fdSelect, fdArray)
+		charStrings.fromXML(name, attrs, content)
+		return charStrings
+
+
+class CharsetConverter(SimpleConverter):
+	def _read(self, parent, value):
+		isCID = hasattr(parent, "ROS")
+		if value > 2:
+			numGlyphs = parent.numGlyphs
+			file = parent.file
+			file.seek(value)
+			log.log(DEBUG, "loading charset at %s", value)
+			format = readCard8(file)
+			if format == 0:
+				charset = parseCharset0(numGlyphs, file, parent.strings, isCID)
+			elif format == 1 or format == 2:
+				charset = parseCharset(numGlyphs, file, parent.strings, isCID, format)
+			else:
+				raise NotImplementedError
+			assert len(charset) == numGlyphs
+			log.log(DEBUG, "    charset end at %s", file.tell())
+		else:  # offset == 0 -> no charset data.
+			if isCID or "CharStrings" not in parent.rawDict:
+				# We get here only when processing fontDicts from the FDArray of
+				# CFF-CID fonts. Only the real topDict references the chrset.
+				assert value == 0
+				charset = None
+			elif value == 0:
+				charset = cffISOAdobeStrings
+			elif value == 1:
+				charset = cffIExpertStrings
+			elif value == 2:
+				charset = cffExpertSubsetStrings
+		if charset and (len(charset) != parent.numGlyphs):
+			charset = charset[:parent.numGlyphs]
+		return charset
+
+	def write(self, parent, value):
+		return 0  # dummy value
+
+	def xmlWrite(self, xmlWriter, name, value):
+		# XXX only write charset when not in OT/TTX context, where we
+		# dump charset as a separate "GlyphOrder" table.
+		# # xmlWriter.simpletag("charset")
+		xmlWriter.comment("charset is dumped separately as the 'GlyphOrder' element")
+		xmlWriter.newline()
+
+	def xmlRead(self, name, attrs, content, parent):
+		pass
+
+
+class CharsetCompiler(object):
+
+	def __init__(self, strings, charset, parent):
+		assert charset[0] == '.notdef'
+		isCID = hasattr(parent.dictObj, "ROS")
+		data0 = packCharset0(charset, isCID, strings)
+		data = packCharset(charset, isCID, strings)
+		if len(data) < len(data0):
+			self.data = data
+		else:
+			self.data = data0
+		self.parent = parent
+
+	def setPos(self, pos, endPos):
+		self.parent.rawDict["charset"] = pos
+
+	def getDataLength(self):
+		return len(self.data)
+
+	def toFile(self, file):
+		file.write(self.data)
+
+
+def getStdCharSet(charset):
+	# check to see if we can use a predefined charset value.
+	predefinedCharSetVal = None
+	predefinedCharSets = [
+		(cffISOAdobeStringCount, cffISOAdobeStrings, 0),
+		(cffExpertStringCount, cffIExpertStrings, 1),
+		(cffExpertSubsetStringCount, cffExpertSubsetStrings, 2)]
+	lcs = len(charset)
+	for cnt, pcs, csv in predefinedCharSets:
+		if predefinedCharSetVal is not None:
+			break
+		if lcs > cnt:
+			continue
+		predefinedCharSetVal = csv
+		for i in range(lcs):
+			if charset[i] != pcs[i]:
+				predefinedCharSetVal = None
+				break
+	return predefinedCharSetVal
+
+
+def getCIDfromName(name, strings):
+	return int(name[3:])
+
+
+def getSIDfromName(name, strings):
+	return strings.getSID(name)
+
+
+def packCharset0(charset, isCID, strings):
+	fmt = 0
+	data = [packCard8(fmt)]
+	if isCID:
+		getNameID = getCIDfromName
+	else:
+		getNameID = getSIDfromName
+
+	for name in charset[1:]:
+		data.append(packCard16(getNameID(name, strings)))
+	return bytesjoin(data)
+
+
+def packCharset(charset, isCID, strings):
+	fmt = 1
+	ranges = []
+	first = None
+	end = 0
+	if isCID:
+		getNameID = getCIDfromName
+	else:
+		getNameID = getSIDfromName
+
+	for name in charset[1:]:
+		SID = getNameID(name, strings)
+		if first is None:
+			first = SID
+		elif end + 1 != SID:
+			nLeft = end - first
+			if nLeft > 255:
+				fmt = 2
+			ranges.append((first, nLeft))
+			first = SID
+		end = SID
+	if end:
+		nLeft = end - first
+		if nLeft > 255:
+			fmt = 2
+		ranges.append((first, nLeft))
+
+	data = [packCard8(fmt)]
+	if fmt == 1:
+		nLeftFunc = packCard8
+	else:
+		nLeftFunc = packCard16
+	for first, nLeft in ranges:
+		data.append(packCard16(first) + nLeftFunc(nLeft))
+	return bytesjoin(data)
+
+
+def parseCharset0(numGlyphs, file, strings, isCID):
+	charset = [".notdef"]
+	if isCID:
+		for i in range(numGlyphs - 1):
+			CID = readCard16(file)
+			charset.append("cid" + str(CID).zfill(5))
+	else:
+		for i in range(numGlyphs - 1):
+			SID = readCard16(file)
+			charset.append(strings[SID])
+	return charset
+
+
+def parseCharset(numGlyphs, file, strings, isCID, fmt):
+	charset = ['.notdef']
+	count = 1
+	if fmt == 1:
+		nLeftFunc = readCard8
+	else:
+		nLeftFunc = readCard16
+	while count < numGlyphs:
+		first = readCard16(file)
+		nLeft = nLeftFunc(file)
+		if isCID:
+			for CID in range(first, first + nLeft + 1):
+				charset.append("cid" + str(CID).zfill(5))
+		else:
+			for SID in range(first, first + nLeft + 1):
+				charset.append(strings[SID])
+		count = count + nLeft + 1
+	return charset
+
+
+class EncodingCompiler(object):
+
+	def __init__(self, strings, encoding, parent):
+		assert not isinstance(encoding, basestring)
+		data0 = packEncoding0(parent.dictObj.charset, encoding, parent.strings)
+		data1 = packEncoding1(parent.dictObj.charset, encoding, parent.strings)
+		if len(data0) < len(data1):
+			self.data = data0
+		else:
+			self.data = data1
+		self.parent = parent
+
+	def setPos(self, pos, endPos):
+		self.parent.rawDict["Encoding"] = pos
+
+	def getDataLength(self):
+		return len(self.data)
+
+	def toFile(self, file):
+		file.write(self.data)
+
+
+class EncodingConverter(SimpleConverter):
+
+	def _read(self, parent, value):
+		if value == 0:
+			return "StandardEncoding"
+		elif value == 1:
+			return "ExpertEncoding"
+		else:
+			assert value > 1
+			file = parent.file
+			file.seek(value)
+			log.log(DEBUG, "loading Encoding at %s", value)
+			fmt = readCard8(file)
+			haveSupplement = fmt & 0x80
+			if haveSupplement:
+				raise NotImplementedError("Encoding supplements are not yet supported")
+			fmt = fmt & 0x7f
+			if fmt == 0:
+				encoding = parseEncoding0(parent.charset, file, haveSupplement,
+						parent.strings)
+			elif fmt == 1:
+				encoding = parseEncoding1(parent.charset, file, haveSupplement,
+						parent.strings)
+			return encoding
+
+	def write(self, parent, value):
+		if value == "StandardEncoding":
+			return 0
+		elif value == "ExpertEncoding":
+			return 1
+		return 0  # dummy value
+
+	def xmlWrite(self, xmlWriter, name, value):
+		if value in ("StandardEncoding", "ExpertEncoding"):
+			xmlWriter.simpletag(name, name=value)
+			xmlWriter.newline()
+			return
+		xmlWriter.begintag(name)
+		xmlWriter.newline()
+		for code in range(len(value)):
+			glyphName = value[code]
+			if glyphName != ".notdef":
+				xmlWriter.simpletag("map", code=hex(code), name=glyphName)
+				xmlWriter.newline()
+		xmlWriter.endtag(name)
+		xmlWriter.newline()
+
+	def xmlRead(self, name, attrs, content, parent):
+		if "name" in attrs:
+			return attrs["name"]
+		encoding = [".notdef"] * 256
+		for element in content:
+			if isinstance(element, basestring):
+				continue
+			name, attrs, content = element
+			code = safeEval(attrs["code"])
+			glyphName = attrs["name"]
+			encoding[code] = glyphName
+		return encoding
+
+
+def parseEncoding0(charset, file, haveSupplement, strings):
+	nCodes = readCard8(file)
+	encoding = [".notdef"] * 256
+	for glyphID in range(1, nCodes + 1):
+		code = readCard8(file)
+		if code != 0:
+			encoding[code] = charset[glyphID]
+	return encoding
+
+
+def parseEncoding1(charset, file, haveSupplement, strings):
+	nRanges = readCard8(file)
+	encoding = [".notdef"] * 256
+	glyphID = 1
+	for i in range(nRanges):
+		code = readCard8(file)
+		nLeft = readCard8(file)
+		for glyphID in range(glyphID, glyphID + nLeft + 1):
+			encoding[code] = charset[glyphID]
+			code = code + 1
+		glyphID = glyphID + 1
+	return encoding
+
+
+def packEncoding0(charset, encoding, strings):
+	fmt = 0
+	m = {}
+	for code in range(len(encoding)):
+		name = encoding[code]
+		if name != ".notdef":
+			m[name] = code
+	codes = []
+	for name in charset[1:]:
+		code = m.get(name)
+		codes.append(code)
+
+	while codes and codes[-1] is None:
+		codes.pop()
+
+	data = [packCard8(fmt), packCard8(len(codes))]
+	for code in codes:
+		if code is None:
+			code = 0
+		data.append(packCard8(code))
+	return bytesjoin(data)
+
+
+def packEncoding1(charset, encoding, strings):
+	fmt = 1
+	m = {}
+	for code in range(len(encoding)):
+		name = encoding[code]
+		if name != ".notdef":
+			m[name] = code
+	ranges = []
+	first = None
+	end = 0
+	for name in charset[1:]:
+		code = m.get(name, -1)
+		if first is None:
+			first = code
+		elif end + 1 != code:
+			nLeft = end - first
+			ranges.append((first, nLeft))
+			first = code
+		end = code
+	nLeft = end - first
+	ranges.append((first, nLeft))
+
+	# remove unencoded glyphs at the end.
+	while ranges and ranges[-1][0] == -1:
+		ranges.pop()
+
+	data = [packCard8(fmt), packCard8(len(ranges))]
+	for first, nLeft in ranges:
+		if first == -1:  # unencoded
+			first = 0
+		data.append(packCard8(first) + packCard8(nLeft))
+	return bytesjoin(data)
+
+
+class FDArrayConverter(TableConverter):
+
+	def _read(self, parent, value):
+		try:
+			vstore = parent.VarStore
+		except AttributeError:
+			vstore = None
+		file = parent.file
+		isCFF2 = parent._isCFF2
+		file.seek(value)
+		fdArray = FDArrayIndex(file, isCFF2=isCFF2)
+		fdArray.vstore = vstore
+		fdArray.strings = parent.strings
+		fdArray.GlobalSubrs = parent.GlobalSubrs
+		return fdArray
+
+	def write(self, parent, value):
+		return 0  # dummy value
+
+	def xmlRead(self, name, attrs, content, parent):
+		fdArray = FDArrayIndex()
+		for element in content:
+			if isinstance(element, basestring):
+				continue
+			name, attrs, content = element
+			fdArray.fromXML(name, attrs, content)
+		return fdArray
+
+
+class FDSelectConverter(SimpleConverter):
+
+	def _read(self, parent, value):
+		file = parent.file
+		file.seek(value)
+		fdSelect = FDSelect(file, parent.numGlyphs)
+		return fdSelect
+
+	def write(self, parent, value):
+		return 0  # dummy value
+
+	# The FDSelect glyph data is written out to XML in the charstring keys,
+	# so we write out only the format selector
+	def xmlWrite(self, xmlWriter, name, value):
+		xmlWriter.simpletag(name, [('format', value.format)])
+		xmlWriter.newline()
+
+	def xmlRead(self, name, attrs, content, parent):
+		fmt = safeEval(attrs["format"])
+		file = None
+		numGlyphs = None
+		fdSelect = FDSelect(file, numGlyphs, fmt)
+		return fdSelect
+
+
+class VarStoreConverter(SimpleConverter):
+
+	def _read(self, parent, value):
+		file = parent.file
+		file.seek(value)
+		varStore = VarStoreData(file)
+		varStore.decompile()
+		return varStore
+
+	def write(self, parent, value):
+		return 0  # dummy value
+
+	def xmlWrite(self, xmlWriter, name, value):
+		value.writeXML(xmlWriter, name)
+
+	def xmlRead(self, name, attrs, content, parent):
+		varStore = VarStoreData()
+		varStore.xmlRead(name, attrs, content, parent)
+		return varStore
+
+
+def packFDSelect0(fdSelectArray):
+	fmt = 0
+	data = [packCard8(fmt)]
+	for index in fdSelectArray:
+		data.append(packCard8(index))
+	return bytesjoin(data)
+
+
+def packFDSelect3(fdSelectArray):
+	fmt = 3
+	fdRanges = []
+	lenArray = len(fdSelectArray)
+	lastFDIndex = -1
+	for i in range(lenArray):
+		fdIndex = fdSelectArray[i]
+		if lastFDIndex != fdIndex:
+			fdRanges.append([i, fdIndex])
+			lastFDIndex = fdIndex
+	sentinelGID = i + 1
+
+	data = [packCard8(fmt)]
+	data.append(packCard16(len(fdRanges)))
+	for fdRange in fdRanges:
+		data.append(packCard16(fdRange[0]))
+		data.append(packCard8(fdRange[1]))
+	data.append(packCard16(sentinelGID))
+	return bytesjoin(data)
+
+
+class FDSelectCompiler(object):
+
+	def __init__(self, fdSelect, parent):
+		fmt = fdSelect.format
+		fdSelectArray = fdSelect.gidArray
+		if fmt == 0:
+			self.data = packFDSelect0(fdSelectArray)
+		elif fmt == 3:
+			self.data = packFDSelect3(fdSelectArray)
+		else:
+			# choose smaller of the two formats
+			data0 = packFDSelect0(fdSelectArray)
+			data3 = packFDSelect3(fdSelectArray)
+			if len(data0) < len(data3):
+				self.data = data0
+				fdSelect.format = 0
+			else:
+				self.data = data3
+				fdSelect.format = 3
+
+		self.parent = parent
+
+	def setPos(self, pos, endPos):
+		self.parent.rawDict["FDSelect"] = pos
+
+	def getDataLength(self):
+		return len(self.data)
+
+	def toFile(self, file):
+		file.write(self.data)
+
+
+class VarStoreCompiler(object):
+
+	def __init__(self, varStoreData, parent):
+		self.parent = parent
+		if not varStoreData.data:
+			varStoreData.compile()
+		data = [
+			packCard16(len(varStoreData.data)),
+			varStoreData.data
+		]
+		self.data = bytesjoin(data)
+
+	def setPos(self, pos, endPos):
+		self.parent.rawDict["VarStore"] = pos
+
+	def getDataLength(self):
+		return len(self.data)
+
+	def toFile(self, file):
+		file.write(self.data)
+
+
+class ROSConverter(SimpleConverter):
+
+	def xmlWrite(self, xmlWriter, name, value):
+		registry, order, supplement = value
+		xmlWriter.simpletag(
+			name,
+			[
+				('Registry', tostr(registry)),
+				('Order', tostr(order)),
+				('Supplement', supplement)
+			])
+		xmlWriter.newline()
+
+	def xmlRead(self, name, attrs, content, parent):
+		return (attrs['Registry'], attrs['Order'], safeEval(attrs['Supplement']))
+
+topDictOperators = [
+#	opcode		name			argument type	default	converter
+	(25,		'maxstack',		'number',	None,	None),
+	((12, 30),	'ROS',	('SID', 'SID', 'number'),	None,	ROSConverter()),
+	((12, 20),	'SyntheticBase',	'number',	None,	None),
+	(0,		'version',		'SID',		None,	None),
+	(1,		'Notice',		'SID',		None,	Latin1Converter()),
+	((12, 0),	'Copyright',		'SID',		None,	Latin1Converter()),
+	(2,		'FullName',		'SID',		None,	None),
+	((12, 38),	'FontName',		'SID',		None,	None),
+	(3,		'FamilyName',		'SID',		None,	None),
+	(4,		'Weight',		'SID',		None,	None),
+	((12, 1),	'isFixedPitch',		'number',	0,	None),
+	((12, 2),	'ItalicAngle',		'number',	0,	None),
+	((12, 3),	'UnderlinePosition',	'number',	-100,	None),
+	((12, 4),	'UnderlineThickness',	'number',	50,	None),
+	((12, 5),	'PaintType',		'number',	0,	None),
+	((12, 6),	'CharstringType',	'number',	2,	None),
+	((12, 7),	'FontMatrix',		'array',	[0.001, 0, 0, 0.001, 0, 0],	None),
+	(13,		'UniqueID',		'number',	None,	None),
+	(5,		'FontBBox',		'array',	[0, 0, 0, 0],	None),
+	((12, 8),	'StrokeWidth',		'number',	0,	None),
+	(14,		'XUID',			'array',	None,	None),
+	((12, 21),	'PostScript',		'SID',		None,	None),
+	((12, 22),	'BaseFontName',		'SID',		None,	None),
+	((12, 23),	'BaseFontBlend',	'delta',	None,	None),
+	((12, 31),	'CIDFontVersion',	'number',	0,	None),
+	((12, 32),	'CIDFontRevision',	'number',	0,	None),
+	((12, 33),	'CIDFontType',		'number',	0,	None),
+	((12, 34),	'CIDCount',		'number',	8720,	None),
+	(15,		'charset',		'number',	None,	CharsetConverter()),
+	((12, 35),	'UIDBase',		'number',	None,	None),
+	(16,		'Encoding',		'number',	0,	EncodingConverter()),
+	(18,		'Private',	('number', 'number'),	None,	PrivateDictConverter()),
+	((12, 37),	'FDSelect',		'number',	None,	FDSelectConverter()),
+	((12, 36),	'FDArray',		'number',	None,	FDArrayConverter()),
+	(17,		'CharStrings',		'number',	None,	CharStringsConverter()),
+	(24,		'VarStore',		'number',	None,	VarStoreConverter()),
+]
+
+topDictOperators2 = [
+#	opcode		name			argument type	default	converter
+	(25,		'maxstack',		'number',	None,	None),
+	((12, 7),	'FontMatrix',		'array',	[0.001, 0, 0, 0.001, 0, 0],	None),
+	((12, 37),	'FDSelect',		'number',	None,	FDSelectConverter()),
+	((12, 36),	'FDArray',		'number',	None,	FDArrayConverter()),
+	(17,		'CharStrings',		'number',	None,	CharStringsConverter()),
+	(24,		'VarStore',		'number',	None,	VarStoreConverter()),
+]
+
+# Note! FDSelect and FDArray must both preceed CharStrings in the output XML build order,
+# in order for the font to compile back from xml.
+
+kBlendDictOpName = "blend"
+blendOp = 23
+
+privateDictOperators = [
+#	opcode		name			argument type	default	converter
+	(22,	"vsindex",		'number',	None,	None),
+	(blendOp,	kBlendDictOpName,		'blendList',	None,	None), # This is for reading to/from XML: it not written to CFF.
+	(6,		'BlueValues',		'delta',	None,	None),
+	(7,		'OtherBlues',		'delta',	None,	None),
+	(8,		'FamilyBlues',		'delta',	None,	None),
+	(9,		'FamilyOtherBlues',	'delta',	None,	None),
+	((12, 9),	'BlueScale',		'number',	0.039625, None),
+	((12, 10),	'BlueShift',		'number',	7,	None),
+	((12, 11),	'BlueFuzz',		'number',	1,	None),
+	(10,		'StdHW',		'number',	None,	None),
+	(11,		'StdVW',		'number',	None,	None),
+	((12, 12),	'StemSnapH',		'delta',	None,	None),
+	((12, 13),	'StemSnapV',		'delta',	None,	None),
+	((12, 14),	'ForceBold',		'number',	0,	None),
+	((12, 15),	'ForceBoldThreshold',	'number',	None,	None), # deprecated
+	((12, 16),	'lenIV',		'number',	None,	None), # deprecated
+	((12, 17),	'LanguageGroup',	'number',	0,	None),
+	((12, 18),	'ExpansionFactor',	'number',	0.06,	None),
+	((12, 19),	'initialRandomSeed',	'number',	0,	None),
+	(20,		'defaultWidthX',	'number',	0,	None),
+	(21,		'nominalWidthX',	'number',	0,	None),
+	(19,		'Subrs',		'number',	None,	SubrsConverter()),
+]
+
+privateDictOperators2 = [
+#	opcode		name			argument type	default	converter
+	(22,	"vsindex",		'number',	None,	None),
+	(blendOp,	kBlendDictOpName,		'blendList',	None,	None), # This is for reading to/from XML: it not written to CFF.
+	(6,		'BlueValues',		'delta',	None,	None),
+	(7,		'OtherBlues',		'delta',	None,	None),
+	(8,		'FamilyBlues',		'delta',	None,	None),
+	(9,		'FamilyOtherBlues',	'delta',	None,	None),
+	((12, 9),	'BlueScale',		'number',	0.039625, None),
+	((12, 10),	'BlueShift',		'number',	7,	None),
+	((12, 11),	'BlueFuzz',		'number',	1,	None),
+	(10,		'StdHW',		'number',	None,	None),
+	(11,		'StdVW',		'number',	None,	None),
+	((12, 12),	'StemSnapH',		'delta',	None,	None),
+	((12, 13),	'StemSnapV',		'delta',	None,	None),
+	(19,		'Subrs',		'number',	None,	SubrsConverter()),
+]
+
+
+def addConverters(table):
+	for i in range(len(table)):
+		op, name, arg, default, conv = table[i]
+		if conv is not None:
+			continue
+		if arg in ("delta", "array"):
+			conv = ArrayConverter()
+		elif arg == "number":
+			conv = NumberConverter()
+		elif arg == "SID":
+			conv = ASCIIConverter()
+		elif arg == 'blendList':
+			conv = None
+		else:
+			assert False
+		table[i] = op, name, arg, default, conv
+
+
+addConverters(privateDictOperators)
+addConverters(topDictOperators)
+
+
+class TopDictDecompiler(psCharStrings.DictDecompiler):
+	operators = buildOperatorDict(topDictOperators)
+
+
+class PrivateDictDecompiler(psCharStrings.DictDecompiler):
+	operators = buildOperatorDict(privateDictOperators)
+
+
+class DictCompiler(object):
+	maxBlendStack = 0
+
+	def __init__(self, dictObj, strings, parent, isCFF2=None):
+		if strings:
+			assert isinstance(strings, IndexedStrings)
+		if isCFF2 is None and hasattr(parent, "isCFF2"):
+			isCFF2 = parent.isCFF2
+			assert isCFF2 is not None
+		self.isCFF2 = isCFF2
+		self.dictObj = dictObj
+		self.strings = strings
+		self.parent = parent
+		rawDict = {}
+		for name in dictObj.order:
+			value = getattr(dictObj, name, None)
+			if value is None:
+				continue
+			conv = dictObj.converters[name]
+			value = conv.write(dictObj, value)
+			if value == dictObj.defaults.get(name):
+				continue
+			rawDict[name] = value
+		self.rawDict = rawDict
+
+	def setPos(self, pos, endPos):
+		pass
+
+	def getDataLength(self):
+		return len(self.compile("getDataLength"))
+
+	def compile(self, reason):
+		log.log(DEBUG, "-- compiling %s for %s", self.__class__.__name__, reason)
+		rawDict = self.rawDict
+		data = []
+		for name in self.dictObj.order:
+			value = rawDict.get(name)
+			if value is None:
+				continue
+			op, argType = self.opcodes[name]
+			if isinstance(argType, tuple):
+				l = len(argType)
+				assert len(value) == l, "value doesn't match arg type"
+				for i in range(l):
+					arg = argType[i]
+					v = value[i]
+					arghandler = getattr(self, "arg_" + arg)
+					data.append(arghandler(v))
+			else:
+				arghandler = getattr(self, "arg_" + argType)
+				data.append(arghandler(value))
+			data.append(op)
+		data = bytesjoin(data)
+		return data
+
+	def toFile(self, file):
+		data = self.compile("toFile")
+		file.write(data)
+
+	def arg_number(self, num):
+		if isinstance(num, list):
+			data = [encodeNumber(val) for val in num]
+			data.append(encodeNumber(1))
+			data.append(bytechr(blendOp))
+			datum = bytesjoin(data)
+		else:
+			datum = encodeNumber(num)
+		return datum
+
+	def arg_SID(self, s):
+		return psCharStrings.encodeIntCFF(self.strings.getSID(s))
+
+	def arg_array(self, value):
+		data = []
+		for num in value:
+			data.append(self.arg_number(num))
+		return bytesjoin(data)
+
+	def arg_delta(self, value):
+		if not value:
+			return b""
+		val0 = value[0]
+		if isinstance(val0, list):
+			data = self.arg_delta_blend(value)
+		else:
+			out = []
+			last = 0
+			for v in value:
+				out.append(v - last)
+				last = v
+			data = []
+			for num in out:
+				data.append(encodeNumber(num))
+		return bytesjoin(data)
+
+
+	def arg_delta_blend(self, value):
+		""" A delta list with blend lists has to be *all* blend lists.
+		The value is a list is arranged as follows.
+		[
+		   [V0, d0..dn] 
+		   [V1, d0..dn]
+		   ...
+		   [Vm, d0..dn]
+		]
+		V is the absolute coordinate value from the default font, and d0-dn are
+		the delta values from the n regions. Each V is an absolute coordinate
+		from the default font.
+		We want to return a list:
+		[
+		   [v0, v1..vm] 
+		   [d0..dn]
+		   ...
+		   [d0..dn]
+		   numBlends
+		   blendOp
+		]
+		where each v is relative to the previous default font value.
+		"""
+		numMasters = len(value[0])
+		numBlends = len(value)
+		numStack = (numBlends * numMasters) + 1
+		if numStack > self.maxBlendStack:
+			# Figure out the max number of value we can blend
+			# and divide this list up into chunks of that size.
+
+			numBlendValues = int((self.maxBlendStack - 1) / numMasters)
+			out = []
+			while True:
+				numVal = min(len(value), numBlendValues)
+				if numVal == 0:
+					break
+				valList = value[0:numVal]
+				out1 = self.arg_delta_blend(valList)
+				out.extend(out1)
+				value = value[numVal:]
+		else:
+			firstList = [0] * numBlends
+			deltaList = [None] * numBlends
+			i = 0
+			prevVal = 0
+			while i < numBlends:
+				# For PrivateDict BlueValues, the default font
+				# values are absolute, not relative.
+				# Must convert these back to relative coordinates
+				# befor writing to CFF2.
+				defaultValue = value[i][0]
+				firstList[i] = defaultValue - prevVal
+				prevVal = defaultValue
+				deltaList[i] = value[i][1:]
+				i += 1
+
+			relValueList = firstList
+			for blendList in deltaList:
+				relValueList.extend(blendList)
+			out = [encodeNumber(val) for val in relValueList]
+			out.append(encodeNumber(numBlends))
+			out.append(bytechr(blendOp))
+		return out
+
+
+def encodeNumber(num):
+	if isinstance(num, float):
+		return psCharStrings.encodeFloat(num)
+	else:
+		return psCharStrings.encodeIntCFF(num)
+
+
+class TopDictCompiler(DictCompiler):
+
+	opcodes = buildOpcodeDict(topDictOperators)
+
+	def getChildren(self, strings):
+		isCFF2 = self.isCFF2
+		children = []
+		if self.dictObj.cff2GetGlyphOrder is None:
+			if hasattr(self.dictObj, "charset") and self.dictObj.charset:
+				if hasattr(self.dictObj, "ROS"):  # aka isCID
+					charsetCode = None
+				else:
+					charsetCode = getStdCharSet(self.dictObj.charset)
+				if charsetCode is None:
+					children.append(CharsetCompiler(strings, self.dictObj.charset, self))
+				else:
+					self.rawDict["charset"] = charsetCode
+			if hasattr(self.dictObj, "Encoding") and self.dictObj.Encoding:
+				encoding = self.dictObj.Encoding
+				if not isinstance(encoding, basestring):
+					children.append(EncodingCompiler(strings, encoding, self))
+		else:
+			if hasattr(self.dictObj, "VarStore"):
+				varStoreData = self.dictObj.VarStore
+				varStoreComp = VarStoreCompiler(varStoreData, self)
+				children.append(varStoreComp)
+		if hasattr(self.dictObj, "FDSelect"):
+			# I have not yet supported merging a ttx CFF-CID font, as there are
+			# interesting issues about merging the FDArrays. Here I assume that
+			# either the font was read from XML, and the FDSelect indices are all
+			# in the charstring data, or the FDSelect array is already fully defined.
+			fdSelect = self.dictObj.FDSelect
+			# probably read in from XML; assume fdIndex in CharString data
+			if len(fdSelect) == 0:
+				charStrings = self.dictObj.CharStrings
+				for name in self.dictObj.charset:
+					fdSelect.append(charStrings[name].fdSelectIndex)
+			fdSelectComp = FDSelectCompiler(fdSelect, self)
+			children.append(fdSelectComp)
+		if hasattr(self.dictObj, "CharStrings"):
+			items = []
+			charStrings = self.dictObj.CharStrings
+			for name in self.dictObj.charset:
+				items.append(charStrings[name])
+			charStringsComp = CharStringsCompiler(
+				items, strings, self, isCFF2=isCFF2)
+			children.append(charStringsComp)
+		if hasattr(self.dictObj, "FDArray"):
+			# I have not yet supported merging a ttx CFF-CID font, as there are
+			# interesting issues about merging the FDArrays. Here I assume that the
+			# FDArray info is correct and complete.
+			fdArrayIndexComp = self.dictObj.FDArray.getCompiler(strings, self)
+			children.append(fdArrayIndexComp)
+			children.extend(fdArrayIndexComp.getChildren(strings))
+		if hasattr(self.dictObj, "Private"):
+			privComp = self.dictObj.Private.getCompiler(strings, self)
+			children.append(privComp)
+			children.extend(privComp.getChildren(strings))
+		return children
+
+
+class FontDictCompiler(DictCompiler):
+	opcodes = buildOpcodeDict(topDictOperators)
+
+	def __init__(self, dictObj, strings, parent, isCFF2=None):
+		super(FontDictCompiler, self).__init__(dictObj, strings, parent, isCFF2=isCFF2)
+		#
+		# We now take some effort to detect if there were any key/value pairs
+		# supplied that were ignored in the FontDict context, and issue a warning
+		# for those cases.
+		#
+		ignoredNames = []
+		dictObj = self.dictObj
+		for name in sorted(set(dictObj.converters) - set(dictObj.order)):
+			if name in dictObj.rawDict:
+				# The font was directly read from binary. In this
+				# case, we want to report *all* "useless" key/value
+				# pairs that are in the font, not just the ones that
+				# are different from the default.
+				ignoredNames.append(name)
+			else:
+				# The font was probably read from a TTX file. We only
+				# warn about keys whos value is not the default. The
+				# ones that have the default value will not be written
+				# to binary anyway.
+				default = dictObj.defaults.get(name)
+				if default is not None:
+					conv = dictObj.converters[name]
+					default = conv.read(dictObj, default)
+				if getattr(dictObj, name, None) != default:
+					ignoredNames.append(name)
+		if ignoredNames:
+			log.warning(
+				"Some CFF FDArray/FontDict keys were ignored upon compile: " +
+				" ".join(sorted(ignoredNames)))
+
+	def getChildren(self, strings):
+		children = []
+		if hasattr(self.dictObj, "Private"):
+			privComp = self.dictObj.Private.getCompiler(strings, self)
+			children.append(privComp)
+			children.extend(privComp.getChildren(strings))
+		return children
+
+
+class PrivateDictCompiler(DictCompiler):
+
+	maxBlendStack = maxStackLimit
+	opcodes = buildOpcodeDict(privateDictOperators)
+
+	def setPos(self, pos, endPos):
+		size = endPos - pos
+		self.parent.rawDict["Private"] = size, pos
+		self.pos = pos
+
+	def getChildren(self, strings):
+		children = []
+		if hasattr(self.dictObj, "Subrs"):
+			children.append(self.dictObj.Subrs.getCompiler(strings, self))
+		return children
+
+
+class BaseDict(object):
+
+	def __init__(self, strings=None, file=None, offset=None, isCFF2=None):
+		assert (isCFF2 is None) == (file is None)
+		self.rawDict = {}
+		self.skipNames = []
+		self.strings = strings
+		if file is None:
+			return
+		self._isCFF2 = isCFF2
+		self.file = file
+		if offset is not None:
+			log.log(DEBUG, "loading %s at %s", self.__class__.__name__, offset)
+			self.offset = offset
+
+	def decompile(self, data):
+		log.log(DEBUG, "    length %s is %d", self.__class__.__name__, len(data))
+		dec = self.decompilerClass(self.strings, self)
+		dec.decompile(data)
+		self.rawDict = dec.getDict()
+		self.postDecompile()
+
+	def postDecompile(self):
+		pass
+
+	def getCompiler(self, strings, parent, isCFF2=None):
+		return self.compilerClass(self, strings, parent, isCFF2=isCFF2)
+
+	def __getattr__(self, name):
+		value = self.rawDict.get(name, None)
+		if value is None:
+			value = self.defaults.get(name)
+		if value is None:
+			raise AttributeError(name)
+		conv = self.converters[name]
+		value = conv.read(self, value)
+		setattr(self, name, value)
+		return value
+
+	def toXML(self, xmlWriter):
+		for name in self.order:
+			if name in self.skipNames:
+				continue
+			value = getattr(self, name, None)
+			# XXX For "charset" we never skip calling xmlWrite even if the
+			# value is None, so we always write the following XML comment:
+			#
+			# <!-- charset is dumped separately as the 'GlyphOrder' element -->
+			#
+			# Charset is None when 'CFF ' table is imported from XML into an
+			# empty TTFont(). By writing this comment all the time, we obtain
+			# the same XML output whether roundtripping XML-to-XML or
+			# dumping binary-to-XML
+			if value is None and name != "charset":
+				continue
+			conv = self.converters[name]
+			conv.xmlWrite(xmlWriter, name, value)
+		ignoredNames = set(self.rawDict) - set(self.order)
+		if ignoredNames:
+			xmlWriter.comment(
+				"some keys were ignored: %s" % " ".join(sorted(ignoredNames)))
+			xmlWriter.newline()
+
+	def fromXML(self, name, attrs, content):
+		conv = self.converters[name]
+		value = conv.xmlRead(name, attrs, content, self)
+		setattr(self, name, value)
+
+
+class TopDict(BaseDict):
+
+	defaults = buildDefaults(topDictOperators)
+	converters = buildConverters(topDictOperators)
+	compilerClass = TopDictCompiler
+	order = buildOrder(topDictOperators)
+	decompilerClass = TopDictDecompiler
+
+	def __init__(self, strings=None, file=None, offset=None,
+			GlobalSubrs=None, cff2GetGlyphOrder=None, isCFF2=None):
+		super(TopDict, self).__init__(strings, file, offset, isCFF2=isCFF2)
+		self.cff2GetGlyphOrder = cff2GetGlyphOrder
+		self.GlobalSubrs = GlobalSubrs
+		if isCFF2:
+			self.defaults = buildDefaults(topDictOperators2)
+			self.charset = cff2GetGlyphOrder()
+			self.order = buildOrder(topDictOperators2)
+		else:
+			self.defaults = buildDefaults(topDictOperators)
+			self.order = buildOrder(topDictOperators)
+
+	def getGlyphOrder(self):
+		return self.charset
+
+	def postDecompile(self):
+		offset = self.rawDict.get("CharStrings")
+		if offset is None:
+			return
+		# get the number of glyphs beforehand.
+		self.file.seek(offset)
+		if self._isCFF2:
+			self.numGlyphs = readCard32(self.file)
+		else:
+			self.numGlyphs = readCard16(self.file)
+
+	def toXML(self, xmlWriter):
+		if hasattr(self, "CharStrings"):
+			self.decompileAllCharStrings()
+		if hasattr(self, "ROS"):
+			self.skipNames = ['Encoding']
+		if not hasattr(self, "ROS") or not hasattr(self, "CharStrings"):
+			# these values have default values, but I only want them to show up
+			# in CID fonts.
+			self.skipNames = [
+				'CIDFontVersion', 'CIDFontRevision', 'CIDFontType', 'CIDCount']
+		BaseDict.toXML(self, xmlWriter)
+
+	def decompileAllCharStrings(self):
+		# Make sure that all the Private Dicts have been instantiated.
+		for charString in self.CharStrings.values():
+			try:
+				charString.decompile()
+			except:
+				log.error("Error in charstring %s", i)
+				raise
+
+	def recalcFontBBox(self):
+		fontBBox = None
+		for charString in self.CharStrings.values():
+			bounds = charString.calcBounds(self.CharStrings)
+			if bounds is not None:
+				if fontBBox is not None:
+					fontBBox = unionRect(fontBBox, bounds)
+				else:
+					fontBBox = bounds
+
+		if fontBBox is None:
+			self.FontBBox = self.defaults['FontBBox'][:]
+		else:
+			self.FontBBox = list(intRect(fontBBox))
+
+
+class FontDict(BaseDict):
+	#
+	# Since fonttools used to pass a lot of fields that are not relevant in the FDArray
+	# FontDict, there are 'ttx' files in the wild that contain all these. These got in
+	# the ttx files because fonttools writes explicit values for all the TopDict default
+	# values. These are not actually illegal in the context of an FDArray FontDict - you
+	# can legally, per spec, put any arbitrary key/value pair in a FontDict - but are
+	# useless since current major company CFF interpreters ignore anything but the set
+	# listed in this file. So, we just silently skip them. An exception is Weight: this
+	# is not used by any interpreter, but some foundries have asked that this be
+	# supported in FDArray FontDicts just to preserve information about the design when
+	# the font is being inspected.
+	#
+	# On top of that, there are fonts out there that contain such useless FontDict values.
+	#
+	# By subclassing TopDict, we *allow* all key/values from TopDict, both when reading
+	# from binary or when reading from XML, but by overriding `order` with a limited
+	# list of names, we ensure that only the useful names ever get exported to XML and
+	# ever get compiled into the binary font.
+	#
+	# We override compilerClass so we can warn about "useless" key/value pairs, either
+	# from the original binary font or from TTX input.
+	#
+	# See:
+	# - https://github.com/fonttools/fonttools/issues/740
+	# - https://github.com/fonttools/fonttools/issues/601
+	# - https://github.com/adobe-type-tools/afdko/issues/137
+	#
+	defaults = {}
+	converters = buildConverters(topDictOperators)
+	compilerClass = FontDictCompiler
+	orderCFF = ['FontName', 'FontMatrix', 'Weight', 'Private']
+	orderCFF2 = ['Private']
+	decompilerClass = TopDictDecompiler
+
+	def __init__(self, strings=None, file=None, offset=None,
+			GlobalSubrs=None, isCFF2=None, vstore=None):
+		super(FontDict, self).__init__(strings, file, offset, isCFF2=isCFF2)
+		self.vstore = vstore
+		self.setCFF2(isCFF2)
+
+	def setCFF2(self, isCFF2):
+		# isCFF2 may be None.
+		if isCFF2:
+			self.order = self.orderCFF2
+			self._isCFF2 = True
+		else:
+			self.order = self.orderCFF
+			self._isCFF2 = False
+
+
+class PrivateDict(BaseDict):
+	defaults = buildDefaults(privateDictOperators)
+	converters = buildConverters(privateDictOperators)
+	order = buildOrder(privateDictOperators)
+	decompilerClass = PrivateDictDecompiler
+	compilerClass = PrivateDictCompiler
+
+	def __init__(self, strings=None, file=None, offset=None, isCFF2=None,
+			vstore=None):
+		super(PrivateDict, self).__init__(strings, file, offset, isCFF2=isCFF2)
+		self.vstore = vstore
+		if isCFF2:
+			self.defaults = buildDefaults(privateDictOperators2)
+			self.order = buildOrder(privateDictOperators2)
+		else:
+			self.defaults = buildDefaults(privateDictOperators)
+			self.order = buildOrder(privateDictOperators)
+
+	def getNumRegions(self, vi=None):  # called from misc/psCharStrings.py
+		# if getNumRegions is being called, we can assume that VarStore exists.
+		if vi is None:
+			if hasattr(self, 'vsindex'):
+				vi = self.vsindex
+			else:
+				vi = 0
+		numRegions = self.vstore.getNumRegions(vi)
+		return numRegions
+
+
+class IndexedStrings(object):
+
+	"""SID -> string mapping."""
+
+	def __init__(self, file=None):
+		if file is None:
+			strings = []
+		else:
+			strings = [
+				tostr(s, encoding="latin1")
+				for s in Index(file, isCFF2=False)
+			]
+		self.strings = strings
+
+	def getCompiler(self):
+		return IndexedStringsCompiler(self, None, self, isCFF2=False)
+
+	def __len__(self):
+		return len(self.strings)
+
+	def __getitem__(self, SID):
+		if SID < cffStandardStringCount:
+			return cffStandardStrings[SID]
+		else:
+			return self.strings[SID - cffStandardStringCount]
+
+	def getSID(self, s):
+		if not hasattr(self, "stringMapping"):
+			self.buildStringMapping()
+		s = tostr(s, encoding="latin1")
+		if s in cffStandardStringMapping:
+			SID = cffStandardStringMapping[s]
+		elif s in self.stringMapping:
+			SID = self.stringMapping[s]
+		else:
+			SID = len(self.strings) + cffStandardStringCount
+			self.strings.append(s)
+			self.stringMapping[s] = SID
+		return SID
+
+	def getStrings(self):
+		return self.strings
+
+	def buildStringMapping(self):
+		self.stringMapping = {}
+		for index in range(len(self.strings)):
+			self.stringMapping[self.strings[index]] = index + cffStandardStringCount
+
+
+# The 391 Standard Strings as used in the CFF format.
+# from Adobe Technical None #5176, version 1.0, 18 March 1998
+
+cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign',
+		'dollar', 'percent', 'ampersand', 'quoteright', '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', 'quoteleft', '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', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin',
+		'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft',
+		'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger',
+		'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase',
+		'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand',
+		'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve',
+		'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron',
+		'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae',
+		'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior',
+		'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn',
+		'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters',
+		'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior',
+		'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring',
+		'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave',
+		'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute',
+		'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute',
+		'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron',
+		'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
+		'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
+		'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
+		'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
+		'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall',
+		'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall',
+		'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
+		'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle',
+		'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle',
+		'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
+		'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
+		'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior',
+		'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior',
+		'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
+		'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall',
+		'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
+		'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall',
+		'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
+		'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall',
+		'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
+		'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
+		'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths',
+		'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior',
+		'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior',
+		'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior',
+		'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior',
+		'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
+		'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall',
+		'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall',
+		'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
+		'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
+		'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall',
+		'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
+		'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002',
+		'001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman',
+		'Semibold'
+]
+
+cffStandardStringCount = 391
+assert len(cffStandardStrings) == cffStandardStringCount
+# build reverse mapping
+cffStandardStringMapping = {}
+for _i in range(cffStandardStringCount):
+	cffStandardStringMapping[cffStandardStrings[_i]] = _i
+
+cffISOAdobeStrings = [".notdef", "space", "exclam", "quotedbl", "numbersign",
+"dollar", "percent", "ampersand", "quoteright", "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", "quoteleft", "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", "exclamdown", "cent",
+"sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle",
+"quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl",
+"endash", "dagger", "daggerdbl", "periodcentered", "paragraph", "bullet",
+"quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis",
+"perthousand", "questiondown", "grave", "acute", "circumflex", "tilde",
+"macron", "breve", "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut",
+"ogonek", "caron", "emdash", "AE", "ordfeminine", "Lslash", "Oslash", "OE",
+"ordmasculine", "ae", "dotlessi", "lslash", "oslash", "oe", "germandbls",
+"onesuperior", "logicalnot", "mu", "trademark", "Eth", "onehalf", "plusminus",
+"Thorn", "onequarter", "divide", "brokenbar", "degree", "thorn",
+"threequarters", "twosuperior", "registered", "minus", "eth", "multiply",
+"threesuperior", "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave",
+"Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex", "Edieresis", "Egrave",
+"Iacute", "Icircumflex", "Idieresis", "Igrave", "Ntilde", "Oacute",
+"Ocircumflex", "Odieresis", "Ograve", "Otilde", "Scaron", "Uacute",
+"Ucircumflex", "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron", "aacute",
+"acircumflex", "adieresis", "agrave", "aring", "atilde", "ccedilla", "eacute",
+"ecircumflex", "edieresis", "egrave", "iacute", "icircumflex", "idieresis",
+"igrave", "ntilde", "oacute", "ocircumflex", "odieresis", "ograve", "otilde",
+"scaron", "uacute", "ucircumflex", "udieresis", "ugrave", "yacute", "ydieresis",
+"zcaron"]
+
+cffISOAdobeStringCount = 229
+assert len(cffISOAdobeStrings) == cffISOAdobeStringCount
+
+cffIExpertStrings = [".notdef", "space", "exclamsmall", "Hungarumlautsmall",
+"dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall",
+"parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader",
+"comma", "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle",
+"twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle",
+"sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon", "semicolon",
+"commasuperior", "threequartersemdash", "periodsuperior", "questionsmall",
+"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
+"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
+"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
+"parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall",
+"Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall",
+"Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall",
+"Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall",
+"Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall",
+"exclamdownsmall", "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall",
+"Dieresissmall", "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall",
+"figuredash", "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall",
+"onequarter", "onehalf", "threequarters", "questiondownsmall", "oneeighth",
+"threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds",
+"zerosuperior", "onesuperior", "twosuperior", "threesuperior", "foursuperior",
+"fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior",
+"zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior",
+"fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior",
+"centinferior", "dollarinferior", "periodinferior", "commainferior",
+"Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall",
+"Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall",
+"Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall",
+"Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall",
+"Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall",
+"Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall",
+"Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall",
+"Ydieresissmall"]
+
+cffExpertStringCount = 166
+assert len(cffIExpertStrings) == cffExpertStringCount
+
+cffExpertSubsetStrings = [".notdef", "space", "dollaroldstyle",
+"dollarsuperior", "parenleftsuperior", "parenrightsuperior", "twodotenleader",
+"onedotenleader", "comma", "hyphen", "period", "fraction", "zerooldstyle",
+"oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle",
+"sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon",
+"semicolon", "commasuperior", "threequartersemdash", "periodsuperior",
+"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
+"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
+"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
+"parenrightinferior", "hyphensuperior", "colonmonetary", "onefitted", "rupiah",
+"centoldstyle", "figuredash", "hypheninferior", "onequarter", "onehalf",
+"threequarters", "oneeighth", "threeeighths", "fiveeighths", "seveneighths",
+"onethird", "twothirds", "zerosuperior", "onesuperior", "twosuperior",
+"threesuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior",
+"eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior",
+"threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior",
+"eightinferior", "nineinferior", "centinferior", "dollarinferior",
+"periodinferior", "commainferior"]
+
+cffExpertSubsetStringCount = 87
+assert len(cffExpertSubsetStrings) == cffExpertSubsetStringCount
diff --git a/Lib/fontTools/cffLib/specializer.py b/Lib/fontTools/cffLib/specializer.py
new file mode 100644
index 0000000..7f19faf
--- /dev/null
+++ b/Lib/fontTools/cffLib/specializer.py
@@ -0,0 +1,551 @@
+# -*- coding: utf-8 -*-
+
+"""T2CharString operator specializer and generalizer."""
+
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+
+
+def stringToProgram(string):
+	if isinstance(string, basestring):
+		string = string.split()
+	program = []
+	for token in string:
+		try:
+			token = int(token)
+		except ValueError:
+			try:
+				token = float(token)
+			except ValueError:
+				pass
+		program.append(token)
+	return program
+
+def programToString(program):
+	return ' '.join(str(x) for x in program)
+
+
+def programToCommands(program):
+	"""Takes a T2CharString program list and returns list of commands.
+	Each command is a two-tuple of commandname,arg-list.  The commandname might
+	be empty string if no commandname shall be emitted (used for glyph width,
+	hintmask/cntrmask argument, as well as stray arguments at the end of the
+	program (¯\_(ツ)_/¯)."""
+
+	width = None
+	commands = []
+	stack = []
+	it = iter(program)
+	for token in it:
+		if not isinstance(token, basestring):
+			stack.append(token)
+			continue
+
+		if width is None and token in {'hstem', 'hstemhm', 'vstem', 'vstemhm',
+					       'cntrmask', 'hintmask',
+					       'hmoveto', 'vmoveto', 'rmoveto',
+					       'endchar'}:
+			parity = token in {'hmoveto', 'vmoveto'}
+			if stack and (len(stack) % 2) ^ parity:
+				width = stack.pop(0)
+				commands.append(('', [width]))
+
+		if token in {'hintmask', 'cntrmask'}:
+			if stack:
+				commands.append(('', stack))
+			commands.append((token, []))
+			commands.append(('', [next(it)]))
+		else:
+			commands.append((token,stack))
+		stack = []
+	if stack:
+		commands.append(('', stack))
+	return commands
+
+def commandsToProgram(commands):
+	"""Takes a commands list as returned by programToCommands() and converts
+	it back to a T2CharString program list."""
+	program = []
+	for op,args in commands:
+		program.extend(args)
+		if op:
+			program.append(op)
+	return program
+
+
+def _everyN(el, n):
+	"""Group the list el into groups of size n"""
+	if len(el) % n != 0: raise ValueError(el)
+	for i in range(0, len(el), n):
+		yield el[i:i+n]
+
+
+class _GeneralizerDecombinerCommandsMap(object):
+
+	@staticmethod
+	def rmoveto(args):
+		if len(args) != 2: raise ValueError(args)
+		yield ('rmoveto', args)
+	@staticmethod
+	def hmoveto(args):
+		if len(args) != 1: raise ValueError(args)
+		yield ('rmoveto', [args[0], 0])
+	@staticmethod
+	def vmoveto(args):
+		if len(args) != 1: raise ValueError(args)
+		yield ('rmoveto', [0, args[0]])
+
+	@staticmethod
+	def rlineto(args):
+		if not args: raise ValueError(args)
+		for args in _everyN(args, 2):
+			yield ('rlineto', args)
+	@staticmethod
+	def hlineto(args):
+		if not args: raise ValueError(args)
+		it = iter(args)
+		try:
+			while True:
+				yield ('rlineto', [next(it), 0])
+				yield ('rlineto', [0, next(it)])
+		except StopIteration:
+			pass
+	@staticmethod
+	def vlineto(args):
+		if not args: raise ValueError(args)
+		it = iter(args)
+		try:
+			while True:
+				yield ('rlineto', [0, next(it)])
+				yield ('rlineto', [next(it), 0])
+		except StopIteration:
+			pass
+	@staticmethod
+	def rrcurveto(args):
+		if not args: raise ValueError(args)
+		for args in _everyN(args, 6):
+			yield ('rrcurveto', args)
+	@staticmethod
+	def hhcurveto(args):
+		if len(args) < 4 or len(args) % 4 > 1: raise ValueError(args)
+		if len(args) % 2 == 1:
+			yield ('rrcurveto', [args[1], args[0], args[2], args[3], args[4], 0])
+			args = args[5:]
+		for args in _everyN(args, 4):
+			yield ('rrcurveto', [args[0], 0, args[1], args[2], args[3], 0])
+	@staticmethod
+	def vvcurveto(args):
+		if len(args) < 4 or len(args) % 4 > 1: raise ValueError(args)
+		if len(args) % 2 == 1:
+			yield ('rrcurveto', [args[0], args[1], args[2], args[3], 0, args[4]])
+			args = args[5:]
+		for args in _everyN(args, 4):
+			yield ('rrcurveto', [0, args[0], args[1], args[2], 0, args[3]])
+	@staticmethod
+	def hvcurveto(args):
+		if len(args) < 4 or len(args) % 8 not in {0,1,4,5}: raise ValueError(args)
+		last_args = None
+		if len(args) % 2 == 1:
+			lastStraight = len(args) % 8 == 5
+			args, last_args = args[:-5], args[-5:]
+		it = _everyN(args, 4)
+		try:
+			while True:
+				args = next(it)
+				yield ('rrcurveto', [args[0], 0, args[1], args[2], 0, args[3]])
+				args = next(it)
+				yield ('rrcurveto', [0, args[0], args[1], args[2], args[3], 0])
+		except StopIteration:
+			pass
+		if last_args:
+			args = last_args
+			if lastStraight:
+				yield ('rrcurveto', [args[0], 0, args[1], args[2], args[4], args[3]])
+			else:
+				yield ('rrcurveto', [0, args[0], args[1], args[2], args[3], args[4]])
+	@staticmethod
+	def vhcurveto(args):
+		if len(args) < 4 or len(args) % 8 not in {0,1,4,5}: raise ValueError(args)
+		last_args = None
+		if len(args) % 2 == 1:
+			lastStraight = len(args) % 8 == 5
+			args, last_args = args[:-5], args[-5:]
+		it = _everyN(args, 4)
+		try:
+			while True:
+				args = next(it)
+				yield ('rrcurveto', [0, args[0], args[1], args[2], args[3], 0])
+				args = next(it)
+				yield ('rrcurveto', [args[0], 0, args[1], args[2], 0, args[3]])
+		except StopIteration:
+			pass
+		if last_args:
+			args = last_args
+			if lastStraight:
+				yield ('rrcurveto', [0, args[0], args[1], args[2], args[3], args[4]])
+			else:
+				yield ('rrcurveto', [args[0], 0, args[1], args[2], args[4], args[3]])
+
+	@staticmethod
+	def rcurveline(args):
+		if len(args) < 8 or len(args) % 6 != 2: raise ValueError(args)
+		args, last_args = args[:-2], args[-2:]
+		for args in _everyN(args, 6):
+			yield ('rrcurveto', args)
+		yield ('rlineto', last_args)
+	@staticmethod
+	def rlinecurve(args):
+		if len(args) < 8 or len(args) % 2 != 0: raise ValueError(args)
+		args, last_args = args[:-6], args[-6:]
+		for args in _everyN(args, 2):
+			yield ('rlineto', args)
+		yield ('rrcurveto', last_args)
+
+
+def generalizeCommands(commands, ignoreErrors=False):
+	result = []
+	mapping = _GeneralizerDecombinerCommandsMap
+	for op,args in commands:
+		func = getattr(mapping, op, None)
+		if not func:
+			result.append((op,args))
+			continue
+		try:
+			for command in func(args):
+				result.append(command)
+		except ValueError:
+			if ignoreErrors:
+				# Store op as data, such that consumers of commands do not have to
+				# deal with incorrect number of arguments.
+				result.append(('', args))
+				result.append(('', [op]))
+			else:
+				raise
+	return result
+
+def generalizeProgram(program, **kwargs):
+	return commandsToProgram(generalizeCommands(programToCommands(program), **kwargs))
+
+
+def _categorizeVector(v):
+	"""
+	Takes X,Y vector v and returns one of r, h, v, or 0 depending on which
+	of X and/or Y are zero, plus tuple of nonzero ones.  If both are zero,
+	it returns a single zero still.
+
+	>>> _categorizeVector((0,0))
+	('0', (0,))
+	>>> _categorizeVector((1,0))
+	('h', (1,))
+	>>> _categorizeVector((0,2))
+	('v', (2,))
+	>>> _categorizeVector((1,2))
+	('r', (1, 2))
+	"""
+	if not v[0]:
+		if not v[1]:
+			return '0', v[:1]
+		else:
+			return 'v', v[1:]
+	else:
+		if not v[1]:
+			return 'h', v[:1]
+		else:
+			return 'r', v
+
+def _mergeCategories(a, b):
+	if a == '0': return b
+	if b == '0': return a
+	if a == b: return a
+	return None
+
+def _negateCategory(a):
+	if a == 'h': return 'v'
+	if a == 'v': return 'h'
+	assert a in '0r'
+	return a
+
+def specializeCommands(commands,
+		       ignoreErrors=False,
+		       generalizeFirst=True,
+		       preserveTopology=False,
+		       maxstack=48):
+
+	# We perform several rounds of optimizations.  They are carefully ordered and are:
+	#
+	# 0. Generalize commands.
+	#    This ensures that they are in our expected simple form, with each line/curve only
+	#    having arguments for one segment, and using the generic form (rlineto/rrcurveto).
+	#    If caller is sure the input is in this form, they can turn off generalization to
+	#    save time.
+	#
+	# 1. Combine successive rmoveto operations.
+	#
+	# 2. Specialize rmoveto/rlineto/rrcurveto operators into horizontal/vertical variants.
+	#    We specialize into some, made-up, variants as well, which simplifies following
+	#    passes.
+	#
+	# 3. Merge or delete redundant operations, to the extent requested.
+	#    OpenType spec declares point numbers in CFF undefined.  As such, we happily
+	#    change topology.  If client relies on point numbers (in GPOS anchors, or for
+	#    hinting purposes(what?)) they can turn this off.
+	#
+	# 4. Peephole optimization to revert back some of the h/v variants back into their
+	#    original "relative" operator (rline/rrcurveto) if that saves a byte.
+	#
+	# 5. Combine adjacent operators when possible, minding not to go over max stack size.
+	#
+	# 6. Resolve any remaining made-up operators into real operators.
+	#
+	# I have convinced myself that this produces optimal bytecode (except for, possibly
+	# one byte each time maxstack size prohibits combining.)  YMMV, but you'd be wrong. :-)
+	# A dynamic-programming approach can do the same but would be significantly slower.
+
+
+	# 0. Generalize commands.
+	if generalizeFirst:
+		commands = generalizeCommands(commands, ignoreErrors=ignoreErrors)
+	else:
+		commands = list(commands) # Make copy since we modify in-place later.
+
+	# 1. Combine successive rmoveto operations.
+	for i in range(len(commands)-1, 0, -1):
+		if 'rmoveto' == commands[i][0] == commands[i-1][0]:
+			v1, v2 = commands[i-1][1], commands[i][1]
+			commands[i-1] = ('rmoveto', [v1[0]+v2[0], v1[1]+v2[1]])
+			del commands[i]
+
+	# 2. Specialize rmoveto/rlineto/rrcurveto operators into horizontal/vertical variants.
+	#
+	# We, in fact, specialize into more, made-up, variants that special-case when both
+	# X and Y components are zero.  This simplifies the following optimization passes.
+	# This case is rare, but OCD does not let me skip it.
+	#
+	# After this round, we will have four variants that use the following mnemonics:
+	#
+	#  - 'r' for relative,   ie. non-zero X and non-zero Y,
+	#  - 'h' for horizontal, ie. zero X and non-zero Y,
+	#  - 'v' for vertical,   ie. non-zero X and zero Y,
+	#  - '0' for zeros,      ie. zero X and zero Y.
+	#
+	# The '0' pseudo-operators are not part of the spec, but help simplify the following
+	# optimization rounds.  We resolve them at the end.  So, after this, we will have four
+	# moveto and four lineto variants:
+	#
+	#  - 0moveto, 0lineto
+	#  - hmoveto, hlineto
+	#  - vmoveto, vlineto
+	#  - rmoveto, rlineto
+	#
+	# and sixteen curveto variants.  For example, a '0hcurveto' operator means a curve
+	# dx0,dy0,dx1,dy1,dx2,dy2,dx3,dy3 where dx0, dx1, and dy3 are zero but not dx3.
+	# An 'rvcurveto' means dx3 is zero but not dx0,dy0,dy3.
+	#
+	# There are nine different variants of curves without the '0'.  Those nine map exactly
+	# to the existing curve variants in the spec: rrcurveto, and the four variants hhcurveto,
+	# vvcurveto, hvcurveto, and vhcurveto each cover two cases, one with an odd number of
+	# arguments and one without.  Eg. an hhcurveto with an extra argument (odd number of
+	# arguments) is in fact an rhcurveto.  The operators in the spec are designed such that
+	# all four of rhcurveto, rvcurveto, hrcurveto, and vrcurveto are encodable for one curve.
+	#
+	# Of the curve types with '0', the 00curveto is equivalent to a lineto variant.  The rest
+	# of the curve types with a 0 need to be encoded as a h or v variant.  Ie. a '0' can be
+	# thought of a "don't care" and can be used as either an 'h' or a 'v'.  As such, we always
+	# encode a number 0 as argument when we use a '0' variant.  Later on, we can just substitute
+	# the '0' with either 'h' or 'v' and it works.
+	#
+	# When we get to curve splines however, things become more complicated...  XXX finish this.
+	# There's one more complexity with splines.  If one side of the spline is not horizontal or
+	# vertical (or zero), ie. if it's 'r', then it limits which spline types we can encode.
+	# Only hhcurveto and vvcurveto operators can encode a spline starting with 'r', and
+	# only hvcurveto and vhcurveto operators can encode a spline ending with 'r'.
+	# This limits our merge opportunities later.
+	#
+	for i in range(len(commands)):
+		op,args = commands[i]
+
+		if op in {'rmoveto', 'rlineto'}:
+			c, args = _categorizeVector(args)
+			commands[i] = c+op[1:], args
+			continue
+
+		if op == 'rrcurveto':
+			c1, args1 = _categorizeVector(args[:2])
+			c2, args2 = _categorizeVector(args[-2:])
+			commands[i] = c1+c2+'curveto', args1+args[2:4]+args2
+			continue
+
+	# 3. Merge or delete redundant operations, to the extent requested.
+	#
+	# TODO
+	# A 0moveto that comes before all other path operations can be removed.
+	# though I find conflicting evidence for this.
+	#
+	# TODO
+	# "If hstem and vstem hints are both declared at the beginning of a
+	# CharString, and this sequence is followed directly by the hintmask or
+	# cntrmask operators, then the vstem hint operator (or, if applicable,
+	# the vstemhm operator) need not be included."
+	#
+	# "The sequence and form of a CFF2 CharString program may be represented as:
+	# {hs* vs* cm* hm* mt subpath}? {mt subpath}*"
+	#
+	# https://www.microsoft.com/typography/otspec/cff2charstr.htm#section3.1
+	#
+	# For Type2 CharStrings the sequence is:
+	# w? {hs* vs* cm* hm* mt subpath}? {mt subpath}* endchar"
+
+
+	# Some other redundancies change topology (point numbers).
+	if not preserveTopology:
+		for i in range(len(commands)-1, -1, -1):
+			op, args = commands[i]
+
+			# A 00curveto is demoted to a (specialized) lineto.
+			if op == '00curveto':
+				assert len(args) == 4
+				c, args = _categorizeVector(args[1:3])
+				op = c+'lineto'
+				commands[i] = op, args
+				# and then...
+
+			# A 0lineto can be deleted.
+			if op == '0lineto':
+				del commands[i]
+				continue
+
+			# Merge adjacent hlineto's and vlineto's.
+			if i and op in {'hlineto', 'vlineto'} and op == commands[i-1][0]:
+				_, other_args = commands[i-1]
+				assert len(args) == 1 and len(other_args) == 1
+				commands[i-1] = (op, [other_args[0]+args[0]])
+				del commands[i]
+				continue
+
+	# 4. Peephole optimization to revert back some of the h/v variants back into their
+	#    original "relative" operator (rline/rrcurveto) if that saves a byte.
+	for i in range(1, len(commands)-1):
+		op,args = commands[i]
+		prv,nxt = commands[i-1][0], commands[i+1][0]
+
+		if op in {'0lineto', 'hlineto', 'vlineto'} and prv == nxt == 'rlineto':
+			assert len(args) == 1
+			args = [0, args[0]] if op[0] == 'v' else [args[0], 0]
+			commands[i] = ('rlineto', args)
+			continue
+
+		if op[2:] == 'curveto' and len(args) == 5 and prv == nxt == 'rrcurveto':
+			assert (op[0] == 'r') ^ (op[1] == 'r')
+			if op[0] == 'v':
+				pos = 0
+			elif op[0] != 'r':
+				pos = 1
+			elif op[1] == 'v':
+				pos = 4
+			else:
+				pos = 5
+			# Insert, while maintaining the type of args (can be tuple or list).
+			args = args[:pos] + type(args)((0,)) + args[pos:]
+			commands[i] = ('rrcurveto', args)
+			continue
+
+	# 5. Combine adjacent operators when possible, minding not to go over max stack size.
+	for i in range(len(commands)-1, 0, -1):
+		op1,args1 = commands[i-1]
+		op2,args2 = commands[i]
+		new_op = None
+
+		# Merge logic...
+		if {op1, op2} <= {'rlineto', 'rrcurveto'}:
+			if op1 == op2:
+				new_op = op1
+			else:
+				if op2 == 'rrcurveto' and len(args2) == 6:
+					new_op = 'rlinecurve'
+				elif len(args2) == 2:
+					new_op = 'rcurveline'
+
+		elif (op1, op2) in {('rlineto', 'rlinecurve'), ('rrcurveto', 'rcurveline')}:
+			new_op = op2
+
+		elif {op1, op2} == {'vlineto', 'hlineto'}:
+			new_op = op1
+
+		elif 'curveto' == op1[2:] == op2[2:]:
+			d0, d1 = op1[:2]
+			d2, d3 = op2[:2]
+
+			if d1 == 'r' or d2 == 'r' or d0 == d3 == 'r':
+				continue
+
+			d = _mergeCategories(d1, d2)
+			if d is None: continue
+			if d0 == 'r':
+				d = _mergeCategories(d, d3)
+				if d is None: continue
+				new_op = 'r'+d+'curveto'
+			elif d3 == 'r':
+				d0 = _mergeCategories(d0, _negateCategory(d))
+				if d0 is None: continue
+				new_op = d0+'r'+'curveto'
+			else:
+				d0 = _mergeCategories(d0, d3)
+				if d0 is None: continue
+				new_op = d0+d+'curveto'
+
+		if new_op and len(args1) + len(args2) <= maxstack:
+			commands[i-1] = (new_op, args1+args2)
+			del commands[i]
+
+	# 6. Resolve any remaining made-up operators into real operators.
+	for i in range(len(commands)):
+		op,args = commands[i]
+
+		if op in {'0moveto', '0lineto'}:
+			commands[i] = 'h'+op[1:], args
+			continue
+
+		if op[2:] == 'curveto' and op[:2] not in {'rr', 'hh', 'vv', 'vh', 'hv'}:
+			op0, op1 = op[:2]
+			if (op0 == 'r') ^ (op1 == 'r'):
+				assert len(args) % 2 == 1
+			if op0 == '0': op0 = 'h'
+			if op1 == '0': op1 = 'h'
+			if op0 == 'r': op0 = op1
+			if op1 == 'r': op1 = _negateCategory(op0)
+			assert {op0,op1} <= {'h','v'}, (op0, op1)
+
+			if len(args) % 2:
+				if op0 != op1: # vhcurveto / hvcurveto
+					if (op0 == 'h') ^ (len(args) % 8 == 1):
+						# Swap last two args order
+						args = args[:-2]+args[-1:]+args[-2:-1]
+				else: # hhcurveto / vvcurveto
+					if op0 == 'h': # hhcurveto
+						# Swap first two args order
+						args = args[1:2]+args[:1]+args[2:]
+
+			commands[i] = op0+op1+'curveto', args
+			continue
+
+	return commands
+
+def specializeProgram(program, **kwargs):
+	return commandsToProgram(specializeCommands(programToCommands(program), **kwargs))
+
+
+if __name__ == '__main__':
+	import sys
+	if len(sys.argv) == 1:
+		import doctest
+		sys.exit(doctest.testmod().failed)
+	program = stringToProgram(sys.argv[1:])
+	print("Program:"); print(programToString(program))
+	commands = programToCommands(program)
+	print("Commands:"); print(commands)
+	program2 = commandsToProgram(commands)
+	print("Program from commands:"); print(programToString(program2))
+	assert program == program2
+	print("Generalized program:"); print(programToString(generalizeProgram(program)))
+	print("Specialized program:"); print(programToString(specializeProgram(program)))
+
diff --git a/Lib/fontTools/cffLib/width.py b/Lib/fontTools/cffLib/width.py
new file mode 100644
index 0000000..a9e5532
--- /dev/null
+++ b/Lib/fontTools/cffLib/width.py
@@ -0,0 +1,163 @@
+# -*- coding: utf-8 -*-
+
+"""T2CharString glyph width optimizer."""
+
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.ttLib import TTFont, getTableClass
+from collections import defaultdict
+from operator import add
+from functools import partial, reduce
+
+
+class missingdict(dict):
+	def __init__(self, missing_func):
+		self.missing_func = missing_func
+	def __missing__(self, v):
+		return self.missing_func(v)
+
+def cumSum(f, op=add, start=0, decreasing=False):
+	
+	keys = sorted(f.keys())
+	minx, maxx = keys[0], keys[-1]
+
+	total = reduce(op, f.values(), start)
+
+	if decreasing:
+		missing = lambda x: start if x > maxx else total
+		domain = range(maxx, minx - 1, -1)
+	else:
+		missing = lambda x: start if x < minx else total
+		domain = range(minx, maxx + 1)
+
+	out = missingdict(missing)
+
+	v = start
+	for x in domain:
+		v = op(v, f[x])
+		out[x] = v
+
+	return out
+
+def byteCost(widths, default, nominal):
+
+	if not hasattr(widths, 'items'):
+		d = defaultdict(int)
+		for w in widths:
+			d[w] += 1
+		widths = d
+
+	cost = 0
+	for w,freq in widths.items():
+		if w == default: continue
+		diff = abs(w - nominal)
+		if diff <= 107:
+			cost += freq
+		elif diff <= 1131:
+			cost += freq * 2
+		else:
+			cost += freq * 5
+	return cost
+
+
+def optimizeWidthsBruteforce(widths):
+	"""Bruteforce version.  Veeeeeeeeeeeeeeeeery slow.  Only works for smallests of fonts."""
+
+	d = defaultdict(int)
+	for w in widths:
+		d[w] += 1
+
+	# Maximum number of bytes using default can possibly save
+	maxDefaultAdvantage = 5 * max(d.values())
+
+	minw, maxw = min(widths), max(widths)
+	domain = list(range(minw, maxw+1))
+
+	bestCostWithoutDefault = min(byteCost(widths, None, nominal) for nominal in domain)
+
+	bestCost = len(widths) * 5 + 1
+	for nominal in domain:
+		if byteCost(widths, None, nominal) > bestCost + maxDefaultAdvantage:
+			continue
+		for default in domain:
+			cost = byteCost(widths, default, nominal)
+			if cost < bestCost:
+				bestCost = cost
+				bestDefault = default
+				bestNominal = nominal
+
+	return bestDefault, bestNominal
+
+
+def optimizeWidths(widths):
+	"""Given a list of glyph widths, or dictionary mapping glyph width to number of
+	glyphs having that, returns a tuple of best CFF default and nominal glyph widths.
+
+	This algorithm is linear in UPEM+numGlyphs."""
+
+	if not hasattr(widths, 'items'):
+		d = defaultdict(int)
+		for w in widths:
+			d[w] += 1
+		widths = d
+	
+	keys = sorted(widths.keys())
+	minw, maxw = keys[0], keys[-1]
+	domain = list(range(minw, maxw+1))
+
+	# Cumulative sum/max forward/backward.
+	cumFrqU = cumSum(widths, op=add)
+	cumMaxU = cumSum(widths, op=max)
+	cumFrqD = cumSum(widths, op=add, decreasing=True)
+	cumMaxD = cumSum(widths, op=max, decreasing=True)
+
+	# Cost per nominal choice, without default consideration.
+	nomnCostU = missingdict(lambda x: cumFrqU[x] + cumFrqU[x-108] + cumFrqU[x-1132]*3)
+	nomnCostD = missingdict(lambda x: cumFrqD[x] + cumFrqD[x+108] + cumFrqD[x+1132]*3)
+	nomnCost  = missingdict(lambda x: nomnCostU[x] + nomnCostD[x] - widths[x])
+
+	# Cost-saving per nominal choice, by best default choice.
+	dfltCostU = missingdict(lambda x: max(cumMaxU[x], cumMaxU[x-108]*2, cumMaxU[x-1132]*5))
+	dfltCostD = missingdict(lambda x: max(cumMaxD[x], cumMaxD[x+108]*2, cumMaxD[x+1132]*5))
+	dfltCost  = missingdict(lambda x: max(dfltCostU[x], dfltCostD[x]))
+
+	# Combined cost per nominal choice.
+	bestCost  = missingdict(lambda x: nomnCost[x] - dfltCost[x])
+
+	# Best nominal.
+	nominal = min(domain, key=lambda x: bestCost[x])
+
+	# Work back the best default.
+	bestC = bestCost[nominal]
+	dfltC = nomnCost[nominal] - bestCost[nominal]
+	ends = []
+	if dfltC == dfltCostU[nominal]:
+		starts = [nominal, nominal-108, nominal-1131]
+		for start in starts:
+			while cumMaxU[start] and cumMaxU[start] == cumMaxU[start-1]:
+				start -= 1
+			ends.append(start)
+	else:
+		starts = [nominal, nominal+108, nominal+1131]
+		for start in starts:
+			while cumMaxD[start] and cumMaxD[start] == cumMaxD[start+1]:
+				start += 1
+			ends.append(start)
+	default = min(ends, key=lambda default: byteCost(widths, default, nominal))
+
+	return default, nominal
+
+
+if __name__ == '__main__':
+	import sys
+	if len(sys.argv) == 1:
+		import doctest
+		sys.exit(doctest.testmod().failed)
+	for fontfile in sys.argv[1:]:
+		font = TTFont(fontfile)
+		hmtx = font['hmtx']
+		widths = [m[0] for m in hmtx.metrics.values()]
+		default, nominal = optimizeWidths(widths)
+		print("glyphs=%d default=%d nominal=%d byteCost=%d" % (len(widths), default, nominal, byteCost(widths, default, nominal)))
+		#default, nominal = optimizeWidthsBruteforce(widths)
+		#print("glyphs=%d default=%d nominal=%d byteCost=%d" % (len(widths), default, nominal, byteCost(widths, default, nominal)))
diff --git a/Lib/fontTools/designspaceLib/__init__.py b/Lib/fontTools/designspaceLib/__init__.py
new file mode 100644
index 0000000..8063ac5
--- /dev/null
+++ b/Lib/fontTools/designspaceLib/__init__.py
@@ -0,0 +1,1252 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.loggingTools import LogMixin
+import collections
+import os
+import posixpath
+import plistlib
+
+try:
+    import xml.etree.cElementTree as ET
+except ImportError:
+    import xml.etree.ElementTree as ET
+
+"""
+    designSpaceDocument
+
+    - read and write designspace files
+"""
+
+__all__ = [
+    'DesignSpaceDocumentError', 'DesignSpaceDocument', 'SourceDescriptor',
+    'InstanceDescriptor', 'AxisDescriptor', 'RuleDescriptor', 'BaseDocReader',
+    'BaseDocWriter'
+]
+
+# ElementTree allows to find namespace-prefixed elements, but not attributes
+# so we have to do it ourselves for 'xml:lang'
+XML_NS = "{http://www.w3.org/XML/1998/namespace}"
+XML_LANG = XML_NS + "lang"
+
+
+def to_plist(value):
+    try:
+        # Python 2
+        string = plistlib.writePlistToString(value)
+    except AttributeError:
+        # Python 3
+        string = plistlib.dumps(value).decode()
+    return ET.fromstring(string)[0]
+
+
+def from_plist(element):
+    if element is None:
+        return {}
+    plist = ET.Element('plist')
+    plist.append(element)
+    string = ET.tostring(plist)
+    try:
+        # Python 2
+        return plistlib.readPlistFromString(string)
+    except AttributeError:
+        # Python 3
+        return plistlib.loads(string, fmt=plistlib.FMT_XML)
+
+
+def posix(path):
+    """Normalize paths using forward slash to work also on Windows."""
+    new_path = posixpath.join(*path.split(os.path.sep))
+    if path.startswith('/'):
+        # The above transformation loses absolute paths
+        new_path = '/' + new_path
+    return new_path
+
+
+def posixpath_property(private_name):
+    def getter(self):
+        # Normal getter
+        return getattr(self, private_name)
+
+    def setter(self, value):
+        # The setter rewrites paths using forward slashes
+        if value is not None:
+            value = posix(value)
+        setattr(self, private_name, value)
+
+    return property(getter, setter)
+
+
+class DesignSpaceDocumentError(Exception):
+    def __init__(self, msg, obj=None):
+        self.msg = msg
+        self.obj = obj
+
+    def __str__(self):
+        return str(self.msg) + (
+            ": %r" % self.obj if self.obj is not None else "")
+
+
+def _indent(elem, whitespace="    ", level=0):
+    # taken from http://effbot.org/zone/element-lib.htm#prettyprint
+    i = "\n" + level * whitespace
+    if len(elem):
+        if not elem.text or not elem.text.strip():
+            elem.text = i + whitespace
+        if not elem.tail or not elem.tail.strip():
+            elem.tail = i
+        for elem in elem:
+            _indent(elem, whitespace, level+1)
+        if not elem.tail or not elem.tail.strip():
+            elem.tail = i
+    else:
+        if level and (not elem.tail or not elem.tail.strip()):
+            elem.tail = i
+
+
+class SimpleDescriptor(object):
+    """ Containers for a bunch of attributes"""
+
+    # XXX this is ugly. The 'print' is inappropriate here, and instead of
+    # assert, it should simply return True/False
+    def compare(self, other):
+        # test if this object contains the same data as the other
+        for attr in self._attrs:
+            try:
+                assert(getattr(self, attr) == getattr(other, attr))
+            except AssertionError:
+                print("failed attribute", attr, getattr(self, attr), "!=", getattr(other, attr))
+
+
+class SourceDescriptor(SimpleDescriptor):
+    """Simple container for data related to the source"""
+    flavor = "source"
+    _attrs = ['filename', 'path', 'name', 'layerName',
+              'location', 'copyLib',
+              'copyGroups', 'copyFeatures',
+              'muteKerning', 'muteInfo',
+              'mutedGlyphNames',
+              'familyName', 'styleName']
+
+    def __init__(self):
+        self.filename = None
+        """The original path as found in the document."""
+
+        self.path = None
+        """The absolute path, calculated from filename."""
+
+        self.font = None
+        """Any Python object. Optional. Points to a representation of this
+        source font that is loaded in memory, as a Python object (e.g. a
+        ``defcon.Font`` or a ``fontTools.ttFont.TTFont``).
+
+        The default document reader will not fill-in this attribute, and the
+        default writer will not use this attribute. It is up to the user of
+        ``designspaceLib`` to either load the resource identified by
+        ``filename`` and store it in this field, or write the contents of
+        this field to the disk and make ```filename`` point to that.
+        """
+
+        self.name = None
+        self.location = None
+        self.layerName = None
+        self.copyLib = False
+        self.copyInfo = False
+        self.copyGroups = False
+        self.copyFeatures = False
+        self.muteKerning = False
+        self.muteInfo = False
+        self.mutedGlyphNames = []
+        self.familyName = None
+        self.styleName = None
+
+    path = posixpath_property("_path")
+    filename = posixpath_property("_filename")
+
+
+class RuleDescriptor(SimpleDescriptor):
+    """<!-- optional: list of substitution rules -->
+    <rules>
+        <rule name="vertical.bars">
+            <conditionset>
+                <condition minimum="250.000000" maximum="750.000000" name="weight"/>
+                <condition minimum="100" name="width"/>
+                <condition minimum="10" maximum="40" name="optical"/>
+            </conditionset>
+            <sub name="cent" with="cent.alt"/>
+            <sub name="dollar" with="dollar.alt"/>
+        </rule>
+    </rules>
+    """
+    _attrs = ['name', 'conditionSets', 'subs']   # what do we need here
+
+    def __init__(self):
+        self.name = None
+        self.conditionSets = []  # list of list of dict(name='aaaa', minimum=0, maximum=1000)
+        self.subs = []  # list of substitutions stored as tuples of glyphnames ("a", "a.alt")
+
+
+def evaluateRule(rule, location):
+    """ Return True if any of the rule's conditionsets matches the given location."""
+    return any(evaluateConditions(c, location) for c in rule.conditionSets)
+
+
+def evaluateConditions(conditions, location):
+    """ Return True if all the conditions matches the given location.
+        If a condition has no minimum, check for < maximum.
+        If a condition has no maximum, check for > minimum.
+    """
+    for cd in conditions:
+        value = location[cd['name']]
+        if cd.get('minimum') is None:
+            if value > cd['maximum']:
+                return False
+        elif cd.get('maximum') is None:
+            if cd['minimum'] > value:
+                return False
+        elif not cd['minimum'] <= value <= cd['maximum']:
+            return False
+    return True
+
+
+def processRules(rules, location, glyphNames):
+    """ Apply these rules at this location to these glyphnames.minimum
+        - rule order matters
+    """
+    newNames = []
+    for rule in rules:
+        if evaluateRule(rule, location):
+            for name in glyphNames:
+                swap = False
+                for a, b in rule.subs:
+                    if name == a:
+                        swap = True
+                        break
+                if swap:
+                    newNames.append(b)
+                else:
+                    newNames.append(name)
+            glyphNames = newNames
+            newNames = []
+    return glyphNames
+
+
+class InstanceDescriptor(SimpleDescriptor):
+    """Simple container for data related to the instance"""
+    flavor = "instance"
+    _defaultLanguageCode = "en"
+    _attrs = ['path',
+              'name',
+              'location',
+              'familyName',
+              'styleName',
+              'postScriptFontName',
+              'styleMapFamilyName',
+              'styleMapStyleName',
+              'kerning',
+              'info',
+              'lib']
+
+    def __init__(self):
+        self.filename = None    # the original path as found in the document
+        self.path = None        # the absolute path, calculated from filename
+        self.name = None
+        self.location = None
+        self.familyName = None
+        self.styleName = None
+        self.postScriptFontName = None
+        self.styleMapFamilyName = None
+        self.styleMapStyleName = None
+        self.localisedStyleName = {}
+        self.localisedFamilyName = {}
+        self.localisedStyleMapStyleName = {}
+        self.localisedStyleMapFamilyName = {}
+        self.glyphs = {}
+        self.mutedGlyphNames = []
+        self.kerning = True
+        self.info = True
+
+        self.lib = {}
+        """Custom data associated with this instance."""
+
+    path = posixpath_property("_path")
+    filename = posixpath_property("_filename")
+
+    def setStyleName(self, styleName, languageCode="en"):
+        self.localisedStyleName[languageCode] = styleName
+
+    def getStyleName(self, languageCode="en"):
+        return self.localisedStyleName.get(languageCode)
+
+    def setFamilyName(self, familyName, languageCode="en"):
+        self.localisedFamilyName[languageCode] = familyName
+
+    def getFamilyName(self, languageCode="en"):
+        return self.localisedFamilyName.get(languageCode)
+
+    def setStyleMapStyleName(self, styleMapStyleName, languageCode="en"):
+        self.localisedStyleMapStyleName[languageCode] = styleMapStyleName
+
+    def getStyleMapStyleName(self, languageCode="en"):
+        return self.localisedStyleMapStyleName.get(languageCode)
+
+    def setStyleMapFamilyName(self, styleMapFamilyName, languageCode="en"):
+        self.localisedStyleMapFamilyName[languageCode] = styleMapFamilyName
+
+    def getStyleMapFamilyName(self, languageCode="en"):
+        return self.localisedStyleMapFamilyName.get(languageCode)
+
+
+def tagForAxisName(name):
+    # try to find or make a tag name for this axis name
+    names = {
+        'weight':   ('wght', dict(en = 'Weight')),
+        'width':    ('wdth', dict(en = 'Width')),
+        'optical':  ('opsz', dict(en = 'Optical Size')),
+        'slant':    ('slnt', dict(en = 'Slant')),
+        'italic':   ('ital', dict(en = 'Italic')),
+    }
+    if name.lower() in names:
+        return names[name.lower()]
+    if len(name) < 4:
+        tag = name + "*" * (4 - len(name))
+    else:
+        tag = name[:4]
+    return tag, dict(en=name)
+
+
+class AxisDescriptor(SimpleDescriptor):
+    """ Simple container for the axis data
+        Add more localisations?
+    """
+    flavor = "axis"
+    _attrs = ['tag', 'name', 'maximum', 'minimum', 'default', 'map']
+
+    def __init__(self):
+        self.tag = None       # opentype tag for this axis
+        self.name = None      # name of the axis used in locations
+        self.labelNames = {}  # names for UI purposes, if this is not a standard axis,
+        self.minimum = None
+        self.maximum = None
+        self.default = None
+        self.hidden = False
+        self.map = []
+
+    def serialize(self):
+        # output to a dict, used in testing
+        return dict(
+            tag=self.tag,
+            name=self.name,
+            labelNames=self.labelNames,
+            maximum=self.maximum,
+            minimum=self.minimum,
+            default=self.default,
+            hidden=self.hidden,
+            map=self.map,
+        )
+
+
+class BaseDocWriter(object):
+    _whiteSpace = "    "
+    ruleDescriptorClass = RuleDescriptor
+    axisDescriptorClass = AxisDescriptor
+    sourceDescriptorClass = SourceDescriptor
+    instanceDescriptorClass = InstanceDescriptor
+
+    @classmethod
+    def getAxisDecriptor(cls):
+        return cls.axisDescriptorClass()
+
+    @classmethod
+    def getSourceDescriptor(cls):
+        return cls.sourceDescriptorClass()
+
+    @classmethod
+    def getInstanceDescriptor(cls):
+        return cls.instanceDescriptorClass()
+
+    @classmethod
+    def getRuleDescriptor(cls):
+        return cls.ruleDescriptorClass()
+
+    def __init__(self, documentPath, documentObject):
+        self.path = documentPath
+        self.documentObject = documentObject
+        self.documentVersion = "4.0"
+        self.root = ET.Element("designspace")
+        self.root.attrib['format'] = self.documentVersion
+        self._axes = []     # for use by the writer only
+        self._rules = []    # for use by the writer only
+
+    def write(self, pretty=True):
+        if self.documentObject.axes:
+            self.root.append(ET.Element("axes"))
+        for axisObject in self.documentObject.axes:
+            self._addAxis(axisObject)
+
+        if self.documentObject.rules:
+            self.root.append(ET.Element("rules"))
+        for ruleObject in self.documentObject.rules:
+            self._addRule(ruleObject)
+
+        if self.documentObject.sources:
+            self.root.append(ET.Element("sources"))
+        for sourceObject in self.documentObject.sources:
+            self._addSource(sourceObject)
+
+        if self.documentObject.instances:
+            self.root.append(ET.Element("instances"))
+        for instanceObject in self.documentObject.instances:
+            self._addInstance(instanceObject)
+
+        if self.documentObject.lib:
+            self._addLib(self.documentObject.lib)
+
+        if pretty:
+            _indent(self.root, whitespace=self._whiteSpace)
+        tree = ET.ElementTree(self.root)
+        tree.write(self.path, encoding="utf-8", method='xml', xml_declaration=True)
+
+    def _makeLocationElement(self, locationObject, name=None):
+        """ Convert Location dict to a locationElement."""
+        locElement = ET.Element("location")
+        if name is not None:
+            locElement.attrib['name'] = name
+        validatedLocation = self.documentObject.newDefaultLocation()
+        for axisName, axisValue in locationObject.items():
+            if axisName in validatedLocation:
+                # only accept values we know
+                validatedLocation[axisName] = axisValue
+        for dimensionName, dimensionValue in validatedLocation.items():
+            dimElement = ET.Element('dimension')
+            dimElement.attrib['name'] = dimensionName
+            if type(dimensionValue) == tuple:
+                dimElement.attrib['xvalue'] = self.intOrFloat(dimensionValue[0])
+                dimElement.attrib['yvalue'] = self.intOrFloat(dimensionValue[1])
+            else:
+                dimElement.attrib['xvalue'] = self.intOrFloat(dimensionValue)
+            locElement.append(dimElement)
+        return locElement, validatedLocation
+
+    def intOrFloat(self, num):
+        if int(num) == num:
+            return "%d" % num
+        return "%f" % num
+
+    def _addRule(self, ruleObject):
+        # if none of the conditions have minimum or maximum values, do not add the rule.
+        self._rules.append(ruleObject)
+        ruleElement = ET.Element('rule')
+        if ruleObject.name is not None:
+            ruleElement.attrib['name'] = ruleObject.name
+        for conditions in ruleObject.conditionSets:
+            conditionsetElement = ET.Element('conditionset')
+            for cond in conditions:
+                if cond.get('minimum') is None and cond.get('maximum') is None:
+                    # neither is defined, don't add this condition
+                    continue
+                conditionElement = ET.Element('condition')
+                conditionElement.attrib['name'] = cond.get('name')
+                if cond.get('minimum') is not None:
+                    conditionElement.attrib['minimum'] = self.intOrFloat(cond.get('minimum'))
+                if cond.get('maximum') is not None:
+                    conditionElement.attrib['maximum'] = self.intOrFloat(cond.get('maximum'))
+                conditionsetElement.append(conditionElement)
+            if len(conditionsetElement):
+                ruleElement.append(conditionsetElement)
+        for sub in ruleObject.subs:
+            subElement = ET.Element('sub')
+            subElement.attrib['name'] = sub[0]
+            subElement.attrib['with'] = sub[1]
+            ruleElement.append(subElement)
+        if len(ruleElement):
+            self.root.findall('.rules')[0].append(ruleElement)
+
+    def _addAxis(self, axisObject):
+        self._axes.append(axisObject)
+        axisElement = ET.Element('axis')
+        axisElement.attrib['tag'] = axisObject.tag
+        axisElement.attrib['name'] = axisObject.name
+        axisElement.attrib['minimum'] = self.intOrFloat(axisObject.minimum)
+        axisElement.attrib['maximum'] = self.intOrFloat(axisObject.maximum)
+        axisElement.attrib['default'] = self.intOrFloat(axisObject.default)
+        if axisObject.hidden:
+            axisElement.attrib['hidden'] = "1"
+        for languageCode, labelName in sorted(axisObject.labelNames.items()):
+            languageElement = ET.Element('labelname')
+            languageElement.attrib[u'xml:lang'] = languageCode
+            languageElement.text = labelName
+            axisElement.append(languageElement)
+        if axisObject.map:
+            for inputValue, outputValue in axisObject.map:
+                mapElement = ET.Element('map')
+                mapElement.attrib['input'] = self.intOrFloat(inputValue)
+                mapElement.attrib['output'] = self.intOrFloat(outputValue)
+                axisElement.append(mapElement)
+        self.root.findall('.axes')[0].append(axisElement)
+
+    def _addInstance(self, instanceObject):
+        instanceElement = ET.Element('instance')
+        if instanceObject.name is not None:
+            instanceElement.attrib['name'] = instanceObject.name
+        if instanceObject.familyName is not None:
+            instanceElement.attrib['familyname'] = instanceObject.familyName
+        if instanceObject.styleName is not None:
+            instanceElement.attrib['stylename'] = instanceObject.styleName
+        # add localisations
+        if instanceObject.localisedStyleName:
+            languageCodes = list(instanceObject.localisedStyleName.keys())
+            languageCodes.sort()
+            for code in languageCodes:
+                if code == "en":
+                    continue  # already stored in the element attribute
+                localisedStyleNameElement = ET.Element('stylename')
+                localisedStyleNameElement.attrib["xml:lang"] = code
+                localisedStyleNameElement.text = instanceObject.getStyleName(code)
+                instanceElement.append(localisedStyleNameElement)
+        if instanceObject.localisedFamilyName:
+            languageCodes = list(instanceObject.localisedFamilyName.keys())
+            languageCodes.sort()
+            for code in languageCodes:
+                if code == "en":
+                    continue  # already stored in the element attribute
+                localisedFamilyNameElement = ET.Element('familyname')
+                localisedFamilyNameElement.attrib["xml:lang"] = code
+                localisedFamilyNameElement.text = instanceObject.getFamilyName(code)
+                instanceElement.append(localisedFamilyNameElement)
+        if instanceObject.localisedStyleMapStyleName:
+            languageCodes = list(instanceObject.localisedStyleMapStyleName.keys())
+            languageCodes.sort()
+            for code in languageCodes:
+                if code == "en":
+                    continue
+                localisedStyleMapStyleNameElement = ET.Element('stylemapstylename')
+                localisedStyleMapStyleNameElement.attrib["xml:lang"] = code
+                localisedStyleMapStyleNameElement.text = instanceObject.getStyleMapStyleName(code)
+                instanceElement.append(localisedStyleMapStyleNameElement)
+        if instanceObject.localisedStyleMapFamilyName:
+            languageCodes = list(instanceObject.localisedStyleMapFamilyName.keys())
+            languageCodes.sort()
+            for code in languageCodes:
+                if code == "en":
+                    continue
+                localisedStyleMapFamilyNameElement = ET.Element('stylemapfamilyname')
+                localisedStyleMapFamilyNameElement.attrib["xml:lang"] = code
+                localisedStyleMapFamilyNameElement.text = instanceObject.getStyleMapFamilyName(code)
+                instanceElement.append(localisedStyleMapFamilyNameElement)
+
+        if instanceObject.location is not None:
+            locationElement, instanceObject.location = self._makeLocationElement(instanceObject.location)
+            instanceElement.append(locationElement)
+        if instanceObject.filename is not None:
+            instanceElement.attrib['filename'] = instanceObject.filename
+        if instanceObject.postScriptFontName is not None:
+            instanceElement.attrib['postscriptfontname'] = instanceObject.postScriptFontName
+        if instanceObject.styleMapFamilyName is not None:
+            instanceElement.attrib['stylemapfamilyname'] = instanceObject.styleMapFamilyName
+        if instanceObject.styleMapStyleName is not None:
+            instanceElement.attrib['stylemapstylename'] = instanceObject.styleMapStyleName
+        if instanceObject.glyphs:
+            if instanceElement.findall('.glyphs') == []:
+                glyphsElement = ET.Element('glyphs')
+                instanceElement.append(glyphsElement)
+            glyphsElement = instanceElement.findall('.glyphs')[0]
+            for glyphName, data in sorted(instanceObject.glyphs.items()):
+                glyphElement = self._writeGlyphElement(instanceElement, instanceObject, glyphName, data)
+                glyphsElement.append(glyphElement)
+        if instanceObject.kerning:
+            kerningElement = ET.Element('kerning')
+            instanceElement.append(kerningElement)
+        if instanceObject.info:
+            infoElement = ET.Element('info')
+            instanceElement.append(infoElement)
+        if instanceObject.lib:
+            libElement = ET.Element('lib')
+            libElement.append(to_plist(instanceObject.lib))
+            instanceElement.append(libElement)
+        self.root.findall('.instances')[0].append(instanceElement)
+
+    def _addSource(self, sourceObject):
+        sourceElement = ET.Element("source")
+        if sourceObject.filename is not None:
+            sourceElement.attrib['filename'] = sourceObject.filename
+        if sourceObject.name is not None:
+            if sourceObject.name.find("temp_master") != 0:
+                # do not save temporary source names
+                sourceElement.attrib['name'] = sourceObject.name
+        if sourceObject.familyName is not None:
+            sourceElement.attrib['familyname'] = sourceObject.familyName
+        if sourceObject.styleName is not None:
+            sourceElement.attrib['stylename'] = sourceObject.styleName
+        if sourceObject.layerName is not None:
+            sourceElement.attrib['layer'] = sourceObject.layerName
+        if sourceObject.copyLib:
+            libElement = ET.Element('lib')
+            libElement.attrib['copy'] = "1"
+            sourceElement.append(libElement)
+        if sourceObject.copyGroups:
+            groupsElement = ET.Element('groups')
+            groupsElement.attrib['copy'] = "1"
+            sourceElement.append(groupsElement)
+        if sourceObject.copyFeatures:
+            featuresElement = ET.Element('features')
+            featuresElement.attrib['copy'] = "1"
+            sourceElement.append(featuresElement)
+        if sourceObject.copyInfo or sourceObject.muteInfo:
+            infoElement = ET.Element('info')
+            if sourceObject.copyInfo:
+                infoElement.attrib['copy'] = "1"
+            if sourceObject.muteInfo:
+                infoElement.attrib['mute'] = "1"
+            sourceElement.append(infoElement)
+        if sourceObject.muteKerning:
+            kerningElement = ET.Element("kerning")
+            kerningElement.attrib["mute"] = '1'
+            sourceElement.append(kerningElement)
+        if sourceObject.mutedGlyphNames:
+            for name in sourceObject.mutedGlyphNames:
+                glyphElement = ET.Element("glyph")
+                glyphElement.attrib["name"] = name
+                glyphElement.attrib["mute"] = '1'
+                sourceElement.append(glyphElement)
+        locationElement, sourceObject.location = self._makeLocationElement(sourceObject.location)
+        sourceElement.append(locationElement)
+        self.root.findall('.sources')[0].append(sourceElement)
+
+    def _addLib(self, dict):
+        libElement = ET.Element('lib')
+        libElement.append(to_plist(dict))
+        self.root.append(libElement)
+
+    def _writeGlyphElement(self, instanceElement, instanceObject, glyphName, data):
+        glyphElement = ET.Element('glyph')
+        if data.get('mute'):
+            glyphElement.attrib['mute'] = "1"
+        if data.get('unicodes') is not None:
+            glyphElement.attrib['unicode'] = " ".join([hex(u) for u in data.get('unicodes')])
+        if data.get('instanceLocation') is not None:
+            locationElement, data['instanceLocation'] = self._makeLocationElement(data.get('instanceLocation'))
+            glyphElement.append(locationElement)
+        if glyphName is not None:
+            glyphElement.attrib['name'] = glyphName
+        if data.get('note') is not None:
+            noteElement = ET.Element('note')
+            noteElement.text = data.get('note')
+            glyphElement.append(noteElement)
+        if data.get('masters') is not None:
+            mastersElement = ET.Element("masters")
+            for m in data.get('masters'):
+                masterElement = ET.Element("master")
+                if m.get('glyphName') is not None:
+                    masterElement.attrib['glyphname'] = m.get('glyphName')
+                if m.get('font') is not None:
+                    masterElement.attrib['source'] = m.get('font')
+                if m.get('location') is not None:
+                    locationElement, m['location'] = self._makeLocationElement(m.get('location'))
+                    masterElement.append(locationElement)
+                mastersElement.append(masterElement)
+            glyphElement.append(mastersElement)
+        return glyphElement
+
+
+class BaseDocReader(LogMixin):
+    ruleDescriptorClass = RuleDescriptor
+    axisDescriptorClass = AxisDescriptor
+    sourceDescriptorClass = SourceDescriptor
+    instanceDescriptorClass = InstanceDescriptor
+
+    def __init__(self, documentPath, documentObject):
+        self.path = documentPath
+        self.documentObject = documentObject
+        tree = ET.parse(self.path)
+        self.root = tree.getroot()
+        self.documentObject.formatVersion = self.root.attrib.get("format", "3.0")
+        self._axes = []
+        self.rules = []
+        self.sources = []
+        self.instances = []
+        self.axisDefaults = {}
+        self._strictAxisNames = True
+
+    def read(self):
+        self.readAxes()
+        self.readRules()
+        self.readSources()
+        self.readInstances()
+        self.readLib()
+
+    def getSourcePaths(self, makeGlyphs=True, makeKerning=True, makeInfo=True):
+        paths = []
+        for name in self.documentObject.sources.keys():
+            paths.append(self.documentObject.sources[name][0].path)
+        return paths
+
+    def readRules(self):
+        # we also need to read any conditions that are outside of a condition set.
+        rules = []
+        for ruleElement in self.root.findall(".rules/rule"):
+            ruleObject = self.ruleDescriptorClass()
+            ruleName = ruleObject.name = ruleElement.attrib.get("name")
+            # read any stray conditions outside a condition set
+            externalConditions = self._readConditionElements(
+                ruleElement,
+                ruleName,
+            )
+            if externalConditions:
+                ruleObject.conditionSets.append(externalConditions)
+                self.log.info(
+                    "Found stray rule conditions outside a conditionset. "
+                    "Wrapped them in a new conditionset."
+                )
+            # read the conditionsets
+            for conditionSetElement in ruleElement.findall('.conditionset'):
+                conditionSet = self._readConditionElements(
+                    conditionSetElement,
+                    ruleName,
+                )
+                if conditionSet is not None:
+                    ruleObject.conditionSets.append(conditionSet)
+            for subElement in ruleElement.findall('.sub'):
+                a = subElement.attrib['name']
+                b = subElement.attrib['with']
+                ruleObject.subs.append((a, b))
+            rules.append(ruleObject)
+        self.documentObject.rules = rules
+
+    def _readConditionElements(self, parentElement, ruleName=None):
+        cds = []
+        for conditionElement in parentElement.findall('.condition'):
+            cd = {}
+            cdMin = conditionElement.attrib.get("minimum")
+            if cdMin is not None:
+                cd['minimum'] = float(cdMin)
+            else:
+                # will allow these to be None, assume axis.minimum
+                cd['minimum'] = None
+            cdMax = conditionElement.attrib.get("maximum")
+            if cdMax is not None:
+                cd['maximum'] = float(cdMax)
+            else:
+                # will allow these to be None, assume axis.maximum
+                cd['maximum'] = None
+            cd['name'] = conditionElement.attrib.get("name")
+            # # test for things
+            if cd.get('minimum') is None and cd.get('maximum') is None:
+                raise DesignSpaceDocumentError(
+                    "condition missing required minimum or maximum in rule" +
+                    (" '%s'" % ruleName if ruleName is not None else ""))
+            cds.append(cd)
+        return cds
+
+    def readAxes(self):
+        # read the axes elements, including the warp map.
+        if len(self.root.findall(".axes/axis")) == 0:
+            self._strictAxisNames = False
+            return
+        for axisElement in self.root.findall(".axes/axis"):
+            axisObject = self.axisDescriptorClass()
+            axisObject.name = axisElement.attrib.get("name")
+            axisObject.minimum = float(axisElement.attrib.get("minimum"))
+            axisObject.maximum = float(axisElement.attrib.get("maximum"))
+            if axisElement.attrib.get('hidden', False):
+                axisObject.hidden = True
+            axisObject.default = float(axisElement.attrib.get("default"))
+            axisObject.tag = axisElement.attrib.get("tag")
+            for mapElement in axisElement.findall('map'):
+                a = float(mapElement.attrib['input'])
+                b = float(mapElement.attrib['output'])
+                axisObject.map.append((a, b))
+            for labelNameElement in axisElement.findall('labelname'):
+                # Note: elementtree reads the xml:lang attribute name as
+                # '{http://www.w3.org/XML/1998/namespace}lang'
+                for key, lang in labelNameElement.items():
+                    if key == XML_LANG:
+                        labelName = labelNameElement.text
+                        axisObject.labelNames[lang] = labelName
+            self.documentObject.axes.append(axisObject)
+            self.axisDefaults[axisObject.name] = axisObject.default
+        self.documentObject.defaultLoc = self.axisDefaults
+
+    def readSources(self):
+        for sourceCount, sourceElement in enumerate(self.root.findall(".sources/source")):
+            filename = sourceElement.attrib.get('filename')
+            if filename is not None and self.path is not None:
+                sourcePath = os.path.abspath(os.path.join(os.path.dirname(self.path), filename))
+            else:
+                sourcePath = None
+            sourceName = sourceElement.attrib.get('name')
+            if sourceName is None:
+                # add a temporary source name
+                sourceName = "temp_master.%d" % (sourceCount)
+            sourceObject = self.sourceDescriptorClass()
+            sourceObject.path = sourcePath        # absolute path to the ufo source
+            sourceObject.filename = filename      # path as it is stored in the document
+            sourceObject.name = sourceName
+            familyName = sourceElement.attrib.get("familyname")
+            if familyName is not None:
+                sourceObject.familyName = familyName
+            styleName = sourceElement.attrib.get("stylename")
+            if styleName is not None:
+                sourceObject.styleName = styleName
+            sourceObject.location = self.locationFromElement(sourceElement)
+            layerName = sourceElement.attrib.get('layer')
+            if layerName is not None:
+                sourceObject.layerName = layerName
+            for libElement in sourceElement.findall('.lib'):
+                if libElement.attrib.get('copy') == '1':
+                    sourceObject.copyLib = True
+            for groupsElement in sourceElement.findall('.groups'):
+                if groupsElement.attrib.get('copy') == '1':
+                    sourceObject.copyGroups = True
+            for infoElement in sourceElement.findall(".info"):
+                if infoElement.attrib.get('copy') == '1':
+                    sourceObject.copyInfo = True
+                if infoElement.attrib.get('mute') == '1':
+                    sourceObject.muteInfo = True
+            for featuresElement in sourceElement.findall(".features"):
+                if featuresElement.attrib.get('copy') == '1':
+                    sourceObject.copyFeatures = True
+            for glyphElement in sourceElement.findall(".glyph"):
+                glyphName = glyphElement.attrib.get('name')
+                if glyphName is None:
+                    continue
+                if glyphElement.attrib.get('mute') == '1':
+                    sourceObject.mutedGlyphNames.append(glyphName)
+            for kerningElement in sourceElement.findall(".kerning"):
+                if kerningElement.attrib.get('mute') == '1':
+                    sourceObject.muteKerning = True
+            self.documentObject.sources.append(sourceObject)
+
+    def locationFromElement(self, element):
+        elementLocation = None
+        for locationElement in element.findall('.location'):
+            elementLocation = self.readLocationElement(locationElement)
+            break
+        return elementLocation
+
+    def readLocationElement(self, locationElement):
+        """ Format 0 location reader """
+        if not self.documentObject.axes:
+            raise DesignSpaceDocumentError("No axes defined")
+        loc = {}
+        for dimensionElement in locationElement.findall(".dimension"):
+            dimName = dimensionElement.attrib.get("name")
+            if self._strictAxisNames and dimName not in self.axisDefaults:
+                # In case the document contains no axis definitions,
+                self.log.warning("Location with undefined axis: \"%s\".", dimName)
+                continue
+            xValue = yValue = None
+            try:
+                xValue = dimensionElement.attrib.get('xvalue')
+                xValue = float(xValue)
+            except ValueError:
+                self.log.warning("KeyError in readLocation xValue %3.3f", xValue)
+            try:
+                yValue = dimensionElement.attrib.get('yvalue')
+                if yValue is not None:
+                    yValue = float(yValue)
+            except ValueError:
+                pass
+            if yValue is not None:
+                loc[dimName] = (xValue, yValue)
+            else:
+                loc[dimName] = xValue
+        return loc
+
+    def readInstances(self, makeGlyphs=True, makeKerning=True, makeInfo=True):
+        instanceElements = self.root.findall('.instances/instance')
+        for instanceElement in instanceElements:
+            self._readSingleInstanceElement(instanceElement, makeGlyphs=makeGlyphs, makeKerning=makeKerning, makeInfo=makeInfo)
+
+    def _readSingleInstanceElement(self, instanceElement, makeGlyphs=True, makeKerning=True, makeInfo=True):
+        filename = instanceElement.attrib.get('filename')
+        if filename is not None:
+            instancePath = os.path.join(os.path.dirname(self.documentObject.path), filename)
+        else:
+            instancePath = None
+        instanceObject = self.instanceDescriptorClass()
+        instanceObject.path = instancePath    # absolute path to the instance
+        instanceObject.filename = filename    # path as it is stored in the document
+        name = instanceElement.attrib.get("name")
+        if name is not None:
+            instanceObject.name = name
+        familyname = instanceElement.attrib.get('familyname')
+        if familyname is not None:
+            instanceObject.familyName = familyname
+        stylename = instanceElement.attrib.get('stylename')
+        if stylename is not None:
+            instanceObject.styleName = stylename
+        postScriptFontName = instanceElement.attrib.get('postscriptfontname')
+        if postScriptFontName is not None:
+            instanceObject.postScriptFontName = postScriptFontName
+        styleMapFamilyName = instanceElement.attrib.get('stylemapfamilyname')
+        if styleMapFamilyName is not None:
+            instanceObject.styleMapFamilyName = styleMapFamilyName
+        styleMapStyleName = instanceElement.attrib.get('stylemapstylename')
+        if styleMapStyleName is not None:
+            instanceObject.styleMapStyleName = styleMapStyleName
+        # read localised names
+        for styleNameElement in instanceElement.findall('stylename'):
+            for key, lang in styleNameElement.items():
+                if key == XML_LANG:
+                    styleName = styleNameElement.text
+                    instanceObject.setStyleName(styleName, lang)
+        for familyNameElement in instanceElement.findall('familyname'):
+            for key, lang in familyNameElement.items():
+                if key == XML_LANG:
+                    familyName = familyNameElement.text
+                    instanceObject.setFamilyName(familyName, lang)
+        for styleMapStyleNameElement in instanceElement.findall('stylemapstylename'):
+            for key, lang in styleMapStyleNameElement.items():
+                if key == XML_LANG:
+                    styleMapStyleName = styleMapStyleNameElement.text
+                    instanceObject.setStyleMapStyleName(styleMapStyleName, lang)
+        for styleMapFamilyNameElement in instanceElement.findall('stylemapfamilyname'):
+            for key, lang in styleMapFamilyNameElement.items():
+                if key == XML_LANG:
+                    styleMapFamilyName = styleMapFamilyNameElement.text
+                    instanceObject.setStyleMapFamilyName(styleMapFamilyName, lang)
+        instanceLocation = self.locationFromElement(instanceElement)
+        if instanceLocation is not None:
+            instanceObject.location = instanceLocation
+        for glyphElement in instanceElement.findall('.glyphs/glyph'):
+            self.readGlyphElement(glyphElement, instanceObject)
+        for infoElement in instanceElement.findall("info"):
+            self.readInfoElement(infoElement, instanceObject)
+        for libElement in instanceElement.findall('lib'):
+            self.readLibElement(libElement, instanceObject)
+        self.documentObject.instances.append(instanceObject)
+
+    def readLibElement(self, libElement, instanceObject):
+        """Read the lib element for the given instance."""
+        instanceObject.lib = from_plist(libElement[0])
+
+    def readInfoElement(self, infoElement, instanceObject):
+        """ Read the info element."""
+        instanceObject.info = True
+
+    def readKerningElement(self, kerningElement, instanceObject):
+        """ Read the kerning element."""
+        kerningLocation = self.locationFromElement(kerningElement)
+        instanceObject.addKerning(kerningLocation)
+
+    def readGlyphElement(self, glyphElement, instanceObject):
+        """
+        Read the glyph element.
+            <glyph name="b" unicode="0x62"/>
+            <glyph name="b"/>
+            <glyph name="b">
+                <master location="location-token-bbb" source="master-token-aaa2"/>
+                <master glyphname="b.alt1" location="location-token-ccc" source="master-token-aaa3"/>
+                <note>
+                    This is an instance from an anisotropic interpolation.
+                </note>
+            </glyph>
+        """
+        glyphData = {}
+        glyphName = glyphElement.attrib.get('name')
+        if glyphName is None:
+            raise DesignSpaceDocumentError("Glyph object without name attribute")
+        mute = glyphElement.attrib.get("mute")
+        if mute == "1":
+            glyphData['mute'] = True
+        # unicode
+        unicodes = glyphElement.attrib.get('unicode')
+        if unicodes is not None:
+            try:
+                unicodes = [int(u, 16) for u in unicodes.split(" ")]
+                glyphData['unicodes'] = unicodes
+            except ValueError:
+                raise DesignSpaceDocumentError("unicode values %s are not integers" % unicodes)
+
+        for noteElement in glyphElement.findall('.note'):
+            glyphData['note'] = noteElement.text
+            break
+        instanceLocation = self.locationFromElement(glyphElement)
+        if instanceLocation is not None:
+            glyphData['instanceLocation'] = instanceLocation
+        glyphSources = None
+        for masterElement in glyphElement.findall('.masters/master'):
+            fontSourceName = masterElement.attrib.get('source')
+            sourceLocation = self.locationFromElement(masterElement)
+            masterGlyphName = masterElement.attrib.get('glyphname')
+            if masterGlyphName is None:
+                # if we don't read a glyphname, use the one we have
+                masterGlyphName = glyphName
+            d = dict(font=fontSourceName,
+                     location=sourceLocation,
+                     glyphName=masterGlyphName)
+            if glyphSources is None:
+                glyphSources = []
+            glyphSources.append(d)
+        if glyphSources is not None:
+            glyphData['masters'] = glyphSources
+        instanceObject.glyphs[glyphName] = glyphData
+
+    def readLib(self):
+        """Read the lib element for the whole document."""
+        for libElement in self.root.findall(".lib"):
+            self.documentObject.lib = from_plist(libElement[0])
+
+
+class DesignSpaceDocument(LogMixin):
+    """ Read, write data from the designspace file"""
+    def __init__(self, readerClass=None, writerClass=None):
+        self.path = None
+        self.filename = None
+        """String, optional. When the document is read from the disk, this is
+        its original file name, i.e. the last part of its path.
+
+        When the document is produced by a Python script and still only exists
+        in memory, the producing script can write here an indication of a
+        possible "good" filename, in case one wants to save the file somewhere.
+        """
+
+        self.formatVersion = None
+        self.sources = []
+        self.instances = []
+        self.axes = []
+        self.rules = []
+        self.default = None         # name of the default master
+        self.defaultLoc = None
+
+        self.lib = {}
+        """Custom data associated with the whole document."""
+
+        #
+        if readerClass is not None:
+            self.readerClass = readerClass
+        else:
+            self.readerClass = BaseDocReader
+        if writerClass is not None:
+            self.writerClass = writerClass
+        else:
+            self.writerClass = BaseDocWriter
+
+    def read(self, path):
+        self.path = path
+        self.filename = os.path.basename(path)
+        reader = self.readerClass(path, self)
+        reader.read()
+        if self.sources:
+            self.findDefault()
+
+    def write(self, path):
+        self.path = path
+        self.filename = os.path.basename(path)
+        self.updatePaths()
+        writer = self.writerClass(path, self)
+        writer.write()
+
+    def _posixRelativePath(self, otherPath):
+        relative = os.path.relpath(otherPath, os.path.dirname(self.path))
+        return posix(relative)
+
+    def updatePaths(self):
+        """
+            Right before we save we need to identify and respond to the following situations:
+            In each descriptor, we have to do the right thing for the filename attribute.
+
+            case 1.
+            descriptor.filename == None
+            descriptor.path == None
+
+            -- action:
+            write as is, descriptors will not have a filename attr.
+            useless, but no reason to interfere.
+
+
+            case 2.
+            descriptor.filename == "../something"
+            descriptor.path == None
+
+            -- action:
+            write as is. The filename attr should not be touched.
+
+
+            case 3.
+            descriptor.filename == None
+            descriptor.path == "~/absolute/path/there"
+
+            -- action:
+            calculate the relative path for filename.
+            We're not overwriting some other value for filename, it should be fine
+
+
+            case 4.
+            descriptor.filename == '../somewhere'
+            descriptor.path == "~/absolute/path/there"
+
+            -- action:
+            there is a conflict between the given filename, and the path.
+            So we know where the file is relative to the document.
+            Can't guess why they're different, we just choose for path to be correct and update filename.
+
+
+        """
+        for descriptor in self.sources + self.instances:
+            # check what the relative path really should be?
+            expectedFilename = None
+            if descriptor.path is not None and self.path is not None:
+                expectedFilename = self._posixRelativePath(descriptor.path)
+
+            # 3
+            if descriptor.filename is None and descriptor.path is not None and self.path is not None:
+                descriptor.filename = self._posixRelativePath(descriptor.path)
+                continue
+
+            # 4
+            if descriptor.filename is not None and descriptor.path is not None and self.path is not None:
+                if descriptor.filename is not expectedFilename:
+                    descriptor.filename = expectedFilename
+
+    def addSource(self, sourceDescriptor):
+        self.sources.append(sourceDescriptor)
+
+    def addInstance(self, instanceDescriptor):
+        self.instances.append(instanceDescriptor)
+
+    def addAxis(self, axisDescriptor):
+        self.axes.append(axisDescriptor)
+
+    def addRule(self, ruleDescriptor):
+        self.rules.append(ruleDescriptor)
+
+    def newDefaultLocation(self):
+        # Without OrderedDict, output XML would be non-deterministic.
+        # https://github.com/LettError/designSpaceDocument/issues/10
+        loc = collections.OrderedDict()
+        for axisDescriptor in self.axes:
+            loc[axisDescriptor.name] = axisDescriptor.default
+        return loc
+
+    def updateFilenameFromPath(self, masters=True, instances=True, force=False):
+        # set a descriptor filename attr from the path and this document path
+        # if the filename attribute is not None: skip it.
+        if masters:
+            for descriptor in self.sources:
+                if descriptor.filename is not None and not force:
+                    continue
+                if self.path is not None:
+                    descriptor.filename = self._posixRelativePath(descriptor.path)
+        if instances:
+            for descriptor in self.instances:
+                if descriptor.filename is not None and not force:
+                    continue
+                if self.path is not None:
+                    descriptor.filename = self._posixRelativePath(descriptor.path)
+
+    def newAxisDescriptor(self):
+        # Ask the writer class to make us a new axisDescriptor
+        return self.writerClass.getAxisDecriptor()
+
+    def newSourceDescriptor(self):
+        # Ask the writer class to make us a new sourceDescriptor
+        return self.writerClass.getSourceDescriptor()
+
+    def newInstanceDescriptor(self):
+        # Ask the writer class to make us a new instanceDescriptor
+        return self.writerClass.getInstanceDescriptor()
+
+    def getAxisOrder(self):
+        names = []
+        for axisDescriptor in self.axes:
+            names.append(axisDescriptor.name)
+        return names
+
+    def getAxis(self, name):
+        for axisDescriptor in self.axes:
+            if axisDescriptor.name == name:
+                return axisDescriptor
+        return None
+
+    def findDefault(self):
+        # new default finder
+        # take the sourcedescriptor with the location at all the defaults
+        # if we can't find it, return None, let someone else figure it out
+        self.default = None
+        for sourceDescriptor in self.sources:
+            if sourceDescriptor.location == self.defaultLoc:
+                # we choose you!
+                self.default = sourceDescriptor
+                return sourceDescriptor
+        return None
+
+    def normalizeLocation(self, location):
+        # adapted from fontTools.varlib.models.normalizeLocation because:
+        #   - this needs to work with axis names, not tags
+        #   - this needs to accomodate anisotropic locations
+        #   - the axes are stored differently here, it's just math
+        new = {}
+        for axis in self.axes:
+            if axis.name not in location:
+                # skipping this dimension it seems
+                continue
+            v = location.get(axis.name, axis.default)
+            if type(v) == tuple:
+                v = v[0]
+            if v == axis.default:
+                v = 0.0
+            elif v < axis.default:
+                if axis.default == axis.minimum:
+                    v = 0.0
+                else:
+                    v = (max(v, axis.minimum) - axis.default) / (axis.default - axis.minimum)
+            else:
+                if axis.default == axis.maximum:
+                    v = 0.0
+                else:
+                    v = (min(v, axis.maximum) - axis.default) / (axis.maximum - axis.default)
+            new[axis.name] = v
+        return new
+
+    def normalize(self):
+        # Normalise the geometry of this designspace:
+        #   scale all the locations of all masters and instances to the -1 - 0 - 1 value.
+        #   we need the axis data to do the scaling, so we do those last.
+        # masters
+        for item in self.sources:
+            item.location = self.normalizeLocation(item.location)
+        # instances
+        for item in self.instances:
+            # glyph masters for this instance
+            for _, glyphData in item.glyphs.items():
+                glyphData['instanceLocation'] = self.normalizeLocation(glyphData['instanceLocation'])
+                for glyphMaster in glyphData['masters']:
+                    glyphMaster['location'] = self.normalizeLocation(glyphMaster['location'])
+            item.location = self.normalizeLocation(item.location)
+        # the axes
+        for axis in self.axes:
+            # scale the map first
+            newMap = []
+            for inputValue, outputValue in axis.map:
+                newOutputValue = self.normalizeLocation({axis.name: outputValue}).get(axis.name)
+                newMap.append((inputValue, newOutputValue))
+            if newMap:
+                axis.map = newMap
+            # finally the axis values
+            minimum = self.normalizeLocation({axis.name: axis.minimum}).get(axis.name)
+            maximum = self.normalizeLocation({axis.name: axis.maximum}).get(axis.name)
+            default = self.normalizeLocation({axis.name: axis.default}).get(axis.name)
+            # and set them in the axis.minimum
+            axis.minimum = minimum
+            axis.maximum = maximum
+            axis.default = default
+        # now the rules
+        for rule in self.rules:
+            newConditionSets = []
+            for conditions in rule.conditionSets:
+                newConditions = []
+                for cond in conditions:
+                    if cond.get('minimum') is not None:
+                        minimum = self.normalizeLocation({cond['name']: cond['minimum']}).get(cond['name'])
+                    else:
+                        minimum = None
+                    if cond.get('maximum') is not None:
+                        maximum = self.normalizeLocation({cond['name']: cond['maximum']}).get(cond['name'])
+                    else:
+                        maximum = None
+                    newConditions.append(dict(name=cond['name'], minimum=minimum, maximum=maximum))
+                newConditionSets.append(newConditions)
+            rule.conditionSets = newConditionSets
diff --git a/Lib/fontTools/encodings/MacRoman.py b/Lib/fontTools/encodings/MacRoman.py
index bfeb0d5..43c58eb 100644
--- a/Lib/fontTools/encodings/MacRoman.py
+++ b/Lib/fontTools/encodings/MacRoman.py
@@ -1,37 +1,39 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+
 MacRoman = [
-		'NUL', 'Eth', 'eth', 'Lslash', 'lslash', 'Scaron', 'scaron', 'Yacute', 
-		'yacute', 'HT', 'LF', 'Thorn', 'thorn', 'CR', 'Zcaron', 'zcaron', 'DLE', 'DC1', 
-		'DC2', 'DC3', 'DC4', 'onehalf', 'onequarter', 'onesuperior', 'threequarters', 
-		'threesuperior', 'twosuperior', 'brokenbar', 'minus', 'multiply', 'RS', 'US', 
-		'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 
-		'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', 'DEL', 'Adieresis', 'Aring', 'Ccedilla', 'Eacute', 
-		'Ntilde', 'Odieresis', 'Udieresis', 'aacute', 'agrave', 'acircumflex', 
-		'adieresis', 'atilde', 'aring', 'ccedilla', 'eacute', 'egrave', 'ecircumflex', 
-		'edieresis', 'iacute', 'igrave', 'icircumflex', 'idieresis', 'ntilde', 
-		'oacute', 'ograve', 'ocircumflex', 'odieresis', 'otilde', 'uacute', 'ugrave', 
-		'ucircumflex', 'udieresis', 'dagger', 'degree', 'cent', 'sterling', 'section', 
-		'bullet', 'paragraph', 'germandbls', 'registered', 'copyright', 'trademark', 
-		'acute', 'dieresis', 'notequal', 'AE', 'Oslash', 'infinity', 'plusminus', 
-		'lessequal', 'greaterequal', 'yen', 'mu', 'partialdiff', 'summation', 
-		'product', 'pi', 'integral', 'ordfeminine', 'ordmasculine', 'Omega', 'ae', 
-		'oslash', 'questiondown', 'exclamdown', 'logicalnot', 'radical', 'florin', 
-		'approxequal', 'Delta', 'guillemotleft', 'guillemotright', 'ellipsis', 
-		'nbspace', 'Agrave', 'Atilde', 'Otilde', 'OE', 'oe', 'endash', 'emdash', 
-		'quotedblleft', 'quotedblright', 'quoteleft', 'quoteright', 'divide', 'lozenge', 
-		'ydieresis', 'Ydieresis', 'fraction', 'currency', 'guilsinglleft', 
-		'guilsinglright', 'fi', 'fl', 'daggerdbl', 'periodcentered', 'quotesinglbase', 
-		'quotedblbase', 'perthousand', 'Acircumflex', 'Ecircumflex', 'Aacute', 
-		'Edieresis', 'Egrave', 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Oacute', 
-		'Ocircumflex', 'apple', 'Ograve', 'Uacute', 'Ucircumflex', 'Ugrave', 'dotlessi', 
-		'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', 'ring', 'cedilla', 
+		'NUL', 'Eth', 'eth', 'Lslash', 'lslash', 'Scaron', 'scaron', 'Yacute',
+		'yacute', 'HT', 'LF', 'Thorn', 'thorn', 'CR', 'Zcaron', 'zcaron', 'DLE', 'DC1',
+		'DC2', 'DC3', 'DC4', 'onehalf', 'onequarter', 'onesuperior', 'threequarters',
+		'threesuperior', 'twosuperior', 'brokenbar', 'minus', 'multiply', 'RS', 'US',
+		'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand',
+		'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', 'DEL', 'Adieresis', 'Aring', 'Ccedilla', 'Eacute',
+		'Ntilde', 'Odieresis', 'Udieresis', 'aacute', 'agrave', 'acircumflex',
+		'adieresis', 'atilde', 'aring', 'ccedilla', 'eacute', 'egrave', 'ecircumflex',
+		'edieresis', 'iacute', 'igrave', 'icircumflex', 'idieresis', 'ntilde',
+		'oacute', 'ograve', 'ocircumflex', 'odieresis', 'otilde', 'uacute', 'ugrave',
+		'ucircumflex', 'udieresis', 'dagger', 'degree', 'cent', 'sterling', 'section',
+		'bullet', 'paragraph', 'germandbls', 'registered', 'copyright', 'trademark',
+		'acute', 'dieresis', 'notequal', 'AE', 'Oslash', 'infinity', 'plusminus',
+		'lessequal', 'greaterequal', 'yen', 'mu', 'partialdiff', 'summation',
+		'product', 'pi', 'integral', 'ordfeminine', 'ordmasculine', 'Omega', 'ae',
+		'oslash', 'questiondown', 'exclamdown', 'logicalnot', 'radical', 'florin',
+		'approxequal', 'Delta', 'guillemotleft', 'guillemotright', 'ellipsis',
+		'nbspace', 'Agrave', 'Atilde', 'Otilde', 'OE', 'oe', 'endash', 'emdash',
+		'quotedblleft', 'quotedblright', 'quoteleft', 'quoteright', 'divide', 'lozenge',
+		'ydieresis', 'Ydieresis', 'fraction', 'currency', 'guilsinglleft',
+		'guilsinglright', 'fi', 'fl', 'daggerdbl', 'periodcentered', 'quotesinglbase',
+		'quotedblbase', 'perthousand', 'Acircumflex', 'Ecircumflex', 'Aacute',
+		'Edieresis', 'Egrave', 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Oacute',
+		'Ocircumflex', 'apple', 'Ograve', 'Uacute', 'Ucircumflex', 'Ugrave', 'dotlessi',
+		'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', 'ring', 'cedilla',
 		'hungarumlaut', 'ogonek', 'caron'
 		]
-
diff --git a/Lib/fontTools/encodings/StandardEncoding.py b/Lib/fontTools/encodings/StandardEncoding.py
index 810b2a0..dc01ef8 100644
--- a/Lib/fontTools/encodings/StandardEncoding.py
+++ b/Lib/fontTools/encodings/StandardEncoding.py
@@ -1,3 +1,6 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+
 StandardEncoding = [
 		'.notdef', '.notdef', '.notdef', '.notdef', '.notdef',
 		'.notdef', '.notdef', '.notdef', '.notdef', '.notdef',
diff --git a/Lib/fontTools/encodings/__init__.py b/Lib/fontTools/encodings/__init__.py
index e001bb2..3f9abc9 100644
--- a/Lib/fontTools/encodings/__init__.py
+++ b/Lib/fontTools/encodings/__init__.py
@@ -1,3 +1,4 @@
-"""Empty __init__.py file to signal Python this directory is a package.
-(It can't be completely empty since WinZip seems to skip empty files.)
-"""
+"""Empty __init__.py file to signal Python this directory is a package."""
+
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
diff --git a/Lib/fontTools/encodings/codecs.py b/Lib/fontTools/encodings/codecs.py
new file mode 100644
index 0000000..30e4691
--- /dev/null
+++ b/Lib/fontTools/encodings/codecs.py
@@ -0,0 +1,135 @@
+"""Extend the Python codecs module with a few encodings that are used in OpenType (name table)
+but missing from Python.  See https://github.com/behdad/fonttools/issues/236 for details."""
+
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+import codecs
+import encodings
+
+class ExtendCodec(codecs.Codec):
+
+	def __init__(self, name, base_encoding, mapping):
+		self.name = name
+		self.base_encoding = base_encoding
+		self.mapping = mapping
+		self.reverse = {v:k for k,v in mapping.items()}
+		self.max_len = max(len(v) for v in mapping.values())
+		self.info = codecs.CodecInfo(name=self.name, encode=self.encode, decode=self.decode)
+		codecs.register_error(name, self.error)
+
+	def encode(self, input, errors='strict'):
+		assert errors == 'strict'
+		#return codecs.encode(input, self.base_encoding, self.name), len(input)
+
+		# The above line could totally be all we needed, relying on the error
+		# handling to replace the unencodable Unicode characters with our extended
+		# byte sequences.
+		#
+		# However, there seems to be a design bug in Python (probably intentional):
+		# the error handler for encoding is supposed to return a **Unicode** character,
+		# that then needs to be encodable itself...  Ugh.
+		#
+		# So we implement what codecs.encode() should have been doing: which is expect
+		# error handler to return bytes() to be added to the output.
+		#
+		# This seems to have been fixed in Python 3.3.  We should try using that and
+		# use fallback only if that failed.
+		# https://docs.python.org/3.3/library/codecs.html#codecs.register_error
+
+		length = len(input)
+		out = b''
+		while input:
+			try:
+				part = codecs.encode(input, self.base_encoding)
+				out += part
+				input = '' # All converted
+			except UnicodeEncodeError as e:
+				# Convert the correct part
+				out += codecs.encode(input[:e.start], self.base_encoding)
+				replacement, pos = self.error(e)
+				out += replacement
+				input = input[pos:]
+		return out, length
+
+	def decode(self, input, errors='strict'):
+		assert errors == 'strict'
+		return codecs.decode(input, self.base_encoding, self.name), len(input)
+
+	def error(self, e):
+		if isinstance(e, UnicodeDecodeError):
+			for end in range(e.start + 1, e.end + 1):
+				s = e.object[e.start:end]
+				if s in self.mapping:
+					return self.mapping[s], end
+		elif isinstance(e, UnicodeEncodeError):
+			for end in range(e.start + 1, e.start + self.max_len + 1):
+				s = e.object[e.start:end]
+				if s in self.reverse:
+					return self.reverse[s], end
+		e.encoding = self.name
+		raise e
+
+
+_extended_encodings = {
+	"x_mac_japanese_ttx": ("shift_jis", {
+					b"\xFC": unichr(0x007C),
+					b"\x7E": unichr(0x007E),
+					b"\x80": unichr(0x005C),
+					b"\xA0": unichr(0x00A0),
+					b"\xFD": unichr(0x00A9),
+					b"\xFE": unichr(0x2122),
+					b"\xFF": unichr(0x2026),
+				}),
+	"x_mac_trad_chinese_ttx": ("big5", {
+					b"\x80": unichr(0x005C),
+					b"\xA0": unichr(0x00A0),
+					b"\xFD": unichr(0x00A9),
+					b"\xFE": unichr(0x2122),
+					b"\xFF": unichr(0x2026),
+				}),
+	"x_mac_korean_ttx": ("euc_kr", {
+					b"\x80": unichr(0x00A0),
+					b"\x81": unichr(0x20A9),
+					b"\x82": unichr(0x2014),
+					b"\x83": unichr(0x00A9),
+					b"\xFE": unichr(0x2122),
+					b"\xFF": unichr(0x2026),
+				}),
+	"x_mac_simp_chinese_ttx": ("gb2312", {
+					b"\x80": unichr(0x00FC),
+					b"\xA0": unichr(0x00A0),
+					b"\xFD": unichr(0x00A9),
+					b"\xFE": unichr(0x2122),
+					b"\xFF": unichr(0x2026),
+				}),
+}
+
+_cache = {}
+
+def search_function(name):
+	name = encodings.normalize_encoding(name) # Rather undocumented...
+	if name in _extended_encodings:
+		if name not in _cache:
+			base_encoding, mapping = _extended_encodings[name]
+			assert(name[-4:] == "_ttx")
+			# Python 2 didn't have any of the encodings that we are implementing
+			# in this file.  Python 3 added aliases for the East Asian ones, mapping
+			# them "temporarily" to the same base encoding as us, with a comment
+			# suggesting that full implementation will appear some time later.
+			# As such, try the Python version of the x_mac_... first, if that is found,
+			# use *that* as our base encoding.  This would make our encoding upgrade
+			# to the full encoding when and if Python finally implements that.
+			# http://bugs.python.org/issue24041
+			base_encodings = [name[:-4], base_encoding]
+			for base_encoding in base_encodings:
+				try:
+					codecs.lookup(base_encoding)
+				except LookupError:
+					continue
+				_cache[name] = ExtendCodec(name, base_encoding, mapping)
+				break
+		return _cache[name].info
+
+	return None
+
+codecs.register(search_function)
diff --git a/Lib/fontTools/feaLib/__init__.py b/Lib/fontTools/feaLib/__init__.py
new file mode 100644
index 0000000..ae532cd
--- /dev/null
+++ b/Lib/fontTools/feaLib/__init__.py
@@ -0,0 +1,4 @@
+"""fontTools.feaLib -- a package for dealing with OpenType feature files."""
+
+# The structure of OpenType feature files is defined here:
+# http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
diff --git a/Lib/fontTools/feaLib/__main__.py b/Lib/fontTools/feaLib/__main__.py
new file mode 100644
index 0000000..e446db6
--- /dev/null
+++ b/Lib/fontTools/feaLib/__main__.py
@@ -0,0 +1,42 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.ttLib import TTFont
+from fontTools.feaLib.builder import addOpenTypeFeatures
+from fontTools import configLogger
+from fontTools.misc.cliTools import makeOutputFileName
+import sys
+import argparse
+import logging
+
+
+log = logging.getLogger("fontTools.feaLib")
+
+
+def main(args=None):
+    parser = argparse.ArgumentParser(
+        description="Use fontTools to compile OpenType feature files (*.fea).")
+    parser.add_argument(
+        "input_fea", metavar="FEATURES", help="Path to the feature file")
+    parser.add_argument(
+        "input_font", metavar="INPUT_FONT", help="Path to the input font")
+    parser.add_argument(
+        "-o", "--output", dest="output_font", metavar="OUTPUT_FONT",
+        help="Path to the output font.")
+    parser.add_argument(
+        "-v", "--verbose", help="increase the logger verbosity. Multiple -v "
+        "options are allowed.", action="count", default=0)
+    options = parser.parse_args(args)
+
+    levels = ["WARNING", "INFO", "DEBUG"]
+    configLogger(level=levels[min(len(levels) - 1, options.verbose)])
+
+    output_font = options.output_font or makeOutputFileName(options.input_font)
+    log.info("Compiling features to '%s'" % (output_font))
+
+    font = TTFont(options.input_font)
+    addOpenTypeFeatures(font, options.input_fea)
+    font.save(output_font)
+
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/Lib/fontTools/feaLib/ast.py b/Lib/fontTools/feaLib/ast.py
new file mode 100644
index 0000000..3c2c5f6
--- /dev/null
+++ b/Lib/fontTools/feaLib/ast.py
@@ -0,0 +1,1346 @@
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.feaLib.error import FeatureLibError
+from fontTools.misc.encodingTools import getEncoding
+from collections import OrderedDict
+import itertools
+
+SHIFT = " " * 4
+
+__all__ = [
+    'AlternateSubstStatement',
+    'Anchor',
+    'AnchorDefinition',
+    'AnonymousBlock',
+    'AttachStatement',
+    'BaseAxis',
+    'Block',
+    'BytesIO',
+    'CVParametersNameStatement',
+    'ChainContextPosStatement',
+    'ChainContextSubstStatement',
+    'CharacterStatement',
+    'Comment',
+    'CursivePosStatement',
+    'Element',
+    'Expression',
+    'FeatureBlock',
+    'FeatureFile',
+    'FeatureLibError',
+    'FeatureNameStatement',
+    'FeatureReferenceStatement',
+    'FontRevisionStatement',
+    'GlyphClass',
+    'GlyphClassDefStatement',
+    'GlyphClassDefinition',
+    'GlyphClassName',
+    'GlyphName',
+    'HheaField',
+    'IgnorePosStatement',
+    'IgnoreSubstStatement',
+    'IncludeStatement',
+    'LanguageStatement',
+    'LanguageSystemStatement',
+    'LigatureCaretByIndexStatement',
+    'LigatureCaretByPosStatement',
+    'LigatureSubstStatement',
+    'LookupBlock',
+    'LookupFlagStatement',
+    'LookupReferenceStatement',
+    'MarkBasePosStatement',
+    'MarkClass',
+    'MarkClassDefinition',
+    'MarkClassName',
+    'MarkLigPosStatement',
+    'MarkMarkPosStatement',
+    'MultipleSubstStatement',
+    'NameRecord',
+    'NestedBlock',
+    'OS2Field',
+    'OrderedDict',
+    'PairPosStatement',
+    'Py23Error',
+    'ReverseChainSingleSubstStatement',
+    'ScriptStatement',
+    'SimpleNamespace',
+    'SinglePosStatement',
+    'SingleSubstStatement',
+    'SizeParameters',
+    'Statement',
+    'StringIO',
+    'SubtableStatement',
+    'TableBlock',
+    'Tag',
+    'UnicodeIO',
+    'ValueRecord',
+    'ValueRecordDefinition',
+    'VheaField',
+]
+
+
+def deviceToString(device):
+    if device is None:
+        return "<device NULL>"
+    else:
+        return "<device %s>" % ", ".join("%d %d" % t for t in device)
+
+
+fea_keywords = set([
+    "anchor", "anchordef", "anon", "anonymous",
+    "by",
+    "contour", "cursive",
+    "device",
+    "enum", "enumerate", "excludedflt", "exclude_dflt",
+    "feature", "from",
+    "ignore", "ignorebaseglyphs", "ignoreligatures", "ignoremarks",
+    "include", "includedflt", "include_dflt",
+    "language", "languagesystem", "lookup", "lookupflag",
+    "mark", "markattachmenttype", "markclass",
+    "nameid", "null",
+    "parameters", "pos", "position",
+    "required", "righttoleft", "reversesub", "rsub",
+    "script", "sub", "substitute", "subtable",
+    "table",
+    "usemarkfilteringset", "useextension", "valuerecorddef"]
+)
+
+
+def asFea(g):
+    if hasattr(g, 'asFea'):
+        return g.asFea()
+    elif isinstance(g, tuple) and len(g) == 2:
+        return asFea(g[0]) + "-" + asFea(g[1])   # a range
+    elif g.lower() in fea_keywords:
+        return "\\" + g
+    else:
+        return g
+
+
+class Element(object):
+
+    def __init__(self, location=None):
+        self.location = location
+
+    def build(self, builder):
+        pass
+
+    def asFea(self, indent=""):
+        raise NotImplementedError
+
+    def __str__(self):
+        return self.asFea()
+
+
+class Statement(Element):
+    pass
+
+
+class Expression(Element):
+    pass
+
+
+class Comment(Element):
+    def __init__(self, text, location=None):
+        super(Comment, self).__init__(location)
+        self.text = text
+
+    def asFea(self, indent=""):
+        return self.text
+
+
+class GlyphName(Expression):
+    """A single glyph name, such as cedilla."""
+    def __init__(self, glyph, location=None):
+        Expression.__init__(self, location)
+        self.glyph = glyph
+
+    def glyphSet(self):
+        return (self.glyph,)
+
+    def asFea(self, indent=""):
+        return str(self.glyph)
+
+
+class GlyphClass(Expression):
+    """A glyph class, such as [acute cedilla grave]."""
+    def __init__(self, glyphs=None, location=None):
+        Expression.__init__(self, location)
+        self.glyphs = glyphs if glyphs is not None else []
+        self.original = []
+        self.curr = 0
+
+    def glyphSet(self):
+        return tuple(self.glyphs)
+
+    def asFea(self, indent=""):
+        if len(self.original):
+            if self.curr < len(self.glyphs):
+                self.original.extend(self.glyphs[self.curr:])
+                self.curr = len(self.glyphs)
+            return "[" + " ".join(map(asFea, self.original)) + "]"
+        else:
+            return "[" + " ".join(map(asFea, self.glyphs)) + "]"
+
+    def extend(self, glyphs):
+        self.glyphs.extend(glyphs)
+
+    def append(self, glyph):
+        self.glyphs.append(glyph)
+
+    def add_range(self, start, end, glyphs):
+        if self.curr < len(self.glyphs):
+            self.original.extend(self.glyphs[self.curr:])
+        self.original.append((start, end))
+        self.glyphs.extend(glyphs)
+        self.curr = len(self.glyphs)
+
+    def add_cid_range(self, start, end, glyphs):
+        if self.curr < len(self.glyphs):
+            self.original.extend(self.glyphs[self.curr:])
+        self.original.append(("cid{:05d}".format(start), "cid{:05d}".format(end)))
+        self.glyphs.extend(glyphs)
+        self.curr = len(self.glyphs)
+
+    def add_class(self, gc):
+        if self.curr < len(self.glyphs):
+            self.original.extend(self.glyphs[self.curr:])
+        self.original.append(gc)
+        self.glyphs.extend(gc.glyphSet())
+        self.curr = len(self.glyphs)
+
+
+class GlyphClassName(Expression):
+    """A glyph class name, such as @FRENCH_MARKS."""
+    def __init__(self, glyphclass, location=None):
+        Expression.__init__(self, location)
+        assert isinstance(glyphclass, GlyphClassDefinition)
+        self.glyphclass = glyphclass
+
+    def glyphSet(self):
+        return tuple(self.glyphclass.glyphSet())
+
+    def asFea(self, indent=""):
+        return "@" + self.glyphclass.name
+
+
+class MarkClassName(Expression):
+    """A mark class name, such as @FRENCH_MARKS defined with markClass."""
+    def __init__(self, markClass, location=None):
+        Expression.__init__(self, location)
+        assert isinstance(markClass, MarkClass)
+        self.markClass = markClass
+
+    def glyphSet(self):
+        return self.markClass.glyphSet()
+
+    def asFea(self, indent=""):
+        return "@" + self.markClass.name
+
+
+class AnonymousBlock(Statement):
+    def __init__(self, tag, content, location=None):
+        Statement.__init__(self, location)
+        self.tag, self.content = tag, content
+
+    def asFea(self, indent=""):
+        res = "anon {} {{\n".format(self.tag)
+        res += self.content
+        res += "}} {};\n\n".format(self.tag)
+        return res
+
+
+class Block(Statement):
+    def __init__(self, location=None):
+        Statement.__init__(self, location)
+        self.statements = []
+
+    def build(self, builder):
+        for s in self.statements:
+            s.build(builder)
+
+    def asFea(self, indent=""):
+        indent += SHIFT
+        return indent + ("\n" + indent).join(
+            [s.asFea(indent=indent) for s in self.statements]) + "\n"
+
+
+class FeatureFile(Block):
+    def __init__(self):
+        Block.__init__(self, location=None)
+        self.markClasses = {}  # name --> ast.MarkClass
+
+    def asFea(self, indent=""):
+        return "\n".join(s.asFea(indent=indent) for s in self.statements)
+
+
+class FeatureBlock(Block):
+    def __init__(self, name, use_extension=False, location=None):
+        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)
+        # language exclude_dflt statements modify builder.features_
+        # limit them to this block with temporary builder.features_
+        features = builder.features_
+        builder.features_ = {}
+        Block.build(self, builder)
+        for key, value in builder.features_.items():
+            features.setdefault(key, []).extend(value)
+        builder.features_ = features
+        builder.end_feature()
+
+    def asFea(self, indent=""):
+        res = indent + "feature %s {\n" % self.name.strip()
+        res += Block.asFea(self, indent=indent)
+        res += indent + "} %s;\n" % self.name.strip()
+        return res
+
+
+class NestedBlock(Block):
+    def __init__(self, tag, block_name, location=None):
+        Block.__init__(self, location)
+        self.tag = tag
+        self.block_name = block_name
+
+    def build(self, builder):
+        Block.build(self, builder)
+        if self.block_name == "ParamUILabelNameID":
+            builder.add_to_cv_num_named_params(self.tag)
+
+    def asFea(self, indent=""):
+        res = "{}{} {{\n".format(indent, self.block_name)
+        res += Block.asFea(self, indent=indent)
+        res += "{}}};\n".format(indent)
+        return res
+
+
+class LookupBlock(Block):
+    def __init__(self, name, use_extension=False, location=None):
+        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()
+
+    def asFea(self, indent=""):
+        res = "lookup {} {{\n".format(self.name)
+        res += Block.asFea(self, indent=indent)
+        res += "{}}} {};\n".format(indent, self.name)
+        return res
+
+
+class TableBlock(Block):
+    def __init__(self, name, location=None):
+        Block.__init__(self, location)
+        self.name = name
+
+    def asFea(self, indent=""):
+        res = "table {} {{\n".format(self.name.strip())
+        res += super(TableBlock, self).asFea(indent=indent)
+        res += "}} {};\n".format(self.name.strip())
+        return res
+
+
+class GlyphClassDefinition(Statement):
+    """Example: @UPPERCASE = [A-Z];"""
+    def __init__(self, name, glyphs, location=None):
+        Statement.__init__(self, location)
+        self.name = name
+        self.glyphs = glyphs
+
+    def glyphSet(self):
+        return tuple(self.glyphs.glyphSet())
+
+    def asFea(self, indent=""):
+        return "@" + self.name + " = " + self.glyphs.asFea() + ";"
+
+
+class GlyphClassDefStatement(Statement):
+    """Example: GlyphClassDef @UPPERCASE, [B], [C], [D];"""
+    def __init__(self, baseGlyphs, markGlyphs, ligatureGlyphs,
+                 componentGlyphs, location=None):
+        Statement.__init__(self, location)
+        self.baseGlyphs, self.markGlyphs = (baseGlyphs, markGlyphs)
+        self.ligatureGlyphs = ligatureGlyphs
+        self.componentGlyphs = componentGlyphs
+
+    def build(self, builder):
+        base = self.baseGlyphs.glyphSet() if self.baseGlyphs else tuple()
+        liga = self.ligatureGlyphs.glyphSet() \
+            if self.ligatureGlyphs else tuple()
+        mark = self.markGlyphs.glyphSet() if self.markGlyphs else tuple()
+        comp = (self.componentGlyphs.glyphSet()
+                if self.componentGlyphs else tuple())
+        builder.add_glyphClassDef(self.location, base, liga, mark, comp)
+
+    def asFea(self, indent=""):
+        return "GlyphClassDef {}, {}, {}, {};".format(
+            self.baseGlyphs.asFea() if self.baseGlyphs else "",
+            self.ligatureGlyphs.asFea() if self.ligatureGlyphs else "",
+            self.markGlyphs.asFea() if self.markGlyphs else "",
+            self.componentGlyphs.asFea() if self.componentGlyphs else "")
+
+
+# While glyph classes can be defined only once, the feature file format
+# allows expanding mark classes with multiple definitions, each using
+# different glyphs and anchors. The following are two MarkClassDefinitions
+# for the same MarkClass:
+#     markClass [acute grave] <anchor 350 800> @FRENCH_ACCENTS;
+#     markClass [cedilla] <anchor 350 -200> @FRENCH_ACCENTS;
+class MarkClass(object):
+    def __init__(self, name):
+        self.name = name
+        self.definitions = []
+        self.glyphs = OrderedDict()  # glyph --> ast.MarkClassDefinitions
+
+    def addDefinition(self, definition):
+        assert isinstance(definition, MarkClassDefinition)
+        self.definitions.append(definition)
+        for glyph in definition.glyphSet():
+            if glyph in self.glyphs:
+                otherLoc = self.glyphs[glyph].location
+                if otherLoc is None:
+                    end = ""
+                else:
+                    end = " at %s:%d:%d" % (
+                        otherLoc[0], otherLoc[1], otherLoc[2])
+                raise FeatureLibError(
+                    "Glyph %s already defined%s" % (glyph, end),
+                    definition.location)
+            self.glyphs[glyph] = definition
+
+    def glyphSet(self):
+        return tuple(self.glyphs.keys())
+
+    def asFea(self, indent=""):
+        res = "\n".join(d.asFea(indent=indent) for d in self.definitions)
+        return res
+
+
+class MarkClassDefinition(Statement):
+    def __init__(self, markClass, anchor, glyphs, location=None):
+        Statement.__init__(self, location)
+        assert isinstance(markClass, MarkClass)
+        assert isinstance(anchor, Anchor) and isinstance(glyphs, Expression)
+        self.markClass, self.anchor, self.glyphs = markClass, anchor, glyphs
+
+    def glyphSet(self):
+        return self.glyphs.glyphSet()
+
+    def asFea(self, indent=""):
+        return "{}markClass {} {} @{};".format(
+            indent, self.glyphs.asFea(), self.anchor.asFea(),
+            self.markClass.name)
+
+
+class AlternateSubstStatement(Statement):
+    def __init__(self, prefix, glyph, suffix, replacement, location=None):
+        Statement.__init__(self, location)
+        self.prefix, self.glyph, self.suffix = (prefix, glyph, suffix)
+        self.replacement = replacement
+
+    def build(self, builder):
+        glyph = self.glyph.glyphSet()
+        assert len(glyph) == 1, glyph
+        glyph = list(glyph)[0]
+        prefix = [p.glyphSet() for p in self.prefix]
+        suffix = [s.glyphSet() for s in self.suffix]
+        replacement = self.replacement.glyphSet()
+        builder.add_alternate_subst(self.location, prefix, glyph, suffix,
+                                    replacement)
+
+    def asFea(self, indent=""):
+        res = "sub "
+        if len(self.prefix) or len(self.suffix):
+            if len(self.prefix):
+                res += " ".join(map(asFea, self.prefix)) + " "
+            res += asFea(self.glyph) + "'"    # even though we really only use 1
+            if len(self.suffix):
+                res += " " + " ".join(map(asFea, self.suffix))
+        else:
+            res += asFea(self.glyph)
+        res += " from "
+        res += asFea(self.replacement)
+        res += ";"
+        return res
+
+
+class Anchor(Expression):
+    def __init__(self, x, y, name=None, contourpoint=None,
+                 xDeviceTable=None, yDeviceTable=None, location=None):
+        Expression.__init__(self, location)
+        self.name = name
+        self.x, self.y, self.contourpoint = x, y, contourpoint
+        self.xDeviceTable, self.yDeviceTable = xDeviceTable, yDeviceTable
+
+    def asFea(self, indent=""):
+        if self.name is not None:
+            return "<anchor {}>".format(self.name)
+        res = "<anchor {} {}".format(self.x, self.y)
+        if self.contourpoint:
+            res += " contourpoint {}".format(self.contourpoint)
+        if self.xDeviceTable or self.yDeviceTable:
+            res += " "
+            res += deviceToString(self.xDeviceTable)
+            res += " "
+            res += deviceToString(self.yDeviceTable)
+        res += ">"
+        return res
+
+
+class AnchorDefinition(Statement):
+    def __init__(self, name, x, y, contourpoint=None, location=None):
+        Statement.__init__(self, location)
+        self.name, self.x, self.y, self.contourpoint = name, x, y, contourpoint
+
+    def asFea(self, indent=""):
+        res = "anchorDef {} {}".format(self.x, self.y)
+        if self.contourpoint:
+            res += " contourpoint {}".format(self.contourpoint)
+        res += " {};".format(self.name)
+        return res
+
+
+class AttachStatement(Statement):
+    def __init__(self, glyphs, contourPoints, location=None):
+        Statement.__init__(self, location)
+        self.glyphs, self.contourPoints = (glyphs, contourPoints)
+
+    def build(self, builder):
+        glyphs = self.glyphs.glyphSet()
+        builder.add_attach_points(self.location, glyphs, self.contourPoints)
+
+    def asFea(self, indent=""):
+        return "Attach {} {};".format(
+            self.glyphs.asFea(), " ".join(str(c) for c in self.contourPoints))
+
+
+class ChainContextPosStatement(Statement):
+    def __init__(self, prefix, glyphs, suffix, lookups, location=None):
+        Statement.__init__(self, location)
+        self.prefix, self.glyphs, self.suffix = prefix, glyphs, suffix
+        self.lookups = lookups
+
+    def build(self, builder):
+        prefix = [p.glyphSet() for p in self.prefix]
+        glyphs = [g.glyphSet() for g in self.glyphs]
+        suffix = [s.glyphSet() for s in self.suffix]
+        builder.add_chain_context_pos(
+            self.location, prefix, glyphs, suffix, self.lookups)
+
+    def asFea(self, indent=""):
+        res = "pos "
+        if len(self.prefix) or len(self.suffix) or any([x is not None for x in self.lookups]):
+            if len(self.prefix):
+                res += " ".join(g.asFea() for g in self.prefix) + " "
+            for i, g in enumerate(self.glyphs):
+                res += g.asFea() + "'"
+                if self.lookups[i] is not None:
+                    res += " lookup " + self.lookups[i].name
+                if i < len(self.glyphs) - 1:
+                    res += " "
+            if len(self.suffix):
+                res += " " + " ".join(map(asFea, self.suffix))
+        else:
+            res += " ".join(map(asFea, self.glyph))
+        res += ";"
+        return res
+
+
+class ChainContextSubstStatement(Statement):
+    def __init__(self, prefix, glyphs, suffix, lookups, location=None):
+        Statement.__init__(self, location)
+        self.prefix, self.glyphs, self.suffix = prefix, glyphs, suffix
+        self.lookups = lookups
+
+    def build(self, builder):
+        prefix = [p.glyphSet() for p in self.prefix]
+        glyphs = [g.glyphSet() for g in self.glyphs]
+        suffix = [s.glyphSet() for s in self.suffix]
+        builder.add_chain_context_subst(
+            self.location, prefix, glyphs, suffix, self.lookups)
+
+    def asFea(self, indent=""):
+        res = "sub "
+        if len(self.prefix) or len(self.suffix) or any([x is not None for x in self.lookups]):
+            if len(self.prefix):
+                res += " ".join(g.asFea() for g in self.prefix) + " "
+            for i, g in enumerate(self.glyphs):
+                res += g.asFea() + "'"
+                if self.lookups[i] is not None:
+                    res += " lookup " + self.lookups[i].name
+                if i < len(self.glyphs) - 1:
+                    res += " "
+            if len(self.suffix):
+                res += " " + " ".join(map(asFea, self.suffix))
+        else:
+            res += " ".join(map(asFea, self.glyph))
+        res += ";"
+        return res
+
+
+class CursivePosStatement(Statement):
+    def __init__(self, glyphclass, entryAnchor, exitAnchor, location=None):
+        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.glyphSet(), self.entryAnchor, self.exitAnchor)
+
+    def asFea(self, indent=""):
+        entry = self.entryAnchor.asFea() if self.entryAnchor else "<anchor NULL>"
+        exit = self.exitAnchor.asFea() if self.exitAnchor else "<anchor NULL>"
+        return "pos cursive {} {} {};".format(self.glyphclass.asFea(), entry, exit)
+
+
+class FeatureReferenceStatement(Statement):
+    """Example: feature salt;"""
+    def __init__(self, featureName, location=None):
+        Statement.__init__(self, location)
+        self.location, self.featureName = (location, featureName)
+
+    def build(self, builder):
+        builder.add_feature_reference(self.location, self.featureName)
+
+    def asFea(self, indent=""):
+        return "feature {};".format(self.featureName)
+
+
+class IgnorePosStatement(Statement):
+    def __init__(self, chainContexts, location=None):
+        Statement.__init__(self, location)
+        self.chainContexts = chainContexts
+
+    def build(self, builder):
+        for prefix, glyphs, suffix in self.chainContexts:
+            prefix = [p.glyphSet() for p in prefix]
+            glyphs = [g.glyphSet() for g in glyphs]
+            suffix = [s.glyphSet() for s in suffix]
+            builder.add_chain_context_pos(
+                self.location, prefix, glyphs, suffix, [])
+
+    def asFea(self, indent=""):
+        contexts = []
+        for prefix, glyphs, suffix in self.chainContexts:
+            res = ""
+            if len(prefix) or len(suffix):
+                if len(prefix):
+                    res += " ".join(map(asFea, prefix)) + " "
+                res += " ".join(g.asFea() + "'" for g in glyphs)
+                if len(suffix):
+                    res += " " + " ".join(map(asFea, suffix))
+            else:
+                res += " ".join(map(asFea, glyphs))
+            contexts.append(res)
+        return "ignore pos " + ", ".join(contexts) + ";"
+
+
+class IgnoreSubstStatement(Statement):
+    def __init__(self, chainContexts, location=None):
+        Statement.__init__(self, location)
+        self.chainContexts = chainContexts
+
+    def build(self, builder):
+        for prefix, glyphs, suffix in self.chainContexts:
+            prefix = [p.glyphSet() for p in prefix]
+            glyphs = [g.glyphSet() for g in glyphs]
+            suffix = [s.glyphSet() for s in suffix]
+            builder.add_chain_context_subst(
+                self.location, prefix, glyphs, suffix, [])
+
+    def asFea(self, indent=""):
+        contexts = []
+        for prefix, glyphs, suffix in self.chainContexts:
+            res = ""
+            if len(prefix) or len(suffix):
+                if len(prefix):
+                    res += " ".join(map(asFea, prefix)) + " "
+                res += " ".join(g.asFea() + "'" for g in glyphs)
+                if len(suffix):
+                    res += " " + " ".join(map(asFea, suffix))
+            else:
+                res += " ".join(map(asFea, glyphs))
+            contexts.append(res)
+        return "ignore sub " + ", ".join(contexts) + ";"
+
+
+class IncludeStatement(Statement):
+    def __init__(self, filename, location=None):
+        super(IncludeStatement, self).__init__(location)
+        self.filename = filename
+
+    def build(self):
+        # TODO: consider lazy-loading the including parser/lexer?
+        raise FeatureLibError(
+            "Building an include statement is not implemented yet. "
+            "Instead, use Parser(..., followIncludes=True) for building.",
+            self.location)
+
+    def asFea(self, indent=""):
+        return indent + "include(%s);" % self.filename
+
+
+class LanguageStatement(Statement):
+    def __init__(self, language, include_default=True, required=False,
+                 location=None):
+        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)
+
+    def asFea(self, indent=""):
+        res = "language {}".format(self.language.strip())
+        if not self.include_default:
+            res += " exclude_dflt"
+        if self.required:
+            res += " required"
+        res += ";"
+        return res
+
+
+class LanguageSystemStatement(Statement):
+    def __init__(self, script, language, location=None):
+        Statement.__init__(self, location)
+        self.script, self.language = (script, language)
+
+    def build(self, builder):
+        builder.add_language_system(self.location, self.script, self.language)
+
+    def asFea(self, indent=""):
+        return "languagesystem {} {};".format(self.script, self.language.strip())
+
+
+class FontRevisionStatement(Statement):
+    def __init__(self, revision, location=None):
+        Statement.__init__(self, location)
+        self.revision = revision
+
+    def build(self, builder):
+        builder.set_font_revision(self.location, self.revision)
+
+    def asFea(self, indent=""):
+        return "FontRevision {:.3f};".format(self.revision)
+
+
+class LigatureCaretByIndexStatement(Statement):
+    def __init__(self, glyphs, carets, location=None):
+        Statement.__init__(self, location)
+        self.glyphs, self.carets = (glyphs, carets)
+
+    def build(self, builder):
+        glyphs = self.glyphs.glyphSet()
+        builder.add_ligatureCaretByIndex_(self.location, glyphs, set(self.carets))
+
+    def asFea(self, indent=""):
+        return "LigatureCaretByIndex {} {};".format(
+            self.glyphs.asFea(), " ".join(str(x) for x in self.carets))
+
+
+class LigatureCaretByPosStatement(Statement):
+    def __init__(self, glyphs, carets, location=None):
+        Statement.__init__(self, location)
+        self.glyphs, self.carets = (glyphs, carets)
+
+    def build(self, builder):
+        glyphs = self.glyphs.glyphSet()
+        builder.add_ligatureCaretByPos_(self.location, glyphs, set(self.carets))
+
+    def asFea(self, indent=""):
+        return "LigatureCaretByPos {} {};".format(
+            self.glyphs.asFea(), " ".join(str(x) for x in self.carets))
+
+
+class LigatureSubstStatement(Statement):
+    def __init__(self, prefix, glyphs, suffix, replacement,
+                 forceChain, location=None):
+        Statement.__init__(self, location)
+        self.prefix, self.glyphs, self.suffix = (prefix, glyphs, suffix)
+        self.replacement, self.forceChain = replacement, forceChain
+
+    def build(self, builder):
+        prefix = [p.glyphSet() for p in self.prefix]
+        glyphs = [g.glyphSet() for g in self.glyphs]
+        suffix = [s.glyphSet() for s in self.suffix]
+        builder.add_ligature_subst(
+            self.location, prefix, glyphs, suffix, self.replacement,
+            self.forceChain)
+
+    def asFea(self, indent=""):
+        res = "sub "
+        if len(self.prefix) or len(self.suffix) or self.forceChain:
+            if len(self.prefix):
+                res += " ".join(g.asFea() for g in self.prefix) + " "
+            res += " ".join(g.asFea() + "'" for g in self.glyphs)
+            if len(self.suffix):
+                res += " " + " ".join(g.asFea() for g in self.suffix)
+        else:
+            res += " ".join(g.asFea() for g in self.glyphs)
+        res += " by "
+        res += asFea(self.replacement)
+        res += ";"
+        return res
+
+
+class LookupFlagStatement(Statement):
+    def __init__(self, value=0, markAttachment=None, markFilteringSet=None,
+                 location=None):
+        Statement.__init__(self, location)
+        self.value = value
+        self.markAttachment = markAttachment
+        self.markFilteringSet = markFilteringSet
+
+    def build(self, builder):
+        markAttach = None
+        if self.markAttachment is not None:
+            markAttach = self.markAttachment.glyphSet()
+        markFilter = None
+        if self.markFilteringSet is not None:
+            markFilter = self.markFilteringSet.glyphSet()
+        builder.set_lookup_flag(self.location, self.value,
+                                markAttach, markFilter)
+
+    def asFea(self, indent=""):
+        res = "lookupflag"
+        flags = ["RightToLeft", "IgnoreBaseGlyphs", "IgnoreLigatures", "IgnoreMarks"]
+        curr = 1
+        for i in range(len(flags)):
+            if self.value & curr != 0:
+                res += " " + flags[i]
+            curr = curr << 1
+        if self.markAttachment is not None:
+            res += " MarkAttachmentType {}".format(self.markAttachment.asFea())
+        if self.markFilteringSet is not None:
+            res += " UseMarkFilteringSet {}".format(self.markFilteringSet.asFea())
+        res += ";"
+        return res
+
+
+class LookupReferenceStatement(Statement):
+    def __init__(self, lookup, location=None):
+        Statement.__init__(self, location)
+        self.location, self.lookup = (location, lookup)
+
+    def build(self, builder):
+        builder.add_lookup_call(self.lookup.name)
+
+    def asFea(self, indent=""):
+        return "lookup {};".format(self.lookup.name)
+
+
+class MarkBasePosStatement(Statement):
+    def __init__(self, base, marks, location=None):
+        Statement.__init__(self, location)
+        self.base, self.marks = base, marks
+
+    def build(self, builder):
+        builder.add_mark_base_pos(self.location, self.base.glyphSet(), self.marks)
+
+    def asFea(self, indent=""):
+        res = "pos base {}".format(self.base.asFea())
+        for a, m in self.marks:
+            res += " {} mark @{}".format(a.asFea(), m.name)
+        res += ";"
+        return res
+
+
+class MarkLigPosStatement(Statement):
+    def __init__(self, ligatures, marks, location=None):
+        Statement.__init__(self, location)
+        self.ligatures, self.marks = ligatures, marks
+
+    def build(self, builder):
+        builder.add_mark_lig_pos(self.location, self.ligatures.glyphSet(), self.marks)
+
+    def asFea(self, indent=""):
+        res = "pos ligature {}".format(self.ligatures.asFea())
+        ligs = []
+        for l in self.marks:
+            temp = ""
+            if l is None or not len(l):
+                temp = " <anchor NULL>"
+            else:
+                for a, m in l:
+                    temp += " {} mark @{}".format(a.asFea(), m.name)
+            ligs.append(temp)
+        res += ("\n" + indent + SHIFT + "ligComponent").join(ligs)
+        res += ";"
+        return res
+
+
+class MarkMarkPosStatement(Statement):
+    def __init__(self, baseMarks, marks, location=None):
+        Statement.__init__(self, location)
+        self.baseMarks, self.marks = baseMarks, marks
+
+    def build(self, builder):
+        builder.add_mark_mark_pos(self.location, self.baseMarks.glyphSet(), self.marks)
+
+    def asFea(self, indent=""):
+        res = "pos mark {}".format(self.baseMarks.asFea())
+        for a, m in self.marks:
+            res += " {} mark @{}".format(a.asFea(), m.name)
+        res += ";"
+        return res
+
+
+class MultipleSubstStatement(Statement):
+    def __init__(self, prefix, glyph, suffix, replacement, location=None):
+        Statement.__init__(self, location)
+        self.prefix, self.glyph, self.suffix = prefix, glyph, suffix
+        self.replacement = replacement
+
+    def build(self, builder):
+        prefix = [p.glyphSet() for p in self.prefix]
+        suffix = [s.glyphSet() for s in self.suffix]
+        builder.add_multiple_subst(
+            self.location, prefix, self.glyph, suffix, self.replacement)
+
+    def asFea(self, indent=""):
+        res = "sub "
+        if len(self.prefix) or len(self.suffix):
+            if len(self.prefix):
+                res += " ".join(map(asFea, self.prefix)) + " "
+            res += asFea(self.glyph) + "'"
+            if len(self.suffix):
+                res += " " + " ".join(map(asFea, self.suffix))
+        else:
+            res += asFea(self.glyph)
+        res += " by "
+        res += " ".join(map(asFea, self.replacement))
+        res += ";"
+        return res
+
+
+class PairPosStatement(Statement):
+    def __init__(self, glyphs1, valuerecord1, glyphs2, valuerecord2,
+                 enumerated=False, location=None):
+        Statement.__init__(self, location)
+        self.enumerated = enumerated
+        self.glyphs1, self.valuerecord1 = glyphs1, valuerecord1
+        self.glyphs2, self.valuerecord2 = glyphs2, valuerecord2
+
+    def build(self, builder):
+        if self.enumerated:
+            g = [self.glyphs1.glyphSet(), self.glyphs2.glyphSet()]
+            for glyph1, glyph2 in itertools.product(*g):
+                builder.add_specific_pair_pos(
+                    self.location, glyph1, self.valuerecord1,
+                    glyph2, self.valuerecord2)
+            return
+
+        is_specific = (isinstance(self.glyphs1, GlyphName) and
+                       isinstance(self.glyphs2, GlyphName))
+        if is_specific:
+            builder.add_specific_pair_pos(
+                self.location, self.glyphs1.glyph, self.valuerecord1,
+                self.glyphs2.glyph, self.valuerecord2)
+        else:
+            builder.add_class_pair_pos(
+                self.location, self.glyphs1.glyphSet(), self.valuerecord1,
+                self.glyphs2.glyphSet(), self.valuerecord2)
+
+    def asFea(self, indent=""):
+        res = "enum " if self.enumerated else ""
+        if self.valuerecord2:
+            res += "pos {} {} {} {};".format(
+                self.glyphs1.asFea(), self.valuerecord1.makeString(),
+                self.glyphs2.asFea(), self.valuerecord2.makeString())
+        else:
+            res += "pos {} {} {};".format(
+                self.glyphs1.asFea(), self.glyphs2.asFea(),
+                self.valuerecord1.makeString())
+        return res
+
+
+class ReverseChainSingleSubstStatement(Statement):
+    def __init__(self, old_prefix, old_suffix, glyphs, replacements,
+                 location=None):
+        Statement.__init__(self, location)
+        self.old_prefix, self.old_suffix = old_prefix, old_suffix
+        self.glyphs = glyphs
+        self.replacements = replacements
+
+    def build(self, builder):
+        prefix = [p.glyphSet() for p in self.old_prefix]
+        suffix = [s.glyphSet() for s in self.old_suffix]
+        originals = self.glyphs[0].glyphSet()
+        replaces = self.replacements[0].glyphSet()
+        if len(replaces) == 1:
+            replaces = replaces * len(originals)
+        builder.add_reverse_chain_single_subst(
+            self.location, prefix, suffix, dict(zip(originals, replaces)))
+
+    def asFea(self, indent=""):
+        res = "rsub "
+        if len(self.old_prefix) or len(self.old_suffix):
+            if len(self.old_prefix):
+                res += " ".join(asFea(g) for g in self.old_prefix) + " "
+            res += " ".join(asFea(g) + "'" for g in self.glyphs)
+            if len(self.old_suffix):
+                res += " " + " ".join(asFea(g) for g in self.old_suffix)
+        else:
+            res += " ".join(map(asFea, self.glyphs))
+        res += " by {};".format(" ".join(asFea(g) for g in self.replacements))
+        return res
+
+
+class SingleSubstStatement(Statement):
+    def __init__(self, glyphs, replace, prefix, suffix, forceChain,
+                 location=None):
+        Statement.__init__(self, location)
+        self.prefix, self.suffix = prefix, suffix
+        self.forceChain = forceChain
+        self.glyphs = glyphs
+        self.replacements = replace
+
+    def build(self, builder):
+        prefix = [p.glyphSet() for p in self.prefix]
+        suffix = [s.glyphSet() for s in self.suffix]
+        originals = self.glyphs[0].glyphSet()
+        replaces = self.replacements[0].glyphSet()
+        if len(replaces) == 1:
+            replaces = replaces * len(originals)
+        builder.add_single_subst(self.location, prefix, suffix,
+                                 OrderedDict(zip(originals, replaces)),
+                                 self.forceChain)
+
+    def asFea(self, indent=""):
+        res = "sub "
+        if len(self.prefix) or len(self.suffix) or self.forceChain:
+            if len(self.prefix):
+                res += " ".join(asFea(g) for g in self.prefix) + " "
+            res += " ".join(asFea(g) + "'" for g in self.glyphs)
+            if len(self.suffix):
+                res += " " + " ".join(asFea(g) for g in self.suffix)
+        else:
+            res += " ".join(asFea(g) for g in self.glyphs)
+        res += " by {};".format(" ".join(asFea(g) for g in self.replacements))
+        return res
+
+
+class ScriptStatement(Statement):
+    def __init__(self, script, location=None):
+        Statement.__init__(self, location)
+        self.script = script
+
+    def build(self, builder):
+        builder.set_script(self.location, self.script)
+
+    def asFea(self, indent=""):
+        return "script {};".format(self.script.strip())
+
+
+class SinglePosStatement(Statement):
+    def __init__(self, pos, prefix, suffix, forceChain, location=None):
+        Statement.__init__(self, location)
+        self.pos, self.prefix, self.suffix = pos, prefix, suffix
+        self.forceChain = forceChain
+
+    def build(self, builder):
+        prefix = [p.glyphSet() for p in self.prefix]
+        suffix = [s.glyphSet() for s in self.suffix]
+        pos = [(g.glyphSet(), value) for g, value in self.pos]
+        builder.add_single_pos(self.location, prefix, suffix,
+                               pos, self.forceChain)
+
+    def asFea(self, indent=""):
+        res = "pos "
+        if len(self.prefix) or len(self.suffix) or self.forceChain:
+            if len(self.prefix):
+                res += " ".join(map(asFea, self.prefix)) + " "
+            res += " ".join([asFea(x[0]) + "'" + (
+                (" " + x[1].makeString()) if x[1] else "") for x in self.pos])
+            if len(self.suffix):
+                res += " " + " ".join(map(asFea, self.suffix))
+        else:
+            res += " ".join([asFea(x[0]) + " " +
+                             (x[1].makeString() if x[1] else "") for x in self.pos])
+        res += ";"
+        return res
+
+
+class SubtableStatement(Statement):
+    def __init__(self, location=None):
+        Statement.__init__(self, location)
+
+    def asFea(self, indent=""):
+        return indent + "subtable;"
+
+
+class ValueRecord(Expression):
+    def __init__(self, xPlacement=None, yPlacement=None,
+                 xAdvance=None, yAdvance=None,
+                 xPlaDevice=None, yPlaDevice=None,
+                 xAdvDevice=None, yAdvDevice=None,
+                 vertical=False, location=None):
+        Expression.__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)
+        self.vertical = vertical
+
+    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=None):
+        x, y = self.xPlacement, self.yPlacement
+        xAdvance, yAdvance = self.xAdvance, self.yAdvance
+        xPlaDevice, yPlaDevice = self.xPlaDevice, self.yPlaDevice
+        xAdvDevice, yAdvDevice = self.xAdvDevice, self.yAdvDevice
+        if vertical is None:
+            vertical = self.vertical
+
+        # Try format A, if possible.
+        if x is None and y is None:
+            if xAdvance is None and vertical:
+                return str(yAdvance)
+            elif yAdvance is None 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>" % (
+            x, y, xAdvance, yAdvance,
+            deviceToString(xPlaDevice), deviceToString(yPlaDevice),
+            deviceToString(xAdvDevice), deviceToString(yAdvDevice))
+
+
+class ValueRecordDefinition(Statement):
+    def __init__(self, name, value, location=None):
+        Statement.__init__(self, location)
+        self.name = name
+        self.value = value
+
+    def asFea(self, indent=""):
+        return "valueRecordDef {} {};".format(self.value.asFea(), self.name)
+
+
+def simplify_name_attributes(pid, eid, lid):
+    if pid == 3 and eid == 1 and lid == 1033:
+        return ""
+    elif pid == 1 and eid == 0 and lid == 0:
+        return "1"
+    else:
+        return "{} {} {}".format(pid, eid, lid)
+
+
+class NameRecord(Statement):
+    def __init__(self, nameID, platformID, platEncID, langID, string,
+                 location=None):
+        Statement.__init__(self, location)
+        self.nameID = nameID
+        self.platformID = platformID
+        self.platEncID = platEncID
+        self.langID = langID
+        self.string = string
+
+    def build(self, builder):
+        builder.add_name_record(
+            self.location, self.nameID, self.platformID,
+            self.platEncID, self.langID, self.string)
+
+    def asFea(self, indent=""):
+        def escape(c, escape_pattern):
+            # Also escape U+0022 QUOTATION MARK and U+005C REVERSE SOLIDUS
+            if c >= 0x20 and c <= 0x7E and c not in (0x22, 0x5C):
+                return unichr(c)
+            else:
+                return escape_pattern % c
+        encoding = getEncoding(self.platformID, self.platEncID, self.langID)
+        if encoding is None:
+            raise FeatureLibError("Unsupported encoding", self.location)
+        s = tobytes(self.string, encoding=encoding)
+        if encoding == "utf_16_be":
+            escaped_string = "".join([
+                escape(byteord(s[i]) * 256 + byteord(s[i + 1]), r"\%04x")
+                for i in range(0, len(s), 2)])
+        else:
+            escaped_string = "".join([escape(byteord(b), r"\%02x") for b in s])
+        plat = simplify_name_attributes(
+            self.platformID, self.platEncID, self.langID)
+        if plat != "":
+            plat += " "
+        return "nameid {} {}\"{}\";".format(self.nameID, plat, escaped_string)
+
+
+class FeatureNameStatement(NameRecord):
+    def build(self, builder):
+        NameRecord.build(self, builder)
+        builder.add_featureName(self.nameID)
+
+    def asFea(self, indent=""):
+        if self.nameID == "size":
+            tag = "sizemenuname"
+        else:
+            tag = "name"
+        plat = simplify_name_attributes(self.platformID, self.platEncID, self.langID)
+        if plat != "":
+            plat += " "
+        return "{} {}\"{}\";".format(tag, plat, self.string)
+
+
+class SizeParameters(Statement):
+    def __init__(self, DesignSize, SubfamilyID, RangeStart, RangeEnd,
+                 location=None):
+        Statement.__init__(self, location)
+        self.DesignSize = DesignSize
+        self.SubfamilyID = SubfamilyID
+        self.RangeStart = RangeStart
+        self.RangeEnd = RangeEnd
+
+    def build(self, builder):
+        builder.set_size_parameters(self.location, self.DesignSize,
+                                    self.SubfamilyID, self.RangeStart, self.RangeEnd)
+
+    def asFea(self, indent=""):
+        res = "parameters {:.1f} {}".format(self.DesignSize, self.SubfamilyID)
+        if self.RangeStart != 0 or self.RangeEnd != 0:
+            res += " {} {}".format(int(self.RangeStart * 10), int(self.RangeEnd * 10))
+        return res + ";"
+
+
+class CVParametersNameStatement(NameRecord):
+    def __init__(self, nameID, platformID, platEncID, langID, string,
+                 block_name, location=None):
+        NameRecord.__init__(self, nameID, platformID, platEncID, langID,
+                            string, location=location)
+        self.block_name = block_name
+
+    def build(self, builder):
+        item = ""
+        if self.block_name == "ParamUILabelNameID":
+            item = "_{}".format(builder.cv_num_named_params_.get(self.nameID, 0))
+        builder.add_cv_parameter(self.nameID)
+        self.nameID = (self.nameID, self.block_name + item)
+        NameRecord.build(self, builder)
+
+    def asFea(self, indent=""):
+        plat = simplify_name_attributes(self.platformID, self.platEncID,
+                                        self.langID)
+        if plat != "":
+            plat += " "
+        return "name {}\"{}\";".format(plat, self.string)
+
+
+class CharacterStatement(Statement):
+    """
+    Statement used in cvParameters blocks of Character Variant features (cvXX).
+    The Unicode value may be written with either decimal or hexadecimal
+    notation. The value must be preceded by '0x' if it is a hexadecimal value.
+    The largest Unicode value allowed is 0xFFFFFF.
+    """
+    def __init__(self, character, tag, location=None):
+        Statement.__init__(self, location)
+        self.character = character
+        self.tag = tag
+
+    def build(self, builder):
+        builder.add_cv_character(self.character, self.tag)
+
+    def asFea(self, indent=""):
+        return "Character {:#x};".format(self.character)
+
+
+class BaseAxis(Statement):
+    def __init__(self, bases, scripts, vertical, location=None):
+        Statement.__init__(self, location)
+        self.bases = bases
+        self.scripts = scripts
+        self.vertical = vertical
+
+    def build(self, builder):
+        builder.set_base_axis(self.bases, self.scripts, self.vertical)
+
+    def asFea(self, indent=""):
+        direction = "Vert" if self.vertical else "Horiz"
+        scripts = ["{} {} {}".format(a[0], a[1], " ".join(map(str, a[2]))) for a in self.scripts]
+        return "{}Axis.BaseTagList {};\n{}{}Axis.BaseScriptList {};".format(
+            direction, " ".join(self.bases), indent, direction, ", ".join(scripts))
+
+
+class OS2Field(Statement):
+    def __init__(self, key, value, location=None):
+        Statement.__init__(self, location)
+        self.key = key
+        self.value = value
+
+    def build(self, builder):
+        builder.add_os2_field(self.key, self.value)
+
+    def asFea(self, indent=""):
+        def intarr2str(x):
+            return " ".join(map(str, x))
+        numbers = ("FSType", "TypoAscender", "TypoDescender", "TypoLineGap",
+                   "winAscent", "winDescent", "XHeight", "CapHeight",
+                   "WeightClass", "WidthClass", "LowerOpSize", "UpperOpSize")
+        ranges = ("UnicodeRange", "CodePageRange")
+        keywords = dict([(x.lower(), [x, str]) for x in numbers])
+        keywords.update([(x.lower(), [x, intarr2str]) for x in ranges])
+        keywords["panose"] = ["Panose", intarr2str]
+        keywords["vendor"] = ["Vendor", lambda y: '"{}"'.format(y)]
+        if self.key in keywords:
+            return "{} {};".format(keywords[self.key][0], keywords[self.key][1](self.value))
+        return ""   # should raise exception
+
+
+class HheaField(Statement):
+    def __init__(self, key, value, location=None):
+        Statement.__init__(self, location)
+        self.key = key
+        self.value = value
+
+    def build(self, builder):
+        builder.add_hhea_field(self.key, self.value)
+
+    def asFea(self, indent=""):
+        fields = ("CaretOffset", "Ascender", "Descender", "LineGap")
+        keywords = dict([(x.lower(), x) for x in fields])
+        return "{} {};".format(keywords[self.key], self.value)
+
+
+class VheaField(Statement):
+    def __init__(self, key, value, location=None):
+        Statement.__init__(self, location)
+        self.key = key
+        self.value = value
+
+    def build(self, builder):
+        builder.add_vhea_field(self.key, self.value)
+
+    def asFea(self, indent=""):
+        fields = ("VertTypoAscender", "VertTypoDescender", "VertTypoLineGap")
+        keywords = dict([(x.lower(), x) for x in fields])
+        return "{} {};".format(keywords[self.key], self.value)
diff --git a/Lib/fontTools/feaLib/builder.py b/Lib/fontTools/feaLib/builder.py
new file mode 100644
index 0000000..447ded5
--- /dev/null
+++ b/Lib/fontTools/feaLib/builder.py
@@ -0,0 +1,1588 @@
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import binary2num, safeEval
+from fontTools.feaLib.error import FeatureLibError
+from fontTools.feaLib.parser import Parser
+from fontTools.feaLib.ast import FeatureFile
+from fontTools.otlLib import builder as otl
+from fontTools.ttLib import newTable, getTableModule
+from fontTools.ttLib.tables import otBase, otTables
+from collections import defaultdict
+import itertools
+import logging
+
+
+log = logging.getLogger(__name__)
+
+
+def addOpenTypeFeatures(font, featurefile, tables=None):
+    builder = Builder(font, featurefile)
+    builder.build(tables=tables)
+
+
+def addOpenTypeFeaturesFromString(font, features, filename=None, tables=None):
+    featurefile = UnicodeIO(tounicode(features))
+    if filename:
+        # the directory containing 'filename' is used as the root of relative
+        # include paths; if None is provided, the current directory is assumed
+        featurefile.name = filename
+    addOpenTypeFeatures(font, featurefile, tables=tables)
+
+
+class Builder(object):
+
+    supportedTables = frozenset(Tag(tag) for tag in [
+        "BASE",
+        "GDEF",
+        "GPOS",
+        "GSUB",
+        "OS/2",
+        "head",
+        "hhea",
+        "name",
+        "vhea",
+    ])
+
+    def __init__(self, font, featurefile):
+        self.font = font
+        # 'featurefile' can be either a path or file object (in which case we
+        # parse it into an AST), or a pre-parsed AST instance
+        if isinstance(featurefile, FeatureFile):
+            self.parseTree, self.file = featurefile, None
+        else:
+            self.parseTree, self.file = None, featurefile
+        self.glyphMap = font.getReverseGlyphMap()
+        self.default_language_systems_ = set()
+        self.script_ = None
+        self.lookupflag_ = 0
+        self.lookupflag_markFilterSet_ = None
+        self.language_systems = set()
+        self.named_lookups_ = {}
+        self.cur_lookup_ = None
+        self.cur_lookup_name_ = None
+        self.cur_feature_name_ = None
+        self.lookups_ = []
+        self.features_ = {}  # ('latn', 'DEU ', 'smcp') --> [LookupBuilder*]
+        self.required_features_ = {}  # ('latn', 'DEU ') --> 'scmp'
+        # for feature 'aalt'
+        self.aalt_features_ = []  # [(location, featureName)*], for 'aalt'
+        self.aalt_location_ = None
+        self.aalt_alternates_ = {}
+        # for 'featureNames'
+        self.featureNames_ = set()
+        self.featureNames_ids_ = {}
+        # for 'cvParameters'
+        self.cv_parameters_ = set()
+        self.cv_parameters_ids_ = {}
+        self.cv_num_named_params_ = {}
+        self.cv_characters_ = defaultdict(list)
+        # for feature 'size'
+        self.size_parameters_ = None
+        # for table 'head'
+        self.fontRevision_ = None  # 2.71
+        # for table 'name'
+        self.names_ = []
+        # for table 'BASE'
+        self.base_horiz_axis_ = None
+        self.base_vert_axis_ = None
+        # for table 'GDEF'
+        self.attachPoints_ = {}  # "a" --> {3, 7}
+        self.ligCaretCoords_ = {}  # "f_f_i" --> {300, 600}
+        self.ligCaretPoints_ = {}  # "f_f_i" --> {3, 7}
+        self.glyphClassDefs_ = {}  # "fi" --> (2, (file, line, column))
+        self.markAttach_ = {}  # "acute" --> (4, (file, line, column))
+        self.markAttachClassID_ = {}  # frozenset({"acute", "grave"}) --> 4
+        self.markFilterSets_ = {}  # frozenset({"acute", "grave"}) --> 4
+        # for table 'OS/2'
+        self.os2_ = {}
+        # for table 'hhea'
+        self.hhea_ = {}
+        # for table 'vhea'
+        self.vhea_ = {}
+
+    def build(self, tables=None):
+        if self.parseTree is None:
+            self.parseTree = Parser(self.file, self.glyphMap).parse()
+        self.parseTree.build(self)
+        # by default, build all the supported tables
+        if tables is None:
+            tables = self.supportedTables
+        else:
+            tables = frozenset(tables)
+            unsupported = tables - self.supportedTables
+            assert not unsupported, unsupported
+        if "GSUB" in tables:
+            self.build_feature_aalt_()
+        if "head" in tables:
+            self.build_head()
+        if "hhea" in tables:
+            self.build_hhea()
+        if "vhea" in tables:
+            self.build_vhea()
+        if "name" in tables:
+            self.build_name()
+        if "OS/2" in tables:
+            self.build_OS_2()
+        for tag in ('GPOS', 'GSUB'):
+            if tag not in tables:
+                continue
+            table = self.makeTable(tag)
+            if (table.ScriptList.ScriptCount > 0 or
+                    table.FeatureList.FeatureCount > 0 or
+                    table.LookupList.LookupCount > 0):
+                fontTable = self.font[tag] = newTable(tag)
+                fontTable.table = table
+            elif tag in self.font:
+                del self.font[tag]
+        if "GDEF" in tables:
+            gdef = self.buildGDEF()
+            if gdef:
+                self.font["GDEF"] = gdef
+            elif "GDEF" in self.font:
+                del self.font["GDEF"]
+        if "BASE" in tables:
+            base = self.buildBASE()
+            if base:
+                self.font["BASE"] = base
+            elif "BASE" in self.font:
+                del self.font["BASE"]
+
+    def get_chained_lookup_(self, location, builder_class):
+        result = builder_class(self.font, location)
+        result.lookupflag = self.lookupflag_
+        result.markFilterSet = self.lookupflag_markFilterSet_
+        self.lookups_.append(result)
+        return result
+
+    def add_lookup_to_feature_(self, lookup, feature_name):
+        for script, lang in self.language_systems:
+            key = (script, lang, feature_name)
+            self.features_.setdefault(key, []).append(lookup)
+
+    def get_lookup_(self, location, builder_class):
+        if (self.cur_lookup_ and
+            type(self.cur_lookup_) == builder_class and
+            self.cur_lookup_.lookupflag == self.lookupflag_ and
+            self.cur_lookup_.markFilterSet ==
+                self.lookupflag_markFilterSet_):
+            return self.cur_lookup_
+        if self.cur_lookup_name_ and self.cur_lookup_:
+            raise FeatureLibError(
+                "Within a named lookup block, all rules must be of "
+                "the same lookup type and flag", location)
+        self.cur_lookup_ = builder_class(self.font, location)
+        self.cur_lookup_.lookupflag = self.lookupflag_
+        self.cur_lookup_.markFilterSet = self.lookupflag_markFilterSet_
+        self.lookups_.append(self.cur_lookup_)
+        if self.cur_lookup_name_:
+            # We are starting a lookup rule inside a named lookup block.
+            self.named_lookups_[self.cur_lookup_name_] = self.cur_lookup_
+        if self.cur_feature_name_:
+            # We are starting a lookup rule inside a feature. This includes
+            # lookup rules inside named lookups inside features.
+            self.add_lookup_to_feature_(self.cur_lookup_,
+                                        self.cur_feature_name_)
+        return self.cur_lookup_
+
+    def build_feature_aalt_(self):
+        if not self.aalt_features_ and not self.aalt_alternates_:
+            return
+        alternates = {g: set(a) for g, a in self.aalt_alternates_.items()}
+        for location, name in self.aalt_features_ + [(None, "aalt")]:
+            feature = [(script, lang, feature, lookups)
+                       for (script, lang, feature), lookups
+                       in self.features_.items()
+                       if feature == name]
+            # "aalt" does not have to specify its own lookups, but it might.
+            if not feature and name != "aalt":
+                raise FeatureLibError("Feature %s has not been defined" % name,
+                                      location)
+            for script, lang, feature, lookups in feature:
+                for lookup in lookups:
+                    for glyph, alts in lookup.getAlternateGlyphs().items():
+                        alternates.setdefault(glyph, set()).update(alts)
+        single = {glyph: list(repl)[0] for glyph, repl in alternates.items()
+                  if len(repl) == 1}
+        # TODO: Figure out the glyph alternate ordering used by makeotf.
+        # https://github.com/fonttools/fonttools/issues/836
+        multi = {glyph: sorted(repl, key=self.font.getGlyphID)
+                 for glyph, repl in alternates.items()
+                 if len(repl) > 1}
+        if not single and not multi:
+            return
+        self.features_ = {(script, lang, feature): lookups
+                          for (script, lang, feature), lookups
+                          in self.features_.items()
+                          if feature != "aalt"}
+        old_lookups = self.lookups_
+        self.lookups_ = []
+        self.start_feature(self.aalt_location_, "aalt")
+        if single:
+            single_lookup = self.get_lookup_(location, SingleSubstBuilder)
+            single_lookup.mapping = single
+        if multi:
+            multi_lookup = self.get_lookup_(location, AlternateSubstBuilder)
+            multi_lookup.alternates = multi
+        self.end_feature()
+        self.lookups_.extend(old_lookups)
+
+    def build_head(self):
+        if not self.fontRevision_:
+            return
+        table = self.font.get("head")
+        if not table:  # this only happens for unit tests
+            table = self.font["head"] = newTable("head")
+            table.decompile(b"\0" * 54, self.font)
+            table.tableVersion = 1.0
+            table.created = table.modified = 3406620153  # 2011-12-13 11:22:33
+        table.fontRevision = self.fontRevision_
+
+    def build_hhea(self):
+        if not self.hhea_:
+            return
+        table = self.font.get("hhea")
+        if not table:  # this only happens for unit tests
+            table = self.font["hhea"] = newTable("hhea")
+            table.decompile(b"\0" * 36, self.font)
+            table.tableVersion = 0x00010000
+        if "caretoffset" in self.hhea_:
+            table.caretOffset = self.hhea_["caretoffset"]
+        if "ascender" in self.hhea_:
+            table.ascent = self.hhea_["ascender"]
+        if "descender" in self.hhea_:
+            table.descent = self.hhea_["descender"]
+        if "linegap" in self.hhea_:
+            table.lineGap = self.hhea_["linegap"]
+
+    def build_vhea(self):
+        if not self.vhea_:
+            return
+        table = self.font.get("vhea")
+        if not table:  # this only happens for unit tests
+            table = self.font["vhea"] = newTable("vhea")
+            table.decompile(b"\0" * 36, self.font)
+            table.tableVersion = 0x00011000
+        if "verttypoascender" in self.vhea_:
+            table.ascent = self.vhea_["verttypoascender"]
+        if "verttypodescender" in self.vhea_:
+            table.descent = self.vhea_["verttypodescender"]
+        if "verttypolinegap" in self.vhea_:
+            table.lineGap = self.vhea_["verttypolinegap"]
+
+    def get_user_name_id(self, table):
+        # Try to find first unused font-specific name id
+        nameIDs = [name.nameID for name in table.names]
+        for user_name_id in range(256, 32767):
+            if user_name_id not in nameIDs:
+                return user_name_id
+
+    def buildFeatureParams(self, tag):
+        params = None
+        if tag == "size":
+            params = otTables.FeatureParamsSize()
+            params.DesignSize, params.SubfamilyID, params.RangeStart, \
+                    params.RangeEnd = self.size_parameters_
+            if tag in self.featureNames_ids_:
+                params.SubfamilyNameID = self.featureNames_ids_[tag]
+            else:
+                params.SubfamilyNameID = 0
+        elif tag in self.featureNames_:
+            assert tag in self.featureNames_ids_
+            params = otTables.FeatureParamsStylisticSet()
+            params.Version = 0
+            params.UINameID = self.featureNames_ids_[tag]
+        elif tag in self.cv_parameters_:
+            params = otTables.FeatureParamsCharacterVariants()
+            params.Format = 0
+            params.FeatUILabelNameID = self.cv_parameters_ids_.get(
+                (tag, 'FeatUILabelNameID'), 0)
+            params.FeatUITooltipTextNameID = self.cv_parameters_ids_.get(
+                (tag, 'FeatUITooltipTextNameID'), 0)
+            params.SampleTextNameID = self.cv_parameters_ids_.get(
+                (tag, 'SampleTextNameID'), 0)
+            params.NumNamedParameters = self.cv_num_named_params_.get(tag, 0)
+            params.FirstParamUILabelNameID = self.cv_parameters_ids_.get(
+                (tag, 'ParamUILabelNameID_0'), 0)
+            params.CharCount = len(self.cv_characters_[tag])
+            params.Character = self.cv_characters_[tag]
+        return params
+
+    def build_name(self):
+        if not self.names_:
+            return
+        table = self.font.get("name")
+        if not table:  # this only happens for unit tests
+            table = self.font["name"] = newTable("name")
+            table.names = []
+        for name in self.names_:
+            nameID, platformID, platEncID, langID, string = name
+            # For featureNames block, nameID is 'feature tag'
+            # For cvParameters blocks, nameID is ('feature tag', 'block name')
+            if not isinstance(nameID, int):
+                tag = nameID
+                if tag in self.featureNames_:
+                    if tag not in self.featureNames_ids_:
+                        self.featureNames_ids_[tag] = self.get_user_name_id(table)
+                        assert self.featureNames_ids_[tag] is not None
+                    nameID = self.featureNames_ids_[tag]
+                elif tag[0] in self.cv_parameters_:
+                    if tag not in self.cv_parameters_ids_:
+                        self.cv_parameters_ids_[tag] = self.get_user_name_id(table)
+                        assert self.cv_parameters_ids_[tag] is not None
+                    nameID = self.cv_parameters_ids_[tag]
+            table.setName(string, nameID, platformID, platEncID, langID)
+
+    def build_OS_2(self):
+        if not self.os2_:
+            return
+        table = self.font.get("OS/2")
+        if not table:  # this only happens for unit tests
+            table = self.font["OS/2"] = newTable("OS/2")
+            data = b"\0" * sstruct.calcsize(getTableModule("OS/2").OS2_format_0)
+            table.decompile(data, self.font)
+        version = 0
+        if "fstype" in self.os2_:
+            table.fsType = self.os2_["fstype"]
+        if "panose" in self.os2_:
+            panose = getTableModule("OS/2").Panose()
+            panose.bFamilyType, panose.bSerifStyle, panose.bWeight,\
+                panose.bProportion, panose.bContrast, panose.bStrokeVariation,\
+                panose.bArmStyle, panose.bLetterForm, panose.bMidline, \
+                panose.bXHeight = self.os2_["panose"]
+            table.panose = panose
+        if "typoascender" in self.os2_:
+            table.sTypoAscender = self.os2_["typoascender"]
+        if "typodescender" in self.os2_:
+            table.sTypoDescender = self.os2_["typodescender"]
+        if "typolinegap" in self.os2_:
+            table.sTypoLineGap = self.os2_["typolinegap"]
+        if "winascent" in self.os2_:
+            table.usWinAscent = self.os2_["winascent"]
+        if "windescent" in self.os2_:
+            table.usWinDescent = self.os2_["windescent"]
+        if "vendor" in self.os2_:
+            table.achVendID = safeEval("'''" + self.os2_["vendor"] + "'''")
+        if "weightclass" in self.os2_:
+            table.usWeightClass = self.os2_["weightclass"]
+        if "widthclass" in self.os2_:
+            table.usWidthClass = self.os2_["widthclass"]
+        if "unicoderange" in self.os2_:
+            table.setUnicodeRanges(self.os2_["unicoderange"])
+        if "codepagerange" in self.os2_:
+            pages = self.build_codepages_(self.os2_["codepagerange"])
+            table.ulCodePageRange1, table.ulCodePageRange2 = pages
+            version = 1
+        if "xheight" in self.os2_:
+            table.sxHeight = self.os2_["xheight"]
+            version = 2
+        if "capheight" in self.os2_:
+            table.sCapHeight = self.os2_["capheight"]
+            version = 2
+        if "loweropsize" in self.os2_:
+            table.usLowerOpticalPointSize = self.os2_["loweropsize"]
+            version = 5
+        if "upperopsize" in self.os2_:
+            table.usUpperOpticalPointSize = self.os2_["upperopsize"]
+            version = 5
+        def checkattr(table, attrs):
+            for attr in attrs:
+                if not hasattr(table, attr):
+                    setattr(table, attr, 0)
+        table.version = max(version, table.version)
+        # this only happens for unit tests
+        if version >= 1:
+            checkattr(table, ("ulCodePageRange1", "ulCodePageRange2"))
+        if version >= 2:
+            checkattr(table, ("sxHeight", "sCapHeight", "usDefaultChar",
+                              "usBreakChar", "usMaxContext"))
+        if version >= 5:
+            checkattr(table, ("usLowerOpticalPointSize",
+                              "usUpperOpticalPointSize"))
+
+    def build_codepages_(self, pages):
+        pages2bits = {
+            1252: 0,  1250: 1, 1251: 2, 1253: 3, 1254: 4, 1255: 5, 1256: 6,
+            1257: 7,  1258: 8, 874: 16, 932: 17, 936: 18, 949: 19, 950: 20,
+            1361: 21, 869: 48, 866: 49, 865: 50, 864: 51, 863: 52, 862: 53,
+            861:  54, 860: 55, 857: 56, 855: 57, 852: 58, 775: 59, 737: 60,
+            708:  61, 850: 62, 437: 63,
+        }
+        bits = [pages2bits[p] for p in pages if p in pages2bits]
+        pages = []
+        for i in range(2):
+            pages.append("")
+            for j in range(i * 32, (i + 1) * 32):
+                if j in bits:
+                    pages[i] += "1"
+                else:
+                    pages[i] += "0"
+        return [binary2num(p[::-1]) for p in pages]
+
+    def buildBASE(self):
+        if not self.base_horiz_axis_ and not self.base_vert_axis_:
+            return None
+        base = otTables.BASE()
+        base.Version = 0x00010000
+        base.HorizAxis = self.buildBASEAxis(self.base_horiz_axis_)
+        base.VertAxis = self.buildBASEAxis(self.base_vert_axis_)
+
+        result = newTable("BASE")
+        result.table = base
+        return result
+
+    def buildBASEAxis(self, axis):
+        if not axis:
+            return
+        bases, scripts = axis
+        axis = otTables.Axis()
+        axis.BaseTagList = otTables.BaseTagList()
+        axis.BaseTagList.BaselineTag = bases
+        axis.BaseTagList.BaseTagCount = len(bases)
+        axis.BaseScriptList = otTables.BaseScriptList()
+        axis.BaseScriptList.BaseScriptRecord = []
+        axis.BaseScriptList.BaseScriptCount = len(scripts)
+        for script in sorted(scripts):
+            record = otTables.BaseScriptRecord()
+            record.BaseScriptTag = script[0]
+            record.BaseScript = otTables.BaseScript()
+            record.BaseScript.BaseLangSysCount = 0
+            record.BaseScript.BaseValues = otTables.BaseValues()
+            record.BaseScript.BaseValues.DefaultIndex = bases.index(script[1])
+            record.BaseScript.BaseValues.BaseCoord = []
+            record.BaseScript.BaseValues.BaseCoordCount = len(script[2])
+            for c in script[2]:
+                coord = otTables.BaseCoord()
+                coord.Format = 1
+                coord.Coordinate = c
+                record.BaseScript.BaseValues.BaseCoord.append(coord)
+            axis.BaseScriptList.BaseScriptRecord.append(record)
+        return axis
+
+    def buildGDEF(self):
+        gdef = otTables.GDEF()
+        gdef.GlyphClassDef = self.buildGDEFGlyphClassDef_()
+        gdef.AttachList = \
+            otl.buildAttachList(self.attachPoints_, self.glyphMap)
+        gdef.LigCaretList = \
+            otl.buildLigCaretList(self.ligCaretCoords_, self.ligCaretPoints_,
+                                  self.glyphMap)
+        gdef.MarkAttachClassDef = self.buildGDEFMarkAttachClassDef_()
+        gdef.MarkGlyphSetsDef = self.buildGDEFMarkGlyphSetsDef_()
+        gdef.Version = 0x00010002 if gdef.MarkGlyphSetsDef else 0x00010000
+        if any((gdef.GlyphClassDef, gdef.AttachList, gdef.LigCaretList,
+                gdef.MarkAttachClassDef, gdef.MarkGlyphSetsDef)):
+            result = newTable("GDEF")
+            result.table = gdef
+            return result
+        else:
+            return None
+
+    def buildGDEFGlyphClassDef_(self):
+        if self.glyphClassDefs_:
+            classes = {g: c for (g, (c, _)) in self.glyphClassDefs_.items()}
+        else:
+            classes = {}
+            for lookup in self.lookups_:
+                classes.update(lookup.inferGlyphClasses())
+            for markClass in self.parseTree.markClasses.values():
+                for markClassDef in markClass.definitions:
+                    for glyph in markClassDef.glyphSet():
+                        classes[glyph] = 3
+        if classes:
+            result = otTables.GlyphClassDef()
+            result.classDefs = classes
+            return result
+        else:
+            return None
+
+    def buildGDEFMarkAttachClassDef_(self):
+        classDefs = {g: c for g, (c, _) in self.markAttach_.items()}
+        if not classDefs:
+            return None
+        result = otTables.MarkAttachClassDef()
+        result.classDefs = classDefs
+        return result
+
+    def buildGDEFMarkGlyphSetsDef_(self):
+        sets = []
+        for glyphs, id_ in sorted(self.markFilterSets_.items(),
+                                 key=lambda item: item[1]):
+            sets.append(glyphs)
+        return otl.buildMarkGlyphSetsDef(sets, self.glyphMap)
+
+    def buildLookups_(self, tag):
+        assert tag in ('GPOS', 'GSUB'), tag
+        for lookup in self.lookups_:
+            lookup.lookup_index = None
+        lookups = []
+        for lookup in self.lookups_:
+            if lookup.table != tag:
+                continue
+            lookup.lookup_index = len(lookups)
+            lookups.append(lookup)
+        return [l.build() for l in lookups]
+
+    def makeTable(self, tag):
+        table = getattr(otTables, tag, None)()
+        table.Version = 0x00010000
+        table.ScriptList = otTables.ScriptList()
+        table.ScriptList.ScriptRecord = []
+        table.FeatureList = otTables.FeatureList()
+        table.FeatureList.FeatureRecord = []
+        table.LookupList = otTables.LookupList()
+        table.LookupList.Lookup = self.buildLookups_(tag)
+
+        # Build a table for mapping (tag, lookup_indices) to feature_index.
+        # For example, ('liga', (2,3,7)) --> 23.
+        feature_indices = {}
+        required_feature_indices = {}  # ('latn', 'DEU') --> 23
+        scripts = {}  # 'latn' --> {'DEU': [23, 24]} for feature #23,24
+        # Sort the feature table by feature tag:
+        # https://github.com/behdad/fonttools/issues/568
+        sortFeatureTag = lambda f: (f[0][2], f[0][1], f[0][0], f[1])
+        for key, lookups in sorted(self.features_.items(), key=sortFeatureTag):
+            script, lang, feature_tag = key
+            # l.lookup_index will be None when a lookup is not needed
+            # for the table under construction. For example, substitution
+            # rules will have no lookup_index while building GPOS tables.
+            lookup_indices = tuple([l.lookup_index for l in lookups
+                                    if l.lookup_index is not None])
+
+            size_feature = (tag == "GPOS" and feature_tag == "size")
+            if len(lookup_indices) == 0 and not size_feature:
+                continue
+
+            feature_key = (feature_tag, lookup_indices)
+            feature_index = feature_indices.get(feature_key)
+            if feature_index is None:
+                feature_index = len(table.FeatureList.FeatureRecord)
+                frec = otTables.FeatureRecord()
+                frec.FeatureTag = feature_tag
+                frec.Feature = otTables.Feature()
+                frec.Feature.FeatureParams = self.buildFeatureParams(
+                                                feature_tag)
+                frec.Feature.LookupListIndex = list(lookup_indices)
+                frec.Feature.LookupCount = len(lookup_indices)
+                table.FeatureList.FeatureRecord.append(frec)
+                feature_indices[feature_key] = feature_index
+            scripts.setdefault(script, {}).setdefault(lang, []).append(
+                feature_index)
+            if self.required_features_.get((script, lang)) == feature_tag:
+                required_feature_indices[(script, lang)] = feature_index
+
+        # Build ScriptList.
+        for script, lang_features in sorted(scripts.items()):
+            srec = otTables.ScriptRecord()
+            srec.ScriptTag = script
+            srec.Script = otTables.Script()
+            srec.Script.DefaultLangSys = None
+            srec.Script.LangSysRecord = []
+            for lang, feature_indices in sorted(lang_features.items()):
+                langrec = otTables.LangSysRecord()
+                langrec.LangSys = otTables.LangSys()
+                langrec.LangSys.LookupOrder = None
+
+                req_feature_index = \
+                    required_feature_indices.get((script, lang))
+                if req_feature_index is None:
+                    langrec.LangSys.ReqFeatureIndex = 0xFFFF
+                else:
+                    langrec.LangSys.ReqFeatureIndex = req_feature_index
+
+                langrec.LangSys.FeatureIndex = [i for i in feature_indices
+                                                if i != req_feature_index]
+                langrec.LangSys.FeatureCount = \
+                    len(langrec.LangSys.FeatureIndex)
+
+                if lang == "dflt":
+                    srec.Script.DefaultLangSys = langrec.LangSys
+                else:
+                    langrec.LangSysTag = lang
+                    srec.Script.LangSysRecord.append(langrec)
+            srec.Script.LangSysCount = len(srec.Script.LangSysRecord)
+            table.ScriptList.ScriptRecord.append(srec)
+
+        table.ScriptList.ScriptCount = len(table.ScriptList.ScriptRecord)
+        table.FeatureList.FeatureCount = len(table.FeatureList.FeatureRecord)
+        table.LookupList.LookupCount = len(table.LookupList.Lookup)
+        return table
+
+    def add_language_system(self, location, script, language):
+        # OpenType Feature File Specification, section 4.b.i
+        if (script == "DFLT" and language == "dflt" and
+                self.default_language_systems_):
+            raise FeatureLibError(
+                'If "languagesystem DFLT dflt" is present, it must be '
+                'the first of the languagesystem statements', location)
+        if (script, language) in self.default_language_systems_:
+            raise FeatureLibError(
+                '"languagesystem %s %s" has already been specified' %
+                (script.strip(), language.strip()), location)
+        self.default_language_systems_.add((script, language))
+
+    def get_default_language_systems_(self):
+        # OpenType Feature File specification, 4.b.i. languagesystem:
+        # If no "languagesystem" statement is present, then the
+        # implementation must behave exactly as though the following
+        # statement were present at the beginning of the feature file:
+        # languagesystem DFLT dflt;
+        if self.default_language_systems_:
+            return frozenset(self.default_language_systems_)
+        else:
+            return frozenset({('DFLT', 'dflt')})
+
+    def start_feature(self, location, name):
+        self.language_systems = self.get_default_language_systems_()
+        self.script_ = 'DFLT'
+        self.cur_lookup_ = None
+        self.cur_feature_name_ = name
+        self.lookupflag_ = 0
+        self.lookupflag_markFilterSet_ = None
+        if name == "aalt":
+            self.aalt_location_ = location
+
+    def end_feature(self):
+        assert self.cur_feature_name_ is not None
+        self.cur_feature_name_ = None
+        self.language_systems = None
+        self.cur_lookup_ = None
+        self.lookupflag_ = 0
+        self.lookupflag_markFilterSet_ = None
+
+    def start_lookup_block(self, location, name):
+        if name in self.named_lookups_:
+            raise FeatureLibError(
+                'Lookup "%s" has already been defined' % name, location)
+        if self.cur_feature_name_ == "aalt":
+            raise FeatureLibError(
+                "Lookup blocks cannot be placed inside 'aalt' features; "
+                "move it out, and then refer to it with a lookup statement",
+                location)
+        self.cur_lookup_name_ = name
+        self.named_lookups_[name] = None
+        self.cur_lookup_ = None
+        self.lookupflag_ = 0
+        self.lookupflag_markFilterSet_ = None
+
+    def end_lookup_block(self):
+        assert self.cur_lookup_name_ is not None
+        self.cur_lookup_name_ = None
+        self.cur_lookup_ = None
+        self.lookupflag_ = 0
+        self.lookupflag_markFilterSet_ = None
+
+    def add_lookup_call(self, lookup_name):
+        assert lookup_name in self.named_lookups_, lookup_name
+        self.cur_lookup_ = None
+        lookup = self.named_lookups_[lookup_name]
+        self.add_lookup_to_feature_(lookup, self.cur_feature_name_)
+
+    def set_font_revision(self, location, revision):
+        self.fontRevision_ = revision
+
+    def set_language(self, location, language, include_default, required):
+        assert(len(language) == 4)
+        if self.cur_feature_name_ in ('aalt', 'size'):
+            raise FeatureLibError(
+                "Language statements are not allowed "
+                "within \"feature %s\"" % self.cur_feature_name_, location)
+        if language != 'dflt' and self.script_ == 'DFLT':
+            raise FeatureLibError("Need non-DFLT script when using non-dflt "
+                                  "language (was: \"%s\")" % language, location)
+        self.cur_lookup_ = None
+
+        key = (self.script_, language, self.cur_feature_name_)
+        if not include_default:
+            # don't include any lookups added by script DFLT in this feature
+            self.features_[key] = []
+        elif language != 'dflt':
+            # add rules defined between script statement and its first following
+            # language statement to each of its explicitly specified languages:
+            # http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html#4.b.ii
+            lookups = self.features_.get((key[0], 'dflt', key[2]))
+            dflt_lookups = self.features_.get(('DFLT', 'dflt', key[2]), [])
+            if lookups:
+                if key[:2] in self.get_default_language_systems_():
+                    lookups = [l for l in lookups if l not in dflt_lookups]
+                self.features_.setdefault(key, []).extend(lookups)
+        if self.script_ == 'DFLT':
+            langsys = set(self.get_default_language_systems_())
+        else:
+            langsys = set()
+        langsys.add((self.script_, language))
+        self.language_systems = frozenset(langsys)
+
+        if required:
+            key = (self.script_, language)
+            if key in self.required_features_:
+                raise FeatureLibError(
+                    "Language %s (script %s) has already "
+                    "specified feature %s as its required feature" % (
+                        language.strip(), self.script_.strip(),
+                        self.required_features_[key].strip()),
+                    location)
+            self.required_features_[key] = self.cur_feature_name_
+
+    def getMarkAttachClass_(self, location, glyphs):
+        glyphs = frozenset(glyphs)
+        id_ = self.markAttachClassID_.get(glyphs)
+        if id_ is not None:
+            return id_
+        id_ = len(self.markAttachClassID_) + 1
+        self.markAttachClassID_[glyphs] = id_
+        for glyph in glyphs:
+            if glyph in self.markAttach_:
+                _, loc = self.markAttach_[glyph]
+                raise FeatureLibError(
+                    "Glyph %s already has been assigned "
+                    "a MarkAttachmentType at %s:%d:%d" % (
+                        glyph, loc[0], loc[1], loc[2]),
+                    location)
+            self.markAttach_[glyph] = (id_, location)
+        return id_
+
+    def getMarkFilterSet_(self, location, glyphs):
+        glyphs = frozenset(glyphs)
+        id_ = self.markFilterSets_.get(glyphs)
+        if id_ is not None:
+            return id_
+        id_ = len(self.markFilterSets_)
+        self.markFilterSets_[glyphs] = id_
+        return id_
+
+    def set_lookup_flag(self, location, value, markAttach, markFilter):
+        value = value & 0xFF
+        if markAttach:
+            markAttachClass = self.getMarkAttachClass_(location, markAttach)
+            value = value | (markAttachClass << 8)
+        if markFilter:
+            markFilterSet = self.getMarkFilterSet_(location, markFilter)
+            value = value | 0x10
+            self.lookupflag_markFilterSet_ = markFilterSet
+        else:
+            self.lookupflag_markFilterSet_ = None
+        self.lookupflag_ = value
+
+    def set_script(self, location, script):
+        if self.cur_feature_name_ in ('aalt', 'size'):
+            raise FeatureLibError(
+                "Script statements are not allowed "
+                "within \"feature %s\"" % self.cur_feature_name_, location)
+        self.cur_lookup_ = None
+        self.script_ = script
+        self.lookupflag_ = 0
+        self.lookupflag_markFilterSet_ = None
+        self.set_language(location, "dflt",
+                          include_default=True, required=False)
+
+    def find_lookup_builders_(self, lookups):
+        """Helper for building chain contextual substitutions
+
+        Given a list of lookup names, finds the LookupBuilder for each name.
+        If an input name is None, it gets mapped to a None LookupBuilder.
+        """
+        lookup_builders = []
+        for lookup in lookups:
+            if lookup is not None:
+                lookup_builders.append(self.named_lookups_.get(lookup.name))
+            else:
+                lookup_builders.append(None)
+        return lookup_builders
+
+    def add_attach_points(self, location, glyphs, contourPoints):
+        for glyph in glyphs:
+            self.attachPoints_.setdefault(glyph, set()).update(contourPoints)
+
+    def add_chain_context_pos(self, location, prefix, glyphs, suffix, lookups):
+        lookup = self.get_lookup_(location, ChainContextPosBuilder)
+        lookup.rules.append((prefix, glyphs, suffix,
+                            self.find_lookup_builders_(lookups)))
+
+    def add_chain_context_subst(self, location,
+                                prefix, glyphs, suffix, lookups):
+        lookup = self.get_lookup_(location, ChainContextSubstBuilder)
+        lookup.substitutions.append((prefix, glyphs, suffix,
+                                     self.find_lookup_builders_(lookups)))
+
+    def add_alternate_subst(self, location,
+                            prefix, glyph, suffix, replacement):
+        if self.cur_feature_name_ == "aalt":
+            alts = self.aalt_alternates_.setdefault(glyph, set())
+            alts.update(replacement)
+            return
+        if prefix or suffix:
+            chain = self.get_lookup_(location, ChainContextSubstBuilder)
+            lookup = self.get_chained_lookup_(location, AlternateSubstBuilder)
+            chain.substitutions.append((prefix, [glyph], suffix, [lookup]))
+        else:
+            lookup = self.get_lookup_(location, AlternateSubstBuilder)
+        if glyph in lookup.alternates:
+            raise FeatureLibError(
+                'Already defined alternates for glyph "%s"' % glyph,
+                location)
+        lookup.alternates[glyph] = replacement
+
+    def add_feature_reference(self, location, featureName):
+        if self.cur_feature_name_ != "aalt":
+            raise FeatureLibError(
+                'Feature references are only allowed inside "feature aalt"',
+                location)
+        self.aalt_features_.append((location, featureName))
+
+    def add_featureName(self, tag):
+        self.featureNames_.add(tag)
+
+    def add_cv_parameter(self, tag):
+        self.cv_parameters_.add(tag)
+
+    def add_to_cv_num_named_params(self, tag):
+        """Adds new items to self.cv_num_named_params_
+        or increments the count of existing items."""
+        if tag in self.cv_num_named_params_:
+            self.cv_num_named_params_[tag] += 1
+        else:
+            self.cv_num_named_params_[tag] = 1
+
+    def add_cv_character(self, character, tag):
+        self.cv_characters_[tag].append(character)
+
+    def set_base_axis(self, bases, scripts, vertical):
+        if vertical:
+            self.base_vert_axis_ = (bases, scripts)
+        else:
+            self.base_horiz_axis_ = (bases, scripts)
+
+    def set_size_parameters(self, location, DesignSize, SubfamilyID,
+                            RangeStart, RangeEnd):
+        if self.cur_feature_name_ != 'size':
+            raise FeatureLibError(
+                "Parameters statements are not allowed "
+                "within \"feature %s\"" % self.cur_feature_name_, location)
+        self.size_parameters_ = [DesignSize, SubfamilyID, RangeStart, RangeEnd]
+        for script, lang in self.language_systems:
+            key = (script, lang, self.cur_feature_name_)
+            self.features_.setdefault(key, [])
+
+    def add_ligature_subst(self, location,
+                           prefix, glyphs, suffix, replacement, forceChain):
+        if prefix or suffix or forceChain:
+            chain = self.get_lookup_(location, ChainContextSubstBuilder)
+            lookup = self.get_chained_lookup_(location, LigatureSubstBuilder)
+            chain.substitutions.append((prefix, glyphs, suffix, [lookup]))
+        else:
+            lookup = self.get_lookup_(location, LigatureSubstBuilder)
+
+        # 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 g in sorted(itertools.product(*glyphs)):
+            lookup.ligatures[g] = replacement
+
+    def add_multiple_subst(self, location,
+                           prefix, glyph, suffix, replacements):
+        if prefix or suffix:
+            chain = self.get_lookup_(location, ChainContextSubstBuilder)
+            sub = self.get_chained_lookup_(location, MultipleSubstBuilder)
+            sub.mapping[glyph] = replacements
+            chain.substitutions.append((prefix, [{glyph}], suffix, [sub]))
+            return
+        lookup = self.get_lookup_(location, MultipleSubstBuilder)
+        if glyph in lookup.mapping:
+            raise FeatureLibError(
+                'Already defined substitution for glyph "%s"' % glyph,
+                location)
+        lookup.mapping[glyph] = replacements
+
+    def add_reverse_chain_single_subst(self, location, old_prefix,
+                                       old_suffix, mapping):
+        lookup = self.get_lookup_(location, ReverseChainSingleSubstBuilder)
+        lookup.substitutions.append((old_prefix, old_suffix, mapping))
+
+    def add_single_subst(self, location, prefix, suffix, mapping, forceChain):
+        if self.cur_feature_name_ == "aalt":
+            for (from_glyph, to_glyph) in mapping.items():
+                alts = self.aalt_alternates_.setdefault(from_glyph, set())
+                alts.add(to_glyph)
+            return
+        if prefix or suffix or forceChain:
+            self.add_single_subst_chained_(location, prefix, suffix, mapping)
+            return
+        lookup = self.get_lookup_(location, SingleSubstBuilder)
+        for (from_glyph, to_glyph) in mapping.items():
+            if from_glyph in lookup.mapping:
+                raise FeatureLibError(
+                    'Already defined rule for replacing glyph "%s" by "%s"' %
+                    (from_glyph, lookup.mapping[from_glyph]),
+                    location)
+            lookup.mapping[from_glyph] = to_glyph
+
+    def find_chainable_SingleSubst_(self, chain, glyphs):
+        """Helper for add_single_subst_chained_()"""
+        for _, _, _, substitutions in chain.substitutions:
+            for sub in substitutions:
+                if (isinstance(sub, SingleSubstBuilder) and
+                        not any(g in glyphs for g in sub.mapping.keys())):
+                    return sub
+        return None
+
+    def add_single_subst_chained_(self, location, prefix, suffix, mapping):
+        # https://github.com/behdad/fonttools/issues/512
+        chain = self.get_lookup_(location, ChainContextSubstBuilder)
+        sub = self.find_chainable_SingleSubst_(chain, set(mapping.keys()))
+        if sub is None:
+            sub = self.get_chained_lookup_(location, SingleSubstBuilder)
+        sub.mapping.update(mapping)
+        chain.substitutions.append((prefix, [mapping.keys()], suffix, [sub]))
+
+    def add_cursive_pos(self, location, glyphclass, entryAnchor, exitAnchor):
+        lookup = self.get_lookup_(location, CursivePosBuilder)
+        lookup.add_attachment(
+            location, glyphclass,
+            makeOpenTypeAnchor(entryAnchor),
+            makeOpenTypeAnchor(exitAnchor))
+
+    def add_marks_(self, location, lookupBuilder, marks):
+        """Helper for add_mark_{base,liga,mark}_pos."""
+        for _, markClass in marks:
+            for markClassDef in markClass.definitions:
+                for mark in markClassDef.glyphs.glyphSet():
+                    if mark not in lookupBuilder.marks:
+                        otMarkAnchor = makeOpenTypeAnchor(markClassDef.anchor)
+                        lookupBuilder.marks[mark] = (
+                            markClass.name, otMarkAnchor)
+                    else:
+                        existingMarkClass = lookupBuilder.marks[mark][0]
+                        if markClass.name != existingMarkClass:
+                            raise FeatureLibError(
+                                "Glyph %s cannot be in both @%s and @%s" % (
+                                    mark, existingMarkClass, markClass.name),
+                                location)
+
+    def add_mark_base_pos(self, location, bases, marks):
+        builder = self.get_lookup_(location, MarkBasePosBuilder)
+        self.add_marks_(location, builder, marks)
+        for baseAnchor, markClass in marks:
+            otBaseAnchor = makeOpenTypeAnchor(baseAnchor)
+            for base in bases:
+                builder.bases.setdefault(base, {})[markClass.name] = (
+                    otBaseAnchor)
+
+    def add_mark_lig_pos(self, location, ligatures, components):
+        builder = self.get_lookup_(location, MarkLigPosBuilder)
+        componentAnchors = []
+        for marks in components:
+            anchors = {}
+            self.add_marks_(location, builder, marks)
+            for ligAnchor, markClass in marks:
+                anchors[markClass.name] = makeOpenTypeAnchor(ligAnchor)
+            componentAnchors.append(anchors)
+        for glyph in ligatures:
+            builder.ligatures[glyph] = componentAnchors
+
+    def add_mark_mark_pos(self, location, baseMarks, marks):
+        builder = self.get_lookup_(location, MarkMarkPosBuilder)
+        self.add_marks_(location, builder, marks)
+        for baseAnchor, markClass in marks:
+            otBaseAnchor = makeOpenTypeAnchor(baseAnchor)
+            for baseMark in baseMarks:
+                builder.baseMarks.setdefault(baseMark, {})[markClass.name] = (
+                    otBaseAnchor)
+
+    def add_class_pair_pos(self, location, glyphclass1, value1,
+                           glyphclass2, value2):
+        lookup = self.get_lookup_(location, PairPosBuilder)
+        lookup.addClassPair(location, glyphclass1, value1, glyphclass2, value2)
+
+    def add_specific_pair_pos(self, location, glyph1, value1, glyph2, value2):
+        lookup = self.get_lookup_(location, PairPosBuilder)
+        lookup.addGlyphPair(location, glyph1, value1, glyph2, value2)
+
+    def add_single_pos(self, location, prefix, suffix, pos, forceChain):
+        if prefix or suffix or forceChain:
+            self.add_single_pos_chained_(location, prefix, suffix, pos)
+        else:
+            lookup = self.get_lookup_(location, SinglePosBuilder)
+            for glyphs, value in pos:
+                for glyph in glyphs:
+                    lookup.add_pos(location, glyph, value)
+
+    def find_chainable_SinglePos_(self, lookups, glyphs, value):
+        """Helper for add_single_pos_chained_()"""
+        for look in lookups:
+            if all(look.can_add(glyph, value) for glyph in glyphs):
+                return look
+        return None
+
+    def add_single_pos_chained_(self, location, prefix, suffix, pos):
+        # https://github.com/fonttools/fonttools/issues/514
+        chain = self.get_lookup_(location, ChainContextPosBuilder)
+        targets = []
+        for _, _, _, lookups in chain.rules:
+            for lookup in lookups:
+                if isinstance(lookup, SinglePosBuilder):
+                    targets.append(lookup)
+        subs = []
+        for glyphs, value in pos:
+            if value is None:
+                subs.append(None)
+                continue
+            otValue, _ = makeOpenTypeValueRecord(value, pairPosContext=False)
+            sub = self.find_chainable_SinglePos_(targets, glyphs, otValue)
+            if sub is None:
+                sub = self.get_chained_lookup_(location, SinglePosBuilder)
+                targets.append(sub)
+            for glyph in glyphs:
+                sub.add_pos(location, glyph, value)
+            subs.append(sub)
+        assert len(pos) == len(subs), (pos, subs)
+        chain.rules.append(
+            (prefix, [g for g, v in pos], suffix, subs))
+
+    def setGlyphClass_(self, location, glyph, glyphClass):
+        oldClass, oldLocation = self.glyphClassDefs_.get(glyph, (None, None))
+        if oldClass and oldClass != glyphClass:
+            raise FeatureLibError(
+                "Glyph %s was assigned to a different class at %s:%s:%s" %
+                (glyph, oldLocation[0], oldLocation[1], oldLocation[2]),
+                location)
+        self.glyphClassDefs_[glyph] = (glyphClass, location)
+
+    def add_glyphClassDef(self, location, baseGlyphs, ligatureGlyphs,
+                          markGlyphs, componentGlyphs):
+        for glyph in baseGlyphs:
+            self.setGlyphClass_(location, glyph, 1)
+        for glyph in ligatureGlyphs:
+            self.setGlyphClass_(location, glyph, 2)
+        for glyph in markGlyphs:
+            self.setGlyphClass_(location, glyph, 3)
+        for glyph in componentGlyphs:
+            self.setGlyphClass_(location, glyph, 4)
+
+    def add_ligatureCaretByIndex_(self, location, glyphs, carets):
+        for glyph in glyphs:
+            self.ligCaretPoints_.setdefault(glyph, set()).update(carets)
+
+    def add_ligatureCaretByPos_(self, location, glyphs, carets):
+        for glyph in glyphs:
+            self.ligCaretCoords_.setdefault(glyph, set()).update(carets)
+
+    def add_name_record(self, location, nameID, platformID, platEncID,
+                        langID, string):
+        self.names_.append([nameID, platformID, platEncID, langID, string])
+
+    def add_os2_field(self, key, value):
+        self.os2_[key] = value
+
+    def add_hhea_field(self, key, value):
+        self.hhea_[key] = value
+
+    def add_vhea_field(self, key, value):
+        self.vhea_[key] = value
+
+
+def makeOpenTypeAnchor(anchor):
+    """ast.Anchor --> otTables.Anchor"""
+    if anchor is None:
+        return None
+    deviceX, deviceY = None, None
+    if anchor.xDeviceTable is not None:
+        deviceX = otl.buildDevice(dict(anchor.xDeviceTable))
+    if anchor.yDeviceTable is not None:
+        deviceY = otl.buildDevice(dict(anchor.yDeviceTable))
+    return otl.buildAnchor(anchor.x, anchor.y, anchor.contourpoint,
+                           deviceX, deviceY)
+
+
+_VALUEREC_ATTRS = {
+    name[0].lower() + name[1:]: (name, isDevice)
+    for _, name, isDevice, _ in otBase.valueRecordFormat
+    if not name.startswith("Reserved")
+}
+
+
+def makeOpenTypeValueRecord(v, pairPosContext):
+    """ast.ValueRecord --> (otBase.ValueRecord, int ValueFormat)"""
+    if v is None:
+        return None, 0
+
+    vr = {}
+    for astName, (otName, isDevice) in _VALUEREC_ATTRS.items():
+        val = getattr(v, astName, None)
+        if val:
+            vr[otName] = otl.buildDevice(dict(val)) if isDevice else val
+    if pairPosContext and not vr:
+        vr = {"YAdvance": 0} if v.vertical else {"XAdvance": 0}
+    valRec = otl.buildValue(vr)
+    return valRec, valRec.getFormat()
+
+
+class LookupBuilder(object):
+    def __init__(self, font, location, table, lookup_type):
+        self.font = font
+        self.glyphMap = font.getReverseGlyphMap()
+        self.location = location
+        self.table, self.lookup_type = table, lookup_type
+        self.lookupflag = 0
+        self.markFilterSet = None
+        self.lookup_index = None  # assigned when making final tables
+        assert table in ('GPOS', 'GSUB')
+
+    def equals(self, other):
+        return (isinstance(other, self.__class__) and
+                self.table == other.table and
+                self.lookupflag == other.lookupflag and
+                self.markFilterSet == other.markFilterSet)
+
+    def inferGlyphClasses(self):
+        """Infers glyph glasses for the GDEF table, such as {"cedilla":3}."""
+        return {}
+
+    def getAlternateGlyphs(self):
+        """Helper for building 'aalt' features."""
+        return {}
+
+    def buildLookup_(self, subtables):
+        return otl.buildLookup(subtables, self.lookupflag, self.markFilterSet)
+
+    def buildMarkClasses_(self, marks):
+        """{"cedilla": ("BOTTOM", ast.Anchor), ...} --> {"BOTTOM":0, "TOP":1}
+
+        Helper for MarkBasePostBuilder, MarkLigPosBuilder, and
+        MarkMarkPosBuilder. Seems to return the same numeric IDs
+        for mark classes as the AFDKO makeotf tool.
+        """
+        ids = {}
+        for mark in sorted(marks.keys(), key=self.font.getGlyphID):
+            markClassName, _markAnchor = marks[mark]
+            if markClassName not in ids:
+                ids[markClassName] = len(ids)
+        return ids
+
+    def setBacktrackCoverage_(self, prefix, subtable):
+        subtable.BacktrackGlyphCount = len(prefix)
+        subtable.BacktrackCoverage = []
+        for p in reversed(prefix):
+            coverage = otl.buildCoverage(p, self.glyphMap)
+            subtable.BacktrackCoverage.append(coverage)
+
+    def setLookAheadCoverage_(self, suffix, subtable):
+        subtable.LookAheadGlyphCount = len(suffix)
+        subtable.LookAheadCoverage = []
+        for s in suffix:
+            coverage = otl.buildCoverage(s, self.glyphMap)
+            subtable.LookAheadCoverage.append(coverage)
+
+    def setInputCoverage_(self, glyphs, subtable):
+        subtable.InputGlyphCount = len(glyphs)
+        subtable.InputCoverage = []
+        for g in glyphs:
+            coverage = otl.buildCoverage(g, self.glyphMap)
+            subtable.InputCoverage.append(coverage)
+
+
+class AlternateSubstBuilder(LookupBuilder):
+    def __init__(self, font, location):
+        LookupBuilder.__init__(self, font, location, 'GSUB', 3)
+        self.alternates = {}
+
+    def equals(self, other):
+        return (LookupBuilder.equals(self, other) and
+                self.alternates == other.alternates)
+
+    def build(self):
+        subtable = otl.buildAlternateSubstSubtable(self.alternates)
+        return self.buildLookup_([subtable])
+
+    def getAlternateGlyphs(self):
+        return self.alternates
+
+
+class ChainContextPosBuilder(LookupBuilder):
+    def __init__(self, font, location):
+        LookupBuilder.__init__(self, font, location, 'GPOS', 8)
+        self.rules = []  # (prefix, input, suffix, lookups)
+
+    def equals(self, other):
+        return (LookupBuilder.equals(self, other) and
+                self.rules == other.rules)
+
+    def build(self):
+        subtables = []
+        for (prefix, glyphs, suffix, lookups) in self.rules:
+            st = otTables.ChainContextPos()
+            subtables.append(st)
+            st.Format = 3
+            self.setBacktrackCoverage_(prefix, st)
+            self.setLookAheadCoverage_(suffix, st)
+            self.setInputCoverage_(glyphs, st)
+
+            st.PosCount = len([l for l in lookups if l is not None])
+            st.PosLookupRecord = []
+            for sequenceIndex, l in enumerate(lookups):
+                if l is not None:
+                    rec = otTables.PosLookupRecord()
+                    rec.SequenceIndex = sequenceIndex
+                    rec.LookupListIndex = l.lookup_index
+                    st.PosLookupRecord.append(rec)
+        return self.buildLookup_(subtables)
+
+
+class ChainContextSubstBuilder(LookupBuilder):
+    def __init__(self, font, location):
+        LookupBuilder.__init__(self, font, location, 'GSUB', 6)
+        self.substitutions = []  # (prefix, input, suffix, lookups)
+
+    def equals(self, other):
+        return (LookupBuilder.equals(self, other) and
+                self.substitutions == other.substitutions)
+
+    def build(self):
+        subtables = []
+        for (prefix, input, suffix, lookups) in self.substitutions:
+            st = otTables.ChainContextSubst()
+            subtables.append(st)
+            st.Format = 3
+            self.setBacktrackCoverage_(prefix, st)
+            self.setLookAheadCoverage_(suffix, st)
+            self.setInputCoverage_(input, st)
+
+            st.SubstCount = len([l for l in lookups if l is not None])
+            st.SubstLookupRecord = []
+            for sequenceIndex, l in enumerate(lookups):
+                if l is not None:
+                    rec = otTables.SubstLookupRecord()
+                    rec.SequenceIndex = sequenceIndex
+                    rec.LookupListIndex = l.lookup_index
+                    st.SubstLookupRecord.append(rec)
+        return self.buildLookup_(subtables)
+
+    def getAlternateGlyphs(self):
+        result = {}
+        for (_prefix, _input, _suffix, lookups) in self.substitutions:
+            for lookup in lookups:
+                alts = lookup.getAlternateGlyphs()
+                for glyph, replacements in alts.items():
+                    result.setdefault(glyph, set()).update(replacements)
+        return result
+
+
+class LigatureSubstBuilder(LookupBuilder):
+    def __init__(self, font, location):
+        LookupBuilder.__init__(self, font, location, 'GSUB', 4)
+        self.ligatures = {}  # {('f','f','i'): 'f_f_i'}
+
+    def equals(self, other):
+        return (LookupBuilder.equals(self, other) and
+                self.ligatures == other.ligatures)
+
+    def build(self):
+        subtable = otl.buildLigatureSubstSubtable(self.ligatures)
+        return self.buildLookup_([subtable])
+
+
+class MultipleSubstBuilder(LookupBuilder):
+    def __init__(self, font, location):
+        LookupBuilder.__init__(self, font, location, 'GSUB', 2)
+        self.mapping = {}
+
+    def equals(self, other):
+        return (LookupBuilder.equals(self, other) and
+                self.mapping == other.mapping)
+
+    def build(self):
+        subtable = otl.buildMultipleSubstSubtable(self.mapping)
+        return self.buildLookup_([subtable])
+
+
+class CursivePosBuilder(LookupBuilder):
+    def __init__(self, font, location):
+        LookupBuilder.__init__(self, font, location, 'GPOS', 3)
+        self.attachments = {}
+
+    def equals(self, other):
+        return (LookupBuilder.equals(self, other) and
+                self.attachments == other.attachments)
+
+    def add_attachment(self, location, glyphs, entryAnchor, exitAnchor):
+        for glyph in glyphs:
+            self.attachments[glyph] = (entryAnchor, exitAnchor)
+
+    def build(self):
+        st = otl.buildCursivePosSubtable(self.attachments, self.glyphMap)
+        return self.buildLookup_([st])
+
+
+class MarkBasePosBuilder(LookupBuilder):
+    def __init__(self, font, location):
+        LookupBuilder.__init__(self, font, location, 'GPOS', 4)
+        self.marks = {}  # glyphName -> (markClassName, anchor)
+        self.bases = {}  # glyphName -> {markClassName: anchor}
+
+    def equals(self, other):
+        return (LookupBuilder.equals(self, other) and
+                self.marks == other.marks and
+                self.bases == other.bases)
+
+    def inferGlyphClasses(self):
+        result = {glyph: 1 for glyph in self.bases}
+        result.update({glyph: 3 for glyph in self.marks})
+        return result
+
+    def build(self):
+        markClasses = self.buildMarkClasses_(self.marks)
+        marks = {mark: (markClasses[mc], anchor)
+                 for mark, (mc, anchor) in self.marks.items()}
+        bases = {}
+        for glyph, anchors in self.bases.items():
+            bases[glyph] = {markClasses[mc]: anchor
+                            for (mc, anchor) in anchors.items()}
+        subtables = otl.buildMarkBasePos(marks, bases, self.glyphMap)
+        return self.buildLookup_(subtables)
+
+
+class MarkLigPosBuilder(LookupBuilder):
+    def __init__(self, font, location):
+        LookupBuilder.__init__(self, font, location, 'GPOS', 5)
+        self.marks = {}  # glyphName -> (markClassName, anchor)
+        self.ligatures = {}  # glyphName -> [{markClassName: anchor}, ...]
+
+    def equals(self, other):
+        return (LookupBuilder.equals(self, other) and
+                self.marks == other.marks and
+                self.ligatures == other.ligatures)
+
+    def inferGlyphClasses(self):
+        result = {glyph: 2 for glyph in self.ligatures}
+        result.update({glyph: 3 for glyph in self.marks})
+        return result
+
+    def build(self):
+        markClasses = self.buildMarkClasses_(self.marks)
+        marks = {mark: (markClasses[mc], anchor)
+                 for mark, (mc, anchor) in self.marks.items()}
+        ligs = {}
+        for lig, components in self.ligatures.items():
+            ligs[lig] = []
+            for c in components:
+                ligs[lig].append({markClasses[mc]: a for mc, a in c.items()})
+        subtables = otl.buildMarkLigPos(marks, ligs, self.glyphMap)
+        return self.buildLookup_(subtables)
+
+
+class MarkMarkPosBuilder(LookupBuilder):
+    def __init__(self, font, location):
+        LookupBuilder.__init__(self, font, location, 'GPOS', 6)
+        self.marks = {}      # glyphName -> (markClassName, anchor)
+        self.baseMarks = {}  # glyphName -> {markClassName: anchor}
+
+    def equals(self, other):
+        return (LookupBuilder.equals(self, other) and
+                self.marks == other.marks and
+                self.baseMarks == other.baseMarks)
+
+    def inferGlyphClasses(self):
+        result = {glyph: 3 for glyph in self.baseMarks}
+        result.update({glyph: 3 for glyph in self.marks})
+        return result
+
+    def build(self):
+        markClasses = self.buildMarkClasses_(self.marks)
+        markClassList = sorted(markClasses.keys(), key=markClasses.get)
+        marks = {mark: (markClasses[mc], anchor)
+                 for mark, (mc, anchor) in self.marks.items()}
+
+        st = otTables.MarkMarkPos()
+        st.Format = 1
+        st.ClassCount = len(markClasses)
+        st.Mark1Coverage = otl.buildCoverage(marks, self.glyphMap)
+        st.Mark2Coverage = otl.buildCoverage(self.baseMarks, self.glyphMap)
+        st.Mark1Array = otl.buildMarkArray(marks, self.glyphMap)
+        st.Mark2Array = otTables.Mark2Array()
+        st.Mark2Array.Mark2Count = len(st.Mark2Coverage.glyphs)
+        st.Mark2Array.Mark2Record = []
+        for base in st.Mark2Coverage.glyphs:
+            anchors = [self.baseMarks[base].get(mc) for mc in markClassList]
+            st.Mark2Array.Mark2Record.append(otl.buildMark2Record(anchors))
+        return self.buildLookup_([st])
+
+
+class ReverseChainSingleSubstBuilder(LookupBuilder):
+    def __init__(self, font, location):
+        LookupBuilder.__init__(self, font, location, 'GSUB', 8)
+        self.substitutions = []  # (prefix, suffix, mapping)
+
+    def equals(self, other):
+        return (LookupBuilder.equals(self, other) and
+                self.substitutions == other.substitutions)
+
+    def build(self):
+        subtables = []
+        for prefix, suffix, mapping in self.substitutions:
+            st = otTables.ReverseChainSingleSubst()
+            st.Format = 1
+            self.setBacktrackCoverage_(prefix, st)
+            self.setLookAheadCoverage_(suffix, st)
+            st.Coverage = otl.buildCoverage(mapping.keys(), self.glyphMap)
+            st.GlyphCount = len(mapping)
+            st.Substitute = [mapping[g] for g in st.Coverage.glyphs]
+            subtables.append(st)
+        return self.buildLookup_(subtables)
+
+
+class SingleSubstBuilder(LookupBuilder):
+    def __init__(self, font, location):
+        LookupBuilder.__init__(self, font, location, 'GSUB', 1)
+        self.mapping = {}
+
+    def equals(self, other):
+        return (LookupBuilder.equals(self, other) and
+                self.mapping == other.mapping)
+
+    def build(self):
+        subtable = otl.buildSingleSubstSubtable(self.mapping)
+        return self.buildLookup_([subtable])
+
+    def getAlternateGlyphs(self):
+        return {glyph: set([repl]) for glyph, repl in self.mapping.items()}
+
+
+class ClassPairPosSubtableBuilder(object):
+    def __init__(self, builder, valueFormat1, valueFormat2):
+        self.builder_ = builder
+        self.classDef1_, self.classDef2_ = None, None
+        self.values_ = {}  # (glyphclass1, glyphclass2) --> (value1, value2)
+        self.valueFormat1_, self.valueFormat2_ = valueFormat1, valueFormat2
+        self.forceSubtableBreak_ = False
+        self.subtables_ = []
+
+    def addPair(self, gc1, value1, gc2, value2):
+        mergeable = (not self.forceSubtableBreak_ and
+                     self.classDef1_ is not None and
+                     self.classDef1_.canAdd(gc1) and
+                     self.classDef2_ is not None and
+                     self.classDef2_.canAdd(gc2))
+        if not mergeable:
+            self.flush_()
+            self.classDef1_ = otl.ClassDefBuilder(useClass0=True)
+            self.classDef2_ = otl.ClassDefBuilder(useClass0=False)
+            self.values_ = {}
+        self.classDef1_.add(gc1)
+        self.classDef2_.add(gc2)
+        self.values_[(gc1, gc2)] = (value1, value2)
+
+    def addSubtableBreak(self):
+        self.forceSubtableBreak_ = True
+
+    def subtables(self):
+        self.flush_()
+        return self.subtables_
+
+    def flush_(self):
+        if self.classDef1_ is None or self.classDef2_ is None:
+            return
+        st = otl.buildPairPosClassesSubtable(self.values_,
+                                             self.builder_.glyphMap)
+        self.subtables_.append(st)
+
+
+class PairPosBuilder(LookupBuilder):
+    SUBTABLE_BREAK_ = "SUBTABLE_BREAK"
+
+    def __init__(self, font, location):
+        LookupBuilder.__init__(self, font, location, 'GPOS', 2)
+        self.pairs = []  # [(gc1, value1, gc2, value2)*]
+        self.glyphPairs = {}  # (glyph1, glyph2) --> (value1, value2)
+        self.locations = {}  # (gc1, gc2) --> (filepath, line, column)
+
+    def addClassPair(self, location, glyphclass1, value1, glyphclass2, value2):
+        self.pairs.append((glyphclass1, value1, glyphclass2, value2))
+
+    def addGlyphPair(self, location, glyph1, value1, glyph2, value2):
+        key = (glyph1, glyph2)
+        oldValue = self.glyphPairs.get(key, None)
+        if oldValue is not None:
+            # the Feature File spec explicitly allows specific pairs generated
+            # by an 'enum' rule to be overridden by preceding single pairs;
+            # we emit a warning and use the previously defined value
+            otherLoc = self.locations[key]
+            log.warning(
+                'Already defined position for pair %s %s at %s:%d:%d; '
+                'choosing the first value',
+                glyph1, glyph2, otherLoc[0], otherLoc[1], otherLoc[2])
+        else:
+            val1, _ = makeOpenTypeValueRecord(value1, pairPosContext=True)
+            val2, _ = makeOpenTypeValueRecord(value2, pairPosContext=True)
+            self.glyphPairs[key] = (val1, val2)
+            self.locations[key] = location
+
+    def add_subtable_break(self, location):
+        self.pairs.append((self.SUBTABLE_BREAK_, self.SUBTABLE_BREAK_,
+                           self.SUBTABLE_BREAK_, self.SUBTABLE_BREAK_))
+
+    def equals(self, other):
+        return (LookupBuilder.equals(self, other) and
+                self.glyphPairs == other.glyphPairs and
+                self.pairs == other.pairs)
+
+    def build(self):
+        builders = {}
+        builder = None
+        for glyphclass1, value1, glyphclass2, value2 in self.pairs:
+            if glyphclass1 is self.SUBTABLE_BREAK_:
+                if builder is not None:
+                    builder.addSubtableBreak()
+                continue
+            val1, valFormat1 = makeOpenTypeValueRecord(
+                value1, pairPosContext=True)
+            val2, valFormat2 = makeOpenTypeValueRecord(
+                value2, pairPosContext=True)
+            builder = builders.get((valFormat1, valFormat2))
+            if builder is None:
+                builder = ClassPairPosSubtableBuilder(
+                    self, valFormat1, valFormat2)
+                builders[(valFormat1, valFormat2)] = builder
+            builder.addPair(glyphclass1, val1, glyphclass2, val2)
+        subtables = []
+        if self.glyphPairs:
+            subtables.extend(
+                otl.buildPairPosGlyphs(self.glyphPairs, self.glyphMap))
+        for key in sorted(builders.keys()):
+            subtables.extend(builders[key].subtables())
+        return self.buildLookup_(subtables)
+
+
+class SinglePosBuilder(LookupBuilder):
+    def __init__(self, font, location):
+        LookupBuilder.__init__(self, font, location, 'GPOS', 1)
+        self.locations = {}  # glyph -> (filename, line, column)
+        self.mapping = {}  # glyph -> otTables.ValueRecord
+
+    def add_pos(self, location, glyph, valueRecord):
+        otValueRecord, _ = makeOpenTypeValueRecord(
+            valueRecord, pairPosContext=False)
+        if not self.can_add(glyph, otValueRecord):
+            otherLoc = self.locations[glyph]
+            raise FeatureLibError(
+                'Already defined different position for glyph "%s" at %s:%d:%d'
+                % (glyph, otherLoc[0], otherLoc[1], otherLoc[2]),
+                location)
+        if otValueRecord:
+            self.mapping[glyph] = otValueRecord
+        self.locations[glyph] = location
+
+    def can_add(self, glyph, value):
+        assert isinstance(value, otl.ValueRecord)
+        curValue = self.mapping.get(glyph)
+        return curValue is None or curValue == value
+
+    def equals(self, other):
+        return (LookupBuilder.equals(self, other) and
+                self.mapping == other.mapping)
+
+    def build(self):
+        subtables = otl.buildSinglePos(self.mapping, self.glyphMap)
+        return self.buildLookup_(subtables)
diff --git a/Lib/fontTools/feaLib/error.py b/Lib/fontTools/feaLib/error.py
new file mode 100644
index 0000000..8281f95
--- /dev/null
+++ b/Lib/fontTools/feaLib/error.py
@@ -0,0 +1,20 @@
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+
+
+class FeatureLibError(Exception):
+    def __init__(self, message, location):
+        Exception.__init__(self, message)
+        self.location = location
+
+    def __str__(self):
+        message = Exception.__str__(self)
+        if self.location:
+            path, line, column = self.location
+            return "%s:%d:%d: %s" % (path, line, column, message)
+        else:
+            return message
+
+
+class IncludedFeaNotFound(FeatureLibError):
+    pass
diff --git a/Lib/fontTools/feaLib/lexer.py b/Lib/fontTools/feaLib/lexer.py
new file mode 100644
index 0000000..18849ef
--- /dev/null
+++ b/Lib/fontTools/feaLib/lexer.py
@@ -0,0 +1,261 @@
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.feaLib.error import FeatureLibError, IncludedFeaNotFound
+import re
+import os
+
+
+class Lexer(object):
+    NUMBER = "NUMBER"
+    FLOAT = "FLOAT"
+    STRING = "STRING"
+    NAME = "NAME"
+    FILENAME = "FILENAME"
+    GLYPHCLASS = "GLYPHCLASS"
+    CID = "CID"
+    SYMBOL = "SYMBOL"
+    COMMENT = "COMMENT"
+    NEWLINE = "NEWLINE"
+    ANONYMOUS_BLOCK = "ANONYMOUS_BLOCK"
+
+    CHAR_WHITESPACE_ = " \t"
+    CHAR_NEWLINE_ = "\r\n"
+    CHAR_SYMBOL_ = ",;:-+'{}[]<>()="
+    CHAR_DIGIT_ = "0123456789"
+    CHAR_HEXDIGIT_ = "0123456789ABCDEFabcdef"
+    CHAR_LETTER_ = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+    CHAR_NAME_START_ = CHAR_LETTER_ + "_+*:.^~!\\"
+    CHAR_NAME_CONTINUATION_ = CHAR_LETTER_ + CHAR_DIGIT_ + "_.+*:^~!/-"
+
+    RE_GLYPHCLASS = re.compile(r"^[A-Za-z_0-9.]+$")
+
+    MODE_NORMAL_ = "NORMAL"
+    MODE_FILENAME_ = "FILENAME"
+
+    def __init__(self, text, filename):
+        self.filename_ = filename
+        self.line_ = 1
+        self.pos_ = 0
+        self.line_start_ = 0
+        self.text_ = text
+        self.text_length_ = len(text)
+        self.mode_ = Lexer.MODE_NORMAL_
+
+    def __iter__(self):
+        return self
+
+    def next(self):  # Python 2
+        return self.__next__()
+
+    def __next__(self):  # Python 3
+        while True:
+            token_type, token, location = self.next_()
+            if token_type != Lexer.NEWLINE:
+                return (token_type, token, location)
+
+    def location_(self):
+        column = self.pos_ - self.line_start_ + 1
+        return (self.filename_ or "<features>", self.line_, column)
+
+    def next_(self):
+        self.scan_over_(Lexer.CHAR_WHITESPACE_)
+        location = self.location_()
+        start = self.pos_
+        text = self.text_
+        limit = len(text)
+        if start >= limit:
+            raise StopIteration()
+        cur_char = text[start]
+        next_char = text[start + 1] if start + 1 < limit else None
+
+        if cur_char == "\n":
+            self.pos_ += 1
+            self.line_ += 1
+            self.line_start_ = self.pos_
+            return (Lexer.NEWLINE, None, location)
+        if cur_char == "\r":
+            self.pos_ += (2 if next_char == "\n" else 1)
+            self.line_ += 1
+            self.line_start_ = self.pos_
+            return (Lexer.NEWLINE, None, location)
+        if cur_char == "#":
+            self.scan_until_(Lexer.CHAR_NEWLINE_)
+            return (Lexer.COMMENT, text[start:self.pos_], location)
+
+        if self.mode_ is Lexer.MODE_FILENAME_:
+            if cur_char != "(":
+                raise FeatureLibError("Expected '(' before file name",
+                                      location)
+            self.scan_until_(")")
+            cur_char = text[self.pos_] if self.pos_ < limit else None
+            if cur_char != ")":
+                raise FeatureLibError("Expected ')' after file name",
+                                      location)
+            self.pos_ += 1
+            self.mode_ = Lexer.MODE_NORMAL_
+            return (Lexer.FILENAME, text[start + 1:self.pos_ - 1], location)
+
+        if cur_char == "\\" and next_char in Lexer.CHAR_DIGIT_:
+            self.pos_ += 1
+            self.scan_over_(Lexer.CHAR_DIGIT_)
+            return (Lexer.CID, int(text[start + 1:self.pos_], 10), location)
+        if cur_char == "@":
+            self.pos_ += 1
+            self.scan_over_(Lexer.CHAR_NAME_CONTINUATION_)
+            glyphclass = text[start + 1:self.pos_]
+            if len(glyphclass) < 1:
+                raise FeatureLibError("Expected glyph class name", location)
+            if len(glyphclass) > 63:
+                raise FeatureLibError(
+                    "Glyph class names must not be longer than 63 characters",
+                    location)
+            if not Lexer.RE_GLYPHCLASS.match(glyphclass):
+                raise FeatureLibError(
+                    "Glyph class names must consist of letters, digits, "
+                    "underscore, or period", location)
+            return (Lexer.GLYPHCLASS, glyphclass, location)
+        if cur_char in Lexer.CHAR_NAME_START_:
+            self.pos_ += 1
+            self.scan_over_(Lexer.CHAR_NAME_CONTINUATION_)
+            token = text[start:self.pos_]
+            if token == "include":
+                self.mode_ = Lexer.MODE_FILENAME_
+            return (Lexer.NAME, token, location)
+        if cur_char == "0" and next_char in "xX":
+            self.pos_ += 2
+            self.scan_over_(Lexer.CHAR_HEXDIGIT_)
+            return (Lexer.NUMBER, int(text[start:self.pos_], 16), location)
+        if cur_char in Lexer.CHAR_DIGIT_:
+            self.scan_over_(Lexer.CHAR_DIGIT_)
+            if self.pos_ >= limit or text[self.pos_] != ".":
+                return (Lexer.NUMBER, int(text[start:self.pos_], 10), location)
+            self.scan_over_(".")
+            self.scan_over_(Lexer.CHAR_DIGIT_)
+            return (Lexer.FLOAT, float(text[start:self.pos_]), location)
+        if cur_char == "-" and next_char in Lexer.CHAR_DIGIT_:
+            self.pos_ += 1
+            self.scan_over_(Lexer.CHAR_DIGIT_)
+            if self.pos_ >= limit or text[self.pos_] != ".":
+                return (Lexer.NUMBER, int(text[start:self.pos_], 10), location)
+            self.scan_over_(".")
+            self.scan_over_(Lexer.CHAR_DIGIT_)
+            return (Lexer.FLOAT, float(text[start:self.pos_]), location)
+        if cur_char in Lexer.CHAR_SYMBOL_:
+            self.pos_ += 1
+            return (Lexer.SYMBOL, cur_char, location)
+        if cur_char == '"':
+            self.pos_ += 1
+            self.scan_until_('"')
+            if self.pos_ < self.text_length_ and self.text_[self.pos_] == '"':
+                self.pos_ += 1
+                # strip newlines embedded within a string
+                string = re.sub("[\r\n]", "", text[start + 1:self.pos_ - 1])
+                return (Lexer.STRING, string, location)
+            else:
+                raise FeatureLibError("Expected '\"' to terminate string",
+                                      location)
+        raise FeatureLibError("Unexpected character: %r" % cur_char,
+                              location)
+
+    def scan_over_(self, valid):
+        p = self.pos_
+        while p < self.text_length_ and self.text_[p] in valid:
+            p += 1
+        self.pos_ = p
+
+    def scan_until_(self, stop_at):
+        p = self.pos_
+        while p < self.text_length_ and self.text_[p] not in stop_at:
+            p += 1
+        self.pos_ = p
+
+    def scan_anonymous_block(self, tag):
+        location = self.location_()
+        tag = tag.strip()
+        self.scan_until_(Lexer.CHAR_NEWLINE_)
+        self.scan_over_(Lexer.CHAR_NEWLINE_)
+        regexp = r'}\s*' + tag + r'\s*;'
+        split = re.split(regexp, self.text_[self.pos_:], maxsplit=1)
+        if len(split) != 2:
+            raise FeatureLibError(
+                "Expected '} %s;' to terminate anonymous block" % tag,
+                location)
+        self.pos_ += len(split[0])
+        return (Lexer.ANONYMOUS_BLOCK, split[0], location)
+
+
+class IncludingLexer(object):
+    def __init__(self, featurefile):
+        self.lexers_ = [self.make_lexer_(featurefile)]
+        self.featurefilepath = self.lexers_[0].filename_
+
+    def __iter__(self):
+        return self
+
+    def next(self):  # Python 2
+        return self.__next__()
+
+    def __next__(self):  # Python 3
+        while self.lexers_:
+            lexer = self.lexers_[-1]
+            try:
+                token_type, token, location = next(lexer)
+            except StopIteration:
+                self.lexers_.pop()
+                continue
+            if token_type is Lexer.NAME and token == "include":
+                fname_type, fname_token, fname_location = lexer.next()
+                if fname_type is not Lexer.FILENAME:
+                    raise FeatureLibError("Expected file name", fname_location)
+                #semi_type, semi_token, semi_location = lexer.next()
+                #if semi_type is not Lexer.SYMBOL or semi_token != ";":
+                #    raise FeatureLibError("Expected ';'", semi_location)
+                if os.path.isabs(fname_token):
+                    path = fname_token
+                else:
+                    if self.featurefilepath is not None:
+                        curpath = os.path.dirname(self.featurefilepath)
+                    else:
+                        # if the IncludingLexer was initialized from an in-memory
+                        # file-like stream, it doesn't have a 'name' pointing to
+                        # its filesystem path, therefore we fall back to using the
+                        # current working directory to resolve relative includes
+                        curpath = os.getcwd()
+                    path = os.path.join(curpath, fname_token)
+                if len(self.lexers_) >= 5:
+                    raise FeatureLibError("Too many recursive includes",
+                                          fname_location)
+                try:
+                    self.lexers_.append(self.make_lexer_(path))
+                except IOError as err:
+                    # FileNotFoundError does not exist on Python < 3.3
+                    import errno
+                    if err.errno == errno.ENOENT:
+                        raise IncludedFeaNotFound(fname_token, fname_location)
+                    raise  # pragma: no cover
+            else:
+                return (token_type, token, location)
+        raise StopIteration()
+
+    @staticmethod
+    def make_lexer_(file_or_path):
+        if hasattr(file_or_path, "read"):
+            fileobj, closing = file_or_path, False
+        else:
+            filename, closing = file_or_path, True
+            fileobj = open(filename, "r", encoding="utf-8")
+        data = fileobj.read()
+        filename = getattr(fileobj, "name", None)
+        if closing:
+            fileobj.close()
+        return Lexer(data, filename)
+
+    def scan_anonymous_block(self, tag):
+        return self.lexers_[-1].scan_anonymous_block(tag)
+
+
+class NonIncludingLexer(IncludingLexer):
+    """Lexer that does not follow `include` statements, emits them as-is."""
+    def __next__(self):  # Python 3
+        return next(self.lexers_[0])
diff --git a/Lib/fontTools/feaLib/parser.py b/Lib/fontTools/feaLib/parser.py
new file mode 100644
index 0000000..2b2f2d4
--- /dev/null
+++ b/Lib/fontTools/feaLib/parser.py
@@ -0,0 +1,1694 @@
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+from fontTools.feaLib.error import FeatureLibError
+from fontTools.feaLib.lexer import Lexer, IncludingLexer, NonIncludingLexer
+from fontTools.misc.encodingTools import getEncoding
+from fontTools.misc.py23 import *
+import fontTools.feaLib.ast as ast
+import logging
+import os
+import re
+
+
+log = logging.getLogger(__name__)
+
+
+class Parser(object):
+    extensions = {}
+    ast = ast
+    SS_FEATURE_TAGS = {"ss%02d" % i for i in range(1, 20+1)}
+    CV_FEATURE_TAGS = {"cv%02d" % i for i in range(1, 99+1)}
+
+    def __init__(self, featurefile, glyphNames=(), followIncludes=True,
+                 **kwargs):
+        if "glyphMap" in kwargs:
+            from fontTools.misc.loggingTools import deprecateArgument
+            deprecateArgument("glyphMap", "use 'glyphNames' (iterable) instead")
+            if glyphNames:
+                raise TypeError("'glyphNames' and (deprecated) 'glyphMap' are "
+                                "mutually exclusive")
+            glyphNames = kwargs.pop("glyphMap")
+        if kwargs:
+            raise TypeError("unsupported keyword argument%s: %s"
+                            % ("" if len(kwargs) == 1 else "s",
+                               ", ".join(repr(k) for k in kwargs)))
+
+        self.glyphNames_ = set(glyphNames)
+        self.doc_ = self.ast.FeatureFile()
+        self.anchors_ = SymbolTable()
+        self.glyphclasses_ = SymbolTable()
+        self.lookups_ = SymbolTable()
+        self.valuerecords_ = SymbolTable()
+        self.symbol_tables_ = {
+            self.anchors_, self.valuerecords_
+        }
+        self.next_token_type_, self.next_token_ = (None, None)
+        self.cur_comments_ = []
+        self.next_token_location_ = None
+        lexerClass = IncludingLexer if followIncludes else NonIncludingLexer
+        self.lexer_ = lexerClass(featurefile)
+        self.advance_lexer_(comments=True)
+
+    def parse(self):
+        statements = self.doc_.statements
+        while self.next_token_type_ is not None or self.cur_comments_:
+            self.advance_lexer_(comments=True)
+            if self.cur_token_type_ is Lexer.COMMENT:
+                statements.append(
+                    self.ast.Comment(self.cur_token_,
+                                     location=self.cur_token_location_))
+            elif self.is_cur_keyword_("include"):
+                statements.append(self.parse_include_())
+            elif self.cur_token_type_ is Lexer.GLYPHCLASS:
+                statements.append(self.parse_glyphclass_definition_())
+            elif self.is_cur_keyword_(("anon", "anonymous")):
+                statements.append(self.parse_anonymous_())
+            elif self.is_cur_keyword_("anchorDef"):
+                statements.append(self.parse_anchordef_())
+            elif self.is_cur_keyword_("languagesystem"):
+                statements.append(self.parse_languagesystem_())
+            elif self.is_cur_keyword_("lookup"):
+                statements.append(self.parse_lookup_(vertical=False))
+            elif self.is_cur_keyword_("markClass"):
+                statements.append(self.parse_markClass_())
+            elif self.is_cur_keyword_("feature"):
+                statements.append(self.parse_feature_block_())
+            elif self.is_cur_keyword_("table"):
+                statements.append(self.parse_table_())
+            elif self.is_cur_keyword_("valueRecordDef"):
+                statements.append(
+                    self.parse_valuerecord_definition_(vertical=False))
+            elif self.cur_token_type_ is Lexer.NAME and self.cur_token_ in self.extensions:
+                statements.append(self.extensions[self.cur_token_](self))
+            elif self.cur_token_type_ is Lexer.SYMBOL and self.cur_token_ == ";":
+                continue
+            else:
+                raise FeatureLibError(
+                    "Expected feature, languagesystem, lookup, markClass, "
+                    "table, or glyph class definition, got {} \"{}\"".format(self.cur_token_type_, self.cur_token_),
+                    self.cur_token_location_)
+        return self.doc_
+
+    def parse_anchor_(self):
+        self.expect_symbol_("<")
+        self.expect_keyword_("anchor")
+        location = self.cur_token_location_
+
+        if self.next_token_ == "NULL":
+            self.expect_keyword_("NULL")
+            self.expect_symbol_(">")
+            return None
+
+        if self.next_token_type_ == Lexer.NAME:
+            name = self.expect_name_()
+            anchordef = self.anchors_.resolve(name)
+            if anchordef is None:
+                raise FeatureLibError(
+                    'Unknown anchor "%s"' % name,
+                    self.cur_token_location_)
+            self.expect_symbol_(">")
+            return self.ast.Anchor(anchordef.x, anchordef.y,
+                                   name=name,
+                                   contourpoint=anchordef.contourpoint,
+                                   xDeviceTable=None, yDeviceTable=None,
+                                   location=location)
+
+        x, y = self.expect_number_(), self.expect_number_()
+
+        contourpoint = None
+        if self.next_token_ == "contourpoint":
+            self.expect_keyword_("contourpoint")
+            contourpoint = self.expect_number_()
+
+        if self.next_token_ == "<":
+            xDeviceTable = self.parse_device_()
+            yDeviceTable = self.parse_device_()
+        else:
+            xDeviceTable, yDeviceTable = None, None
+
+        self.expect_symbol_(">")
+        return self.ast.Anchor(x, y, name=None,
+                               contourpoint=contourpoint,
+                               xDeviceTable=xDeviceTable,
+                               yDeviceTable=yDeviceTable,
+                               location=location)
+
+    def parse_anchor_marks_(self):
+        """Parses a sequence of [<anchor> mark @MARKCLASS]*."""
+        anchorMarks = []  # [(self.ast.Anchor, markClassName)*]
+        while self.next_token_ == "<":
+            anchor = self.parse_anchor_()
+            if anchor is None and self.next_token_ != "mark":
+                continue  # <anchor NULL> without mark, eg. in GPOS type 5
+            self.expect_keyword_("mark")
+            markClass = self.expect_markClass_reference_()
+            anchorMarks.append((anchor, markClass))
+        return anchorMarks
+
+    def parse_anchordef_(self):
+        assert self.is_cur_keyword_("anchorDef")
+        location = self.cur_token_location_
+        x, y = self.expect_number_(), self.expect_number_()
+        contourpoint = None
+        if self.next_token_ == "contourpoint":
+            self.expect_keyword_("contourpoint")
+            contourpoint = self.expect_number_()
+        name = self.expect_name_()
+        self.expect_symbol_(";")
+        anchordef = self.ast.AnchorDefinition(name, x, y,
+                                              contourpoint=contourpoint,
+                                              location=location)
+        self.anchors_.define(name, anchordef)
+        return anchordef
+
+    def parse_anonymous_(self):
+        assert self.is_cur_keyword_(("anon", "anonymous"))
+        tag = self.expect_tag_()
+        _, content, location = self.lexer_.scan_anonymous_block(tag)
+        self.advance_lexer_()
+        self.expect_symbol_('}')
+        end_tag = self.expect_tag_()
+        assert tag == end_tag, "bad splitting in Lexer.scan_anonymous_block()"
+        self.expect_symbol_(';')
+        return self.ast.AnonymousBlock(tag, content, location=location)
+
+    def parse_attach_(self):
+        assert self.is_cur_keyword_("Attach")
+        location = self.cur_token_location_
+        glyphs = self.parse_glyphclass_(accept_glyphname=True)
+        contourPoints = {self.expect_number_()}
+        while self.next_token_ != ";":
+            contourPoints.add(self.expect_number_())
+        self.expect_symbol_(";")
+        return self.ast.AttachStatement(glyphs, contourPoints,
+                                        location=location)
+
+    def parse_enumerate_(self, vertical):
+        assert self.cur_token_ in {"enumerate", "enum"}
+        self.advance_lexer_()
+        return self.parse_position_(enumerated=True, vertical=vertical)
+
+    def parse_GlyphClassDef_(self):
+        """Parses 'GlyphClassDef @BASE, @LIGATURES, @MARKS, @COMPONENTS;'"""
+        assert self.is_cur_keyword_("GlyphClassDef")
+        location = self.cur_token_location_
+        if self.next_token_ != ",":
+            baseGlyphs = self.parse_glyphclass_(accept_glyphname=False)
+        else:
+            baseGlyphs = None
+        self.expect_symbol_(",")
+        if self.next_token_ != ",":
+            ligatureGlyphs = self.parse_glyphclass_(accept_glyphname=False)
+        else:
+            ligatureGlyphs = None
+        self.expect_symbol_(",")
+        if self.next_token_ != ",":
+            markGlyphs = self.parse_glyphclass_(accept_glyphname=False)
+        else:
+            markGlyphs = None
+        self.expect_symbol_(",")
+        if self.next_token_ != ";":
+            componentGlyphs = self.parse_glyphclass_(accept_glyphname=False)
+        else:
+            componentGlyphs = None
+        self.expect_symbol_(";")
+        return self.ast.GlyphClassDefStatement(baseGlyphs, markGlyphs,
+                                               ligatureGlyphs, componentGlyphs,
+                                               location=location)
+
+    def parse_glyphclass_definition_(self):
+        """Parses glyph class definitions such as '@UPPERCASE = [A-Z];'"""
+        location, name = self.cur_token_location_, self.cur_token_
+        self.expect_symbol_("=")
+        glyphs = self.parse_glyphclass_(accept_glyphname=False)
+        self.expect_symbol_(";")
+        glyphclass = self.ast.GlyphClassDefinition(name, glyphs,
+                                                   location=location)
+        self.glyphclasses_.define(name, glyphclass)
+        return glyphclass
+
+    def split_glyph_range_(self, name, location):
+        # Since v1.20, the OpenType Feature File specification allows
+        # for dashes in glyph names. A sequence like "a-b-c-d" could
+        # therefore mean a single glyph whose name happens to be
+        # "a-b-c-d", or it could mean a range from glyph "a" to glyph
+        # "b-c-d", or a range from glyph "a-b" to glyph "c-d", or a
+        # range from glyph "a-b-c" to glyph "d".Technically, this
+        # example could be resolved because the (pretty complex)
+        # definition of glyph ranges renders most of these splits
+        # invalid. But the specification does not say that a compiler
+        # should try to apply such fancy heuristics. To encourage
+        # unambiguous feature files, we therefore try all possible
+        # splits and reject the feature file if there are multiple
+        # splits possible. It is intentional that we don't just emit a
+        # warning; warnings tend to get ignored. To fix the problem,
+        # font designers can trivially add spaces around the intended
+        # split point, and we emit a compiler error that suggests
+        # how exactly the source should be rewritten to make things
+        # unambiguous.
+        parts = name.split("-")
+        solutions = []
+        for i in range(len(parts)):
+            start, limit = "-".join(parts[0:i]), "-".join(parts[i:])
+            if start in self.glyphNames_ and limit in self.glyphNames_:
+                solutions.append((start, limit))
+        if len(solutions) == 1:
+            start, limit = solutions[0]
+            return start, limit
+        elif len(solutions) == 0:
+            raise FeatureLibError(
+                "\"%s\" is not a glyph in the font, and it can not be split "
+                "into a range of known glyphs" % name, location)
+        else:
+            ranges = " or ".join(["\"%s - %s\"" % (s, l) for s, l in solutions])
+            raise FeatureLibError(
+                "Ambiguous glyph range \"%s\"; "
+                "please use %s to clarify what you mean" % (name, ranges),
+                location)
+
+    def parse_glyphclass_(self, accept_glyphname):
+        if (accept_glyphname and
+                self.next_token_type_ in (Lexer.NAME, Lexer.CID)):
+            glyph = self.expect_glyph_()
+            return self.ast.GlyphName(glyph, location=self.cur_token_location_)
+        if self.next_token_type_ is Lexer.GLYPHCLASS:
+            self.advance_lexer_()
+            gc = self.glyphclasses_.resolve(self.cur_token_)
+            if gc is None:
+                raise FeatureLibError(
+                    "Unknown glyph class @%s" % self.cur_token_,
+                    self.cur_token_location_)
+            if isinstance(gc, self.ast.MarkClass):
+                return self.ast.MarkClassName(
+                    gc, location=self.cur_token_location_)
+            else:
+                return self.ast.GlyphClassName(
+                    gc, location=self.cur_token_location_)
+
+        self.expect_symbol_("[")
+        location = self.cur_token_location_
+        glyphs = self.ast.GlyphClass(location=location)
+        while self.next_token_ != "]":
+            if self.next_token_type_ is Lexer.NAME:
+                glyph = self.expect_glyph_()
+                location = self.cur_token_location_
+                if '-' in glyph and glyph not in self.glyphNames_:
+                    start, limit = self.split_glyph_range_(glyph, location)
+                    glyphs.add_range(
+                        start, limit,
+                        self.make_glyph_range_(location, start, limit))
+                elif self.next_token_ == "-":
+                    start = glyph
+                    self.expect_symbol_("-")
+                    limit = self.expect_glyph_()
+                    glyphs.add_range(
+                        start, limit,
+                        self.make_glyph_range_(location, start, limit))
+                else:
+                    glyphs.append(glyph)
+            elif self.next_token_type_ is Lexer.CID:
+                glyph = self.expect_glyph_()
+                if self.next_token_ == "-":
+                    range_location = self.cur_token_location_
+                    range_start = self.cur_token_
+                    self.expect_symbol_("-")
+                    range_end = self.expect_cid_()
+                    glyphs.add_cid_range(range_start, range_end,
+                                         self.make_cid_range_(range_location,
+                                                              range_start, range_end))
+                else:
+                    glyphs.append("cid%05d" % self.cur_token_)
+            elif self.next_token_type_ is Lexer.GLYPHCLASS:
+                self.advance_lexer_()
+                gc = self.glyphclasses_.resolve(self.cur_token_)
+                if gc is None:
+                    raise FeatureLibError(
+                        "Unknown glyph class @%s" % self.cur_token_,
+                        self.cur_token_location_)
+                if isinstance(gc, self.ast.MarkClass):
+                    gc = self.ast.MarkClassName(
+                        gc, location=self.cur_token_location_)
+                else:
+                    gc = self.ast.GlyphClassName(
+                        gc, location=self.cur_token_location_)
+                glyphs.add_class(gc)
+            else:
+                raise FeatureLibError(
+                    "Expected glyph name, glyph range, "
+                    "or glyph class reference",
+                    self.next_token_location_)
+        self.expect_symbol_("]")
+        return glyphs
+
+    def parse_class_name_(self):
+        name = self.expect_class_name_()
+        gc = self.glyphclasses_.resolve(name)
+        if gc is None:
+            raise FeatureLibError(
+                "Unknown glyph class @%s" % name,
+                self.cur_token_location_)
+        if isinstance(gc, self.ast.MarkClass):
+            return self.ast.MarkClassName(
+                gc, location=self.cur_token_location_)
+        else:
+            return self.ast.GlyphClassName(
+                gc, location=self.cur_token_location_)
+
+    def parse_glyph_pattern_(self, vertical):
+        prefix, glyphs, lookups, values, suffix = ([], [], [], [], [])
+        hasMarks = False
+        while self.next_token_ not in {"by", "from", ";", ","}:
+            gc = self.parse_glyphclass_(accept_glyphname=True)
+            marked = False
+            if self.next_token_ == "'":
+                self.expect_symbol_("'")
+                hasMarks = marked = True
+            if marked:
+                if suffix:
+                    # makeotf also reports this as an error, while FontForge
+                    # silently inserts ' in all the intervening glyphs.
+                    # https://github.com/fonttools/fonttools/pull/1096
+                    raise FeatureLibError(
+                        "Unsupported contextual target sequence: at most "
+                        "one run of marked (') glyph/class names allowed",
+                        self.cur_token_location_)
+                glyphs.append(gc)
+            elif glyphs:
+                suffix.append(gc)
+            else:
+                prefix.append(gc)
+
+            if self.is_next_value_():
+                values.append(self.parse_valuerecord_(vertical))
+            else:
+                values.append(None)
+
+            lookup = None
+            if self.next_token_ == "lookup":
+                self.expect_keyword_("lookup")
+                if not marked:
+                    raise FeatureLibError(
+                        "Lookups can only follow marked glyphs",
+                        self.cur_token_location_)
+                lookup_name = self.expect_name_()
+                lookup = self.lookups_.resolve(lookup_name)
+                if lookup is None:
+                    raise FeatureLibError(
+                        'Unknown lookup "%s"' % lookup_name,
+                        self.cur_token_location_)
+            if marked:
+                lookups.append(lookup)
+
+        if not glyphs and not suffix:  # eg., "sub f f i by"
+            assert lookups == []
+            return ([], prefix, [None] * len(prefix), values, [], hasMarks)
+        else:
+            assert not any(values[:len(prefix)]), values
+            values = values[len(prefix):][:len(glyphs)]
+            return (prefix, glyphs, lookups, values, suffix, hasMarks)
+
+    def parse_chain_context_(self):
+        location = self.cur_token_location_
+        prefix, glyphs, lookups, values, suffix, hasMarks = \
+            self.parse_glyph_pattern_(vertical=False)
+        chainContext = [(prefix, glyphs, suffix)]
+        hasLookups = any(lookups)
+        while self.next_token_ == ",":
+            self.expect_symbol_(",")
+            prefix, glyphs, lookups, values, suffix, hasMarks = \
+                self.parse_glyph_pattern_(vertical=False)
+            chainContext.append((prefix, glyphs, suffix))
+            hasLookups = hasLookups or any(lookups)
+        self.expect_symbol_(";")
+        return chainContext, hasLookups
+
+    def parse_ignore_(self):
+        assert self.is_cur_keyword_("ignore")
+        location = self.cur_token_location_
+        self.advance_lexer_()
+        if self.cur_token_ in ["substitute", "sub"]:
+            chainContext, hasLookups = self.parse_chain_context_()
+            if hasLookups:
+                raise FeatureLibError(
+                    "No lookups can be specified for \"ignore sub\"",
+                    location)
+            return self.ast.IgnoreSubstStatement(chainContext,
+                                                 location=location)
+        if self.cur_token_ in ["position", "pos"]:
+            chainContext, hasLookups = self.parse_chain_context_()
+            if hasLookups:
+                raise FeatureLibError(
+                    "No lookups can be specified for \"ignore pos\"",
+                    location)
+            return self.ast.IgnorePosStatement(chainContext,
+                                               location=location)
+        raise FeatureLibError(
+            "Expected \"substitute\" or \"position\"",
+            self.cur_token_location_)
+
+    def parse_include_(self):
+        assert self.cur_token_ == "include"
+        location = self.cur_token_location_
+        filename = self.expect_filename_()
+        # self.expect_symbol_(";")
+        return ast.IncludeStatement(filename, location=location)
+
+    def parse_language_(self):
+        assert self.is_cur_keyword_("language")
+        location = self.cur_token_location_
+        language = self.expect_language_tag_()
+        include_default, required = (True, False)
+        if self.next_token_ in {"exclude_dflt", "include_dflt"}:
+            include_default = (self.expect_name_() == "include_dflt")
+        if self.next_token_ == "required":
+            self.expect_keyword_("required")
+            required = True
+        self.expect_symbol_(";")
+        return self.ast.LanguageStatement(language,
+                                          include_default, required,
+                                          location=location)
+
+    def parse_ligatureCaretByIndex_(self):
+        assert self.is_cur_keyword_("LigatureCaretByIndex")
+        location = self.cur_token_location_
+        glyphs = self.parse_glyphclass_(accept_glyphname=True)
+        carets = [self.expect_number_()]
+        while self.next_token_ != ";":
+            carets.append(self.expect_number_())
+        self.expect_symbol_(";")
+        return self.ast.LigatureCaretByIndexStatement(glyphs, carets,
+                                                      location=location)
+
+    def parse_ligatureCaretByPos_(self):
+        assert self.is_cur_keyword_("LigatureCaretByPos")
+        location = self.cur_token_location_
+        glyphs = self.parse_glyphclass_(accept_glyphname=True)
+        carets = [self.expect_number_()]
+        while self.next_token_ != ";":
+            carets.append(self.expect_number_())
+        self.expect_symbol_(";")
+        return self.ast.LigatureCaretByPosStatement(glyphs, carets,
+                                                    location=location)
+
+    def parse_lookup_(self, vertical):
+        assert self.is_cur_keyword_("lookup")
+        location, name = self.cur_token_location_, self.expect_name_()
+
+        if self.next_token_ == ";":
+            lookup = self.lookups_.resolve(name)
+            if lookup is None:
+                raise FeatureLibError("Unknown lookup \"%s\"" % name,
+                                      self.cur_token_location_)
+            self.expect_symbol_(";")
+            return self.ast.LookupReferenceStatement(lookup,
+                                                     location=location)
+
+        use_extension = False
+        if self.next_token_ == "useExtension":
+            self.expect_keyword_("useExtension")
+            use_extension = True
+
+        block = self.ast.LookupBlock(name, use_extension, location=location)
+        self.parse_block_(block, vertical)
+        self.lookups_.define(name, block)
+        return block
+
+    def parse_lookupflag_(self):
+        assert self.is_cur_keyword_("lookupflag")
+        location = self.cur_token_location_
+
+        # format B: "lookupflag 6;"
+        if self.next_token_type_ == Lexer.NUMBER:
+            value = self.expect_number_()
+            self.expect_symbol_(";")
+            return self.ast.LookupFlagStatement(value, location=location)
+
+        # format A: "lookupflag RightToLeft MarkAttachmentType @M;"
+        value, markAttachment, markFilteringSet = 0, None, None
+        flags = {
+            "RightToLeft": 1, "IgnoreBaseGlyphs": 2,
+            "IgnoreLigatures": 4, "IgnoreMarks": 8
+        }
+        seen = set()
+        while self.next_token_ != ";":
+            if self.next_token_ in seen:
+                raise FeatureLibError(
+                    "%s can be specified only once" % self.next_token_,
+                    self.next_token_location_)
+            seen.add(self.next_token_)
+            if self.next_token_ == "MarkAttachmentType":
+                self.expect_keyword_("MarkAttachmentType")
+                markAttachment = self.parse_class_name_()
+            elif self.next_token_ == "UseMarkFilteringSet":
+                self.expect_keyword_("UseMarkFilteringSet")
+                markFilteringSet = self.parse_class_name_()
+            elif self.next_token_ in flags:
+                value = value | flags[self.expect_name_()]
+            else:
+                raise FeatureLibError(
+                    '"%s" is not a recognized lookupflag' % self.next_token_,
+                    self.next_token_location_)
+        self.expect_symbol_(";")
+        return self.ast.LookupFlagStatement(value,
+                                            markAttachment=markAttachment,
+                                            markFilteringSet=markFilteringSet,
+                                            location=location)
+
+    def parse_markClass_(self):
+        assert self.is_cur_keyword_("markClass")
+        location = self.cur_token_location_
+        glyphs = self.parse_glyphclass_(accept_glyphname=True)
+        anchor = self.parse_anchor_()
+        name = self.expect_class_name_()
+        self.expect_symbol_(";")
+        markClass = self.doc_.markClasses.get(name)
+        if markClass is None:
+            markClass = self.ast.MarkClass(name)
+            self.doc_.markClasses[name] = markClass
+            self.glyphclasses_.define(name, markClass)
+        mcdef = self.ast.MarkClassDefinition(markClass, anchor, glyphs,
+                                             location=location)
+        markClass.addDefinition(mcdef)
+        return mcdef
+
+    def parse_position_(self, enumerated, vertical):
+        assert self.cur_token_ in {"position", "pos"}
+        if self.next_token_ == "cursive":  # GPOS type 3
+            return self.parse_position_cursive_(enumerated, vertical)
+        elif self.next_token_ == "base":   # GPOS type 4
+            return self.parse_position_base_(enumerated, vertical)
+        elif self.next_token_ == "ligature":   # GPOS type 5
+            return self.parse_position_ligature_(enumerated, vertical)
+        elif self.next_token_ == "mark":   # GPOS type 6
+            return self.parse_position_mark_(enumerated, vertical)
+
+        location = self.cur_token_location_
+        prefix, glyphs, lookups, values, suffix, hasMarks = \
+            self.parse_glyph_pattern_(vertical)
+        self.expect_symbol_(";")
+
+        if any(lookups):
+            # GPOS type 8: Chaining contextual positioning; explicit lookups
+            if any(values):
+                raise FeatureLibError(
+                    "If \"lookup\" is present, no values must be specified",
+                    location)
+            return self.ast.ChainContextPosStatement(
+                prefix, glyphs, suffix, lookups, location=location)
+
+        # Pair positioning, format A: "pos V 10 A -10;"
+        # Pair positioning, format B: "pos V A -20;"
+        if not prefix and not suffix and len(glyphs) == 2 and not hasMarks:
+            if values[0] is None:  # Format B: "pos V A -20;"
+                values.reverse()
+            return self.ast.PairPosStatement(
+                glyphs[0], values[0], glyphs[1], values[1],
+                enumerated=enumerated,
+                location=location)
+
+        if enumerated:
+            raise FeatureLibError(
+                '"enumerate" is only allowed with pair positionings', location)
+        return self.ast.SinglePosStatement(list(zip(glyphs, values)),
+                                           prefix, suffix, forceChain=hasMarks,
+                                           location=location)
+
+    def parse_position_cursive_(self, enumerated, vertical):
+        location = self.cur_token_location_
+        self.expect_keyword_("cursive")
+        if enumerated:
+            raise FeatureLibError(
+                '"enumerate" is not allowed with '
+                'cursive attachment positioning',
+                location)
+        glyphclass = self.parse_glyphclass_(accept_glyphname=True)
+        entryAnchor = self.parse_anchor_()
+        exitAnchor = self.parse_anchor_()
+        self.expect_symbol_(";")
+        return self.ast.CursivePosStatement(
+            glyphclass, entryAnchor, exitAnchor, location=location)
+
+    def parse_position_base_(self, enumerated, vertical):
+        location = self.cur_token_location_
+        self.expect_keyword_("base")
+        if enumerated:
+            raise FeatureLibError(
+                '"enumerate" is not allowed with '
+                'mark-to-base attachment positioning',
+                location)
+        base = self.parse_glyphclass_(accept_glyphname=True)
+        marks = self.parse_anchor_marks_()
+        self.expect_symbol_(";")
+        return self.ast.MarkBasePosStatement(base, marks, location=location)
+
+    def parse_position_ligature_(self, enumerated, vertical):
+        location = self.cur_token_location_
+        self.expect_keyword_("ligature")
+        if enumerated:
+            raise FeatureLibError(
+                '"enumerate" is not allowed with '
+                'mark-to-ligature attachment positioning',
+                location)
+        ligatures = self.parse_glyphclass_(accept_glyphname=True)
+        marks = [self.parse_anchor_marks_()]
+        while self.next_token_ == "ligComponent":
+            self.expect_keyword_("ligComponent")
+            marks.append(self.parse_anchor_marks_())
+        self.expect_symbol_(";")
+        return self.ast.MarkLigPosStatement(ligatures, marks, location=location)
+
+    def parse_position_mark_(self, enumerated, vertical):
+        location = self.cur_token_location_
+        self.expect_keyword_("mark")
+        if enumerated:
+            raise FeatureLibError(
+                '"enumerate" is not allowed with '
+                'mark-to-mark attachment positioning',
+                location)
+        baseMarks = self.parse_glyphclass_(accept_glyphname=True)
+        marks = self.parse_anchor_marks_()
+        self.expect_symbol_(";")
+        return self.ast.MarkMarkPosStatement(baseMarks, marks,
+                                             location=location)
+
+    def parse_script_(self):
+        assert self.is_cur_keyword_("script")
+        location, script = self.cur_token_location_, self.expect_script_tag_()
+        self.expect_symbol_(";")
+        return self.ast.ScriptStatement(script, location=location)
+
+    def parse_substitute_(self):
+        assert self.cur_token_ in {"substitute", "sub", "reversesub", "rsub"}
+        location = self.cur_token_location_
+        reverse = self.cur_token_ in {"reversesub", "rsub"}
+        old_prefix, old, lookups, values, old_suffix, hasMarks = \
+            self.parse_glyph_pattern_(vertical=False)
+        if any(values):
+            raise FeatureLibError(
+                "Substitution statements cannot contain values", location)
+        new = []
+        if self.next_token_ == "by":
+            keyword = self.expect_keyword_("by")
+            while self.next_token_ != ";":
+                gc = self.parse_glyphclass_(accept_glyphname=True)
+                new.append(gc)
+        elif self.next_token_ == "from":
+            keyword = self.expect_keyword_("from")
+            new = [self.parse_glyphclass_(accept_glyphname=False)]
+        else:
+            keyword = None
+        self.expect_symbol_(";")
+        if len(new) is 0 and not any(lookups):
+            raise FeatureLibError(
+                'Expected "by", "from" or explicit lookup references',
+                self.cur_token_location_)
+
+        # GSUB lookup type 3: Alternate substitution.
+        # Format: "substitute a from [a.1 a.2 a.3];"
+        if keyword == "from":
+            if reverse:
+                raise FeatureLibError(
+                    'Reverse chaining substitutions do not support "from"',
+                    location)
+            if len(old) != 1 or len(old[0].glyphSet()) != 1:
+                raise FeatureLibError(
+                    'Expected a single glyph before "from"',
+                    location)
+            if len(new) != 1:
+                raise FeatureLibError(
+                    'Expected a single glyphclass after "from"',
+                    location)
+            return self.ast.AlternateSubstStatement(
+                old_prefix, old[0], old_suffix, new[0], location=location)
+
+        num_lookups = len([l for l in lookups if l is not None])
+
+        # GSUB lookup type 1: Single substitution.
+        # Format A: "substitute a by a.sc;"
+        # Format B: "substitute [one.fitted one.oldstyle] by one;"
+        # Format C: "substitute [a-d] by [A.sc-D.sc];"
+        if (not reverse and len(old) == 1 and len(new) == 1 and
+                num_lookups == 0):
+            glyphs = list(old[0].glyphSet())
+            replacements = list(new[0].glyphSet())
+            if len(replacements) == 1:
+                replacements = replacements * len(glyphs)
+            if len(glyphs) != len(replacements):
+                raise FeatureLibError(
+                    'Expected a glyph class with %d elements after "by", '
+                    'but found a glyph class with %d elements' %
+                    (len(glyphs), len(replacements)), location)
+            return self.ast.SingleSubstStatement(
+                old, new,
+                old_prefix, old_suffix,
+                forceChain=hasMarks,
+                location=location
+            )
+
+        # GSUB lookup type 2: Multiple substitution.
+        # Format: "substitute f_f_i by f f i;"
+        if (not reverse and
+                len(old) == 1 and len(old[0].glyphSet()) == 1 and
+                len(new) > 1 and max([len(n.glyphSet()) for n in new]) == 1 and
+                num_lookups == 0):
+            return self.ast.MultipleSubstStatement(
+                old_prefix, tuple(old[0].glyphSet())[0], old_suffix,
+                tuple([list(n.glyphSet())[0] for n in new]), location=location)
+
+        # GSUB lookup type 4: Ligature substitution.
+        # Format: "substitute f f i by f_f_i;"
+        if (not reverse and
+                len(old) > 1 and len(new) == 1 and
+                len(new[0].glyphSet()) == 1 and
+                num_lookups == 0):
+            return self.ast.LigatureSubstStatement(
+                old_prefix, old, old_suffix,
+                list(new[0].glyphSet())[0], forceChain=hasMarks,
+                location=location)
+
+        # GSUB lookup type 8: Reverse chaining substitution.
+        if reverse:
+            if len(old) != 1:
+                raise FeatureLibError(
+                    "In reverse chaining single substitutions, "
+                    "only a single glyph or glyph class can be replaced",
+                    location)
+            if len(new) != 1:
+                raise FeatureLibError(
+                    'In reverse chaining single substitutions, '
+                    'the replacement (after "by") must be a single glyph '
+                    'or glyph class', location)
+            if num_lookups != 0:
+                raise FeatureLibError(
+                    "Reverse chaining substitutions cannot call named lookups",
+                    location)
+            glyphs = sorted(list(old[0].glyphSet()))
+            replacements = sorted(list(new[0].glyphSet()))
+            if len(replacements) == 1:
+                replacements = replacements * len(glyphs)
+            if len(glyphs) != len(replacements):
+                raise FeatureLibError(
+                    'Expected a glyph class with %d elements after "by", '
+                    'but found a glyph class with %d elements' %
+                    (len(glyphs), len(replacements)), location)
+            return self.ast.ReverseChainSingleSubstStatement(
+                old_prefix, old_suffix, old, new, location=location)
+
+        if len(old) > 1 and len(new) > 1:
+            raise FeatureLibError(
+                'Direct substitution of multiple glyphs by multiple glyphs '
+                'is not supported',
+                location)
+
+        # GSUB lookup type 6: Chaining contextual substitution.
+        assert len(new) == 0, new
+        rule = self.ast.ChainContextSubstStatement(
+            old_prefix, old, old_suffix, lookups, location=location)
+        return rule
+
+    def parse_subtable_(self):
+        assert self.is_cur_keyword_("subtable")
+        location = self.cur_token_location_
+        self.expect_symbol_(";")
+        return self.ast.SubtableStatement(location=location)
+
+    def parse_size_parameters_(self):
+        assert self.is_cur_keyword_("parameters")
+        location = self.cur_token_location_
+        DesignSize = self.expect_decipoint_()
+        SubfamilyID = self.expect_number_()
+        RangeStart = 0
+        RangeEnd = 0
+        if self.next_token_type_ in (Lexer.NUMBER, Lexer.FLOAT) or \
+                SubfamilyID != 0:
+            RangeStart = self.expect_decipoint_()
+            RangeEnd = self.expect_decipoint_()
+
+        self.expect_symbol_(";")
+        return self.ast.SizeParameters(DesignSize, SubfamilyID,
+                                       RangeStart, RangeEnd,
+                                       location=location)
+
+    def parse_size_menuname_(self):
+        assert self.is_cur_keyword_("sizemenuname")
+        location = self.cur_token_location_
+        platformID, platEncID, langID, string = self.parse_name_()
+        return self.ast.FeatureNameStatement("size", platformID,
+                                             platEncID, langID, string,
+                                             location=location)
+
+    def parse_table_(self):
+        assert self.is_cur_keyword_("table")
+        location, name = self.cur_token_location_, self.expect_tag_()
+        table = self.ast.TableBlock(name, location=location)
+        self.expect_symbol_("{")
+        handler = {
+            "GDEF": self.parse_table_GDEF_,
+            "head": self.parse_table_head_,
+            "hhea": self.parse_table_hhea_,
+            "vhea": self.parse_table_vhea_,
+            "name": self.parse_table_name_,
+            "BASE": self.parse_table_BASE_,
+            "OS/2": self.parse_table_OS_2_,
+        }.get(name)
+        if handler:
+            handler(table)
+        else:
+            raise FeatureLibError('"table %s" is not supported' % name.strip(),
+                                  location)
+        self.expect_symbol_("}")
+        end_tag = self.expect_tag_()
+        if end_tag != name:
+            raise FeatureLibError('Expected "%s"' % name.strip(),
+                                  self.cur_token_location_)
+        self.expect_symbol_(";")
+        return table
+
+    def parse_table_GDEF_(self, table):
+        statements = table.statements
+        while self.next_token_ != "}" or self.cur_comments_:
+            self.advance_lexer_(comments=True)
+            if self.cur_token_type_ is Lexer.COMMENT:
+                statements.append(self.ast.Comment(
+                    self.cur_token_, location=self.cur_token_location_))
+            elif self.is_cur_keyword_("Attach"):
+                statements.append(self.parse_attach_())
+            elif self.is_cur_keyword_("GlyphClassDef"):
+                statements.append(self.parse_GlyphClassDef_())
+            elif self.is_cur_keyword_("LigatureCaretByIndex"):
+                statements.append(self.parse_ligatureCaretByIndex_())
+            elif self.is_cur_keyword_("LigatureCaretByPos"):
+                statements.append(self.parse_ligatureCaretByPos_())
+            elif self.cur_token_ == ";":
+                continue
+            else:
+                raise FeatureLibError(
+                    "Expected Attach, LigatureCaretByIndex, "
+                    "or LigatureCaretByPos",
+                    self.cur_token_location_)
+
+    def parse_table_head_(self, table):
+        statements = table.statements
+        while self.next_token_ != "}" or self.cur_comments_:
+            self.advance_lexer_(comments=True)
+            if self.cur_token_type_ is Lexer.COMMENT:
+                statements.append(self.ast.Comment(
+                    self.cur_token_, location=self.cur_token_location_))
+            elif self.is_cur_keyword_("FontRevision"):
+                statements.append(self.parse_FontRevision_())
+            elif self.cur_token_ == ";":
+                continue
+            else:
+                raise FeatureLibError("Expected FontRevision",
+                                      self.cur_token_location_)
+
+    def parse_table_hhea_(self, table):
+        statements = table.statements
+        fields = ("CaretOffset", "Ascender", "Descender", "LineGap")
+        while self.next_token_ != "}" or self.cur_comments_:
+            self.advance_lexer_(comments=True)
+            if self.cur_token_type_ is Lexer.COMMENT:
+                statements.append(self.ast.Comment(
+                    self.cur_token_, location=self.cur_token_location_))
+            elif self.cur_token_type_ is Lexer.NAME and self.cur_token_ in fields:
+                key = self.cur_token_.lower()
+                value = self.expect_number_()
+                statements.append(
+                    self.ast.HheaField(key, value,
+                                       location=self.cur_token_location_))
+                if self.next_token_ != ";":
+                    raise FeatureLibError("Incomplete statement", self.next_token_location_)
+            elif self.cur_token_ == ";":
+                continue
+            else:
+                raise FeatureLibError("Expected CaretOffset, Ascender, "
+                                      "Descender or LineGap",
+                                      self.cur_token_location_)
+
+    def parse_table_vhea_(self, table):
+        statements = table.statements
+        fields = ("VertTypoAscender", "VertTypoDescender", "VertTypoLineGap")
+        while self.next_token_ != "}" or self.cur_comments_:
+            self.advance_lexer_(comments=True)
+            if self.cur_token_type_ is Lexer.COMMENT:
+                statements.append(self.ast.Comment(
+                    self.cur_token_, location=self.cur_token_location_))
+            elif self.cur_token_type_ is Lexer.NAME and self.cur_token_ in fields:
+                key = self.cur_token_.lower()
+                value = self.expect_number_()
+                statements.append(
+                    self.ast.VheaField(key, value,
+                                       location=self.cur_token_location_))
+                if self.next_token_ != ";":
+                    raise FeatureLibError("Incomplete statement", self.next_token_location_)
+            elif self.cur_token_ == ";":
+                continue
+            else:
+                raise FeatureLibError("Expected VertTypoAscender, "
+                                      "VertTypoDescender or VertTypoLineGap",
+                                      self.cur_token_location_)
+
+    def parse_table_name_(self, table):
+        statements = table.statements
+        while self.next_token_ != "}" or self.cur_comments_:
+            self.advance_lexer_(comments=True)
+            if self.cur_token_type_ is Lexer.COMMENT:
+                statements.append(self.ast.Comment(
+                    self.cur_token_, location=self.cur_token_location_))
+            elif self.is_cur_keyword_("nameid"):
+                statement = self.parse_nameid_()
+                if statement:
+                    statements.append(statement)
+            elif self.cur_token_ == ";":
+                continue
+            else:
+                raise FeatureLibError("Expected nameid",
+                                      self.cur_token_location_)
+
+    def parse_name_(self):
+        platEncID = None
+        langID = None
+        if self.next_token_type_ == Lexer.NUMBER:
+            platformID = self.expect_number_()
+            location = self.cur_token_location_
+            if platformID not in (1, 3):
+                raise FeatureLibError("Expected platform id 1 or 3", location)
+            if self.next_token_type_ == Lexer.NUMBER:
+                platEncID = self.expect_number_()
+                langID = self.expect_number_()
+        else:
+            platformID = 3
+            location = self.cur_token_location_
+
+        if platformID == 1:                # Macintosh
+            platEncID = platEncID or 0     # Roman
+            langID = langID or 0           # English
+        else:                              # 3, Windows
+            platEncID = platEncID or 1     # Unicode
+            langID = langID or 0x0409      # English
+
+        string = self.expect_string_()
+        self.expect_symbol_(";")
+
+        encoding = getEncoding(platformID, platEncID, langID)
+        if encoding is None:
+            raise FeatureLibError("Unsupported encoding", location)
+        unescaped = self.unescape_string_(string, encoding)
+        return platformID, platEncID, langID, unescaped
+
+    def parse_nameid_(self):
+        assert self.cur_token_ == "nameid", self.cur_token_
+        location, nameID = self.cur_token_location_, self.expect_number_()
+        if nameID > 32767:
+            raise FeatureLibError("Name id value cannot be greater than 32767",
+                                  self.cur_token_location_)
+        if 1 <= nameID <= 6:
+            log.warning("Name id %d cannot be set from the feature file. "
+                        "Ignoring record" % nameID)
+            self.parse_name_()  # skip to the next record
+            return None
+
+        platformID, platEncID, langID, string = self.parse_name_()
+        return self.ast.NameRecord(nameID, platformID, platEncID,
+                                   langID, string, location=location)
+
+    def unescape_string_(self, string, encoding):
+        if encoding == "utf_16_be":
+            s = re.sub(r"\\[0-9a-fA-F]{4}", self.unescape_unichr_, string)
+        else:
+            unescape = lambda m: self.unescape_byte_(m, encoding)
+            s = re.sub(r"\\[0-9a-fA-F]{2}", unescape, string)
+        # We now have a Unicode string, but it might contain surrogate pairs.
+        # We convert surrogates to actual Unicode by round-tripping through
+        # Python's UTF-16 codec in a special mode.
+        utf16 = tobytes(s, "utf_16_be", "surrogatepass")
+        return tounicode(utf16, "utf_16_be")
+
+    @staticmethod
+    def unescape_unichr_(match):
+        n = match.group(0)[1:]
+        return unichr(int(n, 16))
+
+    @staticmethod
+    def unescape_byte_(match, encoding):
+        n = match.group(0)[1:]
+        return bytechr(int(n, 16)).decode(encoding)
+
+    def parse_table_BASE_(self, table):
+        statements = table.statements
+        while self.next_token_ != "}" or self.cur_comments_:
+            self.advance_lexer_(comments=True)
+            if self.cur_token_type_ is Lexer.COMMENT:
+                statements.append(self.ast.Comment(
+                    self.cur_token_, location=self.cur_token_location_))
+            elif self.is_cur_keyword_("HorizAxis.BaseTagList"):
+                horiz_bases = self.parse_base_tag_list_()
+            elif self.is_cur_keyword_("HorizAxis.BaseScriptList"):
+                horiz_scripts = self.parse_base_script_list_(len(horiz_bases))
+                statements.append(
+                        self.ast.BaseAxis(horiz_bases,
+                                          horiz_scripts, False,
+                                          location=self.cur_token_location_))
+            elif self.is_cur_keyword_("VertAxis.BaseTagList"):
+                vert_bases = self.parse_base_tag_list_()
+            elif self.is_cur_keyword_("VertAxis.BaseScriptList"):
+                vert_scripts = self.parse_base_script_list_(len(vert_bases))
+                statements.append(
+                        self.ast.BaseAxis(vert_bases,
+                                          vert_scripts, True,
+                                          location=self.cur_token_location_))
+            elif self.cur_token_ == ";":
+                continue
+
+    def parse_table_OS_2_(self, table):
+        statements = table.statements
+        numbers = ("FSType", "TypoAscender", "TypoDescender", "TypoLineGap",
+                   "winAscent", "winDescent", "XHeight", "CapHeight",
+                   "WeightClass", "WidthClass", "LowerOpSize", "UpperOpSize")
+        ranges = ("UnicodeRange", "CodePageRange")
+        while self.next_token_ != "}" or self.cur_comments_:
+            self.advance_lexer_(comments=True)
+            if self.cur_token_type_ is Lexer.COMMENT:
+                statements.append(self.ast.Comment(
+                    self.cur_token_, location=self.cur_token_location_))
+            elif self.cur_token_type_ is Lexer.NAME:
+                key = self.cur_token_.lower()
+                value = None
+                if self.cur_token_ in numbers:
+                    value = self.expect_number_()
+                elif self.is_cur_keyword_("Panose"):
+                    value = []
+                    for i in range(10):
+                        value.append(self.expect_number_())
+                elif self.cur_token_ in ranges:
+                    value = []
+                    while self.next_token_ != ";":
+                         value.append(self.expect_number_())
+                elif self.is_cur_keyword_("Vendor"):
+                    value = self.expect_string_()
+                statements.append(
+                    self.ast.OS2Field(key, value,
+                                      location=self.cur_token_location_))
+            elif self.cur_token_ == ";":
+                continue
+
+    def parse_base_tag_list_(self):
+        assert self.cur_token_ in ("HorizAxis.BaseTagList",
+                                   "VertAxis.BaseTagList"), self.cur_token_
+        bases = []
+        while self.next_token_ != ";":
+            bases.append(self.expect_script_tag_())
+        self.expect_symbol_(";")
+        return bases
+
+    def parse_base_script_list_(self, count):
+        assert self.cur_token_ in ("HorizAxis.BaseScriptList",
+                                   "VertAxis.BaseScriptList"), self.cur_token_
+        scripts = [(self.parse_base_script_record_(count))]
+        while self.next_token_ == ",":
+            self.expect_symbol_(",")
+            scripts.append(self.parse_base_script_record_(count))
+        self.expect_symbol_(";")
+        return scripts
+
+    def parse_base_script_record_(self, count):
+        script_tag = self.expect_script_tag_()
+        base_tag = self.expect_script_tag_()
+        coords = [self.expect_number_() for i in range(count)]
+        return script_tag, base_tag, coords
+
+    def parse_device_(self):
+        result = None
+        self.expect_symbol_("<")
+        self.expect_keyword_("device")
+        if self.next_token_ == "NULL":
+            self.expect_keyword_("NULL")
+        else:
+            result = [(self.expect_number_(), self.expect_number_())]
+            while self.next_token_ == ",":
+                self.expect_symbol_(",")
+                result.append((self.expect_number_(), self.expect_number_()))
+            result = tuple(result)  # make it hashable
+        self.expect_symbol_(">")
+        return result
+
+    def is_next_value_(self):
+        return self.next_token_type_ is Lexer.NUMBER or self.next_token_ == "<"
+
+    def parse_valuerecord_(self, vertical):
+        if self.next_token_type_ is Lexer.NUMBER:
+            number, location = self.expect_number_(), self.cur_token_location_
+            if vertical:
+                val = self.ast.ValueRecord(yAdvance=number,
+                                           vertical=vertical,
+                                           location=location)
+            else:
+                val = self.ast.ValueRecord(xAdvance=number,
+                                           vertical=vertical,
+                                           location=location)
+            return val
+        self.expect_symbol_("<")
+        location = self.cur_token_location_
+        if self.next_token_type_ is Lexer.NAME:
+            name = self.expect_name_()
+            if name == "NULL":
+                self.expect_symbol_(">")
+                return None
+            vrd = self.valuerecords_.resolve(name)
+            if vrd is None:
+                raise FeatureLibError("Unknown valueRecordDef \"%s\"" % name,
+                                      self.cur_token_location_)
+            value = vrd.value
+            xPlacement, yPlacement = (value.xPlacement, value.yPlacement)
+            xAdvance, yAdvance = (value.xAdvance, value.yAdvance)
+        else:
+            xPlacement, yPlacement, xAdvance, yAdvance = (
+                self.expect_number_(), self.expect_number_(),
+                self.expect_number_(), self.expect_number_())
+
+        if self.next_token_ == "<":
+            xPlaDevice, yPlaDevice, xAdvDevice, yAdvDevice = (
+                self.parse_device_(), self.parse_device_(),
+                self.parse_device_(), self.parse_device_())
+            allDeltas = sorted([
+                delta
+                for size, delta
+                in (xPlaDevice if xPlaDevice else ()) +
+                (yPlaDevice if yPlaDevice else ()) +
+                (xAdvDevice if xAdvDevice else ()) +
+                (yAdvDevice if yAdvDevice else ())])
+            if allDeltas[0] < -128 or allDeltas[-1] > 127:
+                raise FeatureLibError(
+                    "Device value out of valid range (-128..127)",
+                    self.cur_token_location_)
+        else:
+            xPlaDevice, yPlaDevice, xAdvDevice, yAdvDevice = (
+                None, None, None, None)
+
+        self.expect_symbol_(">")
+        return self.ast.ValueRecord(
+            xPlacement, yPlacement, xAdvance, yAdvance,
+            xPlaDevice, yPlaDevice, xAdvDevice, yAdvDevice,
+            vertical=vertical, location=location)
+
+    def parse_valuerecord_definition_(self, vertical):
+        assert self.is_cur_keyword_("valueRecordDef")
+        location = self.cur_token_location_
+        value = self.parse_valuerecord_(vertical)
+        name = self.expect_name_()
+        self.expect_symbol_(";")
+        vrd = self.ast.ValueRecordDefinition(name, value, location=location)
+        self.valuerecords_.define(name, vrd)
+        return vrd
+
+    def parse_languagesystem_(self):
+        assert self.cur_token_ == "languagesystem"
+        location = self.cur_token_location_
+        script = self.expect_script_tag_()
+        language = self.expect_language_tag_()
+        self.expect_symbol_(";")
+        if script == "DFLT" and language != "dflt":
+            raise FeatureLibError(
+                'For script "DFLT", the language must be "dflt"',
+                self.cur_token_location_)
+        return self.ast.LanguageSystemStatement(script, language,
+                                                location=location)
+
+    def parse_feature_block_(self):
+        assert self.cur_token_ == "feature"
+        location = self.cur_token_location_
+        tag = self.expect_tag_()
+        vertical = (tag in {"vkrn", "vpal", "vhal", "valt"})
+
+        stylisticset = None
+        cv_feature = None
+        size_feature = False
+        if tag in self.SS_FEATURE_TAGS:
+            stylisticset = tag
+        elif tag in self.CV_FEATURE_TAGS:
+            cv_feature = tag
+        elif tag == "size":
+            size_feature = True
+
+        use_extension = False
+        if self.next_token_ == "useExtension":
+            self.expect_keyword_("useExtension")
+            use_extension = True
+
+        block = self.ast.FeatureBlock(tag, use_extension=use_extension,
+                                      location=location)
+        self.parse_block_(block, vertical, stylisticset, size_feature,
+                          cv_feature)
+        return block
+
+    def parse_feature_reference_(self):
+        assert self.cur_token_ == "feature", self.cur_token_
+        location = self.cur_token_location_
+        featureName = self.expect_tag_()
+        self.expect_symbol_(";")
+        return self.ast.FeatureReferenceStatement(featureName,
+                                                  location=location)
+
+    def parse_featureNames_(self, tag):
+        assert self.cur_token_ == "featureNames", self.cur_token_
+        block = self.ast.NestedBlock(tag, self.cur_token_,
+                                     location=self.cur_token_location_)
+        self.expect_symbol_("{")
+        for symtab in self.symbol_tables_:
+            symtab.enter_scope()
+        while self.next_token_ != "}" or self.cur_comments_:
+            self.advance_lexer_(comments=True)
+            if self.cur_token_type_ is Lexer.COMMENT:
+                block.statements.append(self.ast.Comment(
+                    self.cur_token_, location=self.cur_token_location_))
+            elif self.is_cur_keyword_("name"):
+                location = self.cur_token_location_
+                platformID, platEncID, langID, string = self.parse_name_()
+                block.statements.append(
+                    self.ast.FeatureNameStatement(tag, platformID,
+                                                  platEncID, langID, string,
+                                                  location=location))
+            elif self.cur_token_ == ";":
+                continue
+            else:
+                raise FeatureLibError('Expected "name"',
+                                      self.cur_token_location_)
+        self.expect_symbol_("}")
+        for symtab in self.symbol_tables_:
+            symtab.exit_scope()
+        self.expect_symbol_(";")
+        return block
+
+    def parse_cvParameters_(self, tag):
+        assert self.cur_token_ == "cvParameters", self.cur_token_
+        block = self.ast.NestedBlock(tag, self.cur_token_,
+                                     location=self.cur_token_location_)
+        self.expect_symbol_("{")
+        for symtab in self.symbol_tables_:
+            symtab.enter_scope()
+
+        statements = block.statements
+        while self.next_token_ != "}" or self.cur_comments_:
+            self.advance_lexer_(comments=True)
+            if self.cur_token_type_ is Lexer.COMMENT:
+                statements.append(self.ast.Comment(
+                    self.cur_token_, location=self.cur_token_location_))
+            elif self.is_cur_keyword_({"FeatUILabelNameID",
+                                       "FeatUITooltipTextNameID",
+                                       "SampleTextNameID",
+                                       "ParamUILabelNameID"}):
+                statements.append(self.parse_cvNameIDs_(tag, self.cur_token_))
+            elif self.is_cur_keyword_("Character"):
+                statements.append(self.parse_cvCharacter_(tag))
+            elif self.cur_token_ == ";":
+                continue
+            else:
+                raise FeatureLibError(
+                    "Expected statement: got {} {}".format(
+                        self.cur_token_type_, self.cur_token_),
+                    self.cur_token_location_)
+
+        self.expect_symbol_("}")
+        for symtab in self.symbol_tables_:
+            symtab.exit_scope()
+        self.expect_symbol_(";")
+        return block
+
+    def parse_cvNameIDs_(self, tag, block_name):
+        assert self.cur_token_ == block_name, self.cur_token_
+        block = self.ast.NestedBlock(tag, block_name,
+                                     location=self.cur_token_location_)
+        self.expect_symbol_("{")
+        for symtab in self.symbol_tables_:
+            symtab.enter_scope()
+        while self.next_token_ != "}" or self.cur_comments_:
+            self.advance_lexer_(comments=True)
+            if self.cur_token_type_ is Lexer.COMMENT:
+                block.statements.append(self.ast.Comment(
+                    self.cur_token_, location=self.cur_token_location_))
+            elif self.is_cur_keyword_("name"):
+                location = self.cur_token_location_
+                platformID, platEncID, langID, string = self.parse_name_()
+                block.statements.append(
+                    self.ast.CVParametersNameStatement(
+                        tag, platformID, platEncID, langID, string,
+                        block_name, location=location))
+            elif self.cur_token_ == ";":
+                continue
+            else:
+                raise FeatureLibError('Expected "name"',
+                                      self.cur_token_location_)
+        self.expect_symbol_("}")
+        for symtab in self.symbol_tables_:
+            symtab.exit_scope()
+        self.expect_symbol_(";")
+        return block
+
+    def parse_cvCharacter_(self, tag):
+        assert self.cur_token_ == "Character", self.cur_token_
+        location, character = self.cur_token_location_, self.expect_decimal_or_hexadecimal_()
+        self.expect_symbol_(";")
+        if not (0xFFFFFF >= character >= 0):
+            raise FeatureLibError("Character value must be between "
+                                  "{:#x} and {:#x}".format(0, 0xFFFFFF),
+                                  location)
+        return self.ast.CharacterStatement(character, tag, location=location)
+
+    def parse_FontRevision_(self):
+        assert self.cur_token_ == "FontRevision", self.cur_token_
+        location, version = self.cur_token_location_, self.expect_float_()
+        self.expect_symbol_(";")
+        if version <= 0:
+            raise FeatureLibError("Font revision numbers must be positive",
+                                  location)
+        return self.ast.FontRevisionStatement(version, location=location)
+
+    def parse_block_(self, block, vertical, stylisticset=None,
+                     size_feature=False, cv_feature=None):
+        self.expect_symbol_("{")
+        for symtab in self.symbol_tables_:
+            symtab.enter_scope()
+
+        statements = block.statements
+        while self.next_token_ != "}" or self.cur_comments_:
+            self.advance_lexer_(comments=True)
+            if self.cur_token_type_ is Lexer.COMMENT:
+                statements.append(self.ast.Comment(
+                    self.cur_token_, location=self.cur_token_location_))
+            elif self.cur_token_type_ is Lexer.GLYPHCLASS:
+                statements.append(self.parse_glyphclass_definition_())
+            elif self.is_cur_keyword_("anchorDef"):
+                statements.append(self.parse_anchordef_())
+            elif self.is_cur_keyword_({"enum", "enumerate"}):
+                statements.append(self.parse_enumerate_(vertical=vertical))
+            elif self.is_cur_keyword_("feature"):
+                statements.append(self.parse_feature_reference_())
+            elif self.is_cur_keyword_("ignore"):
+                statements.append(self.parse_ignore_())
+            elif self.is_cur_keyword_("language"):
+                statements.append(self.parse_language_())
+            elif self.is_cur_keyword_("lookup"):
+                statements.append(self.parse_lookup_(vertical))
+            elif self.is_cur_keyword_("lookupflag"):
+                statements.append(self.parse_lookupflag_())
+            elif self.is_cur_keyword_("markClass"):
+                statements.append(self.parse_markClass_())
+            elif self.is_cur_keyword_({"pos", "position"}):
+                statements.append(
+                    self.parse_position_(enumerated=False, vertical=vertical))
+            elif self.is_cur_keyword_("script"):
+                statements.append(self.parse_script_())
+            elif (self.is_cur_keyword_({"sub", "substitute",
+                                        "rsub", "reversesub"})):
+                statements.append(self.parse_substitute_())
+            elif self.is_cur_keyword_("subtable"):
+                statements.append(self.parse_subtable_())
+            elif self.is_cur_keyword_("valueRecordDef"):
+                statements.append(self.parse_valuerecord_definition_(vertical))
+            elif stylisticset and self.is_cur_keyword_("featureNames"):
+                statements.append(self.parse_featureNames_(stylisticset))
+            elif cv_feature and self.is_cur_keyword_("cvParameters"):
+                statements.append(self.parse_cvParameters_(cv_feature))
+            elif size_feature and self.is_cur_keyword_("parameters"):
+                statements.append(self.parse_size_parameters_())
+            elif size_feature and self.is_cur_keyword_("sizemenuname"):
+                statements.append(self.parse_size_menuname_())
+            elif self.cur_token_type_ is Lexer.NAME and self.cur_token_ in self.extensions:
+                statements.append(self.extensions[self.cur_token_](self))
+            elif self.cur_token_ == ";":
+                continue
+            else:
+                raise FeatureLibError(
+                    "Expected glyph class definition or statement: got {} {}".format(self.cur_token_type_, self.cur_token_),
+                    self.cur_token_location_)
+
+        self.expect_symbol_("}")
+        for symtab in self.symbol_tables_:
+            symtab.exit_scope()
+
+        name = self.expect_name_()
+        if name != block.name.strip():
+            raise FeatureLibError("Expected \"%s\"" % block.name.strip(),
+                                  self.cur_token_location_)
+        self.expect_symbol_(";")
+
+        # A multiple substitution may have a single destination, in which case
+        # it will look just like a single substitution. So if there are both
+        # multiple and single substitutions, upgrade all the single ones to
+        # multiple substitutions.
+
+        # Check if we have a mix of non-contextual singles and multiples.
+        has_single = False
+        has_multiple = False
+        for s in statements:
+            if isinstance(s, self.ast.SingleSubstStatement):
+                has_single = not any([s.prefix, s.suffix, s.forceChain])
+            elif isinstance(s, self.ast.MultipleSubstStatement):
+                has_multiple = not any([s.prefix, s.suffix])
+
+        # Upgrade all single substitutions to multiple substitutions.
+        if has_single and has_multiple:
+            for i, s in enumerate(statements):
+                if isinstance(s, self.ast.SingleSubstStatement):
+                    statements[i] = self.ast.MultipleSubstStatement(
+                        s.prefix, s.glyphs[0].glyphSet()[0], s.suffix,
+                        [r.glyphSet()[0] for r in s.replacements],
+                        location=s.location)
+
+    def is_cur_keyword_(self, k):
+        if self.cur_token_type_ is Lexer.NAME:
+            if isinstance(k, type("")):  # basestring is gone in Python3
+                return self.cur_token_ == k
+            else:
+                return self.cur_token_ in k
+        return False
+
+    def expect_class_name_(self):
+        self.advance_lexer_()
+        if self.cur_token_type_ is not Lexer.GLYPHCLASS:
+            raise FeatureLibError("Expected @NAME", self.cur_token_location_)
+        return self.cur_token_
+
+    def expect_cid_(self):
+        self.advance_lexer_()
+        if self.cur_token_type_ is Lexer.CID:
+            return self.cur_token_
+        raise FeatureLibError("Expected a CID", self.cur_token_location_)
+
+    def expect_filename_(self):
+        self.advance_lexer_()
+        if self.cur_token_type_ is not Lexer.FILENAME:
+            raise FeatureLibError("Expected file name",
+                                  self.cur_token_location_)
+        return self.cur_token_
+
+    def expect_glyph_(self):
+        self.advance_lexer_()
+        if self.cur_token_type_ is Lexer.NAME:
+            self.cur_token_ = self.cur_token_.lstrip("\\")
+            if len(self.cur_token_) > 63:
+                raise FeatureLibError(
+                    "Glyph names must not be longer than 63 characters",
+                    self.cur_token_location_)
+            return self.cur_token_
+        elif self.cur_token_type_ is Lexer.CID:
+            return "cid%05d" % self.cur_token_
+        raise FeatureLibError("Expected a glyph name or CID",
+                              self.cur_token_location_)
+
+    def expect_markClass_reference_(self):
+        name = self.expect_class_name_()
+        mc = self.glyphclasses_.resolve(name)
+        if mc is None:
+            raise FeatureLibError("Unknown markClass @%s" % name,
+                                  self.cur_token_location_)
+        if not isinstance(mc, self.ast.MarkClass):
+            raise FeatureLibError("@%s is not a markClass" % name,
+                                  self.cur_token_location_)
+        return mc
+
+    def expect_tag_(self):
+        self.advance_lexer_()
+        if self.cur_token_type_ is not Lexer.NAME:
+            raise FeatureLibError("Expected a tag", self.cur_token_location_)
+        if len(self.cur_token_) > 4:
+            raise FeatureLibError("Tags can not be longer than 4 characters",
+                                  self.cur_token_location_)
+        return (self.cur_token_ + "    ")[:4]
+
+    def expect_script_tag_(self):
+        tag = self.expect_tag_()
+        if tag == "dflt":
+            raise FeatureLibError(
+                '"dflt" is not a valid script tag; use "DFLT" instead',
+                self.cur_token_location_)
+        return tag
+
+    def expect_language_tag_(self):
+        tag = self.expect_tag_()
+        if tag == "DFLT":
+            raise FeatureLibError(
+                '"DFLT" is not a valid language tag; use "dflt" instead',
+                self.cur_token_location_)
+        return tag
+
+    def expect_symbol_(self, symbol):
+        self.advance_lexer_()
+        if self.cur_token_type_ is Lexer.SYMBOL and self.cur_token_ == symbol:
+            return symbol
+        raise FeatureLibError("Expected '%s'" % symbol,
+                              self.cur_token_location_)
+
+    def expect_keyword_(self, keyword):
+        self.advance_lexer_()
+        if self.cur_token_type_ is Lexer.NAME and self.cur_token_ == keyword:
+            return self.cur_token_
+        raise FeatureLibError("Expected \"%s\"" % keyword,
+                              self.cur_token_location_)
+
+    def expect_name_(self):
+        self.advance_lexer_()
+        if self.cur_token_type_ is Lexer.NAME:
+            return self.cur_token_
+        raise FeatureLibError("Expected a name", self.cur_token_location_)
+
+    # TODO: Don't allow this method to accept hexadecimal values
+    def expect_number_(self):
+        self.advance_lexer_()
+        if self.cur_token_type_ is Lexer.NUMBER:
+            return self.cur_token_
+        raise FeatureLibError("Expected a number", self.cur_token_location_)
+
+    def expect_float_(self):
+        self.advance_lexer_()
+        if self.cur_token_type_ is Lexer.FLOAT:
+            return self.cur_token_
+        raise FeatureLibError("Expected a floating-point number",
+                              self.cur_token_location_)
+
+    # TODO: Don't allow this method to accept hexadecimal values
+    def expect_decipoint_(self):
+        if self.next_token_type_ == Lexer.FLOAT:
+            return self.expect_float_()
+        elif self.next_token_type_ is Lexer.NUMBER:
+            return self.expect_number_() / 10
+        else:
+            raise FeatureLibError("Expected an integer or floating-point number",
+                                  self.cur_token_location_)
+
+    def expect_decimal_or_hexadecimal_(self):
+        # the lexer returns the same token type 'NUMBER' for either decimal or
+        # hexadecimal integers, and casts them both to a `int` type, so it's
+        # impossible to distinguish the two here. This method is implemented
+        # the same as `expect_number_`, only it gives a more informative
+        # error message
+        self.advance_lexer_()
+        if self.cur_token_type_ is Lexer.NUMBER:
+            return self.cur_token_
+        raise FeatureLibError("Expected a decimal or hexadecimal number",
+                              self.cur_token_location_)
+
+    def expect_string_(self):
+        self.advance_lexer_()
+        if self.cur_token_type_ is Lexer.STRING:
+            return self.cur_token_
+        raise FeatureLibError("Expected a string", self.cur_token_location_)
+
+    def advance_lexer_(self, comments=False):
+        if comments and self.cur_comments_:
+            self.cur_token_type_ = Lexer.COMMENT
+            self.cur_token_, self.cur_token_location_ = self.cur_comments_.pop(0)
+            return
+        else:
+            self.cur_token_type_, self.cur_token_, self.cur_token_location_ = (
+                self.next_token_type_, self.next_token_, self.next_token_location_)
+        while True:
+            try:
+                (self.next_token_type_, self.next_token_,
+                 self.next_token_location_) = next(self.lexer_)
+            except StopIteration:
+                self.next_token_type_, self.next_token_ = (None, None)
+            if self.next_token_type_ != Lexer.COMMENT:
+                break
+            self.cur_comments_.append((self.next_token_, self.next_token_location_))
+
+    @staticmethod
+    def reverse_string_(s):
+        """'abc' --> 'cba'"""
+        return ''.join(reversed(list(s)))
+
+    def make_cid_range_(self, location, start, limit):
+        """(location, 999, 1001) --> ["cid00999", "cid01000", "cid01001"]"""
+        result = list()
+        if start > limit:
+            raise FeatureLibError(
+                "Bad range: start should be less than limit", location)
+        for cid in range(start, limit + 1):
+            result.append("cid%05d" % cid)
+        return result
+
+    def make_glyph_range_(self, location, start, limit):
+        """(location, "a.sc", "d.sc") --> ["a.sc", "b.sc", "c.sc", "d.sc"]"""
+        result = list()
+        if len(start) != len(limit):
+            raise FeatureLibError(
+                "Bad range: \"%s\" and \"%s\" should have the same length" %
+                (start, limit), location)
+
+        rev = self.reverse_string_
+        prefix = os.path.commonprefix([start, limit])
+        suffix = rev(os.path.commonprefix([rev(start), rev(limit)]))
+        if len(suffix) > 0:
+            start_range = start[len(prefix):-len(suffix)]
+            limit_range = limit[len(prefix):-len(suffix)]
+        else:
+            start_range = start[len(prefix):]
+            limit_range = limit[len(prefix):]
+
+        if start_range >= limit_range:
+            raise FeatureLibError(
+                "Start of range must be smaller than its end",
+                location)
+
+        uppercase = re.compile(r'^[A-Z]$')
+        if uppercase.match(start_range) and uppercase.match(limit_range):
+            for c in range(ord(start_range), ord(limit_range) + 1):
+                result.append("%s%c%s" % (prefix, c, suffix))
+            return result
+
+        lowercase = re.compile(r'^[a-z]$')
+        if lowercase.match(start_range) and lowercase.match(limit_range):
+            for c in range(ord(start_range), ord(limit_range) + 1):
+                result.append("%s%c%s" % (prefix, c, suffix))
+            return result
+
+        digits = re.compile(r'^[0-9]{1,3}$')
+        if digits.match(start_range) and digits.match(limit_range):
+            for i in range(int(start_range, 10), int(limit_range, 10) + 1):
+                number = ("000" + str(i))[-len(start_range):]
+                result.append("%s%s%s" % (prefix, number, suffix))
+            return result
+
+        raise FeatureLibError("Bad range: \"%s-%s\"" % (start, limit),
+                              location)
+
+
+class SymbolTable(object):
+    def __init__(self):
+        self.scopes_ = [{}]
+
+    def enter_scope(self):
+        self.scopes_.append({})
+
+    def exit_scope(self):
+        self.scopes_.pop()
+
+    def define(self, name, item):
+        self.scopes_[-1][name] = item
+
+    def resolve(self, name):
+        for scope in reversed(self.scopes_):
+            item = scope.get(name)
+            if item:
+                return item
+        return None
diff --git a/Lib/fontTools/inspect.py b/Lib/fontTools/inspect.py
index 875736d..bdacadf 100644
--- a/Lib/fontTools/inspect.py
+++ b/Lib/fontTools/inspect.py
@@ -8,13 +8,18 @@
 from __future__ import print_function, division, absolute_import
 from fontTools.misc.py23 import *
 from fontTools import misc, ttLib, cffLib
-import pygtk
-pygtk.require('2.0')
+try:
+    from gi import pygtkcompat
+except ImportError:
+    pygtkcompat = None
+
+if pygtkcompat is not None:
+    pygtkcompat.enable()
+    pygtkcompat.enable_gtk(version='3.0')
 import gtk
 import sys
 
 
-
 class Row(object):
 	def __init__(self, parent, index, key, value, font):
 		self._parent = parent
@@ -74,7 +79,7 @@
 	def _add_object(self, key, value):
 		# Make sure item is decompiled
 		try:
-			value["asdf"]
+			value.asdf # Any better way?!
 		except (AttributeError, KeyError, TypeError, ttLib.TTLibError):
 			pass
 		if isinstance(value, ttLib.getTableModule('glyf').Glyph):
@@ -110,8 +115,7 @@
 		if len(value) and len(value) <= 32:
 			self._value_str = str(value)
 		else:
-			self._value_str = '%s of %d items' % (value.__class__.__name__,
-							      len(value))
+			self._value_str = '%s of %d items' % (value.__class__.__name__, len(value))
 		self._items = list(enumerate(value))
 
 	def __len__(self):
@@ -253,13 +257,15 @@
 		self.scrolled_window.add(self.treeview)
 		self.window.show_all()
 
-def main(args):
+def main(args=None):
+	if args is None:
+		args = sys.argv[1:]
 	if len(args) < 1:
 		print("usage: pyftinspect font...", file=sys.stderr)
-		sys.exit(1)
+		return 1
 	for arg in args:
 		Inspect(arg)
 	gtk.main()
 
 if __name__ == "__main__":
-	main(sys.argv[1:])
+	sys.exit(main())
diff --git a/Lib/fontTools/merge.py b/Lib/fontTools/merge.py
index 54e91e3..7dcb8b5 100644
--- a/Lib/fontTools/merge.py
+++ b/Lib/fontTools/merge.py
@@ -7,13 +7,21 @@
 
 from __future__ import print_function, division, absolute_import
 from fontTools.misc.py23 import *
+from fontTools.misc.timeTools import timestampNow
 from fontTools import ttLib, cffLib
 from fontTools.ttLib.tables import otTables, _h_e_a_d
 from fontTools.ttLib.tables.DefaultTable import DefaultTable
+from fontTools.misc.loggingTools import Timer
+from fontTools.pens.recordingPen import DecomposingRecordingPen
 from functools import reduce
 import sys
 import time
 import operator
+import logging
+
+
+log = logging.getLogger("fontTools.merge")
+timer = Timer(logger=logging.getLogger(__name__+".timer"), level=logging.INFO)
 
 
 def _add_method(*clazzes, **kwargs):
@@ -21,11 +29,13 @@
 	more classes."""
 	allowDefault = kwargs.get('allowDefaultTable', False)
 	def wrapper(method):
+		done = []
 		for clazz in clazzes:
+			if clazz in done: continue # Support multiple names of a clazz
+			done.append(clazz)
 			assert allowDefault or clazz != DefaultTable, 'Oops, table class not found.'
 			assert method.__name__ not in clazz.__dict__, \
-				"Oops, class '%s' has method '%s'." % (clazz.__name__,
-								       method.__name__)
+				"Oops, class '%s' has method '%s'." % (clazz.__name__, method.__name__)
 			setattr(clazz, method.__name__, method)
 		return None
 	return wrapper
@@ -46,7 +56,7 @@
 	return NotImplemented
 
 def current_time(lst):
-	return int(time.time() - _h_e_a_d.mac_epoch_diff)
+	return timestampNow()
 
 def bitwise_and(lst):
 	return reduce(operator.and_, lst)
@@ -141,7 +151,7 @@
 @_add_method(DefaultTable, allowDefaultTable=True)
 def merge(self, m, tables):
 	if not hasattr(self, 'mergeMap'):
-		m.log("Don't know how to merge '%s'." % self.tableTag)
+		log.info("Don't know how to merge '%s'.", self.tableTag)
 		return NotImplemented
 
 	logic = self.mergeMap
@@ -307,12 +317,6 @@
 	'metrics': sumDicts,
 }
 
-ttLib.getTableClass('gasp').mergeMap = {
-	'tableTag': equal,
-	'version': max,
-	'gaspRange': first, # FIXME? Appears irreconcilable
-}
-
 ttLib.getTableClass('name').mergeMap = {
 	'tableTag': equal,
 	'names': first, # FIXME? Does mixing name records make sense?
@@ -346,48 +350,186 @@
 ttLib.getTableClass('prep').mergeMap = lambda self, lst: first(lst)
 ttLib.getTableClass('fpgm').mergeMap = lambda self, lst: first(lst)
 ttLib.getTableClass('cvt ').mergeMap = lambda self, lst: first(lst)
+ttLib.getTableClass('gasp').mergeMap = lambda self, lst: first(lst) # FIXME? Appears irreconcilable
+
+def _glyphsAreSame(glyphSet1, glyphSet2, glyph1, glyph2):
+	pen1 = DecomposingRecordingPen(glyphSet1)
+	pen2 = DecomposingRecordingPen(glyphSet2)
+	g1 = glyphSet1[glyph1]
+	g2 = glyphSet2[glyph2]
+	g1.draw(pen1)
+	g2.draw(pen2)
+	return (pen1.value == pen2.value and
+		g1.width == g2.width and
+		(not hasattr(g1, 'height') or g1.height == g2.height))
 
 @_add_method(ttLib.getTableClass('cmap'))
 def merge(self, m, tables):
 	# TODO Handle format=14.
-	cmapTables = [(t,fontIdx) for fontIdx,table in enumerate(tables) for t in table.tables
-		      if t.isUnicode()]
-	# TODO Better handle format-4 and format-12 coexisting in same font.
-	# TODO Insert both a format-4 and format-12 if needed.
-	module = ttLib.getTableModule('cmap')
-	assert all(t.format in [4, 12] for t,_ in cmapTables)
-	format = max(t.format for t,_ in cmapTables)
-	cmapTable = module.cmap_classes[format](format)
-	cmapTable.cmap = {}
-	cmapTable.platformID = 3
-	cmapTable.platEncID = max(t.platEncID for t,_ in cmapTables)
-	cmapTable.language = 0
-	cmap = cmapTable.cmap
+	# Only merges 4/3/1 and 12/3/10 subtables, ignores all other subtables
+	# If there is a format 12 table for the same font, ignore the format 4 table
+	cmapTables = []
+	for fontIdx,table in enumerate(tables):
+		format4 = None
+		format12 = None
+		for subtable in table.tables:
+			properties = (subtable.format, subtable.platformID, subtable.platEncID)
+			if properties == (4,3,1):
+				format4 = subtable
+			elif properties == (12,3,10):
+				format12 = subtable
+		if format12 is not None:
+			cmapTables.append((format12, fontIdx))
+		elif format4 is not None:
+			cmapTables.append((format4, fontIdx))
+
+	# Build a unicode mapping, then decide which format is needed to store it.
+	cmap = {}
+	fontIndexForGlyph = {}
+	glyphSets = [None for f in m.fonts] if hasattr(m, 'fonts') else None
 	for table,fontIdx in cmapTables:
-		# TODO handle duplicates.
+		# handle duplicates
 		for uni,gid in table.cmap.items():
 			oldgid = cmap.get(uni, None)
 			if oldgid is None:
 				cmap[uni] = gid
+				fontIndexForGlyph[gid] = fontIdx
 			elif oldgid != gid:
 				# Char previously mapped to oldgid, now to gid.
 				# Record, to fix up in GSUB 'locl' later.
-				assert m.duplicateGlyphsPerFont[fontIdx].get(oldgid, gid) == gid
-				m.duplicateGlyphsPerFont[fontIdx][oldgid] = gid
+				if m.duplicateGlyphsPerFont[fontIdx].get(oldgid) is None:
+					if glyphSets is not None:
+						oldFontIdx = fontIndexForGlyph[oldgid]
+						for idx in (fontIdx, oldFontIdx):
+							if glyphSets[idx] is None:
+								glyphSets[idx] = m.fonts[idx].getGlyphSet()
+						if _glyphsAreSame(glyphSets[oldFontIdx], glyphSets[fontIdx], oldgid, gid):
+							continue
+					m.duplicateGlyphsPerFont[fontIdx][oldgid] = gid
+				elif m.duplicateGlyphsPerFont[fontIdx][oldgid] != gid:
+					# Char previously mapped to oldgid but oldgid is already remapped to a different
+					# gid, because of another Unicode character.
+					# TODO: Try harder to do something about these.
+					log.warning("Dropped mapping from codepoint %#06X to glyphId '%s'", uni, gid)
+
+	cmapBmpOnly = {uni: gid for uni,gid in cmap.items() if uni <= 0xFFFF}
+	self.tables = []
+	module = ttLib.getTableModule('cmap')
+	if len(cmapBmpOnly) != len(cmap):
+		# format-12 required.
+		cmapTable = module.cmap_classes[12](12)
+		cmapTable.platformID = 3
+		cmapTable.platEncID = 10
+		cmapTable.language = 0
+		cmapTable.cmap = cmap
+		self.tables.append(cmapTable)
+	# always create format-4
+	cmapTable = module.cmap_classes[4](4)
+	cmapTable.platformID = 3
+	cmapTable.platEncID = 1
+	cmapTable.language = 0
+	cmapTable.cmap = cmapBmpOnly
+	# ordered by platform then encoding
+	self.tables.insert(0, cmapTable)
 	self.tableVersion = 0
-	self.tables = [cmapTable]
 	self.numSubTables = len(self.tables)
 	return self
 
 
+def mergeLookupLists(lst):
+	# TODO Do smarter merge.
+	return sumLists(lst)
+
+def mergeFeatures(lst):
+	assert lst
+	self = otTables.Feature()
+	self.FeatureParams = None
+	self.LookupListIndex = mergeLookupLists([l.LookupListIndex for l in lst if l.LookupListIndex])
+	self.LookupCount = len(self.LookupListIndex)
+	return self
+
+def mergeFeatureLists(lst):
+	d = {}
+	for l in lst:
+		for f in l:
+			tag = f.FeatureTag
+			if tag not in d:
+				d[tag] = []
+			d[tag].append(f.Feature)
+	ret = []
+	for tag in sorted(d.keys()):
+		rec = otTables.FeatureRecord()
+		rec.FeatureTag = tag
+		rec.Feature = mergeFeatures(d[tag])
+		ret.append(rec)
+	return ret
+
+def mergeLangSyses(lst):
+	assert lst
+
+	# TODO Support merging ReqFeatureIndex
+	assert all(l.ReqFeatureIndex == 0xFFFF for l in lst)
+
+	self = otTables.LangSys()
+	self.LookupOrder = None
+	self.ReqFeatureIndex = 0xFFFF
+	self.FeatureIndex = mergeFeatureLists([l.FeatureIndex for l in lst if l.FeatureIndex])
+	self.FeatureCount = len(self.FeatureIndex)
+	return self
+
+def mergeScripts(lst):
+	assert lst
+
+	if len(lst) == 1:
+		return lst[0]
+	langSyses = {}
+	for sr in lst:
+		for lsr in sr.LangSysRecord:
+			if lsr.LangSysTag not in langSyses:
+				langSyses[lsr.LangSysTag] = []
+			langSyses[lsr.LangSysTag].append(lsr.LangSys)
+	lsrecords = []
+	for tag, langSys_list in sorted(langSyses.items()):
+		lsr = otTables.LangSysRecord()
+		lsr.LangSys = mergeLangSyses(langSys_list)
+		lsr.LangSysTag = tag
+		lsrecords.append(lsr)
+
+	self = otTables.Script()
+	self.LangSysRecord = lsrecords
+	self.LangSysCount = len(lsrecords)
+	self.DefaultLangSys = mergeLangSyses([s.DefaultLangSys for s in lst if s.DefaultLangSys])
+	return self
+
+def mergeScriptRecords(lst):
+	d = {}
+	for l in lst:
+		for s in l:
+			tag = s.ScriptTag
+			if tag not in d:
+				d[tag] = []
+			d[tag].append(s.Script)
+	ret = []
+	for tag in sorted(d.keys()):
+		rec = otTables.ScriptRecord()
+		rec.ScriptTag = tag
+		rec.Script = mergeScripts(d[tag])
+		ret.append(rec)
+	return ret
+
 otTables.ScriptList.mergeMap = {
-	'ScriptCount': sum,
-	'ScriptRecord': lambda lst: sorted(sumLists(lst), key=lambda s: s.ScriptTag),
+	'ScriptCount': lambda lst: None, # TODO
+	'ScriptRecord': mergeScriptRecords,
+}
+otTables.BaseScriptList.mergeMap = {
+	'BaseScriptCount': lambda lst: None, # TODO
+	# TODO: Merge duplicate entries
+	'BaseScriptRecord': lambda lst: sorted(sumLists(lst), key=lambda s: s.BaseScriptTag),
 }
 
 otTables.FeatureList.mergeMap = {
 	'FeatureCount': sum,
-	'FeatureRecord': sumLists,
+	'FeatureRecord': lambda lst: sorted(sumLists(lst), key=lambda s: s.FeatureTag),
 }
 
 otTables.LookupList.mergeMap = {
@@ -396,10 +538,12 @@
 }
 
 otTables.Coverage.mergeMap = {
+	'Format': min,
 	'glyphs': sumLists,
 }
 
 otTables.ClassDef.mergeMap = {
+	'Format': min,
 	'classDefs': sumDicts,
 }
 
@@ -422,12 +566,23 @@
 	'Coverage': sumLists,
 }
 
-otTables.GDEF.mergeMap = {
+otTables.Axis.mergeMap = {
 	'*': mergeObjects,
-	'Version': max,
 }
 
-otTables.GSUB.mergeMap = otTables.GPOS.mergeMap = {
+# XXX Fix BASE table merging
+otTables.BaseTagList.mergeMap = {
+	'BaseTagCount': sum,
+	'BaselineTag': sumLists,
+}
+
+otTables.GDEF.mergeMap = \
+otTables.GSUB.mergeMap = \
+otTables.GPOS.mergeMap = \
+otTables.BASE.mergeMap = \
+otTables.JSTF.mergeMap = \
+otTables.MATH.mergeMap = \
+{
 	'*': mergeObjects,
 	'Version': max,
 }
@@ -449,15 +604,14 @@
 	assert len(tables) == len(m.duplicateGlyphsPerFont)
 	for i,(table,dups) in enumerate(zip(tables, m.duplicateGlyphsPerFont)):
 		if not dups: continue
-		assert (table is not None and table is not NotImplemented), "Have duplicates to resolve for font %d but no GSUB" % (i + 1)
-		lookupMap = dict((id(v),v) for v in table.table.LookupList.Lookup)
-		featureMap = dict((id(v),v) for v in table.table.FeatureList.FeatureRecord)
+		assert (table is not None and table is not NotImplemented), "Have duplicates to resolve for font %d but no GSUB: %s" % (i + 1, dups)
 		synthFeature = None
 		synthLookup = None
 		for script in table.table.ScriptList.ScriptRecord:
 			if script.ScriptTag == 'DFLT': continue # XXX
 			for langsys in [script.Script.DefaultLangSys] + [l.LangSys for l in script.Script.LangSysRecord]:
-				feature = [featureMap[v] for v in langsys.FeatureIndex if featureMap[v].FeatureTag == 'locl']
+				if langsys is None: continue # XXX Create!
+				feature = [v for v in langsys.FeatureIndex if v.FeatureTag == 'locl']
 				assert len(feature) <= 1
 				if feature:
 					feature = feature[0]
@@ -469,9 +623,8 @@
 						f.FeatureParams = None
 						f.LookupCount = 0
 						f.LookupListIndex = []
-						langsys.FeatureIndex.append(id(synthFeature))
-						featureMap[id(synthFeature)] = synthFeature
-						langsys.FeatureIndex.sort(key=lambda v: featureMap[v].FeatureTag)
+						langsys.FeatureIndex.append(synthFeature)
+						langsys.FeatureIndex.sort(key=lambda v: v.FeatureTag)
 						table.table.FeatureList.FeatureRecord.append(synthFeature)
 						table.table.FeatureList.FeatureCount += 1
 					feature = synthFeature
@@ -484,98 +637,110 @@
 					synthLookup.LookupType = 1
 					synthLookup.SubTableCount = 1
 					synthLookup.SubTable = [subtable]
+					if table.table.LookupList is None:
+						# mtiLib uses None as default value for LookupList,
+						# while feaLib points to an empty array with count 0
+						# TODO: make them do the same
+						table.table.LookupList = otTables.LookupList()
+						table.table.LookupList.Lookup = []
+						table.table.LookupList.LookupCount = 0
 					table.table.LookupList.Lookup.append(synthLookup)
 					table.table.LookupList.LookupCount += 1
 
-				feature.Feature.LookupListIndex[:0] = [id(synthLookup)]
+				feature.Feature.LookupListIndex[:0] = [synthLookup]
 				feature.Feature.LookupCount += 1
 
-
 	DefaultTable.merge(self, m, tables)
 	return self
 
-
-
 @_add_method(otTables.SingleSubst,
-             otTables.MultipleSubst,
-             otTables.AlternateSubst,
-             otTables.LigatureSubst,
-             otTables.ReverseChainSingleSubst,
-             otTables.SinglePos,
-             otTables.PairPos,
-             otTables.CursivePos,
-             otTables.MarkBasePos,
-             otTables.MarkLigPos,
-             otTables.MarkMarkPos)
+		otTables.MultipleSubst,
+		otTables.AlternateSubst,
+		otTables.LigatureSubst,
+		otTables.ReverseChainSingleSubst,
+		otTables.SinglePos,
+		otTables.PairPos,
+		otTables.CursivePos,
+		otTables.MarkBasePos,
+		otTables.MarkLigPos,
+		otTables.MarkMarkPos)
 def mapLookups(self, lookupMap):
-  pass
+	pass
 
 # Copied and trimmed down from subset.py
 @_add_method(otTables.ContextSubst,
-             otTables.ChainContextSubst,
-             otTables.ContextPos,
-             otTables.ChainContextPos)
-def __classify_context(self):
+		otTables.ChainContextSubst,
+		otTables.ContextPos,
+		otTables.ChainContextPos)
+def __merge_classify_context(self):
 
-  class ContextHelper(object):
-    def __init__(self, klass, Format):
-      if klass.__name__.endswith('Subst'):
-        Typ = 'Sub'
-        Type = 'Subst'
-      else:
-        Typ = 'Pos'
-        Type = 'Pos'
-      if klass.__name__.startswith('Chain'):
-        Chain = 'Chain'
-      else:
-        Chain = ''
-      ChainTyp = Chain+Typ
+	class ContextHelper(object):
+		def __init__(self, klass, Format):
+			if klass.__name__.endswith('Subst'):
+				Typ = 'Sub'
+				Type = 'Subst'
+			else:
+				Typ = 'Pos'
+				Type = 'Pos'
+			if klass.__name__.startswith('Chain'):
+				Chain = 'Chain'
+			else:
+				Chain = ''
+			ChainTyp = Chain+Typ
 
-      self.Typ = Typ
-      self.Type = Type
-      self.Chain = Chain
-      self.ChainTyp = ChainTyp
+			self.Typ = Typ
+			self.Type = Type
+			self.Chain = Chain
+			self.ChainTyp = ChainTyp
 
-      self.LookupRecord = Type+'LookupRecord'
+			self.LookupRecord = Type+'LookupRecord'
 
-      if Format == 1:
-        self.Rule = ChainTyp+'Rule'
-        self.RuleSet = ChainTyp+'RuleSet'
-      elif Format == 2:
-        self.Rule = ChainTyp+'ClassRule'
-        self.RuleSet = ChainTyp+'ClassSet'
+			if Format == 1:
+				self.Rule = ChainTyp+'Rule'
+				self.RuleSet = ChainTyp+'RuleSet'
+			elif Format == 2:
+				self.Rule = ChainTyp+'ClassRule'
+				self.RuleSet = ChainTyp+'ClassSet'
 
-  if self.Format not in [1, 2, 3]:
-    return None  # Don't shoot the messenger; let it go
-  if not hasattr(self.__class__, "__ContextHelpers"):
-    self.__class__.__ContextHelpers = {}
-  if self.Format not in self.__class__.__ContextHelpers:
-    helper = ContextHelper(self.__class__, self.Format)
-    self.__class__.__ContextHelpers[self.Format] = helper
-  return self.__class__.__ContextHelpers[self.Format]
+	if self.Format not in [1, 2, 3]:
+		return None  # Don't shoot the messenger; let it go
+	if not hasattr(self.__class__, "__ContextHelpers"):
+		self.__class__.__ContextHelpers = {}
+	if self.Format not in self.__class__.__ContextHelpers:
+		helper = ContextHelper(self.__class__, self.Format)
+		self.__class__.__ContextHelpers[self.Format] = helper
+	return self.__class__.__ContextHelpers[self.Format]
 
 
 @_add_method(otTables.ContextSubst,
-             otTables.ChainContextSubst,
-             otTables.ContextPos,
-             otTables.ChainContextPos)
+		otTables.ChainContextSubst,
+		otTables.ContextPos,
+		otTables.ChainContextPos)
 def mapLookups(self, lookupMap):
-  c = self.__classify_context()
+	c = self.__merge_classify_context()
 
-  if self.Format in [1, 2]:
-    for rs in getattr(self, c.RuleSet):
-      if not rs: continue
-      for r in getattr(rs, c.Rule):
-        if not r: continue
-        for ll in getattr(r, c.LookupRecord):
-          if not ll: continue
-          ll.LookupListIndex = lookupMap[ll.LookupListIndex]
-  elif self.Format == 3:
-    for ll in getattr(self, c.LookupRecord):
-      if not ll: continue
-      ll.LookupListIndex = lookupMap[ll.LookupListIndex]
-  else:
-    assert 0, "unknown format: %s" % self.Format
+	if self.Format in [1, 2]:
+		for rs in getattr(self, c.RuleSet):
+			if not rs: continue
+			for r in getattr(rs, c.Rule):
+				if not r: continue
+				for ll in getattr(r, c.LookupRecord):
+					if not ll: continue
+					ll.LookupListIndex = lookupMap[ll.LookupListIndex]
+	elif self.Format == 3:
+		for ll in getattr(self, c.LookupRecord):
+			if not ll: continue
+			ll.LookupListIndex = lookupMap[ll.LookupListIndex]
+	else:
+		assert 0, "unknown format: %s" % self.Format
+
+@_add_method(otTables.ExtensionSubst,
+		otTables.ExtensionPos)
+def mapLookups(self, lookupMap):
+	if self.Format == 1:
+		self.ExtSubTable.mapLookups(lookupMap)
+	else:
+		assert 0, "unknown format: %s" % self.Format
 
 @_add_method(otTables.Lookup)
 def mapLookups(self, lookupMap):
@@ -600,7 +765,7 @@
 		f.Feature.mapLookups(lookupMap)
 
 @_add_method(otTables.DefaultLangSys,
-             otTables.LangSys)
+		otTables.LangSys)
 def mapFeatures(self, featureMap):
 	self.FeatureIndex = [featureMap[i] for i in self.FeatureIndex]
 	if self.ReqFeatureIndex != 65535:
@@ -623,91 +788,135 @@
 
 class Options(object):
 
-  class UnknownOptionError(Exception):
-    pass
+	class UnknownOptionError(Exception):
+		pass
 
-  def __init__(self, **kwargs):
+	def __init__(self, **kwargs):
 
-    self.set(**kwargs)
+		self.verbose = False
+		self.timing = False
 
-  def set(self, **kwargs):
-    for k,v in kwargs.items():
-      if not hasattr(self, k):
-        raise self.UnknownOptionError("Unknown option '%s'" % k)
-      setattr(self, k, v)
+		self.set(**kwargs)
 
-  def parse_opts(self, argv, ignore_unknown=False):
-    ret = []
-    opts = {}
-    for a in argv:
-      orig_a = a
-      if not a.startswith('--'):
-        ret.append(a)
-        continue
-      a = a[2:]
-      i = a.find('=')
-      op = '='
-      if i == -1:
-        if a.startswith("no-"):
-          k = a[3:]
-          v = False
-        else:
-          k = a
-          v = True
-      else:
-        k = a[:i]
-        if k[-1] in "-+":
-          op = k[-1]+'='  # Ops is '-=' or '+=' now.
-          k = k[:-1]
-        v = a[i+1:]
-      k = k.replace('-', '_')
-      if not hasattr(self, k):
-        if ignore_unknown == True or k in ignore_unknown:
-          ret.append(orig_a)
-          continue
-        else:
-          raise self.UnknownOptionError("Unknown option '%s'" % a)
+	def set(self, **kwargs):
+		for k,v in kwargs.items():
+			if not hasattr(self, k):
+				raise self.UnknownOptionError("Unknown option '%s'" % k)
+			setattr(self, k, v)
 
-      ov = getattr(self, k)
-      if isinstance(ov, bool):
-        v = bool(v)
-      elif isinstance(ov, int):
-        v = int(v)
-      elif isinstance(ov, list):
-        vv = v.split(',')
-        if vv == ['']:
-          vv = []
-        vv = [int(x, 0) if len(x) and x[0] in "0123456789" else x for x in vv]
-        if op == '=':
-          v = vv
-        elif op == '+=':
-          v = ov
-          v.extend(vv)
-        elif op == '-=':
-          v = ov
-          for x in vv:
-            if x in v:
-              v.remove(x)
-        else:
-          assert 0
+	def parse_opts(self, argv, ignore_unknown=[]):
+		ret = []
+		opts = {}
+		for a in argv:
+			orig_a = a
+			if not a.startswith('--'):
+				ret.append(a)
+				continue
+			a = a[2:]
+			i = a.find('=')
+			op = '='
+			if i == -1:
+				if a.startswith("no-"):
+					k = a[3:]
+					v = False
+				else:
+					k = a
+					v = True
+			else:
+				k = a[:i]
+				if k[-1] in "-+":
+					op = k[-1]+'='  # Ops is '-=' or '+=' now.
+					k = k[:-1]
+				v = a[i+1:]
+			k = k.replace('-', '_')
+			if not hasattr(self, k):
+				if ignore_unknown is True or k in ignore_unknown:
+					ret.append(orig_a)
+					continue
+				else:
+					raise self.UnknownOptionError("Unknown option '%s'" % a)
 
-      opts[k] = v
-    self.set(**opts)
+			ov = getattr(self, k)
+			if isinstance(ov, bool):
+				v = bool(v)
+			elif isinstance(ov, int):
+				v = int(v)
+			elif isinstance(ov, list):
+				vv = v.split(',')
+				if vv == ['']:
+					vv = []
+				vv = [int(x, 0) if len(x) and x[0] in "0123456789" else x for x in vv]
+				if op == '=':
+					v = vv
+				elif op == '+=':
+					v = ov
+					v.extend(vv)
+				elif op == '-=':
+					v = ov
+					for x in vv:
+						if x in v:
+							v.remove(x)
+				else:
+					assert 0
 
-    return ret
+			opts[k] = v
+		self.set(**opts)
 
+		return ret
+
+class _AttendanceRecordingIdentityDict(object):
+	"""A dictionary-like object that records indices of items actually accessed
+	from a list."""
+
+	def __init__(self, lst):
+		self.l = lst
+		self.d = {id(v):i for i,v in enumerate(lst)}
+		self.s = set()
+
+	def __getitem__(self, v):
+		self.s.add(self.d[id(v)])
+		return v
+
+class _GregariousIdentityDict(object):
+	"""A dictionary-like object that welcomes guests without reservations and
+	adds them to the end of the guest list."""
+
+	def __init__(self, lst):
+		self.l = lst
+		self.s = set(id(v) for v in lst)
+
+	def __getitem__(self, v):
+		if id(v) not in self.s:
+			self.s.add(id(v))
+			self.l.append(v)
+		return v
+
+class _NonhashableDict(object):
+	"""A dictionary-like object mapping objects to values."""
+
+	def __init__(self, keys, values=None):
+		if values is None:
+			self.d = {id(v):i for i,v in enumerate(keys)}
+		else:
+			self.d = {id(k):v for k,v in zip(keys, values)}
+
+	def __getitem__(self, k):
+		return self.d[id(k)]
+
+	def __setitem__(self, k, v):
+		self.d[id(k)] = v
+
+	def __delitem__(self, k):
+		del self.d[id(k)]
 
 class Merger(object):
 
-	def __init__(self, options=None, log=None):
+	def __init__(self, options=None):
 
-		if not log:
-			log = Logger()
 		if not options:
 			options = Options()
 
 		self.options = options
-		self.log = log
 
 	def merge(self, fontfiles):
 
@@ -730,29 +939,37 @@
 		for font in fonts:
 			self._preMerge(font)
 
+		self.fonts = fonts
 		self.duplicateGlyphsPerFont = [{} for f in fonts]
 
 		allTags = reduce(set.union, (list(font.keys()) for font in fonts), set())
 		allTags.remove('GlyphOrder')
-		allTags.remove('cmap')
-		allTags.remove('GSUB')
-		allTags = ['cmap', 'GSUB'] + list(allTags)
+
+		# Make sure we process cmap before GSUB as we have a dependency there.
+		if 'GSUB' in allTags:
+			allTags.remove('GSUB')
+			allTags = ['GSUB'] + list(allTags)
+		if 'cmap' in allTags:
+			allTags.remove('cmap')
+			allTags = ['cmap'] + list(allTags)
+
 		for tag in allTags:
+			with timer("merge '%s'" % tag):
+				tables = [font.get(tag, NotImplemented) for font in fonts]
 
-			tables = [font.get(tag, NotImplemented) for font in fonts]
+				log.info("Merging '%s'.", tag)
+				clazz = ttLib.getTableClass(tag)
+				table = clazz(tag).merge(self, tables)
+				# XXX Clean this up and use:  table = mergeObjects(tables)
 
-			clazz = ttLib.getTableClass(tag)
-			table = clazz(tag).merge(self, tables)
-			# XXX Clean this up and use:  table = mergeObjects(tables)
-
-			if table is not NotImplemented and table is not False:
-				mega[tag] = table
-				self.log("Merged '%s'." % tag)
-			else:
-				self.log("Dropped '%s'." % tag)
-			self.log.lapse("merge '%s'" % tag)
+				if table is not NotImplemented and table is not False:
+					mega[tag] = table
+					log.info("Merged '%s'.", tag)
+				else:
+					log.info("Dropped '%s'.", tag)
 
 		del self.duplicateGlyphsPerFont
+		del self.fonts
 
 		self._postMerge(mega)
 
@@ -784,7 +1001,7 @@
 				try:
 					mergeLogic = logic['*']
 				except KeyError:
-					raise Exception("Don't know how to merge key %s of class %s" % 
+					raise Exception("Don't know how to merge key %s of class %s" %
 							(key, returnTable.__class__.__name__))
 			if mergeLogic is NotImplemented:
 				continue
@@ -806,14 +1023,12 @@
 			if not t: continue
 
 			if t.table.LookupList:
-				lookupMap = dict((i,id(v)) for i,v in enumerate(t.table.LookupList.Lookup))
+				lookupMap = {i:v for i,v in enumerate(t.table.LookupList.Lookup)}
 				t.table.LookupList.mapLookups(lookupMap)
-				if t.table.FeatureList:
-					# XXX Handle present FeatureList but absent LookupList
-					t.table.FeatureList.mapLookups(lookupMap)
+				t.table.FeatureList.mapLookups(lookupMap)
 
 			if t.table.FeatureList and t.table.ScriptList:
-				featureMap = dict((i,id(v)) for i,v in enumerate(t.table.FeatureList.FeatureRecord))
+				featureMap = {i:v for i,v in enumerate(t.table.FeatureList.FeatureRecord)}
 				t.table.ScriptList.mapFeatures(featureMap)
 
 		# TODO GDEF/Lookup MarkFilteringSets
@@ -830,92 +1045,85 @@
 		for t in [GSUB, GPOS]:
 			if not t: continue
 
-			if t.table.LookupList:
-				lookupMap = dict((id(v),i) for i,v in enumerate(t.table.LookupList.Lookup))
-				t.table.LookupList.mapLookups(lookupMap)
-				if t.table.FeatureList:
-					# XXX Handle present FeatureList but absent LookupList
-					t.table.FeatureList.mapLookups(lookupMap)
-
 			if t.table.FeatureList and t.table.ScriptList:
-				# XXX Handle present ScriptList but absent FeatureList
-				featureMap = dict((id(v),i) for i,v in enumerate(t.table.FeatureList.FeatureRecord))
+
+				# Collect unregistered (new) features.
+				featureMap = _GregariousIdentityDict(t.table.FeatureList.FeatureRecord)
 				t.table.ScriptList.mapFeatures(featureMap)
 
+				# Record used features.
+				featureMap = _AttendanceRecordingIdentityDict(t.table.FeatureList.FeatureRecord)
+				t.table.ScriptList.mapFeatures(featureMap)
+				usedIndices = featureMap.s
+
+				# Remove unused features
+				t.table.FeatureList.FeatureRecord = [f for i,f in enumerate(t.table.FeatureList.FeatureRecord) if i in usedIndices]
+
+				# Map back to indices.
+				featureMap = _NonhashableDict(t.table.FeatureList.FeatureRecord)
+				t.table.ScriptList.mapFeatures(featureMap)
+
+				t.table.FeatureList.FeatureCount = len(t.table.FeatureList.FeatureRecord)
+
+			if t.table.LookupList:
+
+				# Collect unregistered (new) lookups.
+				lookupMap = _GregariousIdentityDict(t.table.LookupList.Lookup)
+				t.table.FeatureList.mapLookups(lookupMap)
+				t.table.LookupList.mapLookups(lookupMap)
+
+				# Record used lookups.
+				lookupMap = _AttendanceRecordingIdentityDict(t.table.LookupList.Lookup)
+				t.table.FeatureList.mapLookups(lookupMap)
+				t.table.LookupList.mapLookups(lookupMap)
+				usedIndices = lookupMap.s
+
+				# Remove unused lookups
+				t.table.LookupList.Lookup = [l for i,l in enumerate(t.table.LookupList.Lookup) if i in usedIndices]
+
+				# Map back to indices.
+				lookupMap = _NonhashableDict(t.table.LookupList.Lookup)
+				t.table.FeatureList.mapLookups(lookupMap)
+				t.table.LookupList.mapLookups(lookupMap)
+
+				t.table.LookupList.LookupCount = len(t.table.LookupList.Lookup)
+
 		# TODO GDEF/Lookup MarkFilteringSets
 		# TODO FeatureParams nameIDs
 
 
-class Logger(object):
-
-  def __init__(self, verbose=False, xml=False, timing=False):
-    self.verbose = verbose
-    self.xml = xml
-    self.timing = timing
-    self.last_time = self.start_time = time.time()
-
-  def parse_opts(self, argv):
-    argv = argv[:]
-    for v in ['verbose', 'xml', 'timing']:
-      if "--"+v in argv:
-        setattr(self, v, True)
-        argv.remove("--"+v)
-    return argv
-
-  def __call__(self, *things):
-    if not self.verbose:
-      return
-    print(' '.join(str(x) for x in things))
-
-  def lapse(self, *things):
-    if not self.timing:
-      return
-    new_time = time.time()
-    print("Took %0.3fs to %s" %(new_time - self.last_time,
-                                 ' '.join(str(x) for x in things)))
-    self.last_time = new_time
-
-  def font(self, font, file=sys.stdout):
-    if not self.xml:
-      return
-    from fontTools.misc import xmlWriter
-    writer = xmlWriter.XMLWriter(file)
-    font.disassembleInstructions = False  # Work around ttLib bug
-    for tag in font.keys():
-      writer.begintag(tag)
-      writer.newline()
-      font[tag].toXML(writer, font)
-      writer.endtag(tag)
-      writer.newline()
-
-
 __all__ = [
-  'Options',
-  'Merger',
-  'Logger',
-  'main'
+	'Options',
+	'Merger',
+	'main'
 ]
 
-def main(args):
+@timer("make one with everything (TOTAL TIME)")
+def main(args=None):
+	from fontTools import configLogger
 
-	log = Logger()
-	args = log.parse_opts(args)
+	if args is None:
+		args = sys.argv[1:]
 
 	options = Options()
 	args = options.parse_opts(args)
 
 	if len(args) < 1:
 		print("usage: pyftmerge font...", file=sys.stderr)
-		sys.exit(1)
+		return 1
 
-	merger = Merger(options=options, log=log)
+	configLogger(level=logging.INFO if options.verbose else logging.WARNING)
+	if options.timing:
+		timer.logger.setLevel(logging.DEBUG)
+	else:
+		timer.logger.disabled = True
+
+	merger = Merger(options=options)
 	font = merger.merge(args)
 	outfile = 'merged.ttf'
-	font.save(outfile)
-	log.lapse("compile and save font")
+	with timer("compile and save font"):
+		font.save(outfile)
 
-	log.last_time = log.start_time
-	log.lapse("make one with everything(TOTAL TIME)")
 
 if __name__ == "__main__":
-	main(sys.argv[1:])
+	sys.exit(main())
diff --git a/Lib/fontTools/misc/__init__.py b/Lib/fontTools/misc/__init__.py
index e001bb2..3f9abc9 100644
--- a/Lib/fontTools/misc/__init__.py
+++ b/Lib/fontTools/misc/__init__.py
@@ -1,3 +1,4 @@
-"""Empty __init__.py file to signal Python this directory is a package.
-(It can't be completely empty since WinZip seems to skip empty files.)
-"""
+"""Empty __init__.py file to signal Python this directory is a package."""
+
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
diff --git a/Lib/fontTools/misc/arrayTools.py b/Lib/fontTools/misc/arrayTools.py
index 0daabd9..f2cfac8 100644
--- a/Lib/fontTools/misc/arrayTools.py
+++ b/Lib/fontTools/misc/arrayTools.py
@@ -6,7 +6,9 @@
 
 from __future__ import print_function, division, absolute_import
 from fontTools.misc.py23 import *
+from numbers import Number
 import math
+import operator
 
 def calcBounds(array):
     """Return the bounding rectangle of a 2D points array as a tuple:
@@ -21,13 +23,9 @@
 def calcIntBounds(array):
     """Return the integer bounding rectangle of a 2D points array as a
     tuple: (xMin, yMin, xMax, yMax)
+    Values are rounded to closest integer.
     """
-    xMin, yMin, xMax, yMax = calcBounds(array)
-    xMin = int(math.floor(xMin))
-    xMax = int(math.ceil(xMax))
-    yMin = int(math.floor(yMin))
-    yMax = int(math.ceil(yMax))
-    return xMin, yMin, xMax, yMax
+    return tuple(round(v) for v in calcBounds(array))
 
 
 def updateBounds(bounds, p, min=min, max=max):
@@ -43,7 +41,7 @@
     return (xMin <= x <= xMax) and (yMin <= y <= yMax)
 
 def pointsInRect(array, rect):
-    """Find out which points or array are inside rect. 
+    """Find out which points or array are inside rect.
     Returns an array with a boolean for each point.
     """
     if len(array) < 1:
@@ -59,7 +57,7 @@
 def asInt16(array):
     """Round and cast to 16 bit integer."""
     return [int(math.floor(i+0.5)) for i in array]
-    
+
 
 def normRect(rect):
     """Normalize the rectangle so that the following holds:
@@ -124,6 +122,136 @@
     return (xMin, yMin, xMax, yMax)
 
 
+class Vector(object):
+    """A math-like vector."""
+
+    def __init__(self, values, keep=False):
+        self.values = values if keep else list(values)
+
+    def __getitem__(self, index):
+        return self.values[index]
+
+    def __len__(self):
+        return len(self.values)
+
+    def __repr__(self):
+        return "Vector(%s)" % self.values
+
+    def _vectorOp(self, other, op):
+        if isinstance(other, Vector):
+            assert len(self.values) == len(other.values)
+            a = self.values
+            b = other.values
+            return [op(a[i], b[i]) for i in range(len(self.values))]
+        if isinstance(other, Number):
+            return [op(v, other) for v in self.values]
+        raise NotImplementedError
+
+    def _scalarOp(self, other, op):
+        if isinstance(other, Number):
+            return [op(v, other) for v in self.values]
+        raise NotImplementedError
+
+    def _unaryOp(self, op):
+        return [op(v) for v in self.values]
+
+    def __add__(self, other):
+        return Vector(self._vectorOp(other, operator.add), keep=True)
+    def __iadd__(self, other):
+        self.values = self._vectorOp(other, operator.add)
+        return self
+    __radd__ = __add__
+
+    def __sub__(self, other):
+        return Vector(self._vectorOp(other, operator.sub), keep=True)
+    def __isub__(self, other):
+        self.values = self._vectorOp(other, operator.sub)
+        return self
+    def __rsub__(self, other):
+        return other + (-self)
+
+    def __mul__(self, other):
+        return Vector(self._scalarOp(other, operator.mul), keep=True)
+    def __imul__(self, other):
+        self.values = self._scalarOp(other, operator.mul)
+        return self
+    __rmul__ = __mul__
+
+    def __truediv__(self, other):
+        return Vector(self._scalarOp(other, operator.div), keep=True)
+    def __itruediv__(self, other):
+        self.values = self._scalarOp(other, operator.div)
+        return self
+
+    def __pos__(self):
+        return Vector(self._unaryOp(operator.pos), keep=True)
+    def __neg__(self):
+        return Vector(self._unaryOp(operator.neg), keep=True)
+    def __round__(self):
+        return Vector(self._unaryOp(round), keep=True)
+    def toInt(self):
+        return self.__round__()
+
+    def __eq__(self, other):
+        if type(other) == Vector:
+            return self.values == other.values
+        else:
+            return self.values == other
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def __bool__(self):
+        return any(self.values)
+    __nonzero__ = __bool__
+
+    def __abs__(self):
+        return math.sqrt(sum([x*x for x in self.values]))
+    def dot(self, other):
+        a = self.values
+        b = other.values if type(other) == Vector else b
+        assert len(a) == len(b)
+        return sum([a[i] * b[i] for i in range(len(a))])
+
+
+def pairwise(iterable, reverse=False):
+    """Iterate over current and next items in iterable, optionally in
+    reverse order.
+
+    >>> tuple(pairwise([]))
+    ()
+    >>> tuple(pairwise([], reverse=True))
+    ()
+    >>> tuple(pairwise([0]))
+    ((0, 0),)
+    >>> tuple(pairwise([0], reverse=True))
+    ((0, 0),)
+    >>> tuple(pairwise([0, 1]))
+    ((0, 1), (1, 0))
+    >>> tuple(pairwise([0, 1], reverse=True))
+    ((1, 0), (0, 1))
+    >>> tuple(pairwise([0, 1, 2]))
+    ((0, 1), (1, 2), (2, 0))
+    >>> tuple(pairwise([0, 1, 2], reverse=True))
+    ((2, 1), (1, 0), (0, 2))
+    >>> tuple(pairwise(['a', 'b', 'c', 'd']))
+    (('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a'))
+    >>> tuple(pairwise(['a', 'b', 'c', 'd'], reverse=True))
+    (('d', 'c'), ('c', 'b'), ('b', 'a'), ('a', 'd'))
+    """
+    if not iterable:
+        return
+    if reverse:
+        it = reversed(iterable)
+    else:
+        it = iter(iterable)
+    first = next(it, None)
+    a = first
+    for b in it:
+        yield (a, b)
+        a = b
+    yield (a, first)
+
+
 def _test():
     """
     >>> import math
@@ -180,5 +308,6 @@
     """
 
 if __name__ == "__main__":
+    import sys
     import doctest
-    doctest.testmod()
+    sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/misc/bezierTools.py b/Lib/fontTools/misc/bezierTools.py
index 6d9f8ce..7305305 100644
--- a/Lib/fontTools/misc/bezierTools.py
+++ b/Lib/fontTools/misc/bezierTools.py
@@ -1,12 +1,24 @@
+# -*- coding: utf-8 -*-
 """fontTools.misc.bezierTools.py -- tools for working with bezier path segments.
 """
 
 from __future__ import print_function, division, absolute_import
+from fontTools.misc.arrayTools import calcBounds
 from fontTools.misc.py23 import *
+import math
+
 
 __all__ = [
-    "calcQuadraticBounds",
+    "approximateCubicArcLength",
+    "approximateCubicArcLengthC",
+    "approximateQuadraticArcLength",
+    "approximateQuadraticArcLengthC",
+    "calcCubicArcLength",
+    "calcCubicArcLengthC",
+    "calcQuadraticArcLength",
+    "calcQuadraticArcLengthC",
     "calcCubicBounds",
+    "calcQuadraticBounds",
     "splitLine",
     "splitQuadratic",
     "splitCubic",
@@ -16,9 +28,121 @@
     "solveCubic",
 ]
 
-from fontTools.misc.arrayTools import calcBounds
 
-epsilon = 1e-12
+def calcCubicArcLength(pt1, pt2, pt3, pt4, tolerance=0.005):
+    """Return the arc length for a cubic bezier segment."""
+    return calcCubicArcLengthC(complex(*pt1), complex(*pt2), complex(*pt3), complex(*pt4), tolerance)
+
+
+def _split_cubic_into_two(p0, p1, p2, p3):
+    mid = (p0 + 3 * (p1 + p2) + p3) * .125
+    deriv3 = (p3 + p2 - p1 - p0) * .125
+    return ((p0, (p0 + p1) * .5, mid - deriv3, mid),
+            (mid, mid + deriv3, (p2 + p3) * .5, p3))
+
+def _calcCubicArcLengthCRecurse(mult, p0, p1, p2, p3):
+	arch = abs(p0-p3)
+	box = abs(p0-p1) + abs(p1-p2) + abs(p2-p3)
+	if arch * mult >= box:
+		return (arch + box) * .5
+	else:
+		one,two = _split_cubic_into_two(p0,p1,p2,p3)
+		return _calcCubicArcLengthCRecurse(mult, *one) + _calcCubicArcLengthCRecurse(mult, *two)
+
+def calcCubicArcLengthC(pt1, pt2, pt3, pt4, tolerance=0.005):
+    """Return the arc length for a cubic bezier segment using complex points."""
+    mult = 1. + 1.5 * tolerance # The 1.5 is a empirical hack; no math
+    return _calcCubicArcLengthCRecurse(mult, pt1, pt2, pt3, pt4)
+
+
+epsilonDigits = 6
+epsilon = 1e-10
+
+
+def _dot(v1, v2):
+    return (v1 * v2.conjugate()).real
+
+
+def _intSecAtan(x):
+    # In : sympy.integrate(sp.sec(sp.atan(x)))
+    # Out: x*sqrt(x**2 + 1)/2 + asinh(x)/2
+    return x * math.sqrt(x**2 + 1)/2 + math.asinh(x)/2
+
+
+def calcQuadraticArcLength(pt1, pt2, pt3):
+    """Return the arc length for a qudratic bezier segment.
+    pt1 and pt3 are the "anchor" points, pt2 is the "handle".
+
+        >>> calcQuadraticArcLength((0, 0), (0, 0), (0, 0)) # empty segment
+        0.0
+        >>> calcQuadraticArcLength((0, 0), (50, 0), (80, 0)) # collinear points
+        80.0
+        >>> calcQuadraticArcLength((0, 0), (0, 50), (0, 80)) # collinear points vertical
+        80.0
+        >>> calcQuadraticArcLength((0, 0), (50, 20), (100, 40)) # collinear points
+        107.70329614269008
+        >>> calcQuadraticArcLength((0, 0), (0, 100), (100, 0))
+        154.02976155645263
+        >>> calcQuadraticArcLength((0, 0), (0, 50), (100, 0))
+        120.21581243984076
+        >>> calcQuadraticArcLength((0, 0), (50, -10), (80, 50))
+        102.53273816445825
+        >>> calcQuadraticArcLength((0, 0), (40, 0), (-40, 0)) # collinear points, control point outside
+        66.66666666666667
+        >>> calcQuadraticArcLength((0, 0), (40, 0), (0, 0)) # collinear points, looping back
+        40.0
+    """
+    return calcQuadraticArcLengthC(complex(*pt1), complex(*pt2), complex(*pt3))
+
+
+def calcQuadraticArcLengthC(pt1, pt2, pt3):
+    """Return the arc length for a qudratic bezier segment using complex points.
+    pt1 and pt3 are the "anchor" points, pt2 is the "handle"."""
+
+    # Analytical solution to the length of a quadratic bezier.
+    # I'll explain how I arrived at this later.
+    d0 = pt2 - pt1
+    d1 = pt3 - pt2
+    d = d1 - d0
+    n = d * 1j
+    scale = abs(n)
+    if scale == 0.:
+        return abs(pt3-pt1)
+    origDist = _dot(n,d0)
+    if abs(origDist) < epsilon:
+        if _dot(d0,d1) >= 0:
+            return abs(pt3-pt1)
+        a, b = abs(d0), abs(d1)
+        return (a*a + b*b) / (a+b)
+    x0 = _dot(d,d0) / origDist
+    x1 = _dot(d,d1) / origDist
+    Len = abs(2 * (_intSecAtan(x1) - _intSecAtan(x0)) * origDist / (scale * (x1 - x0)))
+    return Len
+
+
+def approximateQuadraticArcLength(pt1, pt2, pt3):
+    # Approximate length of quadratic Bezier curve using Gauss-Legendre quadrature
+    # with n=3 points.
+    return approximateQuadraticArcLengthC(complex(*pt1), complex(*pt2), complex(*pt3))
+
+
+def approximateQuadraticArcLengthC(pt1, pt2, pt3):
+    # Approximate length of quadratic Bezier curve using Gauss-Legendre quadrature
+    # with n=3 points for complex points.
+    #
+    # This, essentially, approximates the length-of-derivative function
+    # to be integrated with the best-matching fifth-degree polynomial
+    # approximation of it.
+    #
+    #https://en.wikipedia.org/wiki/Gaussian_quadrature#Gauss.E2.80.93Legendre_quadrature
+
+    # abs(BezierCurveC[2].diff(t).subs({t:T})) for T in sorted(.5, .5±sqrt(3/5)/2),
+    # weighted 5/18, 8/18, 5/18 respectively.
+    v0 = abs(-0.492943519233745*pt1 + 0.430331482911935*pt2 + 0.0626120363218102*pt3)
+    v1 = abs(pt3-pt1)*0.4444444444444444
+    v2 = abs(-0.0626120363218102*pt1 - 0.430331482911935*pt2 + 0.492943519233745*pt3)
+
+    return v0 + v1 + v2
 
 
 def calcQuadraticBounds(pt1, pt2, pt3):
@@ -42,6 +166,50 @@
     return calcBounds(points)
 
 
+def approximateCubicArcLength(pt1, pt2, pt3, pt4):
+    """Return the approximate arc length for a cubic bezier segment.
+    pt1 and pt4 are the "anchor" points, pt2 and pt3 are the "handles".
+
+        >>> approximateCubicArcLength((0, 0), (25, 100), (75, 100), (100, 0))
+        190.04332968932817
+        >>> approximateCubicArcLength((0, 0), (50, 0), (100, 50), (100, 100))
+        154.8852074945903
+        >>> approximateCubicArcLength((0, 0), (50, 0), (100, 0), (150, 0)) # line; exact result should be 150.
+        149.99999999999991
+        >>> approximateCubicArcLength((0, 0), (50, 0), (100, 0), (-50, 0)) # cusp; exact result should be 150.
+        136.9267662156362
+        >>> approximateCubicArcLength((0, 0), (50, 0), (100, -50), (-50, 0)) # cusp
+        154.80848416537057
+    """
+    # Approximate length of cubic Bezier curve using Gauss-Lobatto quadrature
+    # with n=5 points.
+    return approximateCubicArcLengthC(complex(*pt1), complex(*pt2), complex(*pt3), complex(*pt4))
+
+
+def approximateCubicArcLengthC(pt1, pt2, pt3, pt4):
+    """Return the approximate arc length for a cubic bezier segment of complex points.
+    pt1 and pt4 are the "anchor" points, pt2 and pt3 are the "handles"."""
+
+    # Approximate length of cubic Bezier curve using Gauss-Lobatto quadrature
+    # with n=5 points for complex points.
+    #
+    # This, essentially, approximates the length-of-derivative function
+    # to be integrated with the best-matching seventh-degree polynomial
+    # approximation of it.
+    #
+    # https://en.wikipedia.org/wiki/Gaussian_quadrature#Gauss.E2.80.93Lobatto_rules
+
+    # abs(BezierCurveC[3].diff(t).subs({t:T})) for T in sorted(0, .5±(3/7)**.5/2, .5, 1),
+    # weighted 1/20, 49/180, 32/90, 49/180, 1/20 respectively.
+    v0 = abs(pt2-pt1)*.15
+    v1 = abs(-0.558983582205757*pt1 + 0.325650248872424*pt2 + 0.208983582205757*pt3 + 0.024349751127576*pt4)
+    v2 = abs(pt4-pt1+pt3-pt2)*0.26666666666666666
+    v3 = abs(-0.024349751127576*pt1 - 0.208983582205757*pt2 - 0.325650248872424*pt3 + 0.558983582205757*pt4)
+    v4 = abs(pt4-pt3)*.15
+
+    return v0 + v1 + v2 + v3 + v4
+
+
 def calcCubicBounds(pt1, pt2, pt3, pt4):
     """Return the bounding rectangle for a cubic bezier segment.
     pt1 and pt4 are the "anchor" points, pt2 and pt3 are the "handles".
@@ -50,7 +218,7 @@
         (0, 0, 100, 75.0)
         >>> calcCubicBounds((0, 0), (50, 0), (100, 50), (100, 100))
         (0.0, 0.0, 100, 100)
-        >>> print "%f %f %f %f" % calcCubicBounds((50, 0), (0, 100), (100, 100), (50, 0))
+        >>> print("%f %f %f %f" % calcCubicBounds((50, 0), (0, 100), (100, 100), (50, 0)))
         35.566243 0.000000 64.433757 75.000000
     """
     (ax, ay), (bx, by), (cx, cy), (dx, dy) = calcCubicParameters(pt1, pt2, pt3, pt4)
@@ -62,7 +230,7 @@
     xRoots = [t for t in solveQuadratic(ax3, bx2, cx) if 0 <= t < 1]
     yRoots = [t for t in solveQuadratic(ay3, by2, cy) if 0 <= t < 1]
     roots = xRoots + yRoots
-    
+
     points = [(ax*t*t*t + bx*t*t + cx * t + dx, ay*t*t*t + by*t*t + cy * t + dy) for t in roots] + [pt1, pt4]
     return calcBounds(points)
 
@@ -75,16 +243,22 @@
     line.
 
         >>> printSegments(splitLine((0, 0), (100, 100), 50, True))
-        ((0, 0), (50.0, 50.0))
-        ((50.0, 50.0), (100, 100))
+        ((0, 0), (50, 50))
+        ((50, 50), (100, 100))
         >>> printSegments(splitLine((0, 0), (100, 100), 100, True))
         ((0, 0), (100, 100))
         >>> printSegments(splitLine((0, 0), (100, 100), 0, True))
-        ((0, 0), (0.0, 0.0))
-        ((0.0, 0.0), (100, 100))
+        ((0, 0), (0, 0))
+        ((0, 0), (100, 100))
         >>> printSegments(splitLine((0, 0), (100, 100), 0, False))
-        ((0, 0), (0.0, 0.0))
-        ((0.0, 0.0), (100, 100))
+        ((0, 0), (0, 0))
+        ((0, 0), (100, 100))
+        >>> printSegments(splitLine((100, 0), (0, 0), 50, False))
+        ((100, 0), (50, 0))
+        ((50, 0), (0, 0))
+        >>> printSegments(splitLine((0, 100), (0, 0), 50, True))
+        ((0, 100), (0, 50))
+        ((0, 50), (0, 0))
     """
     pt1x, pt1y = pt1
     pt2x, pt2y = pt2
@@ -95,10 +269,11 @@
     bx = pt1x
     by = pt1y
 
-    if ax == 0:
-        return [(pt1, pt2)]
+    a = (ax, ay)[isHorizontal]
 
-    t = (where - (bx, by)[isHorizontal]) / ax
+    if a == 0:
+        return [(pt1, pt2)]
+    t = (where - (bx, by)[isHorizontal]) / a
     if 0 <= t < 1:
         midPt = ax * t + bx, ay * t + by
         return [(pt1, midPt), (midPt, pt2)]
@@ -114,20 +289,20 @@
         >>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 150, False))
         ((0, 0), (50, 100), (100, 0))
         >>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 50, False))
-        ((0.0, 0.0), (25.0, 50.0), (50.0, 50.0))
-        ((50.0, 50.0), (75.0, 50.0), (100.0, 0.0))
+        ((0, 0), (25, 50), (50, 50))
+        ((50, 50), (75, 50), (100, 0))
         >>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 25, False))
-        ((0.0, 0.0), (12.5, 25.0), (25.0, 37.5))
-        ((25.0, 37.5), (62.5, 75.0), (100.0, 0.0))
+        ((0, 0), (12.5, 25), (25, 37.5))
+        ((25, 37.5), (62.5, 75), (100, 0))
         >>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 25, True))
-        ((0.0, 0.0), (7.32233047034, 14.6446609407), (14.6446609407, 25.0))
-        ((14.6446609407, 25.0), (50.0, 75.0), (85.3553390593, 25.0))
-        ((85.3553390593, 25.0), (92.6776695297, 14.6446609407), (100.0, -7.1054273576e-15))
+        ((0, 0), (7.32233, 14.6447), (14.6447, 25))
+        ((14.6447, 25), (50, 75), (85.3553, 25))
+        ((85.3553, 25), (92.6777, 14.6447), (100, -7.10543e-15))
         >>> # XXX I'm not at all sure if the following behavior is desirable:
         >>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 50, True))
-        ((0.0, 0.0), (25.0, 50.0), (50.0, 50.0))
-        ((50.0, 50.0), (50.0, 50.0), (50.0, 50.0))
-        ((50.0, 50.0), (75.0, 50.0), (100.0, 0.0))
+        ((0, 0), (25, 50), (50, 50))
+        ((50, 50), (50, 50), (50, 50))
+        ((50, 50), (75, 50), (100, 0))
     """
     a, b, c = calcQuadraticParameters(pt1, pt2, pt3)
     solutions = solveQuadratic(a[isHorizontal], b[isHorizontal],
@@ -146,12 +321,12 @@
         >>> printSegments(splitCubic((0, 0), (25, 100), (75, 100), (100, 0), 150, False))
         ((0, 0), (25, 100), (75, 100), (100, 0))
         >>> printSegments(splitCubic((0, 0), (25, 100), (75, 100), (100, 0), 50, False))
-        ((0.0, 0.0), (12.5, 50.0), (31.25, 75.0), (50.0, 75.0))
-        ((50.0, 75.0), (68.75, 75.0), (87.5, 50.0), (100.0, 0.0))
+        ((0, 0), (12.5, 50), (31.25, 75), (50, 75))
+        ((50, 75), (68.75, 75), (87.5, 50), (100, 0))
         >>> printSegments(splitCubic((0, 0), (25, 100), (75, 100), (100, 0), 25, True))
-        ((0.0, 0.0), (2.2937927384, 9.17517095361), (4.79804488188, 17.5085042869), (7.47413641001, 25.0))
-        ((7.47413641001, 25.0), (31.2886200204, 91.6666666667), (68.7113799796, 91.6666666667), (92.52586359, 25.0))
-        ((92.52586359, 25.0), (95.2019551181, 17.5085042869), (97.7062072616, 9.17517095361), (100.0, 1.7763568394e-15))
+        ((0, 0), (2.29379, 9.17517), (4.79804, 17.5085), (7.47414, 25))
+        ((7.47414, 25), (31.2886, 91.6667), (68.7114, 91.6667), (92.5259, 25))
+        ((92.5259, 25), (95.202, 17.5085), (97.7062, 9.17517), (100, 1.77636e-15))
     """
     a, b, c, d = calcCubicParameters(pt1, pt2, pt3, pt4)
     solutions = solveCubic(a[isHorizontal], b[isHorizontal], c[isHorizontal],
@@ -167,12 +342,12 @@
     values of t. Return a list of curve segments.
 
         >>> printSegments(splitQuadraticAtT((0, 0), (50, 100), (100, 0), 0.5))
-        ((0.0, 0.0), (25.0, 50.0), (50.0, 50.0))
-        ((50.0, 50.0), (75.0, 50.0), (100.0, 0.0))
+        ((0, 0), (25, 50), (50, 50))
+        ((50, 50), (75, 50), (100, 0))
         >>> printSegments(splitQuadraticAtT((0, 0), (50, 100), (100, 0), 0.5, 0.75))
-        ((0.0, 0.0), (25.0, 50.0), (50.0, 50.0))
-        ((50.0, 50.0), (62.5, 50.0), (75.0, 37.5))
-        ((75.0, 37.5), (87.5, 25.0), (100.0, 0.0))
+        ((0, 0), (25, 50), (50, 50))
+        ((50, 50), (62.5, 50), (75, 37.5))
+        ((75, 37.5), (87.5, 25), (100, 0))
     """
     a, b, c = calcQuadraticParameters(pt1, pt2, pt3)
     return _splitQuadraticAtT(a, b, c, *ts)
@@ -183,12 +358,12 @@
     values of t. Return a list of curve segments.
 
         >>> printSegments(splitCubicAtT((0, 0), (25, 100), (75, 100), (100, 0), 0.5))
-        ((0.0, 0.0), (12.5, 50.0), (31.25, 75.0), (50.0, 75.0))
-        ((50.0, 75.0), (68.75, 75.0), (87.5, 50.0), (100.0, 0.0))
+        ((0, 0), (12.5, 50), (31.25, 75), (50, 75))
+        ((50, 75), (68.75, 75), (87.5, 50), (100, 0))
         >>> printSegments(splitCubicAtT((0, 0), (25, 100), (75, 100), (100, 0), 0.5, 0.75))
-        ((0.0, 0.0), (12.5, 50.0), (31.25, 75.0), (50.0, 75.0))
-        ((50.0, 75.0), (59.375, 75.0), (68.75, 68.75), (77.34375, 56.25))
-        ((77.34375, 56.25), (85.9375, 43.75), (93.75, 25.0), (100.0, 0.0))
+        ((0, 0), (12.5, 50), (31.25, 75), (50, 75))
+        ((50, 75), (59.375, 75), (68.75, 68.75), (77.3438, 56.25))
+        ((77.3438, 56.25), (85.9375, 43.75), (93.75, 25), (100, 0))
     """
     a, b, c, d = calcCubicParameters(pt1, pt2, pt3, pt4)
     return _splitCubicAtT(a, b, c, d, *ts)
@@ -207,13 +382,15 @@
         t2 = ts[i+1]
         delta = (t2 - t1)
         # calc new a, b and c
-        a1x = ax * delta**2
-        a1y = ay * delta**2
+        delta_2 = delta*delta
+        a1x = ax * delta_2
+        a1y = ay * delta_2
         b1x = (2*ax*t1 + bx) * delta
         b1y = (2*ay*t1 + by) * delta
-        c1x = ax*t1**2 + bx*t1 + cx
-        c1y = ay*t1**2 + by*t1 + cy
-    
+        t1_2 = t1*t1
+        c1x = ax*t1_2 + bx*t1 + cx
+        c1y = ay*t1_2 + by*t1 + cy
+
         pt1, pt2, pt3 = calcQuadraticPoints((a1x, a1y), (b1x, b1y), (c1x, c1y))
         segments.append((pt1, pt2, pt3))
     return segments
@@ -232,15 +409,21 @@
         t1 = ts[i]
         t2 = ts[i+1]
         delta = (t2 - t1)
+
+        delta_2 = delta*delta
+        delta_3 = delta*delta_2
+        t1_2 = t1*t1
+        t1_3 = t1*t1_2
+
         # calc new a, b, c and d
-        a1x = ax * delta**3
-        a1y = ay * delta**3
-        b1x = (3*ax*t1 + bx) * delta**2
-        b1y = (3*ay*t1 + by) * delta**2
-        c1x = (2*bx*t1 + cx + 3*ax*t1**2) * delta
-        c1y = (2*by*t1 + cy + 3*ay*t1**2) * delta
-        d1x = ax*t1**3 + bx*t1**2 + cx*t1 + dx
-        d1y = ay*t1**3 + by*t1**2 + cy*t1 + dy
+        a1x = ax * delta_3
+        a1y = ay * delta_3
+        b1x = (3*ax*t1 + bx) * delta_2
+        b1y = (3*ay*t1 + by) * delta_2
+        c1x = (2*bx*t1 + cx + 3*ax*t1_2) * delta
+        c1y = (2*by*t1 + cy + 3*ay*t1_2) * delta
+        d1x = ax*t1_3 + bx*t1_2 + cx*t1 + dx
+        d1y = ay*t1_3 + by*t1_2 + cy*t1 + dy
         pt1, pt2, pt3, pt4 = calcCubicPoints((a1x, a1y), (b1x, b1y), (c1x, c1y), (d1x, d1y))
         segments.append((pt1, pt2, pt3, pt4))
     return segments
@@ -284,6 +467,21 @@
         a*x*x*x + b*x*x + c*x + d = 0
     This function returns a list of roots. Note that the returned list
     is neither guaranteed to be sorted nor to contain unique values!
+
+    >>> solveCubic(1, 1, -6, 0)
+    [-3.0, -0.0, 2.0]
+    >>> solveCubic(-10.0, -9.0, 48.0, -29.0)
+    [-2.9, 1.0, 1.0]
+    >>> solveCubic(-9.875, -9.0, 47.625, -28.75)
+    [-2.911392, 1.0, 1.0]
+    >>> solveCubic(1.0, -4.5, 6.75, -3.375)
+    [1.5, 1.5, 1.5]
+    >>> solveCubic(-12.0, 18.0, -9.0, 1.50023651123)
+    [0.5, 0.5, 0.5]
+    >>> solveCubic(
+    ...     9.0, 0.0, 0.0, -7.62939453125e-05
+    ... ) == [-0.0, -0.0, -0.0]
+    True
     """
     #
     # adapted from:
@@ -299,27 +497,49 @@
     a1 = b/a
     a2 = c/a
     a3 = d/a
-    
+
     Q = (a1*a1 - 3.0*a2)/9.0
     R = (2.0*a1*a1*a1 - 9.0*a1*a2 + 27.0*a3)/54.0
-    R2_Q3 = R*R - Q*Q*Q
 
-    if R2_Q3 < 0:
-        theta = acos(R/sqrt(Q*Q*Q))
+    R2 = R*R
+    Q3 = Q*Q*Q
+    R2 = 0 if R2 < epsilon else R2
+    Q3 = 0 if abs(Q3) < epsilon else Q3
+
+    R2_Q3 = R2 - Q3
+
+    if R2 == 0. and Q3 == 0.:
+        x = round(-a1/3.0, epsilonDigits)
+        return [x, x, x]
+    elif R2_Q3 <= epsilon * .5:
+        # The epsilon * .5 above ensures that Q3 is not zero.
+        theta = acos(max(min(R/sqrt(Q3), 1.0), -1.0))
         rQ2 = -2.0*sqrt(Q)
-        x0 = rQ2*cos(theta/3.0) - a1/3.0
-        x1 = rQ2*cos((theta+2.0*pi)/3.0) - a1/3.0
-        x2 = rQ2*cos((theta+4.0*pi)/3.0) - a1/3.0
+        a1_3 = a1/3.0
+        x0 = rQ2*cos(theta/3.0) - a1_3
+        x1 = rQ2*cos((theta+2.0*pi)/3.0) - a1_3
+        x2 = rQ2*cos((theta+4.0*pi)/3.0) - a1_3
+        x0, x1, x2 = sorted([x0, x1, x2])
+        # Merge roots that are close-enough
+        if x1 - x0 < epsilon and x2 - x1 < epsilon:
+            x0 = x1 = x2 = round((x0 + x1 + x2) / 3., epsilonDigits)
+        elif x1 - x0 < epsilon:
+            x0 = x1 = round((x0 + x1) / 2., epsilonDigits)
+            x2 = round(x2, epsilonDigits)
+        elif x2 - x1 < epsilon:
+            x0 = round(x0, epsilonDigits)
+            x1 = x2 = round((x1 + x2) / 2., epsilonDigits)
+        else:
+            x0 = round(x0, epsilonDigits)
+            x1 = round(x1, epsilonDigits)
+            x2 = round(x2, epsilonDigits)
         return [x0, x1, x2]
     else:
-        if Q == 0 and R == 0:
-            x = 0
-        else:
-            x = pow(sqrt(R2_Q3)+abs(R), 1/3.0)
-            x = x + Q/x
+        x = pow(sqrt(R2_Q3)+abs(R), 1/3.0)
+        x = x + Q/x
         if R >= 0.0:
             x = -x
-        x = x - a1/3.0
+        x = round(x - a1/3.0, epsilonDigits)
         return [x]
 
 
@@ -389,7 +609,7 @@
     try:
         it = iter(obj)
     except TypeError:
-        return str(obj)
+        return "%g" % obj
     else:
         return "(%s)" % ", ".join([_segmentrepr(x) for x in it])
 
@@ -402,5 +622,6 @@
         print(_segmentrepr(segment))
 
 if __name__ == "__main__":
+    import sys
     import doctest
-    doctest.testmod()
+    sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/misc/classifyTools.py b/Lib/fontTools/misc/classifyTools.py
new file mode 100644
index 0000000..64bcdc8
--- /dev/null
+++ b/Lib/fontTools/misc/classifyTools.py
@@ -0,0 +1,173 @@
+""" fontTools.misc.classifyTools.py -- tools for classifying things.
+"""
+
+from __future__ import print_function, absolute_import
+from fontTools.misc.py23 import *
+
+class Classifier(object):
+
+	"""
+	Main Classifier object, used to classify things into similar sets.
+	"""
+
+	def __init__(self, sort=True):
+
+		self._things = set() # set of all things known so far
+		self._sets = [] # list of class sets produced so far
+		self._mapping = {} # map from things to their class set
+		self._dirty = False
+		self._sort = sort
+
+	def add(self, set_of_things):
+		"""
+		Add a set to the classifier.  Any iterable is accepted.
+		"""
+		if not set_of_things:
+			return
+
+		self._dirty = True
+
+		things, sets, mapping = self._things, self._sets, self._mapping
+
+		s = set(set_of_things)
+		intersection = s.intersection(things) # existing things
+		s.difference_update(intersection) # new things
+		difference = s
+		del s
+
+		# Add new class for new things
+		if difference:
+			things.update(difference)
+			sets.append(difference)
+			for thing in difference:
+				mapping[thing] = difference
+		del difference
+
+		while intersection:
+			# Take one item and process the old class it belongs to
+			old_class = mapping[next(iter(intersection))]
+			old_class_intersection = old_class.intersection(intersection)
+
+			# Update old class to remove items from new set
+			old_class.difference_update(old_class_intersection)
+
+			# Remove processed items from todo list
+			intersection.difference_update(old_class_intersection)
+
+			# Add new class for the intersection with old class
+			sets.append(old_class_intersection)
+			for thing in old_class_intersection:
+				mapping[thing] = old_class_intersection
+			del old_class_intersection
+
+	def update(self, list_of_sets):
+		"""
+		Add a a list of sets to the classifier.  Any iterable of iterables is accepted.
+		"""
+		for s in list_of_sets:
+			self.add(s)
+
+	def _process(self):
+		if not self._dirty:
+			return
+
+		# Do any deferred processing
+		sets = self._sets
+		self._sets = [s for s in sets if s]
+
+		if self._sort:
+			self._sets = sorted(self._sets, key=lambda s: (-len(s), sorted(s)))
+
+		self._dirty = False
+
+	# Output methods
+
+	def getThings(self):
+		"""Returns the set of all things known so far.
+
+		The return value belongs to the Classifier object and should NOT
+		be modified while the classifier is still in use.
+		"""
+		self._process()
+		return self._things
+
+	def getMapping(self):
+		"""Returns the mapping from things to their class set.
+
+		The return value belongs to the Classifier object and should NOT
+		be modified while the classifier is still in use.
+		"""
+		self._process()
+		return self._mapping
+
+	def getClasses(self):
+		"""Returns the list of class sets.
+
+		The return value belongs to the Classifier object and should NOT
+		be modified while the classifier is still in use.
+		"""
+		self._process()
+		return self._sets
+
+
+def classify(list_of_sets, sort=True):
+	"""
+	Takes a iterable of iterables (list of sets from here on; but any
+	iterable works.), and returns the smallest list of sets such that
+	each set, is either a subset, or is disjoint from, each of the input
+	sets.
+
+	In other words, this function classifies all the things present in
+	any of the input sets, into similar classes, based on which sets
+	things are a member of.
+
+	If sort=True, return class sets are sorted by decreasing size and
+	their natural sort order within each class size.  Otherwise, class
+	sets are returned in the order that they were identified, which is
+	generally not significant.
+
+	>>> classify([]) == ([], {})
+	True
+	>>> classify([[]]) == ([], {})
+	True
+	>>> classify([[], []]) == ([], {})
+	True
+	>>> classify([[1]]) == ([{1}], {1: {1}})
+	True
+	>>> classify([[1,2]]) == ([{1, 2}], {1: {1, 2}, 2: {1, 2}})
+	True
+	>>> classify([[1],[2]]) == ([{1}, {2}], {1: {1}, 2: {2}})
+	True
+	>>> classify([[1,2],[2]]) == ([{1}, {2}], {1: {1}, 2: {2}})
+	True
+	>>> classify([[1,2],[2,4]]) == ([{1}, {2}, {4}], {1: {1}, 2: {2}, 4: {4}})
+	True
+	>>> classify([[1,2],[2,4,5]]) == (
+	...     [{4, 5}, {1}, {2}], {1: {1}, 2: {2}, 4: {4, 5}, 5: {4, 5}})
+	True
+	>>> classify([[1,2],[2,4,5]], sort=False) == (
+	...     [{1}, {4, 5}, {2}], {1: {1}, 2: {2}, 4: {4, 5}, 5: {4, 5}})
+	True
+	>>> classify([[1,2,9],[2,4,5]], sort=False) == (
+	...     [{1, 9}, {4, 5}, {2}], {1: {1, 9}, 2: {2}, 4: {4, 5}, 5: {4, 5},
+	...     9: {1, 9}})
+	True
+	>>> classify([[1,2,9,15],[2,4,5]], sort=False) == (
+	...     [{1, 9, 15}, {4, 5}, {2}], {1: {1, 9, 15}, 2: {2}, 4: {4, 5},
+	...     5: {4, 5}, 9: {1, 9, 15}, 15: {1, 9, 15}})
+	True
+	>>> classes, mapping = classify([[1,2,9,15],[2,4,5],[15,5]], sort=False)
+	>>> set([frozenset(c) for c in classes]) == set(
+	...     [frozenset(s) for s in ({1, 9}, {4}, {2}, {5}, {15})])
+	True
+	>>> mapping == {1: {1, 9}, 2: {2}, 4: {4}, 5: {5}, 9: {1, 9}, 15: {15}}
+	True
+	"""
+	classifier = Classifier(sort=sort)
+	classifier.update(list_of_sets)
+	return classifier.getClasses(), classifier.getMapping()
+
+
+if __name__ == "__main__":
+	import sys, doctest
+	sys.exit(doctest.testmod(optionflags=doctest.ELLIPSIS).failed)
diff --git a/Lib/fontTools/misc/cliTools.py b/Lib/fontTools/misc/cliTools.py
new file mode 100644
index 0000000..59ac3be
--- /dev/null
+++ b/Lib/fontTools/misc/cliTools.py
@@ -0,0 +1,26 @@
+"""Collection of utilities for command-line interfaces and console scripts."""
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+import os
+import re
+
+
+numberAddedRE = re.compile("#\d+$")
+
+
+def makeOutputFileName(input, outputDir=None, extension=None, overWrite=False):
+    dirName, fileName = os.path.split(input)
+    fileName, ext = os.path.splitext(fileName)
+    if outputDir:
+        dirName = outputDir
+    fileName = numberAddedRE.split(fileName)[0]
+    if extension is None:
+        extension = os.path.splitext(input)[1]
+    output = os.path.join(dirName, fileName + extension)
+    n = 1
+    if not overWrite:
+        while os.path.exists(output):
+            output = os.path.join(
+                dirName, fileName + "#" + repr(n) + extension)
+            n += 1
+    return output
diff --git a/Lib/fontTools/misc/eexec.py b/Lib/fontTools/misc/eexec.py
index b7656d7..0efa6f5 100644
--- a/Lib/fontTools/misc/eexec.py
+++ b/Lib/fontTools/misc/eexec.py
@@ -1,4 +1,4 @@
-"""fontTools.misc.eexec.py -- Module implementing the eexec and 
+"""fontTools.misc.eexec.py -- Module implementing the eexec and
 charstring encryption algorithm as used by PostScript Type 1 fonts.
 """
 
@@ -19,19 +19,35 @@
 
 
 def decrypt(cipherstring, R):
+	r"""
+	>>> testStr = b"\0\0asdadads asds\265"
+	>>> decryptedStr, R = decrypt(testStr, 12321)
+	>>> decryptedStr == b'0d\nh\x15\xe8\xc4\xb2\x15\x1d\x108\x1a<6\xa1'
+	True
+	>>> R == 36142
+	True
+	"""
 	plainList = []
 	for cipher in cipherstring:
 		plain, R = _decryptChar(cipher, R)
 		plainList.append(plain)
-	plainstring = strjoin(plainList)
+	plainstring = bytesjoin(plainList)
 	return plainstring, int(R)
 
 def encrypt(plainstring, R):
+	r"""
+	>>> testStr = b'0d\nh\x15\xe8\xc4\xb2\x15\x1d\x108\x1a<6\xa1'
+	>>> encryptedStr, R = encrypt(testStr, 12321)
+	>>> encryptedStr == b"\0\0asdadads asds\265"
+	True
+	>>> R == 36142
+	True
+	"""
 	cipherList = []
 	for plain in plainstring:
 		cipher, R = _encryptChar(plain, R)
 		cipherList.append(cipher)
-	cipherstring = strjoin(cipherList)
+	cipherstring = bytesjoin(cipherList)
 	return cipherstring, int(R)
 
 
@@ -41,15 +57,11 @@
 
 def deHexString(h):
 	import binascii
-	h = strjoin(h.split())
+	h = bytesjoin(h.split())
 	return binascii.unhexlify(h)
 
 
-def _test():
-	testStr = "\0\0asdadads asds\265"
-	print(decrypt, decrypt(testStr, 12321))
-	print(encrypt, encrypt(testStr, 12321))
-
-
 if __name__ == "__main__":
-	_test()
+	import sys
+	import doctest
+	sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/misc/encodingTools.py b/Lib/fontTools/misc/encodingTools.py
new file mode 100644
index 0000000..275ae9f
--- /dev/null
+++ b/Lib/fontTools/misc/encodingTools.py
@@ -0,0 +1,73 @@
+"""fontTools.misc.encodingTools.py -- tools for working with OpenType encodings.
+"""
+
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+import fontTools.encodings.codecs
+
+# Map keyed by platformID, then platEncID, then possibly langID
+_encodingMap = {
+	0: { # Unicode
+		0: 'utf_16_be',
+		1: 'utf_16_be',
+		2: 'utf_16_be',
+		3: 'utf_16_be',
+		4: 'utf_16_be',
+		5: 'utf_16_be',
+		6: 'utf_16_be',
+	},
+	1: { # Macintosh
+		# See
+		# https://github.com/behdad/fonttools/issues/236
+		0: { # Macintosh, platEncID==0, keyed by langID
+			15: "mac_iceland",
+			17: "mac_turkish",
+			18: "mac_croatian",
+			24: "mac_latin2",
+			25: "mac_latin2",
+			26: "mac_latin2",
+			27: "mac_latin2",
+			28: "mac_latin2",
+			36: "mac_latin2",
+			37: "mac_romanian",
+			38: "mac_latin2",
+			39: "mac_latin2",
+			40: "mac_latin2",
+			Ellipsis: 'mac_roman', # Other
+		},
+		1: 'x_mac_japanese_ttx',
+		2: 'x_mac_trad_chinese_ttx',
+		3: 'x_mac_korean_ttx',
+		6: 'mac_greek',
+		7: 'mac_cyrillic',
+		25: 'x_mac_simp_chinese_ttx',
+		29: 'mac_latin2',
+		35: 'mac_turkish',
+		37: 'mac_iceland',
+	},
+	2: { # ISO
+		0: 'ascii',
+		1: 'utf_16_be',
+		2: 'latin1',
+	},
+	3: { # Microsoft
+		0: 'utf_16_be',
+		1: 'utf_16_be',
+		2: 'shift_jis',
+		3: 'gb2312',
+		4: 'big5',
+		5: 'euc_kr',
+		6: 'johab',
+		10: 'utf_16_be',
+	},
+}
+
+def getEncoding(platformID, platEncID, langID, default=None):
+	"""Returns the Python encoding name for OpenType platformID/encodingID/langID
+	triplet.  If encoding for these values is not known, by default None is
+	returned.  That can be overriden by passing a value to the default argument.
+	"""
+	encoding = _encodingMap.get(platformID, {}).get(platEncID, default)
+	if isinstance(encoding, dict):
+		encoding = encoding.get(langID, encoding[Ellipsis])
+	return encoding
diff --git a/Lib/fontTools/misc/filenames.py b/Lib/fontTools/misc/filenames.py
new file mode 100644
index 0000000..6cf02e3
--- /dev/null
+++ b/Lib/fontTools/misc/filenames.py
@@ -0,0 +1,224 @@
+"""
+User name to file name conversion based on the UFO 3 spec:
+http://unifiedfontobject.org/versions/ufo3/conventions/
+
+The code was copied from:
+https://github.com/unified-font-object/ufoLib/blob/8747da7/Lib/ufoLib/filenames.py
+
+Author: Tal Leming
+Copyright (c) 2005-2016, The RoboFab Developers:
+	Erik van Blokland
+	Tal Leming
+	Just van Rossum
+"""
+from __future__ import unicode_literals
+from fontTools.misc.py23 import basestring, unicode
+
+
+illegalCharacters = "\" * + / : < > ? [ \ ] | \0".split(" ")
+illegalCharacters += [chr(i) for i in range(1, 32)]
+illegalCharacters += [chr(0x7F)]
+reservedFileNames = "CON PRN AUX CLOCK$ NUL A:-Z: COM1".lower().split(" ")
+reservedFileNames += "LPT1 LPT2 LPT3 COM2 COM3 COM4".lower().split(" ")
+maxFileNameLength = 255
+
+
+class NameTranslationError(Exception):
+	pass
+
+
+def userNameToFileName(userName, existing=[], prefix="", suffix=""):
+	"""
+	existing should be a case-insensitive list
+	of all existing file names.
+
+	>>> userNameToFileName("a") == "a"
+	True
+	>>> userNameToFileName("A") == "A_"
+	True
+	>>> userNameToFileName("AE") == "A_E_"
+	True
+	>>> userNameToFileName("Ae") == "A_e"
+	True
+	>>> userNameToFileName("ae") == "ae"
+	True
+	>>> userNameToFileName("aE") == "aE_"
+	True
+	>>> userNameToFileName("a.alt") == "a.alt"
+	True
+	>>> userNameToFileName("A.alt") == "A_.alt"
+	True
+	>>> userNameToFileName("A.Alt") == "A_.A_lt"
+	True
+	>>> userNameToFileName("A.aLt") == "A_.aL_t"
+	True
+	>>> userNameToFileName(u"A.alT") == "A_.alT_"
+	True
+	>>> userNameToFileName("T_H") == "T__H_"
+	True
+	>>> userNameToFileName("T_h") == "T__h"
+	True
+	>>> userNameToFileName("t_h") == "t_h"
+	True
+	>>> userNameToFileName("F_F_I") == "F__F__I_"
+	True
+	>>> userNameToFileName("f_f_i") == "f_f_i"
+	True
+	>>> userNameToFileName("Aacute_V.swash") == "A_acute_V_.swash"
+	True
+	>>> userNameToFileName(".notdef") == "_notdef"
+	True
+	>>> userNameToFileName("con") == "_con"
+	True
+	>>> userNameToFileName("CON") == "C_O_N_"
+	True
+	>>> userNameToFileName("con.alt") == "_con.alt"
+	True
+	>>> userNameToFileName("alt.con") == "alt._con"
+	True
+	"""
+	# the incoming name must be a unicode string
+	if not isinstance(userName, unicode):
+		raise ValueError("The value for userName must be a unicode string.")
+	# establish the prefix and suffix lengths
+	prefixLength = len(prefix)
+	suffixLength = len(suffix)
+	# replace an initial period with an _
+	# if no prefix is to be added
+	if not prefix and userName[0] == ".":
+		userName = "_" + userName[1:]
+	# filter the user name
+	filteredUserName = []
+	for character in userName:
+		# replace illegal characters with _
+		if character in illegalCharacters:
+			character = "_"
+		# add _ to all non-lower characters
+		elif character != character.lower():
+			character += "_"
+		filteredUserName.append(character)
+	userName = "".join(filteredUserName)
+	# clip to 255
+	sliceLength = maxFileNameLength - prefixLength - suffixLength
+	userName = userName[:sliceLength]
+	# test for illegal files names
+	parts = []
+	for part in userName.split("."):
+		if part.lower() in reservedFileNames:
+			part = "_" + part
+		parts.append(part)
+	userName = ".".join(parts)
+	# test for clash
+	fullName = prefix + userName + suffix
+	if fullName.lower() in existing:
+		fullName = handleClash1(userName, existing, prefix, suffix)
+	# finished
+	return fullName
+
+def handleClash1(userName, existing=[], prefix="", suffix=""):
+	"""
+	existing should be a case-insensitive list
+	of all existing file names.
+
+	>>> prefix = ("0" * 5) + "."
+	>>> suffix = "." + ("0" * 10)
+	>>> existing = ["a" * 5]
+
+	>>> e = list(existing)
+	>>> handleClash1(userName="A" * 5, existing=e,
+	...		prefix=prefix, suffix=suffix) == (
+	... 	'00000.AAAAA000000000000001.0000000000')
+	True
+
+	>>> e = list(existing)
+	>>> e.append(prefix + "aaaaa" + "1".zfill(15) + suffix)
+	>>> handleClash1(userName="A" * 5, existing=e,
+	...		prefix=prefix, suffix=suffix) == (
+	... 	'00000.AAAAA000000000000002.0000000000')
+	True
+
+	>>> e = list(existing)
+	>>> e.append(prefix + "AAAAA" + "2".zfill(15) + suffix)
+	>>> handleClash1(userName="A" * 5, existing=e,
+	...		prefix=prefix, suffix=suffix) == (
+	... 	'00000.AAAAA000000000000001.0000000000')
+	True
+	"""
+	# if the prefix length + user name length + suffix length + 15 is at
+	# or past the maximum length, silce 15 characters off of the user name
+	prefixLength = len(prefix)
+	suffixLength = len(suffix)
+	if prefixLength + len(userName) + suffixLength + 15 > maxFileNameLength:
+		l = (prefixLength + len(userName) + suffixLength + 15)
+		sliceLength = maxFileNameLength - l
+		userName = userName[:sliceLength]
+	finalName = None
+	# try to add numbers to create a unique name
+	counter = 1
+	while finalName is None:
+		name = userName + str(counter).zfill(15)
+		fullName = prefix + name + suffix
+		if fullName.lower() not in existing:
+			finalName = fullName
+			break
+		else:
+			counter += 1
+		if counter >= 999999999999999:
+			break
+	# if there is a clash, go to the next fallback
+	if finalName is None:
+		finalName = handleClash2(existing, prefix, suffix)
+	# finished
+	return finalName
+
+def handleClash2(existing=[], prefix="", suffix=""):
+	"""
+	existing should be a case-insensitive list
+	of all existing file names.
+
+	>>> prefix = ("0" * 5) + "."
+	>>> suffix = "." + ("0" * 10)
+	>>> existing = [prefix + str(i) + suffix for i in range(100)]
+
+	>>> e = list(existing)
+	>>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == (
+	... 	'00000.100.0000000000')
+	True
+
+	>>> e = list(existing)
+	>>> e.remove(prefix + "1" + suffix)
+	>>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == (
+	... 	'00000.1.0000000000')
+	True
+
+	>>> e = list(existing)
+	>>> e.remove(prefix + "2" + suffix)
+	>>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == (
+	... 	'00000.2.0000000000')
+	True
+	"""
+	# calculate the longest possible string
+	maxLength = maxFileNameLength - len(prefix) - len(suffix)
+	maxValue = int("9" * maxLength)
+	# try to find a number
+	finalName = None
+	counter = 1
+	while finalName is None:
+		fullName = prefix + str(counter) + suffix
+		if fullName.lower() not in existing:
+			finalName = fullName
+			break
+		else:
+			counter += 1
+		if counter >= maxValue:
+			break
+	# raise an error if nothing has been found
+	if finalName is None:
+		raise NameTranslationError("No unique name could be found.")
+	# finished
+	return finalName
+
+if __name__ == "__main__":
+	import doctest
+	import sys
+	sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/misc/fixedTools.py b/Lib/fontTools/misc/fixedTools.py
index 59c55dd..c5119ab 100644
--- a/Lib/fontTools/misc/fixedTools.py
+++ b/Lib/fontTools/misc/fixedTools.py
@@ -3,63 +3,93 @@
 
 from __future__ import print_function, division, absolute_import
 from fontTools.misc.py23 import *
+import math
+import logging
+
+log = logging.getLogger(__name__)
 
 __all__ = [
-    "fixedToFloat",
-    "floatToFixed",
+	"otRound",
+	"fixedToFloat",
+	"floatToFixed",
+    "floatToFixedToFloat",
+	"ensureVersionIsLong",
+	"versionToFixed",
 ]
 
+
+def otRound(value):
+	"""Round float value to nearest integer towards +Infinity.
+	For fractional values of 0.5 and higher, take the next higher integer;
+	for other fractional values, truncate.
+
+	https://docs.microsoft.com/en-us/typography/opentype/spec/otvaroverview
+	https://github.com/fonttools/fonttools/issues/1248#issuecomment-383198166
+	"""
+	return int(math.floor(value + 0.5))
+
+
 def fixedToFloat(value, precisionBits):
 	"""Converts a fixed-point number to a float, choosing the float
 	that has the shortest decimal reprentation.  Eg. to convert a
 	fixed number in a 2.14 format, use precisionBits=14.  This is
 	pretty slow compared to a simple division.  Use sporadically.
-	
-	>>> fixedToFloat(13107, 14)
-	0.8
-	>>> fixedToFloat(0, 14)
-	0.0
-	>>> fixedToFloat(0x4000, 14)
-	1.0
-	"""
 
+	precisionBits is only supported up to 16.
+	"""
 	if not value: return 0.0
 
 	scale = 1 << precisionBits
 	value /= scale
 	eps = .5 / scale
-	digits = (precisionBits + 2) // 3
-	fmt = "%%.%df" % digits
-	lo = fmt % (value - eps)
-	hi = fmt % (value + eps)
-	out = []
-	length = min(len(lo), len(hi))
-	for i in range(length):
+	lo = value - eps
+	hi = value + eps
+	# If the range of valid choices spans an integer, return the integer.
+	if int(lo) != int(hi):
+		return float(round(value))
+	fmt = "%.8f"
+	lo = fmt % lo
+	hi = fmt % hi
+	assert len(lo) == len(hi) and lo != hi
+	for i in range(len(lo)):
 		if lo[i] != hi[i]:
-			break;
-		out.append(lo[i])
-	outlen = len(out)
-	if outlen < length:
-		out.append(max(lo[outlen], hi[outlen]))
-	return float(strjoin(out))
+			break
+	period = lo.find('.')
+	assert period < i
+	fmt = "%%.%df" % (i - period)
+	value = fmt % value
+	return float(value)
 
 def floatToFixed(value, precisionBits):
 	"""Converts a float to a fixed-point number given the number of
-	precisionBits.  Ie. int(round(value * (1<<precisionBits))).
-
-	>>> floatToFixed(0.8, 14)
-	13107
-	>>> floatToFixed(1.0, 14)
-	16384
-	>>> floatToFixed(1, 14)
-	16384
-	>>> floatToFixed(0, 14)
-	0
+	precisionBits.  Ie. round(value * (1<<precisionBits)).
 	"""
+	return otRound(value * (1<<precisionBits))
 
-	return int(round(value * (1<<precisionBits)))
+def floatToFixedToFloat(value, precisionBits):
+	"""Converts a float to a fixed-point number given the number of
+	precisionBits, round it, then convert it back to float again.
+	Ie. round(value * (1<<precisionBits)) / (1<<precisionBits)
+	Note: this is *not* equivalent to fixedToFloat(floatToFixed(value)),
+	which would return the shortest representation of the rounded value.
+	"""
+	scale = 1<<precisionBits
+	return otRound(value * scale) / scale
+
+def ensureVersionIsLong(value):
+	"""Ensure a table version is an unsigned long (unsigned short major,
+	unsigned short minor) instead of a float."""
+	if value < 0x10000:
+		newValue = floatToFixed(value, 16)
+		log.warning(
+			"Table version value is a float: %.4f; "
+			"fix to use hex instead: 0x%08x", value, newValue)
+		value = newValue
+	return value
 
 
-if __name__ == "__main__":
-    import doctest
-    doctest.testmod()
+def versionToFixed(value):
+	"""Converts a table version to a fixed"""
+	value = int(value, 0) if value.startswith("0") else float(value)
+	value = ensureVersionIsLong(value)
+	return value
diff --git a/Lib/fontTools/misc/homeResFile.py b/Lib/fontTools/misc/homeResFile.py
deleted file mode 100644
index a2d1c8c..0000000
--- a/Lib/fontTools/misc/homeResFile.py
+++ /dev/null
@@ -1,96 +0,0 @@
-"""Mac-only module to find the home file of a resource."""
-
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
-from fontTools.misc import sstruct
-import array
-import calldll
-import macfs, Res
-
-
-def HomeResFile(res):
-	"""Return a path to the file in which resource 'res' lives."""
-	return GetFileLocation(res.HomeResFile())
-
-
-def GetFileLocation(refNum):
-	"""Return a path to the open file identified with refNum."""
-	pb = ParamBlock(refNum)
-	return pb.getPath()
-
-#
-# Internal cruft, adapted from MoreFiles
-#
-
-_InterfaceLib = calldll.getlibrary("InterfaceLib")
-GetVRefNum = calldll.newcall(_InterfaceLib.GetVRefNum, "None", "InShort", "OutShort")
-_getInfo = calldll.newcall(_InterfaceLib.PBGetFCBInfoSync, "Short", "InLong")
-
-
-_FCBPBFormat = """
-	qLink:        l
-	qType:        h
-	ioTrap:       h
-	ioCmdAddr:    l
-	ioCompletion: l
-	ioResult:     h
-	ioNamePtr:    l
-	ioVRefNum:    h
-	ioRefNum:     h
-	filler:       h
-	ioFCBIndx:    h
-	filler1:      h
-	ioFCBFINm:    l
-	ioFCBFlags:   h
-	ioFCBStBlk:   h
-	ioFCBEOF:     l
-	ioFCBPLen:    l
-	ioFCBCrPs:    l
-	ioFCBVRefNum: h
-	ioFCBClpSiz:  l
-	ioFCBParID:   l
-"""
-
-class ParamBlock(object):
-	
-	"""Wrapper for the very low level FCBPB record."""
-	
-	def __init__(self, refNum):
-		self.__fileName = array.array("c", "\0" * 64)
-		sstruct.unpack(_FCBPBFormat, 
-				"\0" * sstruct.calcsize(_FCBPBFormat), self)
-		self.ioNamePtr = self.__fileName.buffer_info()[0]
-		self.ioRefNum = refNum
-		self.ioVRefNum = GetVRefNum(refNum)
-		self.__haveInfo = 0
-	
-	def getInfo(self):
-		if self.__haveInfo:
-			return
-		data = sstruct.pack(_FCBPBFormat, self)
-		buf = array.array("c", data)
-		ptr = buf.buffer_info()[0]
-		err = _getInfo(ptr)
-		if err:
-			raise Res.Error("can't get file info", err)
-		sstruct.unpack(_FCBPBFormat, buf.tostring(), self)
-		self.__haveInfo = 1
-	
-	def getFileName(self):
-		self.getInfo()
-		data = self.__fileName.tostring()
-		return data[1:byteord(data[0])+1]
-	
-	def getFSSpec(self):
-		self.getInfo()
-		vRefNum = self.ioVRefNum
-		parID = self.ioFCBParID
-		return macfs.FSSpec((vRefNum, parID, self.getFileName()))
-	
-	def getPath(self):
-		return self.getFSSpec().as_pathname()
-
-
-if __name__ == "__main__":
-	fond = Res.GetNamedResource("FOND", "Helvetica")
-	print(HomeResFile(fond))
diff --git a/Lib/fontTools/misc/loggingTools.py b/Lib/fontTools/misc/loggingTools.py
new file mode 100644
index 0000000..ebc0d0a
--- /dev/null
+++ b/Lib/fontTools/misc/loggingTools.py
@@ -0,0 +1,593 @@
+""" fontTools.misc.loggingTools.py -- tools for interfacing with the Python
+logging package.
+"""
+
+from __future__ import print_function, absolute_import
+from fontTools.misc.py23 import *
+import sys
+import logging
+import timeit
+from functools import wraps
+import collections
+import warnings
+
+try:
+	from logging import PercentStyle
+except ImportError:
+	PercentStyle = None
+
+
+# default logging level used by Timer class
+TIME_LEVEL = logging.DEBUG
+
+# per-level format strings used by the default formatter
+# (the level name is not printed for INFO and DEBUG messages)
+DEFAULT_FORMATS = {
+	"*": "%(levelname)s: %(message)s",
+	"INFO": "%(message)s",
+	"DEBUG": "%(message)s",
+	}
+
+
+class LevelFormatter(logging.Formatter):
+	""" Formatter class which optionally takes a dict of logging levels to
+	format strings, allowing to customise the log records appearance for
+	specific levels.
+	The '*' key identifies the default format string.
+
+	>>> import sys
+	>>> handler = logging.StreamHandler(sys.stdout)
+	>>> formatter = LevelFormatter(
+	...     fmt={
+	...         '*':     '[%(levelname)s] %(message)s',
+	...         'DEBUG': '%(name)s [%(levelname)s] %(message)s',
+	...         'INFO':  '%(message)s',
+	...     })
+	>>> handler.setFormatter(formatter)
+	>>> log = logging.getLogger('test')
+	>>> log.setLevel(logging.DEBUG)
+	>>> log.addHandler(handler)
+	>>> log.debug('this uses a custom format string')
+	test [DEBUG] this uses a custom format string
+	>>> log.info('this also uses a custom format string')
+	this also uses a custom format string
+	>>> log.warning("this one uses the default format string")
+	[WARNING] this one uses the default format string
+	"""
+
+	def __init__(self, fmt=None, datefmt=None, style="%"):
+		if style != '%':
+			raise ValueError(
+				"only '%' percent style is supported in both python 2 and 3")
+		if fmt is None:
+			fmt = DEFAULT_FORMATS
+		if isinstance(fmt, basestring):
+			default_format = fmt
+			custom_formats = {}
+		elif isinstance(fmt, collections.Mapping):
+			custom_formats = dict(fmt)
+			default_format = custom_formats.pop("*", None)
+		else:
+			raise TypeError('fmt must be a str or a dict of str: %r' % fmt)
+		super(LevelFormatter, self).__init__(default_format, datefmt)
+		self.default_format = self._fmt
+		self.custom_formats = {}
+		for level, fmt in custom_formats.items():
+			level = logging._checkLevel(level)
+			self.custom_formats[level] = fmt
+
+	def format(self, record):
+		if self.custom_formats:
+			fmt = self.custom_formats.get(record.levelno, self.default_format)
+			if self._fmt != fmt:
+				self._fmt = fmt
+				# for python >= 3.2, _style needs to be set if _fmt changes
+				if PercentStyle:
+					self._style = PercentStyle(fmt)
+		return super(LevelFormatter, self).format(record)
+
+
+def configLogger(**kwargs):
+	""" Do basic configuration for the logging system. This is more or less
+	the same as logging.basicConfig with some additional options and defaults.
+
+	The default behaviour is to create a StreamHandler which writes to
+	sys.stderr, set a formatter using the DEFAULT_FORMATS strings, and add
+	the handler to the top-level library logger ("fontTools").
+
+	A number of optional keyword arguments may be specified, which can alter
+	the default behaviour.
+
+	logger    Specifies the logger name or a Logger instance to be configured.
+	          (it defaults to "fontTools" logger). Unlike basicConfig, this
+	          function can be called multiple times to reconfigure a logger.
+	          If the logger or any of its children already exists before the
+	          call is made, they will be reset before the new configuration
+	          is applied.
+	filename  Specifies that a FileHandler be created, using the specified
+	          filename, rather than a StreamHandler.
+	filemode  Specifies the mode to open the file, if filename is specified
+	          (if filemode is unspecified, it defaults to 'a').
+	format    Use the specified format string for the handler. This argument
+	          also accepts a dictionary of format strings keyed by level name,
+	          to allow customising the records appearance for specific levels.
+	          The special '*' key is for 'any other' level.
+	datefmt   Use the specified date/time format.
+	level     Set the logger level to the specified level.
+	stream    Use the specified stream to initialize the StreamHandler. Note
+	          that this argument is incompatible with 'filename' - if both
+	          are present, 'stream' is ignored.
+	handlers  If specified, this should be an iterable of already created
+	          handlers, which will be added to the logger. Any handler
+	          in the list which does not have a formatter assigned will be
+	          assigned the formatter created in this function.
+	filters   If specified, this should be an iterable of already created
+	          filters, which will be added to the handler(s), if the latter
+	          do(es) not already have filters assigned.
+	propagate All loggers have a "propagate" attribute initially set to True,
+	          which determines whether to continue searching for handlers up
+	          the logging hierarchy. By default, this arguments sets the
+	          "propagate" attribute to False.
+	"""
+	# using kwargs to enforce keyword-only arguments in py2.
+	handlers = kwargs.pop("handlers", None)
+	if handlers is None:
+		if "stream" in kwargs and "filename" in kwargs:
+			raise ValueError("'stream' and 'filename' should not be "
+							 "specified together")
+	else:
+		if "stream" in kwargs or "filename" in kwargs:
+			raise ValueError("'stream' or 'filename' should not be "
+							 "specified together with 'handlers'")
+	if handlers is None:
+		filename = kwargs.pop("filename", None)
+		mode = kwargs.pop("filemode", 'a')
+		if filename:
+			h = logging.FileHandler(filename, mode)
+		else:
+			stream = kwargs.pop("stream", None)
+			h = logging.StreamHandler(stream)
+		handlers = [h]
+	# By default, the top-level library logger is configured.
+	logger = kwargs.pop("logger", "fontTools")
+	if not logger or isinstance(logger, basestring):
+		# empty "" or None means the 'root' logger
+		logger = logging.getLogger(logger)
+	# before (re)configuring, reset named logger and its children (if exist)
+	_resetExistingLoggers(parent=logger.name)
+	# use DEFAULT_FORMATS if 'format' is None
+	fs = kwargs.pop("format", None)
+	dfs = kwargs.pop("datefmt", None)
+	# XXX: '%' is the only format style supported on both py2 and 3
+	style = kwargs.pop("style", '%')
+	fmt = LevelFormatter(fs, dfs, style)
+	filters = kwargs.pop("filters", [])
+	for h in handlers:
+		if h.formatter is None:
+			h.setFormatter(fmt)
+		if not h.filters:
+			for f in filters:
+				h.addFilter(f)
+		logger.addHandler(h)
+	if logger.name != "root":
+		# stop searching up the hierarchy for handlers
+		logger.propagate = kwargs.pop("propagate", False)
+	# set a custom severity level
+	level = kwargs.pop("level", None)
+	if level is not None:
+		logger.setLevel(level)
+	if kwargs:
+		keys = ', '.join(kwargs.keys())
+		raise ValueError('Unrecognised argument(s): %s' % keys)
+
+
+def _resetExistingLoggers(parent="root"):
+	""" Reset the logger named 'parent' and all its children to their initial
+	state, if they already exist in the current configuration.
+	"""
+	root = logging.root
+	# get sorted list of all existing loggers
+	existing = sorted(root.manager.loggerDict.keys())
+	if parent == "root":
+		# all the existing loggers are children of 'root'
+		loggers_to_reset = [parent] + existing
+	elif parent not in existing:
+		# nothing to do
+		return
+	elif parent in existing:
+		loggers_to_reset = [parent]
+		# collect children, starting with the entry after parent name
+		i = existing.index(parent) + 1
+		prefixed = parent + "."
+		pflen = len(prefixed)
+		num_existing = len(existing)
+		while i < num_existing:
+			if existing[i][:pflen] == prefixed:
+				loggers_to_reset.append(existing[i])
+			i += 1
+	for name in loggers_to_reset:
+		if name == "root":
+			root.setLevel(logging.WARNING)
+			for h in root.handlers[:]:
+				root.removeHandler(h)
+			for f in root.filters[:]:
+				root.removeFilters(f)
+			root.disabled = False
+		else:
+			logger = root.manager.loggerDict[name]
+			logger.level = logging.NOTSET
+			logger.handlers = []
+			logger.filters = []
+			logger.propagate = True
+			logger.disabled = False
+
+
+class Timer(object):
+	""" Keeps track of overall time and split/lap times.
+
+	>>> import time
+	>>> timer = Timer()
+	>>> time.sleep(0.01)
+	>>> print("First lap:", timer.split())
+	First lap: ...
+	>>> time.sleep(0.02)
+	>>> print("Second lap:", timer.split())
+	Second lap: ...
+	>>> print("Overall time:", timer.time())
+	Overall time: ...
+
+	Can be used as a context manager inside with-statements.
+
+	>>> with Timer() as t:
+	...     time.sleep(0.01)
+	>>> print("%0.3f seconds" % t.elapsed)
+	0... seconds
+
+	If initialised with a logger, it can log the elapsed time automatically
+	upon exiting the with-statement.
+
+	>>> import logging
+	>>> log = logging.getLogger("fontTools")
+	>>> configLogger(level="DEBUG", format="%(message)s", stream=sys.stdout)
+	>>> with Timer(log, 'do something'):
+	...     time.sleep(0.01)
+	Took ... to do something
+
+	The same Timer instance, holding a reference to a logger, can be reused
+	in multiple with-statements, optionally with different messages or levels.
+
+	>>> timer = Timer(log)
+	>>> with timer():
+	...     time.sleep(0.01)
+	elapsed time: ...s
+	>>> with timer('redo it', level=logging.INFO):
+	...     time.sleep(0.02)
+	Took ... to redo it
+
+	It can also be used as a function decorator to log the time elapsed to run
+	the decorated function.
+
+	>>> @timer()
+	... def test1():
+	...    time.sleep(0.01)
+	>>> @timer('run test 2', level=logging.INFO)
+	... def test2():
+	...    time.sleep(0.02)
+	>>> test1()
+	Took ... to run 'test1'
+	>>> test2()
+	Took ... to run test 2
+	"""
+
+	# timeit.default_timer choses the most accurate clock for each platform
+	_time = timeit.default_timer
+	default_msg = "elapsed time: %(time).3fs"
+	default_format = "Took %(time).3fs to %(msg)s"
+
+	def __init__(self, logger=None, msg=None, level=None, start=None):
+		self.reset(start)
+		if logger is None:
+			for arg in ('msg', 'level'):
+				if locals().get(arg) is not None:
+					raise ValueError(
+						"'%s' can't be specified without a 'logger'" % arg)
+		self.logger = logger
+		self.level = level if level is not None else TIME_LEVEL
+		self.msg = msg
+
+	def reset(self, start=None):
+		""" Reset timer to 'start_time' or the current time. """
+		if start is None:
+			self.start = self._time()
+		else:
+			self.start = start
+		self.last = self.start
+		self.elapsed = 0.0
+
+	def time(self):
+		""" Return the overall time (in seconds) since the timer started. """
+		return self._time() - self.start
+
+	def split(self):
+		""" Split and return the lap time (in seconds) in between splits. """
+		current = self._time()
+		self.elapsed = current - self.last
+		self.last = current
+		return self.elapsed
+
+	def formatTime(self, msg, time):
+		""" Format 'time' value in 'msg' and return formatted string.
+		If 'msg' contains a '%(time)' format string, try to use that.
+		Otherwise, use the predefined 'default_format'.
+		If 'msg' is empty or None, fall back to 'default_msg'.
+		"""
+		if not msg:
+			msg = self.default_msg
+		if msg.find("%(time)") < 0:
+			msg = self.default_format % {"msg": msg, "time": time}
+		else:
+			try:
+				msg = msg % {"time": time}
+			except (KeyError, ValueError):
+				pass  # skip if the format string is malformed
+		return msg
+
+	def __enter__(self):
+		""" Start a new lap """
+		self.last = self._time()
+		self.elapsed = 0.0
+		return self
+
+	def __exit__(self, exc_type, exc_value, traceback):
+		""" End the current lap. If timer has a logger, log the time elapsed,
+		using the format string in self.msg (or the default one).
+		"""
+		time = self.split()
+		if self.logger is None or exc_type:
+			# if there's no logger attached, or if any exception occurred in
+			# the with-statement, exit without logging the time
+			return
+		message = self.formatTime(self.msg, time)
+		# Allow log handlers to see the individual parts to facilitate things
+		# like a server accumulating aggregate stats.
+		msg_parts = { 'msg': self.msg, 'time': time }
+		self.logger.log(self.level, message, msg_parts)
+
+	def __call__(self, func_or_msg=None, **kwargs):
+		""" If the first argument is a function, return a decorator which runs
+		the wrapped function inside Timer's context manager.
+		Otherwise, treat the first argument as a 'msg' string and return an updated
+		Timer instance, referencing the same logger.
+		A 'level' keyword can also be passed to override self.level.
+		"""
+		if isinstance(func_or_msg, collections.Callable):
+			func = func_or_msg
+			# use the function name when no explicit 'msg' is provided
+			if not self.msg:
+				self.msg = "run '%s'" % func.__name__
+
+			@wraps(func)
+			def wrapper(*args, **kwds):
+				with self:
+					return func(*args, **kwds)
+			return wrapper
+		else:
+			msg = func_or_msg or kwargs.get("msg")
+			level = kwargs.get("level", self.level)
+			return self.__class__(self.logger, msg, level)
+
+	def __float__(self):
+		return self.elapsed
+
+	def __int__(self):
+		return int(self.elapsed)
+
+	def __str__(self):
+		return "%.3f" % self.elapsed
+
+
+class ChannelsFilter(logging.Filter):
+	""" Filter out records emitted from a list of enabled channel names,
+	including their children. It works the same as the logging.Filter class,
+	but allows to specify multiple channel names.
+
+	>>> import sys
+	>>> handler = logging.StreamHandler(sys.stdout)
+	>>> handler.setFormatter(logging.Formatter("%(message)s"))
+	>>> filter = ChannelsFilter("A.B", "C.D")
+	>>> handler.addFilter(filter)
+	>>> root = logging.getLogger()
+	>>> root.addHandler(handler)
+	>>> root.setLevel(level=logging.DEBUG)
+	>>> logging.getLogger('A.B').debug('this record passes through')
+	this record passes through
+	>>> logging.getLogger('A.B.C').debug('records from children also pass')
+	records from children also pass
+	>>> logging.getLogger('C.D').debug('this one as well')
+	this one as well
+	>>> logging.getLogger('A.B.').debug('also this one')
+	also this one
+	>>> logging.getLogger('A.F').debug('but this one does not!')
+	>>> logging.getLogger('C.DE').debug('neither this one!')
+	"""
+
+	def __init__(self, *names):
+		self.names = names
+		self.num = len(names)
+		self.lenghts = {n: len(n) for n in names}
+
+	def filter(self, record):
+		if self.num == 0:
+			return True
+		for name in self.names:
+			nlen = self.lenghts[name]
+			if name == record.name:
+				return True
+			elif (record.name.find(name, 0, nlen) == 0
+					and record.name[nlen] == "."):
+				return True
+		return False
+
+
+class CapturingLogHandler(logging.Handler):
+	def __init__(self, logger, level):
+		self.records = []
+		self.level = logging._checkLevel(level)
+		if isinstance(logger, basestring):
+			self.logger = logging.getLogger(logger)
+		else:
+			self.logger = logger
+
+	def __enter__(self):
+		self.original_disabled = self.logger.disabled
+		self.original_level = self.logger.level
+
+		self.logger.addHandler(self)
+		self.logger.level = self.level
+		self.logger.disabled = False
+
+		return self
+
+	def __exit__(self, type, value, traceback):
+		self.logger.removeHandler(self)
+		self.logger.level = self.original_level
+		self.logger.disabled = self.logger.disabled
+		return self
+
+	def handle(self, record):
+		self.records.append(record)
+
+	def emit(self, record):
+		pass
+
+	def createLock(self):
+		self.lock = None
+
+	def assertRegex(self, regexp):
+		import re
+		pattern = re.compile(regexp)
+		for r in self.records:
+			if pattern.search(r.getMessage()):
+				return True
+		assert 0, "Pattern '%s' not found in logger records" % regexp
+
+
+class LogMixin(object):
+	""" Mixin class that adds logging functionality to another class.
+	You can define a new class that subclasses from LogMixin as well as
+	other base classes through multiple inheritance.
+	All instances of that class will have a 'log' property that returns
+	a logging.Logger named after their respective <module>.<class>.
+	For example:
+
+	>>> class BaseClass(object):
+	...     pass
+	>>> class MyClass(LogMixin, BaseClass):
+	...     pass
+	>>> a = MyClass()
+	>>> isinstance(a.log, logging.Logger)
+	True
+	>>> print(a.log.name)
+	fontTools.misc.loggingTools.MyClass
+	>>> class AnotherClass(MyClass):
+	...     pass
+	>>> b = AnotherClass()
+	>>> isinstance(b.log, logging.Logger)
+	True
+	>>> print(b.log.name)
+	fontTools.misc.loggingTools.AnotherClass
+	"""
+
+	@property
+	def log(self):
+		if not hasattr(self, "_log"):
+			name = ".".join(
+				(self.__class__.__module__, self.__class__.__name__)
+			)
+			self._log = logging.getLogger(name)
+		return self._log
+
+
+def deprecateArgument(name, msg, category=UserWarning):
+	""" Raise a warning about deprecated function argument 'name'. """
+	warnings.warn(
+		"%r is deprecated; %s" % (name, msg), category=category, stacklevel=3)
+
+
+def deprecateFunction(msg, category=UserWarning):
+	""" Decorator to raise a warning when a deprecated function is called. """
+	def decorator(func):
+		@wraps(func)
+		def wrapper(*args, **kwargs):
+			warnings.warn(
+				"%r is deprecated; %s" % (func.__name__, msg),
+				category=category, stacklevel=2)
+			return func(*args, **kwargs)
+		return wrapper
+	return decorator
+
+
+class LastResortLogger(logging.Logger):
+	""" Adds support for 'lastResort' handler introduced in Python 3.2.
+	It allows to print messages to sys.stderr even when no explicit handler
+	was configured.
+	To enable it, you can do:
+
+		import logging
+		logging.lastResort = StderrHandler(logging.WARNING)
+		logging.setLoggerClass(LastResortLogger)
+	"""
+
+	def callHandlers(self, record):
+		# this is the same as Python 3.5's logging.Logger.callHandlers
+		c = self
+		found = 0
+		while c:
+			for hdlr in c.handlers:
+				found = found + 1
+				if record.levelno >= hdlr.level:
+					hdlr.handle(record)
+			if not c.propagate:
+				c = None  # break out
+			else:
+				c = c.parent
+		if found == 0:
+			if logging.lastResort:
+				if record.levelno >= logging.lastResort.level:
+					logging.lastResort.handle(record)
+			elif (
+				logging.raiseExceptions
+				and not self.manager.emittedNoHandlerWarning
+			):
+				sys.stderr.write(
+					"No handlers could be found for logger"
+					' "%s"\n' % self.name
+				)
+				self.manager.emittedNoHandlerWarning = True
+
+
+class StderrHandler(logging.StreamHandler):
+	""" This class is like a StreamHandler using sys.stderr, but always uses
+	whateve sys.stderr is currently set to rather than the value of
+	sys.stderr at handler construction time.
+	"""
+
+	def __init__(self, level=logging.NOTSET):
+		"""
+		Initialize the handler.
+		"""
+		logging.Handler.__init__(self, level)
+
+	@property
+	def stream(self):
+		# the try/execept avoids failures during interpreter shutdown, when
+		# globals are set to None
+		try:
+			return sys.stderr
+		except AttributeError:
+			return __import__("sys").stderr
+
+
+if __name__ == "__main__":
+	import doctest
+	sys.exit(doctest.testmod(optionflags=doctest.ELLIPSIS).failed)
diff --git a/Lib/fontTools/misc/macCreatorType.py b/Lib/fontTools/misc/macCreatorType.py
index 5f2e18a..2b33e89 100644
--- a/Lib/fontTools/misc/macCreatorType.py
+++ b/Lib/fontTools/misc/macCreatorType.py
@@ -2,10 +2,14 @@
 from fontTools.misc.py23 import *
 import sys
 try:
+	import xattr
+except ImportError:
+	xattr = None
+try:
 	import MacOS
 except ImportError:
 	MacOS = None
-from .py23 import *
+
 
 def _reverseString(s):
 	s = list(s)
@@ -14,11 +18,21 @@
 
 
 def getMacCreatorAndType(path):
+	if xattr is not None:
+		try:
+			finderInfo = xattr.getxattr(path, 'com.apple.FinderInfo')
+		except (KeyError, IOError):
+			pass
+		else:
+			fileType = Tag(finderInfo[:4])
+			fileCreator = Tag(finderInfo[4:8])
+			return fileCreator, fileType
 	if MacOS is not None:
 		fileCreator, fileType = MacOS.GetCreatorAndType(path)
-		if sys.byteorder == "little":
+		if sys.version_info[:2] < (2, 7) and sys.byteorder == "little":
 			# work around bug in MacOS.GetCreatorAndType() on intel:
 			# http://bugs.python.org/issue1594
+			# (fixed with Python 2.7)
 			fileCreator = _reverseString(fileCreator)
 			fileType = _reverseString(fileType)
 		return fileCreator, fileType
@@ -27,10 +41,11 @@
 
 
 def setMacCreatorAndType(path, fileCreator, fileType):
+	if xattr is not None:
+		from fontTools.misc.textTools import pad
+		if not all(len(s) == 4 for s in (fileCreator, fileType)):
+			raise TypeError('arg must be string of 4 chars')
+		finderInfo = pad(bytesjoin([fileType, fileCreator]), 32)
+		xattr.setxattr(path, 'com.apple.FinderInfo', finderInfo)
 	if MacOS is not None:
-		if sys.byteorder == "little":
-			# work around bug in MacOS.SetCreatorAndType() on intel:
-			# http://bugs.python.org/issue1594
-			fileCreator = _reverseString(fileCreator)
-			fileType = _reverseString(fileType)
 		MacOS.SetCreatorAndType(path, fileCreator, fileType)
diff --git a/Lib/fontTools/misc/macRes.py b/Lib/fontTools/misc/macRes.py
new file mode 100644
index 0000000..20bfa71
--- /dev/null
+++ b/Lib/fontTools/misc/macRes.py
@@ -0,0 +1,233 @@
+""" Tools for reading Mac resource forks. """
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+import struct
+from fontTools.misc import sstruct
+from collections import OrderedDict
+try:
+	from collections.abc import MutableMapping
+except ImportError:
+	from UserDict import DictMixin as MutableMapping
+
+
+class ResourceError(Exception):
+	pass
+
+
+class ResourceReader(MutableMapping):
+
+	def __init__(self, fileOrPath):
+		self._resources = OrderedDict()
+		if hasattr(fileOrPath, 'read'):
+			self.file = fileOrPath
+		else:
+			try:
+				# try reading from the resource fork (only works on OS X)
+				self.file = self.openResourceFork(fileOrPath)
+				self._readFile()
+				return
+			except (ResourceError, IOError):
+				# if it fails, use the data fork
+				self.file = self.openDataFork(fileOrPath)
+		self._readFile()
+
+	@staticmethod
+	def openResourceFork(path):
+		with open(path + '/..namedfork/rsrc', 'rb') as resfork:
+			data = resfork.read()
+		infile = BytesIO(data)
+		infile.name = path
+		return infile
+
+	@staticmethod
+	def openDataFork(path):
+		with open(path, 'rb') as datafork:
+			data = datafork.read()
+		infile = BytesIO(data)
+		infile.name = path
+		return infile
+
+	def _readFile(self):
+		self._readHeaderAndMap()
+		self._readTypeList()
+
+	def _read(self, numBytes, offset=None):
+		if offset is not None:
+			try:
+				self.file.seek(offset)
+			except OverflowError:
+				raise ResourceError("Failed to seek offset ('offset' is too large)")
+			if self.file.tell() != offset:
+				raise ResourceError('Failed to seek offset (reached EOF)')
+		try:
+			data = self.file.read(numBytes)
+		except OverflowError:
+			raise ResourceError("Cannot read resource ('numBytes' is too large)")
+		if len(data) != numBytes:
+			raise ResourceError('Cannot read resource (not enough data)')
+		return data
+
+	def _readHeaderAndMap(self):
+		self.file.seek(0)
+		headerData = self._read(ResourceForkHeaderSize)
+		sstruct.unpack(ResourceForkHeader, headerData, self)
+		# seek to resource map, skip reserved
+		mapOffset = self.mapOffset + 22
+		resourceMapData = self._read(ResourceMapHeaderSize, mapOffset)
+		sstruct.unpack(ResourceMapHeader, resourceMapData, self)
+		self.absTypeListOffset = self.mapOffset + self.typeListOffset
+		self.absNameListOffset = self.mapOffset + self.nameListOffset
+
+	def _readTypeList(self):
+		absTypeListOffset = self.absTypeListOffset
+		numTypesData = self._read(2, absTypeListOffset)
+		self.numTypes, = struct.unpack('>H', numTypesData)
+		absTypeListOffset2 = absTypeListOffset + 2
+		for i in range(self.numTypes + 1):
+			resTypeItemOffset = absTypeListOffset2 + ResourceTypeItemSize * i
+			resTypeItemData = self._read(ResourceTypeItemSize, resTypeItemOffset)
+			item = sstruct.unpack(ResourceTypeItem, resTypeItemData)
+			resType = tostr(item['type'], encoding='mac-roman')
+			refListOffset = absTypeListOffset + item['refListOffset']
+			numRes = item['numRes'] + 1
+			resources = self._readReferenceList(resType, refListOffset, numRes)
+			self._resources[resType] = resources
+
+	def _readReferenceList(self, resType, refListOffset, numRes):
+		resources = []
+		for i in range(numRes):
+			refOffset = refListOffset + ResourceRefItemSize * i
+			refData = self._read(ResourceRefItemSize, refOffset)
+			res = Resource(resType)
+			res.decompile(refData, self)
+			resources.append(res)
+		return resources
+
+	def __getitem__(self, resType):
+		return self._resources[resType]
+
+	def __delitem__(self, resType):
+		del self._resources[resType]
+
+	def __setitem__(self, resType, resources):
+		self._resources[resType] = resources
+
+	def __len__(self):
+		return len(self._resources)
+
+	def __iter__(self):
+		return iter(self._resources)
+
+	def keys(self):
+		return self._resources.keys()
+
+	@property
+	def types(self):
+		return list(self._resources.keys())
+
+	def countResources(self, resType):
+		"""Return the number of resources of a given type."""
+		try:
+			return len(self[resType])
+		except KeyError:
+			return 0
+
+	def getIndices(self, resType):
+		numRes = self.countResources(resType)
+		if numRes:
+			return list(range(1, numRes+1))
+		else:
+			return []
+
+	def getNames(self, resType):
+		"""Return list of names of all resources of a given type."""
+		return [res.name for res in self.get(resType, []) if res.name is not None]
+
+	def getIndResource(self, resType, index):
+		"""Return resource of given type located at an index ranging from 1
+		to the number of resources for that type, or None if not found.
+		"""
+		if index < 1:
+			return None
+		try:
+			res = self[resType][index-1]
+		except (KeyError, IndexError):
+			return None
+		return res
+
+	def getNamedResource(self, resType, name):
+		"""Return the named resource of given type, else return None."""
+		name = tostr(name, encoding='mac-roman')
+		for res in self.get(resType, []):
+			if res.name == name:
+				return res
+		return None
+
+	def close(self):
+		if not self.file.closed:
+			self.file.close()
+
+
+class Resource(object):
+
+	def __init__(self, resType=None, resData=None, resID=None, resName=None,
+			     resAttr=None):
+		self.type = resType
+		self.data = resData
+		self.id = resID
+		self.name = resName
+		self.attr = resAttr
+
+	def decompile(self, refData, reader):
+		sstruct.unpack(ResourceRefItem, refData, self)
+		# interpret 3-byte dataOffset as (padded) ULONG to unpack it with struct
+		self.dataOffset, = struct.unpack('>L', bytesjoin([b"\0", self.dataOffset]))
+		absDataOffset = reader.dataOffset + self.dataOffset
+		dataLength, = struct.unpack(">L", reader._read(4, absDataOffset))
+		self.data = reader._read(dataLength)
+		if self.nameOffset == -1:
+			return
+		absNameOffset = reader.absNameListOffset + self.nameOffset
+		nameLength, = struct.unpack('B', reader._read(1, absNameOffset))
+		name, = struct.unpack('>%ss' % nameLength, reader._read(nameLength))
+		self.name = tostr(name, encoding='mac-roman')
+
+
+ResourceForkHeader = """
+		> # big endian
+		dataOffset:     L
+		mapOffset:      L
+		dataLen:        L
+		mapLen:         L
+"""
+
+ResourceForkHeaderSize = sstruct.calcsize(ResourceForkHeader)
+
+ResourceMapHeader = """
+		> # big endian
+		attr:              H
+		typeListOffset:    H
+		nameListOffset:    H
+"""
+
+ResourceMapHeaderSize = sstruct.calcsize(ResourceMapHeader)
+
+ResourceTypeItem = """
+		> # big endian
+		type:              4s
+		numRes:            H
+		refListOffset:     H
+"""
+
+ResourceTypeItemSize = sstruct.calcsize(ResourceTypeItem)
+
+ResourceRefItem = """
+		> # big endian
+		id:                h
+		nameOffset:        h
+		attr:              B
+		dataOffset:        3s
+		reserved:          L
+"""
+
+ResourceRefItemSize = sstruct.calcsize(ResourceRefItem)
diff --git a/Lib/fontTools/misc/psCharStrings.py b/Lib/fontTools/misc/psCharStrings.py
index 6ffdb99..ff78271 100644
--- a/Lib/fontTools/misc/psCharStrings.py
+++ b/Lib/fontTools/misc/psCharStrings.py
@@ -1,79 +1,97 @@
-"""psCharStrings.py -- module implementing various kinds of CharStrings: 
+"""psCharStrings.py -- module implementing various kinds of CharStrings:
 CFF dictionary data and Type1/Type2 CharStrings.
 """
 
 from __future__ import print_function, division, absolute_import
 from fontTools.misc.py23 import *
+from fontTools.misc.fixedTools import fixedToFloat, otRound
+from fontTools.pens.boundsPen import BoundsPen
 import struct
+import logging
 
 
-DEBUG = 0
+log = logging.getLogger(__name__)
+
+
+def read_operator(self, b0, data, index):
+	if b0 == 12:
+		op = (b0, byteord(data[index]))
+		index = index+1
+	else:
+		op = b0
+	try:
+		operator = self.operators[op]
+	except KeyError:
+		return None, index
+	value = self.handle_operator(operator)
+	return value, index
+
+def read_byte(self, b0, data, index):
+	return b0 - 139, index
+
+def read_smallInt1(self, b0, data, index):
+	b1 = byteord(data[index])
+	return (b0-247)*256 + b1 + 108, index+1
+
+def read_smallInt2(self, b0, data, index):
+	b1 = byteord(data[index])
+	return -(b0-251)*256 - b1 - 108, index+1
+
+def read_shortInt(self, b0, data, index):
+	value, = struct.unpack(">h", data[index:index+2])
+	return value, index+2
+
+def read_longInt(self, b0, data, index):
+	value, = struct.unpack(">l", data[index:index+4])
+	return value, index+4
+
+def read_fixed1616(self, b0, data, index):
+	value, = struct.unpack(">l", data[index:index+4])
+	return fixedToFloat(value, precisionBits=16), index+4
+
+def read_reserved(self, b0, data, index):
+	assert NotImplementedError
+	return NotImplemented, index
+
+def read_realNumber(self, b0, data, index):
+	number = ''
+	while True:
+		b = byteord(data[index])
+		index = index + 1
+		nibble0 = (b & 0xf0) >> 4
+		nibble1 = b & 0x0f
+		if nibble0 == 0xf:
+			break
+		number = number + realNibbles[nibble0]
+		if nibble1 == 0xf:
+			break
+		number = number + realNibbles[nibble1]
+	return float(number), index
 
 
 t1OperandEncoding = [None] * 256
-t1OperandEncoding[0:32] = (32) * ["do_operator"]
-t1OperandEncoding[32:247] = (247 - 32) * ["read_byte"]
-t1OperandEncoding[247:251] = (251 - 247) * ["read_smallInt1"]
-t1OperandEncoding[251:255] = (255 - 251) * ["read_smallInt2"]
-t1OperandEncoding[255] = "read_longInt"
+t1OperandEncoding[0:32] = (32) * [read_operator]
+t1OperandEncoding[32:247] = (247 - 32) * [read_byte]
+t1OperandEncoding[247:251] = (251 - 247) * [read_smallInt1]
+t1OperandEncoding[251:255] = (255 - 251) * [read_smallInt2]
+t1OperandEncoding[255] = read_longInt
 assert len(t1OperandEncoding) == 256
 
 t2OperandEncoding = t1OperandEncoding[:]
-t2OperandEncoding[28] = "read_shortInt"
-t2OperandEncoding[255] = "read_fixed1616"
+t2OperandEncoding[28] = read_shortInt
+t2OperandEncoding[255] = read_fixed1616
 
 cffDictOperandEncoding = t2OperandEncoding[:]
-cffDictOperandEncoding[29] = "read_longInt"
-cffDictOperandEncoding[30] = "read_realNumber"
-cffDictOperandEncoding[255] = "reserved"
+cffDictOperandEncoding[29] = read_longInt
+cffDictOperandEncoding[30] = read_realNumber
+cffDictOperandEncoding[255] = read_reserved
 
 
-realNibbles = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 
+realNibbles = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
 		'.', 'E', 'E-', None, '-']
-realNibblesDict = {}
-for _i in range(len(realNibbles)):
-	realNibblesDict[realNibbles[_i]] = _i
+realNibblesDict = {v:i for i,v in enumerate(realNibbles)}
 
-
-class ByteCodeBase(object):
-	
-	def read_byte(self, b0, data, index):
-		return b0 - 139, index
-	
-	def read_smallInt1(self, b0, data, index):
-		b1 = byteord(data[index])
-		return (b0-247)*256 + b1 + 108, index+1
-	
-	def read_smallInt2(self, b0, data, index):
-		b1 = byteord(data[index])
-		return -(b0-251)*256 - b1 - 108, index+1
-	
-	def read_shortInt(self, b0, data, index):
-		value, = struct.unpack(">h", data[index:index+2])
-		return value, index+2
-	
-	def read_longInt(self, b0, data, index):
-		value, = struct.unpack(">l", data[index:index+4])
-		return value, index+4
-	
-	def read_fixed1616(self, b0, data, index):
-		value, = struct.unpack(">l", data[index:index+4])
-		return value / 65536, index+4
-	
-	def read_realNumber(self, b0, data, index):
-		number = ''
-		while True:
-			b = byteord(data[index])
-			index = index + 1
-			nibble0 = (b & 0xf0) >> 4
-			nibble1 = b & 0x0f
-			if nibble0 == 0xf:
-				break
-			number = number + realNibbles[nibble0]
-			if nibble1 == 0xf:
-				break
-			number = number + realNibbles[nibble1]
-		return float(number), index
+maxOpStack = 193
 
 
 def buildOperatorDict(operatorList):
@@ -92,63 +110,63 @@
 
 
 t2Operators = [
-#	opcode     name
-	(1,        'hstem'),
-	(3,        'vstem'),
-	(4,        'vmoveto'),
-	(5,        'rlineto'),
-	(6,        'hlineto'),
-	(7,        'vlineto'),
-	(8,        'rrcurveto'),
-	(10,       'callsubr'),
-	(11,       'return'),
-	(14,       'endchar'),
-	(16,       'blend'),
-	(18,       'hstemhm'),
-	(19,       'hintmask'),
-	(20,       'cntrmask'),
-	(21,       'rmoveto'),
-	(22,       'hmoveto'),
-	(23,       'vstemhm'),
-	(24,       'rcurveline'),
-	(25,       'rlinecurve'),
-	(26,       'vvcurveto'),
-	(27,       'hhcurveto'),
-#	(28,       'shortint'),  # not really an operator
-	(29,       'callgsubr'),
-	(30,       'vhcurveto'),
-	(31,       'hvcurveto'),
-	((12, 0),  'ignore'),  # dotsection. Yes, there a few very early OTF/CFF
-	                   # fonts with this deprecated operator. Just ignore it.
-	((12, 3),  'and'),
-	((12, 4),  'or'),
-	((12, 5),  'not'),
-	((12, 8),  'store'),
-	((12, 9),  'abs'),
-	((12, 10), 'add'),
-	((12, 11), 'sub'),
-	((12, 12), 'div'),
-	((12, 13), 'load'),
-	((12, 14), 'neg'),
-	((12, 15), 'eq'),
-	((12, 18), 'drop'),
-	((12, 20), 'put'),
-	((12, 21), 'get'),
-	((12, 22), 'ifelse'),
-	((12, 23), 'random'),
-	((12, 24), 'mul'),
-	((12, 26), 'sqrt'),
-	((12, 27), 'dup'),
-	((12, 28), 'exch'),
-	((12, 29), 'index'),
-	((12, 30), 'roll'),
-	((12, 34), 'hflex'),
-	((12, 35), 'flex'),
-	((12, 36), 'hflex1'),
-	((12, 37), 'flex1'),
+#	opcode		name
+	(1,		'hstem'),
+	(3,		'vstem'),
+	(4,		'vmoveto'),
+	(5,		'rlineto'),
+	(6,		'hlineto'),
+	(7,		'vlineto'),
+	(8,		'rrcurveto'),
+	(10,		'callsubr'),
+	(11,		'return'),
+	(14,		'endchar'),
+	(15,		'vsindex'),
+	(16,		'blend'),
+	(18,		'hstemhm'),
+	(19,		'hintmask'),
+	(20,		'cntrmask'),
+	(21,		'rmoveto'),
+	(22,		'hmoveto'),
+	(23,		'vstemhm'),
+	(24,		'rcurveline'),
+	(25,		'rlinecurve'),
+	(26,		'vvcurveto'),
+	(27,		'hhcurveto'),
+#	(28,		'shortint'),  # not really an operator
+	(29,		'callgsubr'),
+	(30,		'vhcurveto'),
+	(31,		'hvcurveto'),
+	((12, 0),	'ignore'),	# dotsection. Yes, there a few very early OTF/CFF
+							# fonts with this deprecated operator. Just ignore it.
+	((12, 3),	'and'),
+	((12, 4),	'or'),
+	((12, 5),	'not'),
+	((12, 8),	'store'),
+	((12, 9),	'abs'),
+	((12, 10),	'add'),
+	((12, 11),	'sub'),
+	((12, 12),	'div'),
+	((12, 13),	'load'),
+	((12, 14),	'neg'),
+	((12, 15),	'eq'),
+	((12, 18),	'drop'),
+	((12, 20),	'put'),
+	((12, 21),	'get'),
+	((12, 22),	'ifelse'),
+	((12, 23),	'random'),
+	((12, 24),	'mul'),
+	((12, 26),	'sqrt'),
+	((12, 27),	'dup'),
+	((12, 28),	'exch'),
+	((12, 29),	'index'),
+	((12, 30),	'roll'),
+	((12, 34),	'hflex'),
+	((12, 35),	'flex'),
+	((12, 36),	'hflex1'),
+	((12, 37),	'flex1'),
 ]
 
-
 def getIntEncoder(format):
 	if format == "cff":
 		fourByteOp = bytechr(29)
@@ -157,7 +175,7 @@
 	else:
 		assert format == "t2"
 		fourByteOp = None
-	
+
 	def encodeInt(value, fourByteOp=fourByteOp, bytechr=bytechr,
 			pack=struct.pack, unpack=struct.unpack):
 		if -107 <= value <= 107:
@@ -189,7 +207,7 @@
 		else:
 			code = fourByteOp + pack(">l", value)
 		return code
-	
+
 	return encodeInt
 
 
@@ -199,7 +217,7 @@
 
 def encodeFixed(f, pack=struct.pack):
 	# For T2 only
-	return b"\xff" + pack(">l", int(round(f * 65536)))
+	return b"\xff" + pack(">l", otRound(f * 65536))
 
 def encodeFloat(f):
 	# For CFF only, used in cffLib
@@ -228,275 +246,39 @@
 class CharStringCompileError(Exception): pass
 
 
-class T2CharString(ByteCodeBase):
-	
-	operandEncoding = t2OperandEncoding
-	operators, opcodes = buildOperatorDict(t2Operators)
-	
-	def __init__(self, bytecode=None, program=None, private=None, globalSubrs=None):
-		if program is None:
-			program = []
-		self.bytecode = bytecode
-		self.program = program
-		self.private = private
-		self.globalSubrs = globalSubrs if globalSubrs is not None else []
-	
-	def __repr__(self):
-		if self.bytecode is None:
-			return "<%s (source) at %x>" % (self.__class__.__name__, id(self))
-		else:
-			return "<%s (bytecode) at %x>" % (self.__class__.__name__, id(self))
-	
-	def getIntEncoder(self):
-		return encodeIntT2
-	
-	def getFixedEncoder(self):
-		return encodeFixed
-
-	def decompile(self):
-		if not self.needsDecompilation():
-			return
-		subrs = getattr(self.private, "Subrs", [])
-		decompiler = SimpleT2Decompiler(subrs, self.globalSubrs)
-		decompiler.execute(self)
-	
-	def draw(self, pen):
-		subrs = getattr(self.private, "Subrs", [])
-		extractor = T2OutlineExtractor(pen, subrs, self.globalSubrs,
-				self.private.nominalWidthX, self.private.defaultWidthX)
-		extractor.execute(self)
-		self.width = extractor.width
-	
-	def compile(self):
-		if self.bytecode is not None:
-			return
-		assert self.program, "illegal CharString: decompiled to empty program"
-		assert self.program[-1] in ("endchar", "return", "callsubr", "callgsubr",
-				"seac"), "illegal CharString"
-		bytecode = []
-		opcodes = self.opcodes
-		program = self.program
-		encodeInt = self.getIntEncoder()
-		encodeFixed = self.getFixedEncoder()
-		i = 0
-		end = len(program)
-		while i < end:
-			token = program[i]
-			i = i + 1
-			tp = type(token)
-			if issubclass(tp, basestring):
-				try:
-					bytecode.extend(bytechr(b) for b in opcodes[token])
-				except KeyError:
-					raise CharStringCompileError("illegal operator: %s" % token)
-				if token in ('hintmask', 'cntrmask'):
-					bytecode.append(program[i])  # hint mask
-					i = i + 1
-			elif tp == int:
-				bytecode.append(encodeInt(token))
-			elif tp == float:
-				bytecode.append(encodeFixed(token))
-			else:
-				assert 0, "unsupported type: %s" % tp
-		try:
-			bytecode = bytesjoin(bytecode)
-		except TypeError:
-			print(bytecode)
-			raise
-		self.setBytecode(bytecode)
-	
-	def needsDecompilation(self):
-		return self.bytecode is not None
-	
-	def setProgram(self, program):
-		self.program = program
-		self.bytecode = None
-	
-	def setBytecode(self, bytecode):
-		self.bytecode = bytecode
-		self.program = None
-	
-	def getToken(self, index, 
-			len=len, byteord=byteord, getattr=getattr, type=type, StringType=str):
-		if self.bytecode is not None:
-			if index >= len(self.bytecode):
-				return None, 0, 0
-			b0 = byteord(self.bytecode[index])
-			index = index + 1
-			code = self.operandEncoding[b0]
-			handler = getattr(self, code)
-			token, index = handler(b0, self.bytecode, index)
-		else:
-			if index >= len(self.program):
-				return None, 0, 0
-			token = self.program[index]
-			index = index + 1
-		isOperator = isinstance(token, StringType)
-		return token, isOperator, index
-	
-	def getBytes(self, index, nBytes):
-		if self.bytecode is not None:
-			newIndex = index + nBytes
-			bytes = self.bytecode[index:newIndex]
-			index = newIndex
-		else:
-			bytes = self.program[index]
-			index = index + 1
-		assert len(bytes) == nBytes
-		return bytes, index
-	
-	def do_operator(self, b0, data, index):
-		if b0 == 12:
-			op = (b0, byteord(data[index]))
-			index = index+1
-		else:
-			op = b0
-		operator = self.operators[op]
-		return operator, index
-	
-	def toXML(self, xmlWriter):
-		from fontTools.misc.textTools import num2binary
-		if self.bytecode is not None:
-			xmlWriter.dumphex(self.bytecode)
-		else:
-			index = 0
-			args = []
-			while True:
-				token, isOperator, index = self.getToken(index)
-				if token is None:
-					break
-				if isOperator:
-					args = [str(arg) for arg in args]
-					if token in ('hintmask', 'cntrmask'):
-						hintMask, isOperator, index = self.getToken(index)
-						bits = []
-						for byte in hintMask:
-							bits.append(num2binary(byteord(byte), 8))
-						hintMask = strjoin(bits)
-						line = ' '.join(args + [token, hintMask])
-					else:
-						line = ' '.join(args + [token])
-					xmlWriter.write(line)
-					xmlWriter.newline()
-					args = []
-				else:
-					args.append(token)
-	
-	def fromXML(self, name, attrs, content):
-		from fontTools.misc.textTools import binary2num, readHex
-		if attrs.get("raw"):
-			self.setBytecode(readHex(content))
-			return
-		content = strjoin(content)
-		content = content.split()
-		program = []
-		end = len(content)
-		i = 0
-		while i < end:
-			token = content[i]
-			i = i + 1
-			try:
-				token = int(token)
-			except ValueError:
-				try:
-					token = float(token)
-				except ValueError:
-					program.append(token)
-					if token in ('hintmask', 'cntrmask'):
-						mask = content[i]
-						maskBytes = b""
-						for j in range(0, len(mask), 8):
-							maskBytes = maskBytes + bytechr(binary2num(mask[j:j+8]))
-						program.append(maskBytes)
-						i = i + 1
-				else:
-					program.append(token)
-			else:
-				program.append(token)
-		self.setProgram(program)
-
-
-t1Operators = [
-#	opcode     name
-	(1,        'hstem'),
-	(3,        'vstem'),
-	(4,        'vmoveto'),
-	(5,        'rlineto'),
-	(6,        'hlineto'),
-	(7,        'vlineto'),
-	(8,        'rrcurveto'),
-	(9,        'closepath'),
-	(10,       'callsubr'),
-	(11,       'return'),
-	(13,       'hsbw'),
-	(14,       'endchar'),
-	(21,       'rmoveto'),
-	(22,       'hmoveto'),
-	(30,       'vhcurveto'),
-	(31,       'hvcurveto'),
-	((12, 0),  'dotsection'),
-	((12, 1),  'vstem3'),
-	((12, 2),  'hstem3'),
-	((12, 6),  'seac'),
-	((12, 7),  'sbw'),
-	((12, 12), 'div'),
-	((12, 16), 'callothersubr'),
-	((12, 17), 'pop'),
-	((12, 33), 'setcurrentpoint'),
-]
-
-class T1CharString(T2CharString):
-	
-	operandEncoding = t1OperandEncoding
-	operators, opcodes = buildOperatorDict(t1Operators)
-	
-	def __init__(self, bytecode=None, program=None, subrs=None):
-		if program is None:
-			program = []
-		self.bytecode = bytecode
-		self.program = program
-		self.subrs = subrs
-
-	def getIntEncoder(self):
-		return encodeIntT1
-
-	def getFixedEncoder(self):
-		def encodeFixed(value):
-			raise TypeError("Type 1 charstrings don't support floating point operands")
-
-	def decompile(self):
-		if self.bytecode is None:
-			return
-		program = []
-		index = 0
-		while True:
-			token, isOperator, index = self.getToken(index)
-			if token is None:
-				break
-			program.append(token)
-		self.setProgram(program)
-
-	def draw(self, pen):
-		extractor = T1OutlineExtractor(pen, self.subrs)
-		extractor.execute(self)
-		self.width = extractor.width
-
-
 class SimpleT2Decompiler(object):
-	
-	def __init__(self, localSubrs, globalSubrs):
+
+	def __init__(self, localSubrs, globalSubrs, private=None):
 		self.localSubrs = localSubrs
 		self.localBias = calcSubrBias(localSubrs)
 		self.globalSubrs = globalSubrs
 		self.globalBias = calcSubrBias(globalSubrs)
+		self.private = private
 		self.reset()
-	
+
 	def reset(self):
 		self.callingStack = []
 		self.operandStack = []
 		self.hintCount = 0
 		self.hintMaskBytes = 0
-	
+		self.numRegions = 0
+
+	def check_program(self, program):
+		if not hasattr(self, 'private') or self.private is None:
+			# Type 1 charstrings don't have self.private.
+			# Type2 CFF charstrings may have self.private == None.
+			# In both cases, they are not CFF2 charstrings
+			isCFF2 = False
+		else:
+			isCFF2 = self.private._isCFF2
+		if isCFF2:
+			if program:
+				assert program[-1] not in ("seac",), "illegal CharString Terminator"
+		else:
+			assert program, "illegal CharString: decompiled to empty program"
+			assert program[-1] in ("endchar", "return", "callsubr", "callgsubr",
+					"seac"), "illegal CharString"
+
 	def execute(self, charString):
 		self.callingStack.append(charString)
 		needsDecompilation = charString.needsDecompilation()
@@ -514,8 +296,8 @@
 			pushToProgram(token)
 			if isOperator:
 				handlerName = "op_" + token
-				if hasattr(self, handlerName):
-					handler = getattr(self, handlerName)
+				handler = getattr(self, handlerName, None)
+				if handler is not None:
 					rv = handler(index)
 					if rv:
 						hintMaskBytes, index = rv
@@ -525,29 +307,27 @@
 			else:
 				pushToStack(token)
 		if needsDecompilation:
-			assert program, "illegal CharString: decompiled to empty program"
-			assert program[-1] in ("endchar", "return", "callsubr", "callgsubr",
-					"seac"), "illegal CharString"
+			self.check_program(program)
 			charString.setProgram(program)
 		del self.callingStack[-1]
-	
+
 	def pop(self):
 		value = self.operandStack[-1]
 		del self.operandStack[-1]
 		return value
-	
+
 	def popall(self):
 		stack = self.operandStack[:]
 		self.operandStack[:] = []
 		return stack
-	
+
 	def push(self, value):
 		self.operandStack.append(value)
-	
+
 	def op_return(self, index):
 		if self.operandStack:
 			pass
-	
+
 	def op_endchar(self, index):
 		pass
 
@@ -558,12 +338,12 @@
 		subrIndex = self.pop()
 		subr = self.localSubrs[subrIndex+self.localBias]
 		self.execute(subr)
-	
+
 	def op_callgsubr(self, index):
 		subrIndex = self.pop()
 		subr = self.globalSubrs[subrIndex+self.globalBias]
 		self.execute(subr)
-	
+
 	def op_hstem(self, index):
 		self.countHints()
 	def op_vstem(self, index):
@@ -572,16 +352,16 @@
 		self.countHints()
 	def op_vstemhm(self, index):
 		self.countHints()
-	
+
 	def op_hintmask(self, index):
 		if not self.hintMaskBytes:
 			self.countHints()
 			self.hintMaskBytes = (self.hintCount + 7) // 8
 		hintMaskBytes, index = self.callingStack[-1].getBytes(index, self.hintMaskBytes)
 		return hintMaskBytes, index
-	
+
 	op_cntrmask = op_hintmask
-	
+
 	def countHints(self):
 		args = self.popall()
 		self.hintCount = self.hintCount + len(args) // 2
@@ -632,28 +412,109 @@
 	def op_roll(self, index):
 		raise NotImplementedError
 
-class T2OutlineExtractor(SimpleT2Decompiler):
-	
-	def __init__(self, pen, localSubrs, globalSubrs, nominalWidthX, defaultWidthX):
+	# TODO(behdad): move to T2OutlineExtractor and add a 'setVariation'
+	# method that takes VarStoreData and a location
+	def op_blend(self, index):
+		if self.numRegions == 0:
+			self.numRegions = self.private.getNumRegions()
+		numBlends = self.pop()
+		numOps = numBlends * (self.numRegions + 1)
+		blendArgs = self.operandStack[-numOps:]
+		del self.operandStack[:-(numOps-numBlends)] # Leave the default operands on the stack.
+
+	def op_vsindex(self, index):
+		vi = self.pop()
+		self.numRegions = self.private.getNumRegions(vi)
+
+
+t1Operators = [
+#	opcode		name
+	(1,		'hstem'),
+	(3,		'vstem'),
+	(4,		'vmoveto'),
+	(5,		'rlineto'),
+	(6,		'hlineto'),
+	(7,		'vlineto'),
+	(8,		'rrcurveto'),
+	(9,		'closepath'),
+	(10,		'callsubr'),
+	(11,		'return'),
+	(13,		'hsbw'),
+	(14,		'endchar'),
+	(21,		'rmoveto'),
+	(22,		'hmoveto'),
+	(30,		'vhcurveto'),
+	(31,		'hvcurveto'),
+	((12, 0),	'dotsection'),
+	((12, 1),	'vstem3'),
+	((12, 2),	'hstem3'),
+	((12, 6),	'seac'),
+	((12, 7),	'sbw'),
+	((12, 12),	'div'),
+	((12, 16),	'callothersubr'),
+	((12, 17),	'pop'),
+	((12, 33),	'setcurrentpoint'),
+]
+
+
+class T2WidthExtractor(SimpleT2Decompiler):
+
+	def __init__(self, localSubrs, globalSubrs, nominalWidthX, defaultWidthX):
 		SimpleT2Decompiler.__init__(self, localSubrs, globalSubrs)
-		self.pen = pen
 		self.nominalWidthX = nominalWidthX
 		self.defaultWidthX = defaultWidthX
-	
+
 	def reset(self):
 		SimpleT2Decompiler.reset(self)
-		self.hints = []
 		self.gotWidth = 0
 		self.width = 0
+
+	def popallWidth(self, evenOdd=0):
+		args = self.popall()
+		if not self.gotWidth:
+			if evenOdd ^ (len(args) % 2):
+				self.width = self.nominalWidthX + args[0]
+				args = args[1:]
+			else:
+				self.width = self.defaultWidthX
+			self.gotWidth = 1
+		return args
+
+	def countHints(self):
+		args = self.popallWidth()
+		self.hintCount = self.hintCount + len(args) // 2
+
+	def op_rmoveto(self, index):
+		self.popallWidth()
+
+	def op_hmoveto(self, index):
+		self.popallWidth(1)
+
+	def op_vmoveto(self, index):
+		self.popallWidth(1)
+
+	def op_endchar(self, index):
+		self.popallWidth()
+
+
+class T2OutlineExtractor(T2WidthExtractor):
+
+	def __init__(self, pen, localSubrs, globalSubrs, nominalWidthX, defaultWidthX):
+		T2WidthExtractor.__init__(
+			self, localSubrs, globalSubrs, nominalWidthX, defaultWidthX)
+		self.pen = pen
+
+	def reset(self):
+		T2WidthExtractor.reset(self)
 		self.currentPoint = (0, 0)
 		self.sawMoveTo = 0
-	
+
 	def _nextPoint(self, point):
 		x, y = self.currentPoint
 		point = x + point[0], y + point[1]
 		self.currentPoint = point
 		return point
-	
+
 	def rMoveTo(self, point):
 		self.pen.moveTo(self._nextPoint(point))
 		self.sawMoveTo = 1
@@ -668,32 +529,17 @@
 			self.rMoveTo((0, 0))
 		nextPoint = self._nextPoint
 		self.pen.curveTo(nextPoint(pt1), nextPoint(pt2), nextPoint(pt3))
-	
+
 	def closePath(self):
 		if self.sawMoveTo:
 			self.pen.closePath()
 		self.sawMoveTo = 0
-	
+
 	def endPath(self):
 		# In T2 there are no open paths, so always do a closePath when
 		# finishing a sub path.
 		self.closePath()
 
-	def popallWidth(self, evenOdd=0):
-		args = self.popall()
-		if not self.gotWidth:
-			if evenOdd ^ (len(args) % 2):
-				self.width = self.nominalWidthX + args[0]
-				args = args[1:]
-			else:
-				self.width = self.defaultWidthX
-			self.gotWidth = 1
-		return args
-	
-	def countHints(self):
-		args = self.popallWidth()
-		self.hintCount = self.hintCount + len(args) // 2
-	
 	#
 	# hint operators
 	#
@@ -709,7 +555,7 @@
 	#	self.countHints()
 	#def op_cntrmask(self, index):
 	#	self.countHints()
-	
+
 	#
 	# path constructors, moveto
 	#
@@ -734,7 +580,7 @@
 			self.pen.addComponent(baseGlyph, (1, 0, 0, 1, 0, 0))
 			accentGlyph = StandardEncoding[achar]
 			self.pen.addComponent(accentGlyph, (1, 0, 0, 1, adx, ady))
-	
+
 	#
 	# path constructors, lines
 	#
@@ -743,12 +589,12 @@
 		for i in range(0, len(args), 2):
 			point = args[i:i+2]
 			self.rLineTo(point)
-	
+
 	def op_hlineto(self, index):
 		self.alternatingLineto(1)
 	def op_vlineto(self, index):
 		self.alternatingLineto(0)
-	
+
 	#
 	# path constructors, curves
 	#
@@ -758,7 +604,7 @@
 		for i in range(0, len(args), 6):
 			dxa, dya, dxb, dyb, dxc, dyc, = args[i:i+6]
 			self.rCurveTo((dxa, dya), (dxb, dyb), (dxc, dyc))
-	
+
 	def op_rcurveline(self, index):
 		"""{dxa dya dxb dyb dxc dyc}+ dxd dyd rcurveline"""
 		args = self.popall()
@@ -766,7 +612,7 @@
 			dxb, dyb, dxc, dyc, dxd, dyd = args[i:i+6]
 			self.rCurveTo((dxb, dyb), (dxc, dyc), (dxd, dyd))
 		self.rLineTo(args[-2:])
-	
+
 	def op_rlinecurve(self, index):
 		"""{dxa dya}+ dxb dyb dxc dyc dxd dyd rlinecurve"""
 		args = self.popall()
@@ -775,7 +621,7 @@
 			self.rLineTo(lineArgs[i:i+2])
 		dxb, dyb, dxc, dyc, dxd, dyd = args[-6:]
 		self.rCurveTo((dxb, dyb), (dxc, dyc), (dxd, dyd))
-	
+
 	def op_vvcurveto(self, index):
 		"dx1? {dya dxb dyb dyc}+ vvcurveto"
 		args = self.popall()
@@ -788,7 +634,7 @@
 			dya, dxb, dyb, dyc = args[i:i+4]
 			self.rCurveTo((dx1, dya), (dxb, dyb), (0, dyc))
 			dx1 = 0
-	
+
 	def op_hhcurveto(self, index):
 		"""dy1? {dxa dxb dyb dxc}+ hhcurveto"""
 		args = self.popall()
@@ -801,7 +647,7 @@
 			dxa, dxb, dyb, dxc = args[i:i+4]
 			self.rCurveTo((dxa, dy1), (dxb, dyb), (dxc, 0))
 			dy1 = 0
-	
+
 	def op_vhcurveto(self, index):
 		"""dy1 dx2 dy2 dx3 {dxa dxb dyb dyc dyd dxe dye dxf}* dyf? vhcurveto (30)
 		{dya dxb dyb dxc dxd dxe dye dyf}+ dxf? vhcurveto
@@ -811,7 +657,7 @@
 			args = self.vcurveto(args)
 			if args:
 				args = self.hcurveto(args)
-	
+
 	def op_hvcurveto(self, index):
 		"""dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf?
 		{dxa dxb dyb dyc dyd dxe dye dxf}+ dyf?
@@ -821,7 +667,7 @@
 			args = self.hcurveto(args)
 			if args:
 				args = self.vcurveto(args)
-	
+
 	#
 	# path constructors, flex
 	#
@@ -854,13 +700,13 @@
 			dy6 = d6
 		self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3))
 		self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6))
-	
+
 	#
 	# MultipleMaster. Well...
 	#
 	def op_blend(self, index):
 		self.popall()
-	
+
 	# misc
 	def op_and(self, index):
 		raise NotImplementedError
@@ -913,7 +759,7 @@
 		raise NotImplementedError
 	def op_roll(self, index):
 		raise NotImplementedError
-	
+
 	#
 	# miscellaneous helpers
 	#
@@ -926,7 +772,7 @@
 				point = (0, arg)
 			self.rLineTo(point)
 			isHorizontal = not isHorizontal
-	
+
 	def vcurveto(self, args):
 		dya, dxb, dyb, dxc = args[:4]
 		args = args[4:]
@@ -937,7 +783,7 @@
 			dyc = 0
 		self.rCurveTo((0, dya), (dxb, dyb), (dxc, dyc))
 		return args
-	
+
 	def hcurveto(self, args):
 		dxa, dxb, dyb, dyc = args[:4]
 		args = args[4:]
@@ -949,20 +795,19 @@
 		self.rCurveTo((dxa, 0), (dxb, dyb), (dxc, dyc))
 		return args
 
-
 class T1OutlineExtractor(T2OutlineExtractor):
-	
+
 	def __init__(self, pen, subrs):
 		self.pen = pen
 		self.subrs = subrs
 		self.reset()
-	
+
 	def reset(self):
 		self.flexing = 0
 		self.width = 0
 		self.sbx = 0
 		T2OutlineExtractor.reset(self)
-	
+
 	def endPath(self):
 		if self.sawMoveTo:
 			self.pen.endPath()
@@ -970,11 +815,11 @@
 
 	def popallWidth(self, evenOdd=0):
 		return self.popall()
-	
+
 	def exch(self):
 		stack = self.operandStack
 		stack[-1], stack[-2] = stack[-2], stack[-1]
-	
+
 	#
 	# path constructors
 	#
@@ -1004,10 +849,10 @@
 		args = self.popall()
 		x, y = args
 		self.currentPoint = x, y
-	
+
 	def op_endchar(self, index):
 		self.endPath()
-	
+
 	def op_hsbw(self, index):
 		sbx, wx = self.popall()
 		self.width = wx
@@ -1015,7 +860,7 @@
 		self.currentPoint = sbx, self.currentPoint[1]
 	def op_sbw(self, index):
 		self.popall()  # XXX
-	
+
 	#
 	def op_callsubr(self, index):
 		subrIndex = self.pop()
@@ -1033,12 +878,12 @@
 		# ignore...
 	def op_pop(self, index):
 		pass  # ignore...
-	
+
 	def doFlex(self):
 		finaly = self.pop()
 		finalx = self.pop()
 		self.pop()	# flex height is unused
-		
+
 		p3y = self.pop()
 		p3x = self.pop()
 		bcp4y = self.pop()
@@ -1053,7 +898,7 @@
 		bcp1x = self.pop()
 		rpy = self.pop()
 		rpx = self.pop()
-		
+
 		# call rrcurveto
 		self.push(bcp1x+rpx)
 		self.push(bcp1y+rpy)
@@ -1062,7 +907,7 @@
 		self.push(p2x)
 		self.push(p2y)
 		self.op_rrcurveto(None)
-		
+
 		# call rrcurveto
 		self.push(bcp3x)
 		self.push(bcp3y)
@@ -1071,11 +916,11 @@
 		self.push(p3x)
 		self.push(p3y)
 		self.op_rrcurveto(None)
-		
+
 		# Push back final coords so subr 0 can find them
 		self.push(finalx)
 		self.push(finaly)
-	
+
 	def op_dotsection(self, index):
 		self.popall()  # XXX
 	def op_hstem3(self, index):
@@ -1092,20 +937,267 @@
 	def op_vstem3(self, index):
 		self.popall()  # XXX
 
+class T2CharString(object):
 
-class DictDecompiler(ByteCodeBase):
+	operandEncoding = t2OperandEncoding
+	operators, opcodes = buildOperatorDict(t2Operators)
+	decompilerClass = SimpleT2Decompiler
+	outlineExtractor = T2OutlineExtractor
+	isCFF2 = False
 	
+	def __init__(self, bytecode=None, program=None, private=None, globalSubrs=None):
+		if program is None:
+			program = []
+		self.bytecode = bytecode
+		self.program = program
+		self.private = private
+		self.globalSubrs = globalSubrs if globalSubrs is not None else []
+
+	def __repr__(self):
+		if self.bytecode is None:
+			return "<%s (source) at %x>" % (self.__class__.__name__, id(self))
+		else:
+			return "<%s (bytecode) at %x>" % (self.__class__.__name__, id(self))
+
+	def getIntEncoder(self):
+		return encodeIntT2
+
+	def getFixedEncoder(self):
+		return encodeFixed
+
+	def decompile(self):
+		if not self.needsDecompilation():
+			return
+		subrs = getattr(self.private, "Subrs", [])
+		decompiler = self.decompilerClass(subrs, self.globalSubrs, self.private)
+		decompiler.execute(self)
+
+	def draw(self, pen):
+		subrs = getattr(self.private, "Subrs", [])
+		extractor = self.outlineExtractor(pen, subrs, self.globalSubrs,
+				self.private.nominalWidthX, self.private.defaultWidthX)
+		extractor.execute(self)
+		self.width = extractor.width
+
+	def calcBounds(self, glyphSet):
+		boundsPen = BoundsPen(glyphSet)
+		self.draw(boundsPen)
+		return boundsPen.bounds
+
+	def check_program(self, program, isCFF2=False):
+		if isCFF2:
+			if self.program:
+				assert self.program[-1] not in ("seac",), "illegal CFF2 CharString Termination"
+		else:
+			assert self.program, "illegal CharString: decompiled to empty program"
+			assert self.program[-1] in ("endchar", "return", "callsubr", "callgsubr", "seac"), "illegal CharString"
+
+	def compile(self, isCFF2=False):
+		if self.bytecode is not None:
+			return
+		opcodes = self.opcodes
+		program = self.program
+		self.check_program(program, isCFF2=isCFF2)
+		bytecode = []
+		encodeInt = self.getIntEncoder()
+		encodeFixed = self.getFixedEncoder()
+		i = 0
+		end = len(program)
+		while i < end:
+			token = program[i]
+			i = i + 1
+			tp = type(token)
+			if issubclass(tp, basestring):
+				try:
+					bytecode.extend(bytechr(b) for b in opcodes[token])
+				except KeyError:
+					raise CharStringCompileError("illegal operator: %s" % token)
+				if token in ('hintmask', 'cntrmask'):
+					bytecode.append(program[i])  # hint mask
+					i = i + 1
+			elif tp == int:
+				bytecode.append(encodeInt(token))
+			elif tp == float:
+				bytecode.append(encodeFixed(token))
+			else:
+				assert 0, "unsupported type: %s" % tp
+		try:
+			bytecode = bytesjoin(bytecode)
+		except TypeError:
+			log.error(bytecode)
+			raise
+		self.setBytecode(bytecode)
+
+		if isCFF2:
+			# If present, remove return and endchar operators.
+			if self.bytecode and (byteord(self.bytecode[-1]) in (11, 14)):
+				self.bytecode = self.bytecode[:-1]
+
+	def needsDecompilation(self):
+		return self.bytecode is not None
+
+	def setProgram(self, program):
+		self.program = program
+		self.bytecode = None
+
+	def setBytecode(self, bytecode):
+		self.bytecode = bytecode
+		self.program = None
+
+	def getToken(self, index,
+			len=len, byteord=byteord, basestring=basestring,
+			isinstance=isinstance):
+		if self.bytecode is not None:
+			if index >= len(self.bytecode):
+				return None, 0, 0
+			b0 = byteord(self.bytecode[index])
+			index = index + 1
+			handler = self.operandEncoding[b0]
+			token, index = handler(self, b0, self.bytecode, index)
+		else:
+			if index >= len(self.program):
+				return None, 0, 0
+			token = self.program[index]
+			index = index + 1
+		isOperator = isinstance(token, basestring)
+		return token, isOperator, index
+
+	def getBytes(self, index, nBytes):
+		if self.bytecode is not None:
+			newIndex = index + nBytes
+			bytes = self.bytecode[index:newIndex]
+			index = newIndex
+		else:
+			bytes = self.program[index]
+			index = index + 1
+		assert len(bytes) == nBytes
+		return bytes, index
+
+	def handle_operator(self, operator):
+		return operator
+
+	def toXML(self, xmlWriter):
+		from fontTools.misc.textTools import num2binary
+		if self.bytecode is not None:
+			xmlWriter.dumphex(self.bytecode)
+		else:
+			index = 0
+			args = []
+			while True:
+				token, isOperator, index = self.getToken(index)
+				if token is None:
+					break
+				if isOperator:
+					args = [str(arg) for arg in args]
+					if token in ('hintmask', 'cntrmask'):
+						hintMask, isOperator, index = self.getToken(index)
+						bits = []
+						for byte in hintMask:
+							bits.append(num2binary(byteord(byte), 8))
+						hintMask = strjoin(bits)
+						line = ' '.join(args + [token, hintMask])
+					else:
+						line = ' '.join(args + [token])
+					xmlWriter.write(line)
+					xmlWriter.newline()
+					args = []
+				else:
+					args.append(token)
+			if args:
+				if self.isCFF2:
+					# CFF2Subr's can have numeric arguments on the stack after the last operator.
+					args = [str(arg) for arg in args]
+					line = ' '.join(args)
+					xmlWriter.write(line)
+				else:
+					assert 0, "T2Charstring or Subr has items on the stack after last operator."
+
+	def fromXML(self, name, attrs, content):
+		from fontTools.misc.textTools import binary2num, readHex
+		if attrs.get("raw"):
+			self.setBytecode(readHex(content))
+			return
+		content = strjoin(content)
+		content = content.split()
+		program = []
+		end = len(content)
+		i = 0
+		while i < end:
+			token = content[i]
+			i = i + 1
+			try:
+				token = int(token)
+			except ValueError:
+				try:
+					token = float(token)
+				except ValueError:
+					program.append(token)
+					if token in ('hintmask', 'cntrmask'):
+						mask = content[i]
+						maskBytes = b""
+						for j in range(0, len(mask), 8):
+							maskBytes = maskBytes + bytechr(binary2num(mask[j:j+8]))
+						program.append(maskBytes)
+						i = i + 1
+				else:
+					program.append(token)
+			else:
+				program.append(token)
+		self.setProgram(program)
+
+class CFF2Subr(T2CharString):
+	isCFF2 = True
+
+class T1CharString(T2CharString):
+
+	operandEncoding = t1OperandEncoding
+	operators, opcodes = buildOperatorDict(t1Operators)
+
+	def __init__(self, bytecode=None, program=None, subrs=None):
+		if program is None:
+			program = []
+		self.bytecode = bytecode
+		self.program = program
+		self.subrs = subrs
+
+	def getIntEncoder(self):
+		return encodeIntT1
+
+	def getFixedEncoder(self):
+		def encodeFixed(value):
+			raise TypeError("Type 1 charstrings don't support floating point operands")
+
+	def decompile(self):
+		if self.bytecode is None:
+			return
+		program = []
+		index = 0
+		while True:
+			token, isOperator, index = self.getToken(index)
+			if token is None:
+				break
+			program.append(token)
+		self.setProgram(program)
+
+	def draw(self, pen):
+		extractor = T1OutlineExtractor(pen, self.subrs)
+		extractor.execute(self)
+		self.width = extractor.width
+
+class DictDecompiler(object):
+
 	operandEncoding = cffDictOperandEncoding
-	
-	def __init__(self, strings):
+
+	def __init__(self, strings, parent=None):
 		self.stack = []
 		self.strings = strings
 		self.dict = {}
-	
+		self.parent = parent
+
 	def getDict(self):
 		assert len(self.stack) == 0, "non-empty stack"
 		return self.dict
-	
+
 	def decompile(self, data):
 		index = 0
 		lenData = len(data)
@@ -1113,34 +1205,23 @@
 		while index < lenData:
 			b0 = byteord(data[index])
 			index = index + 1
-			code = self.operandEncoding[b0]
-			handler = getattr(self, code)
-			value, index = handler(b0, data, index)
+			handler = self.operandEncoding[b0]
+			value, index = handler(self, b0, data, index)
 			if value is not None:
 				push(value)
-	
 	def pop(self):
 		value = self.stack[-1]
 		del self.stack[-1]
 		return value
-	
+
 	def popall(self):
 		args = self.stack[:]
 		del self.stack[:]
 		return args
-	
-	def do_operator(self, b0, data, index):
-		if b0 == 12:
-			op = (b0, byteord(data[index]))
-			index = index+1
-		else:
-			op = b0
-		operator, argType = self.operators[op]
-		self.handle_operator(operator, argType)
-		return None, index
-	
-	def handle_operator(self, operator, argType):
-		if isinstance(argType, type(())):
+
+	def handle_operator(self, operator):
+		operator, argType = operator
+		if isinstance(argType, tuple):
 			value = ()
 			for i in range(len(argType)-1, -1, -1):
 				arg = argType[i]
@@ -1149,20 +1230,74 @@
 		else:
 			arghandler = getattr(self, "arg_" + argType)
 			value = arghandler(operator)
-		self.dict[operator] = value
-	
+		if operator == "blend":
+			self.stack.extend(value)
+		else:
+			self.dict[operator] = value
+
 	def arg_number(self, name):
-		return self.pop()
+		if isinstance(self.stack[0], list):
+			out = self.arg_blend_number(self.stack)
+		else:
+			out = self.pop()
+		return out
+
+	def arg_blend_number(self, name):
+		out = []
+		blendArgs = self.pop()
+		numMasters = len(blendArgs)
+		out.append(blendArgs)
+		out.append("blend")
+		dummy = self.popall()
+		return blendArgs
+
 	def arg_SID(self, name):
 		return self.strings[self.pop()]
 	def arg_array(self, name):
 		return self.popall()
+	def arg_blendList(self, name):
+		"""
+		There may be non-blend args at the top of the stack. We first calculate
+		where the blend args start in the stack. These are the last
+		numMasters*numBlends) +1 args. 
+		The blend args starts with numMasters relative coordinate values, the  BlueValues in the list from the default master font. This is followed by
+		numBlends list of values. Each of  value in one of these lists is the
+		Variable Font delta for the matching region.
+		
+		We re-arrange this to be a list of numMaster entries. Each entry starts with the corresponding default font relative value, and is followed by 
+		the delta values. We then convert the default values, the first item in each entry, to an absolute value.
+		"""
+		vsindex = self.dict.get('vsindex', 0)
+		numMasters = self.parent.getNumRegions(vsindex) + 1 # only a PrivateDict has blended ops.
+		numBlends = self.pop()
+		args = self.popall()
+		numArgs = len(args)
+		# The spec says that there should be no non-blended Blue Values,.
+		assert(numArgs == numMasters * numBlends)
+		value = [None]*numBlends
+		numDeltas = numMasters-1
+		i = 0
+		prevVal = 0
+		while i < numBlends:
+			newVal = args[i] + prevVal
+			prevVal = newVal
+			masterOffset = numBlends + (i* numDeltas)
+			blendList = [newVal] + args[masterOffset:masterOffset+numDeltas]
+			value[i] = blendList
+			i += 1
+		return value
+
 	def arg_delta(self, name):
+		valueList = self.popall()
 		out = []
-		current = 0
-		for v in self.popall():
-			current = current + v
-			out.append(current)
+		if valueList and isinstance(valueList[0], list):
+			# arg_blendList() has already converted these to absolute values.
+			out = valueList
+		else:
+			current = 0
+			for v in valueList:
+				current = current + v
+				out.append(current)
 		return out
 
 
diff --git a/Lib/fontTools/misc/psLib.py b/Lib/fontTools/misc/psLib.py
index 90faa90..aa01eed 100644
--- a/Lib/fontTools/misc/psLib.py
+++ b/Lib/fontTools/misc/psLib.py
@@ -5,17 +5,20 @@
 import re
 import collections
 from string import whitespace
+import logging
 
 
-ps_special = '()<>[]{}%'	# / is one too, but we take care of that one differently
+log = logging.getLogger(__name__)
 
-skipwhiteRE = re.compile("[%s]*" % whitespace)
-endofthingPat = "[^][(){}<>/%%%s]*" % whitespace
+ps_special = b'()<>[]{}%'	# / is one too, but we take care of that one differently
+
+skipwhiteRE = re.compile(bytesjoin([b"[", whitespace, b"]*"]))
+endofthingPat = bytesjoin([b"[^][(){}<>/%", whitespace, b"]*"])
 endofthingRE = re.compile(endofthingPat)
-commentRE = re.compile("%[^\n\r]*")
+commentRE = re.compile(b"%[^\n\r]*")
 
 # XXX This not entirely correct as it doesn't allow *nested* embedded parens:
-stringPat = r"""
+stringPat = br"""
 	\(
 		(
 			(
@@ -29,17 +32,46 @@
 		[^()]*
 	\)
 """
-stringPat = "".join(stringPat.split())
+stringPat = b"".join(stringPat.split())
 stringRE = re.compile(stringPat)
 
-hexstringRE = re.compile("<[%s0-9A-Fa-f]*>" % whitespace)
+hexstringRE = re.compile(bytesjoin([b"<[", whitespace, b"0-9A-Fa-f]*>"]))
 
 class PSTokenError(Exception): pass
 class PSError(Exception): pass
 
 
-class PSTokenizer(StringIO):
-	
+class PSTokenizer(object):
+
+	def __init__(self, buf=b'', encoding="ascii"):
+		# Force self.buf to be a byte string
+		buf = tobytes(buf)
+		self.buf = buf
+		self.len = len(buf)
+		self.pos = 0
+		self.closed = False
+		self.encoding = encoding
+
+	def read(self, n=-1):
+		"""Read at most 'n' bytes from the buffer, or less if the read
+		hits EOF before obtaining 'n' bytes.
+		If 'n' is negative or omitted, read all data until EOF is reached.
+		"""
+		if self.closed:
+			raise ValueError("I/O operation on closed file")
+		if n is None or n < 0:
+			newpos = self.len
+		else:
+			newpos = min(self.pos+n, self.len)
+		r = self.buf[self.pos:newpos]
+		self.pos = newpos
+		return r
+
+	def close(self):
+		if not self.closed:
+			self.closed = True
+			del self.buf, self.pos
+
 	def getnexttoken(self,
 			# localize some stuff, for performance
 			len=len,
@@ -47,32 +79,30 @@
 			stringmatch=stringRE.match,
 			hexstringmatch=hexstringRE.match,
 			commentmatch=commentRE.match,
-			endmatch=endofthingRE.match, 
-			whitematch=skipwhiteRE.match):
-		
-		_, nextpos = whitematch(self.buf, self.pos).span()
-		self.pos = nextpos
+			endmatch=endofthingRE.match):
+
+		self.skipwhite()
 		if self.pos >= self.len:
 			return None, None
 		pos = self.pos
 		buf = self.buf
-		char = buf[pos]
+		char = bytechr(byteord(buf[pos]))
 		if char in ps_special:
-			if char in '{}[]':
+			if char in b'{}[]':
 				tokentype = 'do_special'
 				token = char
-			elif char == '%':
+			elif char == b'%':
 				tokentype = 'do_comment'
 				_, nextpos = commentmatch(buf, pos).span()
 				token = buf[pos:nextpos]
-			elif char == '(':
+			elif char == b'(':
 				tokentype = 'do_string'
 				m = stringmatch(buf, pos)
 				if m is None:
 					raise PSTokenError('bad string at character %d' % pos)
 				_, nextpos = m.span()
 				token = buf[pos:nextpos]
-			elif char == '<':
+			elif char == b'<':
 				tokentype = 'do_hexstring'
 				m = hexstringmatch(buf, pos)
 				if m is None:
@@ -82,7 +112,7 @@
 			else:
 				raise PSTokenError('bad token at character %d' % pos)
 		else:
-			if char == '/':
+			if char == b'/':
 				tokentype = 'do_literal'
 				m = endmatch(buf, pos+1)
 			else:
@@ -93,43 +123,39 @@
 			_, nextpos = m.span()
 			token = buf[pos:nextpos]
 		self.pos = pos + len(token)
+		token = tostr(token, encoding=self.encoding)
 		return tokentype, token
-	
+
 	def skipwhite(self, whitematch=skipwhiteRE.match):
 		_, nextpos = whitematch(self.buf, self.pos).span()
 		self.pos = nextpos
-	
+
 	def starteexec(self):
 		self.pos = self.pos + 1
-		#self.skipwhite()
 		self.dirtybuf = self.buf[self.pos:]
 		self.buf, R = eexec.decrypt(self.dirtybuf, 55665)
 		self.len = len(self.buf)
 		self.pos = 4
-	
+
 	def stopeexec(self):
 		if not hasattr(self, 'dirtybuf'):
 			return
 		self.buf = self.dirtybuf
 		del self.dirtybuf
-	
-	def flush(self):
-		if self.buflist:
-			self.buf = self.buf + "".join(self.buflist)
-			self.buflist = []
 
 
 class PSInterpreter(PSOperators):
-	
-	def __init__(self):
+
+	def __init__(self, encoding="ascii"):
 		systemdict = {}
 		userdict = {}
+		self.encoding = encoding
 		self.dictstack = [systemdict, userdict]
 		self.stack = []
 		self.proclevel = 0
 		self.procmark = ps_procmark()
 		self.fillsystemdict()
-	
+
 	def fillsystemdict(self):
 		systemdict = self.dictstack[0]
 		systemdict['['] = systemdict['mark'] = self.mark = ps_mark()
@@ -139,7 +165,7 @@
 		systemdict['StandardEncoding'] = ps_array(ps_StandardEncoding)
 		systemdict['FontDirectory'] = ps_dict({})
 		self.suckoperators(systemdict, self.__class__)
-	
+
 	def suckoperators(self, systemdict, klass):
 		for name in dir(klass):
 			attr = getattr(self, name)
@@ -148,16 +174,15 @@
 				systemdict[name] = ps_operator(name, attr)
 		for baseclass in klass.__bases__:
 			self.suckoperators(systemdict, baseclass)
-	
-	def interpret(self, data, getattr = getattr):
-		tokenizer = self.tokenizer = PSTokenizer(data)
+
+	def interpret(self, data, getattr=getattr):
+		tokenizer = self.tokenizer = PSTokenizer(data, self.encoding)
 		getnexttoken = tokenizer.getnexttoken
 		do_token = self.do_token
 		handle_object = self.handle_object
 		try:
 			while 1:
 				tokentype, token = getnexttoken()
-				#print token
 				if not token:
 					break
 				if tokentype:
@@ -169,15 +194,19 @@
 					handle_object(object)
 			tokenizer.close()
 			self.tokenizer = None
-		finally:
+		except:
 			if self.tokenizer is not None:
-				if 0:
-					print('ps error:\n- - - - - - -')
-					print(self.tokenizer.buf[self.tokenizer.pos-50:self.tokenizer.pos])
-					print('>>>')
-					print(self.tokenizer.buf[self.tokenizer.pos:self.tokenizer.pos+50])
-					print('- - - - - - -')
-	
+				log.debug(
+					'ps error:\n'
+					'- - - - - - -\n'
+					'%s\n'
+					'>>>\n'
+					'%s\n'
+					'- - - - - - -',
+					self.tokenizer.buf[self.tokenizer.pos-50:self.tokenizer.pos],
+					self.tokenizer.buf[self.tokenizer.pos:self.tokenizer.pos+50])
+			raise
+
 	def handle_object(self, object):
 		if not (self.proclevel or object.literal or object.type == 'proceduretype'):
 			if object.type != 'operatortype':
@@ -191,21 +220,21 @@
 					object.function()
 		else:
 			self.push(object)
-	
+
 	def call_procedure(self, proc):
 		handle_object = self.handle_object
 		for item in proc.value:
 			handle_object(item)
-	
+
 	def resolve_name(self, name):
 		dictstack = self.dictstack
 		for i in range(len(dictstack)-1, -1, -1):
 			if name in dictstack[i]:
 				return dictstack[i][name]
 		raise PSError('name error: ' + str(name))
-	
+
 	def do_token(self, token,
-				int=int, 
+				int=int,
 				float=float,
 				ps_name=ps_name,
 				ps_integer=ps_integer,
@@ -231,16 +260,16 @@
 				return ps_real(num)
 		else:
 			return ps_integer(num)
-	
+
 	def do_comment(self, token):
 		pass
-	
+
 	def do_literal(self, token):
 		return ps_literal(token[1:])
-	
+
 	def do_string(self, token):
 		return ps_string(token[1:-1])
-	
+
 	def do_hexstring(self, token):
 		hexStr = "".join(token[1:-1].split())
 		if len(hexStr) % 2:
@@ -250,7 +279,7 @@
 			cleanstr.append(chr(int(hexStr[i:i+2], 16)))
 		cleanstr = "".join(cleanstr)
 		return ps_string(cleanstr)
-	
+
 	def do_special(self, token):
 		if token == '{':
 			self.proclevel = self.proclevel + 1
@@ -271,10 +300,10 @@
 			return ps_name(']')
 		else:
 			raise PSTokenError('huh?')
-	
+
 	def push(self, object):
 		self.stack.append(object)
-	
+
 	def pop(self, *types):
 		stack = self.stack
 		if not stack:
@@ -285,7 +314,7 @@
 				raise PSError('typecheck, expected %s, found %s' % (repr(types), object.type))
 		del stack[-1]
 		return object
-	
+
 	def do_makearray(self):
 		array = []
 		while 1:
@@ -295,7 +324,7 @@
 			array.append(topobject)
 		array.reverse()
 		self.push(ps_array(array))
-	
+
 	def close(self):
 		"""Remove circular references."""
 		del self.stack
@@ -318,14 +347,13 @@
 		newitem = item.value
 	return newitem
 
-def suckfont(data):
-	import re
+def suckfont(data, encoding="ascii"):
 	m = re.search(br"/FontName\s+/([^ \t\n\r]+)\s+def", data)
 	if m:
 		fontName = m.group(1)
 	else:
 		fontName = None
-	interpreter = PSInterpreter()
+	interpreter = PSInterpreter(encoding=encoding)
 	interpreter.interpret(b"/Helvetica 4 dict dup /Encoding StandardEncoding put definefont pop")
 	interpreter.interpret(data)
 	fontdir = interpreter.dictstack[0]['FontDirectory'].value
@@ -340,12 +368,3 @@
 		rawfont = fontdir[fontNames[0]]
 	interpreter.close()
 	return unpack_item(rawfont)
-
-
-if __name__ == "__main__":
-	import EasyDialogs
-	path = EasyDialogs.AskFileForOpen()
-	if path:
-		from fontTools import t1Lib
-		data, kind = t1Lib.read(path)
-		font = suckfont(data)
diff --git a/Lib/fontTools/misc/psOperators.py b/Lib/fontTools/misc/psOperators.py
index 57cfbe8..4aa0281 100644
--- a/Lib/fontTools/misc/psOperators.py
+++ b/Lib/fontTools/misc/psOperators.py
@@ -4,24 +4,24 @@
 _accessstrings = {0: "", 1: "readonly", 2: "executeonly", 3: "noaccess"}
 
 
-class ps_object:
-	
+class ps_object(object):
+
 	literal = 1
 	access = 0
 	value = None
-	
+
 	def __init__(self, value):
 		self.value = value
 		self.type = self.__class__.__name__[3:] + "type"
-	
+
 	def __repr__(self):
 		return "<%s %s>" % (self.__class__.__name__[3:], repr(self.value))
 
 
 class ps_operator(ps_object):
-	
+
 	literal = 0
-	
+
 	def __init__(self, name, function):
 		self.name = name
 		self.function = function
@@ -171,7 +171,7 @@
 		return "<dict>"
 
 class ps_mark(ps_object):
-	def __init__(self): 
+	def __init__(self):
 		self.value = 'mark'
 		self.type = self.__class__.__name__[3:] + "type"
 
@@ -204,18 +204,18 @@
 		return repr(self.value)
 
 
-class PSOperators:
-	
+class PSOperators(object):
+
 	def ps_def(self):
 		obj = self.pop()
 		name = self.pop()
 		self.dictstack[-1][name.value] = obj
-	
+
 	def ps_bind(self):
 		proc = self.pop('proceduretype')
 		self.proc_bind(proc)
 		self.push(proc)
-	
+
 	def proc_bind(self, proc):
 		for i in range(len(proc.value)):
 			item = proc.value[i]
@@ -230,7 +230,7 @@
 					else:
 						if obj.type == 'operatortype':
 							proc.value[i] = obj
-	
+
 	def ps_exch(self):
 		if len(self.stack) < 2:
 			raise RuntimeError('stack underflow')
@@ -238,49 +238,49 @@
 		obj2 = self.pop()
 		self.push(obj1)
 		self.push(obj2)
-	
+
 	def ps_dup(self):
 		if not self.stack:
 			raise RuntimeError('stack underflow')
 		self.push(self.stack[-1])
-	
+
 	def ps_exec(self):
 		obj = self.pop()
 		if obj.type == 'proceduretype':
 			self.call_procedure(obj)
 		else:
 			self.handle_object(obj)
-	
+
 	def ps_count(self):
 		self.push(ps_integer(len(self.stack)))
-	
+
 	def ps_eq(self):
 		any1 = self.pop()
 		any2 = self.pop()
 		self.push(ps_boolean(any1.value == any2.value))
-	
+
 	def ps_ne(self):
 		any1 = self.pop()
 		any2 = self.pop()
 		self.push(ps_boolean(any1.value != any2.value))
-	
+
 	def ps_cvx(self):
 		obj = self.pop()
 		obj.literal = 0
 		self.push(obj)
-	
+
 	def ps_matrix(self):
 		matrix = [ps_real(1.0), ps_integer(0), ps_integer(0), ps_real(1.0), ps_integer(0), ps_integer(0)]
 		self.push(ps_array(matrix))
-	
+
 	def ps_string(self):
 		num = self.pop('integertype').value
 		self.push(ps_string('\0' * num))
-	
+
 	def ps_type(self):
 		obj = self.pop()
 		self.push(ps_string(obj.type))
-	
+
 	def ps_store(self):
 		value = self.pop()
 		key = self.pop()
@@ -290,41 +290,41 @@
 				self.dictstack[i][name] = value
 				break
 		self.dictstack[-1][name] = value
-	
+
 	def ps_where(self):
 		name = self.pop()
 		# XXX
 		self.push(ps_boolean(0))
-	
+
 	def ps_systemdict(self):
 		self.push(ps_dict(self.dictstack[0]))
-	
+
 	def ps_userdict(self):
 		self.push(ps_dict(self.dictstack[1]))
-	
+
 	def ps_currentdict(self):
 		self.push(ps_dict(self.dictstack[-1]))
-	
+
 	def ps_currentfile(self):
 		self.push(ps_file(self.tokenizer))
-	
+
 	def ps_eexec(self):
 		f = self.pop('filetype').value
 		f.starteexec()
-	
+
 	def ps_closefile(self):
 		f = self.pop('filetype').value
 		f.skipwhite()
 		f.stopeexec()
-	
+
 	def ps_cleartomark(self):
 		obj = self.pop()
 		while obj != self.mark:
 			obj = self.pop()
-	
+
 	def ps_readstring(self,
-				ps_boolean = ps_boolean,
-				len = len):
+			ps_boolean=ps_boolean,
+			len=len):
 		s = self.pop('stringtype')
 		oldstr = s.value
 		f = self.pop('filetype')
@@ -335,17 +335,17 @@
 		s.value = newstr
 		self.push(s)
 		self.push(ps_boolean(len(oldstr) == len(newstr)))
-	
+
 	def ps_known(self):
 		key = self.pop()
 		d = self.pop('dicttype', 'fonttype')
 		self.push(ps_boolean(key.value in d.value))
-	
+
 	def ps_if(self):
 		proc = self.pop('proceduretype')
 		if self.pop('booleantype').value:
 			self.call_procedure(proc)
-	
+
 	def ps_ifelse(self):
 		proc2 = self.pop('proceduretype')
 		proc1 = self.pop('proceduretype')
@@ -353,36 +353,36 @@
 			self.call_procedure(proc1)
 		else:
 			self.call_procedure(proc2)
-	
+
 	def ps_readonly(self):
 		obj = self.pop()
 		if obj.access < 1:
 			obj.access = 1
 		self.push(obj)
-	
+
 	def ps_executeonly(self):
 		obj = self.pop()
 		if obj.access < 2:
 			obj.access = 2
 		self.push(obj)
-	
+
 	def ps_noaccess(self):
 		obj = self.pop()
 		if obj.access < 3:
 			obj.access = 3
 		self.push(obj)
-	
+
 	def ps_not(self):
 		obj = self.pop('booleantype', 'integertype')
 		if obj.type == 'booleantype':
 			self.push(ps_boolean(not obj.value))
 		else:
 			self.push(ps_integer(~obj.value))
-	
+
 	def ps_print(self):
 		str = self.pop('stringtype')
 		print('PS output --->', str.value)
-	
+
 	def ps_anchorsearch(self):
 		seek = self.pop('stringtype')
 		s = self.pop('stringtype')
@@ -394,22 +394,22 @@
 		else:
 			self.push(s)
 			self.push(ps_boolean(0))
-	
+
 	def ps_array(self):
 		num = self.pop('integertype')
 		array = ps_array([None] * num.value)
 		self.push(array)
-	
+
 	def ps_astore(self):
 		array = self.pop('arraytype')
 		for i in range(len(array.value)-1, -1, -1):
 			array.value[i] = self.pop()
 		self.push(array)
-	
+
 	def ps_load(self):
 		name = self.pop()
 		self.push(self.resolve_name(name.value))
-	
+
 	def ps_put(self):
 		obj1 = self.pop()
 		obj2 = self.pop()
@@ -422,7 +422,7 @@
 		elif tp == 'stringtype':
 			index = obj2.value
 			obj3.value = obj3.value[:index] + chr(obj1.value) + obj3.value[index+1:]
-	
+
 	def ps_get(self):
 		obj1 = self.pop()
 		if obj1.value == "Encoding":
@@ -437,7 +437,7 @@
 			self.push(ps_integer(ord(obj2.value[obj1.value])))
 		else:
 			assert False, "shouldn't get here"
-	
+
 	def ps_getinterval(self):
 		obj1 = self.pop('integertype')
 		obj2 = self.pop('integertype')
@@ -447,7 +447,7 @@
 			self.push(ps_array(obj3.value[obj2.value:obj2.value + obj1.value]))
 		elif tp == 'stringtype':
 			self.push(ps_string(obj3.value[obj2.value:obj2.value + obj1.value]))
-	
+
 	def ps_putinterval(self):
 		obj1 = self.pop('arraytype', 'stringtype')
 		obj2 = self.pop('integertype')
@@ -460,16 +460,16 @@
 			newstr = newstr + obj1.value
 			newstr = newstr + obj3.value[obj2.value + len(obj1.value):]
 			obj3.value = newstr
-	
+
 	def ps_cvn(self):
 		self.push(ps_name(self.pop('stringtype').value))
-	
+
 	def ps_index(self):
 		n = self.pop('integertype').value
 		if n < 0:
 			raise RuntimeError('index may not be negative')
 		self.push(self.stack[-1-n])
-	
+
 	def ps_for(self):
 		proc = self.pop('proceduretype')
 		limit = self.pop('integertype', 'realtype').value
@@ -488,7 +488,7 @@
 				self.push(ps_integer(i))
 			self.call_procedure(proc)
 			i = i + increment
-	
+
 	def ps_forall(self):
 		proc = self.pop('proceduretype')
 		obj = self.pop('arraytype', 'stringtype', 'dicttype')
@@ -505,37 +505,36 @@
 			for key, value in obj.value.items():
 				self.push(ps_name(key))
 				self.push(value)
-				self.call_procedure(proc)		
-	
+				self.call_procedure(proc)
+
 	def ps_definefont(self):
 		font = self.pop('dicttype')
 		name = self.pop()
 		font = ps_font(font.value)
 		self.dictstack[0]['FontDirectory'].value[name.value] = font
 		self.push(font)
-	
+
 	def ps_findfont(self):
 		name = self.pop()
 		font = self.dictstack[0]['FontDirectory'].value[name.value]
 		self.push(font)
-	
+
 	def ps_pop(self):
 		self.pop()
-	
+
 	def ps_dict(self):
 		self.pop('integertype')
 		self.push(ps_dict({}))
-	
+
 	def ps_begin(self):
 		self.dictstack.append(self.pop('dicttype').value)
-	
+
 	def ps_end(self):
 		if len(self.dictstack) > 2:
 			del self.dictstack[-1]
 		else:
 			raise RuntimeError('dictstack underflow')
-	
+
 notdef = '.notdef'
 from fontTools.encodings.StandardEncoding import StandardEncoding
 ps_StandardEncoding = list(map(ps_name, StandardEncoding))
-
diff --git a/Lib/fontTools/misc/py23.py b/Lib/fontTools/misc/py23.py
index 90217a3..0a13b6f 100644
--- a/Lib/fontTools/misc/py23.py
+++ b/Lib/fontTools/misc/py23.py
@@ -1,35 +1,137 @@
 """Python 2/3 compat layer."""
 
 from __future__ import print_function, division, absolute_import
+import sys
+
+
+__all__ = ['basestring', 'unicode', 'unichr', 'byteord', 'bytechr', 'BytesIO',
+		'StringIO', 'UnicodeIO', 'strjoin', 'bytesjoin', 'tobytes', 'tostr',
+		'tounicode', 'Tag', 'open', 'range', 'xrange', 'round', 'Py23Error',
+		'SimpleNamespace', 'zip']
+
+
+class Py23Error(NotImplementedError):
+	pass
+
+
+PY3 = sys.version_info[0] == 3
+PY2 = sys.version_info[0] == 2
+
 
 try:
-	basestring
+	basestring = basestring
 except NameError:
 	basestring = str
 
 try:
-	unicode
+	unicode = unicode
 except NameError:
 	unicode = str
 
 try:
-	unichr
+	unichr = unichr
+
+	if sys.maxunicode < 0x10FFFF:
+		# workarounds for Python 2 "narrow" builds with UCS2-only support.
+
+		_narrow_unichr = unichr
+
+		def unichr(i):
+			"""
+			Return the unicode character whose Unicode code is the integer 'i'.
+			The valid range is 0 to 0x10FFFF inclusive.
+
+			>>> _narrow_unichr(0xFFFF + 1)
+			Traceback (most recent call last):
+			  File "<stdin>", line 1, in ?
+			ValueError: unichr() arg not in range(0x10000) (narrow Python build)
+			>>> unichr(0xFFFF + 1) == u'\U00010000'
+			True
+			>>> unichr(1114111) == u'\U0010FFFF'
+			True
+			>>> unichr(0x10FFFF + 1)
+			Traceback (most recent call last):
+			  File "<stdin>", line 1, in ?
+			ValueError: unichr() arg not in range(0x110000)
+			"""
+			try:
+				return _narrow_unichr(i)
+			except ValueError:
+				try:
+					padded_hex_str = hex(i)[2:].zfill(8)
+					escape_str = "\\U" + padded_hex_str
+					return escape_str.decode("unicode-escape")
+				except UnicodeDecodeError:
+					raise ValueError('unichr() arg not in range(0x110000)')
+
+		import re
+		_unicode_escape_RE = re.compile(r'\\U[A-Fa-f0-9]{8}')
+
+		def byteord(c):
+			"""
+			Given a 8-bit or unicode character, return an integer representing the
+			Unicode code point of the character. If a unicode argument is given, the
+			character's code point must be in the range 0 to 0x10FFFF inclusive.
+
+			>>> ord(u'\U00010000')
+			Traceback (most recent call last):
+			  File "<stdin>", line 1, in ?
+			TypeError: ord() expected a character, but string of length 2 found
+			>>> byteord(u'\U00010000') == 0xFFFF + 1
+			True
+			>>> byteord(u'\U0010FFFF') == 1114111
+			True
+			"""
+			try:
+				return ord(c)
+			except TypeError as e:
+				try:
+					escape_str = c.encode('unicode-escape')
+					if not _unicode_escape_RE.match(escape_str):
+						raise
+					hex_str = escape_str[3:]
+					return int(hex_str, 16)
+				except:
+					raise TypeError(e)
+
+	else:
+		byteord = ord
 	bytechr = chr
-	byteord = ord
-except:
+
+except NameError:
 	unichr = chr
 	def bytechr(n):
 		return bytes([n])
 	def byteord(c):
 		return c if isinstance(c, int) else ord(c)
 
+
+# the 'io' module provides the same I/O interface on both 2 and 3.
+# here we define an alias of io.StringIO to disambiguate it eternally...
+from io import BytesIO
+from io import StringIO as UnicodeIO
 try:
+	# in python 2, by 'StringIO' we still mean a stream of *byte* strings
 	from StringIO import StringIO
 except ImportError:
-	from io import BytesIO as StringIO
+	# in Python 3, we mean instead a stream of *unicode* strings
+	StringIO = UnicodeIO
 
-def strjoin(iterable):
-	return ''.join(iterable)
+
+def strjoin(iterable, joiner=''):
+	return tostr(joiner).join(iterable)
+
+def tobytes(s, encoding='ascii', errors='strict'):
+	if not isinstance(s, bytes):
+		return s.encode(encoding, errors)
+	else:
+		return s
+def tounicode(s, encoding='ascii', errors='strict'):
+	if not isinstance(s, unicode):
+		return s.decode(encoding, errors)
+	else:
+		return s
+
 if str == bytes:
 	class Tag(str):
 		def tobytes(self):
@@ -38,12 +140,7 @@
 			else:
 				return self.encode('latin1')
 
-	def tostr(s, encoding='ascii'):
-		if not isinstance(s, str):
-			return s.encode(encoding)
-		else:
-			return s
-	tobytes = tostr
+	tostr = tobytes
 
 	bytesjoin = strjoin
 else:
@@ -51,7 +148,7 @@
 
 		@staticmethod
 		def transcode(blob):
-			if not isinstance(blob, str):
+			if isinstance(blob, bytes):
 				blob = blob.decode('latin-1')
 			return blob
 
@@ -68,16 +165,326 @@
 		def tobytes(self):
 			return self.encode('latin-1')
 
-	def tostr(s, encoding='ascii'):
-		if not isinstance(s, str):
-			return s.decode(encoding)
-		else:
-			return s
-	def tobytes(s, encoding='ascii'):
-		if not isinstance(s, bytes):
-			return s.encode(encoding)
-		else:
-			return s
+	tostr = tounicode
 
-	def bytesjoin(iterable):
-		return b''.join(tobytes(item) for item in iterable)
+	def bytesjoin(iterable, joiner=b''):
+		return tobytes(joiner).join(tobytes(item) for item in iterable)
+
+
+import os
+import io as _io
+
+try:
+	from msvcrt import setmode as _setmode
+except ImportError:
+	_setmode = None  # only available on the Windows platform
+
+
+def open(file, mode='r', buffering=-1, encoding=None, errors=None,
+		 newline=None, closefd=True, opener=None):
+	""" Wrapper around `io.open` that bridges the differences between Python 2
+	and Python 3's built-in `open` functions. In Python 2, `io.open` is a
+	backport of Python 3's `open`, whereas in Python 3, it is an alias of the
+	built-in `open` function.
+
+	One difference is that the 'opener' keyword argument is only supported in
+	Python 3. Here we pass the value of 'opener' only when it is not None.
+	This causes Python 2 to raise TypeError, complaining about the number of
+	expected arguments, so it must be avoided in py2 or py2-3 contexts.
+
+	Another difference between 2 and 3, this time on Windows, has to do with
+	opening files by name or by file descriptor.
+
+	On the Windows C runtime, the 'O_BINARY' flag is defined which disables
+	the newlines translation ('\r\n' <=> '\n') when reading/writing files.
+	On both Python 2 and 3 this flag is always set when opening files by name.
+	This way, the newlines translation at the MSVCRT level doesn't interfere
+	with the Python io module's own newlines translation.
+
+	However, when opening files via fd, on Python 2 the fd is simply copied,
+	regardless of whether it has the 'O_BINARY' flag set or not.
+	This becomes a problem in the case of stdout, stdin, and stderr, because on
+	Windows these are opened in text mode by default (ie. don't have the
+	O_BINARY flag set).
+
+	On Python 3, this issue has been fixed, and all fds are now opened in
+	binary mode on Windows, including standard streams. Similarly here, I use
+	the `_setmode` function to ensure that integer file descriptors are
+	O_BINARY'ed before I pass them on to io.open.
+
+	For more info, see: https://bugs.python.org/issue10841
+	"""
+	if isinstance(file, int):
+		# the 'file' argument is an integer file descriptor
+		fd = file
+		if fd < 0:
+			raise ValueError('negative file descriptor')
+		if _setmode:
+			# `_setmode` function sets the line-end translation and returns the
+			# value of the previous mode. AFAIK there's no `_getmode`, so to
+			# check if the previous mode already had the bit set, I fist need
+			# to duplicate the file descriptor, set the binary flag on the copy
+			# and check the returned value.
+			fdcopy = os.dup(fd)
+			current_mode = _setmode(fdcopy, os.O_BINARY)
+			if not (current_mode & os.O_BINARY):
+				# the binary mode was not set: use the file descriptor's copy
+				file = fdcopy
+				if closefd:
+					# close the original file descriptor
+					os.close(fd)
+				else:
+					# ensure the copy is closed when the file object is closed
+					closefd = True
+			else:
+				# original file descriptor already had binary flag, close copy
+				os.close(fdcopy)
+
+	if opener is not None:
+		# "opener" is not supported on Python 2, use it at your own risk!
+		return _io.open(
+			file, mode, buffering, encoding, errors, newline, closefd,
+			opener=opener)
+	else:
+		return _io.open(
+			file, mode, buffering, encoding, errors, newline, closefd)
+
+
+# always use iterator for 'range' and 'zip' on both py 2 and 3
+try:
+	range = xrange
+except NameError:
+	range = range
+
+def xrange(*args, **kwargs):
+	raise Py23Error("'xrange' is not defined. Use 'range' instead.")
+
+try:
+	from itertools import izip as zip
+except ImportError:
+	zip = zip
+
+
+import math as _math
+
+try:
+	isclose = _math.isclose
+except AttributeError:
+	# math.isclose() was only added in Python 3.5
+
+	_isinf = _math.isinf
+	_fabs = _math.fabs
+
+	def isclose(a, b, rel_tol=1e-09, abs_tol=0):
+		"""
+		Python 2 implementation of Python 3.5 math.isclose()
+		https://hg.python.org/cpython/file/v3.5.2/Modules/mathmodule.c#l1993
+		"""
+		# sanity check on the inputs
+		if rel_tol < 0 or abs_tol < 0:
+			raise ValueError("tolerances must be non-negative")
+		# short circuit exact equality -- needed to catch two infinities of
+		# the same sign. And perhaps speeds things up a bit sometimes.
+		if a == b:
+			return True
+		# This catches the case of two infinities of opposite sign, or
+		# one infinity and one finite number. Two infinities of opposite
+		# sign would otherwise have an infinite relative tolerance.
+		# Two infinities of the same sign are caught by the equality check
+		# above.
+		if _isinf(a) or _isinf(b):
+			return False
+		# Cast to float to allow decimal.Decimal arguments
+		if not isinstance(a, float):
+			a = float(a)
+		if not isinstance(b, float):
+			b = float(b)
+		# now do the regular computation
+		# this is essentially the "weak" test from the Boost library
+		diff = _fabs(b - a)
+		result = ((diff <= _fabs(rel_tol * a)) or
+				  (diff <= _fabs(rel_tol * b)) or
+				  (diff <= abs_tol))
+		return result
+
+
+import decimal as _decimal
+
+if PY3:
+	def round2(number, ndigits=None):
+		"""
+		Implementation of Python 2 built-in round() function.
+
+		Rounds a number to a given precision in decimal digits (default
+		0 digits). The result is a floating point number. Values are rounded
+		to the closest multiple of 10 to the power minus ndigits; if two
+		multiples are equally close, rounding is done away from 0.
+
+		ndigits may be negative.
+
+		See Python 2 documentation:
+		https://docs.python.org/2/library/functions.html?highlight=round#round
+		"""
+		if ndigits is None:
+			ndigits = 0
+
+		if ndigits < 0:
+			exponent = 10 ** (-ndigits)
+			quotient, remainder = divmod(number, exponent)
+			if remainder >= exponent//2 and number >= 0:
+				quotient += 1
+			return float(quotient * exponent)
+		else:
+			exponent = _decimal.Decimal('10') ** (-ndigits)
+
+			d = _decimal.Decimal.from_float(number).quantize(
+				exponent, rounding=_decimal.ROUND_HALF_UP)
+
+			return float(d)
+
+	if sys.version_info[:2] >= (3, 6):
+		# in Python 3.6, 'round3' is an alias to the built-in 'round'
+		round = round3 = round
+	else:
+		# in Python3 < 3.6 we need work around the inconsistent behavior of
+		# built-in round(), whereby floats accept a second None argument,
+		# while integers raise TypeError. See https://bugs.python.org/issue27936
+		_round = round
+
+		def round3(number, ndigits=None):
+			return _round(number) if ndigits is None else _round(number, ndigits)
+
+		round = round3
+
+else:
+	# in Python 2, 'round2' is an alias to the built-in 'round' and
+	# 'round' is shadowed by 'round3'
+	round2 = round
+
+	def round3(number, ndigits=None):
+		"""
+		Implementation of Python 3 built-in round() function.
+
+		Rounds a number to a given precision in decimal digits (default
+		0 digits). This returns an int when ndigits is omitted or is None,
+		otherwise the same type as the number.
+
+		Values are rounded to the closest multiple of 10 to the power minus
+		ndigits; if two multiples are equally close, rounding is done toward
+		the even choice (aka "Banker's Rounding"). For example, both round(0.5)
+		and round(-0.5) are 0, and round(1.5) is 2.
+
+		ndigits may be negative.
+
+		See Python 3 documentation:
+		https://docs.python.org/3/library/functions.html?highlight=round#round
+
+		Derived from python-future:
+		https://github.com/PythonCharmers/python-future/blob/master/src/future/builtins/newround.py
+		"""
+		if ndigits is None:
+			ndigits = 0
+			# return an int when called with one argument
+			totype = int
+			# shortcut if already an integer, or a float with no decimal digits
+			inumber = totype(number)
+			if inumber == number:
+				return inumber
+		else:
+			# return the same type as the number, when called with two arguments
+			totype = type(number)
+
+		m = number * (10 ** ndigits)
+		# if number is half-way between two multiples, and the mutliple that is
+		# closer to zero is even, we use the (slow) pure-Python implementation
+		if isclose(m % 1, .5) and int(m) % 2 == 0:
+			if ndigits < 0:
+				exponent = 10 ** (-ndigits)
+				quotient, remainder = divmod(number, exponent)
+				half = exponent//2
+				if remainder > half or (remainder == half and quotient % 2 != 0):
+					quotient += 1
+				d = quotient * exponent
+			else:
+				exponent = _decimal.Decimal('10') ** (-ndigits) if ndigits != 0 else 1
+
+				d = _decimal.Decimal.from_float(number).quantize(
+					exponent, rounding=_decimal.ROUND_HALF_EVEN)
+		else:
+			# else we use the built-in round() as it produces the same results
+			d = round2(number, ndigits)
+
+		return totype(d)
+
+	round = round3
+
+
+try:
+	from types import SimpleNamespace
+except ImportError:
+	class SimpleNamespace(object):
+		"""
+		A backport of Python 3.3's ``types.SimpleNamespace``.
+		"""
+		def __init__(self, **kwargs):
+			self.__dict__.update(kwargs)
+
+		def __repr__(self):
+			keys = sorted(self.__dict__)
+			items = ("{0}={1!r}".format(k, self.__dict__[k]) for k in keys)
+			return "{0}({1})".format(type(self).__name__, ", ".join(items))
+
+		def __eq__(self, other):
+			return self.__dict__ == other.__dict__
+
+
+if sys.version_info[:2] > (3, 4):
+	from contextlib import redirect_stdout, redirect_stderr
+else:
+	# `redirect_stdout` was added with python3.4, while `redirect_stderr`
+	# with python3.5. For simplicity, I redefine both for any versions
+	# less than or equal to 3.4.
+	# The code below is copied from:
+	# https://github.com/python/cpython/blob/57161aa/Lib/contextlib.py
+
+	class _RedirectStream(object):
+
+		_stream = None
+
+		def __init__(self, new_target):
+			self._new_target = new_target
+			# We use a list of old targets to make this CM re-entrant
+			self._old_targets = []
+
+		def __enter__(self):
+			self._old_targets.append(getattr(sys, self._stream))
+			setattr(sys, self._stream, self._new_target)
+			return self._new_target
+
+		def __exit__(self, exctype, excinst, exctb):
+			setattr(sys, self._stream, self._old_targets.pop())
+
+
+	class redirect_stdout(_RedirectStream):
+		"""Context manager for temporarily redirecting stdout to another file.
+			# How to send help() to stderr
+			with redirect_stdout(sys.stderr):
+				help(dir)
+			# How to write help() to a file
+			with open('help.txt', 'w') as f:
+				with redirect_stdout(f):
+					help(pow)
+		"""
+
+		_stream = "stdout"
+
+
+	class redirect_stderr(_RedirectStream):
+		"""Context manager for temporarily redirecting stderr to another file."""
+
+		_stream = "stderr"
+
+
+if __name__ == "__main__":
+	import doctest, sys
+	sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/misc/sstruct.py b/Lib/fontTools/misc/sstruct.py
index 8a2b073..528b5e0 100644
--- a/Lib/fontTools/misc/sstruct.py
+++ b/Lib/fontTools/misc/sstruct.py
@@ -1,44 +1,44 @@
 """sstruct.py -- SuperStruct
 
-Higher level layer on top of the struct module, enabling to 
-bind names to struct elements. The interface is similar to 
-struct, except the objects passed and returned are not tuples 
-(or argument lists), but dictionaries or instances. 
+Higher level layer on top of the struct module, enabling to
+bind names to struct elements. The interface is similar to
+struct, except the objects passed and returned are not tuples
+(or argument lists), but dictionaries or instances.
 
-Just like struct, we use fmt strings to describe a data 
-structure, except we use one line per element. Lines are 
-separated by newlines or semi-colons. Each line contains 
-either one of the special struct characters ('@', '=', '<', 
-'>' or '!') or a 'name:formatchar' combo (eg. 'myFloat:f'). 
-Repetitions, like the struct module offers them are not useful 
-in this context, except for fixed length strings  (eg. 'myInt:5h' 
-is not allowed but 'myString:5s' is). The 'x' fmt character 
-(pad byte) is treated as 'special', since it is by definition 
+Just like struct, we use fmt strings to describe a data
+structure, except we use one line per element. Lines are
+separated by newlines or semi-colons. Each line contains
+either one of the special struct characters ('@', '=', '<',
+'>' or '!') or a 'name:formatchar' combo (eg. 'myFloat:f').
+Repetitions, like the struct module offers them are not useful
+in this context, except for fixed length strings  (eg. 'myInt:5h'
+is not allowed but 'myString:5s' is). The 'x' fmt character
+(pad byte) is treated as 'special', since it is by definition
 anonymous. Extra whitespace is allowed everywhere.
 
 The sstruct module offers one feature that the "normal" struct
 module doesn't: support for fixed point numbers. These are spelled
 as "n.mF", where n is the number of bits before the point, and m
-the number of bits after the point. Fixed point numbers get 
+the number of bits after the point. Fixed point numbers get
 converted to floats.
 
 pack(fmt, object):
 	'object' is either a dictionary or an instance (or actually
-	anything that has a __dict__ attribute). If it is a dictionary, 
-	its keys are used for names. If it is an instance, it's 
+	anything that has a __dict__ attribute). If it is a dictionary,
+	its keys are used for names. If it is an instance, it's
 	attributes are used to grab struct elements from. Returns
 	a string containing the data.
 
 unpack(fmt, data, object=None)
-	If 'object' is omitted (or None), a new dictionary will be 
-	returned. If 'object' is a dictionary, it will be used to add 
+	If 'object' is omitted (or None), a new dictionary will be
+	returned. If 'object' is a dictionary, it will be used to add
 	struct elements to. If it is an instance (or in fact anything
-	that has a __dict__ attribute), an attribute will be added for 
-	each struct element. In the latter two cases, 'object' itself 
+	that has a __dict__ attribute), an attribute will be added for
+	each struct element. In the latter two cases, 'object' itself
 	is returned.
 
 unpack2(fmt, data, object=None)
-	Convenience function. Same as unpack, except data may be longer 
+	Convenience function. Same as unpack, except data may be longer
 	than needed. The returned value is a tuple: (object, leftoverdata).
 
 calcsize(fmt)
@@ -133,6 +133,7 @@
 _formatcache = {}
 
 def getformat(fmt):
+	fmt = tostr(fmt, encoding="ascii")
 	try:
 		formatstring, names, fixes = _formatcache[fmt]
 	except KeyError:
@@ -174,7 +175,7 @@
 		# comments are allowed
 		>  # big endian (see documentation for struct)
 		# empty lines are allowed:
-		
+
 		ashort: h
 		along: l
 		abyte: b	# a byte
@@ -183,14 +184,14 @@
 		afloat: f; adouble: d	# multiple "statements" are allowed
 		afixed: 16.16F
 	"""
-	
+
 	print('size:', calcsize(fmt))
-	
+
 	class foo(object):
 		pass
-	
+
 	i = foo()
-	
+
 	i.ashort = 0x7fff
 	i.along = 0x7fffffff
 	i.abyte = 0x7f
@@ -199,7 +200,7 @@
 	i.afloat = 0.5
 	i.adouble = 0.5
 	i.afixed = 1.5
-	
+
 	data = pack(fmt, i)
 	print('data:', repr(data))
 	print(unpack(fmt, data))
diff --git a/Lib/fontTools/misc/symfont.py b/Lib/fontTools/misc/symfont.py
new file mode 100644
index 0000000..d09efac
--- /dev/null
+++ b/Lib/fontTools/misc/symfont.py
@@ -0,0 +1,196 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.pens.basePen import BasePen
+from functools import partial
+from itertools import count
+import sympy as sp
+import sys
+
+n = 3 # Max Bezier degree; 3 for cubic, 2 for quadratic
+
+t, x, y = sp.symbols('t x y', real=True)
+c = sp.symbols('c', real=False) # Complex representation instead of x/y
+
+X = tuple(sp.symbols('x:%d'%(n+1), real=True))
+Y = tuple(sp.symbols('y:%d'%(n+1), real=True))
+P = tuple(zip(*(sp.symbols('p:%d[%s]'%(n+1,w), real=True) for w in '01')))
+C = tuple(sp.symbols('c:%d'%(n+1), real=False))
+
+# Cubic Bernstein basis functions
+BinomialCoefficient = [(1, 0)]
+for i in range(1, n+1):
+	last = BinomialCoefficient[-1]
+	this = tuple(last[j-1]+last[j] for j in range(len(last)))+(0,)
+	BinomialCoefficient.append(this)
+BinomialCoefficient = tuple(tuple(item[:-1]) for item in BinomialCoefficient)
+del last, this
+
+BernsteinPolynomial = tuple(
+	tuple(c * t**i * (1-t)**(n-i) for i,c in enumerate(coeffs))
+	for n,coeffs in enumerate(BinomialCoefficient))
+
+BezierCurve = tuple(
+	tuple(sum(P[i][j]*bernstein for i,bernstein in enumerate(bernsteins))
+		for j in range(2))
+	for n,bernsteins in enumerate(BernsteinPolynomial))
+BezierCurveC = tuple(
+	sum(C[i]*bernstein for i,bernstein in enumerate(bernsteins))
+	for n,bernsteins in enumerate(BernsteinPolynomial))
+
+
+def green(f, curveXY):
+	f = -sp.integrate(sp.sympify(f), y)
+	f = f.subs({x:curveXY[0], y:curveXY[1]})
+	f = sp.integrate(f * sp.diff(curveXY[0], t), (t, 0, 1))
+	return f
+
+
+class _BezierFuncsLazy(dict):
+
+	def __init__(self, symfunc):
+		self._symfunc = symfunc
+		self._bezfuncs = {}
+
+	def __missing__(self, i):
+		args = ['p%d'%d for d in range(i+1)]
+		f = green(self._symfunc, BezierCurve[i])
+		f = sp.gcd_terms(f.collect(sum(P,()))) # Optimize
+		return sp.lambdify(args, f)
+
+class GreenPen(BasePen):
+
+	_BezierFuncs = {}
+
+	@classmethod
+	def _getGreenBezierFuncs(celf, func):
+		funcstr = str(func)
+		if not funcstr in celf._BezierFuncs:
+			celf._BezierFuncs[funcstr] = _BezierFuncsLazy(func)
+		return celf._BezierFuncs[funcstr]
+
+	def __init__(self, func, glyphset=None):
+		BasePen.__init__(self, glyphset)
+		self._funcs = self._getGreenBezierFuncs(func)
+		self.value = 0
+
+	def _moveTo(self, p0):
+		self.__startPoint = p0
+
+	def _closePath(self):
+		p0 = self._getCurrentPoint()
+		if p0 != self.__startPoint:
+			self._lineTo(self.__startPoint)
+
+	def _endPath(self):
+		p0 = self._getCurrentPoint()
+		if p0 != self.__startPoint:
+			# Green theorem is not defined on open contours.
+			raise NotImplementedError
+
+	def _lineTo(self, p1):
+		p0 = self._getCurrentPoint()
+		self.value += self._funcs[1](p0, p1)
+
+	def _qCurveToOne(self, p1, p2):
+		p0 = self._getCurrentPoint()
+		self.value += self._funcs[2](p0, p1, p2)
+
+	def _curveToOne(self, p1, p2, p3):
+		p0 = self._getCurrentPoint()
+		self.value += self._funcs[3](p0, p1, p2, p3)
+
+# Sample pens.
+# Do not use this in real code.
+# Use fontTools.pens.momentsPen.MomentsPen instead.
+AreaPen = partial(GreenPen, func=1)
+MomentXPen = partial(GreenPen, func=x)
+MomentYPen = partial(GreenPen, func=y)
+MomentXXPen = partial(GreenPen, func=x*x)
+MomentYYPen = partial(GreenPen, func=y*y)
+MomentXYPen = partial(GreenPen, func=x*y)
+
+
+def printGreenPen(penName, funcs, file=sys.stdout):
+
+	print(
+'''from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.pens.basePen import BasePen
+
+class %s(BasePen):
+
+	def __init__(self, glyphset=None):
+		BasePen.__init__(self, glyphset)
+'''%penName, file=file)
+	for name,f in funcs:
+		print('		self.%s = 0' % name, file=file)
+	print('''
+	def _moveTo(self, p0):
+		self.__startPoint = p0
+
+	def _closePath(self):
+		p0 = self._getCurrentPoint()
+		if p0 != self.__startPoint:
+			self._lineTo(self.__startPoint)
+
+	def _endPath(self):
+		p0 = self._getCurrentPoint()
+		if p0 != self.__startPoint:
+			# Green theorem is not defined on open contours.
+			raise NotImplementedError
+''', end='', file=file)
+
+	for n in (1, 2, 3):
+
+		if n == 1:
+			print('''
+	def _lineTo(self, p1):
+		x0,y0 = self._getCurrentPoint()
+		x1,y1 = p1
+''', file=file)
+		elif n == 2:
+			print('''
+	def _qCurveToOne(self, p1, p2):
+		x0,y0 = self._getCurrentPoint()
+		x1,y1 = p1
+		x2,y2 = p2
+''', file=file)
+		elif n == 3:
+			print('''
+	def _curveToOne(self, p1, p2, p3):
+		x0,y0 = self._getCurrentPoint()
+		x1,y1 = p1
+		x2,y2 = p2
+		x3,y3 = p3
+''', file=file)
+		subs = {P[i][j]: [X, Y][j][i] for i in range(n+1) for j in range(2)}
+		greens = [green(f, BezierCurve[n]) for name,f in funcs]
+		greens = [sp.gcd_terms(f.collect(sum(P,()))) for f in greens] # Optimize
+		greens = [f.subs(subs) for f in greens] # Convert to p to x/y
+		defs, exprs = sp.cse(greens,
+				     optimizations='basic',
+				     symbols=(sp.Symbol('r%d'%i) for i in count()))
+		for name,value in defs:
+			print('		%s = %s' % (name, value), file=file)
+		print(file=file)
+		for name,value in zip([f[0] for f in funcs], exprs):
+			print('		self.%s += %s' % (name, value), file=file)
+
+	print('''
+if __name__ == '__main__':
+	from fontTools.misc.symfont import x, y, printGreenPen
+	printGreenPen('%s', ['''%penName, file=file)
+	for name,f in funcs:
+		print("		      ('%s', %s)," % (name, str(f)), file=file)
+	print('		     ])', file=file)
+
+
+if __name__ == '__main__':
+	pen = AreaPen()
+	pen.moveTo((100,100))
+	pen.lineTo((100,200))
+	pen.lineTo((200,200))
+	pen.curveTo((200,250),(300,300),(250,350))
+	pen.lineTo((200,100))
+	pen.closePath()
+	print(pen.value)
diff --git a/Lib/fontTools/misc/testTools.py b/Lib/fontTools/misc/testTools.py
new file mode 100644
index 0000000..8a37b7e
--- /dev/null
+++ b/Lib/fontTools/misc/testTools.py
@@ -0,0 +1,183 @@
+"""Helpers for writing unit tests."""
+
+from __future__ import (print_function, division, absolute_import,
+                        unicode_literals)
+import collections
+import os
+import shutil
+import sys
+import tempfile
+from unittest import TestCase as _TestCase
+from fontTools.misc.py23 import *
+from fontTools.misc.xmlWriter import XMLWriter
+
+
+def parseXML(xmlSnippet):
+    """Parses a snippet of XML.
+
+    Input can be either a single string (unicode or UTF-8 bytes), or a
+    a sequence of strings.
+
+    The result is in the same format that would be returned by
+    XMLReader, but the parser imposes no constraints on the root
+    element so it can be called on small snippets of TTX files.
+    """
+    # To support snippets with multiple elements, we add a fake root.
+    reader = TestXMLReader_()
+    xml = b"<root>"
+    if isinstance(xmlSnippet, bytes):
+        xml += xmlSnippet
+    elif isinstance(xmlSnippet, unicode):
+        xml += tobytes(xmlSnippet, 'utf-8')
+    elif isinstance(xmlSnippet, collections.Iterable):
+        xml += b"".join(tobytes(s, 'utf-8') for s in xmlSnippet)
+    else:
+        raise TypeError("expected string or sequence of strings; found %r"
+                        % type(xmlSnippet).__name__)
+    xml += b"</root>"
+    reader.parser.Parse(xml, 0)
+    return reader.root[2]
+
+
+class FakeFont:
+    def __init__(self, glyphs):
+        self.glyphOrder_ = glyphs
+        self.reverseGlyphOrderDict_ = {g: i for i, g in enumerate(glyphs)}
+        self.lazy = False
+        self.tables = {}
+
+    def __getitem__(self, tag):
+        return self.tables[tag]
+
+    def __setitem__(self, tag, table):
+        self.tables[tag] = table
+
+    def get(self, tag, default=None):
+        return self.tables.get(tag, default)
+
+    def getGlyphID(self, name):
+        return self.reverseGlyphOrderDict_[name]
+
+    def getGlyphName(self, glyphID):
+        if glyphID < len(self.glyphOrder_):
+            return self.glyphOrder_[glyphID]
+        else:
+            return "glyph%.5d" % glyphID
+
+    def getGlyphOrder(self):
+        return self.glyphOrder_
+
+    def getReverseGlyphMap(self):
+        return self.reverseGlyphOrderDict_
+
+
+class TestXMLReader_(object):
+    def __init__(self):
+        from xml.parsers.expat import ParserCreate
+        self.parser = ParserCreate()
+        self.parser.StartElementHandler = self.startElement_
+        self.parser.EndElementHandler = self.endElement_
+        self.parser.CharacterDataHandler = self.addCharacterData_
+        self.root = None
+        self.stack = []
+
+    def startElement_(self, name, attrs):
+        element = (name, attrs, [])
+        if self.stack:
+            self.stack[-1][2].append(element)
+        else:
+            self.root = element
+        self.stack.append(element)
+
+    def endElement_(self, name):
+        self.stack.pop()
+
+    def addCharacterData_(self, data):
+        self.stack[-1][2].append(data)
+
+
+def makeXMLWriter(newlinestr='\n'):
+    # don't write OS-specific new lines
+    writer = XMLWriter(BytesIO(), newlinestr=newlinestr)
+    # erase XML declaration
+    writer.file.seek(0)
+    writer.file.truncate()
+    return writer
+
+
+def getXML(func, ttFont=None):
+    """Call the passed toXML function and return the written content as a
+    list of lines (unicode strings).
+    Result is stripped of XML declaration and OS-specific newline characters.
+    """
+    writer = makeXMLWriter()
+    func(writer, ttFont)
+    xml = writer.file.getvalue().decode("utf-8")
+    # toXML methods must always end with a writer.newline()
+    assert xml.endswith("\n")
+    return xml.splitlines()
+
+
+class MockFont(object):
+    """A font-like object that automatically adds any looked up glyphname
+    to its glyphOrder."""
+
+    def __init__(self):
+        self._glyphOrder = ['.notdef']
+
+        class AllocatingDict(dict):
+            def __missing__(reverseDict, key):
+                self._glyphOrder.append(key)
+                gid = len(reverseDict)
+                reverseDict[key] = gid
+                return gid
+        self._reverseGlyphOrder = AllocatingDict({'.notdef': 0})
+        self.lazy = False
+
+    def getGlyphID(self, glyph, requireReal=None):
+        gid = self._reverseGlyphOrder[glyph]
+        return gid
+
+    def getReverseGlyphMap(self):
+        return self._reverseGlyphOrder
+
+    def getGlyphName(self, gid):
+        return self._glyphOrder[gid]
+
+    def getGlyphOrder(self):
+        return self._glyphOrder
+
+
+class TestCase(_TestCase):
+
+    def __init__(self, methodName):
+        _TestCase.__init__(self, methodName)
+        # Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
+        # and fires deprecation warnings if a program uses the old name.
+        if not hasattr(self, "assertRaisesRegex"):
+            self.assertRaisesRegex = self.assertRaisesRegexp
+
+
+class DataFilesHandler(TestCase):
+
+    def setUp(self):
+        self.tempdir = None
+        self.num_tempfiles = 0
+
+    def tearDown(self):
+        if self.tempdir:
+            shutil.rmtree(self.tempdir)
+
+    def getpath(self, testfile):
+        folder = os.path.dirname(sys.modules[self.__module__].__file__)
+        return os.path.join(folder, "data", testfile)
+
+    def temp_dir(self):
+        if not self.tempdir:
+            self.tempdir = tempfile.mkdtemp()
+
+    def temp_font(self, font_path, file_name):
+        self.temp_dir()
+        temppath = os.path.join(self.tempdir, file_name)
+        shutil.copy2(font_path, temppath)
+        return temppath
diff --git a/Lib/fontTools/misc/textTools.py b/Lib/fontTools/misc/textTools.py
index d6e6a2f..08c5990 100644
--- a/Lib/fontTools/misc/textTools.py
+++ b/Lib/fontTools/misc/textTools.py
@@ -7,12 +7,15 @@
 import string
 
 
+# alias kept for backward compatibility
 safeEval = ast.literal_eval
 
+
 def readHex(content):
 	"""Convert a list of hex strings to binary data."""
 	return deHexStr(strjoin(chunk for chunk in content if isinstance(chunk, basestring)))
 
+
 def deHexStr(hexdata):
 	"""Convert a hex string to binary data."""
 	hexdata = strjoin(hexdata.split())
@@ -64,12 +67,37 @@
 
 
 def caselessSort(alist):
-	"""Return a sorted copy of a list. If there are only strings 
+	"""Return a sorted copy of a list. If there are only strings
 	in the list, it will not consider case.
 	"""
-	
+
 	try:
 		return sorted(alist, key=lambda a: (a.lower(), a))
 	except TypeError:
 		return sorted(alist)
 
+
+def pad(data, size):
+	r""" Pad byte string 'data' with null bytes until its length is a
+	multiple of 'size'.
+
+	>>> len(pad(b'abcd', 4))
+	4
+	>>> len(pad(b'abcde', 2))
+	6
+	>>> len(pad(b'abcde', 4))
+	8
+	>>> pad(b'abcdef', 4) == b'abcdef\x00\x00'
+	True
+	"""
+	data = tobytes(data)
+	if size > 1:
+		remainder = len(data) % size
+		if remainder:
+			data += b"\0" * (size - remainder)
+	return data
+
+
+if __name__ == "__main__":
+	import doctest, sys
+	sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/misc/timeTools.py b/Lib/fontTools/misc/timeTools.py
new file mode 100644
index 0000000..c30a377
--- /dev/null
+++ b/Lib/fontTools/misc/timeTools.py
@@ -0,0 +1,64 @@
+"""fontTools.misc.timeTools.py -- tools for working with OpenType timestamps.
+"""
+
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+import os
+import time
+import calendar
+
+
+epoch_diff = calendar.timegm((1904, 1, 1, 0, 0, 0, 0, 0, 0))
+
+DAYNAMES = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
+MONTHNAMES = [None, "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+			  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
+
+
+def asctime(t=None):
+	"""
+	Convert a tuple or struct_time representing a time as returned by gmtime()
+	or localtime() to a 24-character string of the following form:
+
+	>>> asctime(time.gmtime(0))
+	'Thu Jan  1 00:00:00 1970'
+
+	If t is not provided, the current time as returned by localtime() is used.
+	Locale information is not used by asctime().
+
+	This is meant to normalise the output of the built-in time.asctime() across
+	different platforms and Python versions.
+	In Python 3.x, the day of the month is right-justified, whereas on Windows
+	Python 2.7 it is padded with zeros.
+
+	See https://github.com/behdad/fonttools/issues/455
+	"""
+	if t is None:
+		t = time.localtime()
+	s = "%s %s %2s %s" % (
+		DAYNAMES[t.tm_wday], MONTHNAMES[t.tm_mon], t.tm_mday,
+		time.strftime("%H:%M:%S %Y", t))
+	return s
+
+
+def timestampToString(value):
+	return asctime(time.gmtime(max(0, value + epoch_diff)))
+
+def timestampFromString(value):
+	return calendar.timegm(time.strptime(value)) - epoch_diff
+
+def timestampNow():
+	# https://reproducible-builds.org/specs/source-date-epoch/
+	source_date_epoch = os.environ.get("SOURCE_DATE_EPOCH")
+	if source_date_epoch is not None:
+		return int(source_date_epoch) - epoch_diff
+	return int(time.time() - epoch_diff)
+
+def timestampSinceEpoch(value):
+	return int(value - epoch_diff)
+
+
+if __name__ == "__main__":
+	import sys
+	import doctest
+	sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/misc/transform.py b/Lib/fontTools/misc/transform.py
index be7d21a..1296571 100644
--- a/Lib/fontTools/misc/transform.py
+++ b/Lib/fontTools/misc/transform.py
@@ -79,7 +79,7 @@
 		>>> t.scale(2)
 		<Transform [2 0 0 2 0 0]>
 		>>> t.scale(2.5, 5.5)
-		<Transform [2.5 0.0 0.0 5.5 0 0]>
+		<Transform [2.5 0 0 5.5 0 0]>
 		>>>
 		>>> t.scale(2, 3).transformPoint((100, 100))
 		(200, 300)
@@ -172,7 +172,7 @@
 			>>> import math
 			>>> t = Transform()
 			>>> t.skew(math.pi / 4)
-			<Transform [1.0 0.0 1.0 1.0 0 0]>
+			<Transform [1 0 1 1 0 0]>
 			>>>
 		"""
 		import math
@@ -282,9 +282,9 @@
 			>>> t1 = Identity.scale(0.2, 0.3).translate(0.4, 0.6)
 			>>> t2 = Identity.translate(0.08, 0.18).scale(0.2, 0.3)
 			>>> t1
-			<Transform [0.2 0.0 0.0 0.3 0.08 0.18]>
+			<Transform [0.2 0 0 0.3 0.08 0.18]>
 			>>> t2
-			<Transform [0.2 0.0 0.0 0.3 0.08 0.18]>
+			<Transform [0.2 0 0 0.3 0.08 0.18]>
 			>>> t1 == t2
 			0
 			>>>
@@ -306,23 +306,44 @@
 			>>> t1 = Identity.scale(0.2, 0.3).translate(0.4, 0.6)
 			>>> t2 = Identity.translate(0.08, 0.18).scale(0.2, 0.3)
 			>>> t1
-			<Transform [0.2 0.0 0.0 0.3 0.08 0.18]>
+			<Transform [0.2 0 0 0.3 0.08 0.18]>
 			>>> t2
-			<Transform [0.2 0.0 0.0 0.3 0.08 0.18]>
+			<Transform [0.2 0 0 0.3 0.08 0.18]>
 			>>> d = {t1: None}
 			>>> d
-			{<Transform [0.2 0.0 0.0 0.3 0.08 0.18]>: None}
+			{<Transform [0.2 0 0 0.3 0.08 0.18]>: None}
 			>>> d[t2]
 			Traceback (most recent call last):
 			  File "<stdin>", line 1, in ?
-			KeyError: <Transform [0.2 0.0 0.0 0.3 0.08 0.18]>
+			KeyError: <Transform [0.2 0 0 0.3 0.08 0.18]>
 			>>>
 		"""
 		return hash(self.__affine)
 
+	def __bool__(self):
+		"""Returns True if transform is not identity, False otherwise.
+			>>> bool(Identity)
+			False
+			>>> bool(Transform())
+			False
+			>>> bool(Scale(1.))
+			False
+			>>> bool(Scale(2))
+			True
+			>>> bool(Offset())
+			False
+			>>> bool(Offset(0))
+			False
+			>>> bool(Offset(2))
+			True
+		"""
+		return self.__affine != Identity.__affine
+
+	__nonzero__ = __bool__
+
 	def __repr__(self):
-		return "<%s [%s %s %s %s %s %s]>" % ((self.__class__.__name__,) \
-				 + tuple(map(str, self.__affine)))
+		return "<%s [%g %g %g %g %g %g]>" % ((self.__class__.__name__,) \
+				+ self.__affine)
 
 
 Identity = Transform()
@@ -352,5 +373,6 @@
 
 
 if __name__ == "__main__":
+	import sys
 	import doctest
-	doctest.testmod()
+	sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/misc/xmlReader.py b/Lib/fontTools/misc/xmlReader.py
index 85dd441..e9b5cfd 100644
--- a/Lib/fontTools/misc/xmlReader.py
+++ b/Lib/fontTools/misc/xmlReader.py
@@ -3,40 +3,65 @@
 from fontTools import ttLib
 from fontTools.misc.textTools import safeEval
 from fontTools.ttLib.tables.DefaultTable import DefaultTable
+import sys
 import os
+import logging
 
 
+log = logging.getLogger(__name__)
+
 class TTXParseError(Exception): pass
 
 BUFSIZE = 0x4000
 
 
 class XMLReader(object):
-	
-	def __init__(self, fileName, ttFont, progress=None, quiet=False):
+
+	def __init__(self, fileOrPath, ttFont, progress=None, quiet=None, contentOnly=False):
+		if fileOrPath == '-':
+			fileOrPath = sys.stdin
+		if not hasattr(fileOrPath, "read"):
+			self.file = open(fileOrPath, "rb")
+			self._closeStream = True
+		else:
+			# assume readable file object
+			self.file = fileOrPath
+			self._closeStream = False
 		self.ttFont = ttFont
-		self.fileName = fileName
 		self.progress = progress
-		self.quiet = quiet
+		if quiet is not None:
+			from fontTools.misc.loggingTools import deprecateArgument
+			deprecateArgument("quiet", "configure logging instead")
+			self.quiet = quiet
 		self.root = None
 		self.contentStack = []
+		self.contentOnly = contentOnly
 		self.stackSize = 0
-	
-	def read(self):
+
+	def read(self, rootless=False):
+		if rootless:
+			self.stackSize += 1
 		if self.progress:
-			import stat
-			self.progress.set(0, os.stat(self.fileName)[stat.ST_SIZE] // 100 or 1)
-		file = open(self.fileName)
-		self._parseFile(file)
-		file.close()
-	
+			self.file.seek(0, 2)
+			fileSize = self.file.tell()
+			self.progress.set(0, fileSize // 100 or 1)
+			self.file.seek(0)
+		self._parseFile(self.file)
+		if self._closeStream:
+			self.close()
+		if rootless:
+			self.stackSize -= 1
+
+	def close(self):
+		self.file.close()
+
 	def _parseFile(self, file):
 		from xml.parsers.expat import ParserCreate
 		parser = ParserCreate()
 		parser.StartElementHandler = self._startElementHandler
 		parser.EndElementHandler = self._endElementHandler
 		parser.CharacterDataHandler = self._characterDataHandler
-		
+
 		pos = 0
 		while True:
 			chunk = file.read(BUFSIZE)
@@ -47,10 +72,26 @@
 			if self.progress:
 				self.progress.set(pos // 100)
 			parser.Parse(chunk, 0)
-	
+
 	def _startElementHandler(self, name, attrs):
+		if self.stackSize == 1 and self.contentOnly:
+			# We already know the table we're parsing, skip
+			# parsing the table tag and continue to
+			# stack '2' which begins parsing content
+			self.contentStack.append([])
+			self.stackSize = 2
+			return
 		stackSize = self.stackSize
 		self.stackSize = stackSize + 1
+		subFile = attrs.get("src")
+		if subFile is not None:
+			if hasattr(self.file, 'name'):
+				# if file has a name, get its parent directory
+				dirname = os.path.dirname(self.file.name)
+			else:
+				# else fall back to using the current working directory
+				dirname = os.getcwd()
+			subFile = os.path.join(dirname, subFile)
 		if not stackSize:
 			if name != "ttFont":
 				raise TTXParseError("illegal root tag: %s" % name)
@@ -61,22 +102,16 @@
 				self.ttFont.sfntVersion = sfntVersion
 			self.contentStack.append([])
 		elif stackSize == 1:
-			subFile = attrs.get("src")
 			if subFile is not None:
-				subFile = os.path.join(os.path.dirname(self.fileName), subFile)
-				subReader = XMLReader(subFile, self.ttFont, self.progress, self.quiet)
+				subReader = XMLReader(subFile, self.ttFont, self.progress)
 				subReader.read()
 				self.contentStack.append([])
 				return
 			tag = ttLib.xmlToTag(name)
 			msg = "Parsing '%s' table..." % tag
 			if self.progress:
-				self.progress.setlabel(msg)
-			elif self.ttFont.verbose:
-				ttLib.debugmsg(msg)
-			else:
-				if not self.quiet:
-					print(msg)
+				self.progress.setLabel(msg)
+			log.info(msg)
 			if tag == "GlyphOrder":
 				tableClass = ttLib.GlyphOrder
 			elif "ERROR" in attrs or ('raw' in attrs and safeEval(attrs['raw'])):
@@ -93,6 +128,11 @@
 				self.currentTable = tableClass(tag)
 				self.ttFont[tag] = self.currentTable
 			self.contentStack.append([])
+		elif stackSize == 2 and subFile is not None:
+			subReader = XMLReader(subFile, self.ttFont, self.progress, contentOnly=True)
+			subReader.read()
+			self.contentStack.append([])
+			self.root = subReader.root
 		elif stackSize == 2:
 			self.contentStack.append([])
 			self.root = (name, attrs, self.contentStack[-1])
@@ -100,33 +140,33 @@
 			l = []
 			self.contentStack[-1].append((name, attrs, l))
 			self.contentStack.append(l)
-	
+
 	def _characterDataHandler(self, data):
 		if self.stackSize > 1:
 			self.contentStack[-1].append(data)
-	
+
 	def _endElementHandler(self, name):
 		self.stackSize = self.stackSize - 1
 		del self.contentStack[-1]
-		if self.stackSize == 1:
-			self.root = None
-		elif self.stackSize == 2:
-			name, attrs, content = self.root
-			self.currentTable.fromXML(name, attrs, content, self.ttFont)
-			self.root = None
+		if not self.contentOnly:
+			if self.stackSize == 1:
+				self.root = None
+			elif self.stackSize == 2:
+				name, attrs, content = self.root
+				self.currentTable.fromXML(name, attrs, content, self.ttFont)
+				self.root = None
 
 
 class ProgressPrinter(object):
-	
+
 	def __init__(self, title, maxval=100):
 		print(title)
-	
+
 	def set(self, val, maxval=None):
 		pass
-	
+
 	def increment(self, val=1):
 		pass
-	
+
 	def setLabel(self, text):
 		print(text)
-
diff --git a/Lib/fontTools/misc/xmlWriter.py b/Lib/fontTools/misc/xmlWriter.py
index b067c2d..3f30ab5 100644
--- a/Lib/fontTools/misc/xmlWriter.py
+++ b/Lib/fontTools/misc/xmlWriter.py
@@ -3,35 +3,57 @@
 from __future__ import print_function, division, absolute_import
 from fontTools.misc.py23 import *
 import sys
+import os
 import string
 
 INDENT = "  "
 
 
 class XMLWriter(object):
-	
-	def __init__(self, fileOrPath, indentwhite=INDENT, idlefunc=None):
+
+	def __init__(self, fileOrPath, indentwhite=INDENT, idlefunc=None, encoding="utf_8",
+			newlinestr=None):
+		if encoding.lower().replace('-','').replace('_','') != 'utf8':
+			raise Exception('Only UTF-8 encoding is supported.')
+		if fileOrPath == '-':
+			fileOrPath = sys.stdout
 		if not hasattr(fileOrPath, "write"):
-			try:
-				# Python3 has encoding support.
-				self.file = open(fileOrPath, "w", encoding="utf-8")
-			except TypeError:
-				self.file = open(fileOrPath, "w")
+			self.filename = fileOrPath
+			self.file = open(fileOrPath, "wb")
+			self._closeStream = True
 		else:
+			self.filename = None
 			# assume writable file object
 			self.file = fileOrPath
-		self.indentwhite = indentwhite
+			self._closeStream = False
+
+		# Figure out if writer expects bytes or unicodes
+		try:
+			# The bytes check should be first.  See:
+			# https://github.com/behdad/fonttools/pull/233
+			self.file.write(b'')
+			self.totype = tobytes
+		except TypeError:
+			# This better not fail.
+			self.file.write(tounicode(''))
+			self.totype = tounicode
+		self.indentwhite = self.totype(indentwhite)
+		if newlinestr is None:
+			self.newlinestr = self.totype(os.linesep)
+		else:
+			self.newlinestr = self.totype(newlinestr)
 		self.indentlevel = 0
 		self.stack = []
 		self.needindent = 1
 		self.idlefunc = idlefunc
 		self.idlecounter = 0
-		self._writeraw('<?xml version="1.0" encoding="utf-8"?>')
+		self._writeraw('<?xml version="1.0" encoding="UTF-8"?>')
 		self.newline()
-	
+
 	def close(self):
-		self.file.close()
-	
+		if self._closeStream:
+			self.file.close()
+
 	def write(self, string, indent=True):
 		"""Writes text."""
 		self._writeraw(escape(string), indent=indent)
@@ -47,31 +69,28 @@
 		'latin-1'."""
 		self._writeraw(escape8bit(data), strip=strip)
 
-	def write16bit(self, data, strip=False):
-		self._writeraw(escape16bit(data), strip=strip)
-	
 	def write_noindent(self, string):
 		"""Writes text without indentation."""
 		self._writeraw(escape(string), indent=False)
-	
+
 	def _writeraw(self, data, indent=True, strip=False):
 		"""Writes bytes, possibly indented."""
 		if indent and self.needindent:
 			self.file.write(self.indentlevel * self.indentwhite)
 			self.needindent = 0
-		s = tostr(data, encoding="utf-8")
+		s = self.totype(data, encoding="utf_8")
 		if (strip):
 			s = s.strip()
 		self.file.write(s)
-	
+
 	def newline(self):
-		self.file.write("\n")
+		self.file.write(self.newlinestr)
 		self.needindent = 1
 		idlecounter = self.idlecounter
 		if not idlecounter % 100 and self.idlefunc is not None:
 			self.idlefunc()
 		self.idlecounter = idlecounter + 1
-	
+
 	def comment(self, data):
 		data = escape(data)
 		lines = data.split("\n")
@@ -80,26 +99,26 @@
 			self.newline()
 			self._writeraw("     " + line)
 		self._writeraw(" -->")
-	
+
 	def simpletag(self, _TAG_, *args, **kwargs):
 		attrdata = self.stringifyattrs(*args, **kwargs)
 		data = "<%s%s/>" % (_TAG_, attrdata)
 		self._writeraw(data)
-	
+
 	def begintag(self, _TAG_, *args, **kwargs):
 		attrdata = self.stringifyattrs(*args, **kwargs)
 		data = "<%s%s>" % (_TAG_, attrdata)
 		self._writeraw(data)
 		self.stack.append(_TAG_)
 		self.indent()
-	
+
 	def endtag(self, _TAG_):
 		assert self.stack and self.stack[-1] == _TAG_, "nonmatching endtag"
 		del self.stack[-1]
 		self.dedent()
 		data = "</%s>" % _TAG_
 		self._writeraw(data)
-	
+
 	def dumphex(self, data):
 		linelength = 16
 		hexlinelength = linelength * 2
@@ -113,14 +132,14 @@
 				white = " "
 			self._writeraw(line)
 			self.newline()
-	
+
 	def indent(self):
 		self.indentlevel = self.indentlevel + 1
-	
+
 	def dedent(self):
 		assert self.indentlevel > 0
 		self.indentlevel = self.indentlevel - 1
-	
+
 	def stringifyattrs(self, *args, **kwargs):
 		if kwargs:
 			assert not args
@@ -132,15 +151,18 @@
 			return ""
 		data = ""
 		for attr, value in attributes:
-			data = data + ' %s="%s"' % (attr, escapeattr(str(value)))
+			if not isinstance(value, (bytes, unicode)):
+				value = str(value)
+			data = data + ' %s="%s"' % (attr, escapeattr(value))
 		return data
-	
+
 
 def escape(data):
-	data = tostr(data, 'utf-8')
+	data = tostr(data, 'utf_8')
 	data = data.replace("&", "&amp;")
 	data = data.replace("<", "&lt;")
 	data = data.replace(">", "&gt;")
+	data = data.replace("\r", "&#13;")
 	return data
 
 def escapeattr(data):
@@ -158,24 +180,6 @@
 			return "&#" + repr(n) + ";"
 	return strjoin(map(escapechar, data.decode('latin-1')))
 
-def escape16bit(data):
-	import array
-	a = array.array("H")
-	a.fromstring(data)
-	if sys.byteorder != "big":
-		a.byteswap()
-	def escapenum(n, amp=byteord("&"), lt=byteord("<")):
-		if n == amp:
-			return "&amp;"
-		elif n == lt:
-			return "&lt;"
-		elif 32 <= n <= 127:
-			return chr(n)
-		else:
-			return "&#" + repr(n) + ";"
-	return strjoin(map(escapenum, a))
-
-
 def hexStr(s):
 	h = string.hexdigits
 	r = ''
diff --git a/Lib/fontTools/mtiLib/__init__.py b/Lib/fontTools/mtiLib/__init__.py
new file mode 100644
index 0000000..d4f4880
--- /dev/null
+++ b/Lib/fontTools/mtiLib/__init__.py
@@ -0,0 +1,1196 @@
+#!/usr/bin/python
+
+# FontDame-to-FontTools for OpenType Layout tables
+#
+# Source language spec is available at:
+# http://monotype.github.io/OpenType_Table_Source/otl_source.html
+# https://github.com/Monotype/OpenType_Table_Source/
+
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+from fontTools.misc.py23 import *
+from fontTools import ttLib
+from fontTools.ttLib.tables._c_m_a_p import cmap_classes
+from fontTools.ttLib.tables import otTables as ot
+from fontTools.ttLib.tables.otBase import ValueRecord, valueRecordFormatDict
+from fontTools.otlLib import builder as otl
+from contextlib import contextmanager
+from operator import setitem
+import logging
+
+class MtiLibError(Exception): pass
+class ReferenceNotFoundError(MtiLibError): pass
+class FeatureNotFoundError(ReferenceNotFoundError): pass
+class LookupNotFoundError(ReferenceNotFoundError): pass
+
+
+log = logging.getLogger("fontTools.mtiLib")
+
+
+def makeGlyph(s):
+	if s[:2] in ['U ', 'u ']:
+		return ttLib.TTFont._makeGlyphName(int(s[2:], 16))
+	elif s[:2] == '# ':
+		return "glyph%.5d" % int(s[2:])
+	assert s.find(' ') < 0, "Space found in glyph name: %s" % s
+	assert s, "Glyph name is empty"
+	return s
+
+def makeGlyphs(l):
+	return [makeGlyph(g) for g in l]
+
+def mapLookup(sym, mapping):
+	# Lookups are addressed by name.  So resolved them using a map if available.
+	# Fallback to parsing as lookup index if a map isn't provided.
+	if mapping is not None:
+		try:
+			idx = mapping[sym]
+		except KeyError:
+			raise LookupNotFoundError(sym)
+	else:
+		idx = int(sym)
+	return idx
+
+def mapFeature(sym, mapping):
+	# Features are referenced by index according the spec.  So, if symbol is an
+	# integer, use it directly.  Otherwise look up in the map if provided.
+	try:
+		idx = int(sym)
+	except ValueError:
+		try:
+			idx = mapping[sym]
+		except KeyError:
+			raise FeatureNotFoundError(sym)
+	return idx
+
+def setReference(mapper, mapping, sym, setter, collection, key):
+	try:
+		mapped = mapper(sym, mapping)
+	except ReferenceNotFoundError as e:
+		try:
+			if mapping is not None:
+				mapping.addDeferredMapping(lambda ref: setter(collection, key, ref), sym, e)
+				return
+		except AttributeError:
+			pass
+		raise
+	setter(collection, key, mapped)
+
+class DeferredMapping(dict):
+
+	def __init__(self):
+		self._deferredMappings = []
+
+	def addDeferredMapping(self, setter, sym, e):
+		log.debug("Adding deferred mapping for symbol '%s' %s", sym, type(e).__name__)
+		self._deferredMappings.append((setter,sym, e))
+
+	def applyDeferredMappings(self):
+		for setter,sym,e in self._deferredMappings:
+			log.debug("Applying deferred mapping for symbol '%s' %s", sym, type(e).__name__)
+			try:
+				mapped = self[sym]
+			except KeyError:
+				raise e
+			setter(mapped)
+			log.debug("Set to %s", mapped)
+		self._deferredMappings = []
+
+
+def parseScriptList(lines, featureMap=None):
+	self = ot.ScriptList()
+	records = []
+	with lines.between('script table'):
+		for line in lines:
+			while len(line) < 4:
+				line.append('')
+			scriptTag, langSysTag, defaultFeature, features = line
+			log.debug("Adding script %s language-system %s", scriptTag, langSysTag)
+
+			langSys = ot.LangSys()
+			langSys.LookupOrder = None
+			if defaultFeature:
+				setReference(mapFeature, featureMap, defaultFeature, setattr, langSys, 'ReqFeatureIndex')
+			else:
+				langSys.ReqFeatureIndex = 0xFFFF
+			syms = stripSplitComma(features)
+			langSys.FeatureIndex = theList = [3] * len(syms)
+			for i,sym in enumerate(syms):
+				setReference(mapFeature, featureMap, sym, setitem, theList, i)
+			langSys.FeatureCount = len(langSys.FeatureIndex)
+
+			script = [s for s in records if s.ScriptTag == scriptTag]
+			if script:
+				script = script[0].Script
+			else:
+				scriptRec = ot.ScriptRecord()
+				scriptRec.ScriptTag = scriptTag
+				scriptRec.Script = ot.Script()
+				records.append(scriptRec)
+				script = scriptRec.Script
+				script.DefaultLangSys = None
+				script.LangSysRecord = []
+				script.LangSysCount = 0
+
+			if langSysTag == 'default':
+				script.DefaultLangSys = langSys
+			else:
+				langSysRec = ot.LangSysRecord()
+				langSysRec.LangSysTag = langSysTag + ' '*(4 - len(langSysTag))
+				langSysRec.LangSys = langSys
+				script.LangSysRecord.append(langSysRec)
+				script.LangSysCount = len(script.LangSysRecord)
+
+	for script in records:
+		script.Script.LangSysRecord = sorted(script.Script.LangSysRecord, key=lambda rec: rec.LangSysTag)
+	self.ScriptRecord = sorted(records, key=lambda rec: rec.ScriptTag)
+	self.ScriptCount = len(self.ScriptRecord)
+	return self
+
+def parseFeatureList(lines, lookupMap=None, featureMap=None):
+	self = ot.FeatureList()
+	self.FeatureRecord = []
+	with lines.between('feature table'):
+		for line in lines:
+			name, featureTag, lookups = line
+			if featureMap is not None:
+				assert name not in featureMap, "Duplicate feature name: %s" % name
+				featureMap[name] = len(self.FeatureRecord)
+			# If feature name is integer, make sure it matches its index.
+			try:
+				assert int(name) == len(self.FeatureRecord), "%d %d" % (name, len(self.FeatureRecord))
+			except ValueError:
+				pass
+			featureRec = ot.FeatureRecord()
+			featureRec.FeatureTag = featureTag
+			featureRec.Feature = ot.Feature()
+			self.FeatureRecord.append(featureRec)
+			feature = featureRec.Feature
+			feature.FeatureParams = None
+			syms = stripSplitComma(lookups)
+			feature.LookupListIndex = theList = [None] * len(syms)
+			for i,sym in enumerate(syms):
+				setReference(mapLookup, lookupMap, sym, setitem, theList, i)
+			feature.LookupCount = len(feature.LookupListIndex)
+
+	self.FeatureCount = len(self.FeatureRecord)
+	return self
+
+def parseLookupFlags(lines):
+	flags = 0
+	filterset = None
+	allFlags = [
+		'righttoleft',
+		'ignorebaseglyphs',
+		'ignoreligatures',
+		'ignoremarks',
+		'markattachmenttype',
+		'markfiltertype',
+	]
+	while lines.peeks()[0].lower() in allFlags:
+		line = next(lines)
+		flag = {
+			'righttoleft':		0x0001,
+			'ignorebaseglyphs':	0x0002,
+			'ignoreligatures':	0x0004,
+			'ignoremarks':		0x0008,
+			}.get(line[0].lower())
+		if flag:
+			assert line[1].lower() in ['yes', 'no'], line[1]
+			if line[1].lower() == 'yes':
+				flags |= flag
+			continue
+		if line[0].lower() == 'markattachmenttype':
+			flags |= int(line[1]) << 8
+			continue
+		if line[0].lower() == 'markfiltertype':
+			flags |= 0x10
+			filterset = int(line[1])
+	return flags, filterset
+
+def parseSingleSubst(lines, font, _lookupMap=None):
+	mapping = {}
+	for line in lines:
+		assert len(line) == 2, line
+		line = makeGlyphs(line)
+		mapping[line[0]] = line[1]
+	return otl.buildSingleSubstSubtable(mapping)
+
+def parseMultiple(lines, font, _lookupMap=None):
+	mapping = {}
+	for line in lines:
+		line = makeGlyphs(line)
+		mapping[line[0]] = line[1:]
+	return otl.buildMultipleSubstSubtable(mapping)
+
+def parseAlternate(lines, font, _lookupMap=None):
+	mapping = {}
+	for line in lines:
+		line = makeGlyphs(line)
+		mapping[line[0]] = line[1:]
+	return otl.buildAlternateSubstSubtable(mapping)
+
+def parseLigature(lines, font, _lookupMap=None):
+	mapping = {}
+	for line in lines:
+		assert len(line) >= 2, line
+		line = makeGlyphs(line)
+		mapping[tuple(line[1:])] = line[0]
+	return otl.buildLigatureSubstSubtable(mapping)
+
+def parseSinglePos(lines, font, _lookupMap=None):
+	values = {}
+	for line in lines:
+		assert len(line) == 3, line
+		w = line[0].title().replace(' ', '')
+		assert w in valueRecordFormatDict
+		g = makeGlyph(line[1])
+		v = int(line[2])
+		if g not in values:
+			values[g] = ValueRecord()
+		assert not hasattr(values[g], w), (g, w)
+		setattr(values[g], w, v)
+	return otl.buildSinglePosSubtable(values, font.getReverseGlyphMap())
+
+def parsePair(lines, font, _lookupMap=None):
+	self = ot.PairPos()
+	self.ValueFormat1 = self.ValueFormat2 = 0
+	typ = lines.peeks()[0].split()[0].lower()
+	if typ in ('left', 'right'):
+		self.Format = 1
+		values = {}
+		for line in lines:
+			assert len(line) == 4, line
+			side = line[0].split()[0].lower()
+			assert side in ('left', 'right'), side
+			what = line[0][len(side):].title().replace(' ', '')
+			mask = valueRecordFormatDict[what][0]
+			glyph1, glyph2 = makeGlyphs(line[1:3])
+			value = int(line[3])
+			if not glyph1 in values: values[glyph1] = {}
+			if not glyph2 in values[glyph1]: values[glyph1][glyph2] = (ValueRecord(),ValueRecord())
+			rec2 = values[glyph1][glyph2]
+			if side == 'left':
+				self.ValueFormat1 |= mask
+				vr = rec2[0]
+			else:
+				self.ValueFormat2 |= mask
+				vr = rec2[1]
+			assert not hasattr(vr, what), (vr, what)
+			setattr(vr, what, value)
+		self.Coverage = makeCoverage(set(values.keys()), font)
+		self.PairSet = []
+		for glyph1 in self.Coverage.glyphs:
+			values1 = values[glyph1]
+			pairset = ot.PairSet()
+			records = pairset.PairValueRecord = []
+			for glyph2 in sorted(values1.keys(), key=font.getGlyphID):
+				values2 = values1[glyph2]
+				pair = ot.PairValueRecord()
+				pair.SecondGlyph = glyph2
+				pair.Value1 = values2[0]
+				pair.Value2 = values2[1] if self.ValueFormat2 else None
+				records.append(pair)
+			pairset.PairValueCount = len(pairset.PairValueRecord)
+			self.PairSet.append(pairset)
+		self.PairSetCount = len(self.PairSet)
+	elif typ.endswith('class'):
+		self.Format = 2
+		classDefs = [None, None]
+		while lines.peeks()[0].endswith("class definition begin"):
+			typ = lines.peek()[0][:-len("class definition begin")].lower()
+			idx,klass = {
+				'first':	(0,ot.ClassDef1),
+				'second':	(1,ot.ClassDef2),
+			}[typ]
+			assert classDefs[idx] is None
+			classDefs[idx] = parseClassDef(lines, font, klass=klass)
+		self.ClassDef1, self.ClassDef2 = classDefs
+		self.Class1Count, self.Class2Count = (1+max(c.classDefs.values()) for c in classDefs)
+		self.Class1Record = [ot.Class1Record() for i in range(self.Class1Count)]
+		for rec1 in self.Class1Record:
+			rec1.Class2Record = [ot.Class2Record() for j in range(self.Class2Count)]
+			for rec2 in rec1.Class2Record:
+				rec2.Value1 = ValueRecord()
+				rec2.Value2 = ValueRecord()
+		for line in lines:
+			assert len(line) == 4, line
+			side = line[0].split()[0].lower()
+			assert side in ('left', 'right'), side
+			what = line[0][len(side):].title().replace(' ', '')
+			mask = valueRecordFormatDict[what][0]
+			class1, class2, value = (int(x) for x in line[1:4])
+			rec2 = self.Class1Record[class1].Class2Record[class2]
+			if side == 'left':
+				self.ValueFormat1 |= mask
+				vr = rec2.Value1
+			else:
+				self.ValueFormat2 |= mask
+				vr = rec2.Value2
+			assert not hasattr(vr, what), (vr, what)
+			setattr(vr, what, value)
+		for rec1 in self.Class1Record:
+			for rec2 in rec1.Class2Record:
+				rec2.Value1 = ValueRecord(self.ValueFormat1, rec2.Value1)
+				rec2.Value2 = ValueRecord(self.ValueFormat2, rec2.Value2) \
+						if self.ValueFormat2 else None
+
+		self.Coverage = makeCoverage(set(self.ClassDef1.classDefs.keys()), font)
+	else:
+		assert 0, typ
+	return self
+
+def parseKernset(lines, font, _lookupMap=None):
+	typ = lines.peeks()[0].split()[0].lower()
+	if typ in ('left', 'right'):
+		with lines.until(("firstclass definition begin", "secondclass definition begin")):
+			return parsePair(lines, font)
+	return parsePair(lines, font)
+
+def makeAnchor(data, klass=ot.Anchor):
+	assert len(data) <= 2
+	anchor = klass()
+	anchor.Format = 1
+	anchor.XCoordinate,anchor.YCoordinate = intSplitComma(data[0])
+	if len(data) > 1 and data[1] != '':
+		anchor.Format = 2
+		anchor.AnchorPoint = int(data[1])
+	return anchor
+
+def parseCursive(lines, font, _lookupMap=None):
+	records = {}
+	for line in lines:
+		assert len(line) in [3,4], line
+		idx,klass = {
+			'entry':	(0,ot.EntryAnchor),
+			'exit':		(1,ot.ExitAnchor),
+		}[line[0]]
+		glyph = makeGlyph(line[1])
+		if glyph not in records:
+			records[glyph] = [None,None]
+		assert records[glyph][idx] is None, (glyph, idx)
+		records[glyph][idx] = makeAnchor(line[2:], klass)
+	return otl.buildCursivePosSubtable(records, font.getReverseGlyphMap())
+
+def makeMarkRecords(data, coverage, c):
+	records = []
+	for glyph in coverage.glyphs:
+		klass, anchor = data[glyph]
+		record = c.MarkRecordClass()
+		record.Class = klass
+		setattr(record, c.MarkAnchor, anchor)
+		records.append(record)
+	return records
+
+def makeBaseRecords(data, coverage, c, classCount):
+	records = []
+	idx = {}
+	for glyph in coverage.glyphs:
+		idx[glyph] = len(records)
+		record = c.BaseRecordClass()
+		anchors = [None] * classCount
+		setattr(record, c.BaseAnchor, anchors)
+		records.append(record)
+	for (glyph,klass),anchor in data.items():
+		record = records[idx[glyph]]
+		anchors = getattr(record, c.BaseAnchor)
+		assert anchors[klass] is None, (glyph, klass)
+		anchors[klass] = anchor
+	return records
+
+def makeLigatureRecords(data, coverage, c, classCount):
+	records = [None] * len(coverage.glyphs)
+	idx = {g:i for i,g in enumerate(coverage.glyphs)}
+
+	for (glyph,klass,compIdx,compCount),anchor in data.items():
+		record = records[idx[glyph]]
+		if record is None:
+			record = records[idx[glyph]] = ot.LigatureAttach()
+			record.ComponentCount = compCount
+			record.ComponentRecord = [ot.ComponentRecord() for i in range(compCount)]
+			for compRec in record.ComponentRecord:
+				compRec.LigatureAnchor = [None] * classCount
+		assert record.ComponentCount == compCount, (glyph, record.ComponentCount, compCount)
+
+		anchors = record.ComponentRecord[compIdx - 1].LigatureAnchor
+		assert anchors[klass] is None, (glyph, compIdx, klass)
+		anchors[klass] = anchor
+	return records
+
+def parseMarkToSomething(lines, font, c):
+	self = c.Type()
+	self.Format = 1
+	markData = {}
+	baseData = {}
+	Data = {
+		'mark':		(markData, c.MarkAnchorClass),
+		'base':		(baseData, c.BaseAnchorClass),
+		'ligature':	(baseData, c.BaseAnchorClass),
+	}
+	maxKlass = 0
+	for line in lines:
+		typ = line[0]
+		assert typ in ('mark', 'base', 'ligature')
+		glyph = makeGlyph(line[1])
+		data, anchorClass = Data[typ]
+		extraItems = 2 if typ == 'ligature' else 0
+		extras = tuple(int(i) for i in line[2:2+extraItems])
+		klass = int(line[2+extraItems])
+		anchor = makeAnchor(line[3+extraItems:], anchorClass)
+		if typ == 'mark':
+			key,value = glyph,(klass,anchor)
+		else:
+			key,value = ((glyph,klass)+extras),anchor
+		assert key not in data, key
+		data[key] = value
+		maxKlass = max(maxKlass, klass)
+
+	# Mark
+	markCoverage = makeCoverage(set(markData.keys()), font, c.MarkCoverageClass)
+	markArray = c.MarkArrayClass()
+	markRecords = makeMarkRecords(markData, markCoverage, c)
+	setattr(markArray, c.MarkRecord, markRecords)
+	setattr(markArray, c.MarkCount, len(markRecords))
+	setattr(self, c.MarkCoverage, markCoverage)
+	setattr(self, c.MarkArray, markArray)
+	self.ClassCount = maxKlass + 1
+
+	# Base
+	self.classCount = 0 if not baseData else 1+max(k[1] for k,v in baseData.items())
+	baseCoverage = makeCoverage(set([k[0] for k in baseData.keys()]), font, c.BaseCoverageClass)
+	baseArray = c.BaseArrayClass()
+	if c.Base == 'Ligature':
+		baseRecords = makeLigatureRecords(baseData, baseCoverage, c, self.classCount)
+	else:
+		baseRecords = makeBaseRecords(baseData, baseCoverage, c, self.classCount)
+	setattr(baseArray, c.BaseRecord, baseRecords)
+	setattr(baseArray, c.BaseCount, len(baseRecords))
+	setattr(self, c.BaseCoverage, baseCoverage)
+	setattr(self, c.BaseArray, baseArray)
+
+	return self
+
+class MarkHelper(object):
+	def __init__(self):
+		for Which in ('Mark', 'Base'):
+			for What in ('Coverage', 'Array', 'Count', 'Record', 'Anchor'):
+				key = Which + What
+				if Which == 'Mark' and What in ('Count', 'Record', 'Anchor'):
+					value = key
+				else:
+					value = getattr(self, Which) + What
+				if value == 'LigatureRecord':
+					value = 'LigatureAttach'
+				setattr(self, key, value)
+				if What != 'Count':
+					klass = getattr(ot, value)
+					setattr(self, key+'Class', klass)
+
+class MarkToBaseHelper(MarkHelper):
+	Mark = 'Mark'
+	Base = 'Base'
+	Type = ot.MarkBasePos
+class MarkToMarkHelper(MarkHelper):
+	Mark = 'Mark1'
+	Base = 'Mark2'
+	Type = ot.MarkMarkPos
+class MarkToLigatureHelper(MarkHelper):
+	Mark = 'Mark'
+	Base = 'Ligature'
+	Type = ot.MarkLigPos
+
+def parseMarkToBase(lines, font, _lookupMap=None):
+	return parseMarkToSomething(lines, font, MarkToBaseHelper())
+def parseMarkToMark(lines, font, _lookupMap=None):
+	return parseMarkToSomething(lines, font, MarkToMarkHelper())
+def parseMarkToLigature(lines, font, _lookupMap=None):
+	return parseMarkToSomething(lines, font, MarkToLigatureHelper())
+
+def stripSplitComma(line):
+	return [s.strip() for s in line.split(',')] if line else []
+
+def intSplitComma(line):
+	return [int(i) for i in line.split(',')] if line else []
+
+# Copied from fontTools.subset
+class ContextHelper(object):
+	def __init__(self, klassName, Format):
+		if klassName.endswith('Subst'):
+			Typ = 'Sub'
+			Type = 'Subst'
+		else:
+			Typ = 'Pos'
+			Type = 'Pos'
+		if klassName.startswith('Chain'):
+			Chain = 'Chain'
+			InputIdx = 1
+			DataLen = 3
+		else:
+			Chain = ''
+			InputIdx = 0
+			DataLen = 1
+		ChainTyp = Chain+Typ
+
+		self.Typ = Typ
+		self.Type = Type
+		self.Chain = Chain
+		self.ChainTyp = ChainTyp
+		self.InputIdx = InputIdx
+		self.DataLen = DataLen
+
+		self.LookupRecord = Type+'LookupRecord'
+
+		if Format == 1:
+			Coverage = lambda r: r.Coverage
+			ChainCoverage = lambda r: r.Coverage
+			ContextData = lambda r:(None,)
+			ChainContextData = lambda r:(None, None, None)
+			SetContextData = None
+			SetChainContextData = None
+			RuleData = lambda r:(r.Input,)
+			ChainRuleData = lambda r:(r.Backtrack, r.Input, r.LookAhead)
+			def SetRuleData(r, d):
+				(r.Input,) = d
+				(r.GlyphCount,) = (len(x)+1 for x in d)
+			def ChainSetRuleData(r, d):
+				(r.Backtrack, r.Input, r.LookAhead) = d
+				(r.BacktrackGlyphCount,r.InputGlyphCount,r.LookAheadGlyphCount,) = (len(d[0]),len(d[1])+1,len(d[2]))
+		elif Format == 2:
+			Coverage = lambda r: r.Coverage
+			ChainCoverage = lambda r: r.Coverage
+			ContextData = lambda r:(r.ClassDef,)
+			ChainContextData = lambda r:(r.BacktrackClassDef,
+						     r.InputClassDef,
+						     r.LookAheadClassDef)
+			def SetContextData(r, d):
+				(r.ClassDef,) = d
+			def SetChainContextData(r, d):
+				(r.BacktrackClassDef,
+				 r.InputClassDef,
+				 r.LookAheadClassDef) = d
+			RuleData = lambda r:(r.Class,)
+			ChainRuleData = lambda r:(r.Backtrack, r.Input, r.LookAhead)
+			def SetRuleData(r, d):
+				(r.Class,) = d
+				(r.GlyphCount,) = (len(x)+1 for x in d)
+			def ChainSetRuleData(r, d):
+				(r.Backtrack, r.Input, r.LookAhead) = d
+				(r.BacktrackGlyphCount,r.InputGlyphCount,r.LookAheadGlyphCount,) = (len(d[0]),len(d[1])+1,len(d[2]))
+		elif Format == 3:
+			Coverage = lambda r: r.Coverage[0]
+			ChainCoverage = lambda r: r.InputCoverage[0]
+			ContextData = None
+			ChainContextData = None
+			SetContextData = None
+			SetChainContextData = None
+			RuleData = lambda r: r.Coverage
+			ChainRuleData = lambda r:(r.BacktrackCoverage +
+						  r.InputCoverage +
+						  r.LookAheadCoverage)
+			def SetRuleData(r, d):
+				(r.Coverage,) = d
+				(r.GlyphCount,) = (len(x) for x in d)
+			def ChainSetRuleData(r, d):
+				(r.BacktrackCoverage, r.InputCoverage, r.LookAheadCoverage) = d
+				(r.BacktrackGlyphCount,r.InputGlyphCount,r.LookAheadGlyphCount,) = (len(x) for x in d)
+		else:
+			assert 0, "unknown format: %s" % Format
+
+		if Chain:
+			self.Coverage = ChainCoverage
+			self.ContextData = ChainContextData
+			self.SetContextData = SetChainContextData
+			self.RuleData = ChainRuleData
+			self.SetRuleData = ChainSetRuleData
+		else:
+			self.Coverage = Coverage
+			self.ContextData = ContextData
+			self.SetContextData = SetContextData
+			self.RuleData = RuleData
+			self.SetRuleData = SetRuleData
+
+		if Format == 1:
+			self.Rule = ChainTyp+'Rule'
+			self.RuleCount = ChainTyp+'RuleCount'
+			self.RuleSet = ChainTyp+'RuleSet'
+			self.RuleSetCount = ChainTyp+'RuleSetCount'
+			self.Intersect = lambda glyphs, c, r: [r] if r in glyphs else []
+		elif Format == 2:
+			self.Rule = ChainTyp+'ClassRule'
+			self.RuleCount = ChainTyp+'ClassRuleCount'
+			self.RuleSet = ChainTyp+'ClassSet'
+			self.RuleSetCount = ChainTyp+'ClassSetCount'
+			self.Intersect = lambda glyphs, c, r: (c.intersect_class(glyphs, r) if c
+							       else (set(glyphs) if r == 0 else set()))
+
+			self.ClassDef = 'InputClassDef' if Chain else 'ClassDef'
+			self.ClassDefIndex = 1 if Chain else 0
+			self.Input = 'Input' if Chain else 'Class'
+
+def parseLookupRecords(items, klassName, lookupMap=None):
+	klass = getattr(ot, klassName)
+	lst = []
+	for item in items:
+		rec = klass()
+		item = stripSplitComma(item)
+		assert len(item) == 2, item
+		idx = int(item[0])
+		assert idx > 0, idx
+		rec.SequenceIndex = idx - 1
+		setReference(mapLookup, lookupMap, item[1], setattr, rec, 'LookupListIndex')
+		lst.append(rec)
+	return lst
+
+def makeClassDef(classDefs, font, klass=ot.Coverage):
+	if not classDefs: return None
+	self = klass()
+	self.classDefs = dict(classDefs)
+	return self
+
+def parseClassDef(lines, font, klass=ot.ClassDef):
+	classDefs = {}
+	with lines.between('class definition'):
+		for line in lines:
+			glyph = makeGlyph(line[0])
+			assert glyph not in classDefs, glyph
+			classDefs[glyph] = int(line[1])
+	return makeClassDef(classDefs, font, klass)
+
+def makeCoverage(glyphs, font, klass=ot.Coverage):
+	if not glyphs: return None
+	if isinstance(glyphs, set):
+		glyphs = sorted(glyphs)
+	coverage = klass()
+	coverage.glyphs = sorted(set(glyphs), key=font.getGlyphID)
+	return coverage
+
+def parseCoverage(lines, font, klass=ot.Coverage):
+	glyphs = []
+	with lines.between('coverage definition'):
+		for line in lines:
+			glyphs.append(makeGlyph(line[0]))
+	return makeCoverage(glyphs, font, klass)
+
+def bucketizeRules(self, c, rules, bucketKeys):
+	buckets = {}
+	for seq,recs in rules:
+		buckets.setdefault(seq[c.InputIdx][0], []).append((tuple(s[1 if i==c.InputIdx else 0:] for i,s in enumerate(seq)), recs))
+
+	rulesets = []
+	for firstGlyph in bucketKeys:
+		if firstGlyph not in buckets:
+			rulesets.append(None)
+			continue
+		thisRules = []
+		for seq,recs in buckets[firstGlyph]:
+			rule = getattr(ot, c.Rule)()
+			c.SetRuleData(rule, seq)
+			setattr(rule, c.Type+'Count', len(recs))
+			setattr(rule, c.LookupRecord, recs)
+			thisRules.append(rule)
+
+		ruleset = getattr(ot, c.RuleSet)()
+		setattr(ruleset, c.Rule, thisRules)
+		setattr(ruleset, c.RuleCount, len(thisRules))
+		rulesets.append(ruleset)
+
+	setattr(self, c.RuleSet, rulesets)
+	setattr(self, c.RuleSetCount, len(rulesets))
+
+def parseContext(lines, font, Type, lookupMap=None):
+	self = getattr(ot, Type)()
+	typ = lines.peeks()[0].split()[0].lower()
+	if typ == 'glyph':
+		self.Format = 1
+		log.debug("Parsing %s format %s", Type, self.Format)
+		c = ContextHelper(Type, self.Format)
+		rules = []
+		for line in lines:
+			assert line[0].lower() == 'glyph', line[0]
+			while len(line) < 1+c.DataLen: line.append('')
+			seq = tuple(makeGlyphs(stripSplitComma(i)) for i in line[1:1+c.DataLen])
+			recs = parseLookupRecords(line[1+c.DataLen:], c.LookupRecord, lookupMap)
+			rules.append((seq, recs))
+
+		firstGlyphs = set(seq[c.InputIdx][0] for seq,recs in rules)
+		self.Coverage = makeCoverage(firstGlyphs, font)
+		bucketizeRules(self, c, rules, self.Coverage.glyphs)
+	elif typ.endswith('class'):
+		self.Format = 2
+		log.debug("Parsing %s format %s", Type, self.Format)
+		c = ContextHelper(Type, self.Format)
+		classDefs = [None] * c.DataLen
+		while lines.peeks()[0].endswith("class definition begin"):
+			typ = lines.peek()[0][:-len("class definition begin")].lower()
+			idx,klass = {
+			1: {
+				'':		(0,ot.ClassDef),
+			},
+			3: {
+				'backtrack':	(0,ot.BacktrackClassDef),
+				'':		(1,ot.InputClassDef),
+				'lookahead':	(2,ot.LookAheadClassDef),
+			},
+			}[c.DataLen][typ]
+			assert classDefs[idx] is None, idx
+			classDefs[idx] = parseClassDef(lines, font, klass=klass)
+		c.SetContextData(self, classDefs)
+		rules = []
+		for line in lines:
+			assert line[0].lower().startswith('class'), line[0]
+			while len(line) < 1+c.DataLen: line.append('')
+			seq = tuple(intSplitComma(i) for i in line[1:1+c.DataLen])
+			recs = parseLookupRecords(line[1+c.DataLen:], c.LookupRecord, lookupMap)
+			rules.append((seq, recs))
+		firstClasses = set(seq[c.InputIdx][0] for seq,recs in rules)
+		firstGlyphs = set(g for g,c in classDefs[c.InputIdx].classDefs.items() if c in firstClasses)
+		self.Coverage = makeCoverage(firstGlyphs, font)
+		bucketizeRules(self, c, rules, range(max(firstClasses) + 1))
+	elif typ.endswith('coverage'):
+		self.Format = 3
+		log.debug("Parsing %s format %s", Type, self.Format)
+		c = ContextHelper(Type, self.Format)
+		coverages = tuple([] for i in range(c.DataLen))
+		while lines.peeks()[0].endswith("coverage definition begin"):
+			typ = lines.peek()[0][:-len("coverage definition begin")].lower()
+			idx,klass = {
+			1: {
+				'':		(0,ot.Coverage),
+			},
+			3: {
+				'backtrack':	(0,ot.BacktrackCoverage),
+				'input':	(1,ot.InputCoverage),
+				'lookahead':	(2,ot.LookAheadCoverage),
+			},
+			}[c.DataLen][typ]
+			coverages[idx].append(parseCoverage(lines, font, klass=klass))
+		c.SetRuleData(self, coverages)
+		lines = list(lines)
+		assert len(lines) == 1
+		line = lines[0]
+		assert line[0].lower() == 'coverage', line[0]
+		recs = parseLookupRecords(line[1:], c.LookupRecord, lookupMap)
+		setattr(self, c.Type+'Count', len(recs))
+		setattr(self, c.LookupRecord, recs)
+	else:
+		assert 0, typ
+	return self
+
+def parseContextSubst(lines, font, lookupMap=None):
+	return parseContext(lines, font, "ContextSubst", lookupMap=lookupMap)
+def parseContextPos(lines, font, lookupMap=None):
+	return parseContext(lines, font, "ContextPos", lookupMap=lookupMap)
+def parseChainedSubst(lines, font, lookupMap=None):
+	return parseContext(lines, font, "ChainContextSubst", lookupMap=lookupMap)
+def parseChainedPos(lines, font, lookupMap=None):
+	return parseContext(lines, font, "ChainContextPos", lookupMap=lookupMap)
+
+def parseReverseChainedSubst(lines, font, _lookupMap=None):
+	self = ot.ReverseChainSingleSubst()
+	self.Format = 1
+	coverages = ([], [])
+	while lines.peeks()[0].endswith("coverage definition begin"):
+		typ = lines.peek()[0][:-len("coverage definition begin")].lower()
+		idx,klass = {
+			'backtrack':	(0,ot.BacktrackCoverage),
+			'lookahead':	(1,ot.LookAheadCoverage),
+		}[typ]
+		coverages[idx].append(parseCoverage(lines, font, klass=klass))
+	self.BacktrackCoverage = coverages[0]
+	self.BacktrackGlyphCount = len(self.BacktrackCoverage)
+	self.LookAheadCoverage = coverages[1]
+	self.LookAheadGlyphCount = len(self.LookAheadCoverage)
+	mapping = {}
+	for line in lines:
+		assert len(line) == 2, line
+		line = makeGlyphs(line)
+		mapping[line[0]] = line[1]
+	self.Coverage = makeCoverage(set(mapping.keys()), font)
+	self.Substitute = [mapping[k] for k in self.Coverage.glyphs]
+	self.GlyphCount = len(self.Substitute)
+	return self
+
+def parseLookup(lines, tableTag, font, lookupMap=None):
+	line = lines.expect('lookup')
+	_, name, typ = line
+	log.debug("Parsing lookup type %s %s", typ, name)
+	lookup = ot.Lookup()
+	lookup.LookupFlag,filterset = parseLookupFlags(lines)
+	if filterset is not None:
+		lookup.MarkFilteringSet = filterset
+	lookup.LookupType, parseLookupSubTable = {
+		'GSUB': {
+			'single':	(1,	parseSingleSubst),
+			'multiple':	(2,	parseMultiple),
+			'alternate':	(3,	parseAlternate),
+			'ligature':	(4,	parseLigature),
+			'context':	(5,	parseContextSubst),
+			'chained':	(6,	parseChainedSubst),
+			'reversechained':(8,	parseReverseChainedSubst),
+		},
+		'GPOS': {
+			'single':	(1,	parseSinglePos),
+			'pair':		(2,	parsePair),
+			'kernset':	(2,	parseKernset),
+			'cursive':	(3,	parseCursive),
+			'mark to base':	(4,	parseMarkToBase),
+			'mark to ligature':(5,	parseMarkToLigature),
+			'mark to mark':	(6,	parseMarkToMark),
+			'context':	(7,	parseContextPos),
+			'chained':	(8,	parseChainedPos),
+		},
+	}[tableTag][typ]
+
+	with lines.until('lookup end'):
+		subtables = []
+
+		while lines.peek():
+			with lines.until(('% subtable', 'subtable end')):
+				while lines.peek():
+					subtable = parseLookupSubTable(lines, font, lookupMap)
+					assert lookup.LookupType == subtable.LookupType
+					subtables.append(subtable)
+			if lines.peeks()[0] in ('% subtable', 'subtable end'):
+				next(lines)
+	lines.expect('lookup end')
+
+	lookup.SubTable = subtables
+	lookup.SubTableCount = len(lookup.SubTable)
+	if lookup.SubTableCount is 0:
+		# Remove this return when following is fixed:
+		# https://github.com/fonttools/fonttools/issues/789
+		return None
+	return lookup
+
+def parseGSUBGPOS(lines, font, tableTag):
+	container = ttLib.getTableClass(tableTag)()
+	lookupMap = DeferredMapping()
+	featureMap = DeferredMapping()
+	assert tableTag in ('GSUB', 'GPOS')
+	log.debug("Parsing %s", tableTag)
+	self = getattr(ot, tableTag)()
+	self.Version = 0x00010000
+	fields = {
+		'script table begin':
+		('ScriptList',
+		 lambda lines: parseScriptList (lines, featureMap)),
+		'feature table begin':
+		('FeatureList',
+		 lambda lines: parseFeatureList (lines, lookupMap, featureMap)),
+		'lookup':
+		('LookupList',
+		 None),
+	}
+	for attr,parser in fields.values():
+		setattr(self, attr, None)
+	while lines.peek() is not None:
+		typ = lines.peek()[0].lower()
+		if typ not in fields:
+			log.debug('Skipping %s', lines.peek())
+			next(lines)
+			continue
+		attr,parser = fields[typ]
+		if typ == 'lookup':
+			if self.LookupList is None:
+				self.LookupList = ot.LookupList()
+				self.LookupList.Lookup = []
+			_, name, _ = lines.peek()
+			lookup = parseLookup(lines, tableTag, font, lookupMap)
+			if lookupMap is not None:
+				assert name not in lookupMap, "Duplicate lookup name: %s" % name
+				lookupMap[name] = len(self.LookupList.Lookup)
+			else:
+				assert int(name) == len(self.LookupList.Lookup), "%d %d" % (name, len(self.Lookup))
+			self.LookupList.Lookup.append(lookup)
+		else:
+			assert getattr(self, attr) is None, attr
+			setattr(self, attr, parser(lines))
+	if self.LookupList:
+		self.LookupList.LookupCount = len(self.LookupList.Lookup)
+	if lookupMap is not None:
+		lookupMap.applyDeferredMappings()
+	if featureMap is not None:
+		featureMap.applyDeferredMappings()
+	container.table = self
+	return container
+
+def parseGSUB(lines, font):
+	return parseGSUBGPOS(lines, font, 'GSUB')
+def parseGPOS(lines, font):
+	return parseGSUBGPOS(lines, font, 'GPOS')
+
+def parseAttachList(lines, font):
+	points = {}
+	with lines.between('attachment list'):
+		for line in lines:
+			glyph = makeGlyph(line[0])
+			assert glyph not in points, glyph
+			points[glyph] = [int(i) for i in line[1:]]
+	return otl.buildAttachList(points, font.getReverseGlyphMap())
+
+def parseCaretList(lines, font):
+	carets = {}
+	with lines.between('carets'):
+		for line in lines:
+			glyph = makeGlyph(line[0])
+			assert glyph not in carets, glyph
+			num = int(line[1])
+			thisCarets = [int(i) for i in line[2:]]
+			assert num == len(thisCarets), line
+			carets[glyph] = thisCarets
+	return otl.buildLigCaretList(carets, {}, font.getReverseGlyphMap())
+
+def makeMarkFilteringSets(sets, font):
+	self = ot.MarkGlyphSetsDef()
+	self.MarkSetTableFormat = 1
+	self.MarkSetCount = 1 + max(sets.keys())
+	self.Coverage = [None] * self.MarkSetCount
+	for k,v in sorted(sets.items()):
+		self.Coverage[k] = makeCoverage(set(v), font)
+	return self
+
+def parseMarkFilteringSets(lines, font):
+	sets = {}
+	with lines.between('set definition'):
+		for line in lines:
+			assert len(line) == 2, line
+			glyph = makeGlyph(line[0])
+			# TODO accept set names
+			st = int(line[1])
+			if st not in sets:
+				sets[st] = []
+			sets[st].append(glyph)
+	return makeMarkFilteringSets(sets, font)
+
+def parseGDEF(lines, font):
+	container = ttLib.getTableClass('GDEF')()
+	log.debug("Parsing GDEF")
+	self = ot.GDEF()
+	fields = {
+		'class definition begin':
+			('GlyphClassDef',
+			 lambda lines, font: parseClassDef(lines, font, klass=ot.GlyphClassDef)),
+		'attachment list begin':
+			('AttachList', parseAttachList),
+		'carets begin':
+			('LigCaretList', parseCaretList),
+		'mark attachment class definition begin':
+			('MarkAttachClassDef',
+			 lambda lines, font: parseClassDef(lines, font, klass=ot.MarkAttachClassDef)),
+		'markfilter set definition begin':
+			('MarkGlyphSetsDef', parseMarkFilteringSets),
+	}
+	for attr,parser in fields.values():
+		setattr(self, attr, None)
+	while lines.peek() is not None:
+		typ = lines.peek()[0].lower()
+		if typ not in fields:
+			log.debug('Skipping %s', typ)
+			next(lines)
+			continue
+		attr,parser = fields[typ]
+		assert getattr(self, attr) is None, attr
+		setattr(self, attr, parser(lines, font))
+	self.Version = 0x00010000 if self.MarkGlyphSetsDef is None else 0x00010002
+	container.table = self
+	return container
+
+def parseCmap(lines, font):
+	container = ttLib.getTableClass('cmap')()
+	log.debug("Parsing cmap")
+	tables = []
+	while lines.peek() is not None:
+		lines.expect('cmap subtable %d' % len(tables))
+		platId, encId, fmt, lang = [
+			parseCmapId(lines, field)
+			for field in ('platformID', 'encodingID', 'format', 'language')]
+		table = cmap_classes[fmt](fmt)
+		table.platformID = platId
+		table.platEncID = encId
+		table.language = lang
+		table.cmap = {}
+		line = next(lines)
+		while line[0] != 'end subtable':
+			table.cmap[int(line[0], 16)] = line[1]
+			line = next(lines)
+		tables.append(table)
+	container.tableVersion = 0
+	container.tables = tables
+	return container
+
+def parseCmapId(lines, field):
+	line = next(lines)
+	assert field == line[0]
+	return int(line[1])
+
+def parseTable(lines, font, tableTag=None):
+	log.debug("Parsing table")
+	line = lines.peeks()
+	tag = None
+	if line[0].split()[0] == 'FontDame':
+		tag = line[0].split()[1]
+	elif ' '.join(line[0].split()[:3]) == 'Font Chef Table':
+		tag = line[0].split()[3]
+	if tag is not None:
+		next(lines)
+		tag = tag.ljust(4)
+		if tableTag is None:
+			tableTag = tag
+		else:
+			assert tableTag == tag, (tableTag, tag)
+
+	assert tableTag is not None, "Don't know what table to parse and data doesn't specify"
+
+	return {
+		'GSUB': parseGSUB,
+		'GPOS': parseGPOS,
+		'GDEF': parseGDEF,
+		'cmap': parseCmap,
+		}[tableTag](lines, font)
+
+class Tokenizer(object):
+
+	def __init__(self, f):
+		# TODO BytesIO / StringIO as needed?  also, figure out whether we work on bytes or unicode
+		lines = iter(f)
+		try:
+			self.filename = f.name
+		except:
+			self.filename = None
+		self.lines = iter(lines)
+		self.line = ''
+		self.lineno = 0
+		self.stoppers = []
+		self.buffer = None
+
+	def __iter__(self):
+		return self
+
+	def _next_line(self):
+		self.lineno += 1
+		line = self.line = next(self.lines)
+		line = [s.strip() for s in line.split('\t')]
+		if len(line) == 1 and not line[0]:
+			del line[0]
+		if line and not line[-1]:
+			log.warning('trailing tab found on line %d: %s' % (self.lineno, self.line))
+			while line and not line[-1]:
+				del line[-1]
+		return line
+
+	def _next_nonempty(self):
+		while True:
+			line = self._next_line()
+			# Skip comments and empty lines
+			if line and line[0] and (line[0][0] != '%' or line[0] == '% subtable'):
+				return line
+
+	def _next_buffered(self):
+		if self.buffer:
+			ret = self.buffer
+			self.buffer = None
+			return ret
+		else:
+			return self._next_nonempty()
+
+	def __next__(self):
+		line = self._next_buffered()
+		if line[0].lower() in self.stoppers:
+			self.buffer = line
+			raise StopIteration
+		return line
+
+	def next(self):
+		return self.__next__()
+
+	def peek(self):
+		if not self.buffer:
+			try:
+				self.buffer = self._next_nonempty()
+			except StopIteration:
+				return None
+		if self.buffer[0].lower() in self.stoppers:
+			return None
+		return self.buffer
+
+	def peeks(self):
+		ret = self.peek()
+		return ret if ret is not None else ('',)
+
+	@contextmanager
+	def between(self, tag):
+		start = tag + ' begin'
+		end = tag + ' end'
+		self.expectendswith(start)
+		self.stoppers.append(end)
+		yield
+		del self.stoppers[-1]
+		self.expect(tag + ' end')
+
+	@contextmanager
+	def until(self, tags):
+		if type(tags) is not tuple:
+			tags = (tags,)
+		self.stoppers.extend(tags)
+		yield
+		del self.stoppers[-len(tags):]
+
+	def expect(self, s):
+		line = next(self)
+		tag = line[0].lower()
+		assert tag == s, "Expected '%s', got '%s'" % (s, tag)
+		return line
+
+	def expectendswith(self, s):
+		line = next(self)
+		tag = line[0].lower()
+		assert tag.endswith(s), "Expected '*%s', got '%s'" % (s, tag)
+		return line
+
+def build(f, font, tableTag=None):
+	lines = Tokenizer(f)
+	return parseTable(lines, font, tableTag=tableTag)
+
+
+def main(args=None, font=None):
+	import sys
+	from fontTools import configLogger
+	from fontTools.misc.testTools import MockFont
+
+	if args is None:
+		args = sys.argv[1:]
+
+	# configure the library logger (for >= WARNING)
+	configLogger()
+	# comment this out to enable debug messages from mtiLib's logger
+	# log.setLevel(logging.DEBUG)
+
+	if font is None:
+		font = MockFont()
+
+	tableTag = None
+	if args[0].startswith('-t'):
+		tableTag = args[0][2:]
+		del args[0]
+	for f in args:
+		log.debug("Processing %s", f)
+		table = build(open(f, 'rt', encoding="utf-8"), font, tableTag=tableTag)
+		blob = table.compile(font) # Make sure it compiles
+		decompiled = table.__class__()
+		decompiled.decompile(blob, font) # Make sure it decompiles!
+
+		#continue
+		from fontTools.misc import xmlWriter
+		tag = table.tableTag
+		writer = xmlWriter.XMLWriter(sys.stdout)
+		writer.begintag(tag)
+		writer.newline()
+		#table.toXML(writer, font)
+		decompiled.toXML(writer, font)
+		writer.endtag(tag)
+		writer.newline()
+
+
+if __name__ == '__main__':
+	import sys
+	sys.exit(main())
diff --git a/Lib/fontTools/mtiLib/__main__.py b/Lib/fontTools/mtiLib/__main__.py
new file mode 100644
index 0000000..0741237
--- /dev/null
+++ b/Lib/fontTools/mtiLib/__main__.py
@@ -0,0 +1,7 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+import sys
+from fontTools.mtiLib import main
+
+if __name__ == '__main__':
+	sys.exit(main())
diff --git a/Lib/fontTools/otlLib/__init__.py b/Lib/fontTools/otlLib/__init__.py
new file mode 100644
index 0000000..12e414f
--- /dev/null
+++ b/Lib/fontTools/otlLib/__init__.py
@@ -0,0 +1 @@
+"""OpenType Layout-related functionality."""
diff --git a/Lib/fontTools/otlLib/builder.py b/Lib/fontTools/otlLib/builder.py
new file mode 100644
index 0000000..efe66d5
--- /dev/null
+++ b/Lib/fontTools/otlLib/builder.py
@@ -0,0 +1,640 @@
+from __future__ import print_function, division, absolute_import
+from fontTools import ttLib
+from fontTools.ttLib.tables import otTables as ot
+from fontTools.ttLib.tables.otBase import ValueRecord, valueRecordFormatDict
+
+
+def buildCoverage(glyphs, glyphMap):
+    if not glyphs:
+        return None
+    self = ot.Coverage()
+    self.glyphs = sorted(glyphs, key=glyphMap.__getitem__)
+    return self
+
+
+LOOKUP_FLAG_RIGHT_TO_LEFT = 0x0001
+LOOKUP_FLAG_IGNORE_BASE_GLYPHS = 0x0002
+LOOKUP_FLAG_IGNORE_LIGATURES = 0x0004
+LOOKUP_FLAG_IGNORE_MARKS = 0x0008
+LOOKUP_FLAG_USE_MARK_FILTERING_SET = 0x0010
+
+
+def buildLookup(subtables, flags=0, markFilterSet=None):
+    if subtables is None:
+        return None
+    subtables = [st for st in subtables if st is not None]
+    if not subtables:
+        return None
+    assert all(t.LookupType == subtables[0].LookupType for t in subtables), \
+        ("all subtables must have the same LookupType; got %s" %
+         repr([t.LookupType for t in subtables]))
+    self = ot.Lookup()
+    self.LookupType = subtables[0].LookupType
+    self.LookupFlag = flags
+    self.SubTable = subtables
+    self.SubTableCount = len(self.SubTable)
+    if markFilterSet is not None:
+        assert self.LookupFlag & LOOKUP_FLAG_USE_MARK_FILTERING_SET, \
+            ("if markFilterSet is not None, flags must set "
+             "LOOKUP_FLAG_USE_MARK_FILTERING_SET; flags=0x%04x" % flags)
+        assert isinstance(markFilterSet, int), markFilterSet
+        self.MarkFilteringSet = markFilterSet
+    else:
+        assert (self.LookupFlag & LOOKUP_FLAG_USE_MARK_FILTERING_SET) == 0, \
+            ("if markFilterSet is None, flags must not set "
+             "LOOKUP_FLAG_USE_MARK_FILTERING_SET; flags=0x%04x" % flags)
+    return self
+
+
+# GSUB
+
+
+def buildSingleSubstSubtable(mapping):
+    if not mapping:
+        return None
+    self = ot.SingleSubst()
+    self.mapping = dict(mapping)
+    return self
+
+
+def buildMultipleSubstSubtable(mapping):
+    if not mapping:
+        return None
+    self = ot.MultipleSubst()
+    self.mapping = dict(mapping)
+    return self
+
+
+def buildAlternateSubstSubtable(mapping):
+    if not mapping:
+        return None
+    self = ot.AlternateSubst()
+    self.alternates = dict(mapping)
+    return self
+
+
+def _getLigatureKey(components):
+    """Computes a key for ordering ligatures in a GSUB Type-4 lookup.
+
+    When building the OpenType lookup, we need to make sure that
+    the longest sequence of components is listed first, so we
+    use the negative length as the primary key for sorting.
+    To make buildLigatureSubstSubtable() deterministic, we use the
+    component sequence as the secondary key.
+
+    For example, this will sort (f,f,f) < (f,f,i) < (f,f) < (f,i) < (f,l).
+    """
+    return (-len(components), components)
+
+
+def buildLigatureSubstSubtable(mapping):
+    if not mapping:
+        return None
+    self = ot.LigatureSubst()
+    # The following single line can replace the rest of this function
+    # with fontTools >= 3.1:
+    # self.ligatures = dict(mapping)
+    self.ligatures = {}
+    for components in sorted(mapping.keys(), key=_getLigatureKey):
+        ligature = ot.Ligature()
+        ligature.Component = components[1:]
+        ligature.CompCount = len(ligature.Component) + 1
+        ligature.LigGlyph = mapping[components]
+        firstGlyph = components[0]
+        self.ligatures.setdefault(firstGlyph, []).append(ligature)
+    return self
+
+
+# GPOS
+
+
+def buildAnchor(x, y, point=None, deviceX=None, deviceY=None):
+    self = ot.Anchor()
+    self.XCoordinate, self.YCoordinate = x, y
+    self.Format = 1
+    if point is not None:
+        self.AnchorPoint = point
+        self.Format = 2
+    if deviceX is not None or deviceY is not None:
+        assert self.Format == 1, \
+            "Either point, or both of deviceX/deviceY, must be None."
+        self.XDeviceTable = deviceX
+        self.YDeviceTable = deviceY
+        self.Format = 3
+    return self
+
+
+def buildBaseArray(bases, numMarkClasses, glyphMap):
+    self = ot.BaseArray()
+    self.BaseRecord = []
+    for base in sorted(bases, key=glyphMap.__getitem__):
+        b = bases[base]
+        anchors = [b.get(markClass) for markClass in range(numMarkClasses)]
+        self.BaseRecord.append(buildBaseRecord(anchors))
+    self.BaseCount = len(self.BaseRecord)
+    return self
+
+
+def buildBaseRecord(anchors):
+    """[otTables.Anchor, otTables.Anchor, ...] --> otTables.BaseRecord"""
+    self = ot.BaseRecord()
+    self.BaseAnchor = anchors
+    return self
+
+
+def buildComponentRecord(anchors):
+    """[otTables.Anchor, otTables.Anchor, ...] --> otTables.ComponentRecord"""
+    if not anchors:
+        return None
+    self = ot.ComponentRecord()
+    self.LigatureAnchor = anchors
+    return self
+
+
+def buildCursivePosSubtable(attach, glyphMap):
+    """{"alef": (entry, exit)} --> otTables.CursivePos"""
+    if not attach:
+        return None
+    self = ot.CursivePos()
+    self.Format = 1
+    self.Coverage = buildCoverage(attach.keys(), glyphMap)
+    self.EntryExitRecord = []
+    for glyph in self.Coverage.glyphs:
+        entryAnchor, exitAnchor = attach[glyph]
+        rec = ot.EntryExitRecord()
+        rec.EntryAnchor = entryAnchor
+        rec.ExitAnchor = exitAnchor
+        self.EntryExitRecord.append(rec)
+    self.EntryExitCount = len(self.EntryExitRecord)
+    return self
+
+
+def buildDevice(deltas):
+    """{8:+1, 10:-3, ...} --> otTables.Device"""
+    if not deltas:
+        return None
+    self = ot.Device()
+    keys = deltas.keys()
+    self.StartSize = startSize = min(keys)
+    self.EndSize = endSize = max(keys)
+    assert 0 <= startSize <= endSize
+    self.DeltaValue = deltaValues = [
+        deltas.get(size, 0)
+        for size in range(startSize, endSize + 1)]
+    maxDelta = max(deltaValues)
+    minDelta = min(deltaValues)
+    assert minDelta > -129 and maxDelta < 128
+    if minDelta > -3 and maxDelta < 2:
+        self.DeltaFormat = 1
+    elif minDelta > -9 and maxDelta < 8:
+        self.DeltaFormat = 2
+    else:
+        self.DeltaFormat = 3
+    return self
+
+
+def buildLigatureArray(ligs, numMarkClasses, glyphMap):
+    self = ot.LigatureArray()
+    self.LigatureAttach = []
+    for lig in sorted(ligs, key=glyphMap.__getitem__):
+        anchors = []
+        for component in ligs[lig]:
+            anchors.append([component.get(mc) for mc in range(numMarkClasses)])
+        self.LigatureAttach.append(buildLigatureAttach(anchors))
+    self.LigatureCount = len(self.LigatureAttach)
+    return self
+
+
+def buildLigatureAttach(components):
+    """[[Anchor, Anchor], [Anchor, Anchor, Anchor]] --> LigatureAttach"""
+    self = ot.LigatureAttach()
+    self.ComponentRecord = [buildComponentRecord(c) for c in components]
+    self.ComponentCount = len(self.ComponentRecord)
+    return self
+
+
+def buildMarkArray(marks, glyphMap):
+    """{"acute": (markClass, otTables.Anchor)} --> otTables.MarkArray"""
+    self = ot.MarkArray()
+    self.MarkRecord = []
+    for mark in sorted(marks.keys(), key=glyphMap.__getitem__):
+        markClass, anchor = marks[mark]
+        markrec = buildMarkRecord(markClass, anchor)
+        self.MarkRecord.append(markrec)
+    self.MarkCount = len(self.MarkRecord)
+    return self
+
+
+def buildMarkBasePos(marks, bases, glyphMap):
+    """Build a list of MarkBasePos subtables.
+
+    a1, a2, a3, a4, a5 = buildAnchor(500, 100), ...
+    marks = {"acute": (0, a1), "grave": (0, a1), "cedilla": (1, a2)}
+    bases = {"a": {0: a3, 1: a5}, "b": {0: a4, 1: a5}}
+    """
+    # TODO: Consider emitting multiple subtables to save space.
+    # Partition the marks and bases into disjoint subsets, so that
+    # MarkBasePos rules would only access glyphs from a single
+    # subset. This would likely lead to smaller mark/base
+    # matrices, so we might be able to omit many of the empty
+    # anchor tables that we currently produce. Of course, this
+    # would only work if the MarkBasePos rules of real-world fonts
+    # allow partitioning into multiple subsets. We should find out
+    # whether this is the case; if so, implement the optimization.
+    # On the other hand, a very large number of subtables could
+    # slow down layout engines; so this would need profiling.
+    return [buildMarkBasePosSubtable(marks, bases, glyphMap)]
+
+
+def buildMarkBasePosSubtable(marks, bases, glyphMap):
+    """Build a single MarkBasePos subtable.
+
+    a1, a2, a3, a4, a5 = buildAnchor(500, 100), ...
+    marks = {"acute": (0, a1), "grave": (0, a1), "cedilla": (1, a2)}
+    bases = {"a": {0: a3, 1: a5}, "b": {0: a4, 1: a5}}
+    """
+    self = ot.MarkBasePos()
+    self.Format = 1
+    self.MarkCoverage = buildCoverage(marks, glyphMap)
+    self.MarkArray = buildMarkArray(marks, glyphMap)
+    self.ClassCount = max([mc for mc, _ in marks.values()]) + 1
+    self.BaseCoverage = buildCoverage(bases, glyphMap)
+    self.BaseArray = buildBaseArray(bases, self.ClassCount, glyphMap)
+    return self
+
+
+def buildMarkLigPos(marks, ligs, glyphMap):
+    """Build a list of MarkLigPos subtables.
+
+    a1, a2, a3, a4, a5 = buildAnchor(500, 100), ...
+    marks = {"acute": (0, a1), "grave": (0, a1), "cedilla": (1, a2)}
+    ligs = {"f_i": [{0: a3, 1: a5},  {0: a4, 1: a5}], "c_t": [{...}, {...}]}
+    """
+    # TODO: Consider splitting into multiple subtables to save space,
+    # as with MarkBasePos, this would be a trade-off that would need
+    # profiling. And, depending on how typical fonts are structured,
+    # it might not be worth doing at all.
+    return [buildMarkLigPosSubtable(marks, ligs, glyphMap)]
+
+
+def buildMarkLigPosSubtable(marks, ligs, glyphMap):
+    """Build a single MarkLigPos subtable.
+
+    a1, a2, a3, a4, a5 = buildAnchor(500, 100), ...
+    marks = {"acute": (0, a1), "grave": (0, a1), "cedilla": (1, a2)}
+    ligs = {"f_i": [{0: a3, 1: a5},  {0: a4, 1: a5}], "c_t": [{...}, {...}]}
+    """
+    self = ot.MarkLigPos()
+    self.Format = 1
+    self.MarkCoverage = buildCoverage(marks, glyphMap)
+    self.MarkArray = buildMarkArray(marks, glyphMap)
+    self.ClassCount = max([mc for mc, _ in marks.values()]) + 1
+    self.LigatureCoverage = buildCoverage(ligs, glyphMap)
+    self.LigatureArray = buildLigatureArray(ligs, self.ClassCount, glyphMap)
+    return self
+
+
+def buildMarkRecord(classID, anchor):
+    assert isinstance(classID, int)
+    assert isinstance(anchor, ot.Anchor)
+    self = ot.MarkRecord()
+    self.Class = classID
+    self.MarkAnchor = anchor
+    return self
+
+
+def buildMark2Record(anchors):
+    """[otTables.Anchor, otTables.Anchor, ...] --> otTables.Mark2Record"""
+    self = ot.Mark2Record()
+    self.Mark2Anchor = anchors
+    return self
+
+
+def _getValueFormat(f, values, i):
+    """Helper for buildPairPos{Glyphs|Classes}Subtable."""
+    if f is not None:
+        return f
+    mask = 0
+    for value in values:
+        if value is not None and value[i] is not None:
+            mask |= value[i].getFormat()
+    return mask
+
+
+def buildPairPosClassesSubtable(pairs, glyphMap,
+                                valueFormat1=None, valueFormat2=None):
+    coverage = set()
+    classDef1 = ClassDefBuilder(useClass0=True)
+    classDef2 = ClassDefBuilder(useClass0=False)
+    for gc1, gc2 in sorted(pairs):
+        coverage.update(gc1)
+        classDef1.add(gc1)
+        classDef2.add(gc2)
+    self = ot.PairPos()
+    self.Format = 2
+    self.ValueFormat1 = _getValueFormat(valueFormat1, pairs.values(), 0)
+    self.ValueFormat2 = _getValueFormat(valueFormat2, pairs.values(), 1)
+    self.Coverage = buildCoverage(coverage, glyphMap)
+    self.ClassDef1 = classDef1.build()
+    self.ClassDef2 = classDef2.build()
+    classes1 = classDef1.classes()
+    classes2 = classDef2.classes()
+    self.Class1Record = []
+    for c1 in classes1:
+        rec1 = ot.Class1Record()
+        rec1.Class2Record = []
+        self.Class1Record.append(rec1)
+        for c2 in classes2:
+            rec2 = ot.Class2Record()
+            rec2.Value1, rec2.Value2 = pairs.get((c1, c2), (None, None))
+            rec1.Class2Record.append(rec2)
+    self.Class1Count = len(self.Class1Record)
+    self.Class2Count = len(classes2)
+    return self
+
+
+def buildPairPosGlyphs(pairs, glyphMap):
+    p = {}  # (formatA, formatB) --> {(glyphA, glyphB): (valA, valB)}
+    for (glyphA, glyphB), (valA, valB) in pairs.items():
+        formatA = valA.getFormat() if valA is not None else 0
+        formatB = valB.getFormat() if valB is not None else 0
+        pos = p.setdefault((formatA, formatB), {})
+        pos[(glyphA, glyphB)] = (valA, valB)
+    return [
+        buildPairPosGlyphsSubtable(pos, glyphMap, formatA, formatB)
+        for ((formatA, formatB), pos) in sorted(p.items())]
+
+
+def buildPairPosGlyphsSubtable(pairs, glyphMap,
+                               valueFormat1=None, valueFormat2=None):
+    self = ot.PairPos()
+    self.Format = 1
+    self.ValueFormat1 = _getValueFormat(valueFormat1, pairs.values(), 0)
+    self.ValueFormat2 = _getValueFormat(valueFormat2, pairs.values(), 1)
+    p = {}
+    for (glyphA, glyphB), (valA, valB) in pairs.items():
+        p.setdefault(glyphA, []).append((glyphB, valA, valB))
+    self.Coverage = buildCoverage({g for g, _ in pairs.keys()}, glyphMap)
+    self.PairSet = []
+    for glyph in self.Coverage.glyphs:
+        ps = ot.PairSet()
+        ps.PairValueRecord = []
+        self.PairSet.append(ps)
+        for glyph2, val1, val2 in \
+                sorted(p[glyph], key=lambda x: glyphMap[x[0]]):
+            pvr = ot.PairValueRecord()
+            pvr.SecondGlyph = glyph2
+            pvr.Value1 = val1 if val1 and val1.getFormat() != 0 else None
+            pvr.Value2 = val2 if val2 and val2.getFormat() != 0 else None
+            ps.PairValueRecord.append(pvr)
+        ps.PairValueCount = len(ps.PairValueRecord)
+    self.PairSetCount = len(self.PairSet)
+    return self
+
+
+def buildSinglePos(mapping, glyphMap):
+    """{"glyph": ValueRecord} --> [otTables.SinglePos*]"""
+    result, handled = [], set()
+    # In SinglePos format 1, the covered glyphs all share the same ValueRecord.
+    # In format 2, each glyph has its own ValueRecord, but these records
+    # all have the same properties (eg., all have an X but no Y placement).
+    coverages, masks, values = {}, {}, {}
+    for glyph, value in mapping.items():
+        key = _getSinglePosValueKey(value)
+        coverages.setdefault(key, []).append(glyph)
+        masks.setdefault(key[0], []).append(key)
+        values[key] = value
+
+    # If a ValueRecord is shared between multiple glyphs, we generate
+    # a SinglePos format 1 subtable; that is the most compact form.
+    for key, glyphs in coverages.items():
+        if len(glyphs) > 1:
+            format1Mapping = {g: values[key] for g in glyphs}
+            result.append(buildSinglePosSubtable(format1Mapping, glyphMap))
+            handled.add(key)
+
+    # In the remaining ValueRecords, look for those whose valueFormat
+    # (the set of used properties) is shared between multiple records.
+    # These will get encoded in format 2.
+    for valueFormat, keys in masks.items():
+        f2 = [k for k in keys if k not in handled]
+        if len(f2) > 1:
+            format2Mapping = {coverages[k][0]: values[k] for k in f2}
+            result.append(buildSinglePosSubtable(format2Mapping, glyphMap))
+            handled.update(f2)
+
+    # The remaining ValueRecords are singletons in the sense that
+    # they are only used by a single glyph, and their valueFormat
+    # is unique as well. We encode these in format 1 again.
+    for key, glyphs in coverages.items():
+        if key not in handled:
+            assert len(glyphs) == 1, glyphs
+            st = buildSinglePosSubtable({glyphs[0]: values[key]}, glyphMap)
+            result.append(st)
+
+    # When the OpenType layout engine traverses the subtables, it will
+    # stop after the first matching subtable.  Therefore, we sort the
+    # resulting subtables by decreasing coverage size; this increases
+    # the chance that the layout engine can do an early exit. (Of course,
+    # this would only be true if all glyphs were equally frequent, which
+    # is not really the case; but we do not know their distribution).
+    # If two subtables cover the same number of glyphs, we sort them
+    # by glyph ID so that our output is deterministic.
+    result.sort(key=lambda t: _getSinglePosTableKey(t, glyphMap))
+    return result
+
+
+def buildSinglePosSubtable(values, glyphMap):
+    """{glyphName: otBase.ValueRecord} --> otTables.SinglePos"""
+    self = ot.SinglePos()
+    self.Coverage = buildCoverage(values.keys(), glyphMap)
+    valueRecords = [values[g] for g in self.Coverage.glyphs]
+    self.ValueFormat = 0
+    for v in valueRecords:
+        self.ValueFormat |= v.getFormat()
+    if all(v == valueRecords[0] for v in valueRecords):
+        self.Format = 1
+        if self.ValueFormat != 0:
+            self.Value = valueRecords[0]
+        else:
+            self.Value = None
+    else:
+        self.Format = 2
+        self.Value = valueRecords
+        self.ValueCount = len(self.Value)
+    return self
+
+
+def _getSinglePosTableKey(subtable, glyphMap):
+    assert isinstance(subtable, ot.SinglePos), subtable
+    glyphs = subtable.Coverage.glyphs
+    return (-len(glyphs), glyphMap[glyphs[0]])
+
+
+def _getSinglePosValueKey(valueRecord):
+    """otBase.ValueRecord --> (2, ("YPlacement": 12))"""
+    assert isinstance(valueRecord, ValueRecord), valueRecord
+    valueFormat, result = 0, []
+    for name, value in valueRecord.__dict__.items():
+        if isinstance(value, ot.Device):
+            result.append((name, _makeDeviceTuple(value)))
+        else:
+            result.append((name, value))
+        valueFormat |= valueRecordFormatDict[name][0]
+    result.sort()
+    result.insert(0, valueFormat)
+    return tuple(result)
+
+
+def _makeDeviceTuple(device):
+    """otTables.Device --> tuple, for making device tables unique"""
+    return (device.DeltaFormat, device.StartSize, device.EndSize,
+            tuple(device.DeltaValue))
+
+
+def buildValue(value):
+    self = ValueRecord()
+    for k, v in value.items():
+        setattr(self, k, v)
+    return self
+
+
+# GDEF
+
+def buildAttachList(attachPoints, glyphMap):
+    """{"glyphName": [4, 23]} --> otTables.AttachList, or None"""
+    if not attachPoints:
+        return None
+    self = ot.AttachList()
+    self.Coverage = buildCoverage(attachPoints.keys(), glyphMap)
+    self.AttachPoint = [buildAttachPoint(attachPoints[g])
+                        for g in self.Coverage.glyphs]
+    self.GlyphCount = len(self.AttachPoint)
+    return self
+
+
+def buildAttachPoint(points):
+    """[4, 23, 41] --> otTables.AttachPoint"""
+    if not points:
+        return None
+    self = ot.AttachPoint()
+    self.PointIndex = sorted(set(points))
+    self.PointCount = len(self.PointIndex)
+    return self
+
+
+def buildCaretValueForCoord(coord):
+    """500 --> otTables.CaretValue, format 1"""
+    self = ot.CaretValue()
+    self.Format = 1
+    self.Coordinate = coord
+    return self
+
+
+def buildCaretValueForPoint(point):
+    """4 --> otTables.CaretValue, format 2"""
+    self = ot.CaretValue()
+    self.Format = 2
+    self.CaretValuePoint = point
+    return self
+
+
+def buildLigCaretList(coords, points, glyphMap):
+    """{"f_f_i":[300,600]}, {"c_t":[28]} --> otTables.LigCaretList, or None"""
+    glyphs = set(coords.keys()) if coords else set()
+    if points:
+        glyphs.update(points.keys())
+    carets = {g: buildLigGlyph(coords.get(g), points.get(g)) for g in glyphs}
+    carets = {g: c for g, c in carets.items() if c is not None}
+    if not carets:
+        return None
+    self = ot.LigCaretList()
+    self.Coverage = buildCoverage(carets.keys(), glyphMap)
+    self.LigGlyph = [carets[g] for g in self.Coverage.glyphs]
+    self.LigGlyphCount = len(self.LigGlyph)
+    return self
+
+
+def buildLigGlyph(coords, points):
+    """([500], [4]) --> otTables.LigGlyph; None for empty coords/points"""
+    carets = []
+    if coords:
+        carets.extend([buildCaretValueForCoord(c) for c in sorted(coords)])
+    if points:
+        carets.extend([buildCaretValueForPoint(p) for p in sorted(points)])
+    if not carets:
+        return None
+    self = ot.LigGlyph()
+    self.CaretValue = carets
+    self.CaretCount = len(self.CaretValue)
+    return self
+
+
+def buildMarkGlyphSetsDef(markSets, glyphMap):
+    """[{"acute","grave"}, {"caron","grave"}] --> otTables.MarkGlyphSetsDef"""
+    if not markSets:
+        return None
+    self = ot.MarkGlyphSetsDef()
+    self.MarkSetTableFormat = 1
+    self.Coverage = [buildCoverage(m, glyphMap) for m in markSets]
+    self.MarkSetCount = len(self.Coverage)
+    return self
+
+
+class ClassDefBuilder(object):
+    """Helper for building ClassDef tables."""
+    def __init__(self, useClass0):
+        self.classes_ = set()
+        self.glyphs_ = {}
+        self.useClass0_ = useClass0
+
+    def canAdd(self, glyphs):
+        if isinstance(glyphs, (set, frozenset)):
+            glyphs = sorted(glyphs)
+        glyphs = tuple(glyphs)
+        if glyphs in self.classes_:
+            return True
+        for glyph in glyphs:
+            if glyph in self.glyphs_:
+                return False
+        return True
+
+    def add(self, glyphs):
+        if isinstance(glyphs, (set, frozenset)):
+            glyphs = sorted(glyphs)
+        glyphs = tuple(glyphs)
+        if glyphs in self.classes_:
+            return
+        self.classes_.add(glyphs)
+        for glyph in glyphs:
+            assert glyph not in self.glyphs_
+            self.glyphs_[glyph] = glyphs
+
+    def classes(self):
+        # In ClassDef1 tables, class id #0 does not need to be encoded
+        # because zero is the default. Therefore, we use id #0 for the
+        # glyph class that has the largest number of members. However,
+        # in other tables than ClassDef1, 0 means "every other glyph"
+        # so we should not use that ID for any real glyph classes;
+        # we implement this by inserting an empty set at position 0.
+        #
+        # TODO: Instead of counting the number of glyphs in each class,
+        # we should determine the encoded size. If the glyphs in a large
+        # class form a contiguous range, the encoding is actually quite
+        # compact, whereas a non-contiguous set might need a lot of bytes
+        # in the output file. We don't get this right with the key below.
+        result = sorted(self.classes_, key=lambda s: (len(s), s), reverse=True)
+        if not self.useClass0_:
+            result.insert(0, frozenset())
+        return result
+
+    def build(self):
+        glyphClasses = {}
+        for classID, glyphs in enumerate(self.classes()):
+            if classID == 0:
+                continue
+            for glyph in glyphs:
+                glyphClasses[glyph] = classID
+        classDef = ot.ClassDef()
+        classDef.classDefs = glyphClasses
+        return classDef
diff --git a/Lib/fontTools/pens/__init__.py b/Lib/fontTools/pens/__init__.py
index e001bb2..3f9abc9 100644
--- a/Lib/fontTools/pens/__init__.py
+++ b/Lib/fontTools/pens/__init__.py
@@ -1,3 +1,4 @@
-"""Empty __init__.py file to signal Python this directory is a package.
-(It can't be completely empty since WinZip seems to skip empty files.)
-"""
+"""Empty __init__.py file to signal Python this directory is a package."""
+
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
diff --git a/Lib/fontTools/pens/areaPen.py b/Lib/fontTools/pens/areaPen.py
new file mode 100644
index 0000000..564caa4
--- /dev/null
+++ b/Lib/fontTools/pens/areaPen.py
@@ -0,0 +1,59 @@
+"""Calculate the area of a glyph."""
+
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.pens.basePen import BasePen
+
+
+__all__ = ["AreaPen"]
+
+
+class AreaPen(BasePen):
+
+	def __init__(self, glyphset=None):
+		BasePen.__init__(self, glyphset)
+		self.value = 0
+
+	def _moveTo(self, p0):
+		self._p0 = self._startPoint = p0
+
+	def _lineTo(self, p1):
+		x0, y0 = self._p0
+		x1, y1 = p1
+		self.value -= (x1 - x0) * (y1 + y0) * .5
+		self._p0 = p1
+
+	def _qCurveToOne(self, p1, p2):
+		# https://github.com/Pomax/bezierinfo/issues/44
+		p0 = self._p0
+		x0, y0 = p0[0], p0[1]
+		x1, y1 = p1[0] - x0, p1[1] - y0
+		x2, y2 = p2[0] - x0, p2[1] - y0
+		self.value -= (x2 * y1 - x1 * y2) / 3
+		self._lineTo(p2)
+		self._p0 = p2
+
+	def _curveToOne(self, p1, p2, p3):
+		# https://github.com/Pomax/bezierinfo/issues/44
+		p0 = self._p0
+		x0, y0 = p0[0], p0[1]
+		x1, y1 = p1[0] - x0, p1[1] - y0
+		x2, y2 = p2[0] - x0, p2[1] - y0
+		x3, y3 = p3[0] - x0, p3[1] - y0
+		self.value -= (
+				x1 * (   -   y2 -   y3) +
+				x2 * (y1        - 2*y3) +
+				x3 * (y1 + 2*y2       )
+			      ) * 0.15
+		self._lineTo(p3)
+		self._p0 = p3
+
+	def _closePath(self):
+		self._lineTo(self._startPoint)
+		del self._p0, self._startPoint
+
+	def _endPath(self):
+		if self._p0 != self._startPoint:
+			# Area is not defined for open contours.
+			raise NotImplementedError
+		del self._p0, self._startPoint
diff --git a/Lib/fontTools/pens/basePen.py b/Lib/fontTools/pens/basePen.py
index eee4269..e0d3ae0 100644
--- a/Lib/fontTools/pens/basePen.py
+++ b/Lib/fontTools/pens/basePen.py
@@ -10,7 +10,7 @@
 
 The most basic pattern is this:
 
-    outline.draw(pen)  # 'outline' draws itself onto 'pen'
+	outline.draw(pen)  # 'outline' draws itself onto 'pen'
 
 Pens can be used to render outlines to the screen, but also to construct
 new outlines. Eg. an outline object can be both a drawable object (it has a
@@ -38,9 +38,10 @@
 
 from __future__ import print_function, division, absolute_import
 from fontTools.misc.py23 import *
+from fontTools.misc.loggingTools import LogMixin
 
-__all__ = ["AbstractPen", "NullPen", "BasePen",
-           "decomposeSuperBezierSegment", "decomposeQuadraticSegment"]
+__all__ =  ["AbstractPen", "NullPen", "BasePen",
+			"decomposeSuperBezierSegment", "decomposeQuadraticSegment"]
 
 
 class AbstractPen(object):
@@ -141,7 +142,50 @@
 		pass
 
 
-class BasePen(AbstractPen):
+class LoggingPen(LogMixin, AbstractPen):
+	"""A pen with a `log` property (see fontTools.misc.loggingTools.LogMixin)
+	"""
+	pass
+
+
+class DecomposingPen(LoggingPen):
+
+	""" Implements a 'addComponent' method that decomposes components
+	(i.e. draws them onto self as simple contours).
+	It can also be used as a mixin class (e.g. see ContourRecordingPen).
+
+	You must override moveTo, lineTo, curveTo and qCurveTo. You may
+	additionally override closePath, endPath and addComponent.
+	"""
+
+	# By default a warning message is logged when a base glyph is missing;
+	# set this to False if you want to raise a 'KeyError' exception
+	skipMissingComponents = True
+
+	def __init__(self, glyphSet):
+		""" Takes a single 'glyphSet' argument (dict), in which the glyphs
+		that are referenced as components are looked up by their name.
+		"""
+		super(DecomposingPen, self).__init__()
+		self.glyphSet = glyphSet
+
+	def addComponent(self, glyphName, transformation):
+		""" Transform the points of the base glyph and draw it onto self.
+		"""
+		from fontTools.pens.transformPen import TransformPen
+		try:
+			glyph = self.glyphSet[glyphName]
+		except KeyError:
+			if not self.skipMissingComponents:
+				raise
+			self.log.warning(
+				"glyph '%s' is missing from glyphSet; skipped" % glyphName)
+		else:
+			tPen = TransformPen(self, transformation)
+			glyph.draw(tPen)
+
+
+class BasePen(DecomposingPen):
 
 	"""Base class for drawing pens. You must override _moveTo, _lineTo and
 	_curveToOne. You may additionally override _closePath, _endPath,
@@ -149,8 +193,8 @@
 	methods.
 	"""
 
-	def __init__(self, glyphSet):
-		self.glyphSet = glyphSet
+	def __init__(self, glyphSet=None):
+		super(BasePen, self).__init__(glyphSet)
 		self.__currentPoint = None
 
 	# must override
@@ -186,19 +230,6 @@
 		mid2y = pt2y + 0.66666666666666667 * (pt1y - pt2y)
 		self._curveToOne((mid1x, mid1y), (mid2x, mid2y), pt2)
 
-	def addComponent(self, glyphName, transformation):
-		"""This default implementation simply transforms the points
-		of the base glyph and draws it onto self.
-		"""
-		from fontTools.pens.transformPen import TransformPen
-		try:
-			glyph = self.glyphSet[glyphName]
-		except KeyError:
-			pass
-		else:
-			tPen = TransformPen(self, transformation)
-			glyph.draw(tPen)
-
 	# don't override
 
 	def _getCurrentPoint(self):
@@ -307,8 +338,8 @@
 			if pt2 is None:
 				pt2 = temp
 			else:
-				pt3 = (0.5 * (pt2[0] + temp[0]),
-					   0.5 * (pt2[1] + temp[1]))
+				pt3 =  (0.5 * (pt2[0] + temp[0]),
+						0.5 * (pt2[1] + temp[1]))
 				bezierSegments.append((pt1, pt2, pt3))
 				pt1, pt2, pt3 = temp, None, None
 	bezierSegments.append((pt1, points[-2], points[-1]))
diff --git a/Lib/fontTools/pens/boundsPen.py b/Lib/fontTools/pens/boundsPen.py
index 4d14a0a..3a103e3 100644
--- a/Lib/fontTools/pens/boundsPen.py
+++ b/Lib/fontTools/pens/boundsPen.py
@@ -17,25 +17,42 @@
 
 	When the shape has been drawn, the bounds are available as the
 	'bounds' attribute of the pen object. It's a 4-tuple:
-		(xMin, yMin, xMax, yMax)
+		(xMin, yMin, xMax, yMax).
+
+	If 'ignoreSinglePoints' is True, single points are ignored.
 	"""
 
-	def __init__(self, glyphSet):
+	def __init__(self, glyphSet, ignoreSinglePoints=False):
 		BasePen.__init__(self, glyphSet)
-		self.bounds = None
+		self.ignoreSinglePoints = ignoreSinglePoints
+		self.init()
+
+	def init(self):
+	    self.bounds = None
+	    self._start = None
 
 	def _moveTo(self, pt):
+		self._start = pt
+		if not self.ignoreSinglePoints:
+			self._addMoveTo()
+
+	def _addMoveTo(self):
+		if self._start is None:
+			return
 		bounds = self.bounds
 		if bounds:
-			self.bounds = updateBounds(bounds, pt)
+			self.bounds = updateBounds(bounds, self._start)
 		else:
-			x, y = pt
+			x, y = self._start
 			self.bounds = (x, y, x, y)
+		self._start = None
 
 	def _lineTo(self, pt):
+		self._addMoveTo()
 		self.bounds = updateBounds(self.bounds, pt)
 
 	def _curveToOne(self, bcp1, bcp2, pt):
+		self._addMoveTo()
 		bounds = self.bounds
 		bounds = updateBounds(bounds, bcp1)
 		bounds = updateBounds(bounds, bcp2)
@@ -43,6 +60,7 @@
 		self.bounds = bounds
 
 	def _qCurveToOne(self, bcp, pt):
+		self._addMoveTo()
 		bounds = self.bounds
 		bounds = updateBounds(bounds, bcp)
 		bounds = updateBounds(bounds, pt)
@@ -62,6 +80,7 @@
 	"""
 
 	def _curveToOne(self, bcp1, bcp2, pt):
+		self._addMoveTo()
 		bounds = self.bounds
 		bounds = updateBounds(bounds, pt)
 		if not pointInRect(bcp1, bounds) or not pointInRect(bcp2, bounds):
@@ -70,26 +89,10 @@
 		self.bounds = bounds
 
 	def _qCurveToOne(self, bcp, pt):
+		self._addMoveTo()
 		bounds = self.bounds
 		bounds = updateBounds(bounds, pt)
 		if not pointInRect(bcp, bounds):
 			bounds = unionRect(bounds, calcQuadraticBounds(
 					self._getCurrentPoint(), bcp, pt))
 		self.bounds = bounds
-
-
-if __name__ == "__main__":
-	def draw(pen):
-		pen.moveTo((0, 0))
-		pen.lineTo((0, 100))
-		pen.qCurveTo((50, 75), (60, 50), (50, 25), (0, 0))
-		pen.curveTo((-50, 25), (-60, 50), (-50, 75), (0, 100))
-		pen.closePath()
-
-	pen = ControlBoundsPen(None)
-	draw(pen)
-	print(pen.bounds)
-
-	pen = BoundsPen(None)
-	draw(pen)
-	print(pen.bounds)
diff --git a/Lib/fontTools/pens/filterPen.py b/Lib/fontTools/pens/filterPen.py
new file mode 100644
index 0000000..2f94550
--- /dev/null
+++ b/Lib/fontTools/pens/filterPen.py
@@ -0,0 +1,121 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.pens.basePen import AbstractPen
+from fontTools.pens.recordingPen import RecordingPen
+
+
+class _PassThruComponentsMixin(object):
+
+    def addComponent(self, glyphName, transformation):
+        self._outPen.addComponent(glyphName, transformation)
+
+
+class FilterPen(_PassThruComponentsMixin, AbstractPen):
+
+    """ Base class for pens that apply some transformation to the coordinates
+    they receive and pass them to another pen.
+
+    You can override any of its methods. The default implementation does
+    nothing, but passes the commands unmodified to the other pen.
+
+    >>> from fontTools.pens.recordingPen import RecordingPen
+    >>> rec = RecordingPen()
+    >>> pen = FilterPen(rec)
+    >>> v = iter(rec.value)
+
+    >>> pen.moveTo((0, 0))
+    >>> next(v)
+    ('moveTo', ((0, 0),))
+
+    >>> pen.lineTo((1, 1))
+    >>> next(v)
+    ('lineTo', ((1, 1),))
+
+    >>> pen.curveTo((2, 2), (3, 3), (4, 4))
+    >>> next(v)
+    ('curveTo', ((2, 2), (3, 3), (4, 4)))
+
+    >>> pen.qCurveTo((5, 5), (6, 6), (7, 7), (8, 8))
+    >>> next(v)
+    ('qCurveTo', ((5, 5), (6, 6), (7, 7), (8, 8)))
+
+    >>> pen.closePath()
+    >>> next(v)
+    ('closePath', ())
+
+    >>> pen.moveTo((9, 9))
+    >>> next(v)
+    ('moveTo', ((9, 9),))
+
+    >>> pen.endPath()
+    >>> next(v)
+    ('endPath', ())
+
+    >>> pen.addComponent('foo', (1, 0, 0, 1, 0, 0))
+    >>> next(v)
+    ('addComponent', ('foo', (1, 0, 0, 1, 0, 0)))
+    """
+
+    def __init__(self, outPen):
+        self._outPen = outPen
+
+    def moveTo(self, pt):
+        self._outPen.moveTo(pt)
+
+    def lineTo(self, pt):
+        self._outPen.lineTo(pt)
+
+    def curveTo(self, *points):
+        self._outPen.curveTo(*points)
+
+    def qCurveTo(self, *points):
+        self._outPen.qCurveTo(*points)
+
+    def closePath(self):
+        self._outPen.closePath()
+
+    def endPath(self):
+        self._outPen.endPath()
+
+
+class ContourFilterPen(_PassThruComponentsMixin, RecordingPen):
+    """A "buffered" filter pen that accumulates contour data, passes
+    it through a ``filterContour`` method when the contour is closed or ended,
+    and finally draws the result with the output pen.
+
+    Components are passed through unchanged.
+    """
+
+    def __init__(self, outPen):
+        super(ContourFilterPen, self).__init__()
+        self._outPen = outPen
+
+    def closePath(self):
+        super(ContourFilterPen, self).closePath()
+        self._flushContour()
+
+    def endPath(self):
+        super(ContourFilterPen, self).endPath()
+        self._flushContour()
+
+    def _flushContour(self):
+        result = self.filterContour(self.value)
+        if result is not None:
+            self.value = result
+        self.replay(self._outPen)
+        self.value = []
+
+    def filterContour(self, contour):
+        """Subclasses must override this to perform the filtering.
+
+        The contour is a list of pen (operator, operands) tuples.
+        Operators are strings corresponding to the AbstractPen methods:
+        "moveTo", "lineTo", "curveTo", "qCurveTo", "closePath" and
+        "endPath". The operands are the positional arguments that are
+        passed to each method.
+
+        If the method doesn't return a value (i.e. returns None), it's
+        assumed that the argument was modified in-place.
+        Otherwise, the return value is drawn with the output pen.
+        """
+        return  # or return contour
diff --git a/Lib/fontTools/pens/momentsPen.py b/Lib/fontTools/pens/momentsPen.py
new file mode 100644
index 0000000..b83102c
--- /dev/null
+++ b/Lib/fontTools/pens/momentsPen.py
@@ -0,0 +1,294 @@
+"""Pen calculating 0th, 1st, and 2nd moments of area of glyph shapes.
+This is low-level, autogenerated pen. Use statisticsPen instead."""
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.pens.basePen import BasePen
+
+
+__all__ = ["MomentsPen"]
+
+
+class MomentsPen(BasePen):
+
+	def __init__(self, glyphset=None):
+		BasePen.__init__(self, glyphset)
+
+		self.area = 0
+		self.momentX = 0
+		self.momentY = 0
+		self.momentXX = 0
+		self.momentXY = 0
+		self.momentYY = 0
+
+	def _moveTo(self, p0):
+		self.__startPoint = p0
+
+	def _closePath(self):
+		p0 = self._getCurrentPoint()
+		if p0 != self.__startPoint:
+			self._lineTo(self.__startPoint)
+
+	def _endPath(self):
+		p0 = self._getCurrentPoint()
+		if p0 != self.__startPoint:
+			# Green theorem is not defined on open contours.
+			raise NotImplementedError
+
+	def _lineTo(self, p1):
+		x0,y0 = self._getCurrentPoint()
+		x1,y1 = p1
+
+		r0 = x1*y0
+		r1 = x1*y1
+		r2 = x1**2
+		r3 = x0**2
+		r4 = 2*y0
+		r5 = y0 - y1
+		r6 = r5*x0
+		r7 = y0**2
+		r8 = y1**2
+		r9 = x1**3
+		r10 = r4*y1
+		r11 = y0**3
+		r12 = y1**3
+
+		self.area += -r0/2 - r1/2 + x0*(y0 + y1)/2
+		self.momentX += -r2*y0/6 - r2*y1/3 + r3*(r4 + y1)/6 - r6*x1/6
+		self.momentY += -r0*y1/6 - r7*x1/6 - r8*x1/6 + x0*(r7 + r8 + y0*y1)/6
+		self.momentXX += -r2*r6/12 - r3*r5*x1/12 - r9*y0/12 - r9*y1/4 + x0**3*(3*y0 + y1)/12
+		self.momentXY += -r10*r2/24 - r2*r7/24 - r2*r8/8 + r3*(r10 + 3*r7 + r8)/24 - x0*x1*(r7 - r8)/12
+		self.momentYY += -r0*r8/12 - r1*r7/12 - r11*x1/12 - r12*x1/12 + x0*(r11 + r12 + r7*y1 + r8*y0)/12
+
+	def _qCurveToOne(self, p1, p2):
+		x0,y0 = self._getCurrentPoint()
+		x1,y1 = p1
+		x2,y2 = p2
+
+		r0 = 2*x1
+		r1 = r0*y2
+		r2 = 2*y1
+		r3 = r2*x2
+		r4 = 3*y2
+		r5 = r4*x2
+		r6 = 3*y0
+		r7 = x1**2
+		r8 = 2*y2
+		r9 = x2**2
+		r10 = 4*y1
+		r11 = 10*y2
+		r12 = r0*x2
+		r13 = x0**2
+		r14 = 10*y0
+		r15 = x2*y2
+		r16 = r0*y1 + r15
+		r17 = 4*x1
+		r18 = x2*y0
+		r19 = r10*r15
+		r20 = y1**2
+		r21 = 2*r20
+		r22 = y2**2
+		r23 = r22*x2
+		r24 = 5*r23
+		r25 = y0**2
+		r26 = y0*y2
+		r27 = 5*r25
+		r28 = 8*x1**3
+		r29 = x2**3
+		r30 = 30*y1
+		r31 = 6*y1
+		r32 = 10*r9*x1
+		r33 = 4*r7
+		r34 = 5*y2
+		r35 = 12*r7
+		r36 = r5 + 20*x1*y1
+		r37 = 30*x1
+		r38 = 12*x1
+		r39 = 20*r7
+		r40 = 8*r7*y1
+		r41 = r34*r9
+		r42 = 60*y1
+		r43 = 20*r20
+		r44 = 4*r20
+		r45 = 15*r22
+		r46 = r38*x2
+		r47 = y1*y2
+		r48 = 8*r20*x1 + r24
+		r49 = 6*x1
+		r50 = 8*y1**3
+		r51 = y2**3
+		r52 = y0**3
+		r53 = 10*y1
+		r54 = 12*y1
+		r55 = 12*r20
+
+		self.area += r1/6 - r3/6 - r5/6 + x0*(r2 + r6 + y2)/6 - y0*(r0 + x2)/6
+		self.momentX += -r10*r9/30 - r11*r9/30 - r12*(-r8 + y1)/30 + r13*(r10 + r14 + y2)/30 + r7*r8/30 + x0*(r1 + r16 - r17*y0 - r18)/30 - y0*(r12 + 2*r7 + r9)/30
+		self.momentY += r1*(r8 + y1)/30 - r19/30 - r21*x2/30 - r24/30 - r25*(r17 + x2)/30 + x0*(r10*y0 + r2*y2 + r21 + r22 + r26 + r27)/30 - y0*(r16 + r3)/30
+		self.momentXX += r13*(r11*x1 - 5*r18 + r3 + r36 - r37*y0)/420 + r28*y2/420 - r29*r30/420 - r29*y2/4 - r32*(r2 - r4)/420 - r33*x2*(r2 - r34)/420 + x0**3*(r31 + 21*y0 + y2)/84 - x0*(-r15*r38 + r18*r38 + r2*r9 - r35*y2 + r39*y0 - r40 - r41 + r6*r9)/420 - y0*(r28 + 5*r29 + r32 + r35*x2)/420
+		self.momentXY += r13*(r14*y2 + 3*r22 + 105*r25 + r42*y0 + r43 + 12*r47)/840 - r17*x2*(r44 - r45)/840 - r22*r9/8 - r25*(r39 + r46 + 3*r9)/840 + r33*y2*(r10 + r34)/840 - r42*r9*y2/840 - r43*r9/840 + x0*(-r10*r18 + r17*r26 + r19 + r22*r49 - r25*r37 - r27*x2 + r38*r47 + r48)/420 - y0*(r15*r17 + r31*r9 + r40 + r41 + r46*y1)/420
+		self.momentYY += r1*(r11*y1 + r44 + r45)/420 - r15*r43/420 - r23*r30/420 - r25*(r1 + r36 + r53*x2)/420 - r50*x2/420 - r51*x2/12 - r52*(r49 + x2)/84 + x0*(r22*r53 + r22*r6 + r25*r30 + r25*r34 + r26*r54 + r43*y0 + r50 + 5*r51 + 35*r52 + r55*y2)/420 - y0*(-r0*r22 + r15*r54 + r48 + r55*x2)/420
+
+	def _curveToOne(self, p1, p2, p3):
+		x0,y0 = self._getCurrentPoint()
+		x1,y1 = p1
+		x2,y2 = p2
+		x3,y3 = p3
+
+		r0 = 6*x2
+		r1 = r0*y3
+		r2 = 6*y2
+		r3 = 10*y3
+		r4 = r3*x3
+		r5 = 3*x1
+		r6 = 3*y1
+		r7 = 6*x1
+		r8 = 3*x2
+		r9 = 6*y1
+		r10 = 3*y2
+		r11 = x2**2
+		r12 = r11*y3
+		r13 = 45*r12
+		r14 = x3**2
+		r15 = r14*y2
+		r16 = r14*y3
+		r17 = x2*x3
+		r18 = 15*r17
+		r19 = 7*y3
+		r20 = x1**2
+		r21 = 9*r20
+		r22 = x0**2
+		r23 = 21*y1
+		r24 = 9*r11
+		r25 = 9*x2
+		r26 = x2*y3
+		r27 = 15*r26
+		r28 = -r25*y1 + r27
+		r29 = r25*y2
+		r30 = r9*x3
+		r31 = 45*x1
+		r32 = x1*x3
+		r33 = 45*r20
+		r34 = 5*r14
+		r35 = x2*y2
+		r36 = 18*r35
+		r37 = 5*x3
+		r38 = r37*y3
+		r39 = r31*y1 + r36 + r38
+		r40 = x1*y0
+		r41 = x1*y3
+		r42 = x2*y0
+		r43 = x3*y1
+		r44 = r10*x3
+		r45 = x3*y2*y3
+		r46 = y2**2
+		r47 = 45*r46
+		r48 = r47*x3
+		r49 = y3**2
+		r50 = r49*x3
+		r51 = y1**2
+		r52 = 9*r51
+		r53 = y0**2
+		r54 = 21*x1
+		r55 = x3*y2
+		r56 = 15*r55
+		r57 = 9*y2
+		r58 = y2*y3
+		r59 = 15*r58
+		r60 = 9*r46
+		r61 = 3*y3
+		r62 = 45*y1
+		r63 = r8*y3
+		r64 = y0*y1
+		r65 = y0*y2
+		r66 = 30*r65
+		r67 = 5*y3
+		r68 = y1*y3
+		r69 = 45*r51
+		r70 = 5*r49
+		r71 = x2**3
+		r72 = x3**3
+		r73 = 126*x3
+		r74 = x1**3
+		r75 = r14*x2
+		r76 = 63*r11
+		r77 = r76*x3
+		r78 = 15*r35
+		r79 = r19*x3
+		r80 = x1*y1
+		r81 = 63*r35
+		r82 = r38 + 378*r80 + r81
+		r83 = x1*y2
+		r84 = x2*y1
+		r85 = x3*y0
+		r86 = x2*x3*y1
+		r87 = x2*x3*y3
+		r88 = r11*y2
+		r89 = 27*r88
+		r90 = 42*y3
+		r91 = r14*r90
+		r92 = 90*x1*x2
+		r93 = 189*x2
+		r94 = 30*x1*x3
+		r95 = 14*r16 + 126*r20*y1 + 45*r88 + r94*y2
+		r96 = x1*x2
+		r97 = 252*r96
+		r98 = x1*x2*y2
+		r99 = 42*r32
+		r100 = x1*x3*y1
+		r101 = 30*r17
+		r102 = 18*r17
+		r103 = 378*r20
+		r104 = 189*y2
+		r105 = r20*y3
+		r106 = r11*y1
+		r107 = r14*y1
+		r108 = 378*r46
+		r109 = 252*y2
+		r110 = y1*y2
+		r111 = x2*x3*y2
+		r112 = y0*y3
+		r113 = 378*r51
+		r114 = 63*r46
+		r115 = 27*x2
+		r116 = r115*r46 + 42*r50
+		r117 = x2*y1*y3
+		r118 = x3*y1*y2
+		r119 = r49*x2
+		r120 = r51*x3
+		r121 = x3*y3
+		r122 = 14*x3
+		r123 = 30*r117 + r122*r49 + r47*x2 + 126*r51*x1
+		r124 = x1*y1*y3
+		r125 = x1*y2*y3
+		r126 = x2*y1*y2
+		r127 = 54*y3
+		r128 = 21*r55
+		r129 = 630*r53
+		r130 = r46*x1
+		r131 = r49*x1
+		r132 = 126*r53
+		r133 = y2**3
+		r134 = y3**3
+		r135 = 630*r49
+		r136 = y1**3
+		r137 = y0**3
+		r138 = r114*y3 + r23*r49
+		r139 = r49*y2
+
+		self.area += r1/20 - r2*x3/20 - r4/20 + r5*(y2 + y3)/20 - r6*(x2 + x3)/20 + x0*(r10 + r9 + 10*y0 + y3)/20 - y0*(r7 + r8 + x3)/20
+		self.momentX += r13/840 - r15/8 - r16/3 - r18*(r10 - r19)/840 + r21*(r10 + 2*y3)/840 + r22*(r2 + r23 + 56*y0 + y3)/168 + r5*(r28 + r29 - r30 + r4)/840 - r6*(10*r14 + r18 + r24)/840 + x0*(12*r26 + r31*y2 - r37*y0 + r39 - 105*r40 + 15*r41 - 30*r42 - 3*r43 + r44)/840 - y0*(18*r11 + r18 + r31*x2 + 12*r32 + r33 + r34)/840
+		self.momentY += r27*(r10 + r19)/840 - r45/8 - r48/840 + r5*(10*r49 + r57*y1 + r59 + r60 + r9*y3)/840 - r50/6 - r52*(r8 + 2*x3)/840 - r53*(r0 + r54 + x3)/168 - r6*(r29 + r4 + r56)/840 + x0*(18*r46 + 140*r53 + r59 + r62*y2 + 105*r64 + r66 + r67*y0 + 12*r68 + r69 + r70)/840 - y0*(r39 + 15*r43 + 12*r55 - r61*x1 + r62*x2 + r63)/840
+		self.momentXX += -r11*r73*(-r61 + y2)/9240 + r21*(r28 - r37*y1 + r44 + r78 + r79)/9240 + r22*(21*r26 - 630*r40 + 42*r41 - 126*r42 + r57*x3 + r82 + 210*r83 + 42*r84 - 14*r85)/9240 - r5*(r11*r62 + r14*r23 + 14*r15 - r76*y3 + 54*r86 - 84*r87 - r89 - r91)/9240 - r6*(27*r71 + 42*r72 + 70*r75 + r77)/9240 + 3*r71*y3/220 - 3*r72*y2/44 - r72*y3/4 + 3*r74*(r57 + r67)/3080 - r75*(378*y2 - 630*y3)/9240 + x0**3*(r57 + r62 + 165*y0 + y3)/660 + x0*(-18*r100 - r101*y0 - r101*y1 + r102*y2 - r103*y0 + r104*r20 + 63*r105 - 27*r106 - 9*r107 + r13 - r34*y0 - r76*y0 + 42*r87 + r92*y3 + r94*y3 + r95 - r97*y0 + 162*r98 - r99*y0)/9240 - y0*(135*r11*x1 + r14*r54 + r20*r93 + r33*x3 + 45*r71 + 14*r72 + 126*r74 + 42*r75 + r77 + r92*x3)/9240
+		self.momentXY += -r108*r14/18480 + r12*(r109 + 378*y3)/18480 - r14*r49/8 - 3*r14*r58/44 - r17*(252*r46 - 1260*r49)/18480 + r21*(18*r110 + r3*y1 + 15*r46 + 7*r49 + 18*r58)/18480 + r22*(252*r110 + 28*r112 + r113 + r114 + 2310*r53 + 30*r58 + 1260*r64 + 252*r65 + 42*r68 + r70)/18480 - r52*(r102 + 15*r11 + 7*r14)/18480 - r53*(r101 + r103 + r34 + r76 + r97 + r99)/18480 + r7*(-r115*r51 + r116 + 18*r117 - 18*r118 + 42*r119 - 15*r120 + 28*r45 + r81*y3)/18480 - r9*(63*r111 + 42*r15 + 28*r87 + r89 + r91)/18480 + x0*(r1*y0 + r104*r80 + r112*r54 + 21*r119 - 9*r120 - r122*r53 + r123 + 54*r124 + 60*r125 + 54*r126 + r127*r35 + r128*y3 - r129*x1 + 81*r130 + 15*r131 - r132*x2 - r2*r85 - r23*r85 + r30*y3 + 84*r40*y2 - 84*r42*y1 + r60*x3)/9240 - y0*(54*r100 - 9*r105 + 81*r106 + 15*r107 + 54*r111 + r121*r7 + 21*r15 + r24*y3 + 60*r86 + 21*r87 + r95 + 189*r96*y1 + 54*r98)/9240
+		self.momentYY += -r108*r121/9240 - r133*r73/9240 - r134*x3/12 - r135*r55/9240 - 3*r136*(r25 + r37)/3080 - r137*(r25 + r31 + x3)/660 + r26*(r135 + 126*r46 + 378*y2*y3)/9240 + r5*(r110*r127 + 27*r133 + 42*r134 + r138 + 70*r139 + r46*r62 + 27*r51*y2 + 15*r51*y3)/9240 - r52*(r56 + r63 + r78 + r79)/9240 - r53*(r128 + r25*y3 + 42*r43 + r82 + 42*r83 + 210*r84)/9240 - r6*(r114*x3 + r116 - 14*r119 + 84*r45)/9240 + x0*(r104*r51 + r109*r64 + 90*r110*y3 + r113*y0 + r114*y0 + r129*y1 + r132*y2 + 45*r133 + 14*r134 + 126*r136 + 770*r137 + r138 + 42*r139 + 135*r46*y1 + 14*r53*y3 + r64*r90 + r66*y3 + r69*y3 + r70*y0)/9240 - y0*(90*r118 + 63*r120 + r123 - 18*r124 - 30*r125 + 162*r126 - 27*r130 - 9*r131 + r36*y3 + 30*r43*y3 + 42*r45 + r48 + r51*r93)/9240
+
+if __name__ == '__main__':
+	from fontTools.misc.symfont import x, y, printGreenPen
+	printGreenPen('MomentsPen', [
+		      ('area', 1),
+		      ('momentX', x),
+		      ('momentY', y),
+		      ('momentXX', x**2),
+		      ('momentXY', x*y),
+		      ('momentYY', y**2),
+		     ])
diff --git a/Lib/fontTools/pens/perimeterPen.py b/Lib/fontTools/pens/perimeterPen.py
new file mode 100644
index 0000000..12e7e47
--- /dev/null
+++ b/Lib/fontTools/pens/perimeterPen.py
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+"""Calculate the perimeter of a glyph."""
+
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.pens.basePen import BasePen
+from fontTools.misc.bezierTools import approximateQuadraticArcLengthC, calcQuadraticArcLengthC, approximateCubicArcLengthC, calcCubicArcLengthC
+import math
+
+
+__all__ = ["PerimeterPen"]
+
+
+def _distance(p0, p1):
+	return math.hypot(p0[0] - p1[0], p0[1] - p1[1])
+
+class PerimeterPen(BasePen):
+
+	def __init__(self, glyphset=None, tolerance=0.005):
+		BasePen.__init__(self, glyphset)
+		self.value = 0
+		self.tolerance = tolerance
+
+		# Choose which algorithm to use for quadratic and for cubic.
+		# Quadrature is faster but has fixed error characteristic with no strong
+		# error bound.  The cutoff points are derived empirically.
+		self._addCubic = self._addCubicQuadrature if tolerance >= 0.0015 else self._addCubicRecursive
+		self._addQuadratic = self._addQuadraticQuadrature if tolerance >= 0.00075 else self._addQuadraticExact
+
+	def _moveTo(self, p0):
+		self.__startPoint = p0
+
+	def _closePath(self):
+		p0 = self._getCurrentPoint()
+		if p0 != self.__startPoint:
+			self._lineTo(self.__startPoint)
+
+	def _lineTo(self, p1):
+		p0 = self._getCurrentPoint()
+		self.value += _distance(p0, p1)
+
+	def _addQuadraticExact(self, c0, c1, c2):
+		self.value += calcQuadraticArcLengthC(c0, c1, c2)
+
+	def _addQuadraticQuadrature(self, c0, c1, c2):
+		self.value += approximateQuadraticArcLengthC(c0, c1, c2)
+
+	def _qCurveToOne(self, p1, p2):
+		p0 = self._getCurrentPoint()
+		self._addQuadratic(complex(*p0), complex(*p1), complex(*p2))
+
+	def _addCubicRecursive(self, c0, c1, c2, c3):
+		self.value += calcCubicArcLengthC(c0, c1, c2, c3, self.tolerance)
+
+	def _addCubicQuadrature(self, c0, c1, c2, c3):
+		self.value += approximateCubicArcLengthC(c0, c1, c2, c3)
+
+	def _curveToOne(self, p1, p2, p3):
+		p0 = self._getCurrentPoint()
+		self._addCubic(complex(*p0), complex(*p1), complex(*p2), complex(*p3))
diff --git a/Lib/fontTools/pens/pointInsidePen.py b/Lib/fontTools/pens/pointInsidePen.py
index 0b3373f..3311841 100644
--- a/Lib/fontTools/pens/pointInsidePen.py
+++ b/Lib/fontTools/pens/pointInsidePen.py
@@ -11,12 +11,6 @@
 __all__ = ["PointInsidePen"]
 
 
-# working around floating point errors
-EPSILON = 1e-10
-ONE_PLUS_EPSILON = 1 + EPSILON
-ZERO_MINUS_EPSILON = 0 - EPSILON
-
-
 class PointInsidePen(BasePen):
 
 	"""This pen implements "point inside" testing: to test whether
@@ -46,29 +40,33 @@
 	#   http://graphics.cs.ucdavis.edu/~okreylos/TAship/Spring2000/PointInPolygon.html
 	# I extended the principles outlined on that page to curves.
 
-	def __init__(self, glyphSet, testPoint, evenOdd=0):
+	def __init__(self, glyphSet, testPoint, evenOdd=False):
 		BasePen.__init__(self, glyphSet)
 		self.setTestPoint(testPoint, evenOdd)
 
-	def setTestPoint(self, testPoint, evenOdd=0):
+	def setTestPoint(self, testPoint, evenOdd=False):
 		"""Set the point to test. Call this _before_ the outline gets drawn."""
 		self.testPoint = testPoint
 		self.evenOdd = evenOdd
 		self.firstPoint = None
 		self.intersectionCount = 0
 
-	def getResult(self):
-		"""After the shape has been drawn, getResult() returns True if the test
-		point lies within the (black) shape, and False if it doesn't.
-		"""
+	def getWinding(self):
 		if self.firstPoint is not None:
 			# always make sure the sub paths are closed; the algorithm only works
 			# for closed paths.
 			self.closePath()
+		return self.intersectionCount
+
+	def getResult(self):
+		"""After the shape has been drawn, getResult() returns True if the test
+		point lies within the (black) shape, and False if it doesn't.
+		"""
+		winding = self.getWinding()
 		if self.evenOdd:
-			result = self.intersectionCount % 2
-		else:
-			result = self.intersectionCount
+			result = winding % 2
+		else: # non-zero
+			result = self.intersectionCount != 0
 		return not not result
 
 	def _addIntersection(self, goingUp):
@@ -123,7 +121,7 @@
 		by = (y3 - y2) * 3.0 - cy
 		ay = y4 - dy - cy - by
 		solutions = sorted(solveCubic(ay, by, cy, dy - y))
-		solutions = [t for t in solutions if ZERO_MINUS_EPSILON <= t <= ONE_PLUS_EPSILON]
+		solutions = [t for t in solutions if -0. <= t <= 1.]
 		if not solutions:
 			return
 
@@ -142,29 +140,30 @@
 			t3 = t2 * t
 
 			direction = 3*ay*t2 + 2*by*t + cy
+			incomingGoingUp = outgoingGoingUp = direction > 0.0
 			if direction == 0.0:
 				direction = 6*ay*t + 2*by
+				outgoingGoingUp = direction > 0.0
+				incomingGoingUp = not outgoingGoingUp
 				if direction == 0.0:
 					direction = ay
-			goingUp = direction > 0.0
+					incomingGoingUp = outgoingGoingUp = direction > 0.0
 
 			xt = ax*t3 + bx*t2 + cx*t + dx
 			if xt < x:
-				above = goingUp
 				continue
 
-			if t == 0.0:
-				if not goingUp:
-					self._addIntersection(goingUp)
+			if t in (0.0, -0.0):
+				if not outgoingGoingUp:
+					self._addIntersection(outgoingGoingUp)
 			elif t == 1.0:
-				if not above:
-					self._addIntersection(goingUp)
+				if incomingGoingUp:
+					self._addIntersection(incomingGoingUp)
 			else:
-				if above != goingUp:
-					self._addIntersection(goingUp)
+				if incomingGoingUp == outgoingGoingUp:
+					self._addIntersection(outgoingGoingUp)
 				#else:
-				#   we're not really intersecting, merely touching the 'top'
-			above = goingUp
+				#   we're not really intersecting, merely touching
 
 	def _qCurveToOne_unfinished(self, bcp, point):
 		# XXX need to finish this, for now doing it through a cubic
@@ -181,11 +180,13 @@
 		solutions = [t for t in solutions if ZERO_MINUS_EPSILON <= t <= ONE_PLUS_EPSILON]
 		if not solutions:
 			return
-		XXX
+		# XXX
 
 	def _closePath(self):
 		if self._getCurrentPoint() != self.firstPoint:
 			self.lineTo(self.firstPoint)
 		self.firstPoint = None
 
-	_endPath = _closePath
+	def _endPath(self):
+		"""Insideness is not defined for open contours."""
+		raise NotImplementedError
diff --git a/Lib/fontTools/pens/qtPen.py b/Lib/fontTools/pens/qtPen.py
new file mode 100644
index 0000000..01cb37a
--- /dev/null
+++ b/Lib/fontTools/pens/qtPen.py
@@ -0,0 +1,31 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.pens.basePen import BasePen
+
+
+__all__ = ["QtPen"]
+
+
+class QtPen(BasePen):
+
+	def __init__(self, glyphSet, path=None):
+		BasePen.__init__(self, glyphSet)
+		if path is None:
+			from PyQt5.QtGui import QPainterPath
+			path = QPainterPath()
+		self.path = path
+
+	def _moveTo(self, p):
+		self.path.moveTo(*p)
+
+	def _lineTo(self, p):
+		self.path.lineTo(*p)
+
+	def _curveToOne(self, p1, p2, p3):
+		self.path.cubicTo(*p1+p2+p3)
+
+	def _qCurveToOne(self, p1, p2):
+		self.path.quadTo(*p1+p2)
+
+	def _closePath(self):
+		self.path.closeSubpath()
diff --git a/Lib/fontTools/pens/recordingPen.py b/Lib/fontTools/pens/recordingPen.py
new file mode 100644
index 0000000..e583c8f
--- /dev/null
+++ b/Lib/fontTools/pens/recordingPen.py
@@ -0,0 +1,101 @@
+"""Pen recording operations that can be accessed or replayed."""
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.pens.basePen import AbstractPen, DecomposingPen
+
+
+__all__ = ["replayRecording", "RecordingPen", "DecomposingRecordingPen"]
+
+
+def replayRecording(recording, pen):
+	"""Replay a recording, as produced by RecordingPen or DecomposingRecordingPen,
+	to a pen.
+
+	Note that recording does not have to be produced by those pens.
+	It can be any iterable of tuples of method name and tuple-of-arguments.
+	Likewise, pen can be any objects receiving those method calls.
+	"""
+	for operator,operands in recording:
+		getattr(pen, operator)(*operands)
+
+
+class RecordingPen(AbstractPen):
+	"""Pen recording operations that can be accessed or replayed.
+
+	The recording can be accessed as pen.value; or replayed using
+	pen.replay(otherPen).
+
+	Usage example:
+	==============
+	from fontTools.ttLib import TTFont
+	from fontTools.pens.recordingPen import RecordingPen
+
+	glyph_name = 'dollar'
+	font_path = 'MyFont.otf'
+
+	font = TTFont(font_path)
+	glyphset = font.getGlyphSet()
+	glyph = glyphset[glyph_name]
+
+	pen = RecordingPen()
+	glyph.draw(pen)
+	print(pen.value)
+	"""
+
+	def __init__(self):
+		self.value = []
+	def moveTo(self, p0):
+		self.value.append(('moveTo', (p0,)))
+	def lineTo(self, p1):
+		self.value.append(('lineTo', (p1,)))
+	def qCurveTo(self, *points):
+		self.value.append(('qCurveTo', points))
+	def curveTo(self, *points):
+		self.value.append(('curveTo', points))
+	def closePath(self):
+		self.value.append(('closePath', ()))
+	def endPath(self):
+		self.value.append(('endPath', ()))
+	def addComponent(self, glyphName, transformation):
+		self.value.append(('addComponent', (glyphName, transformation)))
+	def replay(self, pen):
+		replayRecording(self.value, pen)
+
+
+class DecomposingRecordingPen(DecomposingPen, RecordingPen):
+	""" Same as RecordingPen, except that it doesn't keep components
+	as references, but draws them decomposed as regular contours.
+
+	The constructor takes a single 'glyphSet' positional argument,
+	a dictionary of glyph objects (i.e. with a 'draw' method) keyed
+	by thir name.
+
+	>>> class SimpleGlyph(object):
+	...     def draw(self, pen):
+	...         pen.moveTo((0, 0))
+	...         pen.curveTo((1, 1), (2, 2), (3, 3))
+	...         pen.closePath()
+	>>> class CompositeGlyph(object):
+	...     def draw(self, pen):
+	...         pen.addComponent('a', (1, 0, 0, 1, -1, 1))
+	>>> glyphSet = {'a': SimpleGlyph(), 'b': CompositeGlyph()}
+	>>> for name, glyph in sorted(glyphSet.items()):
+	...     pen = DecomposingRecordingPen(glyphSet)
+	...     glyph.draw(pen)
+	...     print("{}: {}".format(name, pen.value))
+	a: [('moveTo', ((0, 0),)), ('curveTo', ((1, 1), (2, 2), (3, 3))), ('closePath', ())]
+	b: [('moveTo', ((-1, 1),)), ('curveTo', ((0, 2), (1, 3), (2, 4))), ('closePath', ())]
+	"""
+	# raises KeyError if base glyph is not found in glyphSet
+	skipMissingComponents = False
+
+
+if __name__ == "__main__":
+	from fontTools.pens.basePen import _TestPen
+	pen = RecordingPen()
+	pen.moveTo((0, 0))
+	pen.lineTo((0, 100))
+	pen.curveTo((50, 75), (60, 50), (50, 25))
+	pen.closePath()
+	from pprint import pprint
+	pprint(pen.value)
diff --git a/Lib/fontTools/pens/reportLabPen.py b/Lib/fontTools/pens/reportLabPen.py
index 60792f7..2068b25 100644
--- a/Lib/fontTools/pens/reportLabPen.py
+++ b/Lib/fontTools/pens/reportLabPen.py
@@ -4,6 +4,9 @@
 from reportlab.graphics.shapes import Path
 
 
+__all__ = ["ReportLabPen"]
+
+
 class ReportLabPen(BasePen):
 
 	"""A pen for drawing onto a reportlab.graphics.shapes.Path object."""
diff --git a/Lib/fontTools/pens/reverseContourPen.py b/Lib/fontTools/pens/reverseContourPen.py
new file mode 100644
index 0000000..27e54d7
--- /dev/null
+++ b/Lib/fontTools/pens/reverseContourPen.py
@@ -0,0 +1,97 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.arrayTools import pairwise
+from fontTools.pens.filterPen import ContourFilterPen
+
+
+__all__ = ["reversedContour", "ReverseContourPen"]
+
+
+class ReverseContourPen(ContourFilterPen):
+    """Filter pen that passes outline data to another pen, but reversing
+    the winding direction of all contours. Components are simply passed
+    through unchanged.
+
+    Closed contours are reversed in such a way that the first point remains
+    the first point.
+    """
+
+    def filterContour(self, contour):
+        return reversedContour(contour)
+
+
+def reversedContour(contour):
+    """ Generator that takes a list of pen's (operator, operands) tuples,
+    and yields them with the winding direction reversed.
+    """
+    if not contour:
+        return  # nothing to do, stop iteration
+
+    # valid contours must have at least a starting and ending command,
+    # can't have one without the other
+    assert len(contour) > 1, "invalid contour"
+
+    # the type of the last command determines if the contour is closed
+    contourType = contour.pop()[0]
+    assert contourType in ("endPath", "closePath")
+    closed = contourType == "closePath"
+
+    firstType, firstPts = contour.pop(0)
+    assert firstType in ("moveTo", "qCurveTo"), (
+        "invalid initial segment type: %r" % firstType)
+    firstOnCurve = firstPts[-1]
+    if firstType == "qCurveTo":
+        # special case for TrueType paths contaning only off-curve points
+        assert firstOnCurve is None, (
+            "off-curve only paths must end with 'None'")
+        assert not contour, (
+            "only one qCurveTo allowed per off-curve path")
+        firstPts = ((firstPts[0],) + tuple(reversed(firstPts[1:-1])) +
+                    (None,))
+
+    if not contour:
+        # contour contains only one segment, nothing to reverse
+        if firstType == "moveTo":
+            closed = False  # single-point paths can't be closed
+        else:
+            closed = True  # off-curve paths are closed by definition
+        yield firstType, firstPts
+    else:
+        lastType, lastPts = contour[-1]
+        lastOnCurve = lastPts[-1]
+        if closed:
+            # for closed paths, we keep the starting point
+            yield firstType, firstPts
+            if firstOnCurve != lastOnCurve:
+                # emit an implied line between the last and first points
+                yield "lineTo", (lastOnCurve,)
+                contour[-1] = (lastType,
+                               tuple(lastPts[:-1]) + (firstOnCurve,))
+
+            if len(contour) > 1:
+                secondType, secondPts = contour[0]
+            else:
+                # contour has only two points, the second and last are the same
+                secondType, secondPts = lastType, lastPts
+            # if a lineTo follows the initial moveTo, after reversing it
+            # will be implied by the closePath, so we don't emit one;
+            # unless the lineTo and moveTo overlap, in which case we keep the
+            # duplicate points
+            if secondType == "lineTo" and firstPts != secondPts:
+                del contour[0]
+                if contour:
+                    contour[-1] = (lastType,
+                                   tuple(lastPts[:-1]) + secondPts)
+        else:
+            # for open paths, the last point will become the first
+            yield firstType, (lastOnCurve,)
+            contour[-1] = (lastType, tuple(lastPts[:-1]) + (firstOnCurve,))
+
+        # we iterate over all segment pairs in reverse order, and yield
+        # each one with the off-curve points reversed (if any), and
+        # with the on-curve point of the following segment
+        for (curType, curPts), (_, nextPts) in pairwise(
+                contour, reverse=True):
+            yield curType, tuple(reversed(curPts[:-1])) + (nextPts[-1],)
+
+    yield "closePath" if closed else "endPath", ()
diff --git a/Lib/fontTools/pens/statisticsPen.py b/Lib/fontTools/pens/statisticsPen.py
new file mode 100644
index 0000000..7c2e85c
--- /dev/null
+++ b/Lib/fontTools/pens/statisticsPen.py
@@ -0,0 +1,102 @@
+"""Pen calculating area, center of mass, variance and standard-deviation,
+covariance and correlation, and slant, of glyph shapes."""
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+import math
+from fontTools.pens.momentsPen import MomentsPen
+
+__all__ = ["StatisticsPen"]
+
+
+class StatisticsPen(MomentsPen):
+
+	"""Pen calculating area, center of mass, variance and
+	standard-deviation, covariance and correlation, and slant,
+	of glyph shapes.
+
+	Note that all the calculated values are 'signed'. Ie. if the
+	glyph shape is self-intersecting, the values are not correct
+	(but well-defined). As such, area will be negative if contour
+	directions are clockwise.  Moreover, variance might be negative
+	if the shapes are self-intersecting in certain ways."""
+
+	def __init__(self, glyphset=None):
+		MomentsPen.__init__(self, glyphset=glyphset)
+		self.__zero()
+
+	def _closePath(self):
+		MomentsPen._closePath(self)
+		self.__update()
+
+	def __zero(self):
+		self.meanX = 0
+		self.meanY = 0
+		self.varianceX = 0
+		self.varianceY = 0
+		self.stddevX = 0
+		self.stddevY = 0
+		self.covariance = 0
+		self.correlation = 0
+		self.slant = 0
+
+	def __update(self):
+
+		area = self.area
+		if not area:
+			self.__zero()
+			return
+
+		# Center of mass
+		# https://en.wikipedia.org/wiki/Center_of_mass#A_continuous_volume
+		self.meanX = meanX = self.momentX / area
+		self.meanY = meanY = self.momentY / area
+
+		#  Var(X) = E[X^2] - E[X]^2
+		self.varianceX = varianceX = self.momentXX / area - meanX**2
+		self.varianceY = varianceY = self.momentYY / area - meanY**2
+
+		self.stddevX = stddevX = math.copysign(abs(varianceX)**.5, varianceX)
+		self.stddevY = stddevY = math.copysign(abs(varianceY)**.5, varianceY)
+
+		#  Covariance(X,Y) = ( E[X.Y] - E[X]E[Y] )
+		self.covariance = covariance = self.momentXY / area - meanX*meanY
+
+		#  Correlation(X,Y) = Covariance(X,Y) / ( stddev(X) * stddev(Y) )
+		# https://en.wikipedia.org/wiki/Pearson_product-moment_correlation_coefficient
+		correlation = covariance / (stddevX * stddevY)
+		self.correlation = correlation if abs(correlation) > 1e-3 else 0
+
+		slant = covariance / varianceY
+		self.slant = slant if abs(slant) > 1e-3 else 0
+
+
+def _test(glyphset, upem, glyphs):
+	from fontTools.pens.transformPen import TransformPen
+	from fontTools.misc.transform import Scale
+
+	print('upem', upem)
+
+	for glyph_name in glyphs:
+		print()
+		print("glyph:", glyph_name)
+		glyph = glyphset[glyph_name]
+		pen = StatisticsPen(glyphset=glyphset)
+		transformer = TransformPen(pen, Scale(1./upem))
+		glyph.draw(transformer)
+		for item in ['area', 'momentX', 'momentY', 'momentXX', 'momentYY', 'momentXY', 'meanX', 'meanY', 'varianceX', 'varianceY', 'stddevX', 'stddevY', 'covariance', 'correlation', 'slant']:
+			if item[0] == '_': continue
+			print ("%s: %g" % (item, getattr(pen, item)))
+
+def main(args):
+	if not args:
+		return
+	filename, glyphs = args[0], args[1:]
+	if not glyphs:
+		glyphs = ['e', 'o', 'I', 'slash', 'E', 'zero', 'eight', 'minus', 'equal']
+	from fontTools.ttLib import TTFont
+	font = TTFont(filename)
+	_test(font.getGlyphSet(), font['head'].unitsPerEm, glyphs)
+
+if __name__ == '__main__':
+	import sys
+	main(sys.argv[1:])
diff --git a/Lib/fontTools/pens/svgPathPen.py b/Lib/fontTools/pens/svgPathPen.py
new file mode 100644
index 0000000..17e4ccc
--- /dev/null
+++ b/Lib/fontTools/pens/svgPathPen.py
@@ -0,0 +1,178 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.pens.basePen import BasePen
+
+
+def pointToString(pt):
+    return " ".join([str(i) for i in pt])
+
+
+class SVGPathPen(BasePen):
+
+    def __init__(self, glyphSet):
+        BasePen.__init__(self, glyphSet)
+        self._commands = []
+        self._lastCommand = None
+        self._lastX = None
+        self._lastY = None
+
+    def _handleAnchor(self):
+        """
+        >>> pen = SVGPathPen(None)
+        >>> pen.moveTo((0, 0))
+        >>> pen.moveTo((10, 10))
+        >>> pen._commands
+        ['M10 10']
+        """
+        if self._lastCommand == "M":
+            self._commands.pop(-1)
+
+    def _moveTo(self, pt):
+        """
+        >>> pen = SVGPathPen(None)
+        >>> pen.moveTo((0, 0))
+        >>> pen._commands
+        ['M0 0']
+
+        >>> pen = SVGPathPen(None)
+        >>> pen.moveTo((10, 0))
+        >>> pen._commands
+        ['M10 0']
+
+        >>> pen = SVGPathPen(None)
+        >>> pen.moveTo((0, 10))
+        >>> pen._commands
+        ['M0 10']
+        """
+        self._handleAnchor()
+        t = "M%s" % (pointToString(pt))
+        self._commands.append(t)
+        self._lastCommand = "M"
+        self._lastX, self._lastY = pt
+
+    def _lineTo(self, pt):
+        """
+        # duplicate point
+        >>> pen = SVGPathPen(None)
+        >>> pen.moveTo((10, 10))
+        >>> pen.lineTo((10, 10))
+        >>> pen._commands
+        ['M10 10']
+
+        # vertical line
+        >>> pen = SVGPathPen(None)
+        >>> pen.moveTo((10, 10))
+        >>> pen.lineTo((10, 0))
+        >>> pen._commands
+        ['M10 10', 'V0']
+
+        # horizontal line
+        >>> pen = SVGPathPen(None)
+        >>> pen.moveTo((10, 10))
+        >>> pen.lineTo((0, 10))
+        >>> pen._commands
+        ['M10 10', 'H0']
+
+        # basic
+        >>> pen = SVGPathPen(None)
+        >>> pen.lineTo((70, 80))
+        >>> pen._commands
+        ['L70 80']
+
+        # basic following a moveto
+        >>> pen = SVGPathPen(None)
+        >>> pen.moveTo((0, 0))
+        >>> pen.lineTo((10, 10))
+        >>> pen._commands
+        ['M0 0', ' 10 10']
+        """
+        x, y = pt
+        # duplicate point
+        if x == self._lastX and y == self._lastY:
+            return
+        # vertical line
+        elif x == self._lastX:
+            cmd = "V"
+            pts = str(y)
+        # horizontal line
+        elif y == self._lastY:
+            cmd = "H"
+            pts = str(x)
+        # previous was a moveto
+        elif self._lastCommand == "M":
+            cmd = None
+            pts = " " + pointToString(pt)
+        # basic
+        else:
+            cmd = "L"
+            pts = pointToString(pt)
+        # write the string
+        t = ""
+        if cmd:
+            t += cmd
+            self._lastCommand = cmd
+        t += pts
+        self._commands.append(t)
+        # store for future reference
+        self._lastX, self._lastY = pt
+
+    def _curveToOne(self, pt1, pt2, pt3):
+        """
+        >>> pen = SVGPathPen(None)
+        >>> pen.curveTo((10, 20), (30, 40), (50, 60))
+        >>> pen._commands
+        ['C10 20 30 40 50 60']
+        """
+        t = "C"
+        t += pointToString(pt1) + " "
+        t += pointToString(pt2) + " "
+        t += pointToString(pt3)
+        self._commands.append(t)
+        self._lastCommand = "C"
+        self._lastX, self._lastY = pt3
+
+    def _qCurveToOne(self, pt1, pt2):
+        """
+        >>> pen = SVGPathPen(None)
+        >>> pen.qCurveTo((10, 20), (30, 40))
+        >>> pen._commands
+        ['Q10 20 30 40']
+        """
+        assert pt2 is not None
+        t = "Q"
+        t += pointToString(pt1) + " "
+        t += pointToString(pt2)
+        self._commands.append(t)
+        self._lastCommand = "Q"
+        self._lastX, self._lastY = pt2
+
+    def _closePath(self):
+        """
+        >>> pen = SVGPathPen(None)
+        >>> pen.closePath()
+        >>> pen._commands
+        ['Z']
+        """
+        self._commands.append("Z")
+        self._lastCommand = "Z"
+        self._lastX = self._lastY = None
+
+    def _endPath(self):
+        """
+        >>> pen = SVGPathPen(None)
+        >>> pen.endPath()
+        >>> pen._commands
+        ['Z']
+        """
+        self._closePath()
+        self._lastCommand = None
+        self._lastX = self._lastY = None
+
+    def getCommands(self):
+        return "".join(self._commands)
+
+
+if __name__ == "__main__":
+    import sys
+    import doctest
+    sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/pens/t2CharStringPen.py b/Lib/fontTools/pens/t2CharStringPen.py
new file mode 100644
index 0000000..8cc5e08
--- /dev/null
+++ b/Lib/fontTools/pens/t2CharStringPen.py
@@ -0,0 +1,91 @@
+# Copyright (c) 2009 Type Supply LLC
+# Author: Tal Leming
+
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.fixedTools import otRound
+from fontTools.misc.psCharStrings import T2CharString
+from fontTools.pens.basePen import BasePen
+from fontTools.cffLib.specializer import specializeCommands, commandsToProgram
+
+
+def makeRoundFunc(tolerance):
+    if tolerance < 0:
+        raise ValueError("Rounding tolerance must be positive")
+
+    def _round(number):
+        if tolerance == 0:
+            return number  # no-op
+        rounded = otRound(number)
+        # return rounded integer if the tolerance >= 0.5, or if the absolute
+        # difference between the original float and the rounded integer is
+        # within the tolerance
+        if tolerance >= .5 or abs(rounded - number) <= tolerance:
+            return rounded
+        else:
+            # else return the value un-rounded
+            return number
+
+    def roundPoint(point):
+        x, y = point
+        return _round(x), _round(y)
+
+    return roundPoint
+
+
+class T2CharStringPen(BasePen):
+    """Pen to draw Type 2 CharStrings.
+
+    The 'roundTolerance' argument controls the rounding of point coordinates.
+    It is defined as the maximum absolute difference between the original
+    float and the rounded integer value.
+    The default tolerance of 0.5 means that all floats are rounded to integer;
+    a value of 0 disables rounding; values in between will only round floats
+    which are close to their integral part within the tolerated range.
+    """
+
+    def __init__(self, width, glyphSet, roundTolerance=0.5, CFF2=False):
+        super(T2CharStringPen, self).__init__(glyphSet)
+        self.roundPoint = makeRoundFunc(roundTolerance)
+        self._CFF2 = CFF2
+        self._width = width
+        self._commands = []
+        self._p0 = (0,0)
+
+    def _p(self, pt):
+        p0 = self._p0
+        pt = self._p0 = self.roundPoint(pt)
+        return [pt[0]-p0[0], pt[1]-p0[1]]
+
+    def _moveTo(self, pt):
+        self._commands.append(('rmoveto', self._p(pt)))
+
+    def _lineTo(self, pt):
+        self._commands.append(('rlineto', self._p(pt)))
+
+    def _curveToOne(self, pt1, pt2, pt3):
+        _p = self._p
+        self._commands.append(('rrcurveto', _p(pt1)+_p(pt2)+_p(pt3)))
+
+    def _closePath(self):
+        pass
+
+    def _endPath(self):
+        pass
+
+    def getCharString(self, private=None, globalSubrs=None, optimize=True):
+        commands = self._commands
+        if optimize:
+            maxstack = 48 if not self._CFF2 else 513
+            commands = specializeCommands(commands,
+                                          generalizeFirst=False,
+                                          maxstack=maxstack)
+        program = commandsToProgram(commands)
+        if self._width is not None:
+            assert not self._CFF2, "CFF2 does not allow encoding glyph width in CharString."
+            program.insert(0, otRound(self._width))
+        if not self._CFF2:
+            program.append('endchar')
+        charString = T2CharString(
+            program=program, private=private, globalSubrs=globalSubrs)
+        return charString
diff --git a/Lib/fontTools/pens/teePen.py b/Lib/fontTools/pens/teePen.py
new file mode 100644
index 0000000..ce22c85
--- /dev/null
+++ b/Lib/fontTools/pens/teePen.py
@@ -0,0 +1,48 @@
+"""Pen multiplexing drawing to one or more pens."""
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.pens.basePen import AbstractPen
+
+
+__all__ = ["TeePen"]
+
+
+class TeePen(AbstractPen):
+	"""Pen multiplexing drawing to one or more pens.
+
+	Use either as TeePen(pen1, pen2, ...) or TeePen(iterableOfPens)."""
+
+	def __init__(self, *pens):
+		if len(pens) == 1:
+			pens = pens[0]
+		self.pens = pens
+	def moveTo(self, p0):
+		for pen in self.pens:
+			pen.moveTo(p0)
+	def lineTo(self, p1):
+		for pen in self.pens:
+			pen.lineTo(p1)
+	def qCurveTo(self, *points):
+		for pen in self.pens:
+			pen.qCurveTo(*points)
+	def curveTo(self, *points):
+		for pen in self.pens:
+			pen.curveTo(*points)
+	def closePath(self):
+		for pen in self.pens:
+			pen.closePath()
+	def endPath(self):
+		for pen in self.pens:
+			pen.endPath()
+	def addComponent(self, glyphName, transformation):
+		for pen in self.pens:
+			pen.addComponent(glyphName, transformation)
+
+
+if __name__ == "__main__":
+	from fontTools.pens.basePen import _TestPen
+	pen = TeePen(_TestPen(), _TestPen())
+	pen.moveTo((0, 0))
+	pen.lineTo((0, 100))
+	pen.curveTo((50, 75), (60, 50), (50, 25))
+	pen.closePath()
diff --git a/Lib/fontTools/pens/transformPen.py b/Lib/fontTools/pens/transformPen.py
index 9fca009..8f4fcd0 100644
--- a/Lib/fontTools/pens/transformPen.py
+++ b/Lib/fontTools/pens/transformPen.py
@@ -1,12 +1,12 @@
 from __future__ import print_function, division, absolute_import
 from fontTools.misc.py23 import *
-from fontTools.pens.basePen import AbstractPen
+from fontTools.pens.filterPen import FilterPen
 
 
 __all__ = ["TransformPen"]
 
 
-class TransformPen(AbstractPen):
+class TransformPen(FilterPen):
 
 	"""Pen that transforms all coordinates using a Affine transformation,
 	and passes them to another pen.
@@ -17,12 +17,12 @@
 		transformed coordinates. The 'transformation' argument can either
 		be a six-tuple, or a fontTools.misc.transform.Transform object.
 		"""
+		super(TransformPen, self).__init__(outPen)
 		if not hasattr(transformation, "transformPoint"):
 			from fontTools.misc.transform import Transform
 			transformation = Transform(*transformation)
 		self._transformation = transformation
 		self._transformPoint = transformation.transformPoint
-		self._outPen = outPen
 		self._stack = []
 
 	def moveTo(self, pt):
@@ -42,15 +42,15 @@
 		self._outPen.qCurveTo(*points)
 
 	def _transformPoints(self, points):
-		new = []
 		transformPoint = self._transformPoint
-		for pt in points:
-			new.append(transformPoint(pt))
-		return new
+		return [transformPoint(pt) for pt in points]
 
 	def closePath(self):
 		self._outPen.closePath()
 
+	def endPath(self):
+		self._outPen.endPath()
+
 	def addComponent(self, glyphName, transformation):
 		transformation = self._transformation.transform(transformation)
 		self._outPen.addComponent(glyphName, transformation)
diff --git a/Lib/fontTools/pens/ttGlyphPen.py b/Lib/fontTools/pens/ttGlyphPen.py
new file mode 100644
index 0000000..6e2f0d3
--- /dev/null
+++ b/Lib/fontTools/pens/ttGlyphPen.py
@@ -0,0 +1,158 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from array import array
+from fontTools.pens.basePen import LoggingPen
+from fontTools.pens.transformPen import TransformPen
+from fontTools.ttLib.tables import ttProgram
+from fontTools.ttLib.tables._g_l_y_f import Glyph
+from fontTools.ttLib.tables._g_l_y_f import GlyphComponent
+from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates
+
+
+__all__ = ["TTGlyphPen"]
+
+
+# the max value that can still fit in an F2Dot14:
+# 1.99993896484375
+MAX_F2DOT14 = 0x7FFF / (1 << 14)
+
+
+class TTGlyphPen(LoggingPen):
+    """Pen used for drawing to a TrueType glyph.
+
+    If `handleOverflowingTransforms` is True, the components' transform values
+    are checked that they don't overflow the limits of a F2Dot14 number:
+    -2.0 <= v < +2.0. If any transform value exceeds these, the composite
+    glyph is decomposed.
+    An exception to this rule is done for values that are very close to +2.0
+    (both for consistency with the -2.0 case, and for the relative frequency
+    these occur in real fonts). When almost +2.0 values occur (and all other
+    values are within the range -2.0 <= x <= +2.0), they are clamped to the
+    maximum positive value that can still be encoded as an F2Dot14: i.e.
+    1.99993896484375.
+    If False, no check is done and all components are translated unmodified
+    into the glyf table, followed by an inevitable `struct.error` once an
+    attempt is made to compile them.
+    """
+
+    def __init__(self, glyphSet, handleOverflowingTransforms=True):
+        self.glyphSet = glyphSet
+        self.handleOverflowingTransforms = handleOverflowingTransforms
+        self.init()
+
+    def init(self):
+        self.points = []
+        self.endPts = []
+        self.types = []
+        self.components = []
+
+    def _addPoint(self, pt, onCurve):
+        self.points.append(pt)
+        self.types.append(onCurve)
+
+    def _popPoint(self):
+        self.points.pop()
+        self.types.pop()
+
+    def _isClosed(self):
+        return (
+            (not self.points) or
+            (self.endPts and self.endPts[-1] == len(self.points) - 1))
+
+    def lineTo(self, pt):
+        self._addPoint(pt, 1)
+
+    def moveTo(self, pt):
+        assert self._isClosed(), '"move"-type point must begin a new contour.'
+        self._addPoint(pt, 1)
+
+    def qCurveTo(self, *points):
+        assert len(points) >= 1
+        for pt in points[:-1]:
+            self._addPoint(pt, 0)
+
+        # last point is None if there are no on-curve points
+        if points[-1] is not None:
+            self._addPoint(points[-1], 1)
+
+    def closePath(self):
+        endPt = len(self.points) - 1
+
+        # ignore anchors (one-point paths)
+        if endPt == 0 or (self.endPts and endPt == self.endPts[-1] + 1):
+            self._popPoint()
+            return
+
+        # if first and last point on this path are the same, remove last
+        startPt = 0
+        if self.endPts:
+            startPt = self.endPts[-1] + 1
+        if self.points[startPt] == self.points[endPt]:
+            self._popPoint()
+            endPt -= 1
+
+        self.endPts.append(endPt)
+
+    def endPath(self):
+        # TrueType contours are always "closed"
+        self.closePath()
+
+    def addComponent(self, glyphName, transformation):
+        self.components.append((glyphName, transformation))
+
+    def _buildComponents(self, componentFlags):
+        if self.handleOverflowingTransforms:
+            # we can't encode transform values > 2 or < -2 in F2Dot14,
+            # so we must decompose the glyph if any transform exceeds these
+            overflowing = any(s > 2 or s < -2
+                              for (glyphName, transformation) in self.components
+                              for s in transformation[:4])
+        components = []
+        for glyphName, transformation in self.components:
+            if glyphName not in self.glyphSet:
+                self.log.warning(
+                    "skipped non-existing component '%s'", glyphName
+                )
+                continue
+            if (self.points or
+                    (self.handleOverflowingTransforms and overflowing)):
+                # can't have both coordinates and components, so decompose
+                tpen = TransformPen(self, transformation)
+                self.glyphSet[glyphName].draw(tpen)
+                continue
+
+            component = GlyphComponent()
+            component.glyphName = glyphName
+            component.x, component.y = transformation[4:]
+            transformation = transformation[:4]
+            if transformation != (1, 0, 0, 1):
+                if (self.handleOverflowingTransforms and
+                        any(MAX_F2DOT14 < s <= 2 for s in transformation)):
+                    # clamp values ~= +2.0 so we can keep the component
+                    transformation = tuple(MAX_F2DOT14 if MAX_F2DOT14 < s <= 2
+                                           else s for s in transformation)
+                component.transform = (transformation[:2], transformation[2:])
+            component.flags = componentFlags
+            components.append(component)
+        return components
+
+    def glyph(self, componentFlags=0x4):
+        assert self._isClosed(), "Didn't close last contour."
+
+        components = self._buildComponents(componentFlags)
+
+        glyph = Glyph()
+        glyph.coordinates = GlyphCoordinates(self.points)
+        glyph.endPtsOfContours = self.endPts
+        glyph.flags = array("B", self.types)
+        self.init()
+
+        if components:
+            glyph.components = components
+            glyph.numberOfContours = -1
+        else:
+            glyph.numberOfContours = len(glyph.endPtsOfContours)
+            glyph.program = ttProgram.Program()
+            glyph.program.fromBytecode(b"")
+
+        return glyph
diff --git a/Lib/fontTools/pens/wxPen.py b/Lib/fontTools/pens/wxPen.py
new file mode 100644
index 0000000..8e0e463
--- /dev/null
+++ b/Lib/fontTools/pens/wxPen.py
@@ -0,0 +1,31 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.pens.basePen import BasePen
+
+
+__all__ = ["WxPen"]
+
+
+class WxPen(BasePen):
+
+	def __init__(self, glyphSet, path=None):
+		BasePen.__init__(self, glyphSet)
+		if path is None:
+			import wx
+			path = wx.GraphicsRenderer.GetDefaultRenderer().CreatePath()
+		self.path = path
+
+	def _moveTo(self, p):
+		self.path.MoveToPoint(*p)
+
+	def _lineTo(self, p):
+		self.path.AddLineToPoint(*p)
+
+	def _curveToOne(self, p1, p2, p3):
+		self.path.AddCurveToPoint(*p1+p2+p3)
+
+	def _qCurveToOne(self, p1, p2):
+		self.path.AddQuadCurveToPoint(*p1+p2)
+
+	def _closePath(self):
+		self.path.CloseSubpath()
diff --git a/Lib/fontTools/subset.py b/Lib/fontTools/subset.py
deleted file mode 100644
index 45bd457..0000000
--- a/Lib/fontTools/subset.py
+++ /dev/null
@@ -1,2251 +0,0 @@
-# Copyright 2013 Google, Inc. All Rights Reserved.
-#
-# Google Author(s): Behdad Esfahbod
-
-"""Python OpenType Layout Subsetter.
-
-Later grown into full OpenType subsetter, supporting all standard tables.
-"""
-
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
-from fontTools import ttLib
-from fontTools.ttLib.tables import otTables
-from fontTools.misc import psCharStrings
-from fontTools.pens import basePen
-import sys
-import struct
-import time
-import array
-
-
-def _add_method(*clazzes):
-  """Returns a decorator function that adds a new method to one or
-  more classes."""
-  def wrapper(method):
-    for clazz in clazzes:
-      assert clazz.__name__ != 'DefaultTable', 'Oops, table class not found.'
-      assert not hasattr(clazz, method.__name__), \
-          "Oops, class '%s' has method '%s'." % (clazz.__name__,
-                                                 method.__name__)
-      setattr(clazz, method.__name__, method)
-    return None
-  return wrapper
-
-def _uniq_sort(l):
-  return sorted(set(l))
-
-def _set_update(s, *others):
-  # Jython's set.update only takes one other argument.
-  # Emulate real set.update...
-  for other in others:
-    s.update(other)
-
-
-@_add_method(otTables.Coverage)
-def intersect(self, glyphs):
-  "Returns ascending list of matching coverage values."
-  return [i for i,g in enumerate(self.glyphs) if g in glyphs]
-
-@_add_method(otTables.Coverage)
-def intersect_glyphs(self, glyphs):
-  "Returns set of intersecting glyphs."
-  return set(g for g in self.glyphs if g in glyphs)
-
-@_add_method(otTables.Coverage)
-def subset(self, glyphs):
-  "Returns ascending list of remaining coverage values."
-  indices = self.intersect(glyphs)
-  self.glyphs = [g for g in self.glyphs if g in glyphs]
-  return indices
-
-@_add_method(otTables.Coverage)
-def remap(self, coverage_map):
-  "Remaps coverage."
-  self.glyphs = [self.glyphs[i] for i in coverage_map]
-
-@_add_method(otTables.ClassDef)
-def intersect(self, glyphs):
-  "Returns ascending list of matching class values."
-  return _uniq_sort(
-     ([0] if any(g not in self.classDefs for g in glyphs) else []) +
-      [v for g,v in self.classDefs.items() if g in glyphs])
-
-@_add_method(otTables.ClassDef)
-def intersect_class(self, glyphs, klass):
-  "Returns set of glyphs matching class."
-  if klass == 0:
-    return set(g for g in glyphs if g not in self.classDefs)
-  return set(g for g,v in self.classDefs.items()
-              if v == klass and g in glyphs)
-
-@_add_method(otTables.ClassDef)
-def subset(self, glyphs, remap=False):
-  "Returns ascending list of remaining classes."
-  self.classDefs = dict((g,v) for g,v in self.classDefs.items() if g in glyphs)
-  # Note: while class 0 has the special meaning of "not matched",
-  # if no glyph will ever /not match/, we can optimize class 0 out too.
-  indices = _uniq_sort(
-     ([0] if any(g not in self.classDefs for g in glyphs) else []) +
-      list(self.classDefs.values()))
-  if remap:
-    self.remap(indices)
-  return indices
-
-@_add_method(otTables.ClassDef)
-def remap(self, class_map):
-  "Remaps classes."
-  self.classDefs = dict((g,class_map.index(v))
-                         for g,v in self.classDefs.items())
-
-@_add_method(otTables.SingleSubst)
-def closure_glyphs(self, s, cur_glyphs=None):
-  if cur_glyphs is None: cur_glyphs = s.glyphs
-  s.glyphs.update(v for g,v in self.mapping.items() if g in cur_glyphs)
-
-@_add_method(otTables.SingleSubst)
-def subset_glyphs(self, s):
-  self.mapping = dict((g,v) for g,v in self.mapping.items()
-                      if g in s.glyphs and v in s.glyphs)
-  return bool(self.mapping)
-
-@_add_method(otTables.MultipleSubst)
-def closure_glyphs(self, s, cur_glyphs=None):
-  if cur_glyphs is None: cur_glyphs = s.glyphs
-  indices = self.Coverage.intersect(cur_glyphs)
-  _set_update(s.glyphs, *(self.Sequence[i].Substitute for i in indices))
-
-@_add_method(otTables.MultipleSubst)
-def subset_glyphs(self, s):
-  indices = self.Coverage.subset(s.glyphs)
-  self.Sequence = [self.Sequence[i] for i in indices]
-  # Now drop rules generating glyphs we don't want
-  indices = [i for i,seq in enumerate(self.Sequence)
-       if all(sub in s.glyphs for sub in seq.Substitute)]
-  self.Sequence = [self.Sequence[i] for i in indices]
-  self.Coverage.remap(indices)
-  self.SequenceCount = len(self.Sequence)
-  return bool(self.SequenceCount)
-
-@_add_method(otTables.AlternateSubst)
-def closure_glyphs(self, s, cur_glyphs=None):
-  if cur_glyphs is None: cur_glyphs = s.glyphs
-  _set_update(s.glyphs, *(vlist for g,vlist in self.alternates.items()
-                          if g in cur_glyphs))
-
-@_add_method(otTables.AlternateSubst)
-def subset_glyphs(self, s):
-  self.alternates = dict((g,vlist)
-                         for g,vlist in self.alternates.items()
-                         if g in s.glyphs and
-                            all(v in s.glyphs for v in vlist))
-  return bool(self.alternates)
-
-@_add_method(otTables.LigatureSubst)
-def closure_glyphs(self, s, cur_glyphs=None):
-  if cur_glyphs is None: cur_glyphs = s.glyphs
-  _set_update(s.glyphs, *([seq.LigGlyph for seq in seqs
-                           if all(c in s.glyphs for c in seq.Component)]
-                          for g,seqs in self.ligatures.items()
-                          if g in cur_glyphs))
-
-@_add_method(otTables.LigatureSubst)
-def subset_glyphs(self, s):
-  self.ligatures = dict((g,v) for g,v in self.ligatures.items()
-                        if g in s.glyphs)
-  self.ligatures = dict((g,[seq for seq in seqs
-                            if seq.LigGlyph in s.glyphs and
-                               all(c in s.glyphs for c in seq.Component)])
-                         for g,seqs in self.ligatures.items())
-  self.ligatures = dict((g,v) for g,v in self.ligatures.items() if v)
-  return bool(self.ligatures)
-
-@_add_method(otTables.ReverseChainSingleSubst)
-def closure_glyphs(self, s, cur_glyphs=None):
-  if cur_glyphs is None: cur_glyphs = s.glyphs
-  if self.Format == 1:
-    indices = self.Coverage.intersect(cur_glyphs)
-    if(not indices or
-        not all(c.intersect(s.glyphs)
-                 for c in self.LookAheadCoverage + self.BacktrackCoverage)):
-      return
-    s.glyphs.update(self.Substitute[i] for i in indices)
-  else:
-    assert 0, "unknown format: %s" % self.Format
-
-@_add_method(otTables.ReverseChainSingleSubst)
-def subset_glyphs(self, s):
-  if self.Format == 1:
-    indices = self.Coverage.subset(s.glyphs)
-    self.Substitute = [self.Substitute[i] for i in indices]
-    # Now drop rules generating glyphs we don't want
-    indices = [i for i,sub in enumerate(self.Substitute)
-         if sub in s.glyphs]
-    self.Substitute = [self.Substitute[i] for i in indices]
-    self.Coverage.remap(indices)
-    self.GlyphCount = len(self.Substitute)
-    return bool(self.GlyphCount and
-                 all(c.subset(s.glyphs)
-                      for c in self.LookAheadCoverage+self.BacktrackCoverage))
-  else:
-    assert 0, "unknown format: %s" % self.Format
-
-@_add_method(otTables.SinglePos)
-def subset_glyphs(self, s):
-  if self.Format == 1:
-    return len(self.Coverage.subset(s.glyphs))
-  elif self.Format == 2:
-    indices = self.Coverage.subset(s.glyphs)
-    self.Value = [self.Value[i] for i in indices]
-    self.ValueCount = len(self.Value)
-    return bool(self.ValueCount)
-  else:
-    assert 0, "unknown format: %s" % self.Format
-
-@_add_method(otTables.SinglePos)
-def prune_post_subset(self, options):
-  if not options.hinting:
-    # Drop device tables
-    self.ValueFormat &= ~0x00F0
-  return True
-
-@_add_method(otTables.PairPos)
-def subset_glyphs(self, s):
-  if self.Format == 1:
-    indices = self.Coverage.subset(s.glyphs)
-    self.PairSet = [self.PairSet[i] for i in indices]
-    for p in self.PairSet:
-      p.PairValueRecord = [r for r in p.PairValueRecord
-                           if r.SecondGlyph in s.glyphs]
-      p.PairValueCount = len(p.PairValueRecord)
-    # Remove empty pairsets
-    indices = [i for i,p in enumerate(self.PairSet) if p.PairValueCount]
-    self.Coverage.remap(indices)
-    self.PairSet = [self.PairSet[i] for i in indices]
-    self.PairSetCount = len(self.PairSet)
-    return bool(self.PairSetCount)
-  elif self.Format == 2:
-    class1_map = self.ClassDef1.subset(s.glyphs, remap=True)
-    class2_map = self.ClassDef2.subset(s.glyphs, remap=True)
-    self.Class1Record = [self.Class1Record[i] for i in class1_map]
-    for c in self.Class1Record:
-      c.Class2Record = [c.Class2Record[i] for i in class2_map]
-    self.Class1Count = len(class1_map)
-    self.Class2Count = len(class2_map)
-    return bool(self.Class1Count and
-                 self.Class2Count and
-                 self.Coverage.subset(s.glyphs))
-  else:
-    assert 0, "unknown format: %s" % self.Format
-
-@_add_method(otTables.PairPos)
-def prune_post_subset(self, options):
-  if not options.hinting:
-    # Drop device tables
-    self.ValueFormat1 &= ~0x00F0
-    self.ValueFormat2 &= ~0x00F0
-  return True
-
-@_add_method(otTables.CursivePos)
-def subset_glyphs(self, s):
-  if self.Format == 1:
-    indices = self.Coverage.subset(s.glyphs)
-    self.EntryExitRecord = [self.EntryExitRecord[i] for i in indices]
-    self.EntryExitCount = len(self.EntryExitRecord)
-    return bool(self.EntryExitCount)
-  else:
-    assert 0, "unknown format: %s" % self.Format
-
-@_add_method(otTables.Anchor)
-def prune_hints(self):
-  # Drop device tables / contour anchor point
-  self.ensureDecompiled()
-  self.Format = 1
-
-@_add_method(otTables.CursivePos)
-def prune_post_subset(self, options):
-  if not options.hinting:
-    for rec in self.EntryExitRecord:
-      if rec.EntryAnchor: rec.EntryAnchor.prune_hints()
-      if rec.ExitAnchor: rec.ExitAnchor.prune_hints()
-  return True
-
-@_add_method(otTables.MarkBasePos)
-def subset_glyphs(self, s):
-  if self.Format == 1:
-    mark_indices = self.MarkCoverage.subset(s.glyphs)
-    self.MarkArray.MarkRecord = [self.MarkArray.MarkRecord[i]
-                                 for i in mark_indices]
-    self.MarkArray.MarkCount = len(self.MarkArray.MarkRecord)
-    base_indices = self.BaseCoverage.subset(s.glyphs)
-    self.BaseArray.BaseRecord = [self.BaseArray.BaseRecord[i]
-                                 for i in base_indices]
-    self.BaseArray.BaseCount = len(self.BaseArray.BaseRecord)
-    # Prune empty classes
-    class_indices = _uniq_sort(v.Class for v in self.MarkArray.MarkRecord)
-    self.ClassCount = len(class_indices)
-    for m in self.MarkArray.MarkRecord:
-      m.Class = class_indices.index(m.Class)
-    for b in self.BaseArray.BaseRecord:
-      b.BaseAnchor = [b.BaseAnchor[i] for i in class_indices]
-    return bool(self.ClassCount and
-                 self.MarkArray.MarkCount and
-                 self.BaseArray.BaseCount)
-  else:
-    assert 0, "unknown format: %s" % self.Format
-
-@_add_method(otTables.MarkBasePos)
-def prune_post_subset(self, options):
-    if not options.hinting:
-      for m in self.MarkArray.MarkRecord:
-        if m.MarkAnchor:
-          m.MarkAnchor.prune_hints()
-      for b in self.BaseArray.BaseRecord:
-        for a in b.BaseAnchor:
-          if a:
-            a.prune_hints()
-    return True
-
-@_add_method(otTables.MarkLigPos)
-def subset_glyphs(self, s):
-  if self.Format == 1:
-    mark_indices = self.MarkCoverage.subset(s.glyphs)
-    self.MarkArray.MarkRecord = [self.MarkArray.MarkRecord[i]
-                                 for i in mark_indices]
-    self.MarkArray.MarkCount = len(self.MarkArray.MarkRecord)
-    ligature_indices = self.LigatureCoverage.subset(s.glyphs)
-    self.LigatureArray.LigatureAttach = [self.LigatureArray.LigatureAttach[i]
-                                         for i in ligature_indices]
-    self.LigatureArray.LigatureCount = len(self.LigatureArray.LigatureAttach)
-    # Prune empty classes
-    class_indices = _uniq_sort(v.Class for v in self.MarkArray.MarkRecord)
-    self.ClassCount = len(class_indices)
-    for m in self.MarkArray.MarkRecord:
-      m.Class = class_indices.index(m.Class)
-    for l in self.LigatureArray.LigatureAttach:
-      for c in l.ComponentRecord:
-        c.LigatureAnchor = [c.LigatureAnchor[i] for i in class_indices]
-    return bool(self.ClassCount and
-                 self.MarkArray.MarkCount and
-                 self.LigatureArray.LigatureCount)
-  else:
-    assert 0, "unknown format: %s" % self.Format
-
-@_add_method(otTables.MarkLigPos)
-def prune_post_subset(self, options):
-    if not options.hinting:
-      for m in self.MarkArray.MarkRecord:
-        if m.MarkAnchor:
-          m.MarkAnchor.prune_hints()
-      for l in self.LigatureArray.LigatureAttach:
-        for c in l.ComponentRecord:
-          for a in c.LigatureAnchor:
-            if a:
-              a.prune_hints()
-    return True
-
-@_add_method(otTables.MarkMarkPos)
-def subset_glyphs(self, s):
-  if self.Format == 1:
-    mark1_indices = self.Mark1Coverage.subset(s.glyphs)
-    self.Mark1Array.MarkRecord = [self.Mark1Array.MarkRecord[i]
-                                  for i in mark1_indices]
-    self.Mark1Array.MarkCount = len(self.Mark1Array.MarkRecord)
-    mark2_indices = self.Mark2Coverage.subset(s.glyphs)
-    self.Mark2Array.Mark2Record = [self.Mark2Array.Mark2Record[i]
-                                   for i in mark2_indices]
-    self.Mark2Array.MarkCount = len(self.Mark2Array.Mark2Record)
-    # Prune empty classes
-    class_indices = _uniq_sort(v.Class for v in self.Mark1Array.MarkRecord)
-    self.ClassCount = len(class_indices)
-    for m in self.Mark1Array.MarkRecord:
-      m.Class = class_indices.index(m.Class)
-    for b in self.Mark2Array.Mark2Record:
-      b.Mark2Anchor = [b.Mark2Anchor[i] for i in class_indices]
-    return bool(self.ClassCount and
-                 self.Mark1Array.MarkCount and
-                 self.Mark2Array.MarkCount)
-  else:
-    assert 0, "unknown format: %s" % self.Format
-
-@_add_method(otTables.MarkMarkPos)
-def prune_post_subset(self, options):
-    if not options.hinting:
-      # Drop device tables or contour anchor point
-      for m in self.Mark1Array.MarkRecord:
-        if m.MarkAnchor:
-          m.MarkAnchor.prune_hints()
-      for b in self.Mark2Array.Mark2Record:
-        for m in b.Mark2Anchor:
-          if m:
-            m.prune_hints()
-    return True
-
-@_add_method(otTables.SingleSubst,
-             otTables.MultipleSubst,
-             otTables.AlternateSubst,
-             otTables.LigatureSubst,
-             otTables.ReverseChainSingleSubst,
-             otTables.SinglePos,
-             otTables.PairPos,
-             otTables.CursivePos,
-             otTables.MarkBasePos,
-             otTables.MarkLigPos,
-             otTables.MarkMarkPos)
-def subset_lookups(self, lookup_indices):
-  pass
-
-@_add_method(otTables.SingleSubst,
-             otTables.MultipleSubst,
-             otTables.AlternateSubst,
-             otTables.LigatureSubst,
-             otTables.ReverseChainSingleSubst,
-             otTables.SinglePos,
-             otTables.PairPos,
-             otTables.CursivePos,
-             otTables.MarkBasePos,
-             otTables.MarkLigPos,
-             otTables.MarkMarkPos)
-def collect_lookups(self):
-  return []
-
-@_add_method(otTables.SingleSubst,
-             otTables.MultipleSubst,
-             otTables.AlternateSubst,
-             otTables.LigatureSubst,
-             otTables.ContextSubst,
-             otTables.ChainContextSubst,
-             otTables.ReverseChainSingleSubst,
-             otTables.SinglePos,
-             otTables.PairPos,
-             otTables.CursivePos,
-             otTables.MarkBasePos,
-             otTables.MarkLigPos,
-             otTables.MarkMarkPos,
-             otTables.ContextPos,
-             otTables.ChainContextPos)
-def prune_pre_subset(self, options):
-  return True
-
-@_add_method(otTables.SingleSubst,
-             otTables.MultipleSubst,
-             otTables.AlternateSubst,
-             otTables.LigatureSubst,
-             otTables.ReverseChainSingleSubst,
-             otTables.ContextSubst,
-             otTables.ChainContextSubst,
-             otTables.ContextPos,
-             otTables.ChainContextPos)
-def prune_post_subset(self, options):
-  return True
-
-@_add_method(otTables.SingleSubst,
-             otTables.AlternateSubst,
-             otTables.ReverseChainSingleSubst)
-def may_have_non_1to1(self):
-  return False
-
-@_add_method(otTables.MultipleSubst,
-             otTables.LigatureSubst,
-             otTables.ContextSubst,
-             otTables.ChainContextSubst)
-def may_have_non_1to1(self):
-  return True
-
-@_add_method(otTables.ContextSubst,
-             otTables.ChainContextSubst,
-             otTables.ContextPos,
-             otTables.ChainContextPos)
-def __classify_context(self):
-
-  class ContextHelper(object):
-    def __init__(self, klass, Format):
-      if klass.__name__.endswith('Subst'):
-        Typ = 'Sub'
-        Type = 'Subst'
-      else:
-        Typ = 'Pos'
-        Type = 'Pos'
-      if klass.__name__.startswith('Chain'):
-        Chain = 'Chain'
-      else:
-        Chain = ''
-      ChainTyp = Chain+Typ
-
-      self.Typ = Typ
-      self.Type = Type
-      self.Chain = Chain
-      self.ChainTyp = ChainTyp
-
-      self.LookupRecord = Type+'LookupRecord'
-
-      if Format == 1:
-        Coverage = lambda r: r.Coverage
-        ChainCoverage = lambda r: r.Coverage
-        ContextData = lambda r:(None,)
-        ChainContextData = lambda r:(None, None, None)
-        RuleData = lambda r:(r.Input,)
-        ChainRuleData = lambda r:(r.Backtrack, r.Input, r.LookAhead)
-        SetRuleData = None
-        ChainSetRuleData = None
-      elif Format == 2:
-        Coverage = lambda r: r.Coverage
-        ChainCoverage = lambda r: r.Coverage
-        ContextData = lambda r:(r.ClassDef,)
-        ChainContextData = lambda r:(r.LookAheadClassDef,
-                                      r.InputClassDef,
-                                      r.BacktrackClassDef)
-        RuleData = lambda r:(r.Class,)
-        ChainRuleData = lambda r:(r.LookAhead, r.Input, r.Backtrack)
-        def SetRuleData(r, d):(r.Class,) = d
-        def ChainSetRuleData(r, d):(r.LookAhead, r.Input, r.Backtrack) = d
-      elif Format == 3:
-        Coverage = lambda r: r.Coverage[0]
-        ChainCoverage = lambda r: r.InputCoverage[0]
-        ContextData = None
-        ChainContextData = None
-        RuleData = lambda r: r.Coverage
-        ChainRuleData = lambda r:(r.LookAheadCoverage +
-                                   r.InputCoverage +
-                                   r.BacktrackCoverage)
-        SetRuleData = None
-        ChainSetRuleData = None
-      else:
-        assert 0, "unknown format: %s" % Format
-
-      if Chain:
-        self.Coverage = ChainCoverage
-        self.ContextData = ChainContextData
-        self.RuleData = ChainRuleData
-        self.SetRuleData = ChainSetRuleData
-      else:
-        self.Coverage = Coverage
-        self.ContextData = ContextData
-        self.RuleData = RuleData
-        self.SetRuleData = SetRuleData
-
-      if Format == 1:
-        self.Rule = ChainTyp+'Rule'
-        self.RuleCount = ChainTyp+'RuleCount'
-        self.RuleSet = ChainTyp+'RuleSet'
-        self.RuleSetCount = ChainTyp+'RuleSetCount'
-        self.Intersect = lambda glyphs, c, r: [r] if r in glyphs else []
-      elif Format == 2:
-        self.Rule = ChainTyp+'ClassRule'
-        self.RuleCount = ChainTyp+'ClassRuleCount'
-        self.RuleSet = ChainTyp+'ClassSet'
-        self.RuleSetCount = ChainTyp+'ClassSetCount'
-        self.Intersect = lambda glyphs, c, r: c.intersect_class(glyphs, r)
-
-        self.ClassDef = 'InputClassDef' if Chain else 'ClassDef'
-        self.ClassDefIndex = 1 if Chain else 0
-        self.Input = 'Input' if Chain else 'Class'
-
-  if self.Format not in [1, 2, 3]:
-    return None  # Don't shoot the messenger; let it go
-  if not hasattr(self.__class__, "__ContextHelpers"):
-    self.__class__.__ContextHelpers = {}
-  if self.Format not in self.__class__.__ContextHelpers:
-    helper = ContextHelper(self.__class__, self.Format)
-    self.__class__.__ContextHelpers[self.Format] = helper
-  return self.__class__.__ContextHelpers[self.Format]
-
-@_add_method(otTables.ContextSubst,
-             otTables.ChainContextSubst)
-def closure_glyphs(self, s, cur_glyphs=None):
-  if cur_glyphs is None: cur_glyphs = s.glyphs
-  c = self.__classify_context()
-
-  indices = c.Coverage(self).intersect(s.glyphs)
-  if not indices:
-    return []
-  cur_glyphs = c.Coverage(self).intersect_glyphs(s.glyphs);
-
-  if self.Format == 1:
-    ContextData = c.ContextData(self)
-    rss = getattr(self, c.RuleSet)
-    rssCount = getattr(self, c.RuleSetCount)
-    for i in indices:
-      if i >= rssCount or not rss[i]: continue
-      for r in getattr(rss[i], c.Rule):
-        if not r: continue
-        if all(all(c.Intersect(s.glyphs, cd, k) for k in klist)
-          for cd,klist in zip(ContextData, c.RuleData(r))):
-          chaos = False
-          for ll in getattr(r, c.LookupRecord):
-            if not ll: continue
-            seqi = ll.SequenceIndex
-            if chaos:
-              pos_glyphs = s.glyphs
-            else:
-              if seqi == 0:
-                pos_glyphs = set([c.Coverage(self).glyphs[i]])
-              else:
-                pos_glyphs = set([r.Input[seqi - 1]])
-            lookup = s.table.LookupList.Lookup[ll.LookupListIndex]
-            chaos = chaos or lookup.may_have_non_1to1()
-            lookup.closure_glyphs(s, cur_glyphs=pos_glyphs)
-  elif self.Format == 2:
-    ClassDef = getattr(self, c.ClassDef)
-    indices = ClassDef.intersect(cur_glyphs)
-    ContextData = c.ContextData(self)
-    rss = getattr(self, c.RuleSet)
-    rssCount = getattr(self, c.RuleSetCount)
-    for i in indices:
-      if i >= rssCount or not rss[i]: continue
-      for r in getattr(rss[i], c.Rule):
-        if not r: continue
-        if all(all(c.Intersect(s.glyphs, cd, k) for k in klist)
-          for cd,klist in zip(ContextData, c.RuleData(r))):
-          chaos = False
-          for ll in getattr(r, c.LookupRecord):
-            if not ll: continue
-            seqi = ll.SequenceIndex
-            if chaos:
-              pos_glyphs = s.glyphs
-            else:
-              if seqi == 0:
-                pos_glyphs = ClassDef.intersect_class(cur_glyphs, i)
-              else:
-                pos_glyphs = ClassDef.intersect_class(s.glyphs,
-                                                      getattr(r, c.Input)[seqi - 1])
-            lookup = s.table.LookupList.Lookup[ll.LookupListIndex]
-            chaos = chaos or lookup.may_have_non_1to1()
-            lookup.closure_glyphs(s, cur_glyphs=pos_glyphs)
-  elif self.Format == 3:
-    if not all(x.intersect(s.glyphs) for x in c.RuleData(self)):
-      return []
-    r = self
-    chaos = False
-    for ll in getattr(r, c.LookupRecord):
-      if not ll: continue
-      seqi = ll.SequenceIndex
-      if chaos:
-        pos_glyphs = s.glyphs
-      else:
-        if seqi == 0:
-          pos_glyphs = cur_glyphs
-        else:
-          pos_glyphs = r.InputCoverage[seqi].intersect_glyphs(s.glyphs)
-      lookup = s.table.LookupList.Lookup[ll.LookupListIndex]
-      chaos = chaos or lookup.may_have_non_1to1()
-      lookup.closure_glyphs(s, cur_glyphs=pos_glyphs)
-  else:
-    assert 0, "unknown format: %s" % self.Format
-
-@_add_method(otTables.ContextSubst,
-             otTables.ContextPos,
-             otTables.ChainContextSubst,
-             otTables.ChainContextPos)
-def subset_glyphs(self, s):
-  c = self.__classify_context()
-
-  if self.Format == 1:
-    indices = self.Coverage.subset(s.glyphs)
-    rss = getattr(self, c.RuleSet)
-    rss = [rss[i] for i in indices]
-    for rs in rss:
-      if not rs: continue
-      ss = getattr(rs, c.Rule)
-      ss = [r for r in ss
-            if r and all(all(g in s.glyphs for g in glist)
-              for glist in c.RuleData(r))]
-      setattr(rs, c.Rule, ss)
-      setattr(rs, c.RuleCount, len(ss))
-    # Prune empty subrulesets
-    rss = [rs for rs in rss if rs and getattr(rs, c.Rule)]
-    setattr(self, c.RuleSet, rss)
-    setattr(self, c.RuleSetCount, len(rss))
-    return bool(rss)
-  elif self.Format == 2:
-    if not self.Coverage.subset(s.glyphs):
-      return False
-    ContextData = c.ContextData(self)
-    klass_maps = [x.subset(s.glyphs, remap=True) for x in ContextData]
-
-    # Keep rulesets for class numbers that survived.
-    indices = klass_maps[c.ClassDefIndex]
-    rss = getattr(self, c.RuleSet)
-    rssCount = getattr(self, c.RuleSetCount)
-    rss = [rss[i] for i in indices if i < rssCount]
-    del rssCount
-    # Delete, but not renumber, unreachable rulesets.
-    indices = getattr(self, c.ClassDef).intersect(self.Coverage.glyphs)
-    rss = [rss if i in indices else None for i,rss in enumerate(rss)]
-    while rss and rss[-1] is None:
-      del rss[-1]
-
-    for rs in rss:
-      if not rs: continue
-      ss = getattr(rs, c.Rule)
-      ss = [r for r in ss
-            if r and all(all(k in klass_map for k in klist)
-              for klass_map,klist in zip(klass_maps, c.RuleData(r)))]
-      setattr(rs, c.Rule, ss)
-      setattr(rs, c.RuleCount, len(ss))
-
-      # Remap rule classes
-      for r in ss:
-        c.SetRuleData(r, [[klass_map.index(k) for k in klist]
-               for klass_map,klist in zip(klass_maps, c.RuleData(r))])
-    return bool(rss)
-  elif self.Format == 3:
-    return all(x.subset(s.glyphs) for x in c.RuleData(self))
-  else:
-    assert 0, "unknown format: %s" % self.Format
-
-@_add_method(otTables.ContextSubst,
-             otTables.ChainContextSubst,
-             otTables.ContextPos,
-             otTables.ChainContextPos)
-def subset_lookups(self, lookup_indices):
-  c = self.__classify_context()
-
-  if self.Format in [1, 2]:
-    for rs in getattr(self, c.RuleSet):
-      if not rs: continue
-      for r in getattr(rs, c.Rule):
-        if not r: continue
-        setattr(r, c.LookupRecord,
-                 [ll for ll in getattr(r, c.LookupRecord)
-                  if ll and ll.LookupListIndex in lookup_indices])
-        for ll in getattr(r, c.LookupRecord):
-          if not ll: continue
-          ll.LookupListIndex = lookup_indices.index(ll.LookupListIndex)
-  elif self.Format == 3:
-    setattr(self, c.LookupRecord,
-             [ll for ll in getattr(self, c.LookupRecord)
-              if ll and ll.LookupListIndex in lookup_indices])
-    for ll in getattr(self, c.LookupRecord):
-      if not ll: continue
-      ll.LookupListIndex = lookup_indices.index(ll.LookupListIndex)
-  else:
-    assert 0, "unknown format: %s" % self.Format
-
-@_add_method(otTables.ContextSubst,
-             otTables.ChainContextSubst,
-             otTables.ContextPos,
-             otTables.ChainContextPos)
-def collect_lookups(self):
-  c = self.__classify_context()
-
-  if self.Format in [1, 2]:
-    return [ll.LookupListIndex
-      for rs in getattr(self, c.RuleSet) if rs
-      for r in getattr(rs, c.Rule) if r
-      for ll in getattr(r, c.LookupRecord) if ll]
-  elif self.Format == 3:
-    return [ll.LookupListIndex
-      for ll in getattr(self, c.LookupRecord) if ll]
-  else:
-    assert 0, "unknown format: %s" % self.Format
-
-@_add_method(otTables.ExtensionSubst)
-def closure_glyphs(self, s, cur_glyphs=None):
-  if self.Format == 1:
-    self.ExtSubTable.closure_glyphs(s, cur_glyphs)
-  else:
-    assert 0, "unknown format: %s" % self.Format
-
-@_add_method(otTables.ExtensionSubst)
-def may_have_non_1to1(self):
-  if self.Format == 1:
-    return self.ExtSubTable.may_have_non_1to1()
-  else:
-    assert 0, "unknown format: %s" % self.Format
-
-@_add_method(otTables.ExtensionSubst,
-             otTables.ExtensionPos)
-def prune_pre_subset(self, options):
-  if self.Format == 1:
-    return self.ExtSubTable.prune_pre_subset(options)
-  else:
-    assert 0, "unknown format: %s" % self.Format
-
-@_add_method(otTables.ExtensionSubst,
-             otTables.ExtensionPos)
-def subset_glyphs(self, s):
-  if self.Format == 1:
-    return self.ExtSubTable.subset_glyphs(s)
-  else:
-    assert 0, "unknown format: %s" % self.Format
-
-@_add_method(otTables.ExtensionSubst,
-             otTables.ExtensionPos)
-def prune_post_subset(self, options):
-  if self.Format == 1:
-    return self.ExtSubTable.prune_post_subset(options)
-  else:
-    assert 0, "unknown format: %s" % self.Format
-
-@_add_method(otTables.ExtensionSubst,
-             otTables.ExtensionPos)
-def subset_lookups(self, lookup_indices):
-  if self.Format == 1:
-    return self.ExtSubTable.subset_lookups(lookup_indices)
-  else:
-    assert 0, "unknown format: %s" % self.Format
-
-@_add_method(otTables.ExtensionSubst,
-             otTables.ExtensionPos)
-def collect_lookups(self):
-  if self.Format == 1:
-    return self.ExtSubTable.collect_lookups()
-  else:
-    assert 0, "unknown format: %s" % self.Format
-
-@_add_method(otTables.Lookup)
-def closure_glyphs(self, s, cur_glyphs=None):
-  for st in self.SubTable:
-    if not st: continue
-    st.closure_glyphs(s, cur_glyphs)
-
-@_add_method(otTables.Lookup)
-def prune_pre_subset(self, options):
-  ret = False
-  for st in self.SubTable:
-    if not st: continue
-    if st.prune_pre_subset(options): ret = True
-  return ret
-
-@_add_method(otTables.Lookup)
-def subset_glyphs(self, s):
-  self.SubTable = [st for st in self.SubTable if st and st.subset_glyphs(s)]
-  self.SubTableCount = len(self.SubTable)
-  return bool(self.SubTableCount)
-
-@_add_method(otTables.Lookup)
-def prune_post_subset(self, options):
-  ret = False
-  for st in self.SubTable:
-    if not st: continue
-    if st.prune_post_subset(options): ret = True
-  return ret
-
-@_add_method(otTables.Lookup)
-def subset_lookups(self, lookup_indices):
-  for s in self.SubTable:
-    s.subset_lookups(lookup_indices)
-
-@_add_method(otTables.Lookup)
-def collect_lookups(self):
-  return _uniq_sort(sum((st.collect_lookups() for st in self.SubTable
-                         if st), []))
-
-@_add_method(otTables.Lookup)
-def may_have_non_1to1(self):
-  return any(st.may_have_non_1to1() for st in self.SubTable if st)
-
-@_add_method(otTables.LookupList)
-def prune_pre_subset(self, options):
-  ret = False
-  for l in self.Lookup:
-    if not l: continue
-    if l.prune_pre_subset(options): ret = True
-  return ret
-
-@_add_method(otTables.LookupList)
-def subset_glyphs(self, s):
-  "Returns the indices of nonempty lookups."
-  return [i for i,l in enumerate(self.Lookup) if l and l.subset_glyphs(s)]
-
-@_add_method(otTables.LookupList)
-def prune_post_subset(self, options):
-  ret = False
-  for l in self.Lookup:
-    if not l: continue
-    if l.prune_post_subset(options): ret = True
-  return ret
-
-@_add_method(otTables.LookupList)
-def subset_lookups(self, lookup_indices):
-  self.ensureDecompiled()
-  self.Lookup = [self.Lookup[i] for i in lookup_indices
-                 if i < self.LookupCount]
-  self.LookupCount = len(self.Lookup)
-  for l in self.Lookup:
-    l.subset_lookups(lookup_indices)
-
-@_add_method(otTables.LookupList)
-def closure_lookups(self, lookup_indices):
-  lookup_indices = _uniq_sort(lookup_indices)
-  recurse = lookup_indices
-  while True:
-    recurse_lookups = sum((self.Lookup[i].collect_lookups()
-                            for i in recurse if i < self.LookupCount), [])
-    recurse_lookups = [l for l in recurse_lookups
-                       if l not in lookup_indices and l < self.LookupCount]
-    if not recurse_lookups:
-      return _uniq_sort(lookup_indices)
-    recurse_lookups = _uniq_sort(recurse_lookups)
-    lookup_indices.extend(recurse_lookups)
-    recurse = recurse_lookups
-
-@_add_method(otTables.Feature)
-def subset_lookups(self, lookup_indices):
-  self.LookupListIndex = [l for l in self.LookupListIndex
-                          if l in lookup_indices]
-  # Now map them.
-  self.LookupListIndex = [lookup_indices.index(l)
-                          for l in self.LookupListIndex]
-  self.LookupCount = len(self.LookupListIndex)
-  return self.LookupCount or self.FeatureParams
-
-@_add_method(otTables.Feature)
-def collect_lookups(self):
-  return self.LookupListIndex[:]
-
-@_add_method(otTables.FeatureList)
-def subset_lookups(self, lookup_indices):
-  "Returns the indices of nonempty features."
-  # Note: Never ever drop feature 'pref', even if it's empty.
-  # HarfBuzz chooses shaper for Khmer based on presence of this
-  # feature.  See thread at:
-  # http://lists.freedesktop.org/archives/harfbuzz/2012-November/002660.html
-  feature_indices = [i for i,f in enumerate(self.FeatureRecord)
-                     if (f.Feature.subset_lookups(lookup_indices) or
-                         f.FeatureTag == 'pref')]
-  self.subset_features(feature_indices)
-  return feature_indices
-
-@_add_method(otTables.FeatureList)
-def collect_lookups(self, feature_indices):
-  return _uniq_sort(sum((self.FeatureRecord[i].Feature.collect_lookups()
-                         for i in feature_indices
-                          if i < self.FeatureCount), []))
-
-@_add_method(otTables.FeatureList)
-def subset_features(self, feature_indices):
-  self.ensureDecompiled()
-  self.FeatureRecord = [self.FeatureRecord[i] for i in feature_indices]
-  self.FeatureCount = len(self.FeatureRecord)
-  return bool(self.FeatureCount)
-
-@_add_method(otTables.DefaultLangSys,
-             otTables.LangSys)
-def subset_features(self, feature_indices):
-  if self.ReqFeatureIndex in feature_indices:
-    self.ReqFeatureIndex = feature_indices.index(self.ReqFeatureIndex)
-  else:
-    self.ReqFeatureIndex = 65535
-  self.FeatureIndex = [f for f in self.FeatureIndex if f in feature_indices]
-  # Now map them.
-  self.FeatureIndex = [feature_indices.index(f) for f in self.FeatureIndex
-                       if f in feature_indices]
-  self.FeatureCount = len(self.FeatureIndex)
-  return bool(self.FeatureCount or self.ReqFeatureIndex != 65535)
-
-@_add_method(otTables.DefaultLangSys,
-             otTables.LangSys)
-def collect_features(self):
-  feature_indices = self.FeatureIndex[:]
-  if self.ReqFeatureIndex != 65535:
-    feature_indices.append(self.ReqFeatureIndex)
-  return _uniq_sort(feature_indices)
-
-@_add_method(otTables.Script)
-def subset_features(self, feature_indices):
-  if(self.DefaultLangSys and
-      not self.DefaultLangSys.subset_features(feature_indices)):
-    self.DefaultLangSys = None
-  self.LangSysRecord = [l for l in self.LangSysRecord
-                        if l.LangSys.subset_features(feature_indices)]
-  self.LangSysCount = len(self.LangSysRecord)
-  return bool(self.LangSysCount or self.DefaultLangSys)
-
-@_add_method(otTables.Script)
-def collect_features(self):
-  feature_indices = [l.LangSys.collect_features() for l in self.LangSysRecord]
-  if self.DefaultLangSys:
-    feature_indices.append(self.DefaultLangSys.collect_features())
-  return _uniq_sort(sum(feature_indices, []))
-
-@_add_method(otTables.ScriptList)
-def subset_features(self, feature_indices):
-  self.ScriptRecord = [s for s in self.ScriptRecord
-                       if s.Script.subset_features(feature_indices)]
-  self.ScriptCount = len(self.ScriptRecord)
-  return bool(self.ScriptCount)
-
-@_add_method(otTables.ScriptList)
-def collect_features(self):
-  return _uniq_sort(sum((s.Script.collect_features()
-                         for s in self.ScriptRecord), []))
-
-@_add_method(ttLib.getTableClass('GSUB'))
-def closure_glyphs(self, s):
-  s.table = self.table
-  if self.table.ScriptList:
-    feature_indices = self.table.ScriptList.collect_features()
-  else:
-    feature_indices = []
-  if self.table.FeatureList:
-    lookup_indices = self.table.FeatureList.collect_lookups(feature_indices)
-  else:
-    lookup_indices = []
-  if self.table.LookupList:
-    while True:
-      orig_glyphs = s.glyphs.copy()
-      for i in lookup_indices:
-        if i >= self.table.LookupList.LookupCount: continue
-        if not self.table.LookupList.Lookup[i]: continue
-        self.table.LookupList.Lookup[i].closure_glyphs(s)
-      if orig_glyphs == s.glyphs:
-        break
-  del s.table
-
-@_add_method(ttLib.getTableClass('GSUB'),
-             ttLib.getTableClass('GPOS'))
-def subset_glyphs(self, s):
-  s.glyphs = s.glyphs_gsubed
-  if self.table.LookupList:
-    lookup_indices = self.table.LookupList.subset_glyphs(s)
-  else:
-    lookup_indices = []
-  self.subset_lookups(lookup_indices)
-  self.prune_lookups()
-  return True
-
-@_add_method(ttLib.getTableClass('GSUB'),
-             ttLib.getTableClass('GPOS'))
-def subset_lookups(self, lookup_indices):
-  """Retains specified lookups, then removes empty features, language
-     systems, and scripts."""
-  if self.table.LookupList:
-    self.table.LookupList.subset_lookups(lookup_indices)
-  if self.table.FeatureList:
-    feature_indices = self.table.FeatureList.subset_lookups(lookup_indices)
-  else:
-    feature_indices = []
-  if self.table.ScriptList:
-    self.table.ScriptList.subset_features(feature_indices)
-
-@_add_method(ttLib.getTableClass('GSUB'),
-             ttLib.getTableClass('GPOS'))
-def prune_lookups(self):
-  "Remove unreferenced lookups"
-  if self.table.ScriptList:
-    feature_indices = self.table.ScriptList.collect_features()
-  else:
-    feature_indices = []
-  if self.table.FeatureList:
-    lookup_indices = self.table.FeatureList.collect_lookups(feature_indices)
-  else:
-    lookup_indices = []
-  if self.table.LookupList:
-    lookup_indices = self.table.LookupList.closure_lookups(lookup_indices)
-  else:
-    lookup_indices = []
-  self.subset_lookups(lookup_indices)
-
-@_add_method(ttLib.getTableClass('GSUB'),
-             ttLib.getTableClass('GPOS'))
-def subset_feature_tags(self, feature_tags):
-  if self.table.FeatureList:
-    feature_indices = [i for i,f in
-                       enumerate(self.table.FeatureList.FeatureRecord)
-                       if f.FeatureTag in feature_tags]
-    self.table.FeatureList.subset_features(feature_indices)
-  else:
-    feature_indices = []
-  if self.table.ScriptList:
-    self.table.ScriptList.subset_features(feature_indices)
-
-@_add_method(ttLib.getTableClass('GSUB'),
-             ttLib.getTableClass('GPOS'))
-def prune_features(self):
-  "Remove unreferenced featurs"
-  if self.table.ScriptList:
-    feature_indices = self.table.ScriptList.collect_features()
-  else:
-    feature_indices = []
-  if self.table.FeatureList:
-    self.table.FeatureList.subset_features(feature_indices)
-  if self.table.ScriptList:
-    self.table.ScriptList.subset_features(feature_indices)
-
-@_add_method(ttLib.getTableClass('GSUB'),
-             ttLib.getTableClass('GPOS'))
-def prune_pre_subset(self, options):
-  # Drop undesired features
-  if '*' not in options.layout_features:
-    self.subset_feature_tags(options.layout_features)
-  # Drop unreferenced lookups
-  self.prune_lookups()
-  # Prune lookups themselves
-  if self.table.LookupList:
-    self.table.LookupList.prune_pre_subset(options);
-  return True
-
-@_add_method(ttLib.getTableClass('GSUB'),
-             ttLib.getTableClass('GPOS'))
-def remove_redundant_langsys(self):
-  table = self.table
-  if not table.ScriptList or not table.FeatureList:
-    return
-
-  features = table.FeatureList.FeatureRecord
-
-  for s in table.ScriptList.ScriptRecord:
-    d = s.Script.DefaultLangSys
-    if not d:
-      continue
-    for lr in s.Script.LangSysRecord[:]:
-      l = lr.LangSys
-      # Compare d and l
-      if len(d.FeatureIndex) != len(l.FeatureIndex):
-        continue
-      if (d.ReqFeatureIndex == 65535) != (l.ReqFeatureIndex == 65535):
-        continue
-
-      if d.ReqFeatureIndex != 65535:
-        if features[d.ReqFeatureIndex] != features[l.ReqFeatureIndex]:
-          continue
-
-      for i in range(len(d.FeatureIndex)):
-        if features[d.FeatureIndex[i]] != features[l.FeatureIndex[i]]:
-          break
-      else:
-        # LangSys and default are equal; delete LangSys
-        s.Script.LangSysRecord.remove(lr)
-
-@_add_method(ttLib.getTableClass('GSUB'),
-             ttLib.getTableClass('GPOS'))
-def prune_post_subset(self, options):
-  table = self.table
-
-  # LookupList looks good.  Just prune lookups themselves
-  if table.LookupList:
-    table.LookupList.prune_post_subset(options);
-    # XXX Next two lines disabled because OTS is stupid and
-    # doesn't like NULL offsetse here.
-    #if not table.LookupList.Lookup:
-    #  table.LookupList = None
-
-  if not table.LookupList:
-    table.FeatureList = None
-
-  if table.FeatureList:
-    self.remove_redundant_langsys()
-    # Remove unreferenced features
-    self.prune_features()
-
-  # XXX Next two lines disabled because OTS is stupid and
-  # doesn't like NULL offsetse here.
-  #if table.FeatureList and not table.FeatureList.FeatureRecord:
-  #  table.FeatureList = None
-
-  # Never drop scripts themselves as them just being available
-  # holds semantic significance.
-  # XXX Next two lines disabled because OTS is stupid and
-  # doesn't like NULL offsetse here.
-  #if table.ScriptList and not table.ScriptList.ScriptRecord:
-  #  table.ScriptList = None
-
-  return True
-
-@_add_method(ttLib.getTableClass('GDEF'))
-def subset_glyphs(self, s):
-  glyphs = s.glyphs_gsubed
-  table = self.table
-  if table.LigCaretList:
-    indices = table.LigCaretList.Coverage.subset(glyphs)
-    table.LigCaretList.LigGlyph = [table.LigCaretList.LigGlyph[i]
-                                   for i in indices]
-    table.LigCaretList.LigGlyphCount = len(table.LigCaretList.LigGlyph)
-  if table.MarkAttachClassDef:
-    table.MarkAttachClassDef.classDefs = dict((g,v) for g,v in
-                                              table.MarkAttachClassDef.
-                                                classDefs.items()
-                                              if g in glyphs)
-  if table.GlyphClassDef:
-    table.GlyphClassDef.classDefs = dict((g,v) for g,v in
-                                         table.GlyphClassDef.
-                                           classDefs.items()
-                                         if g in glyphs)
-  if table.AttachList:
-    indices = table.AttachList.Coverage.subset(glyphs)
-    GlyphCount = table.AttachList.GlyphCount
-    table.AttachList.AttachPoint = [table.AttachList.AttachPoint[i]
-                                    for i in indices
-                                    if i < GlyphCount]
-    table.AttachList.GlyphCount = len(table.AttachList.AttachPoint)
-  if hasattr(table, "MarkGlyphSetsDef") and table.MarkGlyphSetsDef:
-    for coverage in table.MarkGlyphSetsDef.Coverage:
-      coverage.subset(glyphs)
-    # TODO: The following is disabled.  If enabling, we need to go fixup all
-    # lookups that use MarkFilteringSet and map their set.
-    #indices = table.MarkGlyphSetsDef.Coverage = [c for c in table.MarkGlyphSetsDef.Coverage if c.glyphs]
-  return True
-
-@_add_method(ttLib.getTableClass('GDEF'))
-def prune_post_subset(self, options):
-  table = self.table
-  # XXX check these against OTS
-  if table.LigCaretList and not table.LigCaretList.LigGlyphCount:
-    table.LigCaretList = None
-  if table.MarkAttachClassDef and not table.MarkAttachClassDef.classDefs:
-    table.MarkAttachClassDef = None
-  if table.GlyphClassDef and not table.GlyphClassDef.classDefs:
-    table.GlyphClassDef = None
-  if table.AttachList and not table.AttachList.GlyphCount:
-    table.AttachList = None
-  if hasattr(table, "MarkGlyphSetsDef") and table.MarkGlyphSetsDef and not table.MarkGlyphSetsDef.Coverage:
-    table.MarkGlyphSetsDef = None
-    if table.Version == 0x00010002/0x10000:
-      table.Version = 1.0
-  return bool(table.LigCaretList or
-              table.MarkAttachClassDef or
-              table.GlyphClassDef or
-              table.AttachList or
-              (table.Version >= 0x00010002/0x10000 and table.MarkGlyphSetsDef))
-
-@_add_method(ttLib.getTableClass('kern'))
-def prune_pre_subset(self, options):
-  # Prune unknown kern table types
-  self.kernTables = [t for t in self.kernTables if hasattr(t, 'kernTable')]
-  return bool(self.kernTables)
-
-@_add_method(ttLib.getTableClass('kern'))
-def subset_glyphs(self, s):
-  glyphs = s.glyphs_gsubed
-  for t in self.kernTables:
-    t.kernTable = dict(((a,b),v) for (a,b),v in t.kernTable.items()
-                       if a in glyphs and b in glyphs)
-  self.kernTables = [t for t in self.kernTables if t.kernTable]
-  return bool(self.kernTables)
-
-@_add_method(ttLib.getTableClass('vmtx'))
-def subset_glyphs(self, s):
-  self.metrics = dict((g,v) for g,v in self.metrics.items() if g in s.glyphs)
-  return bool(self.metrics)
-
-@_add_method(ttLib.getTableClass('hmtx'))
-def subset_glyphs(self, s):
-  self.metrics = dict((g,v) for g,v in self.metrics.items() if g in s.glyphs)
-  return True # Required table
-
-@_add_method(ttLib.getTableClass('hdmx'))
-def subset_glyphs(self, s):
-  self.hdmx = dict((sz,dict((g,v) for g,v in l.items() if g in s.glyphs))
-                   for sz,l in self.hdmx.items())
-  return bool(self.hdmx)
-
-@_add_method(ttLib.getTableClass('VORG'))
-def subset_glyphs(self, s):
-  self.VOriginRecords = dict((g,v) for g,v in self.VOriginRecords.items()
-                             if g in s.glyphs)
-  self.numVertOriginYMetrics = len(self.VOriginRecords)
-  return True  # Never drop; has default metrics
-
-@_add_method(ttLib.getTableClass('post'))
-def prune_pre_subset(self, options):
-  if not options.glyph_names:
-    self.formatType = 3.0
-  return True # Required table
-
-@_add_method(ttLib.getTableClass('post'))
-def subset_glyphs(self, s):
-  self.extraNames = []  # This seems to do it
-  return True # Required table
-
-@_add_method(ttLib.getTableModule('glyf').Glyph)
-def remapComponentsFast(self, indices):
-  if not self.data or struct.unpack(">h", self.data[:2])[0] >= 0:
-    return  # Not composite
-  data = array.array("B", self.data)
-  i = 10
-  more = 1
-  while more:
-    flags =(data[i] << 8) | data[i+1]
-    glyphID =(data[i+2] << 8) | data[i+3]
-    # Remap
-    glyphID = indices.index(glyphID)
-    data[i+2] = glyphID >> 8
-    data[i+3] = glyphID & 0xFF
-    i += 4
-    flags = int(flags)
-
-    if flags & 0x0001: i += 4  # ARG_1_AND_2_ARE_WORDS
-    else: i += 2
-    if flags & 0x0008: i += 2  # WE_HAVE_A_SCALE
-    elif flags & 0x0040: i += 4  # WE_HAVE_AN_X_AND_Y_SCALE
-    elif flags & 0x0080: i += 8  # WE_HAVE_A_TWO_BY_TWO
-    more = flags & 0x0020  # MORE_COMPONENTS
-
-  self.data = data.tostring()
-
-@_add_method(ttLib.getTableClass('glyf'))
-def closure_glyphs(self, s):
-  decompose = s.glyphs
-  while True:
-    components = set()
-    for g in decompose:
-      if g not in self.glyphs:
-        continue
-      gl = self.glyphs[g]
-      for c in gl.getComponentNames(self):
-        if c not in s.glyphs:
-          components.add(c)
-    components = set(c for c in components if c not in s.glyphs)
-    if not components:
-      break
-    decompose = components
-    s.glyphs.update(components)
-
-@_add_method(ttLib.getTableClass('glyf'))
-def prune_pre_subset(self, options):
-  if options.notdef_glyph and not options.notdef_outline:
-    g = self[self.glyphOrder[0]]
-    # Yay, easy!
-    g.__dict__.clear()
-    g.data = ""
-  return True
-
-@_add_method(ttLib.getTableClass('glyf'))
-def subset_glyphs(self, s):
-  self.glyphs = dict((g,v) for g,v in self.glyphs.items() if g in s.glyphs)
-  indices = [i for i,g in enumerate(self.glyphOrder) if g in s.glyphs]
-  for v in self.glyphs.values():
-    if hasattr(v, "data"):
-      v.remapComponentsFast(indices)
-    else:
-      pass  # No need
-  self.glyphOrder = [g for g in self.glyphOrder if g in s.glyphs]
-  # Don't drop empty 'glyf' tables, otherwise 'loca' doesn't get subset.
-  return True
-
-@_add_method(ttLib.getTableClass('glyf'))
-def prune_post_subset(self, options):
-  if not options.hinting:
-    for v in self.glyphs.values():
-      v.removeHinting()
-  return True
-
-@_add_method(ttLib.getTableClass('CFF '))
-def prune_pre_subset(self, options):
-  cff = self.cff
-  # CFF table must have one font only
-  cff.fontNames = cff.fontNames[:1]
-
-  if options.notdef_glyph and not options.notdef_outline:
-    for fontname in cff.keys():
-      font = cff[fontname]
-      c,_ = font.CharStrings.getItemAndSelector('.notdef')
-      # XXX we should preserve the glyph width
-      c.bytecode = '\x0e' # endchar
-      c.program = None
-
-  return True # bool(cff.fontNames)
-
-@_add_method(ttLib.getTableClass('CFF '))
-def subset_glyphs(self, s):
-  cff = self.cff
-  for fontname in cff.keys():
-    font = cff[fontname]
-    cs = font.CharStrings
-
-    # Load all glyphs
-    for g in font.charset:
-      if g not in s.glyphs: continue
-      c,sel = cs.getItemAndSelector(g)
-
-    if cs.charStringsAreIndexed:
-      indices = [i for i,g in enumerate(font.charset) if g in s.glyphs]
-      csi = cs.charStringsIndex
-      csi.items = [csi.items[i] for i in indices]
-      csi.count = len(csi.items)
-      del csi.file, csi.offsets
-      if hasattr(font, "FDSelect"):
-        sel = font.FDSelect
-        sel.format = None
-        sel.gidArray = [sel.gidArray[i] for i in indices]
-      cs.charStrings = dict((g,indices.index(v))
-                            for g,v in cs.charStrings.items()
-                            if g in s.glyphs)
-    else:
-      cs.charStrings = dict((g,v)
-                            for g,v in cs.charStrings.items()
-                            if g in s.glyphs)
-    font.charset = [g for g in font.charset if g in s.glyphs]
-    font.numGlyphs = len(font.charset)
-
-  return True # any(cff[fontname].numGlyphs for fontname in cff.keys())
-
-@_add_method(psCharStrings.T2CharString)
-def subset_subroutines(self, subrs, gsubrs):
-  p = self.program
-  assert len(p)
-  for i in range(1, len(p)):
-    if p[i] == 'callsubr':
-      assert isinstance(p[i-1], int)
-      p[i-1] = subrs._used.index(p[i-1] + subrs._old_bias) - subrs._new_bias
-    elif p[i] == 'callgsubr':
-      assert isinstance(p[i-1], int)
-      p[i-1] = gsubrs._used.index(p[i-1] + gsubrs._old_bias) - gsubrs._new_bias
-
-@_add_method(psCharStrings.T2CharString)
-def drop_hints(self):
-  hints = self._hints
-
-  if hints.has_hint:
-    self.program = self.program[hints.last_hint:]
-    if hasattr(self, 'width'):
-      # Insert width back if needed
-      if self.width != self.private.defaultWidthX:
-        self.program.insert(0, self.width - self.private.nominalWidthX)
-
-  if hints.has_hintmask:
-    i = 0
-    p = self.program
-    while i < len(p):
-      if p[i] in ['hintmask', 'cntrmask']:
-        assert i + 1 <= len(p)
-        del p[i:i+2]
-        continue
-      i += 1
-
-  # TODO: we currently don't drop calls to "empty" subroutines.
-
-  assert len(self.program)
-
-  del self._hints
-
-class _MarkingT2Decompiler(psCharStrings.SimpleT2Decompiler):
-
-  def __init__(self, localSubrs, globalSubrs):
-    psCharStrings.SimpleT2Decompiler.__init__(self,
-                                              localSubrs,
-                                              globalSubrs)
-    for subrs in [localSubrs, globalSubrs]:
-      if subrs and not hasattr(subrs, "_used"):
-        subrs._used = set()
-
-  def op_callsubr(self, index):
-    self.localSubrs._used.add(self.operandStack[-1]+self.localBias)
-    psCharStrings.SimpleT2Decompiler.op_callsubr(self, index)
-
-  def op_callgsubr(self, index):
-    self.globalSubrs._used.add(self.operandStack[-1]+self.globalBias)
-    psCharStrings.SimpleT2Decompiler.op_callgsubr(self, index)
-
-class _DehintingT2Decompiler(psCharStrings.SimpleT2Decompiler):
-
-  class Hints(object):
-    def __init__(self):
-      # Whether calling this charstring produces any hint stems
-      self.has_hint = False
-      # Index to start at to drop all hints
-      self.last_hint = 0
-      # Index up to which we know more hints are possible.  Only
-      # relevant if status is 0 or 1.
-      self.last_checked = 0
-      # The status means:
-      # 0: after dropping hints, this charstring is empty
-      # 1: after dropping hints, there may be more hints continuing after this
-      # 2: no more hints possible after this charstring
-      self.status = 0
-      # Has hintmask instructions; not recursive
-      self.has_hintmask = False
-    pass
-
-  def __init__(self, css, localSubrs, globalSubrs):
-    self._css = css
-    psCharStrings.SimpleT2Decompiler.__init__(self,
-                                              localSubrs,
-                                              globalSubrs)
-
-  def execute(self, charString):
-    old_hints = charString._hints if hasattr(charString, '_hints') else None
-    charString._hints = self.Hints()
-
-    psCharStrings.SimpleT2Decompiler.execute(self, charString)
-
-    hints = charString._hints
-
-    if hints.has_hint or hints.has_hintmask:
-      self._css.add(charString)
-
-    if hints.status != 2:
-      # Check from last_check, make sure we didn't have any operators.
-      for i in range(hints.last_checked, len(charString.program) - 1):
-        if isinstance(charString.program[i], str):
-          hints.status = 2
-          break;
-        else:
-          hints.status = 1 # There's *something* here
-      hints.last_checked = len(charString.program)
-
-    if old_hints:
-      assert hints.__dict__ == old_hints.__dict__
-
-  def op_callsubr(self, index):
-    subr = self.localSubrs[self.operandStack[-1]+self.localBias]
-    psCharStrings.SimpleT2Decompiler.op_callsubr(self, index)
-    self.processSubr(index, subr)
-
-  def op_callgsubr(self, index):
-    subr = self.globalSubrs[self.operandStack[-1]+self.globalBias]
-    psCharStrings.SimpleT2Decompiler.op_callgsubr(self, index)
-    self.processSubr(index, subr)
-
-  def op_hstem(self, index):
-    psCharStrings.SimpleT2Decompiler.op_hstem(self, index)
-    self.processHint(index)
-  def op_vstem(self, index):
-    psCharStrings.SimpleT2Decompiler.op_vstem(self, index)
-    self.processHint(index)
-  def op_hstemhm(self, index):
-    psCharStrings.SimpleT2Decompiler.op_hstemhm(self, index)
-    self.processHint(index)
-  def op_vstemhm(self, index):
-    psCharStrings.SimpleT2Decompiler.op_vstemhm(self, index)
-    self.processHint(index)
-  def op_hintmask(self, index):
-    psCharStrings.SimpleT2Decompiler.op_hintmask(self, index)
-    self.processHintmask(index)
-  def op_cntrmask(self, index):
-    psCharStrings.SimpleT2Decompiler.op_cntrmask(self, index)
-    self.processHintmask(index)
-
-  def processHintmask(self, index):
-    cs = self.callingStack[-1]
-    hints = cs._hints
-    hints.has_hintmask = True
-    if hints.status != 2 and hints.has_hint:
-      # Check from last_check, see if we may be an implicit vstem
-      for i in range(hints.last_checked, index - 1):
-        if isinstance(cs.program[i], str):
-          hints.status = 2
-          break;
-      if hints.status != 2:
-        # We are an implicit vstem
-        hints.last_hint = index + 1
-        hints.status = 0
-    hints.last_checked = index + 1
-
-  def processHint(self, index):
-    cs = self.callingStack[-1]
-    hints = cs._hints
-    hints.has_hint = True
-    hints.last_hint = index
-    hints.last_checked = index
-
-  def processSubr(self, index, subr):
-    cs = self.callingStack[-1]
-    hints = cs._hints
-    subr_hints = subr._hints
-
-    if subr_hints.has_hint:
-      if hints.status != 2:
-        hints.has_hint = True
-        hints.last_checked = index
-        hints.status = subr_hints.status
-        # Decide where to chop off from
-        if subr_hints.status == 0:
-          hints.last_hint = index
-        else:
-          hints.last_hint = index - 2 # Leave the subr call in
-      else:
-        # In my understanding, this is a font bug.  Ie. it has hint stems
-        # *after* path construction.  I've seen this in widespread fonts.
-        # Best to ignore the hints I suppose...
-        pass
-        #assert 0
-    else:
-      hints.status = max(hints.status, subr_hints.status)
-      if hints.status != 2:
-        # Check from last_check, make sure we didn't have
-        # any operators.
-        for i in range(hints.last_checked, index - 1):
-          if isinstance(cs.program[i], str):
-            hints.status = 2
-            break;
-        hints.last_checked = index
-      if hints.status != 2:
-        # Decide where to chop off from
-        if subr_hints.status == 0:
-          hints.last_hint = index
-        else:
-          hints.last_hint = index - 2 # Leave the subr call in
-
-@_add_method(ttLib.getTableClass('CFF '))
-def prune_post_subset(self, options):
-  cff = self.cff
-  for fontname in cff.keys():
-    font = cff[fontname]
-    cs = font.CharStrings
-
-
-    #
-    # Drop unused FontDictionaries
-    #
-    if hasattr(font, "FDSelect"):
-      sel = font.FDSelect
-      indices = _uniq_sort(sel.gidArray)
-      sel.gidArray = [indices.index (ss) for ss in sel.gidArray]
-      arr = font.FDArray
-      arr.items = [arr[i] for i in indices]
-      arr.count = len(arr.items)
-      del arr.file, arr.offsets
-
-
-    #
-    # Drop hints if not needed
-    #
-    if not options.hinting:
-
-      #
-      # This can be tricky, but doesn't have to.  What we do is:
-      #
-      # - Run all used glyph charstrings and recurse into subroutines,
-      # - For each charstring (including subroutines), if it has any
-      #   of the hint stem operators, we mark it as such.  Upon returning,
-      #   for each charstring we note all the subroutine calls it makes
-      #   that (recursively) contain a stem,
-      # - Dropping hinting then consists of the following two ops:
-      #   * Drop the piece of the program in each charstring before the
-      #     last call to a stem op or a stem-calling subroutine,
-      #   * Drop all hintmask operations.
-      # - It's trickier... A hintmask right after hints and a few numbers
-      #   will act as an implicit vstemhm.  As such, we track whether
-      #   we have seen any non-hint operators so far and do the right
-      #   thing, recursively...  Good luck understanding that :(
-      #
-      css = set()
-      for g in font.charset:
-        c,sel = cs.getItemAndSelector(g)
-        # Make sure it's decompiled.  We want our "decompiler" to walk
-        # the program, not the bytecode.
-        c.draw(basePen.NullPen())
-        subrs = getattr(c.private, "Subrs", [])
-        decompiler = _DehintingT2Decompiler(css, subrs, c.globalSubrs)
-        decompiler.execute(c)
-      for charstring in css:
-        charstring.drop_hints()
-
-      # Drop font-wide hinting values
-      all_privs = []
-      if hasattr(font, 'FDSelect'):
-        all_privs.extend(fd.Private for fd in font.FDArray)
-      else:
-        all_privs.append(font.Private)
-      for priv in all_privs:
-        for k in ['BlueValues', 'OtherBlues', 'FamilyBlues', 'FamilyOtherBlues',
-                  'BlueScale', 'BlueShift', 'BlueFuzz',
-                  'StemSnapH', 'StemSnapV', 'StdHW', 'StdVW']:
-          if hasattr(priv, k):
-            setattr(priv, k, None)
-
-
-    #
-    # Renumber subroutines to remove unused ones
-    #
-
-    # Mark all used subroutines
-    for g in font.charset:
-      c,sel = cs.getItemAndSelector(g)
-      subrs = getattr(c.private, "Subrs", [])
-      decompiler = _MarkingT2Decompiler(subrs, c.globalSubrs)
-      decompiler.execute(c)
-
-    all_subrs = [font.GlobalSubrs]
-    if hasattr(font, 'FDSelect'):
-      all_subrs.extend(fd.Private.Subrs for fd in font.FDArray if hasattr(fd.Private, 'Subrs') and fd.Private.Subrs)
-    elif hasattr(font.Private, 'Subrs') and font.Private.Subrs:
-      all_subrs.append(font.Private.Subrs)
-
-    subrs = set(subrs) # Remove duplicates
-
-    # Prepare
-    for subrs in all_subrs:
-      if not hasattr(subrs, '_used'):
-        subrs._used = set()
-      subrs._used = _uniq_sort(subrs._used)
-      subrs._old_bias = psCharStrings.calcSubrBias(subrs)
-      subrs._new_bias = psCharStrings.calcSubrBias(subrs._used)
-
-    # Renumber glyph charstrings
-    for g in font.charset:
-      c,sel = cs.getItemAndSelector(g)
-      subrs = getattr(c.private, "Subrs", [])
-      c.subset_subroutines (subrs, font.GlobalSubrs)
-
-    # Renumber subroutines themselves
-    for subrs in all_subrs:
-
-      if subrs == font.GlobalSubrs:
-        if not hasattr(font, 'FDSelect') and hasattr(font.Private, 'Subrs'):
-          local_subrs = font.Private.Subrs
-        else:
-          local_subrs = []
-      else:
-        local_subrs = subrs
-
-      subrs.items = [subrs.items[i] for i in subrs._used]
-      subrs.count = len(subrs.items)
-      del subrs.file
-      if hasattr(subrs, 'offsets'):
-        del subrs.offsets
-
-      for i in range (subrs.count):
-        subrs[i].subset_subroutines (local_subrs, font.GlobalSubrs)
-
-    # Cleanup
-    for subrs in all_subrs:
-      del subrs._used, subrs._old_bias, subrs._new_bias
-
-  return True
-
-@_add_method(ttLib.getTableClass('cmap'))
-def closure_glyphs(self, s):
-  tables = [t for t in self.tables if t.isUnicode()]
-  for u in s.unicodes_requested:
-    found = False
-    for table in tables:
-      if table.format == 14:
-        for l in table.uvsDict.values():
-          # TODO(behdad) Speed this up!
-          gids = [g for uc,g in l if u == uc and g is not None]
-          s.glyphs.update(gids)
-          # Intentionally not setting found=True here.
-      else:
-        if u in table.cmap:
-          s.glyphs.add(table.cmap[u])
-          found = True
-    if not found:
-      s.log("No default glyph for Unicode %04X found." % u)
-
-@_add_method(ttLib.getTableClass('cmap'))
-def prune_pre_subset(self, options):
-  if not options.legacy_cmap:
-    # Drop non-Unicode / non-Symbol cmaps
-    self.tables = [t for t in self.tables if t.isUnicode() or t.isSymbol()]
-  if not options.symbol_cmap:
-    self.tables = [t for t in self.tables if not t.isSymbol()]
-  # TODO(behdad) Only keep one subtable?
-  # For now, drop format=0 which can't be subset_glyphs easily?
-  self.tables = [t for t in self.tables if t.format != 0]
-  self.numSubTables = len(self.tables)
-  return True # Required table
-
-@_add_method(ttLib.getTableClass('cmap'))
-def subset_glyphs(self, s):
-  s.glyphs = s.glyphs_cmaped
-  for t in self.tables:
-    # For reasons I don't understand I need this here
-    # to force decompilation of the cmap format 14.
-    try:
-      getattr(t, "asdf")
-    except AttributeError:
-      pass
-    if t.format == 14:
-      # TODO(behdad) We drop all the default-UVS mappings for glyphs_requested.
-      # I don't think we care about that...
-      t.uvsDict = dict((v,[(u,g) for u,g in l
-                           if g in s.glyphs or u in s.unicodes_requested])
-                       for v,l in t.uvsDict.items())
-      t.uvsDict = dict((v,l) for v,l in t.uvsDict.items() if l)
-    elif t.isUnicode():
-      t.cmap = dict((u,g) for u,g in t.cmap.items()
-                    if g in s.glyphs_requested or u in s.unicodes_requested)
-    else:
-      t.cmap = dict((u,g) for u,g in t.cmap.items()
-                    if g in s.glyphs_requested)
-  self.tables = [t for t in self.tables
-                 if (t.cmap if t.format != 14 else t.uvsDict)]
-  self.numSubTables = len(self.tables)
-  # TODO(behdad) Convert formats when needed.
-  # In particular, if we have a format=12 without non-BMP
-  # characters, either drop format=12 one or convert it
-  # to format=4 if there's not one.
-  return True # Required table
-
-@_add_method(ttLib.getTableClass('name'))
-def prune_pre_subset(self, options):
-  if '*' not in options.name_IDs:
-    self.names = [n for n in self.names if n.nameID in options.name_IDs]
-  if not options.name_legacy:
-    self.names = [n for n in self.names if n.isUnicode()]
-  # TODO(behdad) Option to keep only one platform's
-  if '*' not in options.name_languages:
-    # TODO(behdad) This is Windows-platform specific!
-    self.names = [n for n in self.names if n.langID in options.name_languages]
-  return True  # Required table
-
-
-# TODO(behdad) OS/2 ulUnicodeRange / ulCodePageRange?
-# TODO(behdad) Drop AAT tables.
-# TODO(behdad) Drop unneeded GSUB/GPOS Script/LangSys entries.
-# TODO(behdad) Drop empty GSUB/GPOS, and GDEF if no GSUB/GPOS left
-# TODO(behdad) Drop GDEF subitems if unused by lookups
-# TODO(behdad) Avoid recursing too much (in GSUB/GPOS and in CFF)
-# TODO(behdad) Text direction considerations.
-# TODO(behdad) Text script / language considerations.
-# TODO(behdad) Optionally drop 'kern' table if GPOS available
-# TODO(behdad) Implement --unicode='*' to choose all cmap'ed
-# TODO(behdad) Drop old-spec Indic scripts
-
-
-class Options(object):
-
-  class UnknownOptionError(Exception):
-    pass
-
-  _drop_tables_default = ['BASE', 'JSTF', 'DSIG', 'EBDT', 'EBLC', 'EBSC', 'SVG ',
-                          'PCLT', 'LTSH']
-  _drop_tables_default += ['Feat', 'Glat', 'Gloc', 'Silf', 'Sill']  # Graphite
-  _drop_tables_default += ['CBLC', 'CBDT', 'sbix', 'COLR', 'CPAL']  # Color
-  _no_subset_tables_default = ['gasp', 'head', 'hhea', 'maxp', 'vhea', 'OS/2',
-                               'loca', 'name', 'cvt ', 'fpgm', 'prep']
-  _hinting_tables_default = ['cvt ', 'fpgm', 'prep', 'hdmx', 'VDMX']
-
-  # Based on HarfBuzz shapers
-  _layout_features_groups = {
-    # Default shaper
-    'common': ['ccmp', 'liga', 'locl', 'mark', 'mkmk', 'rlig'],
-    'horizontal': ['calt', 'clig', 'curs', 'kern', 'rclt'],
-    'vertical':  ['valt', 'vert', 'vkrn', 'vpal', 'vrt2'],
-    'ltr': ['ltra', 'ltrm'],
-    'rtl': ['rtla', 'rtlm'],
-    # Complex shapers
-    'arabic': ['init', 'medi', 'fina', 'isol', 'med2', 'fin2', 'fin3',
-               'cswh', 'mset'],
-    'hangul': ['ljmo', 'vjmo', 'tjmo'],
-    'tibetan': ['abvs', 'blws', 'abvm', 'blwm'],
-    'indic': ['nukt', 'akhn', 'rphf', 'rkrf', 'pref', 'blwf', 'half',
-              'abvf', 'pstf', 'cfar', 'vatu', 'cjct', 'init', 'pres',
-              'abvs', 'blws', 'psts', 'haln', 'dist', 'abvm', 'blwm'],
-  }
-  _layout_features_default = _uniq_sort(sum(
-      iter(_layout_features_groups.values()), []))
-
-  drop_tables = _drop_tables_default
-  no_subset_tables = _no_subset_tables_default
-  hinting_tables = _hinting_tables_default
-  layout_features = _layout_features_default
-  hinting = True
-  glyph_names = False
-  legacy_cmap = False
-  symbol_cmap = False
-  name_IDs = [1, 2]  # Family and Style
-  name_legacy = False
-  name_languages = [0x0409]  # English
-  notdef_glyph = True # gid0 for TrueType / .notdef for CFF
-  notdef_outline = False # No need for notdef to have an outline really
-  recommended_glyphs = False  # gid1, gid2, gid3 for TrueType
-  recalc_bounds = False # Recalculate font bounding boxes
-  recalc_timestamp = False # Recalculate font modified timestamp
-  canonical_order = False # Order tables as recommended
-  flavor = None # May be 'woff'
-
-  def __init__(self, **kwargs):
-
-    self.set(**kwargs)
-
-  def set(self, **kwargs):
-    for k,v in kwargs.items():
-      if not hasattr(self, k):
-        raise self.UnknownOptionError("Unknown option '%s'" % k)
-      setattr(self, k, v)
-
-  def parse_opts(self, argv, ignore_unknown=False):
-    ret = []
-    opts = {}
-    for a in argv:
-      orig_a = a
-      if not a.startswith('--'):
-        ret.append(a)
-        continue
-      a = a[2:]
-      i = a.find('=')
-      op = '='
-      if i == -1:
-        if a.startswith("no-"):
-          k = a[3:]
-          v = False
-        else:
-          k = a
-          v = True
-      else:
-        k = a[:i]
-        if k[-1] in "-+":
-          op = k[-1]+'='  # Ops is '-=' or '+=' now.
-          k = k[:-1]
-        v = a[i+1:]
-      k = k.replace('-', '_')
-      if not hasattr(self, k):
-        if ignore_unknown is True or k in ignore_unknown:
-          ret.append(orig_a)
-          continue
-        else:
-          raise self.UnknownOptionError("Unknown option '%s'" % a)
-
-      ov = getattr(self, k)
-      if isinstance(ov, bool):
-        v = bool(v)
-      elif isinstance(ov, int):
-        v = int(v)
-      elif isinstance(ov, list):
-        vv = v.split(',')
-        if vv == ['']:
-          vv = []
-        vv = [int(x, 0) if len(x) and x[0] in "0123456789" else x for x in vv]
-        if op == '=':
-          v = vv
-        elif op == '+=':
-          v = ov
-          v.extend(vv)
-        elif op == '-=':
-          v = ov
-          for x in vv:
-            if x in v:
-              v.remove(x)
-        else:
-          assert False
-
-      opts[k] = v
-    self.set(**opts)
-
-    return ret
-
-
-class Subsetter(object):
-
-  def __init__(self, options=None, log=None):
-
-    if not log:
-      log = Logger()
-    if not options:
-      options = Options()
-
-    self.options = options
-    self.log = log
-    self.unicodes_requested = set()
-    self.glyphs_requested = set()
-    self.glyphs = set()
-
-  def populate(self, glyphs=[], unicodes=[], text=""):
-    self.unicodes_requested.update(unicodes)
-    if isinstance(text, bytes):
-      text = text.decode("utf8")
-    for u in text:
-      self.unicodes_requested.add(ord(u))
-    self.glyphs_requested.update(glyphs)
-    self.glyphs.update(glyphs)
-
-  def _prune_pre_subset(self, font):
-
-    for tag in font.keys():
-      if tag == 'GlyphOrder': continue
-
-      if(tag in self.options.drop_tables or
-         (tag in self.options.hinting_tables and not self.options.hinting)):
-        self.log(tag, "dropped")
-        del font[tag]
-        continue
-
-      clazz = ttLib.getTableClass(tag)
-
-      if hasattr(clazz, 'prune_pre_subset'):
-        table = font[tag]
-        self.log.lapse("load '%s'" % tag)
-        retain = table.prune_pre_subset(self.options)
-        self.log.lapse("prune  '%s'" % tag)
-        if not retain:
-          self.log(tag, "pruned to empty; dropped")
-          del font[tag]
-          continue
-        else:
-          self.log(tag, "pruned")
-
-  def _closure_glyphs(self, font):
-
-    realGlyphs = set(font.getGlyphOrder())
-
-    self.glyphs = self.glyphs_requested.copy()
-
-    if 'cmap' in font:
-      font['cmap'].closure_glyphs(self)
-      self.glyphs.intersection_update(realGlyphs)
-    self.glyphs_cmaped = self.glyphs
-
-    if self.options.notdef_glyph:
-      if 'glyf' in font:
-        self.glyphs.add(font.getGlyphName(0))
-        self.log("Added gid0 to subset")
-      else:
-        self.glyphs.add('.notdef')
-        self.log("Added .notdef to subset")
-    if self.options.recommended_glyphs:
-      if 'glyf' in font:
-        for i in range(min(4, len(font.getGlyphOrder()))):
-          self.glyphs.add(font.getGlyphName(i))
-        self.log("Added first four glyphs to subset")
-
-    if 'GSUB' in font:
-      self.log("Closing glyph list over 'GSUB': %d glyphs before" %
-                len(self.glyphs))
-      self.log.glyphs(self.glyphs, font=font)
-      font['GSUB'].closure_glyphs(self)
-      self.glyphs.intersection_update(realGlyphs)
-      self.log("Closed  glyph list over 'GSUB': %d glyphs after" %
-                len(self.glyphs))
-      self.log.glyphs(self.glyphs, font=font)
-      self.log.lapse("close glyph list over 'GSUB'")
-    self.glyphs_gsubed = self.glyphs.copy()
-
-    if 'glyf' in font:
-      self.log("Closing glyph list over 'glyf': %d glyphs before" %
-                len(self.glyphs))
-      self.log.glyphs(self.glyphs, font=font)
-      font['glyf'].closure_glyphs(self)
-      self.glyphs.intersection_update(realGlyphs)
-      self.log("Closed  glyph list over 'glyf': %d glyphs after" %
-                len(self.glyphs))
-      self.log.glyphs(self.glyphs, font=font)
-      self.log.lapse("close glyph list over 'glyf'")
-    self.glyphs_glyfed = self.glyphs.copy()
-
-    self.glyphs_all = self.glyphs.copy()
-
-    self.log("Retaining %d glyphs: " % len(self.glyphs_all))
-
-    del self.glyphs
-
-
-  def _subset_glyphs(self, font):
-    for tag in font.keys():
-      if tag == 'GlyphOrder': continue
-      clazz = ttLib.getTableClass(tag)
-
-      if tag in self.options.no_subset_tables:
-        self.log(tag, "subsetting not needed")
-      elif hasattr(clazz, 'subset_glyphs'):
-        table = font[tag]
-        self.glyphs = self.glyphs_all
-        retain = table.subset_glyphs(self)
-        del self.glyphs
-        self.log.lapse("subset '%s'" % tag)
-        if not retain:
-          self.log(tag, "subsetted to empty; dropped")
-          del font[tag]
-        else:
-          self.log(tag, "subsetted")
-      else:
-        self.log(tag, "NOT subset; don't know how to subset; dropped")
-        del font[tag]
-
-    glyphOrder = font.getGlyphOrder()
-    glyphOrder = [g for g in glyphOrder if g in self.glyphs_all]
-    font.setGlyphOrder(glyphOrder)
-    font._buildReverseGlyphOrderDict()
-    self.log.lapse("subset GlyphOrder")
-
-  def _prune_post_subset(self, font):
-    for tag in font.keys():
-      if tag == 'GlyphOrder': continue
-      clazz = ttLib.getTableClass(tag)
-      if hasattr(clazz, 'prune_post_subset'):
-        table = font[tag]
-        retain = table.prune_post_subset(self.options)
-        self.log.lapse("prune  '%s'" % tag)
-        if not retain:
-          self.log(tag, "pruned to empty; dropped")
-          del font[tag]
-        else:
-          self.log(tag, "pruned")
-
-  def subset(self, font):
-
-    self._prune_pre_subset(font)
-    self._closure_glyphs(font)
-    self._subset_glyphs(font)
-    self._prune_post_subset(font)
-
-
-class Logger(object):
-
-  def __init__(self, verbose=False, xml=False, timing=False):
-    self.verbose = verbose
-    self.xml = xml
-    self.timing = timing
-    self.last_time = self.start_time = time.time()
-
-  def parse_opts(self, argv):
-    argv = argv[:]
-    for v in ['verbose', 'xml', 'timing']:
-      if "--"+v in argv:
-        setattr(self, v, True)
-        argv.remove("--"+v)
-    return argv
-
-  def __call__(self, *things):
-    if not self.verbose:
-      return
-    print(' '.join(str(x) for x in things))
-
-  def lapse(self, *things):
-    if not self.timing:
-      return
-    new_time = time.time()
-    print("Took %0.3fs to %s" %(new_time - self.last_time,
-                                 ' '.join(str(x) for x in things)))
-    self.last_time = new_time
-
-  def glyphs(self, glyphs, font=None):
-    if not self.verbose:
-      return
-    self("Names: ", sorted(glyphs))
-    if font:
-      reverseGlyphMap = font.getReverseGlyphMap()
-      self("Gids : ", sorted(reverseGlyphMap[g] for g in glyphs))
-
-  def font(self, font, file=sys.stdout):
-    if not self.xml:
-      return
-    from fontTools.misc import xmlWriter
-    writer = xmlWriter.XMLWriter(file)
-    for tag in font.keys():
-      writer.begintag(tag)
-      writer.newline()
-      font[tag].toXML(writer, font)
-      writer.endtag(tag)
-      writer.newline()
-
-
-def load_font(fontFile,
-              options,
-              allowVID=False,
-              checkChecksums=False,
-              dontLoadGlyphNames=False,
-              lazy=True):
-
-  font = ttLib.TTFont(fontFile,
-                      allowVID=allowVID,
-                      checkChecksums=checkChecksums,
-                      recalcBBoxes=options.recalc_bounds,
-                      recalcTimestamp=options.recalc_timestamp,
-                      lazy=lazy)
-
-  # Hack:
-  #
-  # If we don't need glyph names, change 'post' class to not try to
-  # load them.  It avoid lots of headache with broken fonts as well
-  # as loading time.
-  #
-  # Ideally ttLib should provide a way to ask it to skip loading
-  # glyph names.  But it currently doesn't provide such a thing.
-  #
-  if dontLoadGlyphNames:
-    post = ttLib.getTableClass('post')
-    saved = post.decode_format_2_0
-    post.decode_format_2_0 = post.decode_format_3_0
-    f = font['post']
-    if f.formatType == 2.0:
-      f.formatType = 3.0
-    post.decode_format_2_0 = saved
-
-  return font
-
-def save_font(font, outfile, options):
-  if options.flavor and not hasattr(font, 'flavor'):
-    raise Exception("fonttools version does not support flavors.")
-  font.flavor = options.flavor
-  font.save(outfile, reorderTables=options.canonical_order)
-
-def main(args):
-
-  log = Logger()
-  args = log.parse_opts(args)
-
-  options = Options()
-  args = options.parse_opts(args, ignore_unknown=['text'])
-
-  if len(args) < 2:
-    print("usage: pyftsubset font-file glyph... [--text=ABC]... [--option=value]...", file=sys.stderr)
-    sys.exit(1)
-
-  fontfile = args[0]
-  args = args[1:]
-
-  dontLoadGlyphNames =(not options.glyph_names and
-         all(any(g.startswith(p)
-             for p in ['gid', 'glyph', 'uni', 'U+'])
-              for g in args))
-
-  font = load_font(fontfile, options, dontLoadGlyphNames=dontLoadGlyphNames)
-  log.lapse("load font")
-  subsetter = Subsetter(options=options, log=log)
-
-  names = font.getGlyphNames()
-  log.lapse("loading glyph names")
-
-  glyphs = []
-  unicodes = []
-  text = ""
-  for g in args:
-    if g == '*':
-      glyphs.extend(font.getGlyphOrder())
-      continue
-    if g in names:
-      glyphs.append(g)
-      continue
-    if g.startswith('--text='):
-      text += g[7:]
-      continue
-    if g.startswith('uni') or g.startswith('U+'):
-      if g.startswith('uni') and len(g) > 3:
-        g = g[3:]
-      elif g.startswith('U+') and len(g) > 2:
-        g = g[2:]
-      u = int(g, 16)
-      unicodes.append(u)
-      continue
-    if g.startswith('gid') or g.startswith('glyph'):
-      if g.startswith('gid') and len(g) > 3:
-        g = g[3:]
-      elif g.startswith('glyph') and len(g) > 5:
-        g = g[5:]
-      try:
-        glyphs.append(font.getGlyphName(int(g), requireReal=True))
-      except ValueError:
-        raise Exception("Invalid glyph identifier: %s" % g)
-      continue
-    raise Exception("Invalid glyph identifier: %s" % g)
-  log.lapse("compile glyph list")
-  log("Unicodes:", unicodes)
-  log("Glyphs:", glyphs)
-
-  subsetter.populate(glyphs=glyphs, unicodes=unicodes, text=text)
-  subsetter.subset(font)
-
-  outfile = fontfile + '.subset'
-
-  save_font (font, outfile, options)
-  log.lapse("compile and save font")
-
-  log.last_time = log.start_time
-  log.lapse("make one with everything(TOTAL TIME)")
-
-  if log.verbose:
-    import os
-    log("Input  font: %d bytes" % os.path.getsize(fontfile))
-    log("Subset font: %d bytes" % os.path.getsize(outfile))
-
-  log.font(font)
-
-  font.close()
-
-
-__all__ = [
-  'Options',
-  'Subsetter',
-  'Logger',
-  'load_font',
-  'save_font',
-  'main'
-]
-
-if __name__ == '__main__':
-  main(sys.argv[1:])
diff --git a/Lib/fontTools/subset/__init__.py b/Lib/fontTools/subset/__init__.py
new file mode 100644
index 0000000..8468b7a
--- /dev/null
+++ b/Lib/fontTools/subset/__init__.py
@@ -0,0 +1,3313 @@
+# Copyright 2013 Google, Inc. All Rights Reserved.
+#
+# Google Author(s): Behdad Esfahbod
+
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.fixedTools import otRound
+from fontTools import ttLib
+from fontTools.ttLib.tables import otTables
+from fontTools.misc import psCharStrings
+from fontTools.pens.basePen import NullPen
+from fontTools.misc.loggingTools import Timer
+from fontTools.varLib import varStore
+import sys
+import struct
+import array
+import logging
+from collections import Counter
+from types import MethodType
+
+__usage__ = "pyftsubset font-file [glyph...] [--option=value]..."
+
+__doc__="""\
+pyftsubset -- OpenType font subsetter and optimizer
+
+  pyftsubset is an OpenType font subsetter and optimizer, based on fontTools.
+  It accepts any TT- or CFF-flavored OpenType (.otf or .ttf) or WOFF (.woff)
+  font file. The subsetted glyph set is based on the specified glyphs
+  or characters, and specified OpenType layout features.
+
+  The tool also performs some size-reducing optimizations, aimed for using
+  subset fonts as webfonts.  Individual optimizations can be enabled or
+  disabled, and are enabled by default when they are safe.
+
+Usage:
+  """+__usage__+"""
+
+  At least one glyph or one of --gids, --gids-file, --glyphs, --glyphs-file,
+  --text, --text-file, --unicodes, or --unicodes-file, must be specified.
+
+Arguments:
+  font-file
+    The input font file.
+  glyph
+    Specify one or more glyph identifiers to include in the subset. Must be
+    PS glyph names, or the special string '*' to keep the entire glyph set.
+
+Initial glyph set specification:
+  These options populate the initial glyph set. Same option can appear
+  multiple times, and the results are accummulated.
+  --gids=<NNN>[,<NNN>...]
+      Specify comma/whitespace-separated list of glyph IDs or ranges as
+      decimal numbers.  For example, --gids=10-12,14 adds glyphs with
+      numbers 10, 11, 12, and 14.
+  --gids-file=<path>
+      Like --gids but reads from a file. Anything after a '#' on any line
+      is ignored as comments.
+  --glyphs=<glyphname>[,<glyphname>...]
+      Specify comma/whitespace-separated PS glyph names to add to the subset.
+      Note that only PS glyph names are accepted, not gidNNN, U+XXXX, etc
+      that are accepted on the command line.  The special string '*' will keep
+      the entire glyph set.
+  --glyphs-file=<path>
+      Like --glyphs but reads from a file. Anything after a '#' on any line
+      is ignored as comments.
+  --text=<text>
+      Specify characters to include in the subset, as UTF-8 string.
+  --text-file=<path>
+      Like --text but reads from a file. Newline character are not added to
+      the subset.
+  --unicodes=<XXXX>[,<XXXX>...]
+      Specify comma/whitespace-separated list of Unicode codepoints or
+      ranges as hex numbers, optionally prefixed with 'U+', 'u', etc.
+      For example, --unicodes=41-5a,61-7a adds ASCII letters, so does
+      the more verbose --unicodes=U+0041-005A,U+0061-007A.
+      The special strings '*' will choose all Unicode characters mapped
+      by the font.
+  --unicodes-file=<path>
+      Like --unicodes, but reads from a file. Anything after a '#' on any
+      line in the file is ignored as comments.
+  --ignore-missing-glyphs
+      Do not fail if some requested glyphs or gids are not available in
+      the font.
+  --no-ignore-missing-glyphs
+      Stop and fail if some requested glyphs or gids are not available
+      in the font. [default]
+  --ignore-missing-unicodes [default]
+      Do not fail if some requested Unicode characters (including those
+      indirectly specified using --text or --text-file) are not available
+      in the font.
+  --no-ignore-missing-unicodes
+      Stop and fail if some requested Unicode characters are not available
+      in the font.
+      Note the default discrepancy between ignoring missing glyphs versus
+      unicodes.  This is for historical reasons and in the future
+      --no-ignore-missing-unicodes might become default.
+
+Other options:
+  For the other options listed below, to see the current value of the option,
+  pass a value of '?' to it, with or without a '='.
+  Examples:
+    $ pyftsubset --glyph-names?
+    Current setting for 'glyph-names' is: False
+    $ ./pyftsubset --name-IDs=?
+    Current setting for 'name-IDs' is: [0, 1, 2, 3, 4, 5, 6]
+    $ ./pyftsubset --hinting? --no-hinting --hinting?
+    Current setting for 'hinting' is: True
+    Current setting for 'hinting' is: False
+
+Output options:
+  --output-file=<path>
+      The output font file. If not specified, the subsetted font
+      will be saved in as font-file.subset.
+  --flavor=<type>
+      Specify flavor of output font file. May be 'woff' or 'woff2'.
+      Note that WOFF2 requires the Brotli Python extension, available
+      at https://github.com/google/brotli
+  --with-zopfli
+      Use the Google Zopfli algorithm to compress WOFF. The output is 3-8 %
+      smaller than pure zlib, but the compression speed is much slower.
+      The Zopfli Python bindings are available at:
+      https://pypi.python.org/pypi/zopfli
+
+Glyph set expansion:
+  These options control how additional glyphs are added to the subset.
+  --notdef-glyph
+      Add the '.notdef' glyph to the subset (ie, keep it). [default]
+  --no-notdef-glyph
+      Drop the '.notdef' glyph unless specified in the glyph set. This
+      saves a few bytes, but is not possible for Postscript-flavored
+      fonts, as those require '.notdef'. For TrueType-flavored fonts,
+      this works fine as long as no unsupported glyphs are requested
+      from the font.
+  --notdef-outline
+      Keep the outline of '.notdef' glyph. The '.notdef' glyph outline is
+      used when glyphs not supported by the font are to be shown. It is not
+      needed otherwise.
+  --no-notdef-outline
+      When including a '.notdef' glyph, remove its outline. This saves
+      a few bytes. [default]
+  --recommended-glyphs
+      Add glyphs 0, 1, 2, and 3 to the subset, as recommended for
+      TrueType-flavored fonts: '.notdef', 'NULL' or '.null', 'CR', 'space'.
+      Some legacy software might require this, but no modern system does.
+  --no-recommended-glyphs
+      Do not add glyphs 0, 1, 2, and 3 to the subset, unless specified in
+      glyph set. [default]
+  --layout-features[+|-]=<feature>[,<feature>...]
+      Specify (=), add to (+=) or exclude from (-=) the comma-separated
+      set of OpenType layout feature tags that will be preserved.
+      Glyph variants used by the preserved features are added to the
+      specified subset glyph set. By default, 'calt', 'ccmp', 'clig', 'curs',
+      'dnom', 'frac', 'kern', 'liga', 'locl', 'mark', 'mkmk', 'numr', 'rclt',
+      'rlig', 'rvrn', and all features required for script shaping are
+      preserved. To see the full list, try '--layout-features=?'.
+      Use '*' to keep all features.
+      Multiple --layout-features options can be provided if necessary.
+      Examples:
+        --layout-features+=onum,pnum,ss01
+            * Keep the default set of features and 'onum', 'pnum', 'ss01'.
+        --layout-features-='mark','mkmk'
+            * Keep the default set of features but drop 'mark' and 'mkmk'.
+        --layout-features='kern'
+            * Only keep the 'kern' feature, drop all others.
+        --layout-features=''
+            * Drop all features.
+        --layout-features='*'
+            * Keep all features.
+        --layout-features+=aalt --layout-features-=vrt2
+            * Keep default set of features plus 'aalt', but drop 'vrt2'.
+
+Hinting options:
+  --hinting
+      Keep hinting [default]
+  --no-hinting
+      Drop glyph-specific hinting and font-wide hinting tables, as well
+      as remove hinting-related bits and pieces from other tables (eg. GPOS).
+      See --hinting-tables for list of tables that are dropped by default.
+      Instructions and hints are stripped from 'glyf' and 'CFF ' tables
+      respectively. This produces (sometimes up to 30%) smaller fonts that
+      are suitable for extremely high-resolution systems, like high-end
+      mobile devices and retina displays.
+
+Optimization options:
+  --desubroutinize
+      Remove CFF use of subroutinizes.  Subroutinization is a way to make CFF
+      fonts smaller.  For small subsets however, desubroutinizing might make
+      the font smaller.  It has even been reported that desubroutinized CFF
+      fonts compress better (produce smaller output) WOFF and WOFF2 fonts.
+      Also see note under --no-hinting.
+  --no-desubroutinize [default]
+      Leave CFF subroutinizes as is, only throw away unused subroutinizes.
+
+Font table options:
+  --drop-tables[+|-]=<table>[,<table>...]
+      Specify (=), add to (+=) or exclude from (-=) the comma-separated
+      set of tables that will be be dropped.
+      By default, the following tables are dropped:
+      'BASE', 'JSTF', 'DSIG', 'EBDT', 'EBLC', 'EBSC', 'SVG ', 'PCLT', 'LTSH'
+      and Graphite tables: 'Feat', 'Glat', 'Gloc', 'Silf', 'Sill'
+      and color tables: 'CBLC', 'CBDT', 'sbix'.
+      The tool will attempt to subset the remaining tables.
+      Examples:
+        --drop-tables-='SVG '
+            * Drop the default set of tables but keep 'SVG '.
+        --drop-tables+=GSUB
+            * Drop the default set of tables and 'GSUB'.
+        --drop-tables=DSIG
+            * Only drop the 'DSIG' table, keep all others.
+        --drop-tables=
+            * Keep all tables.
+  --no-subset-tables+=<table>[,<table>...]
+      Add to the set of tables that will not be subsetted.
+      By default, the following tables are included in this list, as
+      they do not need subsetting (ignore the fact that 'loca' is listed
+      here): 'gasp', 'head', 'hhea', 'maxp', 'vhea', 'OS/2', 'loca', 'name',
+      'cvt ', 'fpgm', 'prep', 'VMDX', 'DSIG', 'CPAL', 'MVAR', 'cvar', 'STAT'.
+      By default, tables that the tool does not know how to subset and are not
+      specified here will be dropped from the font, unless --passthrough-tables
+      option is passed.
+      Example:
+         --no-subset-tables+=FFTM
+            * Keep 'FFTM' table in the font by preventing subsetting.
+  --passthrough-tables
+      Do not drop tables that the tool does not know how to subset.
+  --no-passthrough-tables
+      Tables that the tool does not know how to subset and are not specified
+      in --no-subset-tables will be dropped from the font. [default]
+  --hinting-tables[-]=<table>[,<table>...]
+      Specify (=), add to (+=) or exclude from (-=) the list of font-wide
+      hinting tables that will be dropped if --no-hinting is specified,
+      Examples:
+        --hinting-tables-='VDMX'
+            * Drop font-wide hinting tables except 'VDMX'.
+        --hinting-tables=''
+            * Keep all font-wide hinting tables (but strip hints from glyphs).
+  --legacy-kern
+      Keep TrueType 'kern' table even when OpenType 'GPOS' is available.
+  --no-legacy-kern
+      Drop TrueType 'kern' table if OpenType 'GPOS' is available. [default]
+
+Font naming options:
+  These options control what is retained in the 'name' table. For numerical
+  codes, see: http://www.microsoft.com/typography/otspec/name.htm
+  --name-IDs[+|-]=<nameID>[,<nameID>...]
+      Specify (=), add to (+=) or exclude from (-=) the set of 'name' table
+      entry nameIDs that will be preserved. By default, only nameIDs between 0
+      and 6 are preserved, the rest are dropped. Use '*' to keep all entries.
+      Examples:
+        --name-IDs+=7,8,9
+            * Also keep Trademark, Manufacturer and Designer name entries.
+        --name-IDs=''
+            * Drop all 'name' table entries.
+        --name-IDs='*'
+            * keep all 'name' table entries
+  --name-legacy
+      Keep legacy (non-Unicode) 'name' table entries (0.x, 1.x etc.).
+      XXX Note: This might be needed for some fonts that have no Unicode name
+      entires for English. See: https://github.com/behdad/fonttools/issues/146
+  --no-name-legacy
+      Drop legacy (non-Unicode) 'name' table entries [default]
+  --name-languages[+|-]=<langID>[,<langID>]
+      Specify (=), add to (+=) or exclude from (-=) the set of 'name' table
+      langIDs that will be preserved. By default only records with langID
+      0x0409 (English) are preserved. Use '*' to keep all langIDs.
+  --obfuscate-names
+      Make the font unusable as a system font by replacing name IDs 1, 2, 3, 4,
+      and 6 with dummy strings (it is still fully functional as webfont).
+
+Glyph naming and encoding options:
+  --glyph-names
+      Keep PS glyph names in TT-flavored fonts. In general glyph names are
+      not needed for correct use of the font. However, some PDF generators
+      and PDF viewers might rely on glyph names to extract Unicode text
+      from PDF documents.
+  --no-glyph-names
+      Drop PS glyph names in TT-flavored fonts, by using 'post' table
+      version 3.0. [default]
+  --legacy-cmap
+      Keep the legacy 'cmap' subtables (0.x, 1.x, 4.x etc.).
+  --no-legacy-cmap
+      Drop the legacy 'cmap' subtables. [default]
+  --symbol-cmap
+      Keep the 3.0 symbol 'cmap'.
+  --no-symbol-cmap
+      Drop the 3.0 symbol 'cmap'. [default]
+
+Other font-specific options:
+  --recalc-bounds
+      Recalculate font bounding boxes.
+  --no-recalc-bounds
+      Keep original font bounding boxes. This is faster and still safe
+      for all practical purposes. [default]
+  --recalc-timestamp
+      Set font 'modified' timestamp to current time.
+  --no-recalc-timestamp
+      Do not modify font 'modified' timestamp. [default]
+  --canonical-order
+      Order tables as recommended in the OpenType standard. This is not
+      required by the standard, nor by any known implementation.
+  --no-canonical-order
+      Keep original order of font tables. This is faster. [default]
+  --prune-unicode-ranges
+      Update the 'OS/2 ulUnicodeRange*' bits after subsetting. The Unicode
+      ranges defined in the OpenType specification v1.7 are intersected with
+      the Unicode codepoints specified in the font's Unicode 'cmap' subtables:
+      when no overlap is found, the bit will be switched off. However, it will
+      *not* be switched on if an intersection is found.  [default]
+  --no-prune-unicode-ranges
+      Don't change the 'OS/2 ulUnicodeRange*' bits.
+  --recalc-average-width
+      Update the 'OS/2 xAvgCharWidth' field after subsetting.
+  --no-recalc-average-width
+      Don't change the 'OS/2 xAvgCharWidth' field. [default]
+  --font-number=<number>
+      Select font number for TrueType Collection (.ttc/.otc), starting from 0.
+
+Application options:
+  --verbose
+      Display verbose information of the subsetting process.
+  --timing
+      Display detailed timing information of the subsetting process.
+  --xml
+      Display the TTX XML representation of subsetted font.
+
+Example:
+  Produce a subset containing the characters ' !"#$%' without performing
+  size-reducing optimizations:
+
+  $ pyftsubset font.ttf --unicodes="U+0020-0025" \\
+    --layout-features='*' --glyph-names --symbol-cmap --legacy-cmap \\
+    --notdef-glyph --notdef-outline --recommended-glyphs \\
+    --name-IDs='*' --name-legacy --name-languages='*'
+"""
+
+
+log = logging.getLogger("fontTools.subset")
+
+def _log_glyphs(self, glyphs, font=None):
+	self.info("Glyph names: %s", sorted(glyphs))
+	if font:
+		reverseGlyphMap = font.getReverseGlyphMap()
+		self.info("Glyph IDs:   %s", sorted(reverseGlyphMap[g] for g in glyphs))
+
+# bind "glyphs" function to 'log' object
+log.glyphs = MethodType(_log_glyphs, log)
+
+# I use a different timing channel so I can configure it separately from the
+# main module's logger
+timer = Timer(logger=logging.getLogger("fontTools.subset.timer"))
+
+
+def _add_method(*clazzes):
+	"""Returns a decorator function that adds a new method to one or
+	more classes."""
+	def wrapper(method):
+		done = []
+		for clazz in clazzes:
+			if clazz in done: continue # Support multiple names of a clazz
+			done.append(clazz)
+			assert clazz.__name__ != 'DefaultTable', \
+					'Oops, table class not found.'
+			assert not hasattr(clazz, method.__name__), \
+					"Oops, class '%s' has method '%s'." % (clazz.__name__,
+									       method.__name__)
+			setattr(clazz, method.__name__, method)
+		return None
+	return wrapper
+
+def _uniq_sort(l):
+	return sorted(set(l))
+
+def _set_update(s, *others):
+	# Jython's set.update only takes one other argument.
+	# Emulate real set.update...
+	for other in others:
+		s.update(other)
+
+def _dict_subset(d, glyphs):
+	return {g:d[g] for g in glyphs}
+
+
+@_add_method(otTables.Coverage)
+def intersect(self, glyphs):
+	"""Returns ascending list of matching coverage values."""
+	return [i for i,g in enumerate(self.glyphs) if g in glyphs]
+
+@_add_method(otTables.Coverage)
+def intersect_glyphs(self, glyphs):
+	"""Returns set of intersecting glyphs."""
+	return set(g for g in self.glyphs if g in glyphs)
+
+@_add_method(otTables.Coverage)
+def subset(self, glyphs):
+	"""Returns ascending list of remaining coverage values."""
+	indices = self.intersect(glyphs)
+	self.glyphs = [g for g in self.glyphs if g in glyphs]
+	return indices
+
+@_add_method(otTables.Coverage)
+def remap(self, coverage_map):
+	"""Remaps coverage."""
+	self.glyphs = [self.glyphs[i] for i in coverage_map]
+
+@_add_method(otTables.ClassDef)
+def intersect(self, glyphs):
+	"""Returns ascending list of matching class values."""
+	return _uniq_sort(
+		 ([0] if any(g not in self.classDefs for g in glyphs) else []) +
+			[v for g,v in self.classDefs.items() if g in glyphs])
+
+@_add_method(otTables.ClassDef)
+def intersect_class(self, glyphs, klass):
+	"""Returns set of glyphs matching class."""
+	if klass == 0:
+		return set(g for g in glyphs if g not in self.classDefs)
+	return set(g for g,v in self.classDefs.items()
+		     if v == klass and g in glyphs)
+
+@_add_method(otTables.ClassDef)
+def subset(self, glyphs, remap=False):
+	"""Returns ascending list of remaining classes."""
+	self.classDefs = {g:v for g,v in self.classDefs.items() if g in glyphs}
+	# Note: while class 0 has the special meaning of "not matched",
+	# if no glyph will ever /not match/, we can optimize class 0 out too.
+	indices = _uniq_sort(
+		 ([0] if any(g not in self.classDefs for g in glyphs) else []) +
+			list(self.classDefs.values()))
+	if remap:
+		self.remap(indices)
+	return indices
+
+@_add_method(otTables.ClassDef)
+def remap(self, class_map):
+	"""Remaps classes."""
+	self.classDefs = {g:class_map.index(v) for g,v in self.classDefs.items()}
+
+@_add_method(otTables.SingleSubst)
+def closure_glyphs(self, s, cur_glyphs):
+	s.glyphs.update(v for g,v in self.mapping.items() if g in cur_glyphs)
+
+@_add_method(otTables.SingleSubst)
+def subset_glyphs(self, s):
+	self.mapping = {g:v for g,v in self.mapping.items()
+					if g in s.glyphs and v in s.glyphs}
+	return bool(self.mapping)
+
+@_add_method(otTables.MultipleSubst)
+def closure_glyphs(self, s, cur_glyphs):
+	for glyph, subst in self.mapping.items():
+		if glyph in cur_glyphs:
+			_set_update(s.glyphs, subst)
+
+@_add_method(otTables.MultipleSubst)
+def subset_glyphs(self, s):
+	self.mapping = {g:v for g,v in self.mapping.items()
+					if g in s.glyphs and all(sub in s.glyphs for sub in v)}
+	return bool(self.mapping)
+
+@_add_method(otTables.AlternateSubst)
+def closure_glyphs(self, s, cur_glyphs):
+	_set_update(s.glyphs, *(vlist for g,vlist in self.alternates.items()
+				      if g in cur_glyphs))
+
+@_add_method(otTables.AlternateSubst)
+def subset_glyphs(self, s):
+	self.alternates = {g:vlist
+					   for g,vlist in self.alternates.items()
+					   if g in s.glyphs and
+					   all(v in s.glyphs for v in vlist)}
+	return bool(self.alternates)
+
+@_add_method(otTables.LigatureSubst)
+def closure_glyphs(self, s, cur_glyphs):
+	_set_update(s.glyphs, *([seq.LigGlyph for seq in seqs
+					      if all(c in s.glyphs for c in seq.Component)]
+				for g,seqs in self.ligatures.items()
+				if g in cur_glyphs))
+
+@_add_method(otTables.LigatureSubst)
+def subset_glyphs(self, s):
+	self.ligatures = {g:v for g,v in self.ligatures.items()
+					  if g in s.glyphs}
+	self.ligatures = {g:[seq for seq in seqs
+				 if seq.LigGlyph in s.glyphs and
+					all(c in s.glyphs for c in seq.Component)]
+			  for g,seqs in self.ligatures.items()}
+	self.ligatures = {g:v for g,v in self.ligatures.items() if v}
+	return bool(self.ligatures)
+
+@_add_method(otTables.ReverseChainSingleSubst)
+def closure_glyphs(self, s, cur_glyphs):
+	if self.Format == 1:
+		indices = self.Coverage.intersect(cur_glyphs)
+		if(not indices or
+		   not all(c.intersect(s.glyphs)
+				   for c in self.LookAheadCoverage + self.BacktrackCoverage)):
+			return
+		s.glyphs.update(self.Substitute[i] for i in indices)
+	else:
+		assert 0, "unknown format: %s" % self.Format
+
+@_add_method(otTables.ReverseChainSingleSubst)
+def subset_glyphs(self, s):
+	if self.Format == 1:
+		indices = self.Coverage.subset(s.glyphs)
+		self.Substitute = [self.Substitute[i] for i in indices]
+		# Now drop rules generating glyphs we don't want
+		indices = [i for i,sub in enumerate(self.Substitute)
+				 if sub in s.glyphs]
+		self.Substitute = [self.Substitute[i] for i in indices]
+		self.Coverage.remap(indices)
+		self.GlyphCount = len(self.Substitute)
+		return bool(self.GlyphCount and
+			    all(c.subset(s.glyphs)
+				for c in self.LookAheadCoverage+self.BacktrackCoverage))
+	else:
+		assert 0, "unknown format: %s" % self.Format
+
+@_add_method(otTables.SinglePos)
+def subset_glyphs(self, s):
+	if self.Format == 1:
+		return len(self.Coverage.subset(s.glyphs))
+	elif self.Format == 2:
+		indices = self.Coverage.subset(s.glyphs)
+		values = self.Value
+		count = len(values)
+		self.Value = [values[i] for i in indices if i < count]
+		self.ValueCount = len(self.Value)
+		return bool(self.ValueCount)
+	else:
+		assert 0, "unknown format: %s" % self.Format
+
+@_add_method(otTables.SinglePos)
+def prune_post_subset(self, font, options):
+	if not options.hinting:
+		# Drop device tables
+		self.ValueFormat &= ~0x00F0
+	return True
+
+@_add_method(otTables.PairPos)
+def subset_glyphs(self, s):
+	if self.Format == 1:
+		indices = self.Coverage.subset(s.glyphs)
+		pairs = self.PairSet
+		count = len(pairs)
+		self.PairSet = [pairs[i] for i in indices if i < count]
+		for p in self.PairSet:
+			p.PairValueRecord = [r for r in p.PairValueRecord if r.SecondGlyph in s.glyphs]
+			p.PairValueCount = len(p.PairValueRecord)
+		# Remove empty pairsets
+		indices = [i for i,p in enumerate(self.PairSet) if p.PairValueCount]
+		self.Coverage.remap(indices)
+		self.PairSet = [self.PairSet[i] for i in indices]
+		self.PairSetCount = len(self.PairSet)
+		return bool(self.PairSetCount)
+	elif self.Format == 2:
+		class1_map = [c for c in self.ClassDef1.subset(s.glyphs, remap=True) if c < self.Class1Count]
+		class2_map = [c for c in self.ClassDef2.subset(s.glyphs, remap=True) if c < self.Class2Count]
+		self.Class1Record = [self.Class1Record[i] for i in class1_map]
+		for c in self.Class1Record:
+			c.Class2Record = [c.Class2Record[i] for i in class2_map]
+		self.Class1Count = len(class1_map)
+		self.Class2Count = len(class2_map)
+		return bool(self.Class1Count and
+					self.Class2Count and
+					self.Coverage.subset(s.glyphs))
+	else:
+		assert 0, "unknown format: %s" % self.Format
+
+@_add_method(otTables.PairPos)
+def prune_post_subset(self, font, options):
+	if not options.hinting:
+		# Drop device tables
+		self.ValueFormat1 &= ~0x00F0
+		self.ValueFormat2 &= ~0x00F0
+	return True
+
+@_add_method(otTables.CursivePos)
+def subset_glyphs(self, s):
+	if self.Format == 1:
+		indices = self.Coverage.subset(s.glyphs)
+		records = self.EntryExitRecord
+		count = len(records)
+		self.EntryExitRecord = [records[i] for i in indices if i < count]
+		self.EntryExitCount = len(self.EntryExitRecord)
+		return bool(self.EntryExitCount)
+	else:
+		assert 0, "unknown format: %s" % self.Format
+
+@_add_method(otTables.Anchor)
+def prune_hints(self):
+	# Drop device tables / contour anchor point
+	self.ensureDecompiled()
+	self.Format = 1
+
+@_add_method(otTables.CursivePos)
+def prune_post_subset(self, font, options):
+	if not options.hinting:
+		for rec in self.EntryExitRecord:
+			if rec.EntryAnchor: rec.EntryAnchor.prune_hints()
+			if rec.ExitAnchor: rec.ExitAnchor.prune_hints()
+	return True
+
+@_add_method(otTables.MarkBasePos)
+def subset_glyphs(self, s):
+	if self.Format == 1:
+		mark_indices = self.MarkCoverage.subset(s.glyphs)
+		self.MarkArray.MarkRecord = [self.MarkArray.MarkRecord[i] for i in mark_indices]
+		self.MarkArray.MarkCount = len(self.MarkArray.MarkRecord)
+		base_indices = self.BaseCoverage.subset(s.glyphs)
+		self.BaseArray.BaseRecord = [self.BaseArray.BaseRecord[i] for i in base_indices]
+		self.BaseArray.BaseCount = len(self.BaseArray.BaseRecord)
+		# Prune empty classes
+		class_indices = _uniq_sort(v.Class for v in self.MarkArray.MarkRecord)
+		self.ClassCount = len(class_indices)
+		for m in self.MarkArray.MarkRecord:
+			m.Class = class_indices.index(m.Class)
+		for b in self.BaseArray.BaseRecord:
+			b.BaseAnchor = [b.BaseAnchor[i] for i in class_indices]
+		return bool(self.ClassCount and
+					self.MarkArray.MarkCount and
+					self.BaseArray.BaseCount)
+	else:
+		assert 0, "unknown format: %s" % self.Format
+
+@_add_method(otTables.MarkBasePos)
+def prune_post_subset(self, font, options):
+		if not options.hinting:
+			for m in self.MarkArray.MarkRecord:
+				if m.MarkAnchor:
+					m.MarkAnchor.prune_hints()
+			for b in self.BaseArray.BaseRecord:
+				for a in b.BaseAnchor:
+					if a:
+						a.prune_hints()
+		return True
+
+@_add_method(otTables.MarkLigPos)
+def subset_glyphs(self, s):
+	if self.Format == 1:
+		mark_indices = self.MarkCoverage.subset(s.glyphs)
+		self.MarkArray.MarkRecord = [self.MarkArray.MarkRecord[i] for i in mark_indices]
+		self.MarkArray.MarkCount = len(self.MarkArray.MarkRecord)
+		ligature_indices = self.LigatureCoverage.subset(s.glyphs)
+		self.LigatureArray.LigatureAttach = [self.LigatureArray.LigatureAttach[i] for i in ligature_indices]
+		self.LigatureArray.LigatureCount = len(self.LigatureArray.LigatureAttach)
+		# Prune empty classes
+		class_indices = _uniq_sort(v.Class for v in self.MarkArray.MarkRecord)
+		self.ClassCount = len(class_indices)
+		for m in self.MarkArray.MarkRecord:
+			m.Class = class_indices.index(m.Class)
+		for l in self.LigatureArray.LigatureAttach:
+			for c in l.ComponentRecord:
+				c.LigatureAnchor = [c.LigatureAnchor[i] for i in class_indices]
+		return bool(self.ClassCount and
+					self.MarkArray.MarkCount and
+					self.LigatureArray.LigatureCount)
+	else:
+		assert 0, "unknown format: %s" % self.Format
+
+@_add_method(otTables.MarkLigPos)
+def prune_post_subset(self, font, options):
+		if not options.hinting:
+			for m in self.MarkArray.MarkRecord:
+				if m.MarkAnchor:
+					m.MarkAnchor.prune_hints()
+			for l in self.LigatureArray.LigatureAttach:
+				for c in l.ComponentRecord:
+					for a in c.LigatureAnchor:
+						if a:
+							a.prune_hints()
+		return True
+
+@_add_method(otTables.MarkMarkPos)
+def subset_glyphs(self, s):
+	if self.Format == 1:
+		mark1_indices = self.Mark1Coverage.subset(s.glyphs)
+		self.Mark1Array.MarkRecord = [self.Mark1Array.MarkRecord[i] for i in mark1_indices]
+		self.Mark1Array.MarkCount = len(self.Mark1Array.MarkRecord)
+		mark2_indices = self.Mark2Coverage.subset(s.glyphs)
+		self.Mark2Array.Mark2Record = [self.Mark2Array.Mark2Record[i] for i in mark2_indices]
+		self.Mark2Array.MarkCount = len(self.Mark2Array.Mark2Record)
+		# Prune empty classes
+		class_indices = _uniq_sort(v.Class for v in self.Mark1Array.MarkRecord)
+		self.ClassCount = len(class_indices)
+		for m in self.Mark1Array.MarkRecord:
+			m.Class = class_indices.index(m.Class)
+		for b in self.Mark2Array.Mark2Record:
+			b.Mark2Anchor = [b.Mark2Anchor[i] for i in class_indices]
+		return bool(self.ClassCount and
+					self.Mark1Array.MarkCount and
+					self.Mark2Array.MarkCount)
+	else:
+		assert 0, "unknown format: %s" % self.Format
+
+@_add_method(otTables.MarkMarkPos)
+def prune_post_subset(self, font, options):
+		if not options.hinting:
+			# Drop device tables or contour anchor point
+			for m in self.Mark1Array.MarkRecord:
+				if m.MarkAnchor:
+					m.MarkAnchor.prune_hints()
+			for b in self.Mark2Array.Mark2Record:
+				for m in b.Mark2Anchor:
+					if m:
+						m.prune_hints()
+		return True
+
+@_add_method(otTables.SingleSubst,
+			 otTables.MultipleSubst,
+			 otTables.AlternateSubst,
+			 otTables.LigatureSubst,
+			 otTables.ReverseChainSingleSubst,
+			 otTables.SinglePos,
+			 otTables.PairPos,
+			 otTables.CursivePos,
+			 otTables.MarkBasePos,
+			 otTables.MarkLigPos,
+			 otTables.MarkMarkPos)
+def subset_lookups(self, lookup_indices):
+	pass
+
+@_add_method(otTables.SingleSubst,
+			 otTables.MultipleSubst,
+			 otTables.AlternateSubst,
+			 otTables.LigatureSubst,
+			 otTables.ReverseChainSingleSubst,
+			 otTables.SinglePos,
+			 otTables.PairPos,
+			 otTables.CursivePos,
+			 otTables.MarkBasePos,
+			 otTables.MarkLigPos,
+			 otTables.MarkMarkPos)
+def collect_lookups(self):
+	return []
+
+@_add_method(otTables.SingleSubst,
+			 otTables.MultipleSubst,
+			 otTables.AlternateSubst,
+			 otTables.LigatureSubst,
+			 otTables.ReverseChainSingleSubst,
+			 otTables.ContextSubst,
+			 otTables.ChainContextSubst,
+			 otTables.ContextPos,
+			 otTables.ChainContextPos)
+def prune_post_subset(self, font, options):
+	return True
+
+@_add_method(otTables.SingleSubst,
+			 otTables.AlternateSubst,
+			 otTables.ReverseChainSingleSubst)
+def may_have_non_1to1(self):
+	return False
+
+@_add_method(otTables.MultipleSubst,
+			 otTables.LigatureSubst,
+			 otTables.ContextSubst,
+			 otTables.ChainContextSubst)
+def may_have_non_1to1(self):
+	return True
+
+@_add_method(otTables.ContextSubst,
+			 otTables.ChainContextSubst,
+			 otTables.ContextPos,
+			 otTables.ChainContextPos)
+def __subset_classify_context(self):
+
+	class ContextHelper(object):
+		def __init__(self, klass, Format):
+			if klass.__name__.endswith('Subst'):
+				Typ = 'Sub'
+				Type = 'Subst'
+			else:
+				Typ = 'Pos'
+				Type = 'Pos'
+			if klass.__name__.startswith('Chain'):
+				Chain = 'Chain'
+				InputIdx = 1
+				DataLen = 3
+			else:
+				Chain = ''
+				InputIdx = 0
+				DataLen = 1
+			ChainTyp = Chain+Typ
+
+			self.Typ = Typ
+			self.Type = Type
+			self.Chain = Chain
+			self.ChainTyp = ChainTyp
+			self.InputIdx = InputIdx
+			self.DataLen = DataLen
+
+			self.LookupRecord = Type+'LookupRecord'
+
+			if Format == 1:
+				Coverage = lambda r: r.Coverage
+				ChainCoverage = lambda r: r.Coverage
+				ContextData = lambda r:(None,)
+				ChainContextData = lambda r:(None, None, None)
+				SetContextData = None
+				SetChainContextData = None
+				RuleData = lambda r:(r.Input,)
+				ChainRuleData = lambda r:(r.Backtrack, r.Input, r.LookAhead)
+				def SetRuleData(r, d):
+					(r.Input,) = d
+					(r.GlyphCount,) = (len(x)+1 for x in d)
+				def ChainSetRuleData(r, d):
+					(r.Backtrack, r.Input, r.LookAhead) = d
+					(r.BacktrackGlyphCount,r.InputGlyphCount,r.LookAheadGlyphCount,) = (len(d[0]),len(d[1])+1,len(d[2]))
+			elif Format == 2:
+				Coverage = lambda r: r.Coverage
+				ChainCoverage = lambda r: r.Coverage
+				ContextData = lambda r:(r.ClassDef,)
+				ChainContextData = lambda r:(r.BacktrackClassDef,
+							     r.InputClassDef,
+							     r.LookAheadClassDef)
+				def SetContextData(r, d):
+					(r.ClassDef,) = d
+				def SetChainContextData(r, d):
+					(r.BacktrackClassDef,
+					 r.InputClassDef,
+					 r.LookAheadClassDef) = d
+				RuleData = lambda r:(r.Class,)
+				ChainRuleData = lambda r:(r.Backtrack, r.Input, r.LookAhead)
+				def SetRuleData(r, d):
+					(r.Class,) = d
+					(r.GlyphCount,) = (len(x)+1 for x in d)
+				def ChainSetRuleData(r, d):
+					(r.Backtrack, r.Input, r.LookAhead) = d
+					(r.BacktrackGlyphCount,r.InputGlyphCount,r.LookAheadGlyphCount,) = (len(d[0]),len(d[1])+1,len(d[2]))
+			elif Format == 3:
+				Coverage = lambda r: r.Coverage[0]
+				ChainCoverage = lambda r: r.InputCoverage[0]
+				ContextData = None
+				ChainContextData = None
+				SetContextData = None
+				SetChainContextData = None
+				RuleData = lambda r: r.Coverage
+				ChainRuleData = lambda r:(r.BacktrackCoverage +
+							  r.InputCoverage +
+							  r.LookAheadCoverage)
+				def SetRuleData(r, d):
+					(r.Coverage,) = d
+					(r.GlyphCount,) = (len(x) for x in d)
+				def ChainSetRuleData(r, d):
+					(r.BacktrackCoverage, r.InputCoverage, r.LookAheadCoverage) = d
+					(r.BacktrackGlyphCount,r.InputGlyphCount,r.LookAheadGlyphCount,) = (len(x) for x in d)
+			else:
+				assert 0, "unknown format: %s" % Format
+
+			if Chain:
+				self.Coverage = ChainCoverage
+				self.ContextData = ChainContextData
+				self.SetContextData = SetChainContextData
+				self.RuleData = ChainRuleData
+				self.SetRuleData = ChainSetRuleData
+			else:
+				self.Coverage = Coverage
+				self.ContextData = ContextData
+				self.SetContextData = SetContextData
+				self.RuleData = RuleData
+				self.SetRuleData = SetRuleData
+
+			if Format == 1:
+				self.Rule = ChainTyp+'Rule'
+				self.RuleCount = ChainTyp+'RuleCount'
+				self.RuleSet = ChainTyp+'RuleSet'
+				self.RuleSetCount = ChainTyp+'RuleSetCount'
+				self.Intersect = lambda glyphs, c, r: [r] if r in glyphs else []
+			elif Format == 2:
+				self.Rule = ChainTyp+'ClassRule'
+				self.RuleCount = ChainTyp+'ClassRuleCount'
+				self.RuleSet = ChainTyp+'ClassSet'
+				self.RuleSetCount = ChainTyp+'ClassSetCount'
+				self.Intersect = lambda glyphs, c, r: (c.intersect_class(glyphs, r) if c
+								       else (set(glyphs) if r == 0 else set()))
+
+				self.ClassDef = 'InputClassDef' if Chain else 'ClassDef'
+				self.ClassDefIndex = 1 if Chain else 0
+				self.Input = 'Input' if Chain else 'Class'
+
+	if self.Format not in [1, 2, 3]:
+		return None	# Don't shoot the messenger; let it go
+	if not hasattr(self.__class__, "__ContextHelpers"):
+		self.__class__.__ContextHelpers = {}
+	if self.Format not in self.__class__.__ContextHelpers:
+		helper = ContextHelper(self.__class__, self.Format)
+		self.__class__.__ContextHelpers[self.Format] = helper
+	return self.__class__.__ContextHelpers[self.Format]
+
+@_add_method(otTables.ContextSubst,
+			 otTables.ChainContextSubst)
+def closure_glyphs(self, s, cur_glyphs):
+	c = self.__subset_classify_context()
+
+	indices = c.Coverage(self).intersect(cur_glyphs)
+	if not indices:
+		return []
+	cur_glyphs = c.Coverage(self).intersect_glyphs(cur_glyphs)
+
+	if self.Format == 1:
+		ContextData = c.ContextData(self)
+		rss = getattr(self, c.RuleSet)
+		rssCount = getattr(self, c.RuleSetCount)
+		for i in indices:
+			if i >= rssCount or not rss[i]: continue
+			for r in getattr(rss[i], c.Rule):
+				if not r: continue
+				if not all(all(c.Intersect(s.glyphs, cd, k) for k in klist)
+					   for cd,klist in zip(ContextData, c.RuleData(r))):
+					continue
+				chaos = set()
+				for ll in getattr(r, c.LookupRecord):
+					if not ll: continue
+					seqi = ll.SequenceIndex
+					if seqi in chaos:
+						# TODO Can we improve this?
+						pos_glyphs = None
+					else:
+						if seqi == 0:
+							pos_glyphs = frozenset([c.Coverage(self).glyphs[i]])
+						else:
+							pos_glyphs = frozenset([r.Input[seqi - 1]])
+					lookup = s.table.LookupList.Lookup[ll.LookupListIndex]
+					chaos.add(seqi)
+					if lookup.may_have_non_1to1():
+						chaos.update(range(seqi, len(r.Input)+2))
+					lookup.closure_glyphs(s, cur_glyphs=pos_glyphs)
+	elif self.Format == 2:
+		ClassDef = getattr(self, c.ClassDef)
+		indices = ClassDef.intersect(cur_glyphs)
+		ContextData = c.ContextData(self)
+		rss = getattr(self, c.RuleSet)
+		rssCount = getattr(self, c.RuleSetCount)
+		for i in indices:
+			if i >= rssCount or not rss[i]: continue
+			for r in getattr(rss[i], c.Rule):
+				if not r: continue
+				if not all(all(c.Intersect(s.glyphs, cd, k) for k in klist)
+						   for cd,klist in zip(ContextData, c.RuleData(r))):
+					continue
+				chaos = set()
+				for ll in getattr(r, c.LookupRecord):
+					if not ll: continue
+					seqi = ll.SequenceIndex
+					if seqi in chaos:
+						# TODO Can we improve this?
+						pos_glyphs = None
+					else:
+						if seqi == 0:
+							pos_glyphs = frozenset(ClassDef.intersect_class(cur_glyphs, i))
+						else:
+							pos_glyphs = frozenset(ClassDef.intersect_class(s.glyphs, getattr(r, c.Input)[seqi - 1]))
+					lookup = s.table.LookupList.Lookup[ll.LookupListIndex]
+					chaos.add(seqi)
+					if lookup.may_have_non_1to1():
+						chaos.update(range(seqi, len(getattr(r, c.Input))+2))
+					lookup.closure_glyphs(s, cur_glyphs=pos_glyphs)
+	elif self.Format == 3:
+		if not all(x.intersect(s.glyphs) for x in c.RuleData(self)):
+			return []
+		r = self
+		chaos = set()
+		for ll in getattr(r, c.LookupRecord):
+			if not ll: continue
+			seqi = ll.SequenceIndex
+			if seqi in chaos:
+				# TODO Can we improve this?
+				pos_glyphs = None
+			else:
+				if seqi == 0:
+					pos_glyphs = frozenset(cur_glyphs)
+				else:
+					pos_glyphs = frozenset(r.InputCoverage[seqi].intersect_glyphs(s.glyphs))
+			lookup = s.table.LookupList.Lookup[ll.LookupListIndex]
+			chaos.add(seqi)
+			if lookup.may_have_non_1to1():
+				chaos.update(range(seqi, len(r.InputCoverage)+1))
+			lookup.closure_glyphs(s, cur_glyphs=pos_glyphs)
+	else:
+		assert 0, "unknown format: %s" % self.Format
+
+@_add_method(otTables.ContextSubst,
+			 otTables.ContextPos,
+			 otTables.ChainContextSubst,
+			 otTables.ChainContextPos)
+def subset_glyphs(self, s):
+	c = self.__subset_classify_context()
+
+	if self.Format == 1:
+		indices = self.Coverage.subset(s.glyphs)
+		rss = getattr(self, c.RuleSet)
+		rssCount = getattr(self, c.RuleSetCount)
+		rss = [rss[i] for i in indices if i < rssCount]
+		for rs in rss:
+			if not rs: continue
+			ss = getattr(rs, c.Rule)
+			ss = [r for r in ss
+				  if r and all(all(g in s.glyphs for g in glist)
+					       for glist in c.RuleData(r))]
+			setattr(rs, c.Rule, ss)
+			setattr(rs, c.RuleCount, len(ss))
+		# Prune empty rulesets
+		indices = [i for i,rs in enumerate(rss) if rs and getattr(rs, c.Rule)]
+		self.Coverage.remap(indices)
+		rss = [rss[i] for i in indices]
+		setattr(self, c.RuleSet, rss)
+		setattr(self, c.RuleSetCount, len(rss))
+		return bool(rss)
+	elif self.Format == 2:
+		if not self.Coverage.subset(s.glyphs):
+			return False
+		ContextData = c.ContextData(self)
+		klass_maps = [x.subset(s.glyphs, remap=True) if x else None for x in ContextData]
+
+		# Keep rulesets for class numbers that survived.
+		indices = klass_maps[c.ClassDefIndex]
+		rss = getattr(self, c.RuleSet)
+		rssCount = getattr(self, c.RuleSetCount)
+		rss = [rss[i] for i in indices if i < rssCount]
+		del rssCount
+		# Delete, but not renumber, unreachable rulesets.
+		indices = getattr(self, c.ClassDef).intersect(self.Coverage.glyphs)
+		rss = [rss if i in indices else None for i,rss in enumerate(rss)]
+
+		for rs in rss:
+			if not rs: continue
+			ss = getattr(rs, c.Rule)
+			ss = [r for r in ss
+				  if r and all(all(k in klass_map for k in klist)
+					       for klass_map,klist in zip(klass_maps, c.RuleData(r)))]
+			setattr(rs, c.Rule, ss)
+			setattr(rs, c.RuleCount, len(ss))
+
+			# Remap rule classes
+			for r in ss:
+				c.SetRuleData(r, [[klass_map.index(k) for k in klist]
+						  for klass_map,klist in zip(klass_maps, c.RuleData(r))])
+
+		# Prune empty rulesets
+		rss = [rs if rs and getattr(rs, c.Rule) else None for rs in rss]
+		while rss and rss[-1] is None:
+			del rss[-1]
+		setattr(self, c.RuleSet, rss)
+		setattr(self, c.RuleSetCount, len(rss))
+
+		# TODO: We can do a second round of remapping class values based
+		# on classes that are actually used in at least one rule.	Right
+		# now we subset classes to c.glyphs only.	Or better, rewrite
+		# the above to do that.
+
+		return bool(rss)
+	elif self.Format == 3:
+		return all(x.subset(s.glyphs) for x in c.RuleData(self))
+	else:
+		assert 0, "unknown format: %s" % self.Format
+
+@_add_method(otTables.ContextSubst,
+			 otTables.ChainContextSubst,
+			 otTables.ContextPos,
+			 otTables.ChainContextPos)
+def subset_lookups(self, lookup_indices):
+	c = self.__subset_classify_context()
+
+	if self.Format in [1, 2]:
+		for rs in getattr(self, c.RuleSet):
+			if not rs: continue
+			for r in getattr(rs, c.Rule):
+				if not r: continue
+				setattr(r, c.LookupRecord,
+					[ll for ll in getattr(r, c.LookupRecord)
+					 if ll and ll.LookupListIndex in lookup_indices])
+				for ll in getattr(r, c.LookupRecord):
+					if not ll: continue
+					ll.LookupListIndex = lookup_indices.index(ll.LookupListIndex)
+	elif self.Format == 3:
+		setattr(self, c.LookupRecord,
+				[ll for ll in getattr(self, c.LookupRecord)
+				 if ll and ll.LookupListIndex in lookup_indices])
+		for ll in getattr(self, c.LookupRecord):
+			if not ll: continue
+			ll.LookupListIndex = lookup_indices.index(ll.LookupListIndex)
+	else:
+		assert 0, "unknown format: %s" % self.Format
+
+@_add_method(otTables.ContextSubst,
+			 otTables.ChainContextSubst,
+			 otTables.ContextPos,
+			 otTables.ChainContextPos)
+def collect_lookups(self):
+	c = self.__subset_classify_context()
+
+	if self.Format in [1, 2]:
+		return [ll.LookupListIndex
+			for rs in getattr(self, c.RuleSet) if rs
+			for r in getattr(rs, c.Rule) if r
+			for ll in getattr(r, c.LookupRecord) if ll]
+	elif self.Format == 3:
+		return [ll.LookupListIndex
+			for ll in getattr(self, c.LookupRecord) if ll]
+	else:
+		assert 0, "unknown format: %s" % self.Format
+
+@_add_method(otTables.ExtensionSubst)
+def closure_glyphs(self, s, cur_glyphs):
+	if self.Format == 1:
+		self.ExtSubTable.closure_glyphs(s, cur_glyphs)
+	else:
+		assert 0, "unknown format: %s" % self.Format
+
+@_add_method(otTables.ExtensionSubst)
+def may_have_non_1to1(self):
+	if self.Format == 1:
+		return self.ExtSubTable.may_have_non_1to1()
+	else:
+		assert 0, "unknown format: %s" % self.Format
+
+@_add_method(otTables.ExtensionSubst,
+			 otTables.ExtensionPos)
+def subset_glyphs(self, s):
+	if self.Format == 1:
+		return self.ExtSubTable.subset_glyphs(s)
+	else:
+		assert 0, "unknown format: %s" % self.Format
+
+@_add_method(otTables.ExtensionSubst,
+			 otTables.ExtensionPos)
+def prune_post_subset(self, font, options):
+	if self.Format == 1:
+		return self.ExtSubTable.prune_post_subset(font, options)
+	else:
+		assert 0, "unknown format: %s" % self.Format
+
+@_add_method(otTables.ExtensionSubst,
+			 otTables.ExtensionPos)
+def subset_lookups(self, lookup_indices):
+	if self.Format == 1:
+		return self.ExtSubTable.subset_lookups(lookup_indices)
+	else:
+		assert 0, "unknown format: %s" % self.Format
+
+@_add_method(otTables.ExtensionSubst,
+			 otTables.ExtensionPos)
+def collect_lookups(self):
+	if self.Format == 1:
+		return self.ExtSubTable.collect_lookups()
+	else:
+		assert 0, "unknown format: %s" % self.Format
+
+@_add_method(otTables.Lookup)
+def closure_glyphs(self, s, cur_glyphs=None):
+	if cur_glyphs is None:
+		cur_glyphs = frozenset(s.glyphs)
+
+	# Memoize
+	key = id(self)
+	doneLookups = s._doneLookups
+	count,covered = doneLookups.get(key, (0, None))
+	if count != len(s.glyphs):
+		count,covered = doneLookups[key] = (len(s.glyphs), set())
+	if cur_glyphs.issubset(covered):
+		return
+	covered.update(cur_glyphs)
+
+	for st in self.SubTable:
+		if not st: continue
+		st.closure_glyphs(s, cur_glyphs)
+
+@_add_method(otTables.Lookup)
+def subset_glyphs(self, s):
+	self.SubTable = [st for st in self.SubTable if st and st.subset_glyphs(s)]
+	self.SubTableCount = len(self.SubTable)
+	return bool(self.SubTableCount)
+
+@_add_method(otTables.Lookup)
+def prune_post_subset(self, font, options):
+	ret = False
+	for st in self.SubTable:
+		if not st: continue
+		if st.prune_post_subset(font, options): ret = True
+	return ret
+
+@_add_method(otTables.Lookup)
+def subset_lookups(self, lookup_indices):
+	for s in self.SubTable:
+		s.subset_lookups(lookup_indices)
+
+@_add_method(otTables.Lookup)
+def collect_lookups(self):
+	return sum((st.collect_lookups() for st in self.SubTable if st), [])
+
+@_add_method(otTables.Lookup)
+def may_have_non_1to1(self):
+	return any(st.may_have_non_1to1() for st in self.SubTable if st)
+
+@_add_method(otTables.LookupList)
+def subset_glyphs(self, s):
+	"""Returns the indices of nonempty lookups."""
+	return [i for i,l in enumerate(self.Lookup) if l and l.subset_glyphs(s)]
+
+@_add_method(otTables.LookupList)
+def prune_post_subset(self, font, options):
+	ret = False
+	for l in self.Lookup:
+		if not l: continue
+		if l.prune_post_subset(font, options): ret = True
+	return ret
+
+@_add_method(otTables.LookupList)
+def subset_lookups(self, lookup_indices):
+	self.ensureDecompiled()
+	self.Lookup = [self.Lookup[i] for i in lookup_indices
+				   if i < self.LookupCount]
+	self.LookupCount = len(self.Lookup)
+	for l in self.Lookup:
+		l.subset_lookups(lookup_indices)
+
+@_add_method(otTables.LookupList)
+def neuter_lookups(self, lookup_indices):
+	"""Sets lookups not in lookup_indices to None."""
+	self.ensureDecompiled()
+	self.Lookup = [l if i in lookup_indices else None for i,l in enumerate(self.Lookup)]
+
+@_add_method(otTables.LookupList)
+def closure_lookups(self, lookup_indices):
+	"""Returns sorted index of all lookups reachable from lookup_indices."""
+	lookup_indices = _uniq_sort(lookup_indices)
+	recurse = lookup_indices
+	while True:
+		recurse_lookups = sum((self.Lookup[i].collect_lookups()
+				       for i in recurse if i < self.LookupCount), [])
+		recurse_lookups = [l for l in recurse_lookups
+				     if l not in lookup_indices and l < self.LookupCount]
+		if not recurse_lookups:
+			return _uniq_sort(lookup_indices)
+		recurse_lookups = _uniq_sort(recurse_lookups)
+		lookup_indices.extend(recurse_lookups)
+		recurse = recurse_lookups
+
+@_add_method(otTables.Feature)
+def subset_lookups(self, lookup_indices):
+	""""Returns True if feature is non-empty afterwards."""
+	self.LookupListIndex = [l for l in self.LookupListIndex
+				  if l in lookup_indices]
+	# Now map them.
+	self.LookupListIndex = [lookup_indices.index(l)
+				for l in self.LookupListIndex]
+	self.LookupCount = len(self.LookupListIndex)
+	return self.LookupCount or self.FeatureParams
+
+@_add_method(otTables.FeatureList)
+def subset_lookups(self, lookup_indices):
+	"""Returns the indices of nonempty features."""
+	# Note: Never ever drop feature 'pref', even if it's empty.
+	# HarfBuzz chooses shaper for Khmer based on presence of this
+	# feature.	See thread at:
+	# http://lists.freedesktop.org/archives/harfbuzz/2012-November/002660.html
+	return [i for i,f in enumerate(self.FeatureRecord)
+			if (f.Feature.subset_lookups(lookup_indices) or
+				f.FeatureTag == 'pref')]
+
+@_add_method(otTables.FeatureList)
+def collect_lookups(self, feature_indices):
+	return sum((self.FeatureRecord[i].Feature.LookupListIndex
+				for i in feature_indices
+				if i < self.FeatureCount), [])
+
+@_add_method(otTables.FeatureList)
+def subset_features(self, feature_indices):
+	self.ensureDecompiled()
+	self.FeatureRecord = [self.FeatureRecord[i] for i in feature_indices]
+	self.FeatureCount = len(self.FeatureRecord)
+	return bool(self.FeatureCount)
+
+@_add_method(otTables.FeatureTableSubstitution)
+def subset_lookups(self, lookup_indices):
+	"""Returns the indices of nonempty features."""
+	return [r.FeatureIndex for r in self.SubstitutionRecord
+			if r.Feature.subset_lookups(lookup_indices)]
+
+@_add_method(otTables.FeatureVariations)
+def subset_lookups(self, lookup_indices):
+	"""Returns the indices of nonempty features."""
+	return sum((f.FeatureTableSubstitution.subset_lookups(lookup_indices)
+				for f in self.FeatureVariationRecord), [])
+
+@_add_method(otTables.FeatureVariations)
+def collect_lookups(self, feature_indices):
+	return sum((r.Feature.LookupListIndex
+				for vr in self.FeatureVariationRecord
+				for r in vr.FeatureTableSubstitution.SubstitutionRecord
+				if r.FeatureIndex in feature_indices), [])
+
+@_add_method(otTables.FeatureTableSubstitution)
+def subset_features(self, feature_indices):
+	self.ensureDecompiled()
+	self.SubstitutionRecord = [r for r in self.SubstitutionRecord
+				     if r.FeatureIndex in feature_indices]
+	self.SubstitutionCount = len(self.SubstitutionRecord)
+	return bool(self.SubstitutionCount)
+
+@_add_method(otTables.FeatureVariations)
+def subset_features(self, feature_indices):
+	self.ensureDecompiled()
+	self.FeaturVariationRecord = [r for r in self.FeatureVariationRecord
+					if r.FeatureTableSubstitution.subset_features(feature_indices)]
+	self.FeatureVariationCount = len(self.FeatureVariationRecord)
+	return bool(self.FeatureVariationCount)
+
+@_add_method(otTables.DefaultLangSys,
+			 otTables.LangSys)
+def subset_features(self, feature_indices):
+	if self.ReqFeatureIndex in feature_indices:
+		self.ReqFeatureIndex = feature_indices.index(self.ReqFeatureIndex)
+	else:
+		self.ReqFeatureIndex = 65535
+	self.FeatureIndex = [f for f in self.FeatureIndex if f in feature_indices]
+	# Now map them.
+	self.FeatureIndex = [feature_indices.index(f) for f in self.FeatureIndex
+						      if f in feature_indices]
+	self.FeatureCount = len(self.FeatureIndex)
+	return bool(self.FeatureCount or self.ReqFeatureIndex != 65535)
+
+@_add_method(otTables.DefaultLangSys,
+			 otTables.LangSys)
+def collect_features(self):
+	feature_indices = self.FeatureIndex[:]
+	if self.ReqFeatureIndex != 65535:
+		feature_indices.append(self.ReqFeatureIndex)
+	return _uniq_sort(feature_indices)
+
+@_add_method(otTables.Script)
+def subset_features(self, feature_indices, keepEmptyDefaultLangSys=False):
+	if(self.DefaultLangSys and
+	   not self.DefaultLangSys.subset_features(feature_indices) and
+	   not keepEmptyDefaultLangSys):
+		self.DefaultLangSys = None
+	self.LangSysRecord = [l for l in self.LangSysRecord
+				if l.LangSys.subset_features(feature_indices)]
+	self.LangSysCount = len(self.LangSysRecord)
+	return bool(self.LangSysCount or self.DefaultLangSys)
+
+@_add_method(otTables.Script)
+def collect_features(self):
+	feature_indices = [l.LangSys.collect_features() for l in self.LangSysRecord]
+	if self.DefaultLangSys:
+		feature_indices.append(self.DefaultLangSys.collect_features())
+	return _uniq_sort(sum(feature_indices, []))
+
+@_add_method(otTables.ScriptList)
+def subset_features(self, feature_indices, retain_empty):
+	# https://bugzilla.mozilla.org/show_bug.cgi?id=1331737#c32
+	self.ScriptRecord = [s for s in self.ScriptRecord
+			       if s.Script.subset_features(feature_indices, s.ScriptTag=='DFLT') or
+				  retain_empty]
+	self.ScriptCount = len(self.ScriptRecord)
+	return bool(self.ScriptCount)
+
+@_add_method(otTables.ScriptList)
+def collect_features(self):
+	return _uniq_sort(sum((s.Script.collect_features()
+			       for s in self.ScriptRecord), []))
+
+# CBLC will inherit it
+@_add_method(ttLib.getTableClass('EBLC'))
+def subset_glyphs(self, s):
+	for strike in self.strikes:
+		for indexSubTable in strike.indexSubTables:
+			indexSubTable.names = [n for n in indexSubTable.names if n in s.glyphs]
+		strike.indexSubTables = [i for i in strike.indexSubTables if i.names]
+	self.strikes = [s for s in self.strikes if s.indexSubTables]
+
+	return True
+
+# CBDC will inherit it
+@_add_method(ttLib.getTableClass('EBDT'))
+def subset_glyphs(self, s):
+  self.strikeData = [{g: strike[g] for g in s.glyphs if g in strike}
+					 for strike in self.strikeData]
+  return True
+
+@_add_method(ttLib.getTableClass('GSUB'))
+def closure_glyphs(self, s):
+	s.table = self.table
+	if self.table.ScriptList:
+		feature_indices = self.table.ScriptList.collect_features()
+	else:
+		feature_indices = []
+	if self.table.FeatureList:
+		lookup_indices = self.table.FeatureList.collect_lookups(feature_indices)
+	else:
+		lookup_indices = []
+	if getattr(self.table, 'FeatureVariations', None):
+		lookup_indices += self.table.FeatureVariations.collect_lookups(feature_indices)
+	lookup_indices = _uniq_sort(lookup_indices)
+	if self.table.LookupList:
+		s._doneLookups = {}
+		while True:
+			orig_glyphs = frozenset(s.glyphs)
+			for i in lookup_indices:
+				if i >= self.table.LookupList.LookupCount: continue
+				if not self.table.LookupList.Lookup[i]: continue
+				self.table.LookupList.Lookup[i].closure_glyphs(s)
+			if orig_glyphs == s.glyphs:
+				break
+		del s._doneLookups
+	del s.table
+
+@_add_method(ttLib.getTableClass('GSUB'),
+			 ttLib.getTableClass('GPOS'))
+def subset_glyphs(self, s):
+	s.glyphs = s.glyphs_gsubed
+	if self.table.LookupList:
+		lookup_indices = self.table.LookupList.subset_glyphs(s)
+	else:
+		lookup_indices = []
+	self.subset_lookups(lookup_indices)
+	return True
+
+@_add_method(ttLib.getTableClass('GSUB'),
+			 ttLib.getTableClass('GPOS'))
+def retain_empty_scripts(self):
+	# https://github.com/behdad/fonttools/issues/518
+	# https://bugzilla.mozilla.org/show_bug.cgi?id=1080739#c15
+	return self.__class__ == ttLib.getTableClass('GSUB')
+
+@_add_method(ttLib.getTableClass('GSUB'),
+			 ttLib.getTableClass('GPOS'))
+def subset_lookups(self, lookup_indices):
+	"""Retains specified lookups, then removes empty features, language
+	systems, and scripts."""
+	if self.table.LookupList:
+		self.table.LookupList.subset_lookups(lookup_indices)
+	if self.table.FeatureList:
+		feature_indices = self.table.FeatureList.subset_lookups(lookup_indices)
+	else:
+		feature_indices = []
+	if getattr(self.table, 'FeatureVariations', None):
+		feature_indices += self.table.FeatureVariations.subset_lookups(lookup_indices)
+	feature_indices = _uniq_sort(feature_indices)
+	if self.table.FeatureList:
+		self.table.FeatureList.subset_features(feature_indices)
+	if getattr(self.table, 'FeatureVariations', None):
+		self.table.FeatureVariations.subset_features(feature_indices)
+	if self.table.ScriptList:
+		self.table.ScriptList.subset_features(feature_indices, self.retain_empty_scripts())
+
+@_add_method(ttLib.getTableClass('GSUB'),
+			 ttLib.getTableClass('GPOS'))
+def neuter_lookups(self, lookup_indices):
+	"""Sets lookups not in lookup_indices to None."""
+	if self.table.LookupList:
+		self.table.LookupList.neuter_lookups(lookup_indices)
+
+@_add_method(ttLib.getTableClass('GSUB'),
+			 ttLib.getTableClass('GPOS'))
+def prune_lookups(self, remap=True):
+	"""Remove (default) or neuter unreferenced lookups"""
+	if self.table.ScriptList:
+		feature_indices = self.table.ScriptList.collect_features()
+	else:
+		feature_indices = []
+	if self.table.FeatureList:
+		lookup_indices = self.table.FeatureList.collect_lookups(feature_indices)
+	else:
+		lookup_indices = []
+	if getattr(self.table, 'FeatureVariations', None):
+		lookup_indices += self.table.FeatureVariations.collect_lookups(feature_indices)
+	lookup_indices = _uniq_sort(lookup_indices)
+	if self.table.LookupList:
+		lookup_indices = self.table.LookupList.closure_lookups(lookup_indices)
+	else:
+		lookup_indices = []
+	if remap:
+		self.subset_lookups(lookup_indices)
+	else:
+		self.neuter_lookups(lookup_indices)
+
+@_add_method(ttLib.getTableClass('GSUB'),
+			 ttLib.getTableClass('GPOS'))
+def subset_feature_tags(self, feature_tags):
+	if self.table.FeatureList:
+		feature_indices = \
+			[i for i,f in enumerate(self.table.FeatureList.FeatureRecord)
+			 if f.FeatureTag in feature_tags]
+		self.table.FeatureList.subset_features(feature_indices)
+		if getattr(self.table, 'FeatureVariations', None):
+			self.table.FeatureVariations.subset_features(feature_indices)
+	else:
+		feature_indices = []
+	if self.table.ScriptList:
+		self.table.ScriptList.subset_features(feature_indices, self.retain_empty_scripts())
+
+@_add_method(ttLib.getTableClass('GSUB'),
+			 ttLib.getTableClass('GPOS'))
+def prune_features(self):
+	"""Remove unreferenced features"""
+	if self.table.ScriptList:
+		feature_indices = self.table.ScriptList.collect_features()
+	else:
+		feature_indices = []
+	if self.table.FeatureList:
+		self.table.FeatureList.subset_features(feature_indices)
+	if getattr(self.table, 'FeatureVariations', None):
+		self.table.FeatureVariations.subset_features(feature_indices)
+	if self.table.ScriptList:
+		self.table.ScriptList.subset_features(feature_indices, self.retain_empty_scripts())
+
+@_add_method(ttLib.getTableClass('GSUB'),
+			 ttLib.getTableClass('GPOS'))
+def prune_pre_subset(self, font, options):
+	# Drop undesired features
+	if '*' not in options.layout_features:
+		self.subset_feature_tags(options.layout_features)
+	# Neuter unreferenced lookups
+	self.prune_lookups(remap=False)
+	return True
+
+@_add_method(ttLib.getTableClass('GSUB'),
+			 ttLib.getTableClass('GPOS'))
+def remove_redundant_langsys(self):
+	table = self.table
+	if not table.ScriptList or not table.FeatureList:
+		return
+
+	features = table.FeatureList.FeatureRecord
+
+	for s in table.ScriptList.ScriptRecord:
+		d = s.Script.DefaultLangSys
+		if not d:
+			continue
+		for lr in s.Script.LangSysRecord[:]:
+			l = lr.LangSys
+			# Compare d and l
+			if len(d.FeatureIndex) != len(l.FeatureIndex):
+				continue
+			if (d.ReqFeatureIndex == 65535) != (l.ReqFeatureIndex == 65535):
+				continue
+
+			if d.ReqFeatureIndex != 65535:
+				if features[d.ReqFeatureIndex] != features[l.ReqFeatureIndex]:
+					continue
+
+			for i in range(len(d.FeatureIndex)):
+				if features[d.FeatureIndex[i]] != features[l.FeatureIndex[i]]:
+					break
+			else:
+				# LangSys and default are equal; delete LangSys
+				s.Script.LangSysRecord.remove(lr)
+
+@_add_method(ttLib.getTableClass('GSUB'),
+	     ttLib.getTableClass('GPOS'))
+def prune_post_subset(self, font, options):
+	table = self.table
+
+	self.prune_lookups() # XXX Is this actually needed?!
+
+	if table.LookupList:
+		table.LookupList.prune_post_subset(font, options)
+		# XXX Next two lines disabled because OTS is stupid and
+		# doesn't like NULL offsets here.
+		#if not table.LookupList.Lookup:
+		#	table.LookupList = None
+
+	if not table.LookupList:
+		table.FeatureList = None
+
+
+	if table.FeatureList:
+		self.remove_redundant_langsys()
+		# Remove unreferenced features
+		self.prune_features()
+
+	# XXX Next two lines disabled because OTS is stupid and
+	# doesn't like NULL offsets here.
+	#if table.FeatureList and not table.FeatureList.FeatureRecord:
+	#	table.FeatureList = None
+
+	# Never drop scripts themselves as them just being available
+	# holds semantic significance.
+	# XXX Next two lines disabled because OTS is stupid and
+	# doesn't like NULL offsets here.
+	#if table.ScriptList and not table.ScriptList.ScriptRecord:
+	#	table.ScriptList = None
+
+	if not table.FeatureList and hasattr(table, 'FeatureVariations'):
+		table.FeatureVariations = None
+
+	if hasattr(table, 'FeatureVariations') and not table.FeatureVariations:
+		if table.Version == 0x00010001:
+			table.Version = 0x00010000
+
+	return True
+
+@_add_method(ttLib.getTableClass('GDEF'))
+def subset_glyphs(self, s):
+	glyphs = s.glyphs_gsubed
+	table = self.table
+	if table.LigCaretList:
+		indices = table.LigCaretList.Coverage.subset(glyphs)
+		table.LigCaretList.LigGlyph = [table.LigCaretList.LigGlyph[i] for i in indices]
+		table.LigCaretList.LigGlyphCount = len(table.LigCaretList.LigGlyph)
+	if table.MarkAttachClassDef:
+		table.MarkAttachClassDef.classDefs = \
+			{g:v for g,v in table.MarkAttachClassDef.classDefs.items()
+			 if g in glyphs}
+	if table.GlyphClassDef:
+		table.GlyphClassDef.classDefs = \
+			{g:v for g,v in table.GlyphClassDef.classDefs.items()
+			 if g in glyphs}
+	if table.AttachList:
+		indices = table.AttachList.Coverage.subset(glyphs)
+		GlyphCount = table.AttachList.GlyphCount
+		table.AttachList.AttachPoint = [table.AttachList.AttachPoint[i]
+						for i in indices if i < GlyphCount]
+		table.AttachList.GlyphCount = len(table.AttachList.AttachPoint)
+	if hasattr(table, "MarkGlyphSetsDef") and table.MarkGlyphSetsDef:
+		for coverage in table.MarkGlyphSetsDef.Coverage:
+			if coverage:
+				coverage.subset(glyphs)
+
+		# TODO: The following is disabled. If enabling, we need to go fixup all
+		# lookups that use MarkFilteringSet and map their set.
+		# indices = table.MarkGlyphSetsDef.Coverage = \
+		#   [c for c in table.MarkGlyphSetsDef.Coverage if c.glyphs]
+		# TODO: The following is disabled, as ots doesn't like it. Phew...
+		# https://github.com/khaledhosny/ots/issues/172
+		# table.MarkGlyphSetsDef.Coverage = [c if c.glyphs else None for c in table.MarkGlyphSetsDef.Coverage]
+	return True
+
+
+def _pruneGDEF(font):
+	if 'GDEF' not in font: return
+	gdef = font['GDEF']
+	table = gdef.table
+	if not hasattr(table, 'VarStore'): return
+
+	store = table.VarStore
+
+	usedVarIdxes = set()
+
+	# Collect.
+	table.collect_device_varidxes(usedVarIdxes)
+	if 'GPOS' in font:
+		font['GPOS'].table.collect_device_varidxes(usedVarIdxes)
+
+	# Subset.
+	varidx_map = store.subset_varidxes(usedVarIdxes)
+
+	# Map.
+	table.remap_device_varidxes(varidx_map)
+	if 'GPOS' in font:
+		font['GPOS'].table.remap_device_varidxes(varidx_map)
+
+@_add_method(ttLib.getTableClass('GDEF'))
+def prune_post_subset(self, font, options):
+	table = self.table
+	# XXX check these against OTS
+	if table.LigCaretList and not table.LigCaretList.LigGlyphCount:
+		table.LigCaretList = None
+	if table.MarkAttachClassDef and not table.MarkAttachClassDef.classDefs:
+		table.MarkAttachClassDef = None
+	if table.GlyphClassDef and not table.GlyphClassDef.classDefs:
+		table.GlyphClassDef = None
+	if table.AttachList and not table.AttachList.GlyphCount:
+		table.AttachList = None
+	if hasattr(table, "VarStore"):
+		_pruneGDEF(font)
+		if table.VarStore.VarDataCount == 0:
+			if table.Version == 0x00010003:
+				table.Version = 0x00010002
+	if (not hasattr(table, "MarkGlyphSetsDef") or
+		not table.MarkGlyphSetsDef or
+		not table.MarkGlyphSetsDef.Coverage):
+		table.MarkGlyphSetsDef = None
+		if table.Version == 0x00010002:
+			table.Version = 0x00010000
+	return bool(table.LigCaretList or
+		    table.MarkAttachClassDef or
+		    table.GlyphClassDef or
+		    table.AttachList or
+		    (table.Version >= 0x00010002 and table.MarkGlyphSetsDef) or
+		    (table.Version >= 0x00010003 and table.VarStore))
+
+@_add_method(ttLib.getTableClass('kern'))
+def prune_pre_subset(self, font, options):
+	# Prune unknown kern table types
+	self.kernTables = [t for t in self.kernTables if hasattr(t, 'kernTable')]
+	return bool(self.kernTables)
+
+@_add_method(ttLib.getTableClass('kern'))
+def subset_glyphs(self, s):
+	glyphs = s.glyphs_gsubed
+	for t in self.kernTables:
+		t.kernTable = {(a,b):v for (a,b),v in t.kernTable.items()
+				       if a in glyphs and b in glyphs}
+	self.kernTables = [t for t in self.kernTables if t.kernTable]
+	return bool(self.kernTables)
+
+@_add_method(ttLib.getTableClass('vmtx'))
+def subset_glyphs(self, s):
+	self.metrics = _dict_subset(self.metrics, s.glyphs)
+	return bool(self.metrics)
+
+@_add_method(ttLib.getTableClass('hmtx'))
+def subset_glyphs(self, s):
+	self.metrics = _dict_subset(self.metrics, s.glyphs)
+	return True # Required table
+
+@_add_method(ttLib.getTableClass('hdmx'))
+def subset_glyphs(self, s):
+	self.hdmx = {sz:_dict_subset(l, s.glyphs) for sz,l in self.hdmx.items()}
+	return bool(self.hdmx)
+
+@_add_method(ttLib.getTableClass('ankr'))
+def subset_glyphs(self, s):
+	table = self.table.AnchorPoints
+	assert table.Format == 0, "unknown 'ankr' format %s" % table.Format
+	table.Anchors = {glyph: table.Anchors[glyph] for glyph in s.glyphs
+					 if glyph in table.Anchors}
+	return len(table.Anchors) > 0
+
+@_add_method(ttLib.getTableClass('bsln'))
+def closure_glyphs(self, s):
+	table = self.table.Baseline
+	if table.Format in (2, 3):
+		s.glyphs.add(table.StandardGlyph)
+
+@_add_method(ttLib.getTableClass('bsln'))
+def subset_glyphs(self, s):
+	table = self.table.Baseline
+	if table.Format in (1, 3):
+		baselines = {glyph: table.BaselineValues.get(glyph, table.DefaultBaseline)
+					 for glyph in s.glyphs}
+		if len(baselines) > 0:
+			mostCommon, _cnt = Counter(baselines.values()).most_common(1)[0]
+			table.DefaultBaseline = mostCommon
+			baselines = {glyph: b for glyph, b in baselines.items()
+					      if b != mostCommon}
+		if len(baselines) > 0:
+			table.BaselineValues = baselines
+		else:
+			table.Format = {1: 0, 3: 2}[table.Format]
+			del table.BaselineValues
+	return True
+
+@_add_method(ttLib.getTableClass('lcar'))
+def subset_glyphs(self, s):
+	table = self.table.LigatureCarets
+	if table.Format in (0, 1):
+		table.Carets = {glyph: table.Carets[glyph] for glyph in s.glyphs
+							   if glyph in table.Carets}
+		return len(table.Carets) > 0
+	else:
+		assert False, "unknown 'lcar' format %s" % table.Format
+
+@_add_method(ttLib.getTableClass('gvar'))
+def prune_pre_subset(self, font, options):
+	if options.notdef_glyph and not options.notdef_outline:
+		self.variations[font.glyphOrder[0]] = []
+	return True
+
+@_add_method(ttLib.getTableClass('gvar'))
+def subset_glyphs(self, s):
+	self.variations = _dict_subset(self.variations, s.glyphs)
+	self.glyphCount = len(self.variations)
+	return bool(self.variations)
+
+@_add_method(ttLib.getTableClass('HVAR'))
+def subset_glyphs(self, s):
+	table = self.table
+
+	used = set()
+
+	if table.AdvWidthMap:
+		table.AdvWidthMap.mapping = _dict_subset(table.AdvWidthMap.mapping, s.glyphs)
+		used.update(table.AdvWidthMap.mapping.values())
+	else:
+		assert table.LsbMap is None and table.RsbMap is None, "File a bug."
+		used.update(s.reverseOrigGlyphMap.values())
+
+	if table.LsbMap:
+		table.LsbMap.mapping = _dict_subset(table.LsbMap.mapping, s.glyphs)
+		used.update(table.LsbMap.mapping.values())
+	if table.RsbMap:
+		table.RsbMap.mapping = _dict_subset(table.RsbMap.mapping, s.glyphs)
+		used.update(table.RsbMap.mapping.values())
+
+	varidx_map = varStore.VarStore_subset_varidxes(table.VarStore, used)
+
+	if table.AdvWidthMap:
+		table.AdvWidthMap.mapping = {k:varidx_map[v] for k,v in table.AdvWidthMap.mapping.items()}
+	if table.LsbMap:
+		table.LsbMap.mapping = {k:varidx_map[v] for k,v in table.LsbMap.mapping.items()}
+	if table.RsbMap:
+		table.RsbMap.mapping = {k:varidx_map[v] for k,v in table.RsbMap.mapping.items()}
+
+	# TODO Return emptiness...
+	return True
+
+@_add_method(ttLib.getTableClass('VVAR'))
+def subset_glyphs(self, s):
+	table = self.table
+
+	used = set()
+
+	if table.AdvHeightMap:
+		table.AdvHeightMap.mapping = _dict_subset(table.AdvHeightMap.mapping, s.glyphs)
+		used.update(table.AdvHeightMap.mapping.values())
+	else:
+		assert table.TsbMap is None and table.BsbMap is None and table.VOrgMap is None, "File a bug."
+		used.update(s.reverseOrigGlyphMap.values())
+	if table.TsbMap:
+		table.TsbMap.mapping = _dict_subset(table.TsbMap.mapping, s.glyphs)
+		used.update(table.TsbMap.mapping.values())
+	if table.BsbMap:
+		table.BsbMap.mapping = _dict_subset(table.BsbMap.mapping, s.glyphs)
+		used.update(table.BsbMap.mapping.values())
+	if table.VOrgMap:
+		table.VOrgMap.mapping = _dict_subset(table.VOrgMap.mapping, s.glyphs)
+		used.update(table.VOrgMap.mapping.values())
+
+	varidx_map = varStore.VarStore_subset_varidxes(table.VarStore, used)
+
+	if table.AdvHeightMap:
+		table.AdvHeightMap.mapping = {k:varidx_map[v] for k,v in table.AdvHeightMap.mapping.items()}
+	if table.TsbMap:
+		table.TsbMap.mapping = {k:varidx_map[v] for k,v in table.TsbMap.mapping.items()}
+	if table.BsbMap:
+		table.RsbMap.mapping = {k:varidx_map[v] for k,v in table.RsbMap.mapping.items()}
+	if table.VOrgMap:
+		table.RsbMap.mapping = {k:varidx_map[v] for k,v in table.RsbMap.mapping.items()}
+
+	# TODO Return emptiness...
+	return True
+
+@_add_method(ttLib.getTableClass('VORG'))
+def subset_glyphs(self, s):
+	self.VOriginRecords = {g:v for g,v in self.VOriginRecords.items()
+				   if g in s.glyphs}
+	self.numVertOriginYMetrics = len(self.VOriginRecords)
+	return True	# Never drop; has default metrics
+
+@_add_method(ttLib.getTableClass('opbd'))
+def subset_glyphs(self, s):
+	table = self.table.OpticalBounds
+	if table.Format == 0:
+		table.OpticalBoundsDeltas = {glyph: table.OpticalBoundsDeltas[glyph]
+					     for glyph in s.glyphs
+					     if glyph in table.OpticalBoundsDeltas}
+		return len(table.OpticalBoundsDeltas) > 0
+	elif table.Format == 1:
+		table.OpticalBoundsPoints = {glyph: table.OpticalBoundsPoints[glyph]
+					     for glyph in s.glyphs
+					     if glyph in table.OpticalBoundsPoints}
+		return len(table.OpticalBoundsPoints) > 0
+	else:
+		assert False, "unknown 'opbd' format %s" % table.Format
+
+@_add_method(ttLib.getTableClass('post'))
+def prune_pre_subset(self, font, options):
+	if not options.glyph_names:
+		self.formatType = 3.0
+	return True # Required table
+
+@_add_method(ttLib.getTableClass('post'))
+def subset_glyphs(self, s):
+	self.extraNames = []	# This seems to do it
+	return True # Required table
+
+@_add_method(ttLib.getTableClass('prop'))
+def subset_glyphs(self, s):
+	prop = self.table.GlyphProperties
+	if prop.Format == 0:
+		return prop.DefaultProperties != 0
+	elif prop.Format == 1:
+		prop.Properties = {g: prop.Properties.get(g, prop.DefaultProperties)
+				   for g in s.glyphs}
+		mostCommon, _cnt = Counter(prop.Properties.values()).most_common(1)[0]
+		prop.DefaultProperties = mostCommon
+		prop.Properties = {g: prop for g, prop in prop.Properties.items()
+				   if prop != mostCommon}
+		if len(prop.Properties) == 0:
+			del prop.Properties
+			prop.Format = 0
+			return prop.DefaultProperties != 0
+		return True
+	else:
+		assert False, "unknown 'prop' format %s" % prop.Format
+
+@_add_method(ttLib.getTableClass('COLR'))
+def closure_glyphs(self, s):
+	decompose = s.glyphs
+	while decompose:
+		layers = set()
+		for g in decompose:
+			for l in self.ColorLayers.get(g, []):
+				layers.add(l.name)
+		layers -= s.glyphs
+		s.glyphs.update(layers)
+		decompose = layers
+
+@_add_method(ttLib.getTableClass('COLR'))
+def subset_glyphs(self, s):
+	self.ColorLayers = {g: self.ColorLayers[g] for g in s.glyphs if g in self.ColorLayers}
+	return bool(self.ColorLayers)
+
+# TODO: prune unused palettes
+@_add_method(ttLib.getTableClass('CPAL'))
+def prune_post_subset(self, font, options):
+	return True
+
+@_add_method(otTables.MathGlyphConstruction)
+def closure_glyphs(self, glyphs):
+	variants = set()
+	for v in self.MathGlyphVariantRecord:
+		variants.add(v.VariantGlyph)
+	if self.GlyphAssembly:
+		for p in self.GlyphAssembly.PartRecords:
+			variants.add(p.glyph)
+	return variants
+
+@_add_method(otTables.MathVariants)
+def closure_glyphs(self, s):
+	glyphs = frozenset(s.glyphs)
+	variants = set()
+
+	if self.VertGlyphCoverage:
+		indices = self.VertGlyphCoverage.intersect(glyphs)
+		for i in indices:
+			variants.update(self.VertGlyphConstruction[i].closure_glyphs(glyphs))
+
+	if self.HorizGlyphCoverage:
+		indices = self.HorizGlyphCoverage.intersect(glyphs)
+		for i in indices:
+			variants.update(self.HorizGlyphConstruction[i].closure_glyphs(glyphs))
+
+	s.glyphs.update(variants)
+
+@_add_method(ttLib.getTableClass('MATH'))
+def closure_glyphs(self, s):
+	self.table.MathVariants.closure_glyphs(s)
+
+@_add_method(otTables.MathItalicsCorrectionInfo)
+def subset_glyphs(self, s):
+	indices = self.Coverage.subset(s.glyphs)
+	self.ItalicsCorrection = [self.ItalicsCorrection[i] for i in indices]
+	self.ItalicsCorrectionCount = len(self.ItalicsCorrection)
+	return bool(self.ItalicsCorrectionCount)
+
+@_add_method(otTables.MathTopAccentAttachment)
+def subset_glyphs(self, s):
+	indices = self.TopAccentCoverage.subset(s.glyphs)
+	self.TopAccentAttachment = [self.TopAccentAttachment[i] for i in indices]
+	self.TopAccentAttachmentCount = len(self.TopAccentAttachment)
+	return bool(self.TopAccentAttachmentCount)
+
+@_add_method(otTables.MathKernInfo)
+def subset_glyphs(self, s):
+	indices = self.MathKernCoverage.subset(s.glyphs)
+	self.MathKernInfoRecords = [self.MathKernInfoRecords[i] for i in indices]
+	self.MathKernCount = len(self.MathKernInfoRecords)
+	return bool(self.MathKernCount)
+
+@_add_method(otTables.MathGlyphInfo)
+def subset_glyphs(self, s):
+	if self.MathItalicsCorrectionInfo:
+		self.MathItalicsCorrectionInfo.subset_glyphs(s)
+	if self.MathTopAccentAttachment:
+		self.MathTopAccentAttachment.subset_glyphs(s)
+	if self.MathKernInfo:
+		self.MathKernInfo.subset_glyphs(s)
+	if self.ExtendedShapeCoverage:
+		self.ExtendedShapeCoverage.subset(s.glyphs)
+	return True
+
+@_add_method(otTables.MathVariants)
+def subset_glyphs(self, s):
+	if self.VertGlyphCoverage:
+		indices = self.VertGlyphCoverage.subset(s.glyphs)
+		self.VertGlyphConstruction = [self.VertGlyphConstruction[i] for i in indices]
+		self.VertGlyphCount = len(self.VertGlyphConstruction)
+
+	if self.HorizGlyphCoverage:
+		indices = self.HorizGlyphCoverage.subset(s.glyphs)
+		self.HorizGlyphConstruction = [self.HorizGlyphConstruction[i] for i in indices]
+		self.HorizGlyphCount = len(self.HorizGlyphConstruction)
+
+	return True
+
+@_add_method(ttLib.getTableClass('MATH'))
+def subset_glyphs(self, s):
+	s.glyphs = s.glyphs_mathed
+	self.table.MathGlyphInfo.subset_glyphs(s)
+	self.table.MathVariants.subset_glyphs(s)
+	return True
+
+@_add_method(ttLib.getTableModule('glyf').Glyph)
+def remapComponentsFast(self, indices):
+	if not self.data or struct.unpack(">h", self.data[:2])[0] >= 0:
+		return	# Not composite
+	data = array.array("B", self.data)
+	i = 10
+	more = 1
+	while more:
+		flags =(data[i] << 8) | data[i+1]
+		glyphID =(data[i+2] << 8) | data[i+3]
+		# Remap
+		glyphID = indices.index(glyphID)
+		data[i+2] = glyphID >> 8
+		data[i+3] = glyphID & 0xFF
+		i += 4
+		flags = int(flags)
+
+		if flags & 0x0001: i += 4	# ARG_1_AND_2_ARE_WORDS
+		else: i += 2
+		if flags & 0x0008: i += 2	# WE_HAVE_A_SCALE
+		elif flags & 0x0040: i += 4	# WE_HAVE_AN_X_AND_Y_SCALE
+		elif flags & 0x0080: i += 8	# WE_HAVE_A_TWO_BY_TWO
+		more = flags & 0x0020	# MORE_COMPONENTS
+
+	self.data = data.tostring()
+
+@_add_method(ttLib.getTableClass('glyf'))
+def closure_glyphs(self, s):
+	glyphSet = self.glyphs
+	decompose = s.glyphs
+	while decompose:
+		components = set()
+		for g in decompose:
+			if g not in glyphSet:
+				continue
+			gl = glyphSet[g]
+			for c in gl.getComponentNames(self):
+				components.add(c)
+		components -= s.glyphs
+		s.glyphs.update(components)
+		decompose = components
+
+@_add_method(ttLib.getTableClass('glyf'))
+def prune_pre_subset(self, font, options):
+	if options.notdef_glyph and not options.notdef_outline:
+		g = self[self.glyphOrder[0]]
+		# Yay, easy!
+		g.__dict__.clear()
+		g.data = ""
+	return True
+
+@_add_method(ttLib.getTableClass('glyf'))
+def subset_glyphs(self, s):
+	self.glyphs = _dict_subset(self.glyphs, s.glyphs)
+	indices = [i for i,g in enumerate(self.glyphOrder) if g in s.glyphs]
+	for v in self.glyphs.values():
+		if hasattr(v, "data"):
+			v.remapComponentsFast(indices)
+		else:
+			pass	# No need
+	self.glyphOrder = [g for g in self.glyphOrder if g in s.glyphs]
+	# Don't drop empty 'glyf' tables, otherwise 'loca' doesn't get subset.
+	return True
+
+@_add_method(ttLib.getTableClass('glyf'))
+def prune_post_subset(self, font, options):
+	remove_hinting = not options.hinting
+	for v in self.glyphs.values():
+		v.trim(remove_hinting=remove_hinting)
+	return True
+
+
+class _ClosureGlyphsT2Decompiler(psCharStrings.SimpleT2Decompiler):
+
+	def __init__(self, components, localSubrs, globalSubrs):
+		psCharStrings.SimpleT2Decompiler.__init__(self,
+							  localSubrs,
+							  globalSubrs)
+		self.components = components
+
+	def op_endchar(self, index):
+		args = self.popall()
+		if len(args) >= 4:
+			from fontTools.encodings.StandardEncoding import StandardEncoding
+			# endchar can do seac accent bulding; The T2 spec says it's deprecated,
+			# but recent software that shall remain nameless does output it.
+			adx, ady, bchar, achar = args[-4:]
+			baseGlyph = StandardEncoding[bchar]
+			accentGlyph = StandardEncoding[achar]
+			self.components.add(baseGlyph)
+			self.components.add(accentGlyph)
+
+@_add_method(ttLib.getTableClass('CFF '))
+def closure_glyphs(self, s):
+	cff = self.cff
+	assert len(cff) == 1
+	font = cff[cff.keys()[0]]
+	glyphSet = font.CharStrings
+
+	decompose = s.glyphs
+	while decompose:
+		components = set()
+		for g in decompose:
+			if g not in glyphSet:
+				continue
+			gl = glyphSet[g]
+
+			subrs = getattr(gl.private, "Subrs", [])
+			decompiler = _ClosureGlyphsT2Decompiler(components, subrs, gl.globalSubrs)
+			decompiler.execute(gl)
+		components -= s.glyphs
+		s.glyphs.update(components)
+		decompose = components
+
+@_add_method(ttLib.getTableClass('CFF '))
+def prune_pre_subset(self, font, options):
+	cff = self.cff
+	# CFF table must have one font only
+	cff.fontNames = cff.fontNames[:1]
+
+	if options.notdef_glyph and not options.notdef_outline:
+		for fontname in cff.keys():
+			font = cff[fontname]
+			c, fdSelectIndex = font.CharStrings.getItemAndSelector('.notdef')
+			if hasattr(font, 'FDArray') and font.FDArray is not None:
+				private = font.FDArray[fdSelectIndex].Private
+			else:
+				private = font.Private
+			dfltWdX = private.defaultWidthX
+			nmnlWdX = private.nominalWidthX
+			pen = NullPen()
+			c.draw(pen)  # this will set the charstring's width
+			if c.width != dfltWdX:
+				c.program = [c.width - nmnlWdX, 'endchar']
+			else:
+				c.program = ['endchar']
+
+	# Clear useless Encoding
+	for fontname in cff.keys():
+		font = cff[fontname]
+		# https://github.com/behdad/fonttools/issues/620
+		font.Encoding = "StandardEncoding"
+
+	return True # bool(cff.fontNames)
+
+@_add_method(ttLib.getTableClass('CFF '))
+def subset_glyphs(self, s):
+	cff = self.cff
+	for fontname in cff.keys():
+		font = cff[fontname]
+		cs = font.CharStrings
+
+		# Load all glyphs
+		for g in font.charset:
+			if g not in s.glyphs: continue
+			c, _ = cs.getItemAndSelector(g)
+
+		if cs.charStringsAreIndexed:
+			indices = [i for i,g in enumerate(font.charset) if g in s.glyphs]
+			csi = cs.charStringsIndex
+			csi.items = [csi.items[i] for i in indices]
+			del csi.file, csi.offsets
+			if hasattr(font, "FDSelect"):
+				sel = font.FDSelect
+				# XXX We want to set sel.format to None, such that the
+				# most compact format is selected. However, OTS was
+				# broken and couldn't parse a FDSelect format 0 that
+				# happened before CharStrings. As such, always force
+				# format 3 until we fix cffLib to always generate
+				# FDSelect after CharStrings.
+				# https://github.com/khaledhosny/ots/pull/31
+				#sel.format = None
+				sel.format = 3
+				sel.gidArray = [sel.gidArray[i] for i in indices]
+			cs.charStrings = {g:indices.index(v)
+					  for g,v in cs.charStrings.items()
+					  if g in s.glyphs}
+		else:
+			cs.charStrings = {g:v
+					  for g,v in cs.charStrings.items()
+					  if g in s.glyphs}
+		font.charset = [g for g in font.charset if g in s.glyphs]
+		font.numGlyphs = len(font.charset)
+
+	return True # any(cff[fontname].numGlyphs for fontname in cff.keys())
+
+@_add_method(psCharStrings.T2CharString)
+def subset_subroutines(self, subrs, gsubrs):
+	p = self.program
+	assert len(p)
+	for i in range(1, len(p)):
+		if p[i] == 'callsubr':
+			assert isinstance(p[i-1], int)
+			p[i-1] = subrs._used.index(p[i-1] + subrs._old_bias) - subrs._new_bias
+		elif p[i] == 'callgsubr':
+			assert isinstance(p[i-1], int)
+			p[i-1] = gsubrs._used.index(p[i-1] + gsubrs._old_bias) - gsubrs._new_bias
+
+@_add_method(psCharStrings.T2CharString)
+def drop_hints(self):
+	hints = self._hints
+
+	if hints.deletions:
+		p = self.program
+		for idx in reversed(hints.deletions):
+			del p[idx-2:idx]
+
+	if hints.has_hint:
+		assert not hints.deletions or hints.last_hint <= hints.deletions[0]
+		self.program = self.program[hints.last_hint:]
+		if hasattr(self, 'width'):
+			# Insert width back if needed
+			if self.width != self.private.defaultWidthX:
+				self.program.insert(0, self.width - self.private.nominalWidthX)
+
+	if hints.has_hintmask:
+		i = 0
+		p = self.program
+		while i < len(p):
+			if p[i] in ['hintmask', 'cntrmask']:
+				assert i + 1 <= len(p)
+				del p[i:i+2]
+				continue
+			i += 1
+
+	assert len(self.program)
+
+	del self._hints
+
+class _MarkingT2Decompiler(psCharStrings.SimpleT2Decompiler):
+
+	def __init__(self, localSubrs, globalSubrs):
+		psCharStrings.SimpleT2Decompiler.__init__(self,
+							  localSubrs,
+							  globalSubrs)
+		for subrs in [localSubrs, globalSubrs]:
+			if subrs and not hasattr(subrs, "_used"):
+				subrs._used = set()
+
+	def op_callsubr(self, index):
+		self.localSubrs._used.add(self.operandStack[-1]+self.localBias)
+		psCharStrings.SimpleT2Decompiler.op_callsubr(self, index)
+
+	def op_callgsubr(self, index):
+		self.globalSubrs._used.add(self.operandStack[-1]+self.globalBias)
+		psCharStrings.SimpleT2Decompiler.op_callgsubr(self, index)
+
+class _DehintingT2Decompiler(psCharStrings.T2WidthExtractor):
+
+	class Hints(object):
+		def __init__(self):
+			# Whether calling this charstring produces any hint stems
+			# Note that if a charstring starts with hintmask, it will
+			# have has_hint set to True, because it *might* produce an
+			# implicit vstem if called under certain conditions.
+			self.has_hint = False
+			# Index to start at to drop all hints
+			self.last_hint = 0
+			# Index up to which we know more hints are possible.
+			# Only relevant if status is 0 or 1.
+			self.last_checked = 0
+			# The status means:
+			# 0: after dropping hints, this charstring is empty
+			# 1: after dropping hints, there may be more hints
+			#	continuing after this
+			# 2: no more hints possible after this charstring
+			self.status = 0
+			# Has hintmask instructions; not recursive
+			self.has_hintmask = False
+			# List of indices of calls to empty subroutines to remove.
+			self.deletions = []
+		pass
+
+	def __init__(self, css, localSubrs, globalSubrs, nominalWidthX, defaultWidthX):
+		self._css = css
+		psCharStrings.T2WidthExtractor.__init__(
+			self, localSubrs, globalSubrs, nominalWidthX, defaultWidthX)
+
+	def execute(self, charString):
+		old_hints = charString._hints if hasattr(charString, '_hints') else None
+		charString._hints = self.Hints()
+
+		psCharStrings.T2WidthExtractor.execute(self, charString)
+
+		hints = charString._hints
+
+		if hints.has_hint or hints.has_hintmask:
+			self._css.add(charString)
+
+		if hints.status != 2:
+			# Check from last_check, make sure we didn't have any operators.
+			for i in range(hints.last_checked, len(charString.program) - 1):
+				if isinstance(charString.program[i], str):
+					hints.status = 2
+					break
+				else:
+					hints.status = 1 # There's *something* here
+			hints.last_checked = len(charString.program)
+
+		if old_hints:
+			assert hints.__dict__ == old_hints.__dict__
+
+	def op_callsubr(self, index):
+		subr = self.localSubrs[self.operandStack[-1]+self.localBias]
+		psCharStrings.T2WidthExtractor.op_callsubr(self, index)
+		self.processSubr(index, subr)
+
+	def op_callgsubr(self, index):
+		subr = self.globalSubrs[self.operandStack[-1]+self.globalBias]
+		psCharStrings.T2WidthExtractor.op_callgsubr(self, index)
+		self.processSubr(index, subr)
+
+	def op_hstem(self, index):
+		psCharStrings.T2WidthExtractor.op_hstem(self, index)
+		self.processHint(index)
+	def op_vstem(self, index):
+		psCharStrings.T2WidthExtractor.op_vstem(self, index)
+		self.processHint(index)
+	def op_hstemhm(self, index):
+		psCharStrings.T2WidthExtractor.op_hstemhm(self, index)
+		self.processHint(index)
+	def op_vstemhm(self, index):
+		psCharStrings.T2WidthExtractor.op_vstemhm(self, index)
+		self.processHint(index)
+	def op_hintmask(self, index):
+		rv = psCharStrings.T2WidthExtractor.op_hintmask(self, index)
+		self.processHintmask(index)
+		return rv
+	def op_cntrmask(self, index):
+		rv = psCharStrings.T2WidthExtractor.op_cntrmask(self, index)
+		self.processHintmask(index)
+		return rv
+
+	def processHintmask(self, index):
+		cs = self.callingStack[-1]
+		hints = cs._hints
+		hints.has_hintmask = True
+		if hints.status != 2:
+			# Check from last_check, see if we may be an implicit vstem
+			for i in range(hints.last_checked, index - 1):
+				if isinstance(cs.program[i], str):
+					hints.status = 2
+					break
+			else:
+				# We are an implicit vstem
+				hints.has_hint = True
+				hints.last_hint = index + 1
+				hints.status = 0
+		hints.last_checked = index + 1
+
+	def processHint(self, index):
+		cs = self.callingStack[-1]
+		hints = cs._hints
+		hints.has_hint = True
+		hints.last_hint = index
+		hints.last_checked = index
+
+	def processSubr(self, index, subr):
+		cs = self.callingStack[-1]
+		hints = cs._hints
+		subr_hints = subr._hints
+
+		# Check from last_check, make sure we didn't have
+		# any operators.
+		if hints.status != 2:
+			for i in range(hints.last_checked, index - 1):
+				if isinstance(cs.program[i], str):
+					hints.status = 2
+					break
+			hints.last_checked = index
+
+		if hints.status != 2:
+			if subr_hints.has_hint:
+				hints.has_hint = True
+
+			# Decide where to chop off from
+			if subr_hints.status == 0:
+				hints.last_hint = index
+			else:
+				hints.last_hint = index - 2 # Leave the subr call in
+		elif subr_hints.status == 0:
+			hints.deletions.append(index)
+
+		hints.status = max(hints.status, subr_hints.status)
+
+class _DesubroutinizingT2Decompiler(psCharStrings.SimpleT2Decompiler):
+
+	def __init__(self, localSubrs, globalSubrs):
+		psCharStrings.SimpleT2Decompiler.__init__(self,
+							  localSubrs,
+							  globalSubrs)
+
+	def execute(self, charString):
+		# Note: Currently we recompute _desubroutinized each time.
+		# This is more robust in some cases, but in other places we assume
+		# that each subroutine always expands to the same code, so
+		# maybe it doesn't matter. To speed up we can just not
+		# recompute _desubroutinized if it's there. For now I just
+		# double-check that it desubroutinized to the same thing.
+		old_desubroutinized = charString._desubroutinized if hasattr(charString, '_desubroutinized') else None
+
+		charString._patches = []
+		psCharStrings.SimpleT2Decompiler.execute(self, charString)
+		desubroutinized = charString.program[:]
+		for idx,expansion in reversed (charString._patches):
+			assert idx >= 2
+			assert desubroutinized[idx - 1] in ['callsubr', 'callgsubr'], desubroutinized[idx - 1]
+			assert type(desubroutinized[idx - 2]) == int
+			if expansion[-1] == 'return':
+				expansion = expansion[:-1]
+			desubroutinized[idx-2:idx] = expansion
+		if 'endchar' in desubroutinized:
+			# Cut off after first endchar
+			desubroutinized = desubroutinized[:desubroutinized.index('endchar') + 1]
+		else:
+			if not len(desubroutinized) or desubroutinized[-1] != 'return':
+				desubroutinized.append('return')
+
+		charString._desubroutinized = desubroutinized
+		del charString._patches
+
+		if old_desubroutinized:
+			assert desubroutinized == old_desubroutinized
+
+	def op_callsubr(self, index):
+		subr = self.localSubrs[self.operandStack[-1]+self.localBias]
+		psCharStrings.SimpleT2Decompiler.op_callsubr(self, index)
+		self.processSubr(index, subr)
+
+	def op_callgsubr(self, index):
+		subr = self.globalSubrs[self.operandStack[-1]+self.globalBias]
+		psCharStrings.SimpleT2Decompiler.op_callgsubr(self, index)
+		self.processSubr(index, subr)
+
+	def processSubr(self, index, subr):
+		cs = self.callingStack[-1]
+		cs._patches.append((index, subr._desubroutinized))
+
+
+@_add_method(ttLib.getTableClass('CFF '))
+def prune_post_subset(self, font, options):
+	cff = self.cff
+	for fontname in cff.keys():
+		font = cff[fontname]
+		cs = font.CharStrings
+
+		# Drop unused FontDictionaries
+		if hasattr(font, "FDSelect"):
+			sel = font.FDSelect
+			indices = _uniq_sort(sel.gidArray)
+			sel.gidArray = [indices.index (ss) for ss in sel.gidArray]
+			arr = font.FDArray
+			arr.items = [arr[i] for i in indices]
+			del arr.file, arr.offsets
+
+		# Desubroutinize if asked for
+		if options.desubroutinize:
+			for g in font.charset:
+				c, _ = cs.getItemAndSelector(g)
+				c.decompile()
+				subrs = getattr(c.private, "Subrs", [])
+				decompiler = _DesubroutinizingT2Decompiler(subrs, c.globalSubrs)
+				decompiler.execute(c)
+				c.program = c._desubroutinized
+
+		# Drop hints if not needed
+		if not options.hinting:
+
+			# This can be tricky, but doesn't have to. What we do is:
+			#
+			# - Run all used glyph charstrings and recurse into subroutines,
+			# - For each charstring (including subroutines), if it has any
+			#   of the hint stem operators, we mark it as such.
+			#   Upon returning, for each charstring we note all the
+			#   subroutine calls it makes that (recursively) contain a stem,
+			# - Dropping hinting then consists of the following two ops:
+			#   * Drop the piece of the program in each charstring before the
+			#     last call to a stem op or a stem-calling subroutine,
+			#   * Drop all hintmask operations.
+			# - It's trickier... A hintmask right after hints and a few numbers
+			#    will act as an implicit vstemhm. As such, we track whether
+			#    we have seen any non-hint operators so far and do the right
+			#    thing, recursively... Good luck understanding that :(
+			css = set()
+			for g in font.charset:
+				c, _ = cs.getItemAndSelector(g)
+				c.decompile()
+				subrs = getattr(c.private, "Subrs", [])
+				decompiler = _DehintingT2Decompiler(css, subrs, c.globalSubrs,
+								    c.private.nominalWidthX,
+								    c.private.defaultWidthX)
+				decompiler.execute(c)
+				c.width = decompiler.width
+			for charstring in css:
+				charstring.drop_hints()
+			del css
+
+			# Drop font-wide hinting values
+			all_privs = []
+			if hasattr(font, 'FDSelect'):
+				all_privs.extend(fd.Private for fd in font.FDArray)
+			else:
+				all_privs.append(font.Private)
+			for priv in all_privs:
+				for k in ['BlueValues', 'OtherBlues',
+					  'FamilyBlues', 'FamilyOtherBlues',
+					  'BlueScale', 'BlueShift', 'BlueFuzz',
+					  'StemSnapH', 'StemSnapV', 'StdHW', 'StdVW']:
+					if hasattr(priv, k):
+						setattr(priv, k, None)
+
+		# Renumber subroutines to remove unused ones
+
+		# Mark all used subroutines
+		for g in font.charset:
+			c, _ = cs.getItemAndSelector(g)
+			subrs = getattr(c.private, "Subrs", [])
+			decompiler = _MarkingT2Decompiler(subrs, c.globalSubrs)
+			decompiler.execute(c)
+
+		all_subrs = [font.GlobalSubrs]
+		if hasattr(font, 'FDSelect'):
+			all_subrs.extend(fd.Private.Subrs for fd in font.FDArray if hasattr(fd.Private, 'Subrs') and fd.Private.Subrs)
+		elif hasattr(font.Private, 'Subrs') and font.Private.Subrs:
+			all_subrs.append(font.Private.Subrs)
+
+		subrs = set(subrs) # Remove duplicates
+
+		# Prepare
+		for subrs in all_subrs:
+			if not hasattr(subrs, '_used'):
+				subrs._used = set()
+			subrs._used = _uniq_sort(subrs._used)
+			subrs._old_bias = psCharStrings.calcSubrBias(subrs)
+			subrs._new_bias = psCharStrings.calcSubrBias(subrs._used)
+
+		# Renumber glyph charstrings
+		for g in font.charset:
+			c, _ = cs.getItemAndSelector(g)
+			subrs = getattr(c.private, "Subrs", [])
+			c.subset_subroutines (subrs, font.GlobalSubrs)
+
+		# Renumber subroutines themselves
+		for subrs in all_subrs:
+			if subrs == font.GlobalSubrs:
+				if not hasattr(font, 'FDSelect') and hasattr(font.Private, 'Subrs'):
+					local_subrs = font.Private.Subrs
+				else:
+					local_subrs = []
+			else:
+				local_subrs = subrs
+
+			subrs.items = [subrs.items[i] for i in subrs._used]
+			if hasattr(subrs, 'file'):
+				del subrs.file
+			if hasattr(subrs, 'offsets'):
+				del subrs.offsets
+
+			for subr in subrs.items:
+				subr.subset_subroutines (local_subrs, font.GlobalSubrs)
+
+		# Delete local SubrsIndex if empty
+		if hasattr(font, 'FDSelect'):
+			for fd in font.FDArray:
+				_delete_empty_subrs(fd.Private)
+		else:
+			_delete_empty_subrs(font.Private)
+
+		# Cleanup
+		for subrs in all_subrs:
+			del subrs._used, subrs._old_bias, subrs._new_bias
+
+	return True
+
+
+def _delete_empty_subrs(private_dict):
+	if hasattr(private_dict, 'Subrs') and not private_dict.Subrs:
+		if 'Subrs' in private_dict.rawDict:
+			del private_dict.rawDict['Subrs']
+		del private_dict.Subrs
+
+
+@_add_method(ttLib.getTableClass('cmap'))
+def closure_glyphs(self, s):
+	tables = [t for t in self.tables if t.isUnicode()]
+
+	# Close glyphs
+	for table in tables:
+		if table.format == 14:
+			for cmap in table.uvsDict.values():
+				glyphs = {g for u,g in cmap if u in s.unicodes_requested}
+				if None in glyphs:
+					glyphs.remove(None)
+				s.glyphs.update(glyphs)
+		else:
+			cmap = table.cmap
+			intersection = s.unicodes_requested.intersection(cmap.keys())
+			s.glyphs.update(cmap[u] for u in intersection)
+
+	# Calculate unicodes_missing
+	s.unicodes_missing = s.unicodes_requested.copy()
+	for table in tables:
+		s.unicodes_missing.difference_update(table.cmap)
+
+@_add_method(ttLib.getTableClass('cmap'))
+def prune_pre_subset(self, font, options):
+	if not options.legacy_cmap:
+		# Drop non-Unicode / non-Symbol cmaps
+		self.tables = [t for t in self.tables if t.isUnicode() or t.isSymbol()]
+	if not options.symbol_cmap:
+		self.tables = [t for t in self.tables if not t.isSymbol()]
+	# TODO(behdad) Only keep one subtable?
+	# For now, drop format=0 which can't be subset_glyphs easily?
+	self.tables = [t for t in self.tables if t.format != 0]
+	self.numSubTables = len(self.tables)
+	return True # Required table
+
+@_add_method(ttLib.getTableClass('cmap'))
+def subset_glyphs(self, s):
+	s.glyphs = None # We use s.glyphs_requested and s.unicodes_requested only
+	for t in self.tables:
+		if t.format == 14:
+			# TODO(behdad) We drop all the default-UVS mappings
+			# for glyphs_requested.  So it's the caller's responsibility to make
+			# sure those are included.
+			t.uvsDict = {v:[(u,g) for u,g in l
+					      if g in s.glyphs_requested or u in s.unicodes_requested]
+				     for v,l in t.uvsDict.items()}
+			t.uvsDict = {v:l for v,l in t.uvsDict.items() if l}
+		elif t.isUnicode():
+			t.cmap = {u:g for u,g in t.cmap.items()
+				      if g in s.glyphs_requested or u in s.unicodes_requested}
+		else:
+			t.cmap = {u:g for u,g in t.cmap.items()
+				      if g in s.glyphs_requested}
+	self.tables = [t for t in self.tables
+			 if (t.cmap if t.format != 14 else t.uvsDict)]
+	self.numSubTables = len(self.tables)
+	# TODO(behdad) Convert formats when needed.
+	# In particular, if we have a format=12 without non-BMP
+	# characters, either drop format=12 one or convert it
+	# to format=4 if there's not one.
+	return True # Required table
+
+@_add_method(ttLib.getTableClass('DSIG'))
+def prune_pre_subset(self, font, options):
+	# Drop all signatures since they will be invalid
+	self.usNumSigs = 0
+	self.signatureRecords = []
+	return True
+
+@_add_method(ttLib.getTableClass('maxp'))
+def prune_pre_subset(self, font, options):
+	if not options.hinting:
+		if self.tableVersion == 0x00010000:
+			self.maxZones = 1
+			self.maxTwilightPoints = 0
+			self.maxStorage = 0
+			self.maxFunctionDefs = 0
+			self.maxInstructionDefs = 0
+			self.maxStackElements = 0
+			self.maxSizeOfInstructions = 0
+	return True
+
+@_add_method(ttLib.getTableClass('name'))
+def prune_pre_subset(self, font, options):
+	nameIDs = set(options.name_IDs)
+	fvar = font.get('fvar')
+	if fvar:
+		nameIDs.update([axis.axisNameID for axis in fvar.axes])
+		nameIDs.update([inst.subfamilyNameID for inst in fvar.instances])
+		nameIDs.update([inst.postscriptNameID for inst in fvar.instances
+				if inst.postscriptNameID != 0xFFFF])
+	if '*' not in options.name_IDs:
+		self.names = [n for n in self.names if n.nameID in nameIDs]
+	if not options.name_legacy:
+		# TODO(behdad) Sometimes (eg Apple Color Emoji) there's only a macroman
+		# entry for Latin and no Unicode names.
+		self.names = [n for n in self.names if n.isUnicode()]
+	# TODO(behdad) Option to keep only one platform's
+	if '*' not in options.name_languages:
+		# TODO(behdad) This is Windows-platform specific!
+		self.names = [n for n in self.names
+				if n.langID in options.name_languages]
+	if options.obfuscate_names:
+		namerecs = []
+		for n in self.names:
+			if n.nameID in [1, 4]:
+				n.string = ".\x7f".encode('utf_16_be') if n.isUnicode() else ".\x7f"
+			elif n.nameID in [2, 6]:
+				n.string = "\x7f".encode('utf_16_be') if n.isUnicode() else "\x7f"
+			elif n.nameID == 3:
+				n.string = ""
+			elif n.nameID in [16, 17, 18]:
+				continue
+			namerecs.append(n)
+		self.names = namerecs
+	return True	# Required table
+
+
+# TODO(behdad) OS/2 ulCodePageRange?
+# TODO(behdad) Drop AAT tables.
+# TODO(behdad) Drop unneeded GSUB/GPOS Script/LangSys entries.
+# TODO(behdad) Drop empty GSUB/GPOS, and GDEF if no GSUB/GPOS left
+# TODO(behdad) Drop GDEF subitems if unused by lookups
+# TODO(behdad) Avoid recursing too much (in GSUB/GPOS and in CFF)
+# TODO(behdad) Text direction considerations.
+# TODO(behdad) Text script / language considerations.
+# TODO(behdad) Optionally drop 'kern' table if GPOS available
+# TODO(behdad) Implement --unicode='*' to choose all cmap'ed
+# TODO(behdad) Drop old-spec Indic scripts
+
+
+class Options(object):
+
+	class OptionError(Exception): pass
+	class UnknownOptionError(OptionError): pass
+
+	# spaces in tag names (e.g. "SVG ", "cvt ") are stripped by the argument parser
+	_drop_tables_default = ['BASE', 'JSTF', 'DSIG', 'EBDT', 'EBLC',
+				'EBSC', 'SVG', 'PCLT', 'LTSH']
+	_drop_tables_default += ['Feat', 'Glat', 'Gloc', 'Silf', 'Sill']  # Graphite
+	_drop_tables_default += ['sbix']  # Color
+	_no_subset_tables_default = ['avar', 'fvar',
+				     'gasp', 'head', 'hhea', 'maxp',
+				     'vhea', 'OS/2', 'loca', 'name', 'cvt',
+				     'fpgm', 'prep', 'VDMX', 'DSIG', 'CPAL',
+				     'MVAR', 'cvar', 'STAT']
+	_hinting_tables_default = ['cvt', 'cvar', 'fpgm', 'prep', 'hdmx', 'VDMX']
+
+	# Based on HarfBuzz shapers
+	_layout_features_groups = {
+		# Default shaper
+		'common': ['rvrn', 'ccmp', 'liga', 'locl', 'mark', 'mkmk', 'rlig'],
+		'fractions': ['frac', 'numr', 'dnom'],
+		'horizontal': ['calt', 'clig', 'curs', 'kern', 'rclt'],
+		'vertical': ['valt', 'vert', 'vkrn', 'vpal', 'vrt2'],
+		'ltr': ['ltra', 'ltrm'],
+		'rtl': ['rtla', 'rtlm'],
+		# Complex shapers
+		'arabic': ['init', 'medi', 'fina', 'isol', 'med2', 'fin2', 'fin3',
+			   'cswh', 'mset', 'stch'],
+		'hangul': ['ljmo', 'vjmo', 'tjmo'],
+		'tibetan': ['abvs', 'blws', 'abvm', 'blwm'],
+		'indic': ['nukt', 'akhn', 'rphf', 'rkrf', 'pref', 'blwf', 'half',
+			  'abvf', 'pstf', 'cfar', 'vatu', 'cjct', 'init', 'pres',
+			  'abvs', 'blws', 'psts', 'haln', 'dist', 'abvm', 'blwm'],
+	}
+	_layout_features_default = _uniq_sort(sum(
+			iter(_layout_features_groups.values()), []))
+
+	def __init__(self, **kwargs):
+
+		self.drop_tables = self._drop_tables_default[:]
+		self.no_subset_tables = self._no_subset_tables_default[:]
+		self.passthrough_tables = False  # keep/drop tables we can't subset
+		self.hinting_tables = self._hinting_tables_default[:]
+		self.legacy_kern = False # drop 'kern' table if GPOS available
+		self.layout_features = self._layout_features_default[:]
+		self.ignore_missing_glyphs = False
+		self.ignore_missing_unicodes = True
+		self.hinting = True
+		self.glyph_names = False
+		self.legacy_cmap = False
+		self.symbol_cmap = False
+		self.name_IDs = [0, 1, 2, 3, 4, 5, 6] # https://github.com/fonttools/fonttools/issues/1170#issuecomment-364631225
+		self.name_legacy = False
+		self.name_languages = [0x0409] # English
+		self.obfuscate_names = False # to make webfont unusable as a system font
+		self.notdef_glyph = True # gid0 for TrueType / .notdef for CFF
+		self.notdef_outline = False # No need for notdef to have an outline really
+		self.recommended_glyphs = False # gid1, gid2, gid3 for TrueType
+		self.recalc_bounds = False # Recalculate font bounding boxes
+		self.recalc_timestamp = False # Recalculate font modified timestamp
+		self.prune_unicode_ranges = True # Clear unused 'ulUnicodeRange' bits
+		self.recalc_average_width = False # update 'xAvgCharWidth'
+		self.canonical_order = None # Order tables as recommended
+		self.flavor = None  # May be 'woff' or 'woff2'
+		self.with_zopfli = False  # use zopfli instead of zlib for WOFF 1.0
+		self.desubroutinize = False # Desubroutinize CFF CharStrings
+		self.verbose = False
+		self.timing = False
+		self.xml = False
+		self.font_number = -1
+
+		self.set(**kwargs)
+
+	def set(self, **kwargs):
+		for k,v in kwargs.items():
+			if not hasattr(self, k):
+				raise self.UnknownOptionError("Unknown option '%s'" % k)
+			setattr(self, k, v)
+
+	def parse_opts(self, argv, ignore_unknown=[]):
+		posargs = []
+		passthru_options = []
+		for a in argv:
+			orig_a = a
+			if not a.startswith('--'):
+				posargs.append(a)
+				continue
+			a = a[2:]
+			i = a.find('=')
+			op = '='
+			if i == -1:
+				if a.startswith("no-"):
+					k = a[3:]
+					if k == "canonical-order":
+						# reorderTables=None is faster than False (the latter
+						# still reorders to "keep" the original table order)
+						v = None
+					else:
+						v = False
+				else:
+					k = a
+					v = True
+				if k.endswith("?"):
+					k = k[:-1]
+					v = '?'
+			else:
+				k = a[:i]
+				if k[-1] in "-+":
+					op = k[-1]+'='	# Op is '-=' or '+=' now.
+					k = k[:-1]
+				v = a[i+1:]
+			ok = k
+			k = k.replace('-', '_')
+			if not hasattr(self, k):
+				if ignore_unknown is True or ok in ignore_unknown:
+					passthru_options.append(orig_a)
+					continue
+				else:
+					raise self.UnknownOptionError("Unknown option '%s'" % a)
+
+			ov = getattr(self, k)
+			if v == '?':
+					print("Current setting for '%s' is: %s" % (ok, ov))
+					continue
+			if isinstance(ov, bool):
+				v = bool(v)
+			elif isinstance(ov, int):
+				v = int(v)
+			elif isinstance(ov, str):
+				v = str(v) # redundant
+			elif isinstance(ov, list):
+				if isinstance(v, bool):
+					raise self.OptionError("Option '%s' requires values to be specified using '='" % a)
+				vv = v.replace(',', ' ').split()
+				if vv == ['']:
+					vv = []
+				vv = [int(x, 0) if len(x) and x[0] in "0123456789" else x for x in vv]
+				if op == '=':
+					v = vv
+				elif op == '+=':
+					v = ov
+					v.extend(vv)
+				elif op == '-=':
+					v = ov
+					for x in vv:
+						if x in v:
+							v.remove(x)
+				else:
+					assert False
+
+			setattr(self, k, v)
+
+		return posargs + passthru_options
+
+
+class Subsetter(object):
+
+	class SubsettingError(Exception): pass
+	class MissingGlyphsSubsettingError(SubsettingError): pass
+	class MissingUnicodesSubsettingError(SubsettingError): pass
+
+	def __init__(self, options=None):
+
+		if not options:
+			options = Options()
+
+		self.options = options
+		self.unicodes_requested = set()
+		self.glyph_names_requested = set()
+		self.glyph_ids_requested = set()
+
+	def populate(self, glyphs=[], gids=[], unicodes=[], text=""):
+		self.unicodes_requested.update(unicodes)
+		if isinstance(text, bytes):
+			text = text.decode("utf_8")
+		text_utf32 = text.encode("utf-32-be")
+		nchars = len(text_utf32)//4
+		for u in struct.unpack('>%dL' % nchars, text_utf32):
+			self.unicodes_requested.add(u)
+		self.glyph_names_requested.update(glyphs)
+		self.glyph_ids_requested.update(gids)
+
+	def _prune_pre_subset(self, font):
+		for tag in self._sort_tables(font):
+			if (tag.strip() in self.options.drop_tables or
+			    (tag.strip() in self.options.hinting_tables and not self.options.hinting) or
+			    (tag == 'kern' and (not self.options.legacy_kern and 'GPOS' in font))):
+				log.info("%s dropped", tag)
+				del font[tag]
+				continue
+
+			clazz = ttLib.getTableClass(tag)
+
+			if hasattr(clazz, 'prune_pre_subset'):
+				with timer("load '%s'" % tag):
+					table = font[tag]
+				with timer("prune '%s'" % tag):
+					retain = table.prune_pre_subset(font, self.options)
+				if not retain:
+					log.info("%s pruned to empty; dropped", tag)
+					del font[tag]
+					continue
+				else:
+					log.info("%s pruned", tag)
+
+	def _closure_glyphs(self, font):
+
+		realGlyphs = set(font.getGlyphOrder())
+		glyph_order = font.getGlyphOrder()
+
+		self.glyphs_requested = set()
+		self.glyphs_requested.update(self.glyph_names_requested)
+		self.glyphs_requested.update(glyph_order[i]
+					     for i in self.glyph_ids_requested
+					     if i < len(glyph_order))
+
+		self.glyphs_missing = set()
+		self.glyphs_missing.update(self.glyphs_requested.difference(realGlyphs))
+		self.glyphs_missing.update(i for i in self.glyph_ids_requested
+					     if i >= len(glyph_order))
+		if self.glyphs_missing:
+			log.info("Missing requested glyphs: %s", self.glyphs_missing)
+			if not self.options.ignore_missing_glyphs:
+				raise self.MissingGlyphsSubsettingError(self.glyphs_missing)
+
+		self.glyphs = self.glyphs_requested.copy()
+
+		self.unicodes_missing = set()
+		if 'cmap' in font:
+			with timer("close glyph list over 'cmap'"):
+				font['cmap'].closure_glyphs(self)
+				self.glyphs.intersection_update(realGlyphs)
+		self.glyphs_cmaped = frozenset(self.glyphs)
+		if self.unicodes_missing:
+			missing = ["U+%04X" % u for u in self.unicodes_missing]
+			log.info("Missing glyphs for requested Unicodes: %s", missing)
+			if not self.options.ignore_missing_unicodes:
+				raise self.MissingUnicodesSubsettingError(missing)
+			del missing
+
+		if self.options.notdef_glyph:
+			if 'glyf' in font:
+				self.glyphs.add(font.getGlyphName(0))
+				log.info("Added gid0 to subset")
+			else:
+				self.glyphs.add('.notdef')
+				log.info("Added .notdef to subset")
+		if self.options.recommended_glyphs:
+			if 'glyf' in font:
+				for i in range(min(4, len(font.getGlyphOrder()))):
+					self.glyphs.add(font.getGlyphName(i))
+				log.info("Added first four glyphs to subset")
+
+		if 'GSUB' in font:
+			with timer("close glyph list over 'GSUB'"):
+				log.info("Closing glyph list over 'GSUB': %d glyphs before",
+						 len(self.glyphs))
+				log.glyphs(self.glyphs, font=font)
+				font['GSUB'].closure_glyphs(self)
+				self.glyphs.intersection_update(realGlyphs)
+				log.info("Closed glyph list over 'GSUB': %d glyphs after",
+						 len(self.glyphs))
+				log.glyphs(self.glyphs, font=font)
+		self.glyphs_gsubed = frozenset(self.glyphs)
+
+		if 'MATH' in font:
+			with timer("close glyph list over 'MATH'"):
+				log.info("Closing glyph list over 'MATH': %d glyphs before",
+						 len(self.glyphs))
+				log.glyphs(self.glyphs, font=font)
+				font['MATH'].closure_glyphs(self)
+				self.glyphs.intersection_update(realGlyphs)
+				log.info("Closed glyph list over 'MATH': %d glyphs after",
+						 len(self.glyphs))
+				log.glyphs(self.glyphs, font=font)
+		self.glyphs_mathed = frozenset(self.glyphs)
+
+		for table in ('COLR', 'bsln'):
+			if table in font:
+				with timer("close glyph list over '%s'" % table):
+					log.info("Closing glyph list over '%s': %d glyphs before",
+							 table, len(self.glyphs))
+					log.glyphs(self.glyphs, font=font)
+					font[table].closure_glyphs(self)
+					self.glyphs.intersection_update(realGlyphs)
+					log.info("Closed glyph list over '%s': %d glyphs after",
+							 table, len(self.glyphs))
+					log.glyphs(self.glyphs, font=font)
+
+		if 'glyf' in font:
+			with timer("close glyph list over 'glyf'"):
+				log.info("Closing glyph list over 'glyf': %d glyphs before",
+						 len(self.glyphs))
+				log.glyphs(self.glyphs, font=font)
+				font['glyf'].closure_glyphs(self)
+				self.glyphs.intersection_update(realGlyphs)
+				log.info("Closed glyph list over 'glyf': %d glyphs after",
+						 len(self.glyphs))
+				log.glyphs(self.glyphs, font=font)
+		self.glyphs_glyfed = frozenset(self.glyphs)
+
+		if 'CFF ' in font:
+			with timer("close glyph list over 'CFF '"):
+				log.info("Closing glyph list over 'CFF ': %d glyphs before",
+						 len(self.glyphs))
+				log.glyphs(self.glyphs, font=font)
+				font['CFF '].closure_glyphs(self)
+				self.glyphs.intersection_update(realGlyphs)
+				log.info("Closed glyph list over 'CFF ': %d glyphs after",
+						 len(self.glyphs))
+				log.glyphs(self.glyphs, font=font)
+		self.glyphs_cffed = frozenset(self.glyphs)
+
+		self.glyphs_all = frozenset(self.glyphs)
+
+		order = font.getReverseGlyphMap()
+		self.reverseOrigGlyphMap = {g:order[g] for g in self.glyphs_all}
+
+		log.info("Retaining %d glyphs", len(self.glyphs_all))
+
+		del self.glyphs
+
+	def _subset_glyphs(self, font):
+		for tag in self._sort_tables(font):
+			clazz = ttLib.getTableClass(tag)
+
+			if tag.strip() in self.options.no_subset_tables:
+				log.info("%s subsetting not needed", tag)
+			elif hasattr(clazz, 'subset_glyphs'):
+				with timer("subset '%s'" % tag):
+					table = font[tag]
+					self.glyphs = self.glyphs_all
+					retain = table.subset_glyphs(self)
+					del self.glyphs
+				if not retain:
+					log.info("%s subsetted to empty; dropped", tag)
+					del font[tag]
+				else:
+					log.info("%s subsetted", tag)
+			elif self.options.passthrough_tables:
+				log.info("%s NOT subset; don't know how to subset", tag)
+			else:
+				log.warning("%s NOT subset; don't know how to subset; dropped", tag)
+				del font[tag]
+
+		with timer("subset GlyphOrder"):
+			glyphOrder = font.getGlyphOrder()
+			glyphOrder = [g for g in glyphOrder if g in self.glyphs_all]
+			font.setGlyphOrder(glyphOrder)
+			font._buildReverseGlyphOrderDict()
+
+	def _prune_post_subset(self, font):
+		for tag in font.keys():
+			if tag == 'GlyphOrder': continue
+			if tag == 'OS/2' and self.options.prune_unicode_ranges:
+				old_uniranges = font[tag].getUnicodeRanges()
+				new_uniranges = font[tag].recalcUnicodeRanges(font, pruneOnly=True)
+				if old_uniranges != new_uniranges:
+					log.info("%s Unicode ranges pruned: %s", tag, sorted(new_uniranges))
+				if self.options.recalc_average_width:
+					widths = [m[0] for m in font["hmtx"].metrics.values() if m[0] > 0]
+					avg_width = otRound(sum(widths) / len(widths))
+					if avg_width != font[tag].xAvgCharWidth:
+						font[tag].xAvgCharWidth = avg_width
+						log.info("%s xAvgCharWidth updated: %d", tag, avg_width)
+			clazz = ttLib.getTableClass(tag)
+			if hasattr(clazz, 'prune_post_subset'):
+				with timer("prune '%s'" % tag):
+					table = font[tag]
+					retain = table.prune_post_subset(font, self.options)
+				if not retain:
+					log.info("%s pruned to empty; dropped", tag)
+					del font[tag]
+				else:
+					log.info("%s pruned", tag)
+
+	def _sort_tables(self, font):
+		tagOrder = ['fvar', 'avar', 'gvar', 'name', 'glyf']
+		tagOrder = {t: i + 1 for i, t in enumerate(tagOrder)}
+		tags = sorted(font.keys(), key=lambda tag: tagOrder.get(tag, 0))
+		return [t for t in tags if t != 'GlyphOrder']
+
+	def subset(self, font):
+		self._prune_pre_subset(font)
+		self._closure_glyphs(font)
+		self._subset_glyphs(font)
+		self._prune_post_subset(font)
+
+
+@timer("load font")
+def load_font(fontFile,
+	      options,
+	      allowVID=False,
+	      checkChecksums=False,
+	      dontLoadGlyphNames=False,
+	      lazy=True):
+
+	font = ttLib.TTFont(fontFile,
+			    allowVID=allowVID,
+			    checkChecksums=checkChecksums,
+			    recalcBBoxes=options.recalc_bounds,
+			    recalcTimestamp=options.recalc_timestamp,
+			    lazy=lazy,
+			    fontNumber=options.font_number)
+
+	# Hack:
+	#
+	# If we don't need glyph names, change 'post' class to not try to
+	# load them.	It avoid lots of headache with broken fonts as well
+	# as loading time.
+	#
+	# Ideally ttLib should provide a way to ask it to skip loading
+	# glyph names.	But it currently doesn't provide such a thing.
+	#
+	if dontLoadGlyphNames:
+		post = ttLib.getTableClass('post')
+		saved = post.decode_format_2_0
+		post.decode_format_2_0 = post.decode_format_3_0
+		f = font['post']
+		if f.formatType == 2.0:
+			f.formatType = 3.0
+		post.decode_format_2_0 = saved
+
+	return font
+
+@timer("compile and save font")
+def save_font(font, outfile, options):
+	if options.flavor and not hasattr(font, 'flavor'):
+		raise Exception("fonttools version does not support flavors.")
+	if options.with_zopfli and options.flavor == "woff":
+		from fontTools.ttLib import sfnt
+		sfnt.USE_ZOPFLI = True
+	font.flavor = options.flavor
+	font.save(outfile, reorderTables=options.canonical_order)
+
+def parse_unicodes(s):
+	import re
+	s = re.sub (r"0[xX]", " ", s)
+	s = re.sub (r"[<+>,;&#\\xXuU\n	]", " ", s)
+	l = []
+	for item in s.split():
+		fields = item.split('-')
+		if len(fields) == 1:
+			l.append(int(item, 16))
+		else:
+			start,end = fields
+			l.extend(range(int(start, 16), int(end, 16)+1))
+	return l
+
+def parse_gids(s):
+	l = []
+	for item in s.replace(',', ' ').split():
+		fields = item.split('-')
+		if len(fields) == 1:
+			l.append(int(fields[0]))
+		else:
+			l.extend(range(int(fields[0]), int(fields[1])+1))
+	return l
+
+def parse_glyphs(s):
+	return s.replace(',', ' ').split()
+
+def usage():
+	print("usage:", __usage__, file=sys.stderr)
+	print("Try pyftsubset --help for more information.\n", file=sys.stderr)
+
+@timer("make one with everything (TOTAL TIME)")
+def main(args=None):
+	from os.path import splitext
+	from fontTools import configLogger
+
+	if args is None:
+		args = sys.argv[1:]
+
+	if '--help' in args:
+		print(__doc__)
+		return 0
+
+	options = Options()
+	try:
+		args = options.parse_opts(args,
+			ignore_unknown=['gids', 'gids-file',
+							'glyphs', 'glyphs-file',
+							'text', 'text-file',
+							'unicodes', 'unicodes-file',
+							'output-file'])
+	except options.OptionError as e:
+		usage()
+		print("ERROR:", e, file=sys.stderr)
+		return 2
+
+	if len(args) < 2:
+		usage()
+		return 1
+
+	configLogger(level=logging.INFO if options.verbose else logging.WARNING)
+	if options.timing:
+		timer.logger.setLevel(logging.DEBUG)
+	else:
+		timer.logger.disabled = True
+
+	fontfile = args[0]
+	args = args[1:]
+
+	subsetter = Subsetter(options=options)
+	basename, extension = splitext(fontfile)
+	outfile = basename + '.subset' + extension
+	glyphs = []
+	gids = []
+	unicodes = []
+	wildcard_glyphs = False
+	wildcard_unicodes = False
+	text = ""
+	for g in args:
+		if g == '*':
+			wildcard_glyphs = True
+			continue
+		if g.startswith('--output-file='):
+			outfile = g[14:]
+			continue
+		if g.startswith('--text='):
+			text += g[7:]
+			continue
+		if g.startswith('--text-file='):
+			text += open(g[12:], encoding='utf-8').read().replace('\n', '')
+			continue
+		if g.startswith('--unicodes='):
+			if g[11:] == '*':
+				wildcard_unicodes = True
+			else:
+				unicodes.extend(parse_unicodes(g[11:]))
+			continue
+		if g.startswith('--unicodes-file='):
+			for line in open(g[16:]).readlines():
+				unicodes.extend(parse_unicodes(line.split('#')[0]))
+			continue
+		if g.startswith('--gids='):
+			gids.extend(parse_gids(g[7:]))
+			continue
+		if g.startswith('--gids-file='):
+			for line in open(g[12:]).readlines():
+				gids.extend(parse_gids(line.split('#')[0]))
+			continue
+		if g.startswith('--glyphs='):
+			if g[9:] == '*':
+				wildcard_glyphs = True
+			else:
+				glyphs.extend(parse_glyphs(g[9:]))
+			continue
+		if g.startswith('--glyphs-file='):
+			for line in open(g[14:]).readlines():
+				glyphs.extend(parse_glyphs(line.split('#')[0]))
+			continue
+		glyphs.append(g)
+
+	dontLoadGlyphNames = not options.glyph_names and not glyphs
+	font = load_font(fontfile, options, dontLoadGlyphNames=dontLoadGlyphNames)
+
+	with timer("compile glyph list"):
+		if wildcard_glyphs:
+			glyphs.extend(font.getGlyphOrder())
+		if wildcard_unicodes:
+			for t in font['cmap'].tables:
+				if t.isUnicode():
+					unicodes.extend(t.cmap.keys())
+		assert '' not in glyphs
+
+	log.info("Text: '%s'" % text)
+	log.info("Unicodes: %s", unicodes)
+	log.info("Glyphs: %s", glyphs)
+	log.info("Gids: %s", gids)
+
+	subsetter.populate(glyphs=glyphs, gids=gids, unicodes=unicodes, text=text)
+	subsetter.subset(font)
+
+	save_font(font, outfile, options)
+
+	if options.verbose:
+		import os
+		log.info("Input font:% 7d bytes: %s" % (os.path.getsize(fontfile), fontfile))
+		log.info("Subset font:% 7d bytes: %s" % (os.path.getsize(outfile), outfile))
+
+	if options.xml:
+		font.saveXML(sys.stdout)
+
+	font.close()
+
+
+__all__ = [
+	'Options',
+	'Subsetter',
+	'load_font',
+	'save_font',
+	'parse_gids',
+	'parse_glyphs',
+	'parse_unicodes',
+	'main'
+]
+
+if __name__ == '__main__':
+	sys.exit(main())
diff --git a/Lib/fontTools/subset/__main__.py b/Lib/fontTools/subset/__main__.py
new file mode 100644
index 0000000..10e470f
--- /dev/null
+++ b/Lib/fontTools/subset/__main__.py
@@ -0,0 +1,7 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+import sys
+from fontTools.subset import main
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/Lib/fontTools/svgLib/__init__.py b/Lib/fontTools/svgLib/__init__.py
new file mode 100644
index 0000000..b301a3b
--- /dev/null
+++ b/Lib/fontTools/svgLib/__init__.py
@@ -0,0 +1,6 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+
+from .path import SVGPath, parse_path
+
+__all__ = ["SVGPath", "parse_path"]
diff --git a/Lib/fontTools/svgLib/path/__init__.py b/Lib/fontTools/svgLib/path/__init__.py
new file mode 100644
index 0000000..4f17e76
--- /dev/null
+++ b/Lib/fontTools/svgLib/path/__init__.py
@@ -0,0 +1,58 @@
+from __future__ import (
+    print_function, division, absolute_import, unicode_literals)
+from fontTools.misc.py23 import *
+
+from fontTools.pens.transformPen import TransformPen
+from .parser import parse_path
+
+try:
+    from xml.etree import cElementTree as ElementTree  # python 2
+except ImportError:  # pragma nocover
+    from xml.etree import ElementTree  # python 3
+
+
+__all__ = [tostr(s) for s in ("SVGPath", "parse_path")]
+
+
+class SVGPath(object):
+    """ Parse SVG ``path`` elements from a file or string, and draw them
+    onto a glyph object that supports the FontTools Pen protocol.
+
+    For example, reading from an SVG file and drawing to a Defcon Glyph:
+
+        import defcon
+        glyph = defcon.Glyph()
+        pen = glyph.getPen()
+        svg = SVGPath("path/to/a.svg")
+        svg.draw(pen)
+
+    Or reading from a string containing SVG data, using the alternative
+    'fromstring' (a class method):
+
+        data = '<?xml version="1.0" ...'
+        svg = SVGPath.fromstring(data)
+        svg.draw(pen)
+
+    Both constructors can optionally take a 'transform' matrix (6-float
+    tuple, or a FontTools Transform object) to modify the draw output.
+    """
+
+    def __init__(self, filename=None, transform=None):
+        if filename is None:
+            self.root = ElementTree.ElementTree()
+        else:
+            tree = ElementTree.parse(filename)
+            self.root = tree.getroot()
+        self.transform = transform
+
+    @classmethod
+    def fromstring(cls, data, transform=None):
+        self = cls(transform=transform)
+        self.root = ElementTree.fromstring(data)
+        return self
+
+    def draw(self, pen):
+        if self.transform:
+            pen = TransformPen(pen, self.transform)
+        for el in self.root.findall(".//{http://www.w3.org/2000/svg}path[@d]"):
+            parse_path(el.get("d"), pen)
diff --git a/Lib/fontTools/svgLib/path/parser.py b/Lib/fontTools/svgLib/path/parser.py
new file mode 100644
index 0000000..4daefca
--- /dev/null
+++ b/Lib/fontTools/svgLib/path/parser.py
@@ -0,0 +1,216 @@
+# SVG Path specification parser.
+# This is an adaptation from 'svg.path' by Lennart Regebro (@regebro),
+# modified so that the parser takes a FontTools Pen object instead of
+# returning a list of svg.path Path objects.
+# The original code can be found at:
+# https://github.com/regebro/svg.path/blob/4f9b6e3/src/svg/path/parser.py
+# Copyright (c) 2013-2014 Lennart Regebro
+# License: MIT
+
+from __future__ import (
+    print_function, division, absolute_import, unicode_literals)
+from fontTools.misc.py23 import *
+import re
+
+COMMANDS = set('MmZzLlHhVvCcSsQqTtAa')
+UPPERCASE = set('MZLHVCSQTA')
+
+COMMAND_RE = re.compile("([MmZzLlHhVvCcSsQqTtAa])")
+FLOAT_RE = re.compile("[-+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?")
+
+
+def _tokenize_path(pathdef):
+    for x in COMMAND_RE.split(pathdef):
+        if x in COMMANDS:
+            yield x
+        for token in FLOAT_RE.findall(x):
+            yield token
+
+
+def parse_path(pathdef, pen, current_pos=(0, 0)):
+    """ Parse SVG path definition (i.e. "d" attribute of <path> elements)
+    and call a 'pen' object's moveTo, lineTo, curveTo, qCurveTo and closePath
+    methods.
+
+    If 'current_pos' (2-float tuple) is provided, the initial moveTo will
+    be relative to that instead being absolute.
+
+    Arc segments (commands "A" or "a") are not currently supported, and raise
+    NotImplementedError.
+    """
+    # In the SVG specs, initial movetos are absolute, even if
+    # specified as 'm'. This is the default behavior here as well.
+    # But if you pass in a current_pos variable, the initial moveto
+    # will be relative to that current_pos. This is useful.
+    current_pos = complex(*current_pos)
+
+    elements = list(_tokenize_path(pathdef))
+    # Reverse for easy use of .pop()
+    elements.reverse()
+
+    start_pos = None
+    command = None
+    last_control = None
+
+    while elements:
+
+        if elements[-1] in COMMANDS:
+            # New command.
+            last_command = command  # Used by S and T
+            command = elements.pop()
+            absolute = command in UPPERCASE
+            command = command.upper()
+        else:
+            # If this element starts with numbers, it is an implicit command
+            # and we don't change the command. Check that it's allowed:
+            if command is None:
+                raise ValueError("Unallowed implicit command in %s, position %s" % (
+                    pathdef, len(pathdef.split()) - len(elements)))
+            last_command = command  # Used by S and T
+
+        if command == 'M':
+            # Moveto command.
+            x = elements.pop()
+            y = elements.pop()
+            pos = float(x) + float(y) * 1j
+            if absolute:
+                current_pos = pos
+            else:
+                current_pos += pos
+
+            # M is not preceded by Z; it's an open subpath
+            if start_pos is not None:
+                pen.endPath()
+
+            pen.moveTo((current_pos.real, current_pos.imag))
+
+            # when M is called, reset start_pos
+            # This behavior of Z is defined in svg spec:
+            # http://www.w3.org/TR/SVG/paths.html#PathDataClosePathCommand
+            start_pos = current_pos
+
+            # Implicit moveto commands are treated as lineto commands.
+            # So we set command to lineto here, in case there are
+            # further implicit commands after this moveto.
+            command = 'L'
+
+        elif command == 'Z':
+            # Close path
+            if current_pos != start_pos:
+                pen.lineTo((start_pos.real, start_pos.imag))
+            pen.closePath()
+            current_pos = start_pos
+            start_pos = None
+            command = None  # You can't have implicit commands after closing.
+
+        elif command == 'L':
+            x = elements.pop()
+            y = elements.pop()
+            pos = float(x) + float(y) * 1j
+            if not absolute:
+                pos += current_pos
+            pen.lineTo((pos.real, pos.imag))
+            current_pos = pos
+
+        elif command == 'H':
+            x = elements.pop()
+            pos = float(x) + current_pos.imag * 1j
+            if not absolute:
+                pos += current_pos.real
+            pen.lineTo((pos.real, pos.imag))
+            current_pos = pos
+
+        elif command == 'V':
+            y = elements.pop()
+            pos = current_pos.real + float(y) * 1j
+            if not absolute:
+                pos += current_pos.imag * 1j
+            pen.lineTo((pos.real, pos.imag))
+            current_pos = pos
+
+        elif command == 'C':
+            control1 = float(elements.pop()) + float(elements.pop()) * 1j
+            control2 = float(elements.pop()) + float(elements.pop()) * 1j
+            end = float(elements.pop()) + float(elements.pop()) * 1j
+
+            if not absolute:
+                control1 += current_pos
+                control2 += current_pos
+                end += current_pos
+
+            pen.curveTo((control1.real, control1.imag),
+                        (control2.real, control2.imag),
+                        (end.real, end.imag))
+            current_pos = end
+            last_control = control2
+
+        elif command == 'S':
+            # Smooth curve. First control point is the "reflection" of
+            # the second control point in the previous path.
+
+            if last_command not in 'CS':
+                # If there is no previous command or if the previous command
+                # was not an C, c, S or s, assume the first control point is
+                # coincident with the current point.
+                control1 = current_pos
+            else:
+                # The first control point is assumed to be the reflection of
+                # the second control point on the previous command relative
+                # to the current point.
+                control1 = current_pos + current_pos - last_control
+
+            control2 = float(elements.pop()) + float(elements.pop()) * 1j
+            end = float(elements.pop()) + float(elements.pop()) * 1j
+
+            if not absolute:
+                control2 += current_pos
+                end += current_pos
+
+            pen.curveTo((control1.real, control1.imag),
+                        (control2.real, control2.imag),
+                        (end.real, end.imag))
+            current_pos = end
+            last_control = control2
+
+        elif command == 'Q':
+            control = float(elements.pop()) + float(elements.pop()) * 1j
+            end = float(elements.pop()) + float(elements.pop()) * 1j
+
+            if not absolute:
+                control += current_pos
+                end += current_pos
+
+            pen.qCurveTo((control.real, control.imag), (end.real, end.imag))
+            current_pos = end
+            last_control = control
+
+        elif command == 'T':
+            # Smooth curve. Control point is the "reflection" of
+            # the second control point in the previous path.
+
+            if last_command not in 'QT':
+                # If there is no previous command or if the previous command
+                # was not an Q, q, T or t, assume the first control point is
+                # coincident with the current point.
+                control = current_pos
+            else:
+                # The control point is assumed to be the reflection of
+                # the control point on the previous command relative
+                # to the current point.
+                control = current_pos + current_pos - last_control
+
+            end = float(elements.pop()) + float(elements.pop()) * 1j
+
+            if not absolute:
+                end += current_pos
+
+            pen.qCurveTo((control.real, control.imag), (end.real, end.imag))
+            current_pos = end
+            last_control = control
+
+        elif command == 'A':
+            raise NotImplementedError('arcs are not supported')
+
+    # no final Z command, it's an open path
+    if start_pos is not None:
+        pen.endPath()
diff --git a/Lib/fontTools/t1Lib.py b/Lib/fontTools/t1Lib.py
deleted file mode 100644
index 14cc904..0000000
--- a/Lib/fontTools/t1Lib.py
+++ /dev/null
@@ -1,372 +0,0 @@
-"""fontTools.t1Lib.py -- Tools for PostScript Type 1 fonts
-
-Functions for reading and writing raw Type 1 data:
-
-read(path)
-	reads any Type 1 font file, returns the raw data and a type indicator: 
-	'LWFN', 'PFB' or 'OTHER', depending on the format of the file pointed 
-	to by 'path'. 
-	Raises an error when the file does not contain valid Type 1 data.
-
-write(path, data, kind='OTHER', dohex=False)
-	writes raw Type 1 data to the file pointed to by 'path'. 
-	'kind' can be one of 'LWFN', 'PFB' or 'OTHER'; it defaults to 'OTHER'.
-	'dohex' is a flag which determines whether the eexec encrypted
-	part should be written as hexadecimal or binary, but only if kind
-	is 'LWFN' or 'PFB'.
-"""
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
-from fontTools.misc import eexec
-from fontTools.misc.macCreatorType import getMacCreatorAndType
-import os
-import re
-
-__author__ = "jvr"
-__version__ = "1.0b2"
-DEBUG = 0
-
-
-try:
-	try:
-		from Carbon import Res
-	except ImportError:
-		import Res  # MacPython < 2.2
-except ImportError:
-	haveMacSupport = 0
-else:
-	haveMacSupport = 1
-	import MacOS
-	
-
-class T1Error(Exception): pass
-
-
-class T1Font(object):
-	
-	"""Type 1 font class.
-	
-	Uses a minimal interpeter that supports just about enough PS to parse
-	Type 1 fonts.
-	"""
-	
-	def __init__(self, path=None):
-		if path is not None:
-			self.data, type = read(path)
-		else:
-			pass # XXX
-	
-	def saveAs(self, path, type):
-		write(path, self.getData(), type)
-	
-	def getData(self):
-		# XXX Todo: if the data has been converted to Python object,
-		# recreate the PS stream
-		return self.data
-	
-	def getGlyphSet(self):
-		"""Return a generic GlyphSet, which is a dict-like object
-		mapping glyph names to glyph objects. The returned glyph objects
-		have a .draw() method that supports the Pen protocol, and will
-		have an attribute named 'width', but only *after* the .draw() method
-		has been called.
-		
-		In the case of Type 1, the GlyphSet is simply the CharStrings dict.
-		"""
-		return self["CharStrings"]
-	
-	def __getitem__(self, key):
-		if not hasattr(self, "font"):
-			self.parse()
-		return self.font[key]
-	
-	def parse(self):
-		from fontTools.misc import psLib
-		from fontTools.misc import psCharStrings
-		self.font = psLib.suckfont(self.data)
-		charStrings = self.font["CharStrings"]
-		lenIV = self.font["Private"].get("lenIV", 4)
-		assert lenIV >= 0
-		subrs = self.font["Private"]["Subrs"]
-		for glyphName, charString in charStrings.items():
-			charString, R = eexec.decrypt(charString, 4330)
-			charStrings[glyphName] = psCharStrings.T1CharString(charString[lenIV:],
-					subrs=subrs)
-		for i in range(len(subrs)):
-			charString, R = eexec.decrypt(subrs[i], 4330)
-			subrs[i] = psCharStrings.T1CharString(charString[lenIV:], subrs=subrs)
-		del self.data
-
-
-# low level T1 data read and write functions
-
-def read(path, onlyHeader=False):
-	"""reads any Type 1 font file, returns raw data"""
-	normpath = path.lower()
-	creator, typ = getMacCreatorAndType(path)
-	if typ == 'LWFN':
-		return readLWFN(path, onlyHeader), 'LWFN'
-	if normpath[-4:] == '.pfb':
-		return readPFB(path, onlyHeader), 'PFB'
-	else:
-		return readOther(path), 'OTHER'
-
-def write(path, data, kind='OTHER', dohex=False):
-	assertType1(data)
-	kind = kind.upper()
-	try:
-		os.remove(path)
-	except os.error:
-		pass
-	err = 1
-	try:
-		if kind == 'LWFN':
-			writeLWFN(path, data)
-		elif kind == 'PFB':
-			writePFB(path, data)
-		else:
-			writeOther(path, data, dohex)
-		err = 0
-	finally:
-		if err and not DEBUG:
-			try:
-				os.remove(path)
-			except os.error:
-				pass
-
-
-# -- internal -- 
-
-LWFNCHUNKSIZE = 2000
-HEXLINELENGTH = 80
-
-
-def readLWFN(path, onlyHeader=False):
-	"""reads an LWFN font file, returns raw data"""
-	resRef = Res.FSOpenResFile(path, 1)  # read-only
-	try:
-		Res.UseResFile(resRef)
-		n = Res.Count1Resources('POST')
-		data = []
-		for i in range(501, 501 + n):
-			res = Res.Get1Resource('POST', i)
-			code = byteord(res.data[0])
-			if byteord(res.data[1]) != 0:
-				raise T1Error('corrupt LWFN file')
-			if code in [1, 2]:
-				if onlyHeader and code == 2:
-					break
-				data.append(res.data[2:])
-			elif code in [3, 5]:
-				break
-			elif code == 4:
-				f = open(path, "rb")
-				data.append(f.read())
-				f.close()
-			elif code == 0:
-				pass # comment, ignore
-			else:
-				raise T1Error('bad chunk code: ' + repr(code))
-	finally:
-		Res.CloseResFile(resRef)
-	data = bytesjoin(data)
-	assertType1(data)
-	return data
-
-def readPFB(path, onlyHeader=False):
-	"""reads a PFB font file, returns raw data"""
-	f = open(path, "rb")
-	data = []
-	while True:
-		if f.read(1) != bytechr(128):
-			raise T1Error('corrupt PFB file')
-		code = byteord(f.read(1))
-		if code in [1, 2]:
-			chunklen = stringToLong(f.read(4))
-			chunk = f.read(chunklen)
-			assert len(chunk) == chunklen
-			data.append(chunk)
-		elif code == 3:
-			break
-		else:
-			raise T1Error('bad chunk code: ' + repr(code))
-		if onlyHeader:
-			break
-	f.close()
-	data = bytesjoin(data)
-	assertType1(data)
-	return data
-
-def readOther(path):
-	"""reads any (font) file, returns raw data"""
-	f = open(path, "rb")
-	data = f.read()
-	f.close()
-	assertType1(data)
-	
-	chunks = findEncryptedChunks(data)
-	data = []
-	for isEncrypted, chunk in chunks:
-		if isEncrypted and isHex(chunk[:4]):
-			data.append(deHexString(chunk))
-		else:
-			data.append(chunk)
-	return bytesjoin(data)
-
-# file writing tools
-
-def writeLWFN(path, data):
-	Res.FSpCreateResFile(path, "just", "LWFN", 0)
-	resRef = Res.FSOpenResFile(path, 2)  # write-only
-	try:
-		Res.UseResFile(resRef)
-		resID = 501
-		chunks = findEncryptedChunks(data)
-		for isEncrypted, chunk in chunks:
-			if isEncrypted:
-				code = 2
-			else:
-				code = 1
-			while chunk:
-				res = Res.Resource(bytechr(code) + '\0' + chunk[:LWFNCHUNKSIZE - 2])
-				res.AddResource('POST', resID, '')
-				chunk = chunk[LWFNCHUNKSIZE - 2:]
-				resID = resID + 1
-		res = Res.Resource(bytechr(5) + '\0')
-		res.AddResource('POST', resID, '')
-	finally:
-		Res.CloseResFile(resRef)
-
-def writePFB(path, data):
-	chunks = findEncryptedChunks(data)
-	f = open(path, "wb")
-	try:
-		for isEncrypted, chunk in chunks:
-			if isEncrypted:
-				code = 2
-			else:
-				code = 1
-			f.write(bytechr(128) + bytechr(code))
-			f.write(longToString(len(chunk)))
-			f.write(chunk)
-		f.write(bytechr(128) + bytechr(3))
-	finally:
-		f.close()
-
-def writeOther(path, data, dohex=False):
-	chunks = findEncryptedChunks(data)
-	f = open(path, "wb")
-	try:
-		hexlinelen = HEXLINELENGTH // 2
-		for isEncrypted, chunk in chunks:
-			if isEncrypted:
-				code = 2
-			else:
-				code = 1
-			if code == 2 and dohex:
-				while chunk:
-					f.write(eexec.hexString(chunk[:hexlinelen]))
-					f.write('\r')
-					chunk = chunk[hexlinelen:]
-			else:
-				f.write(chunk)
-	finally:
-		f.close()
-
-
-# decryption tools
-
-EEXECBEGIN = "currentfile eexec"
-EEXECEND = '0' * 64
-EEXECINTERNALEND = "currentfile closefile"
-EEXECBEGINMARKER = "%-- eexec start\r"
-EEXECENDMARKER = "%-- eexec end\r"
-
-_ishexRE = re.compile('[0-9A-Fa-f]*$')
-
-def isHex(text):
-	return _ishexRE.match(text) is not None
-
-
-def decryptType1(data):
-	chunks = findEncryptedChunks(data)
-	data = []
-	for isEncrypted, chunk in chunks:
-		if isEncrypted:
-			if isHex(chunk[:4]):
-				chunk = deHexString(chunk)
-			decrypted, R = eexec.decrypt(chunk, 55665)
-			decrypted = decrypted[4:]
-			if decrypted[-len(EEXECINTERNALEND)-1:-1] != EEXECINTERNALEND \
-					and decrypted[-len(EEXECINTERNALEND)-2:-2] != EEXECINTERNALEND:
-				raise T1Error("invalid end of eexec part")
-			decrypted = decrypted[:-len(EEXECINTERNALEND)-2] + '\r'
-			data.append(EEXECBEGINMARKER + decrypted + EEXECENDMARKER)
-		else:
-			if chunk[-len(EEXECBEGIN)-1:-1] == EEXECBEGIN:
-				data.append(chunk[:-len(EEXECBEGIN)-1])
-			else:
-				data.append(chunk)
-	return bytesjoin(data)
-
-def findEncryptedChunks(data):
-	chunks = []
-	while True:
-		eBegin = data.find(EEXECBEGIN)
-		if eBegin < 0:
-			break
-		eBegin = eBegin + len(EEXECBEGIN) + 1
-		eEnd = data.find(EEXECEND, eBegin)
-		if eEnd < 0:
-			raise T1Error("can't find end of eexec part")
-		cypherText = data[eBegin:eEnd + 2]
-		if isHex(cypherText[:4]):
-			cypherText = deHexString(cypherText)
-		plainText, R = eexec.decrypt(cypherText, 55665)
-		eEndLocal = plainText.find(EEXECINTERNALEND)
-		if eEndLocal < 0:
-			raise T1Error("can't find end of eexec part")
-		chunks.append((0, data[:eBegin]))
-		chunks.append((1, cypherText[:eEndLocal + len(EEXECINTERNALEND) + 1]))
-		data = data[eEnd:]
-	chunks.append((0, data))
-	return chunks
-
-def deHexString(hexstring):
-	return eexec.deHexString(strjoin(hexstring.split()))
-
-
-# Type 1 assertion
-
-_fontType1RE = re.compile(br"/FontType\s+1\s+def")
-
-def assertType1(data):
-	for head in [b'%!PS-AdobeFont', b'%!FontType1']:
-		if data[:len(head)] == head:
-			break
-	else:
-		raise T1Error("not a PostScript font")
-	if not _fontType1RE.search(data):
-		raise T1Error("not a Type 1 font")
-	if data.find(b"currentfile eexec") < 0:
-		raise T1Error("not an encrypted Type 1 font")
-	# XXX what else?
-	return data
-
-
-# pfb helpers
-
-def longToString(long):
-	s = ""
-	for i in range(4):
-		s += bytechr((long & (0xff << (i * 8))) >> i * 8)
-	return s
-
-def stringToLong(s):
-	if len(s) != 4:
-		raise ValueError('string must be 4 bytes long')
-	l = 0
-	for i in range(4):
-		l += byteord(s[i]) << (i * 8)
-	return l
-
diff --git a/Lib/fontTools/t1Lib/__init__.py b/Lib/fontTools/t1Lib/__init__.py
new file mode 100644
index 0000000..5da9cea
--- /dev/null
+++ b/Lib/fontTools/t1Lib/__init__.py
@@ -0,0 +1,376 @@
+"""fontTools.t1Lib.py -- Tools for PostScript Type 1 fonts (Python2 only)
+
+Functions for reading and writing raw Type 1 data:
+
+read(path)
+	reads any Type 1 font file, returns the raw data and a type indicator:
+	'LWFN', 'PFB' or 'OTHER', depending on the format of the file pointed
+	to by 'path'.
+	Raises an error when the file does not contain valid Type 1 data.
+
+write(path, data, kind='OTHER', dohex=False)
+	writes raw Type 1 data to the file pointed to by 'path'.
+	'kind' can be one of 'LWFN', 'PFB' or 'OTHER'; it defaults to 'OTHER'.
+	'dohex' is a flag which determines whether the eexec encrypted
+	part should be written as hexadecimal or binary, but only if kind
+	is 'OTHER'.
+"""
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc import eexec
+from fontTools.misc.macCreatorType import getMacCreatorAndType
+import os
+import re
+
+__author__ = "jvr"
+__version__ = "1.0b2"
+DEBUG = 0
+
+
+try:
+	try:
+		from Carbon import Res
+	except ImportError:
+		import Res  # MacPython < 2.2
+except ImportError:
+	haveMacSupport = 0
+else:
+	haveMacSupport = 1
+
+
+class T1Error(Exception): pass
+
+
+class T1Font(object):
+
+	"""Type 1 font class.
+
+	Uses a minimal interpeter that supports just about enough PS to parse
+	Type 1 fonts.
+	"""
+
+	def __init__(self, path, encoding="ascii", kind=None):
+		if kind is None:
+			self.data, _ = read(path)
+		elif kind == "LWFN":
+			self.data = readLWFN(path)
+		elif kind == "PFB":
+			self.data = readPFB(path)
+		elif kind == "OTHER":
+			self.data = readOther(path)
+		else:
+			raise ValueError(kind)
+		self.encoding = encoding
+
+	def saveAs(self, path, type, dohex=False):
+		write(path, self.getData(), type, dohex)
+
+	def getData(self):
+		# XXX Todo: if the data has been converted to Python object,
+		# recreate the PS stream
+		return self.data
+
+	def getGlyphSet(self):
+		"""Return a generic GlyphSet, which is a dict-like object
+		mapping glyph names to glyph objects. The returned glyph objects
+		have a .draw() method that supports the Pen protocol, and will
+		have an attribute named 'width', but only *after* the .draw() method
+		has been called.
+
+		In the case of Type 1, the GlyphSet is simply the CharStrings dict.
+		"""
+		return self["CharStrings"]
+
+	def __getitem__(self, key):
+		if not hasattr(self, "font"):
+			self.parse()
+		return self.font[key]
+
+	def parse(self):
+		from fontTools.misc import psLib
+		from fontTools.misc import psCharStrings
+		self.font = psLib.suckfont(self.data, self.encoding)
+		charStrings = self.font["CharStrings"]
+		lenIV = self.font["Private"].get("lenIV", 4)
+		assert lenIV >= 0
+		subrs = self.font["Private"]["Subrs"]
+		for glyphName, charString in charStrings.items():
+			charString, R = eexec.decrypt(charString, 4330)
+			charStrings[glyphName] = psCharStrings.T1CharString(charString[lenIV:],
+					subrs=subrs)
+		for i in range(len(subrs)):
+			charString, R = eexec.decrypt(subrs[i], 4330)
+			subrs[i] = psCharStrings.T1CharString(charString[lenIV:], subrs=subrs)
+		del self.data
+
+
+# low level T1 data read and write functions
+
+def read(path, onlyHeader=False):
+	"""reads any Type 1 font file, returns raw data"""
+	normpath = path.lower()
+	creator, typ = getMacCreatorAndType(path)
+	if typ == 'LWFN':
+		return readLWFN(path, onlyHeader), 'LWFN'
+	if normpath[-4:] == '.pfb':
+		return readPFB(path, onlyHeader), 'PFB'
+	else:
+		return readOther(path), 'OTHER'
+
+def write(path, data, kind='OTHER', dohex=False):
+	assertType1(data)
+	kind = kind.upper()
+	try:
+		os.remove(path)
+	except os.error:
+		pass
+	err = 1
+	try:
+		if kind == 'LWFN':
+			writeLWFN(path, data)
+		elif kind == 'PFB':
+			writePFB(path, data)
+		else:
+			writeOther(path, data, dohex)
+		err = 0
+	finally:
+		if err and not DEBUG:
+			try:
+				os.remove(path)
+			except os.error:
+				pass
+
+
+# -- internal --
+
+LWFNCHUNKSIZE = 2000
+HEXLINELENGTH = 80
+
+
+def readLWFN(path, onlyHeader=False):
+	"""reads an LWFN font file, returns raw data"""
+	from fontTools.misc.macRes import ResourceReader
+	reader = ResourceReader(path)
+	try:
+		data = []
+		for res in reader.get('POST', []):
+			code = byteord(res.data[0])
+			if byteord(res.data[1]) != 0:
+				raise T1Error('corrupt LWFN file')
+			if code in [1, 2]:
+				if onlyHeader and code == 2:
+					break
+				data.append(res.data[2:])
+			elif code in [3, 5]:
+				break
+			elif code == 4:
+				f = open(path, "rb")
+				data.append(f.read())
+				f.close()
+			elif code == 0:
+				pass # comment, ignore
+			else:
+				raise T1Error('bad chunk code: ' + repr(code))
+	finally:
+		reader.close()
+	data = bytesjoin(data)
+	assertType1(data)
+	return data
+
+def readPFB(path, onlyHeader=False):
+	"""reads a PFB font file, returns raw data"""
+	f = open(path, "rb")
+	data = []
+	while True:
+		if f.read(1) != bytechr(128):
+			raise T1Error('corrupt PFB file')
+		code = byteord(f.read(1))
+		if code in [1, 2]:
+			chunklen = stringToLong(f.read(4))
+			chunk = f.read(chunklen)
+			assert len(chunk) == chunklen
+			data.append(chunk)
+		elif code == 3:
+			break
+		else:
+			raise T1Error('bad chunk code: ' + repr(code))
+		if onlyHeader:
+			break
+	f.close()
+	data = bytesjoin(data)
+	assertType1(data)
+	return data
+
+def readOther(path):
+	"""reads any (font) file, returns raw data"""
+	f = open(path, "rb")
+	data = f.read()
+	f.close()
+	assertType1(data)
+
+	chunks = findEncryptedChunks(data)
+	data = []
+	for isEncrypted, chunk in chunks:
+		if isEncrypted and isHex(chunk[:4]):
+			data.append(deHexString(chunk))
+		else:
+			data.append(chunk)
+	return bytesjoin(data)
+
+# file writing tools
+
+def writeLWFN(path, data):
+	# Res.FSpCreateResFile was deprecated in OS X 10.5
+	Res.FSpCreateResFile(path, "just", "LWFN", 0)
+	resRef = Res.FSOpenResFile(path, 2)  # write-only
+	try:
+		Res.UseResFile(resRef)
+		resID = 501
+		chunks = findEncryptedChunks(data)
+		for isEncrypted, chunk in chunks:
+			if isEncrypted:
+				code = 2
+			else:
+				code = 1
+			while chunk:
+				res = Res.Resource(bytechr(code) + '\0' + chunk[:LWFNCHUNKSIZE - 2])
+				res.AddResource('POST', resID, '')
+				chunk = chunk[LWFNCHUNKSIZE - 2:]
+				resID = resID + 1
+		res = Res.Resource(bytechr(5) + '\0')
+		res.AddResource('POST', resID, '')
+	finally:
+		Res.CloseResFile(resRef)
+
+def writePFB(path, data):
+	chunks = findEncryptedChunks(data)
+	f = open(path, "wb")
+	try:
+		for isEncrypted, chunk in chunks:
+			if isEncrypted:
+				code = 2
+			else:
+				code = 1
+			f.write(bytechr(128) + bytechr(code))
+			f.write(longToString(len(chunk)))
+			f.write(chunk)
+		f.write(bytechr(128) + bytechr(3))
+	finally:
+		f.close()
+
+def writeOther(path, data, dohex=False):
+	chunks = findEncryptedChunks(data)
+	f = open(path, "wb")
+	try:
+		hexlinelen = HEXLINELENGTH // 2
+		for isEncrypted, chunk in chunks:
+			if isEncrypted:
+				code = 2
+			else:
+				code = 1
+			if code == 2 and dohex:
+				while chunk:
+					f.write(eexec.hexString(chunk[:hexlinelen]))
+					f.write(b'\r')
+					chunk = chunk[hexlinelen:]
+			else:
+				f.write(chunk)
+	finally:
+		f.close()
+
+
+# decryption tools
+
+EEXECBEGIN = b"currentfile eexec"
+EEXECEND = b'0' * 64
+EEXECINTERNALEND = b"currentfile closefile"
+EEXECBEGINMARKER = b"%-- eexec start\r"
+EEXECENDMARKER = b"%-- eexec end\r"
+
+_ishexRE = re.compile(b'[0-9A-Fa-f]*$')
+
+def isHex(text):
+	return _ishexRE.match(text) is not None
+
+
+def decryptType1(data):
+	chunks = findEncryptedChunks(data)
+	data = []
+	for isEncrypted, chunk in chunks:
+		if isEncrypted:
+			if isHex(chunk[:4]):
+				chunk = deHexString(chunk)
+			decrypted, R = eexec.decrypt(chunk, 55665)
+			decrypted = decrypted[4:]
+			if decrypted[-len(EEXECINTERNALEND)-1:-1] != EEXECINTERNALEND \
+					and decrypted[-len(EEXECINTERNALEND)-2:-2] != EEXECINTERNALEND:
+				raise T1Error("invalid end of eexec part")
+			decrypted = decrypted[:-len(EEXECINTERNALEND)-2] + b'\r'
+			data.append(EEXECBEGINMARKER + decrypted + EEXECENDMARKER)
+		else:
+			if chunk[-len(EEXECBEGIN)-1:-1] == EEXECBEGIN:
+				data.append(chunk[:-len(EEXECBEGIN)-1])
+			else:
+				data.append(chunk)
+	return bytesjoin(data)
+
+def findEncryptedChunks(data):
+	chunks = []
+	while True:
+		eBegin = data.find(EEXECBEGIN)
+		if eBegin < 0:
+			break
+		eBegin = eBegin + len(EEXECBEGIN) + 1
+		eEnd = data.find(EEXECEND, eBegin)
+		if eEnd < 0:
+			raise T1Error("can't find end of eexec part")
+		cypherText = data[eBegin:eEnd + 2]
+		if isHex(cypherText[:4]):
+			cypherText = deHexString(cypherText)
+		plainText, R = eexec.decrypt(cypherText, 55665)
+		eEndLocal = plainText.find(EEXECINTERNALEND)
+		if eEndLocal < 0:
+			raise T1Error("can't find end of eexec part")
+		chunks.append((0, data[:eBegin]))
+		chunks.append((1, cypherText[:eEndLocal + len(EEXECINTERNALEND) + 1]))
+		data = data[eEnd:]
+	chunks.append((0, data))
+	return chunks
+
+def deHexString(hexstring):
+	return eexec.deHexString(bytesjoin(hexstring.split()))
+
+
+# Type 1 assertion
+
+_fontType1RE = re.compile(br"/FontType\s+1\s+def")
+
+def assertType1(data):
+	for head in [b'%!PS-AdobeFont', b'%!FontType1']:
+		if data[:len(head)] == head:
+			break
+	else:
+		raise T1Error("not a PostScript font")
+	if not _fontType1RE.search(data):
+		raise T1Error("not a Type 1 font")
+	if data.find(b"currentfile eexec") < 0:
+		raise T1Error("not an encrypted Type 1 font")
+	# XXX what else?
+	return data
+
+
+# pfb helpers
+
+def longToString(long):
+	s = b""
+	for i in range(4):
+		s += bytechr((long & (0xff << (i * 8))) >> i * 8)
+	return s
+
+def stringToLong(s):
+	if len(s) != 4:
+		raise ValueError('string must be 4 bytes long')
+	l = 0
+	for i in range(4):
+		l += byteord(s[i]) << (i * 8)
+	return l
diff --git a/Lib/fontTools/ttLib/__init__.py b/Lib/fontTools/ttLib/__init__.py
index 546b327..a3ab303 100644
--- a/Lib/fontTools/ttLib/__init__.py
+++ b/Lib/fontTools/ttLib/__init__.py
@@ -1,6 +1,6 @@
 """fontTools.ttLib -- a package for dealing with TrueType fonts.
 
-This package offers translators to convert TrueType fonts to Python 
+This package offers translators to convert TrueType fonts to Python
 objects and vice versa, and additionally from Python to TTX (an XML-based
 text format) and vice versa.
 
@@ -8,15 +8,15 @@
 
 Python 1.5.2c1 (#43, Mar  9 1999, 13:06:43)  [CW PPC w/GUSI w/MSL]
 Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
->>> from fontTools import ttLib
->>> tt = ttLib.TTFont("afont.ttf")
->>> tt['maxp'].numGlyphs
+>> from fontTools import ttLib
+>> tt = ttLib.TTFont("afont.ttf")
+>> tt['maxp'].numGlyphs
 242
->>> tt['OS/2'].achVendID
+>> tt['OS/2'].achVendID
 'B&H\000'
->>> tt['head'].unitsPerEm
+>> tt['head'].unitsPerEm
 2048
->>> tt.saveXML("afont.ttx")
+>> tt.saveXML("afont.ttx")
 Dumping 'LTSH' table...
 Dumping 'OS/2' table...
 Dumping 'VDMX' table...
@@ -33,958 +33,28 @@
 Dumping 'name' table...
 Dumping 'post' table...
 Dumping 'prep' table...
->>> tt2 = ttLib.TTFont()
->>> tt2.importXML("afont.ttx")
->>> tt2['maxp'].numGlyphs
+>> tt2 = ttLib.TTFont()
+>> tt2.importXML("afont.ttx")
+>> tt2['maxp'].numGlyphs
 242
->>> 
+>>
 
 """
 
 from __future__ import print_function, division, absolute_import
 from fontTools.misc.py23 import *
-import os
-import sys
+from fontTools.misc.loggingTools import deprecateFunction
+import logging
 
-haveMacSupport = 0
-if sys.platform == "mac":
-	haveMacSupport = 1
-elif sys.platform == "darwin" and sys.version_info[:3] != (2, 2, 0):
-	# Python 2.2's Mac support is broken, so don't enable it there.
-	haveMacSupport = 1
 
+log = logging.getLogger(__name__)
 
 class TTLibError(Exception): pass
 
-
-class TTFont(object):
-	
-	"""The main font object. It manages file input and output, and offers
-	a convenient way of accessing tables. 
-	Tables will be only decompiled when necessary, ie. when they're actually
-	accessed. This means that simple operations can be extremely fast.
-	"""
-	
-	def __init__(self, file=None, res_name_or_index=None,
-			sfntVersion="\000\001\000\000", flavor=None, checkChecksums=False,
-			verbose=False, recalcBBoxes=True, allowVID=False, ignoreDecompileErrors=False,
-			recalcTimestamp=True, fontNumber=-1, lazy=False, quiet=False):
-		
-		"""The constructor can be called with a few different arguments.
-		When reading a font from disk, 'file' should be either a pathname
-		pointing to a file, or a readable file object. 
-		
-		It we're running on a Macintosh, 'res_name_or_index' maybe an sfnt 
-		resource name or an sfnt resource index number or zero. The latter 
-		case will cause TTLib to autodetect whether the file is a flat file 
-		or a suitcase. (If it's a suitcase, only the first 'sfnt' resource
-		will be read!)
-		
-		The 'checkChecksums' argument is used to specify how sfnt
-		checksums are treated upon reading a file from disk:
-			0: don't check (default)
-			1: check, print warnings if a wrong checksum is found
-			2: check, raise an exception if a wrong checksum is found.
-		
-		The TTFont constructor can also be called without a 'file' 
-		argument: this is the way to create a new empty font. 
-		In this case you can optionally supply the 'sfntVersion' argument,
-		and a 'flavor' which can be None, or 'woff'.
-		
-		If the recalcBBoxes argument is false, a number of things will *not*
-		be recalculated upon save/compile:
-			1) glyph bounding boxes
-			2) maxp font bounding box
-			3) hhea min/max values
-		(1) is needed for certain kinds of CJK fonts (ask Werner Lemberg ;-).
-		Additionally, upon importing an TTX file, this option cause glyphs
-		to be compiled right away. This should reduce memory consumption 
-		greatly, and therefore should have some impact on the time needed 
-		to parse/compile large fonts.
-
-		If the recalcTimestamp argument is false, the modified timestamp in the
-		'head' table will *not* be recalculated upon save/compile.
-
-		If the allowVID argument is set to true, then virtual GID's are
-		supported. Asking for a glyph ID with a glyph name or GID that is not in
-		the font will return a virtual GID.   This is valid for GSUB and cmap
-		tables. For SING glyphlets, the cmap table is used to specify Unicode
-		values for virtual GI's used in GSUB/GPOS rules. If the gid N is requested
-		and does not exist in the font, or the glyphname has the form glyphN
-		and does not exist in the font, then N is used as the virtual GID.
-		Else, the first virtual GID is assigned as 0x1000 -1; for subsequent new
-		virtual GIDs, the next is one less than the previous.
-
-		If ignoreDecompileErrors is set to True, exceptions raised in
-		individual tables during decompilation will be ignored, falling
-		back to the DefaultTable implementation, which simply keeps the
-		binary data.
-
-		If lazy is set to True, many data structures are loaded lazily, upon
-		access only.
-		"""
-		
-		from fontTools.ttLib import sfnt
-		self.verbose = verbose
-		self.quiet = quiet
-		self.lazy = lazy
-		self.recalcBBoxes = recalcBBoxes
-		self.recalcTimestamp = recalcTimestamp
-		self.tables = {}
-		self.reader = None
-
-		# Permit the user to reference glyphs that are not int the font.
-		self.last_vid = 0xFFFE # Can't make it be 0xFFFF, as the world is full unsigned short integer counters that get incremented after the last seen GID value.
-		self.reverseVIDDict = {}
-		self.VIDDict = {}
-		self.allowVID = allowVID
-		self.ignoreDecompileErrors = ignoreDecompileErrors
-
-		if not file:
-			self.sfntVersion = sfntVersion
-			self.flavor = flavor
-			self.flavorData = None
-			return
-		if not hasattr(file, "read"):
-			# assume file is a string
-			if haveMacSupport and res_name_or_index is not None:
-				# on the mac, we deal with sfnt resources as well as flat files
-				from . import macUtils
-				if res_name_or_index == 0:
-					if macUtils.getSFNTResIndices(file):
-						# get the first available sfnt font.
-						file = macUtils.SFNTResourceReader(file, 1)
-					else:
-						file = open(file, "rb")
-				else:
-					file = macUtils.SFNTResourceReader(file, res_name_or_index)
-			else:
-				file = open(file, "rb")
-		else:
-			pass # assume "file" is a readable file object
-		self.reader = sfnt.SFNTReader(file, checkChecksums, fontNumber=fontNumber)
-		self.sfntVersion = self.reader.sfntVersion
-		self.flavor = self.reader.flavor
-		self.flavorData = self.reader.flavorData
-	
-	def close(self):
-		"""If we still have a reader object, close it."""
-		if self.reader is not None:
-			self.reader.close()
-	
-	def save(self, file, makeSuitcase=False, reorderTables=True):
-		"""Save the font to disk. Similarly to the constructor, 
-		the 'file' argument can be either a pathname or a writable
-		file object.
-		
-		On the Mac, if makeSuitcase is true, a suitcase (resource fork)
-		file will we made instead of a flat .ttf file. 
-		"""
-		from fontTools.ttLib import sfnt
-		if not hasattr(file, "write"):
-			closeStream = 1
-			if os.name == "mac" and makeSuitcase:
-				from . import macUtils
-				file = macUtils.SFNTResourceWriter(file, self)
-			else:
-				file = open(file, "wb")
-				if os.name == "mac":
-					from fontTools.misc.macCreator import setMacCreatorAndType
-					setMacCreatorAndType(file.name, 'mdos', 'BINA')
-		else:
-			# assume "file" is a writable file object
-			closeStream = 0
-		
-		tags = list(self.keys())
-		if "GlyphOrder" in tags:
-			tags.remove("GlyphOrder")
-		numTables = len(tags)
-		if reorderTables:
-			import tempfile
-			tmp = tempfile.TemporaryFile(prefix="ttx-fonttools")
-		else:
-			tmp = file
-		writer = sfnt.SFNTWriter(tmp, numTables, self.sfntVersion, self.flavor, self.flavorData)
-		
-		done = []
-		for tag in tags:
-			self._writeTable(tag, writer, done)
-		
-		writer.close()
-
-		if reorderTables:
-			tmp.flush()
-			tmp.seek(0)
-			reorderFontTables(tmp, file)
-			tmp.close()
-
-		if closeStream:
-			file.close()
-	
-	def saveXML(self, fileOrPath, progress=None, quiet=False,
-			tables=None, skipTables=None, splitTables=False, disassembleInstructions=True,
-			bitmapGlyphDataFormat='raw'):
-		"""Export the font as TTX (an XML-based text file), or as a series of text
-		files when splitTables is true. In the latter case, the 'fileOrPath'
-		argument should be a path to a directory.
-		The 'tables' argument must either be false (dump all tables) or a
-		list of tables to dump. The 'skipTables' argument may be a list of tables
-		to skip, but only when the 'tables' argument is false.
-		"""
-		from fontTools import version
-		from fontTools.misc import xmlWriter
-		
-		self.disassembleInstructions = disassembleInstructions
-		self.bitmapGlyphDataFormat = bitmapGlyphDataFormat
-		if not tables:
-			tables = list(self.keys())
-			if "GlyphOrder" not in tables:
-				tables = ["GlyphOrder"] + tables
-			if skipTables:
-				for tag in skipTables:
-					if tag in tables:
-						tables.remove(tag)
-		numTables = len(tables)
-		if progress:
-			progress.set(0, numTables)
-			idlefunc = getattr(progress, "idle", None)
-		else:
-			idlefunc = None
-		
-		writer = xmlWriter.XMLWriter(fileOrPath, idlefunc=idlefunc)
-		writer.begintag("ttFont", sfntVersion=repr(self.sfntVersion)[1:-1], 
-				ttLibVersion=version)
-		writer.newline()
-		
-		if not splitTables:
-			writer.newline()
-		else:
-			# 'fileOrPath' must now be a path
-			path, ext = os.path.splitext(fileOrPath)
-			fileNameTemplate = path + ".%s" + ext
-		
-		for i in range(numTables):
-			if progress:
-				progress.set(i)
-			tag = tables[i]
-			if splitTables:
-				tablePath = fileNameTemplate % tagToIdentifier(tag)
-				tableWriter = xmlWriter.XMLWriter(tablePath, idlefunc=idlefunc)
-				tableWriter.begintag("ttFont", ttLibVersion=version)
-				tableWriter.newline()
-				tableWriter.newline()
-				writer.simpletag(tagToXML(tag), src=os.path.basename(tablePath))
-				writer.newline()
-			else:
-				tableWriter = writer
-			self._tableToXML(tableWriter, tag, progress, quiet)
-			if splitTables:
-				tableWriter.endtag("ttFont")
-				tableWriter.newline()
-				tableWriter.close()
-		if progress:
-			progress.set((i + 1))
-		writer.endtag("ttFont")
-		writer.newline()
-		writer.close()
-		if self.verbose:
-			debugmsg("Done dumping TTX")
-	
-	def _tableToXML(self, writer, tag, progress, quiet):
-		if tag in self:
-			table = self[tag]
-			report = "Dumping '%s' table..." % tag
-		else:
-			report = "No '%s' table found." % tag
-		if progress:
-			progress.setLabel(report)
-		elif self.verbose:
-			debugmsg(report)
-		else:
-			if not quiet:
-				print(report)
-		if tag not in self:
-			return
-		xmlTag = tagToXML(tag)
-		if hasattr(table, "ERROR"):
-			writer.begintag(xmlTag, ERROR="decompilation error")
-		else:
-			writer.begintag(xmlTag)
-		writer.newline()
-		if tag in ("glyf", "CFF "):
-			table.toXML(writer, self, progress)
-		else:
-			table.toXML(writer, self)
-		writer.endtag(xmlTag)
-		writer.newline()
-		writer.newline()
-	
-	def importXML(self, file, progress=None, quiet=False):
-		"""Import a TTX file (an XML-based text format), so as to recreate
-		a font object.
-		"""
-		if "maxp" in self and "post" in self:
-			# Make sure the glyph order is loaded, as it otherwise gets
-			# lost if the XML doesn't contain the glyph order, yet does
-			# contain the table which was originally used to extract the
-			# glyph names from (ie. 'post', 'cmap' or 'CFF ').
-			self.getGlyphOrder()
-
-		from fontTools.misc import xmlReader
-
-		reader = xmlReader.XMLReader(file, self, progress, quiet)
-		reader.read()
-	
-	def isLoaded(self, tag):
-		"""Return true if the table identified by 'tag' has been 
-		decompiled and loaded into memory."""
-		return tag in self.tables
-	
-	def has_key(self, tag):
-		if self.isLoaded(tag):
-			return True
-		elif self.reader and tag in self.reader:
-			return True
-		elif tag == "GlyphOrder":
-			return True
-		else:
-			return False
-	
-	__contains__ = has_key
-	
-	def keys(self):
-		keys = list(self.tables.keys())
-		if self.reader:
-			for key in list(self.reader.keys()):
-				if key not in keys:
-					keys.append(key)
-
-		if "GlyphOrder" in keys:
-			keys.remove("GlyphOrder")
-		keys = sortedTagList(keys)
-		return ["GlyphOrder"] + keys
-	
-	def __len__(self):
-		return len(list(self.keys()))
-	
-	def __getitem__(self, tag):
-		tag = Tag(tag)
-		try:
-			return self.tables[tag]
-		except KeyError:
-			if tag == "GlyphOrder":
-				table = GlyphOrder(tag)
-				self.tables[tag] = table
-				return table
-			if self.reader is not None:
-				import traceback
-				if self.verbose:
-					debugmsg("Reading '%s' table from disk" % tag)
-				data = self.reader[tag]
-				tableClass = getTableClass(tag)
-				table = tableClass(tag)
-				self.tables[tag] = table
-				if self.verbose:
-					debugmsg("Decompiling '%s' table" % tag)
-				try:
-					table.decompile(data, self)
-				except:
-					if not self.ignoreDecompileErrors:
-						raise
-					# fall back to DefaultTable, retaining the binary table data
-					print("An exception occurred during the decompilation of the '%s' table" % tag)
-					from .tables.DefaultTable import DefaultTable
-					file = StringIO()
-					traceback.print_exc(file=file)
-					table = DefaultTable(tag)
-					table.ERROR = file.getvalue()
-					self.tables[tag] = table
-					table.decompile(data, self)
-				return table
-			else:
-				raise KeyError("'%s' table not found" % tag)
-	
-	def __setitem__(self, tag, table):
-		self.tables[Tag(tag)] = table
-	
-	def __delitem__(self, tag):
-		if tag not in self:
-			raise KeyError("'%s' table not found" % tag)
-		if tag in self.tables:
-			del self.tables[tag]
-		if self.reader and tag in self.reader:
-			del self.reader[tag]
-
-	def get(self, tag, default=None):
-		try:
-			return self[tag]
-		except KeyError:
-			return default
-	
-	def setGlyphOrder(self, glyphOrder):
-		self.glyphOrder = glyphOrder
-	
-	def getGlyphOrder(self):
-		try:
-			return self.glyphOrder
-		except AttributeError:
-			pass
-		if 'CFF ' in self:
-			cff = self['CFF ']
-			self.glyphOrder = cff.getGlyphOrder()
-		elif 'post' in self:
-			# TrueType font
-			glyphOrder = self['post'].getGlyphOrder()
-			if glyphOrder is None:
-				#
-				# No names found in the 'post' table.
-				# Try to create glyph names from the unicode cmap (if available) 
-				# in combination with the Adobe Glyph List (AGL).
-				#
-				self._getGlyphNamesFromCmap()
-			else:
-				self.glyphOrder = glyphOrder
-		else:
-			self._getGlyphNamesFromCmap()
-		return self.glyphOrder
-	
-	def _getGlyphNamesFromCmap(self):
-		#
-		# This is rather convoluted, but then again, it's an interesting problem:
-		# - we need to use the unicode values found in the cmap table to
-		#   build glyph names (eg. because there is only a minimal post table,
-		#   or none at all).
-		# - but the cmap parser also needs glyph names to work with...
-		# So here's what we do:
-		# - make up glyph names based on glyphID
-		# - load a temporary cmap table based on those names
-		# - extract the unicode values, build the "real" glyph names
-		# - unload the temporary cmap table
-		#
-		if self.isLoaded("cmap"):
-			# Bootstrapping: we're getting called by the cmap parser
-			# itself. This means self.tables['cmap'] contains a partially
-			# loaded cmap, making it impossible to get at a unicode
-			# subtable here. We remove the partially loaded cmap and
-			# restore it later.
-			# This only happens if the cmap table is loaded before any
-			# other table that does f.getGlyphOrder()  or f.getGlyphName().
-			cmapLoading = self.tables['cmap']
-			del self.tables['cmap']
-		else:
-			cmapLoading = None
-		# Make up glyph names based on glyphID, which will be used by the
-		# temporary cmap and by the real cmap in case we don't find a unicode
-		# cmap.
-		numGlyphs = int(self['maxp'].numGlyphs)
-		glyphOrder = [None] * numGlyphs
-		glyphOrder[0] = ".notdef"
-		for i in range(1, numGlyphs):
-			glyphOrder[i] = "glyph%.5d" % i
-		# Set the glyph order, so the cmap parser has something
-		# to work with (so we don't get called recursively).
-		self.glyphOrder = glyphOrder
-		# Get a (new) temporary cmap (based on the just invented names)
-		tempcmap = self['cmap'].getcmap(3, 1)
-		if tempcmap is not None:
-			# we have a unicode cmap
-			from fontTools import agl
-			cmap = tempcmap.cmap
-			# create a reverse cmap dict
-			reversecmap = {}
-			for unicode, name in list(cmap.items()):
-				reversecmap[name] = unicode
-			allNames = {}
-			for i in range(numGlyphs):
-				tempName = glyphOrder[i]
-				if tempName in reversecmap:
-					unicode = reversecmap[tempName]
-					if unicode in agl.UV2AGL:
-						# get name from the Adobe Glyph List
-						glyphName = agl.UV2AGL[unicode]
-					else:
-						# create uni<CODE> name
-						glyphName = "uni%04X" % unicode
-					tempName = glyphName
-					n = 1
-					while tempName in allNames:
-						tempName = glyphName + "#" + repr(n)
-						n = n + 1
-					glyphOrder[i] = tempName
-					allNames[tempName] = 1
-			# Delete the temporary cmap table from the cache, so it can
-			# be parsed again with the right names.
-			del self.tables['cmap']
-		else:
-			pass # no unicode cmap available, stick with the invented names
-		self.glyphOrder = glyphOrder
-		if cmapLoading:
-			# restore partially loaded cmap, so it can continue loading
-			# using the proper names.
-			self.tables['cmap'] = cmapLoading
-	
-	def getGlyphNames(self):
-		"""Get a list of glyph names, sorted alphabetically."""
-		glyphNames = sorted(self.getGlyphOrder()[:])
-		return glyphNames
-	
-	def getGlyphNames2(self):
-		"""Get a list of glyph names, sorted alphabetically, 
-		but not case sensitive.
-		"""
-		from fontTools.misc import textTools
-		return textTools.caselessSort(self.getGlyphOrder())
-	
-	def getGlyphName(self, glyphID, requireReal=False):
-		try:
-			return self.getGlyphOrder()[glyphID]
-		except IndexError:
-			if requireReal or not self.allowVID:
-				# XXX The ??.W8.otf font that ships with OSX uses higher glyphIDs in
-				# the cmap table than there are glyphs. I don't think it's legal...
-				return "glyph%.5d" % glyphID
-			else:
-				# user intends virtual GID support 	
-				try:
-					glyphName = self.VIDDict[glyphID]
-				except KeyError:
-					glyphName  ="glyph%.5d" % glyphID
-					self.last_vid = min(glyphID, self.last_vid )
-					self.reverseVIDDict[glyphName] = glyphID
-					self.VIDDict[glyphID] = glyphName
-				return glyphName
-
-	def getGlyphID(self, glyphName, requireReal=False):
-		if not hasattr(self, "_reverseGlyphOrderDict"):
-			self._buildReverseGlyphOrderDict()
-		glyphOrder = self.getGlyphOrder()
-		d = self._reverseGlyphOrderDict
-		if glyphName not in d:
-			if glyphName in glyphOrder:
-				self._buildReverseGlyphOrderDict()
-				return self.getGlyphID(glyphName)
-			else:
-				if requireReal:
-					raise KeyError(glyphName)
-				elif not self.allowVID:
-					# Handle glyphXXX only
-					if glyphName[:5] == "glyph":
-						try:
-							return int(glyphName[5:])
-						except (NameError, ValueError):
-							raise KeyError(glyphName)
-				else:
-					# user intends virtual GID support 	
-					try:
-						glyphID = self.reverseVIDDict[glyphName]
-					except KeyError:
-						# if name is in glyphXXX format, use the specified name.
-						if glyphName[:5] == "glyph":
-							try:
-								glyphID = int(glyphName[5:])
-							except (NameError, ValueError):
-								glyphID = None
-						if glyphID is None:
-							glyphID = self.last_vid -1
-							self.last_vid = glyphID
-						self.reverseVIDDict[glyphName] = glyphID
-						self.VIDDict[glyphID] = glyphName
-					return glyphID
-
-		glyphID = d[glyphName]
-		if glyphName != glyphOrder[glyphID]:
-			self._buildReverseGlyphOrderDict()
-			return self.getGlyphID(glyphName)
-		return glyphID
-
-	def getReverseGlyphMap(self, rebuild=False):
-		if rebuild or not hasattr(self, "_reverseGlyphOrderDict"):
-			self._buildReverseGlyphOrderDict()
-		return self._reverseGlyphOrderDict
-
-	def _buildReverseGlyphOrderDict(self):
-		self._reverseGlyphOrderDict = d = {}
-		glyphOrder = self.getGlyphOrder()
-		for glyphID in range(len(glyphOrder)):
-			d[glyphOrder[glyphID]] = glyphID
-	
-	def _writeTable(self, tag, writer, done):
-		"""Internal helper function for self.save(). Keeps track of 
-		inter-table dependencies.
-		"""
-		if tag in done:
-			return
-		tableClass = getTableClass(tag)
-		for masterTable in tableClass.dependencies:
-			if masterTable not in done:
-				if masterTable in self:
-					self._writeTable(masterTable, writer, done)
-				else:
-					done.append(masterTable)
-		tabledata = self.getTableData(tag)
-		if self.verbose:
-			debugmsg("writing '%s' table to disk" % tag)
-		writer[tag] = tabledata
-		done.append(tag)
-	
-	def getTableData(self, tag):
-		"""Returns raw table data, whether compiled or directly read from disk.
-		"""
-		tag = Tag(tag)
-		if self.isLoaded(tag):
-			if self.verbose:
-				debugmsg("compiling '%s' table" % tag)
-			return self.tables[tag].compile(self)
-		elif self.reader and tag in self.reader:
-			if self.verbose:
-				debugmsg("Reading '%s' table from disk" % tag)
-			return self.reader[tag]
-		else:
-			raise KeyError(tag)
-	
-	def getGlyphSet(self, preferCFF=True):
-		"""Return a generic GlyphSet, which is a dict-like object
-		mapping glyph names to glyph objects. The returned glyph objects
-		have a .draw() method that supports the Pen protocol, and will
-		have an attribute named 'width', but only *after* the .draw() method
-		has been called.
-		
-		If the font is CFF-based, the outlines will be taken from the 'CFF '
-		table. Otherwise the outlines will be taken from the 'glyf' table.
-		If the font contains both a 'CFF ' and a 'glyf' table, you can use
-		the 'preferCFF' argument to specify which one should be taken.
-		"""
-		if preferCFF and "CFF " in self:
-			return list(self["CFF "].cff.values())[0].CharStrings
-		if "glyf" in self:
-			return _TTGlyphSet(self)
-		if "CFF " in self:
-			return list(self["CFF "].cff.values())[0].CharStrings
-		raise TTLibError("Font contains no outlines")
-
-
-class _TTGlyphSet(object):
-	
-	"""Generic dict-like GlyphSet class, meant as a TrueType counterpart
-	to CFF's CharString dict. See TTFont.getGlyphSet().
-	"""
-	
-	# This class is distinct from the 'glyf' table itself because we need
-	# access to the 'hmtx' table, which could cause a dependency problem
-	# there when reading from XML.
-	
-	def __init__(self, ttFont):
-		self._ttFont = ttFont
-	
-	def keys(self):
-		return list(self._ttFont["glyf"].keys())
-	
-	def has_key(self, glyphName):
-		return glyphName in self._ttFont["glyf"]
-	
-	__contains__ = has_key
-
-	def __getitem__(self, glyphName):
-		return _TTGlyph(glyphName, self._ttFont)
-
-	def get(self, glyphName, default=None):
-		try:
-			return self[glyphName]
-		except KeyError:
-			return default
-
-
-class _TTGlyph(object):
-	
-	"""Wrapper for a TrueType glyph that supports the Pen protocol, meaning
-	that it has a .draw() method that takes a pen object as its only
-	argument. Additionally there is a 'width' attribute.
-	"""
-	
-	def __init__(self, glyphName, ttFont):
-		self._glyphName = glyphName
-		self._ttFont = ttFont
-		self.width, self.lsb = self._ttFont['hmtx'][self._glyphName]
-	
-	def draw(self, pen):
-		"""Draw the glyph onto Pen. See fontTools.pens.basePen for details
-		how that works.
-		"""
-		glyfTable = self._ttFont['glyf']
-		glyph = glyfTable[self._glyphName]
-		if hasattr(glyph, "xMin"):
-			offset = self.lsb - glyph.xMin
-		else:
-			offset = 0
-		if glyph.isComposite():
-			for component in glyph:
-				glyphName, transform = component.getComponentInfo()
-				pen.addComponent(glyphName, transform)
-		else:
-			coordinates, endPts, flags = glyph.getCoordinates(glyfTable)
-			if offset:
-				coordinates = coordinates + (offset, 0)
-			start = 0
-			for end in endPts:
-				end = end + 1
-				contour = coordinates[start:end].tolist()
-				cFlags = flags[start:end].tolist()
-				start = end
-				if 1 not in cFlags:
-					# There is not a single on-curve point on the curve,
-					# use pen.qCurveTo's special case by specifying None
-					# as the on-curve point.
-					contour.append(None)
-					pen.qCurveTo(*contour)
-				else:
-					# Shuffle the points so that contour the is guaranteed
-					# to *end* in an on-curve point, which we'll use for
-					# the moveTo.
-					firstOnCurve = cFlags.index(1) + 1
-					contour = contour[firstOnCurve:] + contour[:firstOnCurve]
-					cFlags = cFlags[firstOnCurve:] + cFlags[:firstOnCurve]
-					pen.moveTo(contour[-1])
-					while contour:
-						nextOnCurve = cFlags.index(1) + 1
-						if nextOnCurve == 1:
-							pen.lineTo(contour[0])
-						else:
-							pen.qCurveTo(*contour[:nextOnCurve])
-						contour = contour[nextOnCurve:]
-						cFlags = cFlags[nextOnCurve:]
-				pen.closePath()
-
-
-class GlyphOrder(object):
-	
-	"""A pseudo table. The glyph order isn't in the font as a separate
-	table, but it's nice to present it as such in the TTX format.
-	"""
-	
-	def __init__(self, tag=None):
-		pass
-	
-	def toXML(self, writer, ttFont):
-		glyphOrder = ttFont.getGlyphOrder()
-		writer.comment("The 'id' attribute is only for humans; "
-				"it is ignored when parsed.")
-		writer.newline()
-		for i in range(len(glyphOrder)):
-			glyphName = glyphOrder[i]
-			writer.simpletag("GlyphID", id=i, name=glyphName)
-			writer.newline()
-	
-	def fromXML(self, name, attrs, content, ttFont):
-		if not hasattr(self, "glyphOrder"):
-			self.glyphOrder = []
-			ttFont.setGlyphOrder(self.glyphOrder)
-		if name == "GlyphID":
-			self.glyphOrder.append(attrs["name"])
-
-
-def getTableModule(tag):
-	"""Fetch the packer/unpacker module for a table. 
-	Return None when no module is found.
-	"""
-	from . import tables
-	pyTag = tagToIdentifier(tag)
-	try:
-		__import__("fontTools.ttLib.tables." + pyTag)
-	except ImportError as err:
-		# If pyTag is found in the ImportError message,
-		# means table is not implemented.  If it's not
-		# there, then some other module is missing, don't
-		# suppress the error.
-		if str(err).find(pyTag) >= 0:
-			return None
-		else:
-			raise err
-	else:
-		return getattr(tables, pyTag)
-
-
-def getTableClass(tag):
-	"""Fetch the packer/unpacker class for a table. 
-	Return None when no class is found.
-	"""
-	module = getTableModule(tag)
-	if module is None:
-		from .tables.DefaultTable import DefaultTable
-		return DefaultTable
-	pyTag = tagToIdentifier(tag)
-	tableClass = getattr(module, "table_" + pyTag)
-	return tableClass
-
-
-def getClassTag(klass):
-	"""Fetch the table tag for a class object."""
-	name = klass.__name__
-	assert name[:6] == 'table_'
-	name = name[6:] # Chop 'table_'
-	return identifierToTag(name)
-
-
-
-def newTable(tag):
-	"""Return a new instance of a table."""
-	tableClass = getTableClass(tag)
-	return tableClass(tag)
-
-
-def _escapechar(c):
-	"""Helper function for tagToIdentifier()"""
-	import re
-	if re.match("[a-z0-9]", c):
-		return "_" + c
-	elif re.match("[A-Z]", c):
-		return c + "_"
-	else:
-		return hex(byteord(c))[2:]
-
-
-def tagToIdentifier(tag):
-	"""Convert a table tag to a valid (but UGLY) python identifier, 
-	as well as a filename that's guaranteed to be unique even on a 
-	caseless file system. Each character is mapped to two characters.
-	Lowercase letters get an underscore before the letter, uppercase
-	letters get an underscore after the letter. Trailing spaces are
-	trimmed. Illegal characters are escaped as two hex bytes. If the
-	result starts with a number (as the result of a hex escape), an
-	extra underscore is prepended. Examples: 
-		'glyf' -> '_g_l_y_f'
-		'cvt ' -> '_c_v_t'
-		'OS/2' -> 'O_S_2f_2'
-	"""
-	import re
-	tag = Tag(tag)
-	if tag == "GlyphOrder":
-		return tag
-	assert len(tag) == 4, "tag should be 4 characters long"
-	while len(tag) > 1 and tag[-1] == ' ':
-		tag = tag[:-1]
-	ident = ""
-	for c in tag:
-		ident = ident + _escapechar(c)
-	if re.match("[0-9]", ident):
-		ident = "_" + ident
-	return ident
-
-
-def identifierToTag(ident):
-	"""the opposite of tagToIdentifier()"""
-	if ident == "GlyphOrder":
-		return ident
-	if len(ident) % 2 and ident[0] == "_":
-		ident = ident[1:]
-	assert not (len(ident) % 2)
-	tag = ""
-	for i in range(0, len(ident), 2):
-		if ident[i] == "_":
-			tag = tag + ident[i+1]
-		elif ident[i+1] == "_":
-			tag = tag + ident[i]
-		else:
-			# assume hex
-			tag = tag + chr(int(ident[i:i+2], 16))
-	# append trailing spaces
-	tag = tag + (4 - len(tag)) * ' '
-	return Tag(tag)
-
-
-def tagToXML(tag):
-	"""Similarly to tagToIdentifier(), this converts a TT tag
-	to a valid XML element name. Since XML element names are
-	case sensitive, this is a fairly simple/readable translation.
-	"""
-	import re
-	tag = Tag(tag)
-	if tag == "OS/2":
-		return "OS_2"
-	elif tag == "GlyphOrder":
-		return tag
-	if re.match("[A-Za-z_][A-Za-z_0-9]* *$", tag):
-		return tag.strip()
-	else:
-		return tagToIdentifier(tag)
-
-
-def xmlToTag(tag):
-	"""The opposite of tagToXML()"""
-	if tag == "OS_2":
-		return Tag("OS/2")
-	if len(tag) == 8:
-		return identifierToTag(tag)
-	else:
-		return Tag(tag + " " * (4 - len(tag)))
-
-
+@deprecateFunction("use logging instead", category=DeprecationWarning)
 def debugmsg(msg):
 	import time
 	print(msg + time.strftime("  (%H:%M:%S)", time.localtime(time.time())))
 
-
-# Table order as recommended in the OpenType specification 1.4
-TTFTableOrder = ["head", "hhea", "maxp", "OS/2", "hmtx", "LTSH", "VDMX",
-                  "hdmx", "cmap", "fpgm", "prep", "cvt ", "loca", "glyf",
-                  "kern", "name", "post", "gasp", "PCLT"]
-
-OTFTableOrder = ["head", "hhea", "maxp", "OS/2", "name", "cmap", "post",
-                  "CFF "]
-
-def sortedTagList(tagList, tableOrder=None):
-	"""Return a sorted copy of tagList, sorted according to the OpenType
-	specification, or according to a custom tableOrder. If given and not
-	None, tableOrder needs to be a list of tag names.
-	"""
-	tagList = sorted(tagList)
-	if tableOrder is None:
-		if "DSIG" in tagList:
-			# DSIG should be last (XXX spec reference?)
-			tagList.remove("DSIG")
-			tagList.append("DSIG")
-		if "CFF " in tagList:
-			tableOrder = OTFTableOrder
-		else:
-			tableOrder = TTFTableOrder
-	orderedTables = []
-	for tag in tableOrder:
-		if tag in tagList:
-			orderedTables.append(tag)
-			tagList.remove(tag)
-	orderedTables.extend(tagList)
-	return orderedTables
-
-
-def reorderFontTables(inFile, outFile, tableOrder=None, checkChecksums=False):
-	"""Rewrite a font file, ordering the tables as recommended by the
-	OpenType specification 1.4.
-	"""
-	from fontTools.ttLib.sfnt import SFNTReader, SFNTWriter
-	reader = SFNTReader(inFile, checkChecksums=checkChecksums)
-	writer = SFNTWriter(outFile, len(reader.tables), reader.sfntVersion, reader.flavor, reader.flavorData)
-	tables = list(reader.keys())
-	for tag in sortedTagList(tables, tableOrder):
-		writer[tag] = reader[tag]
-	writer.close()
-
-
-def maxPowerOfTwo(x):
-	"""Return the highest exponent of two, so that
-	(2 ** exponent) <= x.  Return 0 if x is 0.
-	"""
-	exponent = 0
-	while x:
-		x = x >> 1
-		exponent = exponent + 1
-	return max(exponent - 1, 0)
-
-
-def getSearchRange(n, itemSize):
-	"""Calculate searchRange, entrySelector, rangeShift.
-	"""
-	# This stuff needs to be stored in the file, because?
-	exponent = maxPowerOfTwo(n)
-	searchRange = (2 ** exponent) * itemSize
-	entrySelector = exponent
-	rangeShift = max(0, n * itemSize - searchRange)
-	return searchRange, entrySelector, rangeShift
+from fontTools.ttLib.ttFont import *
+from fontTools.ttLib.ttCollection import TTCollection
diff --git a/Lib/fontTools/ttLib/macUtils.py b/Lib/fontTools/ttLib/macUtils.py
index d565528..17dd5ef 100644
--- a/Lib/fontTools/ttLib/macUtils.py
+++ b/Lib/fontTools/ttLib/macUtils.py
@@ -1,42 +1,22 @@
 """ttLib.macUtils.py -- Various Mac-specific stuff."""
-
 from __future__ import print_function, division, absolute_import
 from fontTools.misc.py23 import *
-import sys
-import os
-if sys.platform not in ("mac", "darwin"):
-	raise ImportError("This module is Mac-only!")
-try:
-	from Carbon import Res
-except ImportError:
-	import Res
-
-
-
-def MyOpenResFile(path):
-	mode = 1  # read only
-	try:
-		resref = Res.FSOpenResFile(path, mode)
-	except Res.Error:
-		# try data fork
-		resref = Res.FSOpenResourceFile(path, unicode(), mode)
-	return resref
+from fontTools.misc.macRes import ResourceReader, ResourceError
 
 
 def getSFNTResIndices(path):
-	"""Determine whether a file has a resource fork or not."""
+	"""Determine whether a file has a 'sfnt' resource fork or not."""
 	try:
-		resref = MyOpenResFile(path)
-	except Res.Error:
+		reader = ResourceReader(path)
+		indices = reader.getIndices('sfnt')
+		reader.close()
+		return indices
+	except ResourceError:
 		return []
-	Res.UseResFile(resref)
-	numSFNTs = Res.Count1Resources('sfnt')
-	Res.CloseResFile(resref)
-	return list(range(1, numSFNTs + 1))
 
 
 def openTTFonts(path):
-	"""Given a pathname, return a list of TTFont objects. In the case 
+	"""Given a pathname, return a list of TTFont objects. In the case
 	of a flat TTF/OTF file, the list will contain just one font object;
 	but in the case of a Mac font suitcase it will contain as many
 	font objects as there are sfnt resources in the file.
@@ -54,149 +34,20 @@
 	return fonts
 
 
-class SFNTResourceReader(object):
-	
-	"""Simple (Mac-only) read-only file wrapper for 'sfnt' resources."""
-	
+class SFNTResourceReader(BytesIO):
+
+	"""Simple read-only file wrapper for 'sfnt' resources."""
+
 	def __init__(self, path, res_name_or_index):
-		resref = MyOpenResFile(path)
-		Res.UseResFile(resref)
+		from fontTools import ttLib
+		reader = ResourceReader(path)
 		if isinstance(res_name_or_index, basestring):
-			res = Res.Get1NamedResource('sfnt', res_name_or_index)
+			rsrc = reader.getNamedResource('sfnt', res_name_or_index)
 		else:
-			res = Res.Get1IndResource('sfnt', res_name_or_index)
-		self.file = StringIO(res.data)
-		Res.CloseResFile(resref)
+			rsrc = reader.getIndResource('sfnt', res_name_or_index)
+		if rsrc is None:
+			raise ttLib.TTLibError("sfnt resource not found: %s" % res_name_or_index)
+		reader.close()
+		self.rsrc = rsrc
+		super(SFNTResourceReader, self).__init__(rsrc.data)
 		self.name = path
-	
-	def __getattr__(self, attr):
-		# cheap inheritance
-		return getattr(self.file, attr)
-
-
-class SFNTResourceWriter(object):
-	
-	"""Simple (Mac-only) file wrapper for 'sfnt' resources."""
-	
-	def __init__(self, path, ttFont, res_id=None):
-		self.file = StringIO()
-		self.name = path
-		self.closed = 0
-		fullname = ttFont['name'].getName(4, 1, 0) # Full name, mac, default encoding
-		familyname = ttFont['name'].getName(1, 1, 0) # Fam. name, mac, default encoding
-		psname = ttFont['name'].getName(6, 1, 0) # PostScript name, etc.
-		if fullname is None or fullname is None or psname is None:
-			from fontTools import ttLib
-			raise ttLib.TTLibError("can't make 'sfnt' resource, no Macintosh 'name' table found")
-		self.fullname = fullname.string
-		self.familyname = familyname.string
-		self.psname = psname.string
-		if self.familyname != self.psname[:len(self.familyname)]:
-			# ugh. force fam name to be the same as first part of ps name,
-			# fondLib otherwise barfs.
-			for i in range(min(len(self.psname), len(self.familyname))):
-				if self.familyname[i] != self.psname[i]:
-					break
-			self.familyname = self.psname[:i]
-		
-		self.ttFont = ttFont
-		self.res_id = res_id
-		if os.path.exists(self.name):
-			os.remove(self.name)
-		# XXX datafork support
-		Res.FSpCreateResFile(self.name, 'DMOV', 'FFIL', 0)
-		self.resref = Res.FSOpenResFile(self.name, 3)  # exclusive read/write permission
-	
-	def close(self):
-		if self.closed:
-			return
-		Res.UseResFile(self.resref)
-		try:
-			res = Res.Get1NamedResource('sfnt', self.fullname)
-		except Res.Error:
-			pass
-		else:
-			res.RemoveResource()
-		res = Res.Resource(self.file.getvalue())
-		if self.res_id is None:
-			self.res_id = Res.Unique1ID('sfnt')
-		res.AddResource('sfnt', self.res_id, self.fullname)
-		res.ChangedResource()
-		
-		self.createFond()
-		del self.ttFont
-		Res.CloseResFile(self.resref)
-		self.file.close()
-		self.closed = 1
-	
-	def createFond(self):
-		fond_res = Res.Resource("")
-		fond_res.AddResource('FOND', self.res_id, self.fullname)
-		
-		from fontTools import fondLib
-		fond = fondLib.FontFamily(fond_res, "w")
-		
-		fond.ffFirstChar = 0
-		fond.ffLastChar = 255
-		fond.fondClass = 0
-		fond.fontAssoc = [(0, 0, self.res_id)]
-		fond.ffFlags = 20480	# XXX ???
-		fond.ffIntl = (0, 0)
-		fond.ffLeading = 0
-		fond.ffProperty = (0, 0, 0, 0, 0, 0, 0, 0, 0)
-		fond.ffVersion = 0
-		fond.glyphEncoding = {}
-		if self.familyname == self.psname:
-			fond.styleIndices = (1,) * 48  # uh-oh, fondLib is too dumb.
-		else:
-			fond.styleIndices = (2,) * 48
-		fond.styleStrings = []
-		fond.boundingBoxes = None
-		fond.ffFamID = self.res_id
-		fond.changed = 1
-		fond.glyphTableOffset = 0
-		fond.styleMappingReserved = 0
-		
-		# calc:
-		scale = 4096 / self.ttFont['head'].unitsPerEm
-		fond.ffAscent = scale * self.ttFont['hhea'].ascent
-		fond.ffDescent = scale * self.ttFont['hhea'].descent
-		fond.ffWidMax = scale * self.ttFont['hhea'].advanceWidthMax
-		
-		fond.ffFamilyName = self.familyname
-		fond.psNames = {0: self.psname}
-		
-		fond.widthTables = {}
-		fond.kernTables = {}
-		cmap = self.ttFont['cmap'].getcmap(1, 0)
-		if cmap:
-			names = {}
-			for code, name in cmap.cmap.items():
-				names[name] = code
-			if 'kern' in self.ttFont:
-				kern = self.ttFont['kern'].getkern(0)
-				if kern:
-					fondkerning = []
-					for (left, right), value in kern.kernTable.items():
-						if left in names and right in names:
-							fondkerning.append((names[left], names[right], scale * value))
-					fondkerning.sort()
-					fond.kernTables = {0: fondkerning}
-			if 'hmtx' in self.ttFont:
-				hmtx = self.ttFont['hmtx']
-				fondwidths = [2048] * 256 + [0, 0]  # default width, + plus two zeros.
-				for name, (width, lsb) in hmtx.metrics.items():
-					if name in names:
-						fondwidths[names[name]] = scale * width
-				fond.widthTables = {0: fondwidths}
-		fond.save()
-	
-	def __del__(self):
-		if not self.closed:
-			self.close()
-	
-	def __getattr__(self, attr):
-		# cheap inheritance
-		return getattr(self.file, attr)
-	
-
diff --git a/Lib/fontTools/ttLib/sfnt.py b/Lib/fontTools/ttLib/sfnt.py
index c65fd28..6dc48ba 100644
--- a/Lib/fontTools/ttLib/sfnt.py
+++ b/Lib/fontTools/ttLib/sfnt.py
@@ -4,10 +4,10 @@
 	SFNTReader
 	SFNTWriter
 
-(Normally you don't have to use these classes explicitly; they are 
+(Normally you don't have to use these classes explicitly; they are
 used automatically by ttLib.TTFont.)
 
-The reading and writing of sfnt files is separated in two distinct 
+The reading and writing of sfnt files is separated in two distinct
 classes, since whenever to number of tables changes or whenever
 a table's length chages you need to rewrite the whole file anyway.
 """
@@ -15,12 +15,33 @@
 from __future__ import print_function, division, absolute_import
 from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
-from fontTools.ttLib import getSearchRange
+from fontTools.ttLib import TTLibError
 import struct
+from collections import OrderedDict
+import logging
+
+
+log = logging.getLogger(__name__)
 
 
 class SFNTReader(object):
-	
+
+	def __new__(cls, *args, **kwargs):
+		""" Return an instance of the SFNTReader sub-class which is compatible
+		with the input file type.
+		"""
+		if args and cls is SFNTReader:
+			infile = args[0]
+			infile.seek(0)
+			sfntVersion = Tag(infile.read(4))
+			infile.seek(0)
+			if sfntVersion == "wOF2":
+				# return new WOFF2Reader object
+				from fontTools.ttLib.woff2 import WOFF2Reader
+				return object.__new__(WOFF2Reader)
+		# return default object
+		return object.__new__(cls)
+
 	def __init__(self, file, checkChecksums=1, fontNumber=-1):
 		self.file = file
 		self.checkChecksums = checkChecksums
@@ -28,35 +49,43 @@
 		self.flavor = None
 		self.flavorData = None
 		self.DirectoryEntry = SFNTDirectoryEntry
+		self.file.seek(0)
 		self.sfntVersion = self.file.read(4)
 		self.file.seek(0)
 		if self.sfntVersion == b"ttcf":
-			sstruct.unpack(ttcHeaderFormat, self.file.read(ttcHeaderSize), self)
-			assert self.Version == 0x00010000 or self.Version == 0x00020000, "unrecognized TTC version 0x%08x" % self.Version
-			if not 0 <= fontNumber < self.numFonts:
-				from fontTools import ttLib
-				raise ttLib.TTLibError("specify a font number between 0 and %d (inclusive)" % (self.numFonts - 1))
-			offsetTable = struct.unpack(">%dL" % self.numFonts, self.file.read(self.numFonts * 4))
-			if self.Version == 0x00020000:
-				pass # ignoring version 2.0 signatures
-			self.file.seek(offsetTable[fontNumber])
-			sstruct.unpack(sfntDirectoryFormat, self.file.read(sfntDirectorySize), self)
+			header = readTTCHeader(self.file)
+			numFonts = header.numFonts
+			if not 0 <= fontNumber < numFonts:
+				raise TTLibError("specify a font number between 0 and %d (inclusive)" % (numFonts - 1))
+			self.numFonts = numFonts
+			self.file.seek(header.offsetTable[fontNumber])
+			data = self.file.read(sfntDirectorySize)
+			if len(data) != sfntDirectorySize:
+				raise TTLibError("Not a Font Collection (not enough data)")
+			sstruct.unpack(sfntDirectoryFormat, data, self)
 		elif self.sfntVersion == b"wOFF":
 			self.flavor = "woff"
 			self.DirectoryEntry = WOFFDirectoryEntry
-			sstruct.unpack(woffDirectoryFormat, self.file.read(woffDirectorySize), self)
+			data = self.file.read(woffDirectorySize)
+			if len(data) != woffDirectorySize:
+				raise TTLibError("Not a WOFF font (not enough data)")
+			sstruct.unpack(woffDirectoryFormat, data, self)
 		else:
-			sstruct.unpack(sfntDirectoryFormat, self.file.read(sfntDirectorySize), self)
+			data = self.file.read(sfntDirectorySize)
+			if len(data) != sfntDirectorySize:
+				raise TTLibError("Not a TrueType or OpenType font (not enough data)")
+			sstruct.unpack(sfntDirectoryFormat, data, self)
 		self.sfntVersion = Tag(self.sfntVersion)
 
 		if self.sfntVersion not in ("\x00\x01\x00\x00", "OTTO", "true"):
-			from fontTools import ttLib
-			raise ttLib.TTLibError("Not a TrueType or OpenType font (bad sfntVersion)")
-		self.tables = {}
+			raise TTLibError("Not a TrueType or OpenType font (bad sfntVersion)")
+		tables = {}
 		for i in range(self.numTables):
 			entry = self.DirectoryEntry()
 			entry.fromFile(self.file)
-			self.tables[Tag(entry.tag)] = entry
+			tag = Tag(entry.tag)
+			tables[tag] = entry
+		self.tables = OrderedDict(sorted(tables.items(), key=lambda i: i[1].offset))
 
 		# Load flavor data if any
 		if self.flavor == "woff":
@@ -66,10 +95,10 @@
 		return tag in self.tables
 
 	__contains__ = has_key
-	
+
 	def keys(self):
 		return self.tables.keys()
-	
+
 	def __getitem__(self, tag):
 		"""Fetch the raw table data."""
 		entry = self.tables[Tag(tag)]
@@ -82,23 +111,80 @@
 				checksum = calcChecksum(data)
 			if self.checkChecksums > 1:
 				# Be obnoxious, and barf when it's wrong
-				assert checksum == entry.checksum, "bad checksum for '%s' table" % tag
+				assert checksum == entry.checkSum, "bad checksum for '%s' table" % tag
 			elif checksum != entry.checkSum:
-				# Be friendly, and just print a warning.
-				print("bad checksum for '%s' table" % tag)
+				# Be friendly, and just log a warning.
+				log.warning("bad checksum for '%s' table", tag)
 		return data
-	
+
 	def __delitem__(self, tag):
 		del self.tables[Tag(tag)]
-	
+
 	def close(self):
 		self.file.close()
 
 
+# default compression level for WOFF 1.0 tables and metadata
+ZLIB_COMPRESSION_LEVEL = 6
+
+# if set to True, use zopfli instead of zlib for compressing WOFF 1.0.
+# The Python bindings are available at https://pypi.python.org/pypi/zopfli
+USE_ZOPFLI = False
+
+# mapping between zlib's compression levels and zopfli's 'numiterations'.
+# Use lower values for files over several MB in size or it will be too slow
+ZOPFLI_LEVELS = {
+	# 0: 0,  # can't do 0 iterations...
+	1: 1,
+	2: 3,
+	3: 5,
+	4: 8,
+	5: 10,
+	6: 15,
+	7: 25,
+	8: 50,
+	9: 100,
+}
+
+
+def compress(data, level=ZLIB_COMPRESSION_LEVEL):
+	""" Compress 'data' to Zlib format. If 'USE_ZOPFLI' variable is True,
+	zopfli is used instead of the zlib module.
+	The compression 'level' must be between 0 and 9. 1 gives best speed,
+	9 gives best compression (0 gives no compression at all).
+	The default value is a compromise between speed and compression (6).
+	"""
+	if not (0 <= level <= 9):
+		raise ValueError('Bad compression level: %s' % level)
+	if not USE_ZOPFLI or level == 0:
+		from zlib import compress
+		return compress(data, level)
+	else:
+		from zopfli.zlib import compress
+		return compress(data, numiterations=ZOPFLI_LEVELS[level])
+
+
 class SFNTWriter(object):
-	
+
+	def __new__(cls, *args, **kwargs):
+		""" Return an instance of the SFNTWriter sub-class which is compatible
+		with the specified 'flavor'.
+		"""
+		flavor = None
+		if kwargs and 'flavor' in kwargs:
+			flavor = kwargs['flavor']
+		elif args and len(args) > 3:
+			flavor = args[3]
+		if cls is SFNTWriter:
+			if flavor == "woff2":
+				# return new WOFF2Writer object
+				from fontTools.ttLib.woff2 import WOFF2Writer
+				return object.__new__(WOFF2Writer)
+		# return default object
+		return object.__new__(cls)
+
 	def __init__(self, file, numTables, sfntVersion="\000\001\000\000",
-		     flavor=None, flavorData=None):
+			flavor=None, flavorData=None):
 		self.file = file
 		self.numTables = numTables
 		self.sfntVersion = Tag(sfntVersion)
@@ -111,67 +197,72 @@
 			self.DirectoryEntry = WOFFDirectoryEntry
 
 			self.signature = "wOFF"
+
+			# to calculate WOFF checksum adjustment, we also need the original SFNT offsets
+			self.origNextTableOffset = sfntDirectorySize + numTables * sfntDirectoryEntrySize
 		else:
-			assert not self.flavor,  "Unknown flavor '%s'" % self.flavor
+			assert not self.flavor, "Unknown flavor '%s'" % self.flavor
 			self.directoryFormat = sfntDirectoryFormat
 			self.directorySize = sfntDirectorySize
 			self.DirectoryEntry = SFNTDirectoryEntry
 
+			from fontTools.ttLib import getSearchRange
 			self.searchRange, self.entrySelector, self.rangeShift = getSearchRange(numTables, 16)
 
-		self.nextTableOffset = self.directorySize + numTables * self.DirectoryEntry.formatSize
+		self.directoryOffset = self.file.tell()
+		self.nextTableOffset = self.directoryOffset + self.directorySize + numTables * self.DirectoryEntry.formatSize
 		# clear out directory area
 		self.file.seek(self.nextTableOffset)
 		# make sure we're actually where we want to be. (old cStringIO bug)
 		self.file.write(b'\0' * (self.nextTableOffset - self.file.tell()))
-		self.tables = {}
-	
+		self.tables = OrderedDict()
+
+	def setEntry(self, tag, entry):
+		if tag in self.tables:
+			raise TTLibError("cannot rewrite '%s' table" % tag)
+
+		self.tables[tag] = entry
+
 	def __setitem__(self, tag, data):
 		"""Write raw table data to disk."""
-		reuse = False
 		if tag in self.tables:
-			# We've written this table to file before. If the length
-			# of the data is still the same, we allow overwriting it.
-			entry = self.tables[tag]
-			assert not hasattr(entry.__class__, 'encodeData')
-			if len(data) != entry.length:
-				from fontTools import ttLib
-				raise ttLib.TTLibError("cannot rewrite '%s' table: length does not match directory entry" % tag)
-			reuse = True
-		else:
-			entry = self.DirectoryEntry()
-			entry.tag = tag
+			raise TTLibError("cannot rewrite '%s' table" % tag)
 
+		entry = self.DirectoryEntry()
+		entry.tag = tag
+		entry.offset = self.nextTableOffset
 		if tag == 'head':
 			entry.checkSum = calcChecksum(data[:8] + b'\0\0\0\0' + data[12:])
 			self.headTable = data
 			entry.uncompressed = True
 		else:
 			entry.checkSum = calcChecksum(data)
+		entry.saveData(self.file, data)
 
-		entry.offset = self.nextTableOffset
-		entry.saveData (self.file, data)
+		if self.flavor == "woff":
+			entry.origOffset = self.origNextTableOffset
+			self.origNextTableOffset += (entry.origLength + 3) & ~3
 
-		if not reuse:
-			self.nextTableOffset = self.nextTableOffset + ((entry.length + 3) & ~3)
-
+		self.nextTableOffset = self.nextTableOffset + ((entry.length + 3) & ~3)
 		# Add NUL bytes to pad the table data to a 4-byte boundary.
 		# Don't depend on f.seek() as we need to add the padding even if no
 		# subsequent write follows (seek is lazy), ie. after the final table
 		# in the font.
 		self.file.write(b'\0' * (self.nextTableOffset - self.file.tell()))
 		assert self.nextTableOffset == self.file.tell()
-		
-		self.tables[tag] = entry
-	
+
+		self.setEntry(tag, entry)
+
+	def __getitem__(self, tag):
+		return self.tables[tag]
+
 	def close(self):
 		"""All tables must have been written to disk. Now write the
 		directory.
 		"""
 		tables = sorted(self.tables.items())
 		if len(tables) != self.numTables:
-			from fontTools import ttLib
-			raise ttLib.TTLibError("wrong number of tables; expected %d, found %d" % (self.numTables, len(tables)))
+			raise TTLibError("wrong number of tables; expected %d, found %d" % (self.numTables, len(tables)))
 
 		if self.flavor == "woff":
 			self.signature = b"wOFF"
@@ -195,8 +286,7 @@
 				self.metaOrigLength = len(data.metaData)
 				self.file.seek(0,2)
 				self.metaOffset = self.file.tell()
-				import zlib
-				compressedMetaData = zlib.compress(data.metaData)
+				compressedMetaData = compress(data.metaData)
 				self.metaLength = len(compressedMetaData)
 				self.file.write(compressedMetaData)
 			else:
@@ -216,12 +306,12 @@
 			self.length = self.file.tell()
 
 		else:
-			assert not self.flavor,  "Unknown flavor '%s'" % self.flavor
+			assert not self.flavor, "Unknown flavor '%s'" % self.flavor
 			pass
-		
+
 		directory = sstruct.pack(self.directoryFormat, self)
-		
-		self.file.seek(self.directorySize)
+
+		self.file.seek(self.directoryOffset + self.directorySize)
 		seenHead = 0
 		for tag, entry in tables:
 			if tag == "head":
@@ -229,7 +319,7 @@
 			directory = directory + entry.toString()
 		if seenHead:
 			self.writeMasterChecksum(directory)
-		self.file.seek(0)
+		self.file.seek(self.directoryOffset)
 		self.file.write(directory)
 
 	def _calcMasterChecksum(self, directory):
@@ -239,17 +329,18 @@
 		for i in range(len(tags)):
 			checksums.append(self.tables[tags[i]].checkSum)
 
-		# TODO(behdad) I'm fairly sure the checksum for woff is not working correctly.
-		# Haven't debugged.
 		if self.DirectoryEntry != SFNTDirectoryEntry:
 			# Create a SFNT directory for checksum calculation purposes
+			from fontTools.ttLib import getSearchRange
 			self.searchRange, self.entrySelector, self.rangeShift = getSearchRange(self.numTables, 16)
 			directory = sstruct.pack(sfntDirectoryFormat, self)
 			tables = sorted(self.tables.items())
 			for tag, entry in tables:
 				sfntEntry = SFNTDirectoryEntry()
-				for item in ['tag', 'checkSum', 'offset', 'length']:
-					setattr(sfntEntry, item, getattr(entry, item))
+				sfntEntry.tag = entry.tag
+				sfntEntry.checkSum = entry.checkSum
+				sfntEntry.offset = entry.origOffset
+				sfntEntry.length = entry.origLength
 				directory = directory + sfntEntry.toString()
 
 		directory_end = sfntDirectorySize + len(self.tables) * sfntDirectoryEntrySize
@@ -267,6 +358,9 @@
 		self.file.seek(self.tables['head'].offset + 8)
 		self.file.write(struct.pack(">L", checksumadjustment))
 
+	def reordersTables(self):
+		return False
+
 
 # -- sfnt directory helpers and cruft
 
@@ -336,19 +430,19 @@
 
 
 class DirectoryEntry(object):
-	
+
 	def __init__(self):
 		self.uncompressed = False # if True, always embed entry raw
 
 	def fromFile(self, file):
 		sstruct.unpack(self.format, file.read(self.formatSize), self)
-	
+
 	def fromString(self, str):
 		sstruct.unpack(self.format, str, self)
-	
+
 	def toString(self):
 		return sstruct.pack(self.format, self)
-	
+
 	def __repr__(self):
 		if hasattr(self, "tag"):
 			return "<%s '%s' at %x>" % (self.__class__.__name__, self.tag, id(self))
@@ -385,7 +479,17 @@
 
 	format = woffDirectoryEntryFormat
 	formatSize = woffDirectoryEntrySize
-	zlibCompressionLevel = 6
+
+	def __init__(self):
+		super(WOFFDirectoryEntry, self).__init__()
+		# With fonttools<=3.1.2, the only way to set a different zlib
+		# compression level for WOFF directory entries was to set the class
+		# attribute 'zlibCompressionLevel'. This is now replaced by a globally
+		# defined `ZLIB_COMPRESSION_LEVEL`, which is also applied when
+		# compressing the metadata. For backward compatibility, we still
+		# use the class attribute if it was already set.
+		if not hasattr(WOFFDirectoryEntry, 'zlibCompressionLevel'):
+			self.zlibCompressionLevel = ZLIB_COMPRESSION_LEVEL
 
 	def decodeData(self, rawData):
 		import zlib
@@ -394,14 +498,13 @@
 		else:
 			assert self.length < self.origLength
 			data = zlib.decompress(rawData)
-			assert len (data) == self.origLength
+			assert len(data) == self.origLength
 		return data
 
 	def encodeData(self, data):
-		import zlib
 		self.origLength = len(data)
 		if not self.uncompressed:
-			compressedData = zlib.compress(data, self.zlibCompressionLevel)
+			compressedData = compress(data, self.zlibCompressionLevel)
 		if self.uncompressed or len(compressedData) >= self.origLength:
 			# Encode uncompressed
 			rawData = data
@@ -443,13 +546,13 @@
 	Optionally takes a 'start' argument, which allows you to
 	calculate a checksum in chunks by feeding it a previous
 	result.
-	
-	If the data length is not a multiple of four, it assumes
-	it is to be padded with null byte. 
 
-		>>> print calcChecksum(b"abcd")
+	If the data length is not a multiple of four, it assumes
+	it is to be padded with null byte.
+
+		>>> print(calcChecksum(b"abcd"))
 		1633837924
-		>>> print calcChecksum(b"abcdxyz")
+		>>> print(calcChecksum(b"abcdxyz"))
 		3655064932
 	"""
 	remainder = len(data) % 4
@@ -464,7 +567,33 @@
 		value = (value + sum(longs)) & 0xffffffff
 	return value
 
+def readTTCHeader(file):
+	file.seek(0)
+	data = file.read(ttcHeaderSize)
+	if len(data) != ttcHeaderSize:
+		raise TTLibError("Not a Font Collection (not enough data)")
+	self = SimpleNamespace()
+	sstruct.unpack(ttcHeaderFormat, data, self)
+	if self.TTCTag != "ttcf":
+		raise TTLibError("Not a Font Collection")
+	assert self.Version == 0x00010000 or self.Version == 0x00020000, "unrecognized TTC version 0x%08x" % self.Version
+	self.offsetTable = struct.unpack(">%dL" % self.numFonts, file.read(self.numFonts * 4))
+	if self.Version == 0x00020000:
+		pass # ignoring version 2.0 signatures
+	return self
+
+def writeTTCHeader(file, numFonts):
+	self = SimpleNamespace()
+	self.TTCTag = 'ttcf'
+	self.Version = 0x00010000
+	self.numFonts = numFonts
+	file.seek(0)
+	file.write(sstruct.pack(ttcHeaderFormat, self))
+	offset = file.tell()
+	file.write(struct.pack(">%dL" % self.numFonts, *([0] * self.numFonts)))
+	return offset
 
 if __name__ == "__main__":
-    import doctest
-    doctest.testmod()
+	import sys
+	import doctest
+	sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/ttLib/standardGlyphOrder.py b/Lib/fontTools/ttLib/standardGlyphOrder.py
index fdb666a..510773a 100644
--- a/Lib/fontTools/ttLib/standardGlyphOrder.py
+++ b/Lib/fontTools/ttLib/standardGlyphOrder.py
@@ -1,3 +1,6 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+
 #
 # 'post' table formats 1.0 and 2.0 rely on this list of "standard"
 # glyphs.
@@ -10,262 +13,262 @@
 #
 
 standardGlyphOrder = [
-	".notdef",              # 0 
-	".null",                # 1 
-	"nonmarkingreturn",     # 2 
-	"space",                # 3 
-	"exclam",               # 4 
-	"quotedbl",             # 5 
-	"numbersign",           # 6 
-	"dollar",               # 7 
-	"percent",              # 8 
-	"ampersand",            # 9 
-	"quotesingle",          # 10 
-	"parenleft",            # 11 
-	"parenright",           # 12 
-	"asterisk",             # 13 
-	"plus",                 # 14 
-	"comma",                # 15 
-	"hyphen",               # 16 
-	"period",               # 17 
-	"slash",                # 18 
-	"zero",                 # 19 
-	"one",                  # 20 
-	"two",                  # 21 
-	"three",                # 22 
-	"four",                 # 23 
-	"five",                 # 24 
-	"six",                  # 25 
-	"seven",                # 26 
-	"eight",                # 27 
-	"nine",                 # 28 
-	"colon",                # 29 
-	"semicolon",            # 30 
-	"less",                 # 31 
-	"equal",                # 32 
-	"greater",              # 33 
-	"question",             # 34 
-	"at",                   # 35 
-	"A",                    # 36 
-	"B",                    # 37 
-	"C",                    # 38 
-	"D",                    # 39 
-	"E",                    # 40 
-	"F",                    # 41 
-	"G",                    # 42 
-	"H",                    # 43 
-	"I",                    # 44 
-	"J",                    # 45 
-	"K",                    # 46 
-	"L",                    # 47 
-	"M",                    # 48 
-	"N",                    # 49 
-	"O",                    # 50 
-	"P",                    # 51 
-	"Q",                    # 52 
-	"R",                    # 53 
-	"S",                    # 54 
-	"T",                    # 55 
-	"U",                    # 56 
-	"V",                    # 57 
-	"W",                    # 58 
-	"X",                    # 59 
-	"Y",                    # 60 
-	"Z",                    # 61 
-	"bracketleft",          # 62 
-	"backslash",            # 63 
-	"bracketright",         # 64 
-	"asciicircum",          # 65 
-	"underscore",           # 66 
-	"grave",                # 67 
-	"a",                    # 68 
-	"b",                    # 69 
-	"c",                    # 70 
-	"d",                    # 71 
-	"e",                    # 72 
-	"f",                    # 73 
-	"g",                    # 74 
-	"h",                    # 75 
-	"i",                    # 76 
-	"j",                    # 77 
-	"k",                    # 78 
-	"l",                    # 79 
-	"m",                    # 80 
-	"n",                    # 81 
-	"o",                    # 82 
-	"p",                    # 83 
-	"q",                    # 84 
-	"r",                    # 85 
-	"s",                    # 86 
-	"t",                    # 87 
-	"u",                    # 88 
-	"v",                    # 89 
-	"w",                    # 90 
-	"x",                    # 91 
-	"y",                    # 92 
-	"z",                    # 93 
-	"braceleft",            # 94 
-	"bar",                  # 95 
-	"braceright",           # 96 
-	"asciitilde",           # 97 
-	"Adieresis",            # 98 
-	"Aring",                # 99 
-	"Ccedilla",             # 100 
-	"Eacute",               # 101 
-	"Ntilde",               # 102 
-	"Odieresis",            # 103 
-	"Udieresis",            # 104 
-	"aacute",               # 105 
-	"agrave",               # 106 
-	"acircumflex",          # 107 
-	"adieresis",            # 108 
-	"atilde",               # 109 
-	"aring",                # 110 
-	"ccedilla",             # 111 
-	"eacute",               # 112 
-	"egrave",               # 113 
-	"ecircumflex",          # 114 
-	"edieresis",            # 115 
-	"iacute",               # 116 
-	"igrave",               # 117 
-	"icircumflex",          # 118 
-	"idieresis",            # 119 
-	"ntilde",               # 120 
-	"oacute",               # 121 
-	"ograve",               # 122 
-	"ocircumflex",          # 123 
-	"odieresis",            # 124 
-	"otilde",               # 125 
-	"uacute",               # 126 
-	"ugrave",               # 127 
-	"ucircumflex",          # 128 
-	"udieresis",            # 129 
-	"dagger",               # 130 
-	"degree",               # 131 
-	"cent",                 # 132 
-	"sterling",             # 133 
-	"section",              # 134 
-	"bullet",               # 135 
-	"paragraph",            # 136 
-	"germandbls",           # 137 
-	"registered",           # 138 
-	"copyright",            # 139 
-	"trademark",            # 140 
-	"acute",                # 141 
-	"dieresis",             # 142 
-	"notequal",             # 143 
-	"AE",                   # 144 
-	"Oslash",               # 145 
-	"infinity",             # 146 
-	"plusminus",            # 147 
-	"lessequal",            # 148 
-	"greaterequal",         # 149 
-	"yen",                  # 150 
-	"mu",                   # 151 
-	"partialdiff",          # 152 
-	"summation",            # 153 
-	"product",              # 154 
-	"pi",                   # 155 
-	"integral",             # 156 
-	"ordfeminine",          # 157 
-	"ordmasculine",         # 158 
-	"Omega",                # 159 
-	"ae",                   # 160 
-	"oslash",               # 161 
-	"questiondown",         # 162 
-	"exclamdown",           # 163 
-	"logicalnot",           # 164 
-	"radical",              # 165 
-	"florin",               # 166 
-	"approxequal",          # 167 
-	"Delta",                # 168 
-	"guillemotleft",        # 169 
-	"guillemotright",       # 170 
-	"ellipsis",             # 171 
-	"nonbreakingspace",     # 172 
-	"Agrave",               # 173 
-	"Atilde",               # 174 
-	"Otilde",               # 175 
-	"OE",                   # 176 
-	"oe",                   # 177 
-	"endash",               # 178 
-	"emdash",               # 179 
-	"quotedblleft",         # 180 
-	"quotedblright",        # 181 
-	"quoteleft",            # 182 
-	"quoteright",           # 183 
-	"divide",               # 184 
-	"lozenge",              # 185 
-	"ydieresis",            # 186 
-	"Ydieresis",            # 187 
+	".notdef",              # 0
+	".null",                # 1
+	"nonmarkingreturn",     # 2
+	"space",                # 3
+	"exclam",               # 4
+	"quotedbl",             # 5
+	"numbersign",           # 6
+	"dollar",               # 7
+	"percent",              # 8
+	"ampersand",            # 9
+	"quotesingle",          # 10
+	"parenleft",            # 11
+	"parenright",           # 12
+	"asterisk",             # 13
+	"plus",                 # 14
+	"comma",                # 15
+	"hyphen",               # 16
+	"period",               # 17
+	"slash",                # 18
+	"zero",                 # 19
+	"one",                  # 20
+	"two",                  # 21
+	"three",                # 22
+	"four",                 # 23
+	"five",                 # 24
+	"six",                  # 25
+	"seven",                # 26
+	"eight",                # 27
+	"nine",                 # 28
+	"colon",                # 29
+	"semicolon",            # 30
+	"less",                 # 31
+	"equal",                # 32
+	"greater",              # 33
+	"question",             # 34
+	"at",                   # 35
+	"A",                    # 36
+	"B",                    # 37
+	"C",                    # 38
+	"D",                    # 39
+	"E",                    # 40
+	"F",                    # 41
+	"G",                    # 42
+	"H",                    # 43
+	"I",                    # 44
+	"J",                    # 45
+	"K",                    # 46
+	"L",                    # 47
+	"M",                    # 48
+	"N",                    # 49
+	"O",                    # 50
+	"P",                    # 51
+	"Q",                    # 52
+	"R",                    # 53
+	"S",                    # 54
+	"T",                    # 55
+	"U",                    # 56
+	"V",                    # 57
+	"W",                    # 58
+	"X",                    # 59
+	"Y",                    # 60
+	"Z",                    # 61
+	"bracketleft",          # 62
+	"backslash",            # 63
+	"bracketright",         # 64
+	"asciicircum",          # 65
+	"underscore",           # 66
+	"grave",                # 67
+	"a",                    # 68
+	"b",                    # 69
+	"c",                    # 70
+	"d",                    # 71
+	"e",                    # 72
+	"f",                    # 73
+	"g",                    # 74
+	"h",                    # 75
+	"i",                    # 76
+	"j",                    # 77
+	"k",                    # 78
+	"l",                    # 79
+	"m",                    # 80
+	"n",                    # 81
+	"o",                    # 82
+	"p",                    # 83
+	"q",                    # 84
+	"r",                    # 85
+	"s",                    # 86
+	"t",                    # 87
+	"u",                    # 88
+	"v",                    # 89
+	"w",                    # 90
+	"x",                    # 91
+	"y",                    # 92
+	"z",                    # 93
+	"braceleft",            # 94
+	"bar",                  # 95
+	"braceright",           # 96
+	"asciitilde",           # 97
+	"Adieresis",            # 98
+	"Aring",                # 99
+	"Ccedilla",             # 100
+	"Eacute",               # 101
+	"Ntilde",               # 102
+	"Odieresis",            # 103
+	"Udieresis",            # 104
+	"aacute",               # 105
+	"agrave",               # 106
+	"acircumflex",          # 107
+	"adieresis",            # 108
+	"atilde",               # 109
+	"aring",                # 110
+	"ccedilla",             # 111
+	"eacute",               # 112
+	"egrave",               # 113
+	"ecircumflex",          # 114
+	"edieresis",            # 115
+	"iacute",               # 116
+	"igrave",               # 117
+	"icircumflex",          # 118
+	"idieresis",            # 119
+	"ntilde",               # 120
+	"oacute",               # 121
+	"ograve",               # 122
+	"ocircumflex",          # 123
+	"odieresis",            # 124
+	"otilde",               # 125
+	"uacute",               # 126
+	"ugrave",               # 127
+	"ucircumflex",          # 128
+	"udieresis",            # 129
+	"dagger",               # 130
+	"degree",               # 131
+	"cent",                 # 132
+	"sterling",             # 133
+	"section",              # 134
+	"bullet",               # 135
+	"paragraph",            # 136
+	"germandbls",           # 137
+	"registered",           # 138
+	"copyright",            # 139
+	"trademark",            # 140
+	"acute",                # 141
+	"dieresis",             # 142
+	"notequal",             # 143
+	"AE",                   # 144
+	"Oslash",               # 145
+	"infinity",             # 146
+	"plusminus",            # 147
+	"lessequal",            # 148
+	"greaterequal",         # 149
+	"yen",                  # 150
+	"mu",                   # 151
+	"partialdiff",          # 152
+	"summation",            # 153
+	"product",              # 154
+	"pi",                   # 155
+	"integral",             # 156
+	"ordfeminine",          # 157
+	"ordmasculine",         # 158
+	"Omega",                # 159
+	"ae",                   # 160
+	"oslash",               # 161
+	"questiondown",         # 162
+	"exclamdown",           # 163
+	"logicalnot",           # 164
+	"radical",              # 165
+	"florin",               # 166
+	"approxequal",          # 167
+	"Delta",                # 168
+	"guillemotleft",        # 169
+	"guillemotright",       # 170
+	"ellipsis",             # 171
+	"nonbreakingspace",     # 172
+	"Agrave",               # 173
+	"Atilde",               # 174
+	"Otilde",               # 175
+	"OE",                   # 176
+	"oe",                   # 177
+	"endash",               # 178
+	"emdash",               # 179
+	"quotedblleft",         # 180
+	"quotedblright",        # 181
+	"quoteleft",            # 182
+	"quoteright",           # 183
+	"divide",               # 184
+	"lozenge",              # 185
+	"ydieresis",            # 186
+	"Ydieresis",            # 187
 	"fraction",             # 188
 	"currency",             # 189
-	"guilsinglleft",        # 190 
-	"guilsinglright",       # 191 
-	"fi",                   # 192 
-	"fl",                   # 193 
-	"daggerdbl",            # 194 
-	"periodcentered",       # 195 
-	"quotesinglbase",       # 196 
-	"quotedblbase",         # 197 
-	"perthousand",          # 198 
-	"Acircumflex",          # 199 
-	"Ecircumflex",          # 200 
-	"Aacute",               # 201 
-	"Edieresis",            # 202 
-	"Egrave",               # 203 
-	"Iacute",               # 204 
-	"Icircumflex",          # 205 
-	"Idieresis",            # 206 
-	"Igrave",               # 207 
-	"Oacute",               # 208 
-	"Ocircumflex",          # 209 
-	"apple",                # 210 
-	"Ograve",               # 211 
-	"Uacute",               # 212 
-	"Ucircumflex",          # 213 
-	"Ugrave",               # 214 
-	"dotlessi",             # 215 
-	"circumflex",           # 216 
-	"tilde",                # 217 
-	"macron",               # 218 
-	"breve",                # 219 
-	"dotaccent",            # 220 
-	"ring",                 # 221 
-	"cedilla",              # 222 
-	"hungarumlaut",         # 223 
-	"ogonek",               # 224 
-	"caron",                # 225 
-	"Lslash",               # 226 
-	"lslash",               # 227 
-	"Scaron",               # 228 
-	"scaron",               # 229 
-	"Zcaron",               # 230 
-	"zcaron",               # 231 
-	"brokenbar",            # 232 
-	"Eth",                  # 233 
-	"eth",                  # 234 
-	"Yacute",               # 235 
-	"yacute",               # 236 
-	"Thorn",                # 237 
-	"thorn",                # 238 
-	"minus",                # 239 
-	"multiply",             # 240 
-	"onesuperior",          # 241 
-	"twosuperior",          # 242 
-	"threesuperior",        # 243 
-	"onehalf",              # 244 
-	"onequarter",           # 245 
-	"threequarters",        # 246 
-	"franc",                # 247 
-	"Gbreve",               # 248 
-	"gbreve",               # 249 
-	"Idotaccent",           # 250 
-	"Scedilla",             # 251 
-	"scedilla",             # 252 
-	"Cacute",               # 253 
-	"cacute",               # 254 
-	"Ccaron",               # 255 
-	"ccaron",               # 256 
-	"dcroat"                # 257 
+	"guilsinglleft",        # 190
+	"guilsinglright",       # 191
+	"fi",                   # 192
+	"fl",                   # 193
+	"daggerdbl",            # 194
+	"periodcentered",       # 195
+	"quotesinglbase",       # 196
+	"quotedblbase",         # 197
+	"perthousand",          # 198
+	"Acircumflex",          # 199
+	"Ecircumflex",          # 200
+	"Aacute",               # 201
+	"Edieresis",            # 202
+	"Egrave",               # 203
+	"Iacute",               # 204
+	"Icircumflex",          # 205
+	"Idieresis",            # 206
+	"Igrave",               # 207
+	"Oacute",               # 208
+	"Ocircumflex",          # 209
+	"apple",                # 210
+	"Ograve",               # 211
+	"Uacute",               # 212
+	"Ucircumflex",          # 213
+	"Ugrave",               # 214
+	"dotlessi",             # 215
+	"circumflex",           # 216
+	"tilde",                # 217
+	"macron",               # 218
+	"breve",                # 219
+	"dotaccent",            # 220
+	"ring",                 # 221
+	"cedilla",              # 222
+	"hungarumlaut",         # 223
+	"ogonek",               # 224
+	"caron",                # 225
+	"Lslash",               # 226
+	"lslash",               # 227
+	"Scaron",               # 228
+	"scaron",               # 229
+	"Zcaron",               # 230
+	"zcaron",               # 231
+	"brokenbar",            # 232
+	"Eth",                  # 233
+	"eth",                  # 234
+	"Yacute",               # 235
+	"yacute",               # 236
+	"Thorn",                # 237
+	"thorn",                # 238
+	"minus",                # 239
+	"multiply",             # 240
+	"onesuperior",          # 241
+	"twosuperior",          # 242
+	"threesuperior",        # 243
+	"onehalf",              # 244
+	"onequarter",           # 245
+	"threequarters",        # 246
+	"franc",                # 247
+	"Gbreve",               # 248
+	"gbreve",               # 249
+	"Idotaccent",           # 250
+	"Scedilla",             # 251
+	"scedilla",             # 252
+	"Cacute",               # 253
+	"cacute",               # 254
+	"Ccaron",               # 255
+	"ccaron",               # 256
+	"dcroat"                # 257
 ]
diff --git a/Lib/fontTools/ttLib/tables/B_A_S_E_.py b/Lib/fontTools/ttLib/tables/B_A_S_E_.py
index 9551e2c..14906b4 100644
--- a/Lib/fontTools/ttLib/tables/B_A_S_E_.py
+++ b/Lib/fontTools/ttLib/tables/B_A_S_E_.py
@@ -1,3 +1,5 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
 from .otBase import BaseTTXConverter
 
 
diff --git a/Lib/fontTools/ttLib/tables/BitmapGlyphMetrics.py b/Lib/fontTools/ttLib/tables/BitmapGlyphMetrics.py
index dfe86f2..685979a 100644
--- a/Lib/fontTools/ttLib/tables/BitmapGlyphMetrics.py
+++ b/Lib/fontTools/ttLib/tables/BitmapGlyphMetrics.py
@@ -4,8 +4,11 @@
 from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
 from fontTools.misc.textTools import safeEval
+import logging
 
 
+log = logging.getLogger(__name__)
+
 bigGlyphMetricsFormat = """
   > # big endian
   height:       B
@@ -48,11 +51,11 @@
 			if name in metricNames:
 				vars(self)[name] = safeEval(attrs['value'])
 			else:
-				print("Warning: unknown name '%s' being ignored in %s." % name, self.__class__.__name__)
+				log.warning("unknown name '%s' being ignored in %s.", name, self.__class__.__name__)
 
 
 class BigGlyphMetrics(BitmapGlyphMetrics):
 	binaryFormat = bigGlyphMetricsFormat
-	
+
 class SmallGlyphMetrics(BitmapGlyphMetrics):
 	binaryFormat = smallGlyphMetricsFormat
diff --git a/Lib/fontTools/ttLib/tables/C_B_L_C_.py b/Lib/fontTools/ttLib/tables/C_B_L_C_.py
index 2f78571..3d67dd0 100644
--- a/Lib/fontTools/ttLib/tables/C_B_L_C_.py
+++ b/Lib/fontTools/ttLib/tables/C_B_L_C_.py
@@ -2,6 +2,8 @@
 #
 # Google Author(s): Matt Fontaine
 
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
 from . import E_B_L_C_
 
 class table_C_B_L_C_(E_B_L_C_.table_E_B_L_C_):
diff --git a/Lib/fontTools/ttLib/tables/C_F_F_.py b/Lib/fontTools/ttLib/tables/C_F_F_.py
index 8167fdf..f175d5c 100644
--- a/Lib/fontTools/ttLib/tables/C_F_F_.py
+++ b/Lib/fontTools/ttLib/tables/C_F_F_.py
@@ -5,44 +5,43 @@
 
 
 class table_C_F_F_(DefaultTable.DefaultTable):
-	
-	def __init__(self, tag):
+
+	def __init__(self, tag=None):
 		DefaultTable.DefaultTable.__init__(self, tag)
 		self.cff = cffLib.CFFFontSet()
 		self._gaveGlyphOrder = False
-	
+
 	def decompile(self, data, otFont):
-		self.cff.decompile(StringIO(data), otFont)
+		self.cff.decompile(BytesIO(data), otFont, isCFF2=False)
 		assert len(self.cff) == 1, "can't deal with multi-font CFF tables."
-	
+
 	def compile(self, otFont):
-		f = StringIO()
-		self.cff.compile(f, otFont)
+		f = BytesIO()
+		self.cff.compile(f, otFont, isCFF2=False)
 		return f.getvalue()
-	
+
 	def haveGlyphNames(self):
 		if hasattr(self.cff[self.cff.fontNames[0]], "ROS"):
 			return False  # CID-keyed font
 		else:
 			return True
-	
+
 	def getGlyphOrder(self):
 		if self._gaveGlyphOrder:
 			from fontTools import ttLib
 			raise ttLib.TTLibError("illegal use of getGlyphOrder()")
 		self._gaveGlyphOrder = True
 		return self.cff[self.cff.fontNames[0]].getGlyphOrder()
-	
+
 	def setGlyphOrder(self, glyphOrder):
 		pass
 		# XXX
 		#self.cff[self.cff.fontNames[0]].setGlyphOrder(glyphOrder)
-	
-	def toXML(self, writer, otFont, progress=None):
-		self.cff.toXML(writer, progress)
-	
+
+	def toXML(self, writer, otFont):
+		self.cff.toXML(writer)
+
 	def fromXML(self, name, attrs, content, otFont):
 		if not hasattr(self, "cff"):
 			self.cff = cffLib.CFFFontSet()
-		self.cff.fromXML(name, attrs, content)
-
+		self.cff.fromXML(name, attrs, content, otFont)
diff --git a/Lib/fontTools/ttLib/tables/C_F_F__2.py b/Lib/fontTools/ttLib/tables/C_F_F__2.py
new file mode 100644
index 0000000..7e30c8f
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/C_F_F__2.py
@@ -0,0 +1,16 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools import cffLib
+from fontTools.ttLib.tables.C_F_F_ import table_C_F_F_
+
+
+class table_C_F_F__2(table_C_F_F_):
+
+    def decompile(self, data, otFont):
+        self.cff.decompile(BytesIO(data), otFont, isCFF2=True)
+        assert len(self.cff) == 1, "can't deal with multi-font CFF tables."
+
+    def compile(self, otFont):
+        f = BytesIO()
+        self.cff.compile(f, otFont, isCFF2=True)
+        return f.getvalue()
diff --git a/Lib/fontTools/ttLib/tables/C_O_L_R_.py b/Lib/fontTools/ttLib/tables/C_O_L_R_.py
index 139de3c..743fa91 100644
--- a/Lib/fontTools/ttLib/tables/C_O_L_R_.py
+++ b/Lib/fontTools/ttLib/tables/C_O_L_R_.py
@@ -51,7 +51,6 @@
 
 		list(map(operator.setitem, [colorLayerLists]*numBaseGlyphRecords, names, layerLists))
 
-
 	def compile(self, ttFont):
 		ordered = []
 		ttFont.getReverseGlyphMap(rebuild=True)
@@ -117,7 +116,6 @@
 		elif "value" in attrs:
 			setattr(self, name, safeEval(attrs["value"]))
 
-
 	def __getitem__(self, glyphSelector):
 		if isinstance(glyphSelector, int):
 			# its a gid, convert to glyph name
@@ -125,7 +123,7 @@
 
 		if glyphSelector not in self.ColorLayers:
 			return None
-			
+
 		return self.ColorLayers[glyphSelector]
 
 	def __setitem__(self, glyphSelector, value):
@@ -143,7 +141,7 @@
 
 class LayerRecord(object):
 
-	def __init__(self, name = None, colorID = None):
+	def __init__(self, name=None, colorID=None):
 		self.name = name
 		self.colorID = colorID
 
diff --git a/Lib/fontTools/ttLib/tables/C_P_A_L_.py b/Lib/fontTools/ttLib/tables/C_P_A_L_.py
index 7c2721a..25d50a5 100644
--- a/Lib/fontTools/ttLib/tables/C_P_A_L_.py
+++ b/Lib/fontTools/ttLib/tables/C_P_A_L_.py
@@ -6,14 +6,23 @@
 from fontTools.misc.py23 import *
 from fontTools.misc.textTools import safeEval
 from . import DefaultTable
+import array
 import struct
+import sys
 
 
 class table_C_P_A_L_(DefaultTable.DefaultTable):
 
+	def __init__(self, tag=None):
+		DefaultTable.DefaultTable.__init__(self, tag)
+		self.palettes = []
+		self.paletteTypes = []
+		self.paletteLabels = []
+		self.paletteEntryLabels = []
+
 	def decompile(self, data, ttFont):
 		self.version, self.numPaletteEntries, numPalettes, numColorRecords, goffsetFirstColorRecord = struct.unpack(">HHHHL", data[:12])
-		assert (self.version == 0), "Version of COLR table is higher than I know how to handle"
+		assert (self.version <= 1), "Version of CPAL table is higher than I know how to handle"
 		self.palettes = []
 		pos = 12
 		for i in range(numPalettes):
@@ -26,58 +35,209 @@
 				palette.append( Color(*struct.unpack(">BBBB", data[ppos:ppos+4])) )
 				ppos += 4
 			self.palettes.append(palette)
+		if self.version == 0:
+			offsetToPaletteTypeArray = 0
+			offsetToPaletteLabelArray = 0
+			offsetToPaletteEntryLabelArray = 0
+		else:
+			pos = 12 + numPalettes * 2
+			(offsetToPaletteTypeArray, offsetToPaletteLabelArray,
+			offsetToPaletteEntryLabelArray) = (
+				struct.unpack(">LLL", data[pos:pos+12]))
+		self.paletteTypes = self._decompileUInt32Array(
+			data, offsetToPaletteTypeArray, numPalettes)
+		self.paletteLabels = self._decompileUInt16Array(
+			data, offsetToPaletteLabelArray, numPalettes)
+		self.paletteEntryLabels = self._decompileUInt16Array(
+			data, offsetToPaletteEntryLabelArray,
+			self.numPaletteEntries)
+
+	def _decompileUInt16Array(self, data, offset, numElements):
+		if offset == 0:
+			return [0] * numElements
+		result = array.array("H", data[offset : offset + 2 * numElements])
+		if sys.byteorder != "big":
+			result.byteswap()
+		assert len(result) == numElements, result
+		return result.tolist()
+
+	def _decompileUInt32Array(self, data, offset, numElements):
+		if offset == 0:
+			return [0] * numElements
+		result = array.array("I", data[offset : offset + 4 * numElements])
+		if sys.byteorder != "big":
+			result.byteswap()
+		assert len(result) == numElements, result
+		return result.tolist()
 
 	def compile(self, ttFont):
-		dataList = [struct.pack(">HHHHL", self.version, self.numPaletteEntries, len(self.palettes), self.numPaletteEntries * len(self.palettes), 12+2*len(self.palettes))]
-		for i in range(len(self.palettes)):
-			dataList.append(struct.pack(">H", i*self.numPaletteEntries))
+		colorRecordIndices, colorRecords = self._compileColorRecords()
+		paletteTypes = self._compilePaletteTypes()
+		paletteLabels = self._compilePaletteLabels()
+		paletteEntryLabels = self._compilePaletteEntryLabels()
+		numColorRecords = len(colorRecords) // 4
+		offsetToFirstColorRecord = 12 + len(colorRecordIndices)
+		if self.version >= 1:
+			offsetToFirstColorRecord += 12
+		header = struct.pack(">HHHHL", self.version,
+                                     self.numPaletteEntries, len(self.palettes),
+                                     numColorRecords, offsetToFirstColorRecord)
+		if self.version == 0:
+			dataList = [header, colorRecordIndices, colorRecords]
+		else:
+			pos = offsetToFirstColorRecord + len(colorRecords)
+			if len(paletteTypes) == 0:
+				offsetToPaletteTypeArray = 0
+			else:
+				offsetToPaletteTypeArray = pos
+				pos += len(paletteTypes)
+			if len(paletteLabels) == 0:
+				offsetToPaletteLabelArray = 0
+			else:
+				offsetToPaletteLabelArray = pos
+				pos += len(paletteLabels)
+			if len(paletteEntryLabels) == 0:
+				offsetToPaletteEntryLabelArray = 0
+			else:
+				offsetToPaletteEntryLabelArray = pos
+				pos += len(paletteLabels)
+			header1 = struct.pack(">LLL",
+				offsetToPaletteTypeArray,
+				offsetToPaletteLabelArray,
+				offsetToPaletteEntryLabelArray)
+			dataList = [header, colorRecordIndices, header1,
+				    colorRecords, paletteTypes, paletteLabels,
+                                    paletteEntryLabels]
+		return bytesjoin(dataList)
+
+	def _compilePalette(self, palette):
+		assert(len(palette) == self.numPaletteEntries)
+		pack = lambda c: struct.pack(">BBBB", c.blue, c.green, c.red, c.alpha)
+		return bytesjoin([pack(color) for color in palette])
+
+	def _compileColorRecords(self):
+		colorRecords, colorRecordIndices, pool = [], [], {}
 		for palette in self.palettes:
-			assert(len(palette) == self.numPaletteEntries)
-			for color in palette:
-				dataList.append(struct.pack(">BBBB", color.blue,color.green,color.red,color.alpha))
-		data = bytesjoin(dataList)
-		return data
+			packedPalette = self._compilePalette(palette)
+			if packedPalette in pool:
+				index = pool[packedPalette]
+			else:
+				index = len(colorRecords)
+				colorRecords.append(packedPalette)
+				pool[packedPalette] = index
+			colorRecordIndices.append(struct.pack(">H", index * self.numPaletteEntries))
+		return bytesjoin(colorRecordIndices), bytesjoin(colorRecords)
+
+	def _compilePaletteTypes(self):
+		if self.version == 0 or not any(self.paletteTypes):
+			return b''
+		assert len(self.paletteTypes) == len(self.palettes)
+		result = bytesjoin([struct.pack(">I", ptype)
+                                    for ptype in self.paletteTypes])
+		assert len(result) == 4 * len(self.palettes)
+		return result
+
+	def _compilePaletteLabels(self):
+		if self.version == 0 or not any(self.paletteLabels):
+			return b''
+		assert len(self.paletteLabels) == len(self.palettes)
+		result = bytesjoin([struct.pack(">H", label)
+                                    for label in self.paletteLabels])
+		assert len(result) == 2 * len(self.palettes)
+		return result
+
+	def _compilePaletteEntryLabels(self):
+		if self.version == 0 or not any(self.paletteEntryLabels):
+			return b''
+		assert len(self.paletteEntryLabels) == self.numPaletteEntries
+		result = bytesjoin([struct.pack(">H", label)
+                                    for label in self.paletteEntryLabels])
+		assert len(result) == 2 * self.numPaletteEntries
+		return result
 
 	def toXML(self, writer, ttFont):
+		numPalettes = len(self.palettes)
+		paletteLabels = {i: nameID
+				for (i, nameID) in enumerate(self.paletteLabels)}
+		paletteTypes = {i: typ for (i, typ) in enumerate(self.paletteTypes)}
 		writer.simpletag("version", value=self.version)
 		writer.newline()
-		writer.simpletag("numPaletteEntries", value=self.numPaletteEntries)
+		writer.simpletag("numPaletteEntries",
+				 value=self.numPaletteEntries)
 		writer.newline()
 		for index, palette in enumerate(self.palettes):
-			writer.begintag("palette", index=index)
+			attrs = {"index": index}
+			paletteType = paletteTypes.get(index)
+			paletteLabel = paletteLabels.get(index)
+			if self.version > 0 and paletteLabel is not None:
+				attrs["label"] = paletteLabel
+			if self.version > 0 and paletteType is not None:
+				attrs["type"] = paletteType
+			writer.begintag("palette", **attrs)
 			writer.newline()
+			if (self.version > 0 and paletteLabel and
+			    ttFont and "name" in ttFont):
+				name = ttFont["name"].getDebugName(paletteLabel)
+				if name is not None:
+					writer.comment(name)
+					writer.newline()
 			assert(len(palette) == self.numPaletteEntries)
 			for cindex, color in enumerate(palette):
 				color.toXML(writer, ttFont, cindex)
 			writer.endtag("palette")
 			writer.newline()
+		if self.version > 0 and any(self.paletteEntryLabels):
+			writer.begintag("paletteEntryLabels")
+			writer.newline()
+			for index, label in enumerate(self.paletteEntryLabels):
+				if label:
+					writer.simpletag("label", index=index, value=label)
+					if (self.version > 0 and label and ttFont and "name" in ttFont):
+						name = ttFont["name"].getDebugName(label)
+						if name is not None:
+							writer.comment(name)
+					writer.newline()
+			writer.endtag("paletteEntryLabels")
+			writer.newline()
 
 	def fromXML(self, name, attrs, content, ttFont):
-		if not hasattr(self, "palettes"):
-			self.palettes = []
 		if name == "palette":
-			palette = []
-			for element in content:
-				if isinstance(element, basestring):
-					continue
+			self.paletteLabels.append(int(attrs.get("label", "0")))
+			self.paletteTypes.append(int(attrs.get("type", "0")))
 			palette = []
 			for element in content:
 				if isinstance(element, basestring):
 					continue
 				color = Color()
 				color.fromXML(element[0], element[1], element[2], ttFont)
-				palette.append (color)
+				palette.append(color)
 			self.palettes.append(palette)
+		elif name == "paletteEntryLabels":
+			colorLabels = {}
+			for element in content:
+				if isinstance(element, basestring):
+					continue
+				elementName, elementAttr, _ = element
+				if elementName == "label":
+					labelIndex = safeEval(elementAttr["index"])
+					nameID = safeEval(elementAttr["value"])
+					colorLabels[labelIndex] = nameID
+			self.paletteEntryLabels = [
+				colorLabels.get(i, 0)
+				for i in range(self.numPaletteEntries)]
 		elif "value" in attrs:
-			value =  safeEval(attrs["value"])
+			value = safeEval(attrs["value"])
 			setattr(self, name, value)
+			if name == "numPaletteEntries":
+				self.paletteEntryLabels = [0] * self.numPaletteEntries
+
 
 class Color(object):
 
 	def __init__(self, blue=None, green=None, red=None, alpha=None):
-		self.blue  = blue
+		self.blue = blue
 		self.green = green
-		self.red   = red
+		self.red = red
 		self.alpha = alpha
 
 	def hex(self):
@@ -94,7 +254,7 @@
 		value = attrs["value"]
 		if value[0] == '#':
 			value = value[1:]
-		self.red   = int(value[0:2], 16)
+		self.red = int(value[0:2], 16)
 		self.green = int(value[2:4], 16)
-		self.blue  = int(value[4:6], 16)
+		self.blue = int(value[4:6], 16)
 		self.alpha = int(value[6:8], 16) if len (value) >= 8 else 0xFF
diff --git a/Lib/fontTools/ttLib/tables/D_S_I_G_.py b/Lib/fontTools/ttLib/tables/D_S_I_G_.py
index 7794bda..af802b9 100644
--- a/Lib/fontTools/ttLib/tables/D_S_I_G_.py
+++ b/Lib/fontTools/ttLib/tables/D_S_I_G_.py
@@ -40,7 +40,7 @@
 #
 
 class table_D_S_I_G_(DefaultTable.DefaultTable):
-	
+
 	def decompile(self, data, ttFont):
 		dummy, newData = sstruct.unpack2(DSIG_HeaderFormat, data, self)
 		assert self.ulVersion == 1, "DSIG ulVersion must be 1"
@@ -55,7 +55,7 @@
 			assert sigrec.usReserved1 == 0, "DSIG signature record #%d usReserverd1 must be 0" % n
 			assert sigrec.usReserved2 == 0, "DSIG signature record #%d usReserverd2 must be 0" % n
 			sigrec.pkcs7 = newData[:sigrec.cbSignature]
-	
+
 	def compile(self, ttFont):
 		packed = sstruct.pack(DSIG_HeaderFormat, self)
 		headers = [packed]
@@ -76,7 +76,7 @@
 			# Pad to even bytes
 			data.append(b'\0')
 		return bytesjoin(headers+data)
-	
+
 	def toXML(self, xmlWriter, ttFont):
 		xmlWriter.comment("note that the Digital Signature will be invalid after recompilation!")
 		xmlWriter.newline()
@@ -85,7 +85,7 @@
 			xmlWriter.newline()
 			sigrec.toXML(xmlWriter, ttFont)
 		xmlWriter.newline()
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		if name == "tableHeader":
 			self.signatureRecords = []
@@ -115,7 +115,7 @@
 class SignatureRecord(object):
 	def __repr__(self):
 		return "<%s: %s>" % (self.__class__.__name__, self.__dict__)
-	
+
 	def toXML(self, writer, ttFont):
 		writer.begintag(self.__class__.__name__, format=self.ulFormat)
 		writer.newline()
@@ -123,7 +123,7 @@
 		writer.write_noindent(b64encode(self.pkcs7))
 		writer.write_noindent("-----END PKCS7-----\n")
 		writer.endtag(self.__class__.__name__)
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		self.ulFormat = safeEval(attrs["format"])
 		self.usReserved1 = safeEval(attrs.get("reserved1", "0"))
diff --git a/Lib/fontTools/ttLib/tables/DefaultTable.py b/Lib/fontTools/ttLib/tables/DefaultTable.py
index 3a6886c..f0e82f5 100644
--- a/Lib/fontTools/ttLib/tables/DefaultTable.py
+++ b/Lib/fontTools/ttLib/tables/DefaultTable.py
@@ -3,21 +3,21 @@
 from fontTools.ttLib import getClassTag
 
 class DefaultTable(object):
-	
+
 	dependencies = []
-	
+
 	def __init__(self, tag=None):
 		if tag is None:
 			tag = getClassTag(self.__class__)
 		self.tableTag = Tag(tag)
-	
+
 	def decompile(self, data, ttFont):
 		self.data = data
-	
+
 	def compile(self, ttFont):
 		return self.data
-	
-	def toXML(self, writer, ttFont, progress=None):
+
+	def toXML(self, writer, ttFont):
 		if hasattr(self, "ERROR"):
 			writer.comment("An error occurred during the decompilation of this table")
 			writer.newline()
@@ -28,20 +28,22 @@
 		writer.dumphex(self.compile(ttFont))
 		writer.endtag("hexdata")
 		writer.newline()
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		from fontTools.misc.textTools import readHex
 		from fontTools import ttLib
 		if name != "hexdata":
 			raise ttLib.TTLibError("can't handle '%s' element" % name)
 		self.decompile(readHex(content), ttFont)
-	
+
 	def __repr__(self):
 		return "<'%s' table at %x>" % (self.tableTag, id(self))
-	
-	def __ne__(self, other):
-		return not self.__eq__(other)
+
 	def __eq__(self, other):
 		if type(self) != type(other):
 			return NotImplemented
 		return self.__dict__ == other.__dict__
+
+	def __ne__(self, other):
+		result = self.__eq__(other)
+		return result if result is NotImplemented else not result
diff --git a/Lib/fontTools/ttLib/tables/E_B_D_T_.py b/Lib/fontTools/ttLib/tables/E_B_D_T_.py
index f119291..3a316e5 100644
--- a/Lib/fontTools/ttLib/tables/E_B_D_T_.py
+++ b/Lib/fontTools/ttLib/tables/E_B_D_T_.py
@@ -7,6 +7,10 @@
 import itertools
 import os
 import struct
+import logging
+
+
+log = logging.getLogger(__name__)
 
 ebdtTableVersionFormat = """
 	> # big endian
@@ -158,7 +162,7 @@
 					continue
 				name, attrs, content = element
 				if name[4:].startswith(_bitmapGlyphSubclassPrefix[4:]):
-					imageFormat =	safeEval(name[len(_bitmapGlyphSubclassPrefix):])
+					imageFormat = safeEval(name[len(_bitmapGlyphSubclassPrefix):])
 					glyphName = attrs['name']
 					imageFormatClass = self.getImageFormatClass(imageFormat)
 					curGlyph = imageFormatClass(None, None)
@@ -166,7 +170,7 @@
 					assert glyphName not in bitmapGlyphDict, "Duplicate glyphs with the same name '%s' in the same strike." % glyphName
 					bitmapGlyphDict[glyphName] = curGlyph
 				else:
-					print("Warning: %s being ignored by %s", name, self.__class__.__name__)
+					log.warning("%s being ignored by %s", name, self.__class__.__name__)
 
 			# Grow the strike data array to the appropriate size. The XML
 			# format allows the strike index value to be out of order.
@@ -196,7 +200,7 @@
 			if name in componentNames:
 				vars(self)[name] = safeEval(attrs['value'])
 			else:
-				print("Warning: unknown name '%s' being ignored by EbdtComponent." % name)
+				log.warning("unknown name '%s' being ignored by EbdtComponent.", name)
 
 # Helper functions for dealing with binary.
 
@@ -338,15 +342,20 @@
 	bitmapObject.setRows(dataRows, bitDepth=bitDepth, metrics=metrics, reverseBytes=True)
 
 def _writeExtFileImageData(strikeIndex, glyphName, bitmapObject, writer, ttFont):
-	folder = 'bitmaps/'
+	try:
+		folder = os.path.dirname(writer.file.name)
+	except AttributeError:
+		# fall back to current directory if output file's directory isn't found
+		folder = '.'
+	folder = os.path.join(folder, 'bitmaps')
 	filename = glyphName + bitmapObject.fileExtension
 	if not os.path.isdir(folder):
 		os.makedirs(folder)
-	folder += 'strike%d/' % strikeIndex
+	folder = os.path.join(folder, 'strike%d' % strikeIndex)
 	if not os.path.isdir(folder):
 		os.makedirs(folder)
 
-	fullPath = folder + filename
+	fullPath = os.path.join(folder, filename)
 	writer.simpletag('extfileimagedata', value=fullPath)
 	writer.newline()
 
@@ -373,10 +382,10 @@
 
 	# Keep track of reading and writing of various forms.
 	xmlDataFunctions = {
-		'raw':       (_writeRawImageData, _readRawImageData),
-		'row':       (_writeRowImageData, _readRowImageData),
-		'bitwise':   (_writeBitwiseImageData, _readBitwiseImageData),
-		'extfile':   (_writeExtFileImageData, _readExtFileImageData),
+		'raw':		(_writeRawImageData, _readRawImageData),
+		'row':		(_writeRowImageData, _readRowImageData),
+		'bitwise':	(_writeBitwiseImageData, _readBitwiseImageData),
+		'extfile':	(_writeExtFileImageData, _readExtFileImageData),
 		}
 
 	def __init__(self, data, ttFont):
@@ -423,7 +432,7 @@
 			# Chop off 'imagedata' from the tag to get just the option.
 			option = name[:-len('imagedata')]
 			assert option in self.__class__.xmlDataFunctions
-			self.readData(name, attrs, content, ttFont)
+			self.readData(name, attr, content, ttFont)
 
 	# Some of the glyphs have the metrics. This allows for metrics to be
 	# added if the glyph format has them. Default behavior is to do nothing.
@@ -473,7 +482,7 @@
 					self.metrics = metricsClass()
 					self.metrics.fromXML(name, attrs, content, ttFont)
 				elif name == oppositeMetricsName:
-					print("Warning: %s being ignored in format %d." % oppositeMetricsName, self.getFormat())
+					log.warning("Warning: %s being ignored in format %d.", oppositeMetricsName, self.getFormat())
 
 	return BitmapPlusMetricsMixin
 
@@ -687,7 +696,7 @@
 						curComponent.fromXML(name, attrs, content, ttFont)
 						self.componentArray.append(curComponent)
 					else:
-						print("Warning: '%s' being ignored in component array." % name)
+						log.warning("'%s' being ignored in component array.", name)
 
 
 class ebdt_bitmap_format_8(BitmapPlusSmallMetricsMixin, ComponentBitmapGlyph):
diff --git a/Lib/fontTools/ttLib/tables/E_B_L_C_.py b/Lib/fontTools/ttLib/tables/E_B_L_C_.py
index 28a2635..0c53e7d 100644
--- a/Lib/fontTools/ttLib/tables/E_B_L_C_.py
+++ b/Lib/fontTools/ttLib/tables/E_B_L_C_.py
@@ -7,6 +7,10 @@
 import struct
 import itertools
 from collections import deque
+import logging
+
+
+log = logging.getLogger(__name__)
 
 eblcHeaderFormat = """
 	> # big endian
@@ -71,44 +75,47 @@
 
 		# Save the original data because offsets are from the start of the table.
 		origData = data
+		i = 0;
 
-		dummy, data = sstruct.unpack2(eblcHeaderFormat, data, self)
+		dummy = sstruct.unpack(eblcHeaderFormat, data[:8], self)
+		i += 8;
 
 		self.strikes = []
 		for curStrikeIndex in range(self.numSizes):
 			curStrike = Strike()
 			self.strikes.append(curStrike)
 			curTable = curStrike.bitmapSizeTable
-			dummy, data = sstruct.unpack2(bitmapSizeTableFormatPart1, data, curTable)
+			dummy = sstruct.unpack2(bitmapSizeTableFormatPart1, data[i:i+16], curTable)
+			i += 16
 			for metric in ('hori', 'vert'):
 				metricObj = SbitLineMetrics()
 				vars(curTable)[metric] = metricObj
-				dummy, data = sstruct.unpack2(sbitLineMetricsFormat, data, metricObj)
-			dummy, data = sstruct.unpack2(bitmapSizeTableFormatPart2, data, curTable)
+				dummy = sstruct.unpack2(sbitLineMetricsFormat, data[i:i+12], metricObj)
+				i += 12
+			dummy = sstruct.unpack(bitmapSizeTableFormatPart2, data[i:i+8], curTable)
+			i += 8
 
 		for curStrike in self.strikes:
 			curTable = curStrike.bitmapSizeTable
 			for subtableIndex in range(curTable.numberOfIndexSubTables):
-				lowerBound = curTable.indexSubTableArrayOffset + subtableIndex * indexSubTableArraySize
-				upperBound = lowerBound + indexSubTableArraySize
-				data = origData[lowerBound:upperBound]
+				i = curTable.indexSubTableArrayOffset + subtableIndex * indexSubTableArraySize
 
-				tup = struct.unpack(indexSubTableArrayFormat, data)
+				tup = struct.unpack(indexSubTableArrayFormat, data[i:i+indexSubTableArraySize])
 				(firstGlyphIndex, lastGlyphIndex, additionalOffsetToIndexSubtable) = tup
-				offsetToIndexSubTable = curTable.indexSubTableArrayOffset + additionalOffsetToIndexSubtable
-				data = origData[offsetToIndexSubTable:]
+				i = curTable.indexSubTableArrayOffset + additionalOffsetToIndexSubtable
 
-				tup = struct.unpack(indexSubHeaderFormat, data[:indexSubHeaderSize])
+				tup = struct.unpack(indexSubHeaderFormat, data[i:i+indexSubHeaderSize])
 				(indexFormat, imageFormat, imageDataOffset) = tup
 
 				indexFormatClass = self.getIndexFormatClass(indexFormat)
-				indexSubTable = indexFormatClass(data[indexSubHeaderSize:], ttFont)
+				indexSubTable = indexFormatClass(data[i+indexSubHeaderSize:], ttFont)
 				indexSubTable.firstGlyphIndex = firstGlyphIndex
 				indexSubTable.lastGlyphIndex = lastGlyphIndex
 				indexSubTable.additionalOffsetToIndexSubtable = additionalOffsetToIndexSubtable
 				indexSubTable.indexFormat = indexFormat
 				indexSubTable.imageFormat = imageFormat
 				indexSubTable.imageDataOffset = imageDataOffset
+				indexSubTable.decompile() # https://github.com/behdad/fonttools/issues/317
 				curStrike.indexSubTables.append(indexSubTable)
 
 	def compile(self, ttFont):
@@ -293,7 +300,7 @@
 			elif name in dataNames:
 				vars(self)[name] = safeEval(attrs['value'])
 			else:
-				print("Warning: unknown name '%s' being ignored in BitmapSizeTable." % name)
+				log.warning("unknown name '%s' being ignored in BitmapSizeTable.", name)
 
 
 class SbitLineMetrics(object):
@@ -336,7 +343,6 @@
 		if not hasattr(self, "data"):
 			raise AttributeError(attr)
 		self.decompile()
-		del self.data, self.ttFont
 		return getattr(self, attr)
 
 	# This method just takes care of the indexSubHeader. Implementing subclasses
@@ -439,6 +445,7 @@
 
 			self.names = list(map(self.ttFont.getGlyphName, glyphIds))
 			self.removeSkipGlyphs()
+			del self.data, self.ttFont
 
 		def compile(self, ttFont):
 			# First make sure that all the data lines up properly. Formats 1 and 3
@@ -503,7 +510,7 @@
 				self.metrics = BigGlyphMetrics()
 				self.metrics.fromXML(name, attrs, content, ttFont)
 			elif name == SmallGlyphMetrics.__name__:
-				print("Warning: SmallGlyphMetrics being ignored in format %d." % self.indexFormat)
+				log.warning("SmallGlyphMetrics being ignored in format %d.", self.indexFormat)
 
 	def padBitmapData(self, data):
 		# Make sure that the data isn't bigger than the fixed size.
@@ -525,12 +532,13 @@
 		offsets = [self.imageSize * i + self.imageDataOffset for i in range(len(glyphIds)+1)]
 		self.locations = list(zip(offsets, offsets[1:]))
 		self.names = list(map(self.ttFont.getGlyphName, glyphIds))
+		del self.data, self.ttFont
 
 	def compile(self, ttFont):
 		glyphIds = list(map(ttFont.getGlyphID, self.names))
 		# Make sure all the ids are consecutive. This is required by Format 2.
 		assert glyphIds == list(range(self.firstGlyphIndex, self.lastGlyphIndex+1)), "Format 2 ids must be consecutive."
-		self.imageDataOffset = min(zip(*self.locations)[0])
+		self.imageDataOffset = min(next(iter(zip(*self.locations))))
 
 		dataList = [EblcIndexSubTable.compile(self, ttFont)]
 		dataList.append(struct.pack(">L", self.imageSize))
@@ -556,6 +564,7 @@
 		offsets = [offset + self.imageDataOffset for offset in offsets]
 		self.locations = list(zip(offsets, offsets[1:]))
 		self.names = list(map(self.ttFont.getGlyphName, glyphIds))
+		del self.data, self.ttFont
 
 	def compile(self, ttFont):
 		# First make sure that all the data lines up properly. Format 4
@@ -594,9 +603,10 @@
 		offsets = [self.imageSize * i + self.imageDataOffset for i in range(len(glyphIds)+1)]
 		self.locations = list(zip(offsets, offsets[1:]))
 		self.names = list(map(self.ttFont.getGlyphName, glyphIds))
+		del self.data, self.ttFont
 
 	def compile(self, ttFont):
-		self.imageDataOffset = min(zip(*self.locations)[0])
+		self.imageDataOffset = min(next(iter(zip(*self.locations))))
 		dataList = [EblcIndexSubTable.compile(self, ttFont)]
 		dataList.append(struct.pack(">L", self.imageSize))
 		dataList.append(sstruct.pack(bigGlyphMetricsFormat, self.metrics))
diff --git a/Lib/fontTools/ttLib/tables/F_F_T_M_.py b/Lib/fontTools/ttLib/tables/F_F_T_M_.py
index e8b1d29..3d110bd 100644
--- a/Lib/fontTools/ttLib/tables/F_F_T_M_.py
+++ b/Lib/fontTools/ttLib/tables/F_F_T_M_.py
@@ -1,10 +1,9 @@
+from __future__ import print_function, division, absolute_import
 from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
 from fontTools.misc.textTools import safeEval
-from ._h_e_a_d import mac_epoch_diff
+from fontTools.misc.timeTools import timestampFromString, timestampToString
 from . import DefaultTable
-import time
-import calendar
 
 FFTMFormat = """
 		>	# big endian
@@ -16,31 +15,28 @@
 
 class table_F_F_T_M_(DefaultTable.DefaultTable):
 
-  def decompile(self, data, ttFont):
-    dummy, rest = sstruct.unpack2(FFTMFormat, data, self)
+	def decompile(self, data, ttFont):
+		dummy, rest = sstruct.unpack2(FFTMFormat, data, self)
 
-  def compile(self, ttFont):
-    data = sstruct.pack(FFTMFormat, self)
-    return data
+	def compile(self, ttFont):
+		data = sstruct.pack(FFTMFormat, self)
+		return data
 
-  def toXML(self, writer, ttFont):
-    writer.comment("FontForge's timestamp, font source creation and modification dates")
-    writer.newline()
-    formatstring, names, fixes = sstruct.getformat(FFTMFormat)
-    for name in names:
-      value = getattr(self, name)
-      if name in ("FFTimeStamp", "sourceCreated", "sourceModified"):
-        try:
-          value = time.asctime(time.gmtime(max(0, value + mac_epoch_diff)))
-        except ValueError:
-          value = time.asctime(time.gmtime(0))
-      writer.simpletag(name, value=value)
-      writer.newline()
+	def toXML(self, writer, ttFont):
+		writer.comment("FontForge's timestamp, font source creation and modification dates")
+		writer.newline()
+		formatstring, names, fixes = sstruct.getformat(FFTMFormat)
+		for name in names:
+			value = getattr(self, name)
+			if name in ("FFTimeStamp", "sourceCreated", "sourceModified"):
+				value = timestampToString(value)
+			writer.simpletag(name, value=value)
+			writer.newline()
 
-  def fromXML(self, name, attrs, content, ttFont):
-    value = attrs["value"]
-    if name in ("FFTimeStamp", "sourceCreated", "sourceModified"):
-      value = calendar.timegm(time.strptime(value)) - mac_epoch_diff
-    else:
-      value = safeEval(value)
-    setattr(self, name, value)
\ No newline at end of file
+	def fromXML(self, name, attrs, content, ttFont):
+		value = attrs["value"]
+		if name in ("FFTimeStamp", "sourceCreated", "sourceModified"):
+			value = timestampFromString(value)
+		else:
+			value = safeEval(value)
+		setattr(self, name, value)
diff --git a/Lib/fontTools/ttLib/tables/F__e_a_t.py b/Lib/fontTools/ttLib/tables/F__e_a_t.py
new file mode 100644
index 0000000..22be4f6
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/F__e_a_t.py
@@ -0,0 +1,114 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import safeEval
+from .otBase import BaseTTXConverter
+from . import DefaultTable
+from . import grUtils
+import struct
+
+Feat_hdr_format='''
+    >
+    version:    16.16F
+'''
+
+class table_F__e_a_t(DefaultTable.DefaultTable):
+
+    def __init__(self, tag=None):
+        DefaultTable.DefaultTable.__init__(self, tag)
+        self.features = {}
+
+    def decompile(self, data, ttFont):
+        (_, data) = sstruct.unpack2(Feat_hdr_format, data, self)
+        numFeats, = struct.unpack('>H', data[:2])
+        data = data[8:]
+        allfeats = []
+        maxsetting = 0
+        for i in range(numFeats):
+            if self.version >= 2.0:
+                (fid, nums, _, offset, flags, lid) = struct.unpack(">LHHLHH",
+                                                            data[16*i:16*(i+1)])
+                offset = int((offset - 12 - 16 * numFeats) / 4)
+            else:
+                (fid, nums, offset, flags, lid) = struct.unpack(">HHLHH",
+                                                            data[12*i:12*(i+1)])
+                offset = int((offset - 12 - 12 * numFeats) / 4)
+            allfeats.append((fid, nums, offset, flags, lid))
+            maxsetting = max(maxsetting, offset + nums)
+        data = data[16*numFeats:]
+        allsettings = []
+        for i in range(maxsetting):
+            if len(data) >= 4 * (i + 1):
+                (val, lid) = struct.unpack(">HH", data[4*i:4*(i+1)])
+                allsettings.append((val, lid))
+        for i,f in enumerate(allfeats):
+            (fid, nums, offset, flags, lid) = f
+            fobj = Feature()
+            fobj.flags = flags
+            fobj.label = lid
+            self.features[grUtils.num2tag(fid)] = fobj
+            fobj.settings = {}
+            fobj.default = None
+            fobj.index = i
+            for i in range(offset, offset + nums):
+                if i >= len(allsettings): continue
+                (vid, vlid) = allsettings[i]
+                fobj.settings[vid] = vlid
+                if fobj.default is None:
+                    fobj.default = vid
+
+    def compile(self, ttFont):
+        fdat = ""
+        vdat = ""
+        offset = 0
+        for f, v in sorted(self.features.items(), key=lambda x:x[1].index):
+            fnum = grUtils.tag2num(f)
+            if self.version >= 2.0:
+                fdat += struct.pack(">LHHLHH", grUtils.tag2num(f), len(v.settings),
+                    0, offset * 4 + 12 + 16 * len(self.features), v.flags, v.label)
+            elif fnum > 65535:      # self healing for alphabetic ids
+                self.version = 2.0
+                return self.compile(ttFont)
+            else:
+                fdat += struct.pack(">HHLHH", grUtils.tag2num(f), len(v.settings),
+                    offset * 4 + 12 + 12 * len(self.features), v.flags, v.label)
+            for s, l in sorted(v.settings.items(), key=lambda x:(-1, x[1]) if x[0] == v.default else x):
+                vdat += struct.pack(">HH", s, l)
+            offset += len(v.settings)
+        hdr = sstruct.pack(Feat_hdr_format, self)
+        return hdr + struct.pack('>HHL', len(self.features), 0, 0) + fdat + vdat
+
+    def toXML(self, writer, ttFont):
+        writer.simpletag('version', version=self.version)
+        writer.newline()
+        for f, v in sorted(self.features.items(), key=lambda x:x[1].index):
+            writer.begintag('feature', fid=f, label=v.label, flags=v.flags,
+                            default=(v.default if v.default else 0))
+            writer.newline()
+            for s, l in sorted(v.settings.items()):
+                writer.simpletag('setting', value=s, label=l)
+                writer.newline()
+            writer.endtag('feature')
+            writer.newline()
+
+    def fromXML(self, name, attrs, content, ttFont):
+        if name == 'version':
+            self.version = float(safeEval(attrs['version']))
+        elif name == 'feature':
+            fid = attrs['fid']
+            fobj = Feature()
+            fobj.flags = int(safeEval(attrs['flags']))
+            fobj.label = int(safeEval(attrs['label']))
+            fobj.default = int(safeEval(attrs.get('default','0')))
+            fobj.index = len(self.features)
+            self.features[fid] = fobj
+            fobj.settings = {}
+            for element in content:
+                if not isinstance(element, tuple): continue
+                tag, a, c = element
+                if tag == 'setting':
+                    fobj.settings[int(safeEval(a['value']))] = int(safeEval(a['label']))
+
+class Feature(object):
+    pass
+
diff --git a/Lib/fontTools/ttLib/tables/G_D_E_F_.py b/Lib/fontTools/ttLib/tables/G_D_E_F_.py
index d4a5741..08faf62 100644
--- a/Lib/fontTools/ttLib/tables/G_D_E_F_.py
+++ b/Lib/fontTools/ttLib/tables/G_D_E_F_.py
@@ -1,3 +1,5 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
 from .otBase import BaseTTXConverter
 
 
diff --git a/Lib/fontTools/ttLib/tables/G_M_A_P_.py b/Lib/fontTools/ttLib/tables/G_M_A_P_.py
index 5db94d9..afa8c4d 100644
--- a/Lib/fontTools/ttLib/tables/G_M_A_P_.py
+++ b/Lib/fontTools/ttLib/tables/G_M_A_P_.py
@@ -13,7 +13,7 @@
 		recordsOffset:		H
 		fontNameLength:		H
 """
-# psFontName is a byte string which follows the record above. This is zero padded 
+# psFontName is a byte string which follows the record above. This is zero padded
 # to the beginning of the records array. The recordsOffsst is 32 bit aligned.
 
 GMAPRecordFormat1 = """
@@ -24,17 +24,16 @@
 		ggid:		H
 		name:		32s
 """
-		
 
 
 class GMAPRecord(object):
-	def __init__(self, uv = 0, cid = 0, gid = 0, ggid = 0, name = ""):
+	def __init__(self, uv=0, cid=0, gid=0, ggid=0, name=""):
 		self.UV = uv
 		self.cid = cid
 		self.gid = gid
 		self.ggid = ggid
 		self.name = name
-		
+
 	def toXML(self, writer, ttFont):
 		writer.begintag("GMAPRecord")
 		writer.newline()
@@ -51,19 +50,17 @@
 		writer.endtag("GMAPRecord")
 		writer.newline()
 
-
 	def fromXML(self, name, attrs, content, ttFont):
 		value = attrs["value"]
 		if name == "GlyphletName":
 			self.name = value
 		else:
 			setattr(self, name, safeEval(value))
-		
 
 	def compile(self, ttFont):
 		if 	self.UV is None:
 			self.UV = 0
-		nameLen =  len(self.name)
+		nameLen = len(self.name)
 		if nameLen < 32:
 			self.name = self.name + "\0"*(32 - nameLen)
 		data = sstruct.pack(GMAPRecordFormat1, self)
@@ -74,9 +71,9 @@
 
 
 class table_G_M_A_P_(DefaultTable.DefaultTable):
-	
+
 	dependencies = []
-	
+
 	def decompile(self, data, ttFont):
 		dummy, newData = sstruct.unpack2(GMAPFormat, data, self)
 		self.psFontName = tostr(newData[:self.fontNameLength])
@@ -87,19 +84,17 @@
 			gmapRecord, newData = sstruct.unpack2(GMAPRecordFormat1, newData, GMAPRecord())
 			gmapRecord.name = gmapRecord.name.strip('\0')
 			self.gmapRecords.append(gmapRecord)
-		
 
 	def compile(self, ttFont):
 		self.recordsCount = len(self.gmapRecords)
 		self.fontNameLength = len(self.psFontName)
-		self.recordsOffset = 4 *(((self.fontNameLength + 12)  + 3) // 4)
+		self.recordsOffset = 4 * (((self.fontNameLength + 12) + 3) // 4)
 		data = sstruct.pack(GMAPFormat, self)
 		data = data + tobytes(self.psFontName)
 		data = data + b"\0" * (self.recordsOffset - len(data))
 		for record in self.gmapRecords:
 			data = data + record.compile(ttFont)
 		return data
-	
 
 	def toXML(self, writer, ttFont):
 		writer.comment("Most of this table will be recalculated by the compiler")
@@ -113,7 +108,7 @@
 		writer.newline()
 		for gmapRecord in self.gmapRecords:
 			gmapRecord.toXML(writer, ttFont)
-		
+
 	def fromXML(self, name, attrs, content, ttFont):
 		if name == "GMAPRecord":
 			if not hasattr(self, "gmapRecords"):
@@ -129,5 +124,5 @@
 			value = attrs["value"]
 			if name == "PSFontName":
 				self.psFontName = value
-			else:	
+			else:
 				setattr(self, name, safeEval(value))
diff --git a/Lib/fontTools/ttLib/tables/G_P_K_G_.py b/Lib/fontTools/ttLib/tables/G_P_K_G_.py
index 4df666f..4e13830 100644
--- a/Lib/fontTools/ttLib/tables/G_P_K_G_.py
+++ b/Lib/fontTools/ttLib/tables/G_P_K_G_.py
@@ -13,12 +13,12 @@
 		numGMAPs:		H
 		numGlyplets:		H
 """
-# psFontName is a byte string which follows the record above. This is zero padded 
+# psFontName is a byte string which follows the record above. This is zero padded
 # to the beginning of the records array. The recordsOffsst is 32 bit aligned.
 
 
 class table_G_P_K_G_(DefaultTable.DefaultTable):
-	
+
 	def decompile(self, data, ttFont):
 		dummy, newData = sstruct.unpack2(GPKGFormat, data, self)
 
@@ -44,7 +44,6 @@
 			end = glyphletOffsets[i+1]
 			self.glyphlets.append(data[start:end])
 
-
 	def compile(self, ttFont):
 		self.numGMAPs = len(self.GMAPs)
 		self.numGlyplets = len(self.glyphlets)
@@ -75,7 +74,7 @@
 		dataList += self.glyphlets
 		data = bytesjoin(dataList)
 		return data
-	
+
 	def toXML(self, writer, ttFont):
 		writer.comment("Most of this table will be recalculated by the compiler")
 		writer.newline()
@@ -126,5 +125,5 @@
 				itemName, itemAttrs, itemContent = element
 				if itemName == "hexdata":
 					self.glyphlets.append(readHex(itemContent))
-		else:	
-			setattr(self, name, safeEval(value))
+		else:
+			setattr(self, name, safeEval(attrs["value"]))
diff --git a/Lib/fontTools/ttLib/tables/G_P_O_S_.py b/Lib/fontTools/ttLib/tables/G_P_O_S_.py
index 013c820..1c36061 100644
--- a/Lib/fontTools/ttLib/tables/G_P_O_S_.py
+++ b/Lib/fontTools/ttLib/tables/G_P_O_S_.py
@@ -1,3 +1,5 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
 from .otBase import BaseTTXConverter
 
 
diff --git a/Lib/fontTools/ttLib/tables/G_S_U_B_.py b/Lib/fontTools/ttLib/tables/G_S_U_B_.py
index 4403649..d23e8ba 100644
--- a/Lib/fontTools/ttLib/tables/G_S_U_B_.py
+++ b/Lib/fontTools/ttLib/tables/G_S_U_B_.py
@@ -1,3 +1,5 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
 from .otBase import BaseTTXConverter
 
 
diff --git a/Lib/fontTools/ttLib/tables/G__l_a_t.py b/Lib/fontTools/ttLib/tables/G__l_a_t.py
new file mode 100644
index 0000000..36ed6df
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/G__l_a_t.py
@@ -0,0 +1,221 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import safeEval
+from itertools import *
+from functools import partial
+from . import DefaultTable
+from . import grUtils
+import struct, operator, warnings
+try:
+    import lz4
+except: 
+    lz4 = None
+
+
+Glat_format_0 = """
+    >        # big endian
+    version: 16.16F
+"""
+
+Glat_format_3 = """
+    >
+    version: 16.16F
+    compression:L    # compression scheme or reserved 
+"""
+
+Glat_format_1_entry = """
+    >
+    attNum:     B    # Attribute number of first attribute
+    num:        B    # Number of attributes in this run
+"""
+Glat_format_23_entry = """
+    >
+    attNum:     H    # Attribute number of first attribute
+    num:        H    # Number of attributes in this run
+"""
+
+Glat_format_3_octabox_metrics = """
+    >
+    subboxBitmap:   H    # Which subboxes exist on 4x4 grid
+    diagNegMin:     B    # Defines minimum negatively-sloped diagonal (si)
+    diagNegMax:     B    # Defines maximum negatively-sloped diagonal (sa)
+    diagPosMin:     B    # Defines minimum positively-sloped diagonal (di)
+    diagPosMax:     B    # Defines maximum positively-sloped diagonal (da)
+"""
+
+Glat_format_3_subbox_entry = """
+    >
+    left:           B    # xi
+    right:          B    # xa
+    bottom:         B    # yi
+    top:            B    # ya
+    diagNegMin:     B    # Defines minimum negatively-sloped diagonal (si)
+    diagNegMax:     B    # Defines maximum negatively-sloped diagonal (sa)
+    diagPosMin:     B    # Defines minimum positively-sloped diagonal (di)
+    diagPosMax:     B    # Defines maximum positively-sloped diagonal (da)
+"""
+
+class _Object() :
+    pass
+
+class _Dict(dict) :
+    pass
+
+class table_G__l_a_t(DefaultTable.DefaultTable):
+    '''
+    Support Graphite Glat tables
+    '''
+
+    def __init__(self, tag=None):
+        DefaultTable.DefaultTable.__init__(self, tag)
+        self.scheme = 0
+
+    def decompile(self, data, ttFont):
+        sstruct.unpack2(Glat_format_0, data, self)
+        if self.version <= 1.9:
+            decoder = partial(self.decompileAttributes12,fmt=Glat_format_1_entry)
+        elif self.version <= 2.9:   
+            decoder = partial(self.decompileAttributes12,fmt=Glat_format_23_entry)
+        elif self.version >= 3.0:
+            (data, self.scheme) = grUtils.decompress(data)
+            sstruct.unpack2(Glat_format_3, data, self)
+            self.hasOctaboxes = (self.compression & 1) == 1
+            decoder = self.decompileAttributes3
+        
+        gloc = ttFont['Gloc']
+        self.attributes = {}
+        count = 0
+        for s,e in zip(gloc,gloc[1:]):
+            self.attributes[ttFont.getGlyphName(count)] = decoder(data[s:e])
+            count += 1
+    
+    def decompileAttributes12(self, data, fmt):
+        attributes = _Dict()
+        while len(data) > 3:
+            e, data = sstruct.unpack2(fmt, data, _Object())
+            keys = range(e.attNum, e.attNum+e.num)
+            if len(data) >= 2 * e.num :
+                vals = struct.unpack_from(('>%dh' % e.num), data)
+                attributes.update(zip(keys,vals))
+                data = data[2*e.num:]
+        return attributes
+
+    def decompileAttributes3(self, data):
+        if self.hasOctaboxes:
+            o, data = sstruct.unpack2(Glat_format_3_octabox_metrics, data, _Object())
+            numsub = bin(o.subboxBitmap).count("1")
+            o.subboxes = []
+            for b in range(numsub):
+                if len(data) >= 8 :
+                    subbox, data = sstruct.unpack2(Glat_format_3_subbox_entry,
+                                                    data, _Object())
+                    o.subboxes.append(subbox)
+        attrs = self.decompileAttributes12(data, Glat_format_23_entry)
+        if self.hasOctaboxes:
+            attrs.octabox = o
+        return attrs
+
+    def compile(self, ttFont):
+        data = sstruct.pack(Glat_format_0, self)
+        if self.version <= 1.9:
+            encoder = partial(self.compileAttributes12, fmt=Glat_format_1_entry)
+        elif self.version <= 2.9:
+            encoder = partial(self.compileAttributes12, fmt=Glat_format_1_entry)
+        elif self.version >= 3.0:
+            self.compression = (self.scheme << 27) + (1 if self.hasOctaboxes else 0)
+            data = sstruct.pack(Glat_format_3, self)
+            encoder = self.compileAttributes3
+
+        glocs = []
+        for n in range(len(self.attributes)):
+            glocs.append(len(data))
+            data += encoder(self.attributes[ttFont.getGlyphName(n)])
+        glocs.append(len(data))
+        ttFont['Gloc'].set(glocs)
+
+        if self.version >= 3.0:
+            data = grUtils.compress(self.scheme, data)
+        return data
+
+    def compileAttributes12(self, attrs, fmt):
+        data = []
+        for e in grUtils.entries(attrs):
+            data.extend(sstruct.pack(fmt, {'attNum' : e[0], 'num' : e[1]}))
+            data.extend(struct.pack(('>%dh' % len(e[2])), *e[2]))
+        return "".join(data)
+    
+    def compileAttributes3(self, attrs):
+        if self.hasOctaboxes:
+            o = attrs.octabox
+            data = sstruct.pack(Glat_format_3_octabox_metrics, o)
+            numsub = bin(o.subboxBitmap).count("1")
+            for b in range(numsub) :
+                data += sstruct.pack(Glat_format_3_subbox_entry, o.subboxes[b])
+        else:
+            data = ""
+        return data + self.compileAttributes12(attrs, Glat_format_23_entry)
+
+    def toXML(self, writer, ttFont):
+        writer.simpletag('version', version=self.version, compressionScheme=self.scheme)
+        writer.newline()
+        for n, a in sorted(self.attributes.items(), key=lambda x:ttFont.getGlyphID(x[0])):
+            writer.begintag('glyph', name=n)
+            writer.newline()
+            if hasattr(a, 'octabox'):
+                o = a.octabox
+                formatstring, names, fixes = sstruct.getformat(Glat_format_3_octabox_metrics)
+                vals = {}
+                for k in names:
+                    if k == 'subboxBitmap': continue
+                    vals[k] = "{:.3f}%".format(getattr(o, k) * 100. / 256)
+                vals['bitmap'] = "{:0X}".format(o.subboxBitmap)
+                writer.begintag('octaboxes', **vals)
+                writer.newline()
+                formatstring, names, fixes = sstruct.getformat(Glat_format_3_subbox_entry)
+                for s in o.subboxes:
+                    vals = {}
+                    for k in names:
+                        vals[k] = "{:.3f}%".format(getattr(s, k) * 100. / 256)
+                    writer.simpletag('octabox', **vals)
+                    writer.newline()
+                writer.endtag('octaboxes')
+                writer.newline()
+            for k, v in sorted(a.items()):
+                writer.simpletag('attribute', index=k, value=v)
+                writer.newline()
+            writer.endtag('glyph')
+            writer.newline()
+
+    def fromXML(self, name, attrs, content, ttFont):
+        if name == 'version' :
+            self.version = float(safeEval(attrs['version']))
+        if name != 'glyph' : return
+        if not hasattr(self, 'attributes'):
+            self.attributes = {}
+        gname = attrs['name']
+        attributes = _Dict()
+        for element in content:
+            if not isinstance(element, tuple): continue
+            tag, attrs, subcontent = element
+            if tag == 'attribute' :
+                k = int(safeEval(attrs['index']))
+                v = int(safeEval(attrs['value']))
+                attributes[k]=v
+            elif tag == 'octaboxes':
+                self.hasOctaboxes = True
+                o = _Object()
+                o.subboxBitmap = int(attrs['bitmap'], 16)
+                o.subboxes = []
+                del attrs['bitmap']
+                for k, v in attrs.items():
+                    setattr(o, k, int(float(v[:-1]) * 256. / 100. + 0.5))
+                for element in subcontent:
+                    if not isinstance(element, tuple): continue
+                    (tag, attrs, subcontent) = element
+                    so = _Object()
+                    for k, v in attrs.items():
+                        setattr(so, k, int(float(v[:-1]) * 256. / 100. + 0.5))
+                    o.subboxes.append(so)
+                attributes.octabox = o
+        self.attributes[gname] = attributes
diff --git a/Lib/fontTools/ttLib/tables/G__l_o_c.py b/Lib/fontTools/ttLib/tables/G__l_o_c.py
new file mode 100644
index 0000000..d77c483
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/G__l_o_c.py
@@ -0,0 +1,71 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import safeEval
+from . import DefaultTable
+import array
+
+Gloc_header = '''
+    >        # big endian
+    version: 16.16F    # Table version
+    flags:        H    # bit 0: 1=long format, 0=short format
+                       # bit 1: 1=attribute names, 0=no names
+    numAttribs:   H    # NUmber of attributes
+'''
+
+class table_G__l_o_c(DefaultTable.DefaultTable):
+    """
+    Support Graphite Gloc tables
+    """
+    dependencies = ['Glat']
+
+    def __init__(self, tag=None):
+        DefaultTable.DefaultTable.__init__(self, tag)
+        self.attribIds = None
+        self.numAttribs = 0
+
+    def decompile(self, data, ttFont):
+        _, data = sstruct.unpack2(Gloc_header, data, self)
+        flags = self.flags
+        del self.flags
+        self.locations = array.array('I' if flags & 1 else 'H')
+        self.locations.fromstring(data[:len(data) - self.numAttribs * (flags & 2)])
+        self.locations.byteswap()
+        self.attribIds = array.array('H')
+        if flags & 2:
+            self.attribIds.fromstring(data[-self.numAttribs * 2:])
+            self.attribIds.byteswap()
+
+    def compile(self, ttFont):
+        data = sstruct.pack(Gloc_header, dict(version=1.0,
+                flags=(bool(self.attribIds) << 1) + (self.locations.typecode == 'I'),
+                numAttribs=self.numAttribs))
+        self.locations.byteswap()
+        data += self.locations.tostring()
+        self.locations.byteswap()
+        if self.attribIds:
+            self.attribIds.byteswap()
+            data += self.attribIds.tostring()
+            self.attribIds.byteswap()
+        return data
+
+    def set(self, locations):
+        long_format = max(locations) >= 65536
+        self.locations = array.array('I' if long_format else 'H', locations)
+
+    def toXML(self, writer, ttFont):
+        writer.simpletag("attributes", number=self.numAttribs)
+        writer.newline()
+
+    def fromXML(self, name, attrs, content, ttFont):
+        if name == 'attributes':
+            self.numAttribs = int(safeEval(attrs['number']))
+
+    def __getitem__(self, index):
+        return self.locations[index]
+
+    def __len__(self):
+        return len(self.locations)
+
+    def __iter__(self):
+        return iter(self.locations)
diff --git a/Lib/fontTools/ttLib/tables/H_V_A_R_.py b/Lib/fontTools/ttLib/tables/H_V_A_R_.py
new file mode 100644
index 0000000..efab4e7
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/H_V_A_R_.py
@@ -0,0 +1,7 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from .otBase import BaseTTXConverter
+
+
+class table_H_V_A_R_(BaseTTXConverter):
+	pass
diff --git a/Lib/fontTools/ttLib/tables/J_S_T_F_.py b/Lib/fontTools/ttLib/tables/J_S_T_F_.py
index ddf5405..dffd08b 100644
--- a/Lib/fontTools/ttLib/tables/J_S_T_F_.py
+++ b/Lib/fontTools/ttLib/tables/J_S_T_F_.py
@@ -1,3 +1,5 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
 from .otBase import BaseTTXConverter
 
 
diff --git a/Lib/fontTools/ttLib/tables/L_T_S_H_.py b/Lib/fontTools/ttLib/tables/L_T_S_H_.py
index de79236..dd0f195 100644
--- a/Lib/fontTools/ttLib/tables/L_T_S_H_.py
+++ b/Lib/fontTools/ttLib/tables/L_T_S_H_.py
@@ -10,7 +10,7 @@
 # XXX back to normal eventually.
 
 class table_L_T_S_H_(DefaultTable.DefaultTable):
-	
+
 	def decompile(self, data, ttFont):
 		version, numGlyphs = struct.unpack(">HH", data[:4])
 		data = data[4:]
@@ -23,7 +23,7 @@
 		self.yPels = {}
 		for i in range(numGlyphs):
 			self.yPels[ttFont.getGlyphName(i)] = yPels[i]
-	
+
 	def compile(self, ttFont):
 		version = 0
 		names = list(self.yPels.keys())
@@ -35,17 +35,16 @@
 			yPels[ttFont.getGlyphID(name)] = self.yPels[name]
 		yPels = array.array("B", yPels)
 		return struct.pack(">HH", version, numGlyphs) + yPels.tostring()
-	
+
 	def toXML(self, writer, ttFont):
 		names = sorted(self.yPels.keys())
 		for name in names:
 			writer.simpletag("yPel", name=name, value=self.yPels[name])
 			writer.newline()
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		if not hasattr(self, "yPels"):
 			self.yPels = {}
 		if name != "yPel":
 			return # ignore unknown tags
 		self.yPels[attrs["name"]] = safeEval(attrs["value"])
-
diff --git a/Lib/fontTools/ttLib/tables/M_A_T_H_.py b/Lib/fontTools/ttLib/tables/M_A_T_H_.py
index d894c08..8c329ba 100644
--- a/Lib/fontTools/ttLib/tables/M_A_T_H_.py
+++ b/Lib/fontTools/ttLib/tables/M_A_T_H_.py
@@ -1,3 +1,5 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
 from .otBase import BaseTTXConverter
 
 
diff --git a/Lib/fontTools/ttLib/tables/M_E_T_A_.py b/Lib/fontTools/ttLib/tables/M_E_T_A_.py
index 60214e8..3eb6550 100644
--- a/Lib/fontTools/ttLib/tables/M_E_T_A_.py
+++ b/Lib/fontTools/ttLib/tables/M_E_T_A_.py
@@ -3,6 +3,7 @@
 from fontTools.misc import sstruct
 from fontTools.misc.textTools import safeEval
 from . import DefaultTable
+import pdb
 import struct
 
 
@@ -26,19 +27,19 @@
 		nMetaEntry:			H
 """
 # This record is followd by a variable data length field:
-# 	USHORT or ULONG	hdrOffset	
+# 	USHORT or ULONG	hdrOffset
 # Offset from start of META table to the beginning
 # of this glyphs array of ns Metadata string entries.
-# Size determined by metaFlags field		
+# Size determined by metaFlags field
 # METAGlyphRecordFormat entries must be sorted by glyph ID
- 
+
 METAStringRecordFormat = """
 		>	# big endian
 		labelID:			H
 		stringLen:			H
 """
 # This record is followd by a variable data length field:
-# 	USHORT or ULONG	stringOffset	
+# 	USHORT or ULONG	stringOffset
 # METAStringRecordFormat entries must be sorted in order of labelID
 # There may be more than one entry with the same labelID
 # There may be more than one strign with the same content.
@@ -46,17 +47,17 @@
 # Strings shall be Unicode UTF-8 encoded, and null-terminated.
 
 METALabelDict = {
-	0 : "MojikumiX4051", # An integer in the range 1-20
-	1 : "UNIUnifiedBaseChars",
-	2 : "BaseFontName",
-	3 : "Language",
-	4 : "CreationDate",
-	5 : "FoundryName",
-	6 : "FoundryCopyright",
-	7 : "OwnerURI",
-	8 : "WritingScript",
-	10 : "StrokeCount",
-	11 : "IndexingRadical",
+	0: "MojikumiX4051", # An integer in the range 1-20
+	1: "UNIUnifiedBaseChars",
+	2: "BaseFontName",
+	3: "Language",
+	4: "CreationDate",
+	5: "FoundryName",
+	6: "FoundryCopyright",
+	7: "OwnerURI",
+	8: "WritingScript",
+	10: "StrokeCount",
+	11: "IndexingRadical",
 }
 
 
@@ -69,9 +70,9 @@
 
 
 class table_M_E_T_A_(DefaultTable.DefaultTable):
-	
+
 	dependencies = []
-	
+
 	def decompile(self, data, ttFont):
 		dummy, newData = sstruct.unpack2(METAHeaderFormat, data, self)
 		self.glyphRecords = []
@@ -97,16 +98,16 @@
 					newData = newData[4:]
 				stringRec.string = data[stringRec.offset:stringRec.offset + stringRec.stringLen]
 				glyphRecord.stringRecs.append(stringRec)
-			self.glyphRecords.append(glyphRecord)	
-			
+			self.glyphRecords.append(glyphRecord)
+
 	def compile(self, ttFont):
 		offsetOK = 0
 		self.nMetaRecs = len(self.glyphRecords)
 		count = 0
-		while ( offsetOK != 1):
+		while (offsetOK != 1):
 			count = count + 1
 			if count > 4:
-				pdb_set_trace()
+				pdb.set_trace()
 			metaData = sstruct.pack(METAHeaderFormat, self)
 			stringRecsOffset = len(metaData) + self.nMetaRecs * (6 + 2*(self.metaFlags & 1))
 			stringRecSize = (6 + 2*(self.metaFlags & 1))
@@ -117,12 +118,12 @@
 					offsetOK = -1
 					break
 				metaData = metaData + glyphRec.compile(self)
-				stringRecsOffset = stringRecsOffset + (glyphRec.nMetaEntry * stringRecSize) 
+				stringRecsOffset = stringRecsOffset + (glyphRec.nMetaEntry * stringRecSize)
 				# this will be the String Record offset for the next GlyphRecord.
-			if 	offsetOK == -1:
+			if offsetOK == -1:
 				offsetOK = 0
 				continue
-			
+
 			# metaData now contains the header and all of the GlyphRecords. Its length should bw
 			# the offset to the first StringRecord.
 			stringOffset = stringRecsOffset
@@ -139,23 +140,22 @@
 			if 	offsetOK == -1:
 				offsetOK = 0
 				continue
-				
+
 			if ((self.metaFlags & 1) == 1) and (stringOffset < 65536):
 				self.metaFlags = self.metaFlags - 1
 				continue
 			else:
 				offsetOK = 1
-					
-								
+
 			# metaData now contains the header and all of the GlyphRecords and all of the String Records.
 			# Its length should be the offset to the first string datum.
 			for glyphRec in self.glyphRecords:
 				for stringRec in glyphRec.stringRecs:
 					assert (stringRec.offset == len(metaData)), "String offset did not compile correctly! for string:" + str(stringRec.string)
 					metaData = metaData + stringRec.string
-		
+
 		return metaData
-	
+
 	def toXML(self, writer, ttFont):
 		writer.comment("Lengths and number of entries in this table will be recalculated by the compiler")
 		writer.newline()
@@ -166,7 +166,7 @@
 			writer.newline()
 		for glyphRec in self.glyphRecords:
 			glyphRec.toXML(writer, ttFont)
-		
+
 	def fromXML(self, name, attrs, content, ttFont):
 		if name == "GlyphRecord":
 			if not hasattr(self, "glyphRecords"):
@@ -180,7 +180,7 @@
 				glyphRec.fromXML(name, attrs, content, ttFont)
 			glyphRec.offset = -1
 			glyphRec.nMetaEntry = len(glyphRec.stringRecs)
-		else:			
+		else:
 			setattr(self, name, safeEval(attrs["value"]))
 
 
@@ -190,7 +190,7 @@
 		self.nMetaEntry = -1
 		self.offset = -1
 		self.stringRecs = []
-		
+
 	def toXML(self, writer, ttFont):
 		writer.begintag("GlyphRecord")
 		writer.newline()
@@ -203,7 +203,6 @@
 		writer.endtag("GlyphRecord")
 		writer.newline()
 
-
 	def fromXML(self, name, attrs, content, ttFont):
 		if name == "StringRecord":
 			stringRec = StringRecord()
@@ -213,7 +212,7 @@
 					continue
 				stringRec.fromXML(name, attrs, content, ttFont)
 			stringRec.stringLen = len(stringRec.string)
-		else:			
+		else:
 			setattr(self, name, safeEval(attrs["value"]))
 
 	def compile(self, parentTable):
@@ -224,7 +223,7 @@
 			datum = struct.pack(">L", self.offset)
 		data = data + datum
 		return data
-	
+
 	def __repr__(self):
 		return "GlyphRecord[ glyphID: " + str(self.glyphID) + ", nMetaEntry: " + str(self.nMetaEntry) + ", offset: " + str(self.offset) + " ]"
 
@@ -246,17 +245,17 @@
 			while string[i] != ";":
 				i = i+1
 			valStr = string[j:i]
-			
+
 			uString = uString + unichr(eval('0x' + valStr))
 		else:
 			uString = uString + unichr(byteord(string[i]))
 		i = i +1
-			
-	return uString.encode('utf8')
+
+	return uString.encode('utf_8')
 
 
 def mapUTF8toXML(string):
-	uString = string.decode('utf8')
+	uString = string.decode('utf_8')
 	string = ""
 	for uChar in uString:
 		i = ord(uChar)
@@ -300,8 +299,7 @@
 			datum = struct.pack(">L", self.offset)
 		data = data + datum
 		return data
-	
+
 	def __repr__(self):
 		return "StringRecord [ labelID: " + str(self.labelID) + " aka " + getLabelString(self.labelID) \
 			+ ", offset: " + str(self.offset) + ", length: " + str(self.stringLen) + ", string: " +self.string + " ]"
-
diff --git a/Lib/fontTools/ttLib/tables/M_V_A_R_.py b/Lib/fontTools/ttLib/tables/M_V_A_R_.py
new file mode 100644
index 0000000..8659ae8
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/M_V_A_R_.py
@@ -0,0 +1,7 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from .otBase import BaseTTXConverter
+
+
+class table_M_V_A_R_(BaseTTXConverter):
+	pass
diff --git a/Lib/fontTools/ttLib/tables/O_S_2f_2.py b/Lib/fontTools/ttLib/tables/O_S_2f_2.py
index d29212f..3e2c30c 100644
--- a/Lib/fontTools/ttLib/tables/O_S_2f_2.py
+++ b/Lib/fontTools/ttLib/tables/O_S_2f_2.py
@@ -2,10 +2,12 @@
 from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
 from fontTools.misc.textTools import safeEval, num2binary, binary2num
-from . import DefaultTable
-import warnings
+from fontTools.ttLib.tables import DefaultTable
+import logging
 
 
+log = logging.getLogger(__name__)
+
 # panose classification
 
 panoseFormat = """
@@ -22,13 +24,13 @@
 """
 
 class Panose(object):
-	
+
 	def toXML(self, writer, ttFont):
 		formatstring, names, fixes = sstruct.getformat(panoseFormat)
 		for name in names:
 			writer.simpletag(name, value=getattr(self, name))
 			writer.newline()
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		setattr(self, name, safeEval(attrs["value"]))
 
@@ -60,8 +62,8 @@
 	ulUnicodeRange4:        L       # character range
 	achVendID:              4s      # font vendor identification
 	fsSelection:            H       # font selection flags
-	fsFirstCharIndex:       H       # first unicode character index
-	fsLastCharIndex:        H       # last unicode character index
+	usFirstCharIndex:       H       # first unicode character index
+	usLastCharIndex:        H       # last unicode character index
 	sTypoAscender:          h       # typographic ascender
 	sTypoDescender:         h       # typographic descender
 	sTypoLineGap:           h       # typographic line gap
@@ -79,7 +81,7 @@
 	sCapHeight:         h
 	usDefaultChar:      H
 	usBreakChar:        H
-	usMaxContex:        H
+	usMaxContext:       H
 """
 
 OS2_format_5_addition =  OS2_format_2_addition + """
@@ -98,9 +100,11 @@
 
 
 class table_O_S_2f_2(DefaultTable.DefaultTable):
-	
+
 	"""the OS/2 table"""
-	
+
+	dependencies = ["head"]
+
 	def decompile(self, data, ttFont):
 		dummy, data = sstruct.unpack2(OS2_format_0, data, self)
 
@@ -116,12 +120,26 @@
 			from fontTools import ttLib
 			raise ttLib.TTLibError("unknown format for OS/2 table: version %s" % self.version)
 		if len(data):
-			warnings.warn("too much 'OS/2' table data")
+			log.warning("too much 'OS/2' table data")
 
 		self.panose = sstruct.unpack(panoseFormat, self.panose, Panose())
-	
+
 	def compile(self, ttFont):
+		self.updateFirstAndLastCharIndex(ttFont)
 		panose = self.panose
+		head = ttFont["head"]
+		if (self.fsSelection & 1) and not (head.macStyle & 1<<1):
+			log.warning("fsSelection bit 0 (italic) and "
+				"head table macStyle bit 1 (italic) should match")
+		if (self.fsSelection & 1<<5) and not (head.macStyle & 1):
+			log.warning("fsSelection bit 5 (bold) and "
+				"head table macStyle bit 0 (bold) should match")
+		if (self.fsSelection & 1<<6) and (self.fsSelection & 1 + (1<<5)):
+			log.warning("fsSelection bit 6 (regular) is set, "
+				"bits 0 (italic) and 5 (bold) must be clear")
+		if self.version < 4 and self.fsSelection & 0b1110000000:
+			log.warning("fsSelection bits 7, 8 and 9 are only defined in "
+				"OS/2 table version 4 and up: version %s", self.version)
 		self.panose = sstruct.pack(panoseFormat, self.panose)
 		if self.version == 0:
 			data = sstruct.pack(OS2_format_0, self)
@@ -131,16 +149,20 @@
 			data = sstruct.pack(OS2_format_2, self)
 		elif self.version == 5:
 			d = self.__dict__.copy()
-			d['usLowerOpticalPointSize'] = int(round(self.usLowerOpticalPointSize * 20))
-			d['usUpperOpticalPointSize'] = int(round(self.usUpperOpticalPointSize * 20))
+			d['usLowerOpticalPointSize'] = round(self.usLowerOpticalPointSize * 20)
+			d['usUpperOpticalPointSize'] = round(self.usUpperOpticalPointSize * 20)
 			data = sstruct.pack(OS2_format_5, d)
 		else:
 			from fontTools import ttLib
 			raise ttLib.TTLibError("unknown format for OS/2 table: version %s" % self.version)
 		self.panose = panose
 		return data
-	
+
 	def toXML(self, writer, ttFont):
+		writer.comment(
+			"The fields 'usFirstCharIndex' and 'usLastCharIndex'\n"
+			"will be recalculated by the compiler")
+		writer.newline()
 		if self.version == 1:
 			format = OS2_format_1
 		elif self.version in (2, 3, 4):
@@ -157,7 +179,7 @@
 				writer.newline()
 				value.toXML(writer, ttFont)
 				writer.endtag("panose")
-			elif name in ("ulUnicodeRange1", "ulUnicodeRange2", 
+			elif name in ("ulUnicodeRange1", "ulUnicodeRange2",
 					"ulUnicodeRange3", "ulUnicodeRange4",
 					"ulCodePageRange1", "ulCodePageRange2"):
 				writer.simpletag(name, value=num2binary(value))
@@ -168,7 +190,7 @@
 			else:
 				writer.simpletag(name, value=value)
 			writer.newline()
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		if name == "panose":
 			self.panose = panose = Panose()
@@ -176,7 +198,7 @@
 				if isinstance(element, tuple):
 					name, attrs, content = element
 					panose.fromXML(name, attrs, content, ttFont)
-		elif name in ("ulUnicodeRange1", "ulUnicodeRange2", 
+		elif name in ("ulUnicodeRange1", "ulUnicodeRange2",
 				"ulUnicodeRange3", "ulUnicodeRange4",
 				"ulCodePageRange1", "ulCodePageRange2",
 				"fsType", "fsSelection"):
@@ -186,4 +208,314 @@
 		else:
 			setattr(self, name, safeEval(attrs["value"]))
 
+	def updateFirstAndLastCharIndex(self, ttFont):
+		if 'cmap' not in ttFont:
+			return
+		codes = set()
+		for table in getattr(ttFont['cmap'], 'tables', []):
+			if table.isUnicode():
+				codes.update(table.cmap.keys())
+		if codes:
+			minCode = min(codes)
+			maxCode = max(codes)
+			# USHORT cannot hold codepoints greater than 0xFFFF
+			self.usFirstCharIndex = min(0xFFFF, minCode)
+			self.usLastCharIndex = min(0xFFFF, maxCode)
 
+	# misspelled attributes kept for legacy reasons
+
+	@property
+	def usMaxContex(self):
+		return self.usMaxContext
+
+	@usMaxContex.setter
+	def usMaxContex(self, value):
+		self.usMaxContext = value
+
+	@property
+	def fsFirstCharIndex(self):
+		return self.usFirstCharIndex
+
+	@fsFirstCharIndex.setter
+	def fsFirstCharIndex(self, value):
+		self.usFirstCharIndex = value
+
+	@property
+	def fsLastCharIndex(self):
+		return self.usLastCharIndex
+
+	@fsLastCharIndex.setter
+	def fsLastCharIndex(self, value):
+		self.usLastCharIndex = value
+
+	def getUnicodeRanges(self):
+		""" Return the set of 'ulUnicodeRange*' bits currently enabled. """
+		bits = set()
+		ul1, ul2 = self.ulUnicodeRange1, self.ulUnicodeRange2
+		ul3, ul4 = self.ulUnicodeRange3, self.ulUnicodeRange4
+		for i in range(32):
+			if ul1 & (1 << i):
+				bits.add(i)
+			if ul2 & (1 << i):
+				bits.add(i + 32)
+			if ul3 & (1 << i):
+				bits.add(i + 64)
+			if ul4 & (1 << i):
+				bits.add(i + 96)
+		return bits
+
+	def setUnicodeRanges(self, bits):
+		""" Set the 'ulUnicodeRange*' fields to the specified 'bits'. """
+		ul1, ul2, ul3, ul4 = 0, 0, 0, 0
+		for bit in bits:
+			if 0 <= bit < 32:
+				ul1 |= (1 << bit)
+			elif 32 <= bit < 64:
+				ul2 |= (1 << (bit - 32))
+			elif 64 <= bit < 96:
+				ul3 |= (1 << (bit - 64))
+			elif 96 <= bit < 123:
+				ul4 |= (1 << (bit - 96))
+			else:
+				raise ValueError('expected 0 <= int <= 122, found: %r' % bit)
+		self.ulUnicodeRange1, self.ulUnicodeRange2 = ul1, ul2
+		self.ulUnicodeRange3, self.ulUnicodeRange4 = ul3, ul4
+
+	def recalcUnicodeRanges(self, ttFont, pruneOnly=False):
+		""" Intersect the codepoints in the font's Unicode cmap subtables with
+		the Unicode block ranges defined in the OpenType specification (v1.7),
+		and set the respective 'ulUnicodeRange*' bits if there is at least ONE
+		intersection.
+		If 'pruneOnly' is True, only clear unused bits with NO intersection.
+		"""
+		unicodes = set()
+		for table in ttFont['cmap'].tables:
+			if table.isUnicode():
+				unicodes.update(table.cmap.keys())
+		if pruneOnly:
+			empty = intersectUnicodeRanges(unicodes, inverse=True)
+			bits = self.getUnicodeRanges() - empty
+		else:
+			bits = intersectUnicodeRanges(unicodes)
+		self.setUnicodeRanges(bits)
+		return bits
+
+
+# Unicode ranges data from the OpenType OS/2 table specification v1.7
+
+OS2_UNICODE_RANGES = (
+	(('Basic Latin',                              (0x0000, 0x007F)),),
+	(('Latin-1 Supplement',                       (0x0080, 0x00FF)),),
+	(('Latin Extended-A',                         (0x0100, 0x017F)),),
+	(('Latin Extended-B',                         (0x0180, 0x024F)),),
+	(('IPA Extensions',                           (0x0250, 0x02AF)),
+	 ('Phonetic Extensions',                      (0x1D00, 0x1D7F)),
+	 ('Phonetic Extensions Supplement',           (0x1D80, 0x1DBF))),
+	(('Spacing Modifier Letters',                 (0x02B0, 0x02FF)),
+	 ('Modifier Tone Letters',                    (0xA700, 0xA71F))),
+	(('Combining Diacritical Marks',              (0x0300, 0x036F)),
+	 ('Combining Diacritical Marks Supplement',   (0x1DC0, 0x1DFF))),
+	(('Greek and Coptic',                         (0x0370, 0x03FF)),),
+	(('Coptic',                                   (0x2C80, 0x2CFF)),),
+	(('Cyrillic',                                 (0x0400, 0x04FF)),
+	 ('Cyrillic Supplement',                      (0x0500, 0x052F)),
+	 ('Cyrillic Extended-A',                      (0x2DE0, 0x2DFF)),
+	 ('Cyrillic Extended-B',                      (0xA640, 0xA69F))),
+	(('Armenian',                                 (0x0530, 0x058F)),),
+	(('Hebrew',                                   (0x0590, 0x05FF)),),
+	(('Vai',                                      (0xA500, 0xA63F)),),
+	(('Arabic',                                   (0x0600, 0x06FF)),
+	 ('Arabic Supplement',                        (0x0750, 0x077F))),
+	(('NKo',                                      (0x07C0, 0x07FF)),),
+	(('Devanagari',                               (0x0900, 0x097F)),),
+	(('Bengali',                                  (0x0980, 0x09FF)),),
+	(('Gurmukhi',                                 (0x0A00, 0x0A7F)),),
+	(('Gujarati',                                 (0x0A80, 0x0AFF)),),
+	(('Oriya',                                    (0x0B00, 0x0B7F)),),
+	(('Tamil',                                    (0x0B80, 0x0BFF)),),
+	(('Telugu',                                   (0x0C00, 0x0C7F)),),
+	(('Kannada',                                  (0x0C80, 0x0CFF)),),
+	(('Malayalam',                                (0x0D00, 0x0D7F)),),
+	(('Thai',                                     (0x0E00, 0x0E7F)),),
+	(('Lao',                                      (0x0E80, 0x0EFF)),),
+	(('Georgian',                                 (0x10A0, 0x10FF)),
+	 ('Georgian Supplement',                      (0x2D00, 0x2D2F))),
+	(('Balinese',                                 (0x1B00, 0x1B7F)),),
+	(('Hangul Jamo',                              (0x1100, 0x11FF)),),
+	(('Latin Extended Additional',                (0x1E00, 0x1EFF)),
+	 ('Latin Extended-C',                         (0x2C60, 0x2C7F)),
+	 ('Latin Extended-D',                         (0xA720, 0xA7FF))),
+	(('Greek Extended',                           (0x1F00, 0x1FFF)),),
+	(('General Punctuation',                      (0x2000, 0x206F)),
+	 ('Supplemental Punctuation',                 (0x2E00, 0x2E7F))),
+	(('Superscripts And Subscripts',              (0x2070, 0x209F)),),
+	(('Currency Symbols',                         (0x20A0, 0x20CF)),),
+	(('Combining Diacritical Marks For Symbols',  (0x20D0, 0x20FF)),),
+	(('Letterlike Symbols',                       (0x2100, 0x214F)),),
+	(('Number Forms',                             (0x2150, 0x218F)),),
+	(('Arrows',                                   (0x2190, 0x21FF)),
+	 ('Supplemental Arrows-A',                    (0x27F0, 0x27FF)),
+	 ('Supplemental Arrows-B',                    (0x2900, 0x297F)),
+	 ('Miscellaneous Symbols and Arrows',         (0x2B00, 0x2BFF))),
+	(('Mathematical Operators',                   (0x2200, 0x22FF)),
+	 ('Supplemental Mathematical Operators',      (0x2A00, 0x2AFF)),
+	 ('Miscellaneous Mathematical Symbols-A',     (0x27C0, 0x27EF)),
+	 ('Miscellaneous Mathematical Symbols-B',     (0x2980, 0x29FF))),
+	(('Miscellaneous Technical',                  (0x2300, 0x23FF)),),
+	(('Control Pictures',                         (0x2400, 0x243F)),),
+	(('Optical Character Recognition',            (0x2440, 0x245F)),),
+	(('Enclosed Alphanumerics',                   (0x2460, 0x24FF)),),
+	(('Box Drawing',                              (0x2500, 0x257F)),),
+	(('Block Elements',                           (0x2580, 0x259F)),),
+	(('Geometric Shapes',                         (0x25A0, 0x25FF)),),
+	(('Miscellaneous Symbols',                    (0x2600, 0x26FF)),),
+	(('Dingbats',                                 (0x2700, 0x27BF)),),
+	(('CJK Symbols And Punctuation',              (0x3000, 0x303F)),),
+	(('Hiragana',                                 (0x3040, 0x309F)),),
+	(('Katakana',                                 (0x30A0, 0x30FF)),
+	 ('Katakana Phonetic Extensions',             (0x31F0, 0x31FF))),
+	(('Bopomofo',                                 (0x3100, 0x312F)),
+	 ('Bopomofo Extended',                        (0x31A0, 0x31BF))),
+	(('Hangul Compatibility Jamo',                (0x3130, 0x318F)),),
+	(('Phags-pa',                                 (0xA840, 0xA87F)),),
+	(('Enclosed CJK Letters And Months',          (0x3200, 0x32FF)),),
+	(('CJK Compatibility',                        (0x3300, 0x33FF)),),
+	(('Hangul Syllables',                         (0xAC00, 0xD7AF)),),
+	(('Non-Plane 0 *',                            (0xD800, 0xDFFF)),),
+	(('Phoenician',                               (0x10900, 0x1091F)),),
+	(('CJK Unified Ideographs',                   (0x4E00, 0x9FFF)),
+	 ('CJK Radicals Supplement',                  (0x2E80, 0x2EFF)),
+	 ('Kangxi Radicals',                          (0x2F00, 0x2FDF)),
+	 ('Ideographic Description Characters',       (0x2FF0, 0x2FFF)),
+	 ('CJK Unified Ideographs Extension A',       (0x3400, 0x4DBF)),
+	 ('CJK Unified Ideographs Extension B',       (0x20000, 0x2A6DF)),
+	 ('Kanbun',                                   (0x3190, 0x319F))),
+	(('Private Use Area (plane 0)',               (0xE000, 0xF8FF)),),
+	(('CJK Strokes',                              (0x31C0, 0x31EF)),
+	 ('CJK Compatibility Ideographs',             (0xF900, 0xFAFF)),
+	 ('CJK Compatibility Ideographs Supplement',  (0x2F800, 0x2FA1F))),
+	(('Alphabetic Presentation Forms',            (0xFB00, 0xFB4F)),),
+	(('Arabic Presentation Forms-A',              (0xFB50, 0xFDFF)),),
+	(('Combining Half Marks',                     (0xFE20, 0xFE2F)),),
+	(('Vertical Forms',                           (0xFE10, 0xFE1F)),
+	 ('CJK Compatibility Forms',                  (0xFE30, 0xFE4F))),
+	(('Small Form Variants',                      (0xFE50, 0xFE6F)),),
+	(('Arabic Presentation Forms-B',              (0xFE70, 0xFEFF)),),
+	(('Halfwidth And Fullwidth Forms',            (0xFF00, 0xFFEF)),),
+	(('Specials',                                 (0xFFF0, 0xFFFF)),),
+	(('Tibetan',                                  (0x0F00, 0x0FFF)),),
+	(('Syriac',                                   (0x0700, 0x074F)),),
+	(('Thaana',                                   (0x0780, 0x07BF)),),
+	(('Sinhala',                                  (0x0D80, 0x0DFF)),),
+	(('Myanmar',                                  (0x1000, 0x109F)),),
+	(('Ethiopic',                                 (0x1200, 0x137F)),
+	 ('Ethiopic Supplement',                      (0x1380, 0x139F)),
+	 ('Ethiopic Extended',                        (0x2D80, 0x2DDF))),
+	(('Cherokee',                                 (0x13A0, 0x13FF)),),
+	(('Unified Canadian Aboriginal Syllabics',    (0x1400, 0x167F)),),
+	(('Ogham',                                    (0x1680, 0x169F)),),
+	(('Runic',                                    (0x16A0, 0x16FF)),),
+	(('Khmer',                                    (0x1780, 0x17FF)),
+	 ('Khmer Symbols',                            (0x19E0, 0x19FF))),
+	(('Mongolian',                                (0x1800, 0x18AF)),),
+	(('Braille Patterns',                         (0x2800, 0x28FF)),),
+	(('Yi Syllables',                             (0xA000, 0xA48F)),
+	 ('Yi Radicals',                              (0xA490, 0xA4CF))),
+	(('Tagalog',                                  (0x1700, 0x171F)),
+	 ('Hanunoo',                                  (0x1720, 0x173F)),
+	 ('Buhid',                                    (0x1740, 0x175F)),
+	 ('Tagbanwa',                                 (0x1760, 0x177F))),
+	(('Old Italic',                               (0x10300, 0x1032F)),),
+	(('Gothic',                                   (0x10330, 0x1034F)),),
+	(('Deseret',                                  (0x10400, 0x1044F)),),
+	(('Byzantine Musical Symbols',                (0x1D000, 0x1D0FF)),
+	 ('Musical Symbols',                          (0x1D100, 0x1D1FF)),
+	 ('Ancient Greek Musical Notation',           (0x1D200, 0x1D24F))),
+	(('Mathematical Alphanumeric Symbols',        (0x1D400, 0x1D7FF)),),
+	(('Private Use (plane 15)',                   (0xF0000, 0xFFFFD)),
+	 ('Private Use (plane 16)',                   (0x100000, 0x10FFFD))),
+	(('Variation Selectors',                      (0xFE00, 0xFE0F)),
+	 ('Variation Selectors Supplement',           (0xE0100, 0xE01EF))),
+	(('Tags',                                     (0xE0000, 0xE007F)),),
+	(('Limbu',                                    (0x1900, 0x194F)),),
+	(('Tai Le',                                   (0x1950, 0x197F)),),
+	(('New Tai Lue',                              (0x1980, 0x19DF)),),
+	(('Buginese',                                 (0x1A00, 0x1A1F)),),
+	(('Glagolitic',                               (0x2C00, 0x2C5F)),),
+	(('Tifinagh',                                 (0x2D30, 0x2D7F)),),
+	(('Yijing Hexagram Symbols',                  (0x4DC0, 0x4DFF)),),
+	(('Syloti Nagri',                             (0xA800, 0xA82F)),),
+	(('Linear B Syllabary',                       (0x10000, 0x1007F)),
+	 ('Linear B Ideograms',                       (0x10080, 0x100FF)),
+	 ('Aegean Numbers',                           (0x10100, 0x1013F))),
+	(('Ancient Greek Numbers',                    (0x10140, 0x1018F)),),
+	(('Ugaritic',                                 (0x10380, 0x1039F)),),
+	(('Old Persian',                              (0x103A0, 0x103DF)),),
+	(('Shavian',                                  (0x10450, 0x1047F)),),
+	(('Osmanya',                                  (0x10480, 0x104AF)),),
+	(('Cypriot Syllabary',                        (0x10800, 0x1083F)),),
+	(('Kharoshthi',                               (0x10A00, 0x10A5F)),),
+	(('Tai Xuan Jing Symbols',                    (0x1D300, 0x1D35F)),),
+	(('Cuneiform',                                (0x12000, 0x123FF)),
+	 ('Cuneiform Numbers and Punctuation',        (0x12400, 0x1247F))),
+	(('Counting Rod Numerals',                    (0x1D360, 0x1D37F)),),
+	(('Sundanese',                                (0x1B80, 0x1BBF)),),
+	(('Lepcha',                                   (0x1C00, 0x1C4F)),),
+	(('Ol Chiki',                                 (0x1C50, 0x1C7F)),),
+	(('Saurashtra',                               (0xA880, 0xA8DF)),),
+	(('Kayah Li',                                 (0xA900, 0xA92F)),),
+	(('Rejang',                                   (0xA930, 0xA95F)),),
+	(('Cham',                                     (0xAA00, 0xAA5F)),),
+	(('Ancient Symbols',                          (0x10190, 0x101CF)),),
+	(('Phaistos Disc',                            (0x101D0, 0x101FF)),),
+	(('Carian',                                   (0x102A0, 0x102DF)),
+	 ('Lycian',                                   (0x10280, 0x1029F)),
+	 ('Lydian',                                   (0x10920, 0x1093F))),
+	(('Domino Tiles',                             (0x1F030, 0x1F09F)),
+	 ('Mahjong Tiles',                            (0x1F000, 0x1F02F))),
+)
+
+
+_unicodeRangeSets = []
+
+def _getUnicodeRangeSets():
+	# build the sets of codepoints for each unicode range bit, and cache result
+	if not _unicodeRangeSets:
+		for bit, blocks in enumerate(OS2_UNICODE_RANGES):
+			rangeset = set()
+			for _, (start, stop) in blocks:
+				rangeset.update(set(range(start, stop+1)))
+			if bit == 57:
+				# The spec says that bit 57 ("Non Plane 0") implies that there's
+				# at least one codepoint beyond the BMP; so I also include all
+				# the non-BMP codepoints here
+				rangeset.update(set(range(0x10000, 0x110000)))
+			_unicodeRangeSets.append(rangeset)
+	return _unicodeRangeSets
+
+
+def intersectUnicodeRanges(unicodes, inverse=False):
+	""" Intersect a sequence of (int) Unicode codepoints with the Unicode block
+	ranges defined in the OpenType specification v1.7, and return the set of
+	'ulUnicodeRanges' bits for which there is at least ONE intersection.
+	If 'inverse' is True, return the the bits for which there is NO intersection.
+
+	>>> intersectUnicodeRanges([0x0410]) == {9}
+	True
+	>>> intersectUnicodeRanges([0x0410, 0x1F000]) == {9, 57, 122}
+	True
+	>>> intersectUnicodeRanges([0x0410, 0x1F000], inverse=True) == (
+	...     set(range(123)) - {9, 57, 122})
+	True
+	"""
+	unicodes = set(unicodes)
+	uniranges = _getUnicodeRangeSets()
+	bits = set([
+		bit for bit, unirange in enumerate(uniranges)
+		if not unirange.isdisjoint(unicodes) ^ inverse])
+	return bits
+
+
+if __name__ == "__main__":
+	import doctest, sys
+	sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/ttLib/tables/S_I_N_G_.py b/Lib/fontTools/ttLib/tables/S_I_N_G_.py
index d9177e0..413ae48 100644
--- a/Lib/fontTools/ttLib/tables/S_I_N_G_.py
+++ b/Lib/fontTools/ttLib/tables/S_I_N_G_.py
@@ -19,26 +19,25 @@
 		nameLength:			1s
 """
 # baseGlyphName is a byte string which follows the record above.
-		
 
 
 class table_S_I_N_G_(DefaultTable.DefaultTable):
-	
+
 	dependencies = []
-	
+
 	def decompile(self, data, ttFont):
 		dummy, rest = sstruct.unpack2(SINGFormat, data, self)
 		self.uniqueName = self.decompileUniqueName(self.uniqueName)
 		self.nameLength = byteord(self.nameLength)
 		assert len(rest) == self.nameLength
 		self.baseGlyphName = tostr(rest)
-		
+
 		rawMETAMD5 = self.METAMD5
 		self.METAMD5 = "[" + hex(byteord(self.METAMD5[0]))
 		for char in rawMETAMD5[1:]:
 			self.METAMD5 = self.METAMD5 + ", " + hex(byteord(char))
 		self.METAMD5 = self.METAMD5 + "]"
-		
+
 	def decompileUniqueName(self, data):
 		name = ""
 		for char in data:
@@ -55,8 +54,7 @@
 					octString.zfill(3)
 				name += "\\" + octString
 		return name
-		
-		
+
 	def compile(self, ttFont):
 		d = self.__dict__.copy()
 		d["nameLength"] = bytechr(len(self.baseGlyphName))
@@ -69,7 +67,7 @@
 		data = sstruct.pack(SINGFormat, d)
 		data = data + tobytes(self.baseGlyphName)
 		return data
-	
+
 	def compilecompileUniqueName(self, name, length):
 		nameLen = len(name)
 		if length <= nameLen:
@@ -78,7 +76,6 @@
 			name += (nameLen - length) * "\000"
 		return name
 
-
 	def toXML(self, writer, ttFont):
 		writer.comment("Most of this table will be recalculated by the compiler")
 		writer.newline()
@@ -89,7 +86,7 @@
 			writer.newline()
 		writer.simpletag("baseGlyphName", value=self.baseGlyphName)
 		writer.newline()
-		
+
 	def fromXML(self, name, attrs, content, ttFont):
 		value = attrs["value"]
 		if name in ["uniqueName", "METAMD5", "baseGlyphName"]:
diff --git a/Lib/fontTools/ttLib/tables/S_T_A_T_.py b/Lib/fontTools/ttLib/tables/S_T_A_T_.py
new file mode 100644
index 0000000..1e044cf
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/S_T_A_T_.py
@@ -0,0 +1,7 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from .otBase import BaseTTXConverter
+
+
+class table_S_T_A_T_(BaseTTXConverter):
+    pass
diff --git a/Lib/fontTools/ttLib/tables/S_V_G_.py b/Lib/fontTools/ttLib/tables/S_V_G_.py
index c3f00dd..b061153 100644
--- a/Lib/fontTools/ttLib/tables/S_V_G_.py
+++ b/Lib/fontTools/ttLib/tables/S_V_G_.py
@@ -3,11 +3,16 @@
 from fontTools.misc import sstruct
 from . import DefaultTable
 try:
-    import xml.etree.cElementTree as ET
+	import xml.etree.cElementTree as ET
 except ImportError:
-    import xml.etree.ElementTree as ET
+	import xml.etree.ElementTree as ET
 import struct
 import re
+import logging
+
+
+log = logging.getLogger(__name__)
+
 
 __doc__="""
 Compiles/decompiles version 0 and 1 SVG tables from/to XML.
@@ -19,34 +24,34 @@
 Version 0 is the joint Adobe-Mozilla proposal, which supports color palettes.
 
 The XML format is:
-  <SVG>
-    <svgDoc endGlyphID="1" startGlyphID="1">
-      <![CDATA[ <complete SVG doc> ]]
-    </svgDoc>
+<SVG>
+	<svgDoc endGlyphID="1" startGlyphID="1">
+		<![CDATA[ <complete SVG doc> ]]
+	</svgDoc>
 ...
 	<svgDoc endGlyphID="n" startGlyphID="m">
-      <![CDATA[ <complete SVG doc> ]]
-    </svgDoc>
+		<![CDATA[ <complete SVG doc> ]]
+	</svgDoc>
 
-    <colorPalettes>
-    	<colorParamUINameID>n</colorParamUINameID>
-    	...
-    	<colorParamUINameID>m</colorParamUINameID>
-    	<colorPalette uiNameID="n">
-    		<colorRecord red="<int>" green="<int>" blue="<int>" alpha="<int>" />
-    		...
-    		<colorRecord red="<int>" green="<int>" blue="<int>" alpha="<int>" />
-    	</colorPalette>
-    	...
-    	<colorPalette uiNameID="m">
-    		<colorRecord red="<int> green="<int>" blue="<int>" alpha="<int>" />
-    		...
-    		<colorRecord red=<int>" green="<int>" blue="<int>" alpha="<int>" />
-    	</colorPalette>
-    </colorPalettes>
+	<colorPalettes>
+		<colorParamUINameID>n</colorParamUINameID>
+		...
+		<colorParamUINameID>m</colorParamUINameID>
+		<colorPalette uiNameID="n">
+			<colorRecord red="<int>" green="<int>" blue="<int>" alpha="<int>" />
+			...
+			<colorRecord red="<int>" green="<int>" blue="<int>" alpha="<int>" />
+		</colorPalette>
+		...
+		<colorPalette uiNameID="m">
+			<colorRecord red="<int> green="<int>" blue="<int>" alpha="<int>" />
+			...
+			<colorRecord red=<int>" green="<int>" blue="<int>" alpha="<int>" />
+		</colorPalette>
+	</colorPalettes>
 </SVG>
 
-Color values must be less than 256. 
+Color values must be less than 256.
 
 The number of color records in each </colorPalette> must be the same as
 the number of <colorParamUINameID> elements.
@@ -93,21 +98,29 @@
 
 
 class table_S_V_G_(DefaultTable.DefaultTable):
-	
+
+	def __init__(self, tag=None):
+		DefaultTable.DefaultTable.__init__(self, tag)
+		self.colorPalettes = None
+
 	def decompile(self, data, ttFont):
 		self.docList = None
 		self.colorPalettes = None
 		pos = 0
 		self.version = struct.unpack(">H", data[pos:pos+2])[0]
-		
+
 		if self.version == 1:
+			# This is pre-standardization version of the table; and obsolete.  But we decompile it for now.
+			# https://wiki.mozilla.org/SVGOpenTypeFonts
 			self.decompile_format_1(data, ttFont)
 		else:
 			if self.version != 0:
-				print("Unknown SVG table version '%s'. Decompiling as version 0." % (self.version))
+				log.warning(
+					"Unknown SVG table version '%s'. Decompiling as version 0.", self.version)
+			# This is the standardized version of the table; and current.
+			# https://www.microsoft.com/typography/otspec/svg.htm
 			self.decompile_format_0(data, ttFont)
 
-
 	def decompile_format_0(self, data, ttFont):
 		dummy, data2 = sstruct.unpack2(SVG_format_0, data, self)
 		# read in SVG Documents Index
@@ -121,37 +134,29 @@
 			if numColorParams > 0:
 				colorPalettes.colorParamUINameIDs = colorParamUINameIDs = []
 				pos = pos + 2
-				i = 0
-				while i < numColorParams:
+				for i in range(numColorParams):
 					nameID = struct.unpack(">H", data[pos:pos+2])[0]
 					colorParamUINameIDs.append(nameID)
 					pos = pos + 2
-					i += 1
 
 				colorPalettes.numColorPalettes = numColorPalettes = struct.unpack(">H", data[pos:pos+2])[0]
 				pos = pos + 2
 				if numColorPalettes > 0:
 					colorPalettes.colorPaletteList = colorPaletteList = []
-					i = 0
-					while i < numColorPalettes:
+					for i in range(numColorPalettes):
 						colorPalette = ColorPalette()
 						colorPaletteList.append(colorPalette)
 						colorPalette.uiNameID = struct.unpack(">H", data[pos:pos+2])[0]
 						pos = pos + 2
 						colorPalette.paletteColors = paletteColors = []
-						j = 0
-						while j < numColorParams:
+						for j in range(numColorParams):
 							colorRecord, colorPaletteData = sstruct.unpack2(colorRecord_format_0, data[pos:], ColorRecord())
 							paletteColors.append(colorRecord)
-							j += 1
 							pos += 4
-						i += 1
 
 	def decompile_format_1(self, data, ttFont):
-		pos = 2
-		self.numEntries = struct.unpack(">H", data[pos:pos+2])[0]
-		pos += 2
-		self.decompileEntryList(data, pos)
+		self.offsetToSVGDocIndex = 2
+		self.decompileEntryList(data)
 
 	def decompileEntryList(self, data):
 		# data starts with the first entry of the entry list.
@@ -162,16 +167,22 @@
 			data2 = data[pos:]
 			self.docList = []
 			self.entries = entries = []
-			i = 0
-			while i < self.numEntries:
+			for i in range(self.numEntries):
 				docIndexEntry, data2 = sstruct.unpack2(doc_index_entry_format_0, data2, DocumentIndexEntry())
 				entries.append(docIndexEntry)
-				i += 1
 
 			for entry in entries:
 				start = entry.svgDocOffset + subTableStart
 				end = start + entry.svgDocLength
-				doc = tostr(data[start:end], "utf-8")
+				doc = data[start:end]
+				if doc.startswith(b"\x1f\x8b"):
+					import gzip
+					bytesIO = BytesIO(doc)
+					with gzip.GzipFile(None, "r", fileobj=bytesIO) as gunzipper:
+						doc = gunzipper.read()
+					self.compressed = True
+					del bytesIO
+				doc = tostr(doc, "utf_8")
 				self.docList.append( [doc, entry.startGlyphID, entry.endGlyphID] )
 
 	def compile(self, ttFont):
@@ -193,11 +204,21 @@
 		curOffset = len(datum) + doc_index_entry_format_0Size*numEntries
 		for doc, startGlyphID, endGlyphID in self.docList:
 			docOffset = curOffset
-			docLength = len(doc)
+			docBytes = tobytes(doc, encoding="utf_8")
+			if getattr(self, "compressed", False) and not docBytes.startswith(b"\x1f\x8b"):
+				import gzip
+				bytesIO = BytesIO()
+				with gzip.GzipFile(None, "w", fileobj=bytesIO) as gzipper:
+					gzipper.write(docBytes)
+				gzipped = bytesIO.getvalue()
+				if len(gzipped) < len(docBytes):
+					docBytes = gzipped
+				del gzipped, bytesIO
+			docLength = len(docBytes)
 			curOffset += docLength
 			entry = struct.pack(">HHLL", startGlyphID, endGlyphID, docOffset, docLength)
 			entryList.append(entry)
-			docList.append(tobytes(doc, encoding="utf-8"))
+			docList.append(docBytes)
 		entryList.extend(docList)
 		svgDocData = bytesjoin(entryList)
 
@@ -239,11 +260,12 @@
 		curOffset = SVG_format_1Size + doc_index_entry_format_0Size*numEntries
 		for doc, startGlyphID, endGlyphID in self.docList:
 			docOffset = curOffset
-			docLength = len(doc)
+			docBytes = tobytes(doc, encoding="utf_8")
+			docLength = len(docBytes)
 			curOffset += docLength
 			entry = struct.pack(">HHLL", startGlyphID, endGlyphID, docOffset, docLength)
 			dataList.append(entry)
-			docList.append(tobytes(doc, encoding="utf-8"))
+			docList.append(docBytes)
 		dataList.extend(docList)
 		data = bytesjoin(dataList)
 		return data
@@ -263,7 +285,7 @@
 			writer.newline()
 			for uiNameID in self.colorPalettes.colorParamUINameIDs:
 				writer.begintag("colorParamUINameID")
-				writer.writeraw(str(uiNameID))
+				writer._writeraw(str(uiNameID))
 				writer.endtag("colorParamUINameID")
 				writer.newline()
 			for colorPalette in self.colorPalettes.colorPaletteList:
@@ -284,13 +306,8 @@
 
 			writer.endtag("colorPalettes")
 			writer.newline()
-		else:
-			writer.begintag("colorPalettes")
-			writer.endtag("colorPalettes")
-			writer.newline()
 
 	def fromXML(self, name, attrs, content, ttFont):
-		import re
 		if name == "svgDoc":
 			if not hasattr(self, "docList"):
 				self.docList = []
@@ -299,13 +316,13 @@
 			startGID = int(attrs["startGlyphID"])
 			endGID = int(attrs["endGlyphID"])
 			self.docList.append( [doc, startGID, endGID] )
-		elif  name == "colorPalettes":
+		elif name == "colorPalettes":
 			self.colorPalettes = ColorPalettes()
 			self.colorPalettes.fromXML(name, attrs, content, ttFont)
 			if self.colorPalettes.numColorParams == 0:
 				self.colorPalettes = None
 		else:
-			print("Unknown", name, content)
+			log.warning("Unknown %s %s", name, content)
 
 class DocumentIndexEntry(object):
 	def __init__(self):
@@ -326,7 +343,7 @@
 
 	def fromXML(self, name, attrs, content, ttFont):
 		for element in content:
-			if isinstance(element, type("")):
+			if not isinstance(element, tuple):
 				continue
 			name, attrib, content = element
 			if name == "colorParamUINameID":
@@ -335,7 +352,7 @@
 			elif name == "colorPalette":
 				colorPalette = ColorPalette()
 				self.colorPaletteList.append(colorPalette)
-				colorPalette.fromXML((name, attrib, content), ttFont)
+				colorPalette.fromXML(name, attrib, content, ttFont)
 
 		self.numColorParams = len(self.colorParamUINameIDs)
 		self.numColorPalettes = len(self.colorPaletteList)
@@ -345,7 +362,7 @@
 
 class ColorPalette(object):
 	def __init__(self):
-		self.uiNameID = None # USHORT. name table ID that describes user interface strings associated with this color palette. 
+		self.uiNameID = None # USHORT. name table ID that describes user interface strings associated with this color palette.
 		self.paletteColors = [] # list of ColorRecords
 
 	def fromXML(self, name, attrs, content, ttFont):
diff --git a/Lib/fontTools/ttLib/tables/S__i_l_f.py b/Lib/fontTools/ttLib/tables/S__i_l_f.py
new file mode 100644
index 0000000..2afd71e
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/S__i_l_f.py
@@ -0,0 +1,877 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import safeEval
+from itertools import *
+from . import DefaultTable
+from . import grUtils
+from array import array
+import struct, operator, warnings, re, sys
+
+Silf_hdr_format = '''
+    >
+    version:            16.16F
+'''
+
+Silf_hdr_format_3 = '''
+    >
+    version:            16.16F
+    compilerVersion:    L
+    numSilf:            H
+                        x
+                        x
+'''
+
+Silf_part1_format_v3 = '''
+    >
+    ruleVersion:        16.16F
+    passOffset:         H
+    pseudosOffset:      H
+'''
+
+Silf_part1_format = '''
+    >
+    maxGlyphID:         H
+    extraAscent:        h
+    extraDescent:       h
+    numPasses:          B
+    iSubst:             B
+    iPos:               B
+    iJust:              B
+    iBidi:              B
+    flags:              B
+    maxPreContext:      B
+    maxPostContext:     B
+    attrPseudo:         B
+    attrBreakWeight:    B
+    attrDirectionality: B
+    attrMirroring:      B
+    attrSkipPasses:     B
+    numJLevels:         B
+'''
+
+Silf_justify_format = '''
+    >
+    attrStretch:        B
+    attrShrink:         B
+    attrStep:           B
+    attrWeight:         B
+    runto:              B
+                        x
+                        x
+                        x
+'''
+
+Silf_part2_format = '''
+    >
+    numLigComp:         H
+    numUserDefn:        B
+    maxCompPerLig:      B
+    direction:          B
+    attCollisions:      B
+                        x
+                        x
+                        x
+    numCritFeatures:    B
+'''
+
+Silf_pseudomap_format = '''
+    >
+    unicode:            L
+    nPseudo:            H
+'''
+
+Silf_classmap_format = '''
+    >
+    numClass:           H
+    numLinear:          H
+'''
+
+Silf_lookupclass_format = '''
+    >
+    numIDs:             H
+    searchRange:        H
+    entrySelector:      H
+    rangeShift:         H
+'''
+
+Silf_lookuppair_format = '''
+    >
+    glyphId:            H
+    index:              H
+'''
+
+Silf_pass_format = '''
+    >
+    flags:              B
+    maxRuleLoop:        B
+    maxRuleContext:     B
+    maxBackup:          B
+    numRules:           H
+    fsmOffset:          H
+    pcCode:             L
+    rcCode:             L
+    aCode:              L
+    oDebug:             L
+    numRows:            H
+    numTransitional:    H
+    numSuccess:         H
+    numColumns:         H
+'''
+
+aCode_info = (
+    ("NOP", 0),
+    ("PUSH_BYTE", "b"),
+    ("PUSH_BYTE_U", "B"),
+    ("PUSH_SHORT", ">h"),
+    ("PUSH_SHORT_U", ">H"),
+    ("PUSH_LONG", ">L"),
+    ("ADD", 0),
+    ("SUB", 0),
+    ("MUL", 0),
+    ("DIV", 0),
+    ("MIN", 0),
+    ("MAX", 0),
+    ("NEG", 0),
+    ("TRUNC8", 0),
+    ("TRUNC16", 0),
+    ("COND", 0),
+    ("AND", 0),         # x10
+    ("OR", 0),
+    ("NOT", 0),
+    ("EQUAL", 0),
+    ("NOT_EQ", 0),
+    ("LESS", 0),
+    ("GTR", 0),
+    ("LESS_EQ", 0),
+    ("GTR_EQ", 0),
+    ("NEXT", 0),
+    ("NEXT_N", "b"),
+    ("COPY_NEXT", 0),
+    ("PUT_GLYPH_8BIT_OBS", "B"),
+    ("PUT_SUBS_8BIT_OBS", "bBB"),
+    ("PUT_COPY", "b"),
+    ("INSERT", 0),
+    ("DELETE", 0),      # x20
+    ("ASSOC", -1),
+    ("CNTXT_ITEM", "bB"),
+    ("ATTR_SET", "B"),
+    ("ATTR_ADD", "B"),
+    ("ATTR_SUB", "B"),
+    ("ATTR_SET_SLOT", "B"),
+    ("IATTR_SET_SLOT", "BB"),
+    ("PUSH_SLOT_ATTR", "Bb"),
+    ("PUSH_GLYPH_ATTR_OBS", "Bb"),
+    ("PUSH_GLYPH_METRIC", "Bbb"),
+    ("PUSH_FEAT", "Bb"),
+    ("PUSH_ATT_TO_GATTR_OBS", "Bb"),
+    ("PUSH_ATT_TO_GLYPH_METRIC", "Bbb"),
+    ("PUSH_ISLOT_ATTR", "Bbb"),
+    ("PUSH_IGLYPH_ATTR", "Bbb"),
+    ("POP_RET", 0),     # x30
+    ("RET_ZERO", 0),
+    ("RET_TRUE", 0),
+    ("IATTR_SET", "BB"),
+    ("IATTR_ADD", "BB"),
+    ("IATTR_SUB", "BB"),
+    ("PUSH_PROC_STATE", "B"),
+    ("PUSH_VERSION", 0),
+    ("PUT_SUBS", ">bHH"),
+    ("PUT_SUBS2", 0),
+    ("PUT_SUBS3", 0),
+    ("PUT_GLYPH", ">H"),
+    ("PUSH_GLYPH_ATTR", ">Hb"),
+    ("PUSH_ATT_TO_GLYPH_ATTR", ">Hb"),
+    ("BITOR", 0),
+    ("BITAND", 0),
+    ("BITNOT", 0),      # x40
+    ("BITSET", ">HH"),
+    ("SET_FEAT", "Bb")
+)
+aCode_map = dict([(x[0], (i, x[1])) for i,x in enumerate(aCode_info)])
+
+def disassemble(aCode):
+    codelen = len(aCode)
+    pc = 0
+    res = []
+    while pc < codelen:
+        opcode = byteord(aCode[pc:pc+1])
+        if opcode > len(aCode_info):
+            instr = aCode_info[0]
+        else:
+            instr = aCode_info[opcode]
+        pc += 1
+        if instr[1] != 0 and pc >= codelen : return res
+        if instr[1] == -1:
+            count = byteord(aCode[pc])
+            fmt = "%dB" % count
+            pc += 1
+        elif instr[1] == 0:
+            fmt = ""
+        else :
+            fmt = instr[1]
+        if fmt == "":
+            res.append(instr[0])
+            continue
+        parms = struct.unpack_from(fmt, aCode[pc:])
+        res.append(instr[0] + "(" + ", ".join(map(str, parms)) + ")")
+        pc += struct.calcsize(fmt)
+    return res
+
+instre = re.compile("^\s*([^(]+)\s*(?:\(([^)]+)\))?")
+def assemble(instrs):
+    res = []
+    for inst in instrs:
+        m = instre.match(inst)
+        if not m or not m.group(1) in aCode_map:
+            continue
+        opcode, parmfmt = aCode_map[m.group(1)]
+        res.append(struct.pack("B", opcode))
+        if m.group(2):
+            if parmfmt == 0:
+                continue
+            parms = [int(x) for x in re.split(",\s*", m.group(2))]
+            if parmfmt == -1:
+                l = len(parms)
+                res.append(struct.pack(("%dB" % (l+1)), l, *parms))
+            else:
+                res.append(struct.pack(parmfmt, *parms))
+    return b"".join(res)
+
+def writecode(tag, writer, instrs):
+    writer.begintag(tag)
+    writer.newline()
+    for l in disassemble(instrs):
+        writer.write(l)
+        writer.newline()
+    writer.endtag(tag)
+    writer.newline()
+
+def readcode(content):
+    res = []
+    for e in content_string(content).split('\n'):
+        e = e.strip()
+        if not len(e): continue
+        res.append(e)
+    return assemble(res)
+    
+attrs_info=('flags', 'extraAscent', 'extraDescent', 'maxGlyphID',
+            'numLigComp', 'numUserDefn', 'maxCompPerLig', 'direction', 'lbGID')
+attrs_passindexes = ('iSubst', 'iPos', 'iJust', 'iBidi')
+attrs_contexts = ('maxPreContext', 'maxPostContext')
+attrs_attributes = ('attrPseudo', 'attrBreakWeight', 'attrDirectionality',
+                    'attrMirroring', 'attrSkipPasses', 'attCollisions')
+pass_attrs_info = ('flags', 'maxRuleLoop', 'maxRuleContext', 'maxBackup',
+            'minRulePreContext', 'maxRulePreContext', 'collisionThreshold')
+pass_attrs_fsm = ('numRows', 'numTransitional', 'numSuccess', 'numColumns')
+
+def writesimple(tag, self, writer, *attrkeys):
+    attrs = dict([(k, getattr(self, k)) for k in attrkeys])
+    writer.simpletag(tag, **attrs)
+    writer.newline()
+
+def getSimple(self, attrs, *attr_list):
+    for k in attr_list:
+        if k in attrs:
+            setattr(self, k, int(safeEval(attrs[k])))
+
+def content_string(contents):
+    res = ""
+    for element in contents:
+        if isinstance(element, tuple): continue
+        res += element
+    return res.strip()
+
+def wrapline(writer, dat, length=80):
+    currline = ""
+    for d in dat:
+        if len(currline) > length:
+            writer.write(currline[:-1])
+            writer.newline()
+            currline = ""
+        currline += d + " "
+    if len(currline):
+        writer.write(currline[:-1])
+        writer.newline()
+
+class _Object() :
+    pass
+
+class table_S__i_l_f(DefaultTable.DefaultTable):
+    '''Silf table support'''
+
+    def __init__(self, tag=None):
+        DefaultTable.DefaultTable.__init__(self, tag)
+        self.silfs = []
+
+    def decompile(self, data, ttFont):
+        sstruct.unpack2(Silf_hdr_format, data, self)
+        if self.version >= 5.0:
+            (data, self.scheme) = grUtils.decompress(data)
+            sstruct.unpack2(Silf_hdr_format_3, data, self)
+            base = sstruct.calcsize(Silf_hdr_format_3)
+        elif self.version < 3.0:
+            self.numSilf = struct.unpack('>H', data[4:6])
+            self.scheme = 0
+            self.compilerVersion = 0
+            base = 8
+        else:
+            self.scheme = 0
+            sstruct.unpack2(Silf_hdr_format_3, data, self)
+            base = sstruct.calcsize(Silf_hdr_format_3)
+
+        silfoffsets = struct.unpack_from(('>%dL' % self.numSilf), data[base:])
+        for offset in silfoffsets:
+            s = Silf()
+            self.silfs.append(s)
+            s.decompile(data[offset:], ttFont, self.version)
+
+    def compile(self, ttFont):
+        self.numSilf = len(self.silfs)
+        if self.version < 3.0:
+            hdr = sstruct.pack(Silf_hdr_format, self)
+            hdr += struct.pack(">HH", self.numSilf, 0)
+        else:
+            hdr = sstruct.pack(Silf_hdr_format_3, self)
+        offset = len(hdr) + 4 * self.numSilf
+        data = ""
+        for s in self.silfs:
+            hdr += struct.pack(">L", offset)
+            subdata = s.compile(ttFont, self.version)
+            offset += len(subdata)
+            data += subdata
+        if self.version >= 5.0:
+            return grUtils.compress(self.scheme, hdr+data)
+        return hdr+data
+
+    def toXML(self, writer, ttFont):
+        writer.comment('Attributes starting with _ are informative only')
+        writer.newline()
+        writer.simpletag('version', version=self.version,
+            compilerVersion=self.compilerVersion, compressionScheme=self.scheme)
+        writer.newline()
+        for s in self.silfs:
+            writer.begintag('silf')
+            writer.newline()
+            s.toXML(writer, ttFont, self.version)
+            writer.endtag('silf')
+            writer.newline()
+
+    def fromXML(self, name, attrs, content, ttFont):
+        if name == 'version':
+            self.scheme=int(safeEval(attrs['compressionScheme']))
+            self.version = float(safeEval(attrs['version']))
+            self.compilerVersion = int(safeEval(attrs['compilerVersion']))
+            return
+        if name == 'silf':
+            s = Silf()
+            self.silfs.append(s)
+            for element in content:
+                if not isinstance(element, tuple): continue
+                tag, attrs, subcontent = element
+                s.fromXML(tag, attrs, subcontent, ttFont, self.version)
+
+class Silf(object):
+    '''A particular Silf subtable'''
+
+    def __init__(self):
+        self.passes = []
+        self.scriptTags = []
+        self.critFeatures = []
+        self.jLevels = []
+        self.pMap = {}
+
+    def decompile(self, data, ttFont, version=2.0):
+        if version >= 3.0 :
+            _, data = sstruct.unpack2(Silf_part1_format_v3, data, self)
+        _, data = sstruct.unpack2(Silf_part1_format, data, self)
+        for jlevel in range(self.numJLevels):
+            j, data = sstruct.unpack2(Silf_justify_format, data, _Object())
+            self.jLevels.append(j)
+        _, data = sstruct.unpack2(Silf_part2_format, data, self)
+        if self.numCritFeatures:
+            self.critFeatures = struct.unpack_from(('>%dH' % self.numCritFeatures), data)
+        data = data[self.numCritFeatures * 2 + 1:]
+        (numScriptTag,) = struct.unpack_from('B', data)
+        if numScriptTag:
+            self.scriptTags = [struct.unpack("4s", data[x:x+4])[0] for x in range(1, 1 + 4 * numScriptTag, 4)]
+        data = data[1 + 4 * numScriptTag:]
+        (self.lbGID,) = struct.unpack('>H', data[:2])
+        if self.numPasses:
+            self.oPasses = struct.unpack(('>%dL' % (self.numPasses+1)), data[2:6+4*self.numPasses])
+        data = data[6 + 4 * self.numPasses:]
+        (numPseudo,) = struct.unpack(">H", data[:2])
+        for i in range(numPseudo):
+            if version >= 3.0:
+                pseudo = sstruct.unpack(Silf_pseudomap_format, data[8+6*i:14+6*i], _Object())
+            else:
+                pseudo = struct.unpack('>HH', data[8+4*i:12+4*i], _Object())
+            self.pMap[pseudo.unicode] = ttFont.getGlyphName(pseudo.nPseudo)
+        data = data[8 + 6 * numPseudo:]
+        currpos = (sstruct.calcsize(Silf_part1_format)
+                    + sstruct.calcsize(Silf_justify_format) * self.numJLevels
+                    + sstruct.calcsize(Silf_part2_format) + 2 * self.numCritFeatures
+                    + 1 + 1 + 4 * numScriptTag + 6 + 4 * self.numPasses + 8 + 6 * numPseudo)
+        if version >= 3.0:
+            currpos += sstruct.calcsize(Silf_part1_format_v3)
+        self.classes = Classes()
+        self.classes.decompile(data, ttFont, version)
+        for i in range(self.numPasses):
+            p = Pass()
+            self.passes.append(p)
+            p.decompile(data[self.oPasses[i]-currpos:self.oPasses[i+1]-currpos],
+                        ttFont, version)
+
+    def compile(self, ttFont, version=2.0):
+        self.numPasses = len(self.passes)
+        self.numJLevels = len(self.jLevels)
+        self.numCritFeatures = len(self.critFeatures)
+        numPseudo = len(self.pMap)
+        data = ""
+        if version >= 3.0:
+            hdroffset = sstruct.calcsize(Silf_part1_format_v3)
+        else:
+            hdroffset = 0
+        data += sstruct.pack(Silf_part1_format, self)
+        for j in self.jLevels:
+            data += sstruct.pack(Silf_justify_format, j)
+        data += sstruct.pack(Silf_part2_format, self)
+        if self.numCritFeatures:
+            data += struct.pack((">%dH" % self.numCritFeaturs), *self.critFeatures)
+        data += struct.pack("BB", 0, len(self.scriptTags))
+        if len(self.scriptTags):
+            tdata = [struct.pack("4s", x) for x in self.scriptTags]
+            data += "".join(tdata)
+        data += struct.pack(">H", self.lbGID)
+        self.passOffset = len(data)
+
+        data1 = grUtils.bininfo(numPseudo, 6)
+        currpos = hdroffset + len(data) + 4 * (self.numPasses + 1)
+        self.pseudosOffset = currpos + len(data1)
+        for u, p in sorted(self.pMap.items()):
+            data1 += struct.pack((">LH" if version >= 3.0 else ">HH"),
+                                u, ttFont.getGlyphID(p))
+        data1 += self.classes.compile(ttFont, version)
+        currpos += len(data1)
+        data2 = ""
+        datao = ""
+        for i, p in enumerate(self.passes):
+            base = currpos + len(data2)
+            datao += struct.pack(">L", base)
+            data2 += p.compile(ttFont, base, version)
+        datao += struct.pack(">L", currpos + len(data2))
+
+        if version >= 3.0:
+            data3 = sstruct.pack(Silf_part1_format_v3, self)
+        else:
+            data3 = ""
+        return data3 + data + datao + data1 + data2
+
+
+    def toXML(self, writer, ttFont, version=2.0):
+        if version >= 3.0:
+            writer.simpletag('version', ruleVersion=self.ruleVersion)
+            writer.newline()
+        writesimple('info', self, writer, *attrs_info)
+        writesimple('passindexes', self, writer, *attrs_passindexes)
+        writesimple('contexts', self, writer, *attrs_contexts)
+        writesimple('attributes', self, writer, *attrs_attributes)
+        if len(self.jLevels):
+            writer.begintag('justifications')
+            writer.newline()
+            jformat, jnames, jfixes = sstruct.getformat(Silf_justify_format)
+            for i, j in enumerate(self.jLevels):
+                attrs = dict([(k, getattr(j, k)) for k in jnames])
+                writer.simpletag('justify', **attrs)
+                writer.newline()
+            writer.endtag('justifications')
+            writer.newline()
+        if len(self.critFeatures):
+            writer.begintag('critFeatures')
+            writer.newline()
+            writer.write(" ".join(map(str, self.critFeatures)))
+            writer.newline()
+            writer.endtag('critFeatures')
+            writer.newline()
+        if len(self.scriptTags):
+            writer.begintag('scriptTags')
+            writer.newline()
+            writer.write(" ".join(self.scriptTags))
+            writer.newline()
+            writer.endtag('scriptTags')
+            writer.newline()
+        if self.pMap:
+            writer.begintag('pseudoMap')
+            writer.newline()
+            for k, v in sorted(self.pMap.items()):
+                writer.simpletag('pseudo', unicode=hex(k), pseudo=v)
+                writer.newline()
+            writer.endtag('pseudoMap')
+            writer.newline()
+        self.classes.toXML(writer, ttFont, version)
+        if len(self.passes):
+            writer.begintag('passes')
+            writer.newline()
+            for i, p in enumerate(self.passes):
+                writer.begintag('pass', _index=i)
+                writer.newline()
+                p.toXML(writer, ttFont, version)
+                writer.endtag('pass')
+                writer.newline()
+            writer.endtag('passes')
+            writer.newline()
+
+    def fromXML(self, name, attrs, content, ttFont, version=2.0):
+        if name == 'version':
+            self.ruleVersion = float(safeEval(attrs.get('ruleVersion', "0")))
+        if name == 'info':
+            getSimple(self, attrs, *attrs_info)
+        elif name == 'passindexes':
+            getSimple(self, attrs, *attrs_passindexes)
+        elif name == 'contexts':
+            getSimple(self, attrs, *attrs_contexts)
+        elif name == 'attributes':
+            getSimple(self, attrs, *attrs_attributes)
+        elif name == 'justifications':
+            for element in content:
+                if not isinstance(element, tuple): continue
+                (tag, attrs, subcontent) = element
+                if tag == 'justify':
+                    j = _Object()
+                    for k, v in attrs.items():
+                        setattr(j, k, int(v))
+                    self.jLevels.append(j)
+        elif name == 'critFeatures':
+            self.critFeatures = []
+            element = content_string(content)
+            self.critFeatures.extend(map(int, element.split()))
+        elif name == 'scriptTags':
+            self.scriptTags = []
+            element = content_string(content)
+            for n in element.split():
+                self.scriptTags.append(n)
+        elif name == 'pseudoMap':
+            self.pMap = {}
+            for element in content:
+                if not isinstance(element, tuple): continue
+                (tag, attrs, subcontent) = element
+                if tag == 'pseudo':
+                    k = int(attrs['unicode'], 16)
+                    v = attrs['pseudo']
+                self.pMap[k] = v
+        elif name == 'classes':
+            self.classes = Classes()
+            for element in content:
+                if not isinstance(element, tuple): continue
+                tag, attrs, subcontent = element
+                self.classes.fromXML(tag, attrs, subcontent, ttFont, version)
+        elif name == 'passes':
+            for element in content:
+                if not isinstance(element, tuple): continue
+                tag, attrs, subcontent = element
+                if tag == 'pass':
+                    p = Pass()
+                    for e in subcontent:
+                        if not isinstance(e, tuple): continue
+                        p.fromXML(e[0], e[1], e[2], ttFont, version)
+                    self.passes.append(p)
+
+
+class Classes(object):
+
+    def __init__(self):
+        self.linear = []
+        self.nonLinear = []
+
+    def decompile(self, data, ttFont, version=2.0):
+        sstruct.unpack2(Silf_classmap_format, data, self)
+        if version >= 4.0 :
+            oClasses = struct.unpack((">%dL" % (self.numClass+1)),
+                                        data[4:8+4*self.numClass])
+        else:
+            oClasses = struct.unpack((">%dH" % (self.numClass+1)),
+                                        data[4:6+2*self.numClass])
+        for s,e in zip(oClasses[:self.numLinear], oClasses[1:self.numLinear+1]):
+            self.linear.append(map(ttFont.getGlyphName,
+                                   struct.unpack((">%dH" % ((e-s)/2)), data[s:e])))
+        for s,e in zip(oClasses[self.numLinear:self.numClass],
+                        oClasses[self.numLinear+1:self.numClass+1]):
+            nonLinids = [struct.unpack(">HH", data[x:x+4]) for x in range(s+8, e, 4)]
+            nonLin = dict([(ttFont.getGlyphName(x[0]), x[1]) for x in nonLinids])
+            self.nonLinear.append(nonLin)
+
+    def compile(self, ttFont, version=2.0):
+        data = ""
+        oClasses = []
+        if version >= 4.0:
+            offset = 8 + 4 * (len(self.linear) + len(self.nonLinear))
+        else:
+            offset = 6 + 2 * (len(self.linear) + len(self.nonLinear))
+        for l in self.linear:
+            oClasses.append(len(data) + offset)
+            gs = map(ttFont.getGlyphID, l)
+            data += struct.pack((">%dH" % len(l)), *gs)
+        for l in self.nonLinear:
+            oClasses.append(len(data) + offset)
+            gs = [(ttFont.getGlyphID(x[0]), x[1]) for x in l.items()]
+            data += grUtils.bininfo(len(gs))
+            data += "".join([struct.pack(">HH", *x) for x in sorted(gs)])
+        oClasses.append(len(data) + offset)
+        self.numClass = len(oClasses) - 1
+        self.numLinear = len(self.linear)
+        return sstruct.pack(Silf_classmap_format, self) + \
+               struct.pack(((">%dL" if version >= 4.0 else ">%dH") % len(oClasses)),
+                            *oClasses) + data
+
+    def toXML(self, writer, ttFont, version=2.0):
+        writer.begintag('classes')
+        writer.newline()
+        writer.begintag('linearClasses')
+        writer.newline()
+        for i,l in enumerate(self.linear):
+            writer.begintag('linear', _index=i)
+            writer.newline()
+            wrapline(writer, l)
+            writer.endtag('linear')
+            writer.newline()
+        writer.endtag('linearClasses')
+        writer.newline()
+        writer.begintag('nonLinearClasses')
+        writer.newline()
+        for i, l in enumerate(self.nonLinear):
+            writer.begintag('nonLinear', _index=i + self.numLinear)
+            writer.newline()
+            for inp, ind in l.items():
+                writer.simpletag('map', glyph=inp, index=ind)
+                writer.newline()
+            writer.endtag('nonLinear')
+            writer.newline()
+        writer.endtag('nonLinearClasses')
+        writer.newline()
+        writer.endtag('classes')
+        writer.newline()
+
+    def fromXML(self, name, attrs, content, ttFont, version=2.0):
+        if name == 'linearClasses':
+            for element in content:
+                if not isinstance(element, tuple): continue
+                tag, attrs, subcontent = element
+                if tag == 'linear':
+                    l = content_string(subcontent).split()
+                    self.linear.append(l)
+        elif name == 'nonLinearClasses':
+            for element in content:
+                if not isinstance(element, tuple): continue
+                tag, attrs, subcontent = element
+                if tag =='nonLinear':
+                    l = {}
+                    for e in subcontent:
+                        if not isinstance(e, tuple): continue
+                        tag, attrs, subsubcontent = e
+                        if tag == 'map':
+                            l[attrs['glyph']] = int(safeEval(attrs['index']))
+                    self.nonLinear.append(l)
+
+class Pass(object):
+
+    def __init__(self):
+        self.colMap = {}
+        self.rules = []
+        self.rulePreContexts = []
+        self.ruleSortKeys = []
+        self.ruleConstraints = []
+        self.passConstraints = ""
+        self.actions = []
+        self.stateTrans = []
+        self.startStates = []
+
+    def decompile(self, data, ttFont, version=2.0):
+        _, data = sstruct.unpack2(Silf_pass_format, data, self)
+        (numRange, _, _, _) = struct.unpack(">4H", data[:8])
+        data = data[8:]
+        for i in range(numRange):
+            (first, last, col) = struct.unpack(">3H", data[6*i:6*i+6])
+            for g in range(first, last+1):
+                self.colMap[ttFont.getGlyphName(g)] = col
+        data = data[6*numRange:]
+        oRuleMap = struct.unpack_from((">%dH" % (self.numSuccess + 1)), data)
+        data = data[2+2*self.numSuccess:]
+        rules = struct.unpack_from((">%dH" % oRuleMap[-1]), data)
+        self.rules = [rules[s:e] for (s,e) in zip(oRuleMap, oRuleMap[1:])]
+        data = data[2*oRuleMap[-1]:]
+        (self.minRulePreContext, self.maxRulePreContext) = struct.unpack('BB', data[:2])
+        numStartStates = self.maxRulePreContext - self.minRulePreContext + 1
+        self.startStates = struct.unpack((">%dH" % numStartStates),
+                                        data[2:2 + numStartStates * 2])
+        data = data[2+numStartStates*2:]
+        self.ruleSortKeys = struct.unpack((">%dH" % self.numRules), data[:2 * self.numRules])
+        data = data[2*self.numRules:]
+        self.rulePreContexts = struct.unpack(("%dB" % self.numRules), data[:self.numRules])
+        data = data[self.numRules:]
+        (self.collisionThreshold, pConstraint) = struct.unpack(">BH", data[:3])
+        oConstraints = list(struct.unpack((">%dH" % (self.numRules + 1)),
+                                        data[3:5 + self.numRules * 2]))
+        data = data[5 + self.numRules * 2:]
+        oActions = list(struct.unpack((">%dH" % (self.numRules + 1)),
+                                        data[:2 + self.numRules * 2]))
+        data = data[2 * self.numRules + 2:]
+        for i in range(self.numTransitional):
+            a = array("H", data[i*self.numColumns*2:(i+1)*self.numColumns*2])
+            a.byteswap()
+            self.stateTrans.append(a)
+        data = data[self.numTransitional * self.numColumns * 2 + 1:]
+        self.passConstraints = data[:pConstraint]
+        data = data[pConstraint:]
+        for i in range(len(oConstraints)-2,-1,-1):
+            if oConstraints[i] == 0 :
+                oConstraints[i] = oConstraints[i+1]
+        self.ruleConstraints = [(data[s:e] if (e-s > 1) else "") for (s,e) in zip(oConstraints, oConstraints[1:])]
+        data = data[oConstraints[-1]:]
+        self.actions = [(data[s:e] if (e-s > 1) else "") for (s,e) in zip(oActions, oActions[1:])]
+        data = data[oActions[-1]:]
+        # not using debug
+
+    def compile(self, ttFont, base, version=2.0):
+        # build it all up backwards
+        oActions = reduce(lambda a, x: (a[0]+len(x), a[1]+[a[0]]), self.actions + [""], (0, []))[1]
+        oConstraints = reduce(lambda a, x: (a[0]+len(x), a[1]+[a[0]]), self.ruleConstraints + [""], (1, []))[1]
+        constraintCode = "\000" + "".join(self.ruleConstraints)
+        transes = []
+        for t in self.stateTrans:
+            t.byteswap()
+            transes.append(t.tostring())
+            t.byteswap()
+        if not len(transes):
+            self.startStates = [0]
+        oRuleMap = reduce(lambda a, x: (a[0]+len(x), a[1]+[a[0]]), self.rules+[[]], (0, []))[1]
+        passRanges = []
+        gidcolmap = dict([(ttFont.getGlyphID(x[0]), x[1]) for x in self.colMap.items()])
+        for e in grUtils.entries(gidcolmap, sameval = True):
+            if e[1]:
+                passRanges.append((e[0], e[0]+e[1]-1, e[2][0]))
+        self.numRules = len(self.actions)
+        self.fsmOffset = (sstruct.calcsize(Silf_pass_format) + 8 + len(passRanges) * 6
+                    + len(oRuleMap) * 2 + 2 * oRuleMap[-1] + 2
+                    + 2 * len(self.startStates) + 3 * self.numRules + 3
+                    + 4 * self.numRules + 4)
+        self.pcCode = self.fsmOffset + 2*self.numTransitional*self.numColumns + 1 + base
+        self.rcCode = self.pcCode + len(self.passConstraints)
+        self.aCode = self.rcCode + len(constraintCode)
+        self.oDebug = 0
+        # now generate output
+        data = sstruct.pack(Silf_pass_format, self)
+        data += grUtils.bininfo(len(passRanges), 6)
+        data += "".join(struct.pack(">3H", *p) for p in passRanges)
+        data += struct.pack((">%dH" % len(oRuleMap)), *oRuleMap)
+        flatrules = reduce(lambda a,x: a+x, self.rules, [])
+        data += struct.pack((">%dH" % oRuleMap[-1]), *flatrules)
+        data += struct.pack("BB", self.minRulePreContext, self.maxRulePreContext)
+        data += struct.pack((">%dH" % len(self.startStates)), *self.startStates)
+        data += struct.pack((">%dH" % self.numRules), *self.ruleSortKeys)
+        data += struct.pack(("%dB" % self.numRules), *self.rulePreContexts)
+        data += struct.pack(">BH", self.collisionThreshold, len(self.passConstraints))
+        data += struct.pack((">%dH" % (self.numRules+1)), *oConstraints)
+        data += struct.pack((">%dH" % (self.numRules+1)), *oActions)
+        return data + "".join(transes) + struct.pack("B", 0) + \
+                self.passConstraints + constraintCode + "".join(self.actions)
+
+    def toXML(self, writer, ttFont, version=2.0):
+        writesimple('info', self, writer, *pass_attrs_info)
+        writesimple('fsminfo', self, writer, *pass_attrs_fsm)
+        writer.begintag('colmap')
+        writer.newline()
+        wrapline(writer, ["{}={}".format(*x) for x in sorted(self.colMap.items(),
+                                        key=lambda x:ttFont.getGlyphID(x[0]))])
+        writer.endtag('colmap')
+        writer.newline()
+        writer.begintag('staterulemap')
+        writer.newline()
+        for i, r in enumerate(self.rules):
+            writer.simpletag('state', number = self.numRows - self.numSuccess + i,
+                                rules = " ".join(map(str, r)))
+            writer.newline()
+        writer.endtag('staterulemap')
+        writer.newline()
+        writer.begintag('rules')
+        writer.newline()
+        for i in range(len(self.actions)):
+            writer.begintag('rule', index=i, precontext=self.rulePreContexts[i],
+                            sortkey=self.ruleSortKeys[i])
+            writer.newline()
+            if len(self.ruleConstraints[i]):
+                writecode('constraint', writer, self.ruleConstraints[i])
+            writecode('action', writer, self.actions[i])
+            writer.endtag('rule')
+            writer.newline()
+        writer.endtag('rules')
+        writer.newline()
+        if len(self.passConstraints):
+            writecode('passConstraint', writer, self.passConstraints)
+        if len(self.stateTrans):
+            writer.begintag('fsm')
+            writer.newline()
+            writer.begintag('starts')
+            writer.write(" ".join(map(str, self.startStates)))
+            writer.endtag('starts')
+            writer.newline()
+            for i, s in enumerate(self.stateTrans):
+                writer.begintag('row', _i=i)
+                # no newlines here
+                writer.write(" ".join(map(str, s)))
+                writer.endtag('row')
+                writer.newline()
+            writer.endtag('fsm')
+            writer.newline()
+
+    def fromXML(self, name, attrs, content, ttFont, version=2.0):
+        if name == 'info':
+            getSimple(self, attrs, *pass_attrs_info)
+        elif name == 'fsminfo':
+            getSimple(self, attrs, *pass_attrs_fsm)
+        elif name == 'colmap':
+            e = content_string(content)
+            for w in e.split():
+                x = w.split('=')
+                if len(x) != 2 or x[0] == '' or x[1] == '': continue
+                self.colMap[x[0]] = int(x[1])
+        elif name == 'staterulemap':
+            for e in content:
+                if not isinstance(e, tuple): continue
+                tag, a, c = e
+                if tag == 'state':
+                    self.rules.append(map(int, a['rules'].split(" ")))
+        elif name == 'rules':
+            for element in content:
+                if not isinstance(element, tuple): continue
+                tag, a, c = element
+                if tag != 'rule': continue
+                self.rulePreContexts.append(int(a['precontext']))
+                self.ruleSortKeys.append(int(a['sortkey']))
+                con = ""
+                act = ""
+                for e in c:
+                    if not isinstance(e, tuple): continue
+                    tag, a, subc = e
+                    if tag == 'constraint':
+                        con = readcode(subc)
+                    elif tag == 'action':
+                        act = readcode(subc)
+                self.actions.append(act)
+                self.ruleConstraints.append(con)
+        elif name == 'passConstraint':
+            self.passConstraints = readcode(content)
+        elif name == 'fsm':
+            for element in content:
+                if not isinstance(element, tuple): continue
+                tag, a, c = element
+                if tag == 'row':
+                    s = array('H')
+                    e = content_string(c)
+                    s.extend(map(int, e.split()))
+                    self.stateTrans.append(s)
+                elif tag == 'starts':
+                    s = []
+                    e = content_string(c)
+                    s.extend(map(int, e.split()))
+                    self.startStates = s
+
diff --git a/Lib/fontTools/ttLib/tables/S__i_l_l.py b/Lib/fontTools/ttLib/tables/S__i_l_l.py
new file mode 100644
index 0000000..7acef1d
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/S__i_l_l.py
@@ -0,0 +1,79 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import safeEval
+from . import DefaultTable
+from . import grUtils
+import struct
+
+Sill_hdr = '''
+    >
+    version:    16.16F
+'''
+
+class table_S__i_l_l(DefaultTable.DefaultTable):
+
+    def __init__(self, tag=None):
+        DefaultTable.DefaultTable.__init__(self, tag)
+        self.langs = {}
+
+    def decompile(self, data, ttFont):
+        (_, data) = sstruct.unpack2(Sill_hdr, data, self)
+        numLangs, = struct.unpack('>H', data[:2])
+        data = data[8:]
+        maxsetting = 0
+        langinfo = []
+        for i in range(numLangs):
+            (langcode, numsettings, offset) = struct.unpack(">4sHH",
+                                                        data[i * 8:(i+1) * 8])
+            offset = int(offset / 8) - (numLangs + 1)
+            langcode = langcode.replace(b'\000', b'')
+            langinfo.append((langcode, numsettings, offset))
+            maxsetting = max(maxsetting, offset + numsettings)
+        data = data[numLangs * 8:]
+        finfo = []
+        for i in range(maxsetting):
+            (fid, val, _) = struct.unpack(">LHH", data[i * 8:(i+1) * 8])
+            finfo.append((fid, val))
+        self.langs = {}
+        for c, n, o in langinfo:
+            self.langs[c] = []
+            for i in range(o, o+n):
+                self.langs[c].append(finfo[i])
+
+    def compile(self, ttFont):
+        ldat = ""
+        fdat = ""
+        offset = 0
+        for c, inf in sorted(self.langs.items()):
+            ldat += struct.pack(">4sHH", c.encode('utf8'), len(inf), 8 * (offset + len(self.langs) + 1))
+            for fid, val in inf:
+                fdat += struct.pack(">LHH", fid, val, 0)
+            offset += len(inf)
+        return sstruct.pack(Sill_hdr, self) + grUtils.bininfo(len(self.langs)) + \
+                ldat + fdat
+
+    def toXML(self, writer, ttFont):
+        writer.simpletag('version', version=self.version)
+        writer.newline()
+        for c, inf in sorted(self.langs.items()):
+            writer.begintag('lang', name=c)
+            writer.newline()
+            for fid, val in inf:
+                writer.simpletag('feature', fid=grUtils.num2tag(fid), val=val)
+                writer.newline()
+            writer.endtag('lang')
+            writer.newline()
+
+    def fromXML(self, name, attrs, content, ttFont):
+        if name == 'version':
+            self.version = float(safeEval(attrs['version']))
+        elif name == 'lang':
+            c = attrs['name']
+            self.langs[c] = []
+            for element in content:
+                if not isinstance(element, tuple): continue
+                tag, a, subcontent = element
+                if tag == 'feature':
+                    self.langs[c].append((grUtils.tag2num(a['fid']),
+                                            int(safeEval(a['val']))))
diff --git a/Lib/fontTools/ttLib/tables/T_S_I_B_.py b/Lib/fontTools/ttLib/tables/T_S_I_B_.py
index 5cc54e2..7d47e3a 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I_B_.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I_B_.py
@@ -1,5 +1,6 @@
-from . import asciiTable
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from .T_S_I_V_ import table_T_S_I_V_
 
-class table_T_S_I_B_(asciiTable.asciiTable):
+class table_T_S_I_B_(table_T_S_I_V_):
 	pass
-
diff --git a/Lib/fontTools/ttLib/tables/T_S_I_D_.py b/Lib/fontTools/ttLib/tables/T_S_I_D_.py
index 8228f8a..f989c9e 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I_D_.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I_D_.py
@@ -1,5 +1,6 @@
-from . import asciiTable
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from .T_S_I_V_ import table_T_S_I_V_
 
-class table_T_S_I_D_(asciiTable.asciiTable):
+class table_T_S_I_D_(table_T_S_I_V_):
 	pass
-
diff --git a/Lib/fontTools/ttLib/tables/T_S_I_J_.py b/Lib/fontTools/ttLib/tables/T_S_I_J_.py
index 0983b57..ca538ba 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I_J_.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I_J_.py
@@ -1,5 +1,6 @@
-from . import asciiTable
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from .T_S_I_V_ import table_T_S_I_V_
 
-class table_T_S_I_J_(asciiTable.asciiTable):
+class table_T_S_I_J_(table_T_S_I_V_):
 	pass
-
diff --git a/Lib/fontTools/ttLib/tables/T_S_I_P_.py b/Lib/fontTools/ttLib/tables/T_S_I_P_.py
index e34a18c..062d332 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I_P_.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I_P_.py
@@ -1,5 +1,6 @@
-from . import asciiTable
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from .T_S_I_V_ import table_T_S_I_V_
 
-class table_T_S_I_P_(asciiTable.asciiTable):
+class table_T_S_I_P_(table_T_S_I_V_):
 	pass
-
diff --git a/Lib/fontTools/ttLib/tables/T_S_I_S_.py b/Lib/fontTools/ttLib/tables/T_S_I_S_.py
index 56373e6..68e22ce 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I_S_.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I_S_.py
@@ -1,5 +1,6 @@
-from . import asciiTable
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from .T_S_I_V_ import table_T_S_I_V_
 
-class table_T_S_I_S_(asciiTable.asciiTable):
+class table_T_S_I_S_(table_T_S_I_V_):
 	pass
-
diff --git a/Lib/fontTools/ttLib/tables/T_S_I_V_.py b/Lib/fontTools/ttLib/tables/T_S_I_V_.py
index a87e3f7..46b2182 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I_V_.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I_V_.py
@@ -1,5 +1,21 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
 from . import asciiTable
 
 class table_T_S_I_V_(asciiTable.asciiTable):
-	pass
 
+	def toXML(self, writer, ttFont):
+		data = tostr(self.data)
+		# removing null bytes. XXX needed??
+		data = data.split('\0')
+		data = strjoin(data)
+		writer.begintag("source")
+		writer.newline()
+		writer.write_noindent(data.replace("\r", "\n"))
+		writer.newline()
+		writer.endtag("source")
+		writer.newline()
+
+	def fromXML(self, name, attrs, content, ttFont):
+		lines = strjoin(content).split("\n")
+		self.data = tobytes("\r".join(lines[1:-1]))
diff --git a/Lib/fontTools/ttLib/tables/T_S_I__0.py b/Lib/fontTools/ttLib/tables/T_S_I__0.py
index bcd6d15..81808ee 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I__0.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I__0.py
@@ -1,18 +1,25 @@
+""" TSI{0,1,2,3,5} are private tables used by Microsoft Visual TrueType (VTT)
+tool to store its hinting source data.
+
+TSI0 is the index table containing the lengths and offsets for the glyph
+programs and 'extra' programs ('fpgm', 'prep', and 'cvt') that are contained
+in the TSI1 table.
+"""
 from __future__ import print_function, division, absolute_import
 from fontTools.misc.py23 import *
 from . import DefaultTable
 import struct
 
-tsi0Format = '>HHl'
+tsi0Format = '>HHL'
 
 def fixlongs(glyphID, textLength, textOffset):
-	return int(glyphID), int(textLength), textOffset	
+	return int(glyphID), int(textLength), textOffset
 
 
 class table_T_S_I__0(DefaultTable.DefaultTable):
-	
+
 	dependencies = ["TSI1"]
-	
+
 	def decompile(self, data, ttFont):
 		numGlyphs = ttFont['maxp'].numGlyphs
 		indices = []
@@ -22,29 +29,28 @@
 			indices.append((glyphID, textLength, textOffset))
 			data = data[size:]
 		assert len(data) == 0
-		assert indices[-5] == (0XFFFE, 0, -1409540300), "bad magic number"  # 0xABFC1F34
+		assert indices[-5] == (0XFFFE, 0, 0xABFC1F34), "bad magic number"
 		self.indices = indices[:-5]
 		self.extra_indices = indices[-4:]
-	
+
 	def compile(self, ttFont):
 		if not hasattr(self, "indices"):
 			# We have no corresponding table (TSI1 or TSI3); let's return
 			# no data, which effectively means "ignore us".
-			return ""
+			return b""
 		data = b""
 		for index, textLength, textOffset in self.indices:
 			data = data + struct.pack(tsi0Format, index, textLength, textOffset)
-		data = data + struct.pack(tsi0Format, 0XFFFE, 0, -1409540300)  # 0xABFC1F34
+		data = data + struct.pack(tsi0Format, 0XFFFE, 0, 0xABFC1F34)
 		for index, textLength, textOffset in self.extra_indices:
 			data = data + struct.pack(tsi0Format, index, textLength, textOffset)
 		return data
-	
+
 	def set(self, indices, extra_indices):
 		# gets called by 'TSI1' or 'TSI3'
 		self.indices = indices
 		self.extra_indices = extra_indices
-	
+
 	def toXML(self, writer, ttFont):
 		writer.comment("This table will be calculated by the compiler")
 		writer.newline()
-
diff --git a/Lib/fontTools/ttLib/tables/T_S_I__1.py b/Lib/fontTools/ttLib/tables/T_S_I__1.py
index 558ce9d..5ef944a 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I__1.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I__1.py
@@ -1,41 +1,82 @@
+""" TSI{0,1,2,3,5} are private tables used by Microsoft Visual TrueType (VTT)
+tool to store its hinting source data.
+
+TSI1 contains the text of the glyph programs in the form of low-level assembly
+code, as well as the 'extra' programs 'fpgm', 'ppgm' (i.e. 'prep'), and 'cvt'.
+"""
 from __future__ import print_function, division, absolute_import
 from fontTools.misc.py23 import *
 from . import DefaultTable
+from fontTools.misc.loggingTools import LogMixin
 
-class table_T_S_I__1(DefaultTable.DefaultTable):
-	
+
+class table_T_S_I__1(LogMixin, DefaultTable.DefaultTable):
+
 	extras = {0xfffa: "ppgm", 0xfffb: "cvt", 0xfffc: "reserved", 0xfffd: "fpgm"}
-	
+
 	indextable = "TSI0"
-	
+
 	def decompile(self, data, ttFont):
+		totalLength = len(data)
 		indextable = ttFont[self.indextable]
-		self.glyphPrograms = {}
-		for i in range(len(indextable.indices)):
-			glyphID, textLength, textOffset = indextable.indices[i]
-			if textLength == 0x8000:
-				# Ugh. Hi Beat!
-				textLength = indextable.indices[i+1][1]
-			if textLength > 0x8000:
-				pass  # XXX Hmmm.
-			text = data[textOffset:textOffset+textLength]
-			assert len(text) == textLength
-			if text:
-				self.glyphPrograms[ttFont.getGlyphName(glyphID)] = text
-		
-		self.extraPrograms = {}
-		for i in range(len(indextable.extra_indices)):
-			extraCode, textLength, textOffset = indextable.extra_indices[i]
-			if textLength == 0x8000:
-				if self.extras[extraCode] == "fpgm":	# this is the last one
-					textLength = len(data) - textOffset
+		for indices, isExtra in zip(
+				(indextable.indices, indextable.extra_indices), (False, True)):
+			programs = {}
+			for i, (glyphID, textLength, textOffset) in enumerate(indices):
+				if isExtra:
+					name = self.extras[glyphID]
 				else:
-					textLength = indextable.extra_indices[i+1][1]
-			text = data[textOffset:textOffset+textLength]
-			assert len(text) == textLength
-			if text:
-				self.extraPrograms[self.extras[extraCode]] = text
-	
+					name = ttFont.getGlyphName(glyphID)
+				if textOffset > totalLength:
+					self.log.warning("textOffset > totalLength; %r skipped" % name)
+					continue
+				if textLength < 0x8000:
+					# If the length stored in the record is less than 32768, then use
+					# that as the length of the record.
+					pass
+				elif textLength == 0x8000:
+					# If the length is 32768, compute the actual length as follows:
+					isLast = i == (len(indices)-1)
+					if isLast:
+						if isExtra:
+							# For the last "extra" record (the very last record of the
+							# table), the length is the difference between the total
+							# length of the TSI1 table and the textOffset of the final
+							# record.
+							nextTextOffset = totalLength
+						else:
+							# For the last "normal" record (the last record just prior
+							# to the record containing the "magic number"), the length
+							# is the difference between the textOffset of the record
+							# following the "magic number" (0xFFFE) record (i.e. the
+							# first "extra" record), and the textOffset of the last
+							# "normal" record.
+							nextTextOffset = indextable.extra_indices[0][2]
+					else:
+						# For all other records with a length of 0x8000, the length is
+						# the difference between the textOffset of the record in
+						# question and the textOffset of the next record.
+						nextTextOffset = indices[i+1][2]
+					assert nextTextOffset >= textOffset, "entries not sorted by offset"
+					if nextTextOffset > totalLength:
+						self.log.warning(
+							"nextTextOffset > totalLength; %r truncated" % name)
+						nextTextOffset = totalLength
+					textLength = nextTextOffset - textOffset
+				else:
+					from fontTools import ttLib
+					raise ttLib.TTLibError(
+						"%r textLength (%d) must not be > 32768" % (name, textLength))
+				text = data[textOffset:textOffset+textLength]
+				assert len(text) == textLength
+				text = tounicode(text, encoding='utf-8')
+				if text:
+					programs[name] = text
+			if isExtra:
+				self.extraPrograms = programs
+			else:
+				self.glyphPrograms = programs
+
 	def compile(self, ttFont):
 		if not hasattr(self, "glyphPrograms"):
 			self.glyphPrograms = {}
@@ -43,22 +84,22 @@
 		data = b''
 		indextable = ttFont[self.indextable]
 		glyphNames = ttFont.getGlyphOrder()
-		
+
 		indices = []
 		for i in range(len(glyphNames)):
 			if len(data) % 2:
 				data = data + b"\015"  # align on 2-byte boundaries, fill with return chars. Yum.
 			name = glyphNames[i]
 			if name in self.glyphPrograms:
-				text = self.glyphPrograms[name]
+				text = tobytes(self.glyphPrograms[name], encoding="utf-8")
 			else:
 				text = b""
 			textLength = len(text)
 			if textLength >= 0x8000:
-				textLength = 0x8000  # XXX ???
+				textLength = 0x8000
 			indices.append((i, textLength, len(data)))
 			data = data + text
-		
+
 		extra_indices = []
 		codes = sorted(self.extras.items())
 		for i in range(len(codes)):
@@ -66,17 +107,17 @@
 				data = data + b"\015"  # align on 2-byte boundaries, fill with return chars.
 			code, name = codes[i]
 			if name in self.extraPrograms:
-				text = self.extraPrograms[name]
+				text = tobytes(self.extraPrograms[name], encoding="utf-8")
 			else:
 				text = b""
 			textLength = len(text)
 			if textLength >= 0x8000:
-				textLength = 0x8000  # XXX ???
+				textLength = 0x8000
 			extra_indices.append((code, textLength, len(data)))
 			data = data + text
 		indextable.set(indices, extra_indices)
 		return data
-	
+
 	def toXML(self, writer, ttFont):
 		names = sorted(self.glyphPrograms.keys())
 		writer.newline()
@@ -103,7 +144,7 @@
 			writer.endtag("extraProgram")
 			writer.newline()
 			writer.newline()
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		if not hasattr(self, "glyphPrograms"):
 			self.glyphPrograms = {}
@@ -114,4 +155,3 @@
 			self.glyphPrograms[attrs["name"]] = text
 		elif name == "extraProgram":
 			self.extraPrograms[attrs["name"]] = text
-
diff --git a/Lib/fontTools/ttLib/tables/T_S_I__2.py b/Lib/fontTools/ttLib/tables/T_S_I__2.py
index 15c02ab..7d41ee8 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I__2.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I__2.py
@@ -1,8 +1,16 @@
+""" TSI{0,1,2,3,5} are private tables used by Microsoft Visual TrueType (VTT)
+tool to store its hinting source data.
+
+TSI2 is the index table containing the lengths and offsets for the glyph
+programs that are contained in the TSI3 table. It uses the same format as
+the TSI0 table.
+"""
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
 from fontTools import ttLib
 
 superclass = ttLib.getTableClass("TSI0")
 
 class table_T_S_I__2(superclass):
-	
-	dependencies = ["TSI3"]
 
+	dependencies = ["TSI3"]
diff --git a/Lib/fontTools/ttLib/tables/T_S_I__3.py b/Lib/fontTools/ttLib/tables/T_S_I__3.py
index eb4087c..ee95d06 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I__3.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I__3.py
@@ -1,11 +1,16 @@
+""" TSI{0,1,2,3,5} are private tables used by Microsoft Visual TrueType (VTT)
+tool to store its hinting source data.
+
+TSI3 contains the text of the glyph programs in the form of 'VTTTalk' code.
+"""
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
 from fontTools import ttLib
 
 superclass = ttLib.getTableClass("TSI1")
 
 class table_T_S_I__3(superclass):
-	
+
 	extras = {0xfffa: "reserved0", 0xfffb: "reserved1", 0xfffc: "reserved2", 0xfffd: "reserved3"}
-	
+
 	indextable = "TSI2"
-
-
diff --git a/Lib/fontTools/ttLib/tables/T_S_I__5.py b/Lib/fontTools/ttLib/tables/T_S_I__5.py
index 8fa801b..dbf9e5a 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I__5.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I__5.py
@@ -1,3 +1,8 @@
+""" TSI{0,1,2,3,5} are private tables used by Microsoft Visual TrueType (VTT)
+tool to store its hinting source data.
+
+TSI5 contains the VTT character groups.
+"""
 from __future__ import print_function, division, absolute_import
 from fontTools.misc.py23 import *
 from fontTools.misc.textTools import safeEval
@@ -7,7 +12,7 @@
 
 
 class table_T_S_I__5(DefaultTable.DefaultTable):
-	
+
 	def decompile(self, data, ttFont):
 		numGlyphs = ttFont['maxp'].numGlyphs
 		assert len(data) == 2 * numGlyphs
@@ -18,26 +23,25 @@
 		self.glyphGrouping = {}
 		for i in range(numGlyphs):
 			self.glyphGrouping[ttFont.getGlyphName(i)] = a[i]
-	
+
 	def compile(self, ttFont):
 		glyphNames = ttFont.getGlyphOrder()
 		a = array.array("H")
 		for i in range(len(glyphNames)):
-			a.append(self.glyphGrouping[glyphNames[i]])
+			a.append(self.glyphGrouping.get(glyphNames[i], 0))
 		if sys.byteorder != "big":
 			a.byteswap()
 		return a.tostring()
-	
+
 	def toXML(self, writer, ttFont):
 		names = sorted(self.glyphGrouping.keys())
 		for glyphName in names:
 			writer.simpletag("glyphgroup", name=glyphName, value=self.glyphGrouping[glyphName])
 			writer.newline()
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		if not hasattr(self, "glyphGrouping"):
 			self.glyphGrouping = {}
 		if name != "glyphgroup":
 			return
 		self.glyphGrouping[attrs["name"]] = safeEval(attrs["value"])
-
diff --git a/Lib/fontTools/ttLib/tables/T_T_F_A_.py b/Lib/fontTools/ttLib/tables/T_T_F_A_.py
new file mode 100644
index 0000000..8ff8d1b
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/T_T_F_A_.py
@@ -0,0 +1,6 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from . import asciiTable
+
+class table_T_T_F_A_(asciiTable.asciiTable):
+	pass
diff --git a/Lib/fontTools/ttLib/tables/TupleVariation.py b/Lib/fontTools/ttLib/tables/TupleVariation.py
new file mode 100644
index 0000000..5fa71c8
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/TupleVariation.py
@@ -0,0 +1,623 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.fixedTools import fixedToFloat, floatToFixed, otRound
+from fontTools.misc.textTools import safeEval
+import array
+import io
+import logging
+import struct
+import sys
+
+
+# https://www.microsoft.com/typography/otspec/otvarcommonformats.htm
+
+EMBEDDED_PEAK_TUPLE = 0x8000
+INTERMEDIATE_REGION = 0x4000
+PRIVATE_POINT_NUMBERS = 0x2000
+
+DELTAS_ARE_ZERO = 0x80
+DELTAS_ARE_WORDS = 0x40
+DELTA_RUN_COUNT_MASK = 0x3f
+
+POINTS_ARE_WORDS = 0x80
+POINT_RUN_COUNT_MASK = 0x7f
+
+TUPLES_SHARE_POINT_NUMBERS = 0x8000
+TUPLE_COUNT_MASK = 0x0fff
+TUPLE_INDEX_MASK = 0x0fff
+
+log = logging.getLogger(__name__)
+
+
+class TupleVariation(object):
+	def __init__(self, axes, coordinates):
+		self.axes = axes.copy()
+		self.coordinates = coordinates[:]
+
+	def __repr__(self):
+		axes = ",".join(sorted(["%s=%s" % (name, value) for (name, value) in self.axes.items()]))
+		return "<TupleVariation %s %s>" % (axes, self.coordinates)
+
+	def __eq__(self, other):
+		return self.coordinates == other.coordinates and self.axes == other.axes
+
+	def getUsedPoints(self):
+		result = set()
+		for i, point in enumerate(self.coordinates):
+			if point is not None:
+				result.add(i)
+		return result
+
+	def hasImpact(self):
+		"""Returns True if this TupleVariation has any visible impact.
+
+		If the result is False, the TupleVariation can be omitted from the font
+		without making any visible difference.
+		"""
+		for c in self.coordinates:
+			if c is not None:
+				return True
+		return False
+
+	def toXML(self, writer, axisTags):
+		writer.begintag("tuple")
+		writer.newline()
+		for axis in axisTags:
+			value = self.axes.get(axis)
+			if value is not None:
+				minValue, value, maxValue = (float(v) for v in value)
+				defaultMinValue = min(value, 0.0)  # -0.3 --> -0.3; 0.7 --> 0.0
+				defaultMaxValue = max(value, 0.0)  # -0.3 -->  0.0; 0.7 --> 0.7
+				if minValue == defaultMinValue and maxValue == defaultMaxValue:
+					writer.simpletag("coord", axis=axis, value=value)
+				else:
+					writer.simpletag("coord", axis=axis, value=value, min=minValue, max=maxValue)
+				writer.newline()
+		wrote_any_deltas = False
+		for i, delta in enumerate(self.coordinates):
+			if type(delta) == tuple and len(delta) == 2:
+				writer.simpletag("delta", pt=i, x=delta[0], y=delta[1])
+				writer.newline()
+				wrote_any_deltas = True
+			elif type(delta) == int:
+				writer.simpletag("delta", cvt=i, value=delta)
+				writer.newline()
+				wrote_any_deltas = True
+			elif delta is not None:
+				log.error("bad delta format")
+				writer.comment("bad delta #%d" % i)
+				writer.newline()
+				wrote_any_deltas = True
+		if not wrote_any_deltas:
+			writer.comment("no deltas")
+			writer.newline()
+		writer.endtag("tuple")
+		writer.newline()
+
+	def fromXML(self, name, attrs, _content):
+		if name == "coord":
+			axis = attrs["axis"]
+			value = float(attrs["value"])
+			defaultMinValue = min(value, 0.0)  # -0.3 --> -0.3; 0.7 --> 0.0
+			defaultMaxValue = max(value, 0.0)  # -0.3 -->  0.0; 0.7 --> 0.7
+			minValue = float(attrs.get("min", defaultMinValue))
+			maxValue = float(attrs.get("max", defaultMaxValue))
+			self.axes[axis] = (minValue, value, maxValue)
+		elif name == "delta":
+			if "pt" in attrs:
+				point = safeEval(attrs["pt"])
+				x = safeEval(attrs["x"])
+				y = safeEval(attrs["y"])
+				self.coordinates[point] = (x, y)
+			elif "cvt" in attrs:
+				cvt = safeEval(attrs["cvt"])
+				value = safeEval(attrs["value"])
+				self.coordinates[cvt] = value
+			else:
+				log.warning("bad delta format: %s" %
+				            ", ".join(sorted(attrs.keys())))
+
+	def compile(self, axisTags, sharedCoordIndices, sharedPoints):
+		tupleData = []
+
+		assert all(tag in axisTags for tag in self.axes.keys()), ("Unknown axis tag found.", self.axes.keys(), axisTags)
+
+		coord = self.compileCoord(axisTags)
+		if coord in sharedCoordIndices:
+			flags = sharedCoordIndices[coord]
+		else:
+			flags = EMBEDDED_PEAK_TUPLE
+			tupleData.append(coord)
+
+		intermediateCoord = self.compileIntermediateCoord(axisTags)
+		if intermediateCoord is not None:
+			flags |= INTERMEDIATE_REGION
+			tupleData.append(intermediateCoord)
+
+		points = self.getUsedPoints()
+		if sharedPoints == points:
+			# Only use the shared points if they are identical to the actually used points
+			auxData = self.compileDeltas(sharedPoints)
+			usesSharedPoints = True
+		else:
+			flags |= PRIVATE_POINT_NUMBERS
+			numPointsInGlyph = len(self.coordinates)
+			auxData = self.compilePoints(points, numPointsInGlyph) + self.compileDeltas(points)
+			usesSharedPoints = False
+
+		tupleData = struct.pack('>HH', len(auxData), flags) + bytesjoin(tupleData)
+		return (tupleData, auxData, usesSharedPoints)
+
+	def compileCoord(self, axisTags):
+		result = []
+		for axis in axisTags:
+			_minValue, value, _maxValue = self.axes.get(axis, (0.0, 0.0, 0.0))
+			result.append(struct.pack(">h", floatToFixed(value, 14)))
+		return bytesjoin(result)
+
+	def compileIntermediateCoord(self, axisTags):
+		needed = False
+		for axis in axisTags:
+			minValue, value, maxValue = self.axes.get(axis, (0.0, 0.0, 0.0))
+			defaultMinValue = min(value, 0.0)  # -0.3 --> -0.3; 0.7 --> 0.0
+			defaultMaxValue = max(value, 0.0)  # -0.3 -->  0.0; 0.7 --> 0.7
+			if (minValue != defaultMinValue) or (maxValue != defaultMaxValue):
+				needed = True
+				break
+		if not needed:
+			return None
+		minCoords = []
+		maxCoords = []
+		for axis in axisTags:
+			minValue, value, maxValue = self.axes.get(axis, (0.0, 0.0, 0.0))
+			minCoords.append(struct.pack(">h", floatToFixed(minValue, 14)))
+			maxCoords.append(struct.pack(">h", floatToFixed(maxValue, 14)))
+		return bytesjoin(minCoords + maxCoords)
+
+	@staticmethod
+	def decompileCoord_(axisTags, data, offset):
+		coord = {}
+		pos = offset
+		for axis in axisTags:
+			coord[axis] = fixedToFloat(struct.unpack(">h", data[pos:pos+2])[0], 14)
+			pos += 2
+		return coord, pos
+
+	@staticmethod
+	def compilePoints(points, numPointsInGlyph):
+		# If the set consists of all points in the glyph, it gets encoded with
+		# a special encoding: a single zero byte.
+		if len(points) == numPointsInGlyph:
+			return b"\0"
+
+		# In the 'gvar' table, the packing of point numbers is a little surprising.
+		# It consists of multiple runs, each being a delta-encoded list of integers.
+		# For example, the point set {17, 18, 19, 20, 21, 22, 23} gets encoded as
+		# [6, 17, 1, 1, 1, 1, 1, 1]. The first value (6) is the run length minus 1.
+		# There are two types of runs, with values being either 8 or 16 bit unsigned
+		# integers.
+		points = list(points)
+		points.sort()
+		numPoints = len(points)
+
+		# The binary representation starts with the total number of points in the set,
+		# encoded into one or two bytes depending on the value.
+		if numPoints < 0x80:
+			result = [bytechr(numPoints)]
+		else:
+			result = [bytechr((numPoints >> 8) | 0x80) + bytechr(numPoints & 0xff)]
+
+		MAX_RUN_LENGTH = 127
+		pos = 0
+		lastValue = 0
+		while pos < numPoints:
+			run = io.BytesIO()
+			runLength = 0
+			useByteEncoding = None
+			while pos < numPoints and runLength <= MAX_RUN_LENGTH:
+				curValue = points[pos]
+				delta = curValue - lastValue
+				if useByteEncoding is None:
+					useByteEncoding = 0 <= delta <= 0xff
+				if useByteEncoding and (delta > 0xff or delta < 0):
+					# we need to start a new run (which will not use byte encoding)
+					break
+				# TODO This never switches back to a byte-encoding from a short-encoding.
+				# That's suboptimal.
+				if useByteEncoding:
+					run.write(bytechr(delta))
+				else:
+					run.write(bytechr(delta >> 8))
+					run.write(bytechr(delta & 0xff))
+				lastValue = curValue
+				pos += 1
+				runLength += 1
+			if useByteEncoding:
+				runHeader = bytechr(runLength - 1)
+			else:
+				runHeader = bytechr((runLength - 1) | POINTS_ARE_WORDS)
+			result.append(runHeader)
+			result.append(run.getvalue())
+
+		return bytesjoin(result)
+
+	@staticmethod
+	def decompilePoints_(numPoints, data, offset, tableTag):
+		"""(numPoints, data, offset, tableTag) --> ([point1, point2, ...], newOffset)"""
+		assert tableTag in ('cvar', 'gvar')
+		pos = offset
+		numPointsInData = byteord(data[pos])
+		pos += 1
+		if (numPointsInData & POINTS_ARE_WORDS) != 0:
+			numPointsInData = (numPointsInData & POINT_RUN_COUNT_MASK) << 8 | byteord(data[pos])
+			pos += 1
+		if numPointsInData == 0:
+			return (range(numPoints), pos)
+
+		result = []
+		while len(result) < numPointsInData:
+			runHeader = byteord(data[pos])
+			pos += 1
+			numPointsInRun = (runHeader & POINT_RUN_COUNT_MASK) + 1
+			point = 0
+			if (runHeader & POINTS_ARE_WORDS) != 0:
+				points = array.array("H")
+				pointsSize = numPointsInRun * 2
+			else:
+				points = array.array("B")
+				pointsSize = numPointsInRun
+			points.fromstring(data[pos:pos+pointsSize])
+			if sys.byteorder != "big":
+				points.byteswap()
+
+			assert len(points) == numPointsInRun
+			pos += pointsSize
+
+			result.extend(points)
+
+		# Convert relative to absolute
+		absolute = []
+		current = 0
+		for delta in result:
+			current += delta
+			absolute.append(current)
+		result = absolute
+		del absolute
+
+		badPoints = {str(p) for p in result if p < 0 or p >= numPoints}
+		if badPoints:
+			log.warning("point %s out of range in '%s' table" %
+			            (",".join(sorted(badPoints)), tableTag))
+		return (result, pos)
+
+	def compileDeltas(self, points):
+		deltaX = []
+		deltaY = []
+		for p in sorted(list(points)):
+			c = self.coordinates[p]
+			if type(c) is tuple and len(c) == 2:
+				deltaX.append(c[0])
+				deltaY.append(c[1])
+			elif type(c) is int:
+				deltaX.append(c)
+			elif c is not None:
+				raise ValueError("invalid type of delta: %s" % type(c))
+		return self.compileDeltaValues_(deltaX) + self.compileDeltaValues_(deltaY)
+
+	@staticmethod
+	def compileDeltaValues_(deltas):
+		"""[value1, value2, value3, ...] --> bytestring
+
+		Emits a sequence of runs. Each run starts with a
+		byte-sized header whose 6 least significant bits
+		(header & 0x3F) indicate how many values are encoded
+		in this run. The stored length is the actual length
+		minus one; run lengths are thus in the range [1..64].
+		If the header byte has its most significant bit (0x80)
+		set, all values in this run are zero, and no data
+		follows. Otherwise, the header byte is followed by
+		((header & 0x3F) + 1) signed values.  If (header &
+		0x40) is clear, the delta values are stored as signed
+		bytes; if (header & 0x40) is set, the delta values are
+		signed 16-bit integers.
+		"""  # Explaining the format because the 'gvar' spec is hard to understand.
+		stream = io.BytesIO()
+		pos = 0
+		while pos < len(deltas):
+			value = deltas[pos]
+			if value == 0:
+				pos = TupleVariation.encodeDeltaRunAsZeroes_(deltas, pos, stream)
+			elif value >= -128 and value <= 127:
+				pos = TupleVariation.encodeDeltaRunAsBytes_(deltas, pos, stream)
+			else:
+				pos = TupleVariation.encodeDeltaRunAsWords_(deltas, pos, stream)
+		return stream.getvalue()
+
+	@staticmethod
+	def encodeDeltaRunAsZeroes_(deltas, offset, stream):
+		runLength = 0
+		pos = offset
+		numDeltas = len(deltas)
+		while pos < numDeltas and runLength < 64 and deltas[pos] == 0:
+			pos += 1
+			runLength += 1
+		assert runLength >= 1 and runLength <= 64
+		stream.write(bytechr(DELTAS_ARE_ZERO | (runLength - 1)))
+		return pos
+
+	@staticmethod
+	def encodeDeltaRunAsBytes_(deltas, offset, stream):
+		runLength = 0
+		pos = offset
+		numDeltas = len(deltas)
+		while pos < numDeltas and runLength < 64:
+			value = deltas[pos]
+			if value < -128 or value > 127:
+				break
+			# Within a byte-encoded run of deltas, a single zero
+			# is best stored literally as 0x00 value. However,
+			# if are two or more zeroes in a sequence, it is
+			# better to start a new run. For example, the sequence
+			# of deltas [15, 15, 0, 15, 15] becomes 6 bytes
+			# (04 0F 0F 00 0F 0F) when storing the zero value
+			# literally, but 7 bytes (01 0F 0F 80 01 0F 0F)
+			# when starting a new run.
+			if value == 0 and pos+1 < numDeltas and deltas[pos+1] == 0:
+				break
+			pos += 1
+			runLength += 1
+		assert runLength >= 1 and runLength <= 64
+		stream.write(bytechr(runLength - 1))
+		for i in range(offset, pos):
+			stream.write(struct.pack('b', otRound(deltas[i])))
+		return pos
+
+	@staticmethod
+	def encodeDeltaRunAsWords_(deltas, offset, stream):
+		runLength = 0
+		pos = offset
+		numDeltas = len(deltas)
+		while pos < numDeltas and runLength < 64:
+			value = deltas[pos]
+			# Within a word-encoded run of deltas, it is easiest
+			# to start a new run (with a different encoding)
+			# whenever we encounter a zero value. For example,
+			# the sequence [0x6666, 0, 0x7777] needs 7 bytes when
+			# storing the zero literally (42 66 66 00 00 77 77),
+			# and equally 7 bytes when starting a new run
+			# (40 66 66 80 40 77 77).
+			if value == 0:
+				break
+
+			# Within a word-encoded run of deltas, a single value
+			# in the range (-128..127) should be encoded literally
+			# because it is more compact. For example, the sequence
+			# [0x6666, 2, 0x7777] becomes 7 bytes when storing
+			# the value literally (42 66 66 00 02 77 77), but 8 bytes
+			# when starting a new run (40 66 66 00 02 40 77 77).
+			isByteEncodable = lambda value: value >= -128 and value <= 127
+			if isByteEncodable(value) and pos+1 < numDeltas and isByteEncodable(deltas[pos+1]):
+				break
+			pos += 1
+			runLength += 1
+		assert runLength >= 1 and runLength <= 64
+		stream.write(bytechr(DELTAS_ARE_WORDS | (runLength - 1)))
+		for i in range(offset, pos):
+			stream.write(struct.pack('>h', otRound(deltas[i])))
+		return pos
+
+	@staticmethod
+	def decompileDeltas_(numDeltas, data, offset):
+		"""(numDeltas, data, offset) --> ([delta, delta, ...], newOffset)"""
+		result = []
+		pos = offset
+		while len(result) < numDeltas:
+			runHeader = byteord(data[pos])
+			pos += 1
+			numDeltasInRun = (runHeader & DELTA_RUN_COUNT_MASK) + 1
+			if (runHeader & DELTAS_ARE_ZERO) != 0:
+				result.extend([0] * numDeltasInRun)
+			else:
+				if (runHeader & DELTAS_ARE_WORDS) != 0:
+					deltas = array.array("h")
+					deltasSize = numDeltasInRun * 2
+				else:
+					deltas = array.array("b")
+					deltasSize = numDeltasInRun
+				deltas.fromstring(data[pos:pos+deltasSize])
+				if sys.byteorder != "big":
+					deltas.byteswap()
+				assert len(deltas) == numDeltasInRun
+				pos += deltasSize
+				result.extend(deltas)
+		assert len(result) == numDeltas
+		return (result, pos)
+
+	@staticmethod
+	def getTupleSize_(flags, axisCount):
+		size = 4
+		if (flags & EMBEDDED_PEAK_TUPLE) != 0:
+			size += axisCount * 2
+		if (flags & INTERMEDIATE_REGION) != 0:
+			size += axisCount * 4
+		return size
+
+
+def decompileSharedTuples(axisTags, sharedTupleCount, data, offset):
+	result = []
+	for _ in range(sharedTupleCount):
+		t, offset = TupleVariation.decompileCoord_(axisTags, data, offset)
+		result.append(t)
+	return result
+
+
+def compileSharedTuples(axisTags, variations):
+	coordCount = {}
+	for var in variations:
+		coord = var.compileCoord(axisTags)
+		coordCount[coord] = coordCount.get(coord, 0) + 1
+	sharedCoords = [(count, coord)
+					for (coord, count) in coordCount.items() if count > 1]
+	sharedCoords.sort(reverse=True)
+	MAX_NUM_SHARED_COORDS = TUPLE_INDEX_MASK + 1
+	sharedCoords = sharedCoords[:MAX_NUM_SHARED_COORDS]
+	return [c[1] for c in sharedCoords]  # Strip off counts.
+
+
+def compileTupleVariationStore(variations, pointCount,
+                               axisTags, sharedTupleIndices,
+                               useSharedPoints=True):
+	variations = [v for v in variations if v.hasImpact()]
+	if len(variations) == 0:
+		return (0, b"", b"")
+
+	# Each glyph variation tuples modifies a set of control points. To
+	# indicate which exact points are getting modified, a single tuple
+	# can either refer to a shared set of points, or the tuple can
+	# supply its private point numbers.  Because the impact of sharing
+	# can be positive (no need for a private point list) or negative
+	# (need to supply 0,0 deltas for unused points), it is not obvious
+	# how to determine which tuples should take their points from the
+	# shared pool versus have their own. Perhaps we should resort to
+	# brute force, and try all combinations? However, if a glyph has n
+	# variation tuples, we would need to try 2^n combinations (because
+	# each tuple may or may not be part of the shared set). How many
+	# variations tuples do glyphs have?
+	#
+	#   Skia.ttf: {3: 1, 5: 11, 6: 41, 7: 62, 8: 387, 13: 1, 14: 3}
+	#   JamRegular.ttf: {3: 13, 4: 122, 5: 1, 7: 4, 8: 1, 9: 1, 10: 1}
+	#   BuffaloGalRegular.ttf: {1: 16, 2: 13, 4: 2, 5: 4, 6: 19, 7: 1, 8: 3, 9: 8}
+	#   (Reading example: In Skia.ttf, 41 glyphs have 6 variation tuples).
+	#
+
+	# Is this even worth optimizing? If we never use a shared point
+	# list, the private lists will consume 112K for Skia, 5K for
+	# BuffaloGalRegular, and 15K for JamRegular. If we always use a
+	# shared point list, the shared lists will consume 16K for Skia,
+	# 3K for BuffaloGalRegular, and 10K for JamRegular. However, in
+	# the latter case the delta arrays will become larger, but I
+	# haven't yet measured by how much. From gut feeling (which may be
+	# wrong), the optimum is to share some but not all points;
+	# however, then we would need to try all combinations.
+	#
+	# For the time being, we try two variants and then pick the better one:
+	# (a) each tuple supplies its own private set of points;
+	# (b) all tuples refer to a shared set of points, which consists of
+	#     "every control point in the glyph that has explicit deltas".
+	usedPoints = set()
+	for v in variations:
+		usedPoints |= v.getUsedPoints()
+	tuples = []
+	data = []
+	someTuplesSharePoints = False
+	sharedPointVariation = None # To keep track of a variation that uses shared points
+	for v in variations:
+		privateTuple, privateData, _ = v.compile(
+			axisTags, sharedTupleIndices, sharedPoints=None)
+		sharedTuple, sharedData, usesSharedPoints = v.compile(
+			axisTags, sharedTupleIndices, sharedPoints=usedPoints)
+		if useSharedPoints and (len(sharedTuple) + len(sharedData)) < (len(privateTuple) + len(privateData)):
+			tuples.append(sharedTuple)
+			data.append(sharedData)
+			someTuplesSharePoints |= usesSharedPoints
+			sharedPointVariation = v
+		else:
+			tuples.append(privateTuple)
+			data.append(privateData)
+	if someTuplesSharePoints:
+		# Use the last of the variations that share points for compiling the packed point data
+		data = sharedPointVariation.compilePoints(usedPoints, len(sharedPointVariation.coordinates)) + bytesjoin(data)
+		tupleVariationCount = TUPLES_SHARE_POINT_NUMBERS | len(tuples)
+	else:
+		data = bytesjoin(data)
+		tupleVariationCount = len(tuples)
+	tuples = bytesjoin(tuples)
+	return tupleVariationCount, tuples, data
+
+
+def decompileTupleVariationStore(tableTag, axisTags,
+                                 tupleVariationCount, pointCount, sharedTuples,
+							     data, pos, dataPos):
+	numAxes = len(axisTags)
+	result = []
+	if (tupleVariationCount & TUPLES_SHARE_POINT_NUMBERS) != 0:
+		sharedPoints, dataPos = TupleVariation.decompilePoints_(
+			pointCount, data, dataPos, tableTag)
+	else:
+		sharedPoints = []
+	for _ in range(tupleVariationCount & TUPLE_COUNT_MASK):
+		dataSize, flags = struct.unpack(">HH", data[pos:pos+4])
+		tupleSize = TupleVariation.getTupleSize_(flags, numAxes)
+		tupleData = data[pos : pos + tupleSize]
+		pointDeltaData = data[dataPos : dataPos + dataSize]
+		result.append(decompileTupleVariation_(
+			pointCount, sharedTuples, sharedPoints,
+			tableTag, axisTags, tupleData, pointDeltaData))
+		pos += tupleSize
+		dataPos += dataSize
+	return result
+
+
+def decompileTupleVariation_(pointCount, sharedTuples, sharedPoints,
+							 tableTag, axisTags, data, tupleData):
+	assert tableTag in ("cvar", "gvar"), tableTag
+	flags = struct.unpack(">H", data[2:4])[0]
+	pos = 4
+	if (flags & EMBEDDED_PEAK_TUPLE) == 0:
+		peak = sharedTuples[flags & TUPLE_INDEX_MASK]
+	else:
+		peak, pos = TupleVariation.decompileCoord_(axisTags, data, pos)
+	if (flags & INTERMEDIATE_REGION) != 0:
+		start, pos = TupleVariation.decompileCoord_(axisTags, data, pos)
+		end, pos = TupleVariation.decompileCoord_(axisTags, data, pos)
+	else:
+		start, end = inferRegion_(peak)
+	axes = {}
+	for axis in axisTags:
+		region = start[axis], peak[axis], end[axis]
+		if region != (0.0, 0.0, 0.0):
+			axes[axis] = region
+	pos = 0
+	if (flags & PRIVATE_POINT_NUMBERS) != 0:
+		points, pos = TupleVariation.decompilePoints_(
+			pointCount, tupleData, pos, tableTag)
+	else:
+		points = sharedPoints
+
+	deltas = [None] * pointCount
+
+	if tableTag == "cvar":
+		deltas_cvt, pos = TupleVariation.decompileDeltas_(
+			len(points), tupleData, pos)
+		for p, delta in zip(points, deltas_cvt):
+			if 0 <= p < pointCount:
+				deltas[p] = delta
+
+	elif tableTag == "gvar":
+		deltas_x, pos = TupleVariation.decompileDeltas_(
+			len(points), tupleData, pos)
+		deltas_y, pos = TupleVariation.decompileDeltas_(
+			len(points), tupleData, pos)
+		for p, x, y in zip(points, deltas_x, deltas_y):
+			if 0 <= p < pointCount:
+				deltas[p] = (x, y)
+
+	return TupleVariation(axes, deltas)
+
+
+def inferRegion_(peak):
+	"""Infer start and end for a (non-intermediate) region
+
+	This helper function computes the applicability region for
+	variation tuples whose INTERMEDIATE_REGION flag is not set in the
+	TupleVariationHeader structure.  Variation tuples apply only to
+	certain regions of the variation space; outside that region, the
+	tuple has no effect.  To make the binary encoding more compact,
+	TupleVariationHeaders can omit the intermediateStartTuple and
+	intermediateEndTuple fields.
+    """
+	start, end = {}, {}
+	for (axis, value) in peak.items():
+		start[axis] = min(value, 0.0)  # -0.3 --> -0.3; 0.7 --> 0.0
+		end[axis] = max(value, 0.0)  # -0.3 -->  0.0; 0.7 --> 0.7
+	return (start, end)
diff --git a/Lib/fontTools/ttLib/tables/V_D_M_X_.py b/Lib/fontTools/ttLib/tables/V_D_M_X_.py
new file mode 100644
index 0000000..abca3bf
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/V_D_M_X_.py
@@ -0,0 +1,234 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from . import DefaultTable
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import safeEval
+import struct
+
+VDMX_HeaderFmt = """
+	>                 # big endian
+	version:     H    # Version number (0 or 1)
+	numRecs:     H    # Number of VDMX groups present
+	numRatios:   H    # Number of aspect ratio groupings
+"""
+# the VMDX header is followed by an array of RatRange[numRatios] (i.e. aspect
+# ratio ranges);
+VDMX_RatRangeFmt = """
+	>                 # big endian
+	bCharSet:    B    # Character set
+	xRatio:      B    # Value to use for x-Ratio
+	yStartRatio: B    # Starting y-Ratio value
+	yEndRatio:   B    # Ending y-Ratio value
+"""
+# followed by an array of offset[numRatios] from start of VDMX table to the
+# VDMX Group for this ratio range (offsets will be re-calculated on compile);
+# followed by an array of Group[numRecs] records;
+VDMX_GroupFmt = """
+	>                 # big endian
+	recs:        H    # Number of height records in this group
+	startsz:     B    # Starting yPelHeight
+	endsz:       B    # Ending yPelHeight
+"""
+# followed by an array of vTable[recs] records.
+VDMX_vTableFmt = """
+	>                 # big endian
+	yPelHeight:  H    # yPelHeight to which values apply
+	yMax:        h    # Maximum value (in pels) for this yPelHeight
+	yMin:        h    # Minimum value (in pels) for this yPelHeight
+"""
+
+
+class table_V_D_M_X_(DefaultTable.DefaultTable):
+
+	def decompile(self, data, ttFont):
+		pos = 0  # track current position from to start of VDMX table
+		dummy, data = sstruct.unpack2(VDMX_HeaderFmt, data, self)
+		pos += sstruct.calcsize(VDMX_HeaderFmt)
+		self.ratRanges = []
+		for i in range(self.numRatios):
+			ratio, data = sstruct.unpack2(VDMX_RatRangeFmt, data)
+			pos += sstruct.calcsize(VDMX_RatRangeFmt)
+			# the mapping between a ratio and a group is defined further below
+			ratio['groupIndex'] = None
+			self.ratRanges.append(ratio)
+		lenOffset = struct.calcsize('>H')
+		_offsets = []  # temporarily store offsets to groups
+		for i in range(self.numRatios):
+			offset = struct.unpack('>H', data[0:lenOffset])[0]
+			data = data[lenOffset:]
+			pos += lenOffset
+			_offsets.append(offset)
+		self.groups = []
+		for groupIndex in range(self.numRecs):
+			# the offset to this group from beginning of the VDMX table
+			currOffset = pos
+			group, data = sstruct.unpack2(VDMX_GroupFmt, data)
+			# the group lenght and bounding sizes are re-calculated on compile
+			recs = group.pop('recs')
+			startsz = group.pop('startsz')
+			endsz = group.pop('endsz')
+			pos += sstruct.calcsize(VDMX_GroupFmt)
+			for j in range(recs):
+				vTable, data = sstruct.unpack2(VDMX_vTableFmt, data)
+				vTableLength = sstruct.calcsize(VDMX_vTableFmt)
+				pos += vTableLength
+				# group is a dict of (yMax, yMin) tuples keyed by yPelHeight
+				group[vTable['yPelHeight']] = (vTable['yMax'], vTable['yMin'])
+			# make sure startsz and endsz match the calculated values
+			minSize = min(group.keys())
+			maxSize = max(group.keys())
+			assert startsz == minSize, \
+				"startsz (%s) must equal min yPelHeight (%s): group %d" % \
+				(group.startsz, minSize, groupIndex)
+			assert endsz == maxSize, \
+				"endsz (%s) must equal max yPelHeight (%s): group %d" % \
+				(group.endsz, maxSize, groupIndex)
+			self.groups.append(group)
+			# match the defined offsets with the current group's offset
+			for offsetIndex, offsetValue in enumerate(_offsets):
+				# when numRecs < numRatios there can more than one ratio range
+				# sharing the same VDMX group
+				if currOffset == offsetValue:
+					# map the group with the ratio range thas has the same
+					# index as the offset to that group (it took me a while..)
+					self.ratRanges[offsetIndex]['groupIndex'] = groupIndex
+		# check that all ratio ranges have a group
+		for i in range(self.numRatios):
+			ratio = self.ratRanges[i]
+			if ratio['groupIndex'] is None:
+				from fontTools import ttLib
+				raise ttLib.TTLibError(
+					"no group defined for ratRange %d" % i)
+
+	def _getOffsets(self):
+		"""
+		Calculate offsets to VDMX_Group records.
+		For each ratRange return a list of offset values from the beginning of
+		the VDMX table to a VDMX_Group.
+		"""
+		lenHeader = sstruct.calcsize(VDMX_HeaderFmt)
+		lenRatRange = sstruct.calcsize(VDMX_RatRangeFmt)
+		lenOffset = struct.calcsize('>H')
+		lenGroupHeader = sstruct.calcsize(VDMX_GroupFmt)
+		lenVTable = sstruct.calcsize(VDMX_vTableFmt)
+		# offset to the first group
+		pos = lenHeader + self.numRatios*lenRatRange + self.numRatios*lenOffset
+		groupOffsets = []
+		for group in self.groups:
+			groupOffsets.append(pos)
+			lenGroup = lenGroupHeader + len(group) * lenVTable
+			pos += lenGroup  # offset to next group
+		offsets = []
+		for ratio in self.ratRanges:
+			groupIndex = ratio['groupIndex']
+			offsets.append(groupOffsets[groupIndex])
+		return offsets
+
+	def compile(self, ttFont):
+		if not(self.version == 0 or self.version == 1):
+			from fontTools import ttLib
+			raise ttLib.TTLibError(
+				"unknown format for VDMX table: version %s" % self.version)
+		data = sstruct.pack(VDMX_HeaderFmt, self)
+		for ratio in self.ratRanges:
+			data += sstruct.pack(VDMX_RatRangeFmt, ratio)
+		# recalculate offsets to VDMX groups
+		for offset in self._getOffsets():
+			data += struct.pack('>H', offset)
+		for group in self.groups:
+			recs = len(group)
+			startsz = min(group.keys())
+			endsz = max(group.keys())
+			gHeader = {'recs': recs, 'startsz': startsz, 'endsz': endsz}
+			data += sstruct.pack(VDMX_GroupFmt, gHeader)
+			for yPelHeight, (yMax, yMin) in sorted(group.items()):
+				vTable = {'yPelHeight': yPelHeight, 'yMax': yMax, 'yMin': yMin}
+				data += sstruct.pack(VDMX_vTableFmt, vTable)
+		return data
+
+	def toXML(self, writer, ttFont):
+		writer.simpletag("version", value=self.version)
+		writer.newline()
+		writer.begintag("ratRanges")
+		writer.newline()
+		for ratio in self.ratRanges:
+			groupIndex = ratio['groupIndex']
+			writer.simpletag(
+				"ratRange",
+				bCharSet=ratio['bCharSet'],
+				xRatio=ratio['xRatio'],
+				yStartRatio=ratio['yStartRatio'],
+				yEndRatio=ratio['yEndRatio'],
+				groupIndex=groupIndex
+				)
+			writer.newline()
+		writer.endtag("ratRanges")
+		writer.newline()
+		writer.begintag("groups")
+		writer.newline()
+		for groupIndex in range(self.numRecs):
+			group = self.groups[groupIndex]
+			recs = len(group)
+			startsz = min(group.keys())
+			endsz = max(group.keys())
+			writer.begintag("group", index=groupIndex)
+			writer.newline()
+			writer.comment("recs=%d, startsz=%d, endsz=%d" %
+							(recs, startsz, endsz))
+			writer.newline()
+			for yPelHeight, (yMax, yMin) in sorted(group.items()):
+				writer.simpletag(
+					"record",
+					[('yPelHeight', yPelHeight), ('yMax', yMax), ('yMin', yMin)])
+				writer.newline()
+			writer.endtag("group")
+			writer.newline()
+		writer.endtag("groups")
+		writer.newline()
+
+	def fromXML(self, name, attrs, content, ttFont):
+		if name == "version":
+			self.version = safeEval(attrs["value"])
+		elif name == "ratRanges":
+			if not hasattr(self, "ratRanges"):
+				self.ratRanges = []
+			for element in content:
+				if not isinstance(element, tuple):
+					continue
+				name, attrs, content = element
+				if name == "ratRange":
+					if not hasattr(self, "numRatios"):
+						self.numRatios = 1
+					else:
+						self.numRatios += 1
+					ratio = {
+						"bCharSet": safeEval(attrs["bCharSet"]),
+						"xRatio": safeEval(attrs["xRatio"]),
+						"yStartRatio": safeEval(attrs["yStartRatio"]),
+						"yEndRatio": safeEval(attrs["yEndRatio"]),
+						"groupIndex": safeEval(attrs["groupIndex"])
+						}
+					self.ratRanges.append(ratio)
+		elif name == "groups":
+			if not hasattr(self, "groups"):
+				self.groups = []
+			for element in content:
+				if not isinstance(element, tuple):
+					continue
+				name, attrs, content = element
+				if name == "group":
+					if not hasattr(self, "numRecs"):
+						self.numRecs = 1
+					else:
+						self.numRecs += 1
+					group = {}
+					for element in content:
+						if not isinstance(element, tuple):
+							continue
+						name, attrs, content = element
+						if name == "record":
+							yPelHeight = safeEval(attrs["yPelHeight"])
+							yMax = safeEval(attrs["yMax"])
+							yMin = safeEval(attrs["yMin"])
+							group[yPelHeight] = (yMax, yMin)
+					self.groups.append(group)
diff --git a/Lib/fontTools/ttLib/tables/V_O_R_G_.py b/Lib/fontTools/ttLib/tables/V_O_R_G_.py
index 19f25b5..8b2c317 100644
--- a/Lib/fontTools/ttLib/tables/V_O_R_G_.py
+++ b/Lib/fontTools/ttLib/tables/V_O_R_G_.py
@@ -37,12 +37,11 @@
 
 		list(map(operator.setitem, [vOrig]*self.numVertOriginYMetrics, names, vids))
 
-
 	def compile(self, ttFont):
 		vorgs = list(self.VOriginRecords.values())
 		names = list(self.VOriginRecords.keys())
 		nameMap = ttFont.getReverseGlyphMap()
-		lenRecords = len(vorgs) 
+		lenRecords = len(vorgs)
 		try:
 			gids = map(operator.getitem, [nameMap]*lenRecords, names)
 		except KeyError:
@@ -94,7 +93,6 @@
 		elif "value" in attrs:
 			setattr(self, name, safeEval(attrs["value"]))
 
-
 	def __getitem__(self, glyphSelector):
 		if isinstance(glyphSelector, int):
 			# its a gid, convert to glyph name
@@ -102,7 +100,7 @@
 
 		if glyphSelector not in self.VOriginRecords:
 			return self.defaultVertOriginY
-			
+
 		return self.VOriginRecords[glyphSelector]
 
 	def __setitem__(self, glyphSelector, value):
@@ -120,7 +118,7 @@
 
 class VOriginRecord(object):
 
-	def __init__(self, name = None, vOrigin = None):
+	def __init__(self, name=None, vOrigin=None):
 		self.glyphName = name
 		self.vOrigin = vOrigin
 
diff --git a/Lib/fontTools/ttLib/tables/V_V_A_R_.py b/Lib/fontTools/ttLib/tables/V_V_A_R_.py
new file mode 100644
index 0000000..530f0e3
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/V_V_A_R_.py
@@ -0,0 +1,7 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from .otBase import BaseTTXConverter
+
+
+class table_V_V_A_R_(BaseTTXConverter):
+	pass
diff --git a/Lib/fontTools/ttLib/tables/__init__.py b/Lib/fontTools/ttLib/tables/__init__.py
index bdf8d96..79a50fd 100644
--- a/Lib/fontTools/ttLib/tables/__init__.py
+++ b/Lib/fontTools/ttLib/tables/__init__.py
@@ -1,29 +1,45 @@
+
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+
 # DON'T EDIT! This file is generated by MetaTools/buildTableList.py.
 def _moduleFinderHint():
 	"""Dummy function to let modulefinder know what tables may be
 	dynamically imported. Generated by MetaTools/buildTableList.py.
+
+		>>> _moduleFinderHint()
 	"""
 	from . import B_A_S_E_
 	from . import C_B_D_T_
 	from . import C_B_L_C_
 	from . import C_F_F_
+	from . import C_F_F__2
 	from . import C_O_L_R_
 	from . import C_P_A_L_
 	from . import D_S_I_G_
 	from . import E_B_D_T_
 	from . import E_B_L_C_
 	from . import F_F_T_M_
+	from . import F__e_a_t
 	from . import G_D_E_F_
 	from . import G_M_A_P_
 	from . import G_P_K_G_
 	from . import G_P_O_S_
 	from . import G_S_U_B_
+	from . import G__l_a_t
+	from . import G__l_o_c
+	from . import H_V_A_R_
 	from . import J_S_T_F_
 	from . import L_T_S_H_
+	from . import M_A_T_H_
 	from . import M_E_T_A_
+	from . import M_V_A_R_
 	from . import O_S_2f_2
 	from . import S_I_N_G_
+	from . import S_T_A_T_
 	from . import S_V_G_
+	from . import S__i_l_f
+	from . import S__i_l_l
 	from . import T_S_I_B_
 	from . import T_S_I_D_
 	from . import T_S_I_J_
@@ -35,22 +51,46 @@
 	from . import T_S_I__2
 	from . import T_S_I__3
 	from . import T_S_I__5
+	from . import T_T_F_A_
+	from . import V_D_M_X_
 	from . import V_O_R_G_
+	from . import V_V_A_R_
+	from . import _a_n_k_r
+	from . import _a_v_a_r
+	from . import _b_s_l_n
+	from . import _c_i_d_g
 	from . import _c_m_a_p
+	from . import _c_v_a_r
 	from . import _c_v_t
+	from . import _f_e_a_t
 	from . import _f_p_g_m
+	from . import _f_v_a_r
 	from . import _g_a_s_p
+	from . import _g_c_i_d
 	from . import _g_l_y_f
+	from . import _g_v_a_r
 	from . import _h_d_m_x
 	from . import _h_e_a_d
 	from . import _h_h_e_a
 	from . import _h_m_t_x
 	from . import _k_e_r_n
+	from . import _l_c_a_r
 	from . import _l_o_c_a
+	from . import _l_t_a_g
 	from . import _m_a_x_p
+	from . import _m_e_t_a
+	from . import _m_o_r_t
+	from . import _m_o_r_x
 	from . import _n_a_m_e
+	from . import _o_p_b_d
 	from . import _p_o_s_t
 	from . import _p_r_e_p
+	from . import _p_r_o_p
 	from . import _s_b_i_x
+	from . import _t_r_a_k
 	from . import _v_h_e_a
 	from . import _v_m_t_x
+
+if __name__ == "__main__":
+	import doctest, sys
+	sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/ttLib/tables/_a_n_k_r.py b/Lib/fontTools/ttLib/tables/_a_n_k_r.py
new file mode 100644
index 0000000..3a1aa08
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_a_n_k_r.py
@@ -0,0 +1,13 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from .otBase import BaseTTXConverter
+
+
+# The anchor point table provides a way to define anchor points.
+# These are points within the coordinate space of a given glyph,
+# independent of the control points used to render the glyph.
+# Anchor points are used in conjunction with the 'kerx' table.
+#
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6ankr.html
+class table__a_n_k_r(BaseTTXConverter):
+    pass
diff --git a/Lib/fontTools/ttLib/tables/_a_v_a_r.py b/Lib/fontTools/ttLib/tables/_a_v_a_r.py
new file mode 100644
index 0000000..57601c5
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_a_v_a_r.py
@@ -0,0 +1,101 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools import ttLib
+from fontTools.misc import sstruct
+from fontTools.misc.fixedTools import fixedToFloat, floatToFixed
+from fontTools.misc.textTools import safeEval
+from fontTools.ttLib import TTLibError
+from . import DefaultTable
+import array
+import struct
+import logging
+
+
+log = logging.getLogger(__name__)
+
+# Apple's documentation of 'avar':
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6avar.html
+
+AVAR_HEADER_FORMAT = """
+    > # big endian
+    majorVersion:  H
+    minorVersion:  H
+    reserved:      H
+    axisCount:     H
+"""
+assert sstruct.calcsize(AVAR_HEADER_FORMAT) == 8, sstruct.calcsize(AVAR_HEADER_FORMAT)
+
+
+class table__a_v_a_r(DefaultTable.DefaultTable):
+
+    dependencies = ["fvar"]
+
+    def __init__(self, tag=None):
+        DefaultTable.DefaultTable.__init__(self, tag)
+        self.segments = {}
+
+    def compile(self, ttFont):
+        axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
+        header = {
+            "majorVersion": 1,
+            "minorVersion": 0,
+            "reserved": 0,
+            "axisCount": len(axisTags)
+        }
+        result = [sstruct.pack(AVAR_HEADER_FORMAT, header)]
+        for axis in axisTags:
+            mappings = sorted(self.segments[axis].items())
+            result.append(struct.pack(">H", len(mappings)))
+            for key, value in mappings:
+                fixedKey = floatToFixed(key, 14)
+                fixedValue = floatToFixed(value, 14)
+                result.append(struct.pack(">hh", fixedKey, fixedValue))
+        return bytesjoin(result)
+
+    def decompile(self, data, ttFont):
+        axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
+        header = {}
+        headerSize = sstruct.calcsize(AVAR_HEADER_FORMAT)
+        header = sstruct.unpack(AVAR_HEADER_FORMAT, data[0:headerSize])
+        majorVersion = header["majorVersion"]
+        if majorVersion != 1:
+            raise TTLibError("unsupported 'avar' version %d" % majorVersion)
+        pos = headerSize
+        for axis in axisTags:
+            segments = self.segments[axis] = {}
+            numPairs = struct.unpack(">H", data[pos:pos+2])[0]
+            pos = pos + 2
+            for _ in range(numPairs):
+                fromValue, toValue = struct.unpack(">hh", data[pos:pos+4])
+                segments[fixedToFloat(fromValue, 14)] = fixedToFloat(toValue, 14)
+                pos = pos + 4
+
+    def toXML(self, writer, ttFont):
+        axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
+        for axis in axisTags:
+            writer.begintag("segment", axis=axis)
+            writer.newline()
+            for key, value in sorted(self.segments[axis].items()):
+                # roundtrip float -> fixed -> float to normalize TTX output
+                # as dumped after decompiling or straight from varLib
+                key = fixedToFloat(floatToFixed(key, 14), 14)
+                value = fixedToFloat(floatToFixed(value, 14), 14)
+                writer.simpletag("mapping", **{"from": key, "to": value})
+                writer.newline()
+            writer.endtag("segment")
+            writer.newline()
+
+    def fromXML(self, name, attrs, content, ttFont):
+        if name == "segment":
+            axis = attrs["axis"]
+            segment = self.segments[axis] = {}
+            for element in content:
+                if isinstance(element, tuple):
+                    elementName, elementAttrs, _ = element
+                    if elementName == "mapping":
+                        fromValue = safeEval(elementAttrs["from"])
+                        toValue = safeEval(elementAttrs["to"])
+                        if fromValue in segment:
+                            log.warning("duplicate entry for %s in axis '%s'",
+                                        fromValue, axis)
+                        segment[fromValue] = toValue
diff --git a/Lib/fontTools/ttLib/tables/_b_s_l_n.py b/Lib/fontTools/ttLib/tables/_b_s_l_n.py
new file mode 100644
index 0000000..31d8399
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_b_s_l_n.py
@@ -0,0 +1,8 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from .otBase import BaseTTXConverter
+
+
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6bsln.html
+class table__b_s_l_n(BaseTTXConverter):
+    pass
diff --git a/Lib/fontTools/ttLib/tables/_c_i_d_g.py b/Lib/fontTools/ttLib/tables/_c_i_d_g.py
new file mode 100644
index 0000000..1d6c502
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_c_i_d_g.py
@@ -0,0 +1,20 @@
+# coding: utf-8
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from .otBase import BaseTTXConverter
+
+
+# The AAT ‘cidg’ table has almost the same structure as ‘gidc’,
+# just mapping CIDs to GlyphIDs instead of the reverse direction.
+#
+# It is useful for fonts that may be used by a PDF renderer in lieu of
+# a font reference with a known glyph collection but no subsetted
+# glyphs.  For instance, a PDF can say “please use a font conforming
+# to Adobe-Japan-1”; the ‘cidg’ mapping is necessary if the font is,
+# say, a TrueType font.  ‘gidc’ is lossy for this purpose and is
+# obsoleted by ‘cidg’.
+#
+# For example, the first font in /System/Library/Fonts/PingFang.ttc
+# (which Apple ships pre-installed on MacOS 10.12.6) has a ‘cidg’ table.
+class table__c_i_d_g(BaseTTXConverter):
+    pass
diff --git a/Lib/fontTools/ttLib/tables/_c_m_a_p.py b/Lib/fontTools/ttLib/tables/_c_m_a_p.py
index 0519e78..eccf69e 100644
--- a/Lib/fontTools/ttLib/tables/_c_m_a_p.py
+++ b/Lib/fontTools/ttLib/tables/_c_m_a_p.py
@@ -1,6 +1,7 @@
 from __future__ import print_function, division, absolute_import
 from fontTools.misc.py23 import *
 from fontTools.misc.textTools import safeEval, readHex
+from fontTools.misc.encodingTools import getEncoding
 from fontTools.ttLib import getSearchRange
 from fontTools.unicode import Unicode
 from . import DefaultTable
@@ -8,17 +9,65 @@
 import struct
 import array
 import operator
+import logging
 
 
+log = logging.getLogger(__name__)
+
+
+def _make_map(font, chars, gids):
+	assert len(chars) == len(gids)
+	cmap = {}
+	glyphOrder = font.getGlyphOrder()
+	for char,gid in zip(chars,gids):
+		if gid is 0:
+			continue
+		try:
+			name = glyphOrder[gid]
+		except IndexError:
+			name = font.getGlyphName(gid)
+		cmap[char] = name
+	return cmap
+
 class table__c_m_a_p(DefaultTable.DefaultTable):
-	
+
 	def getcmap(self, platformID, platEncID):
 		for subtable in self.tables:
-			if (subtable.platformID == platformID and 
+			if (subtable.platformID == platformID and
 					subtable.platEncID == platEncID):
 				return subtable
 		return None # not found
-	
+
+	def getBestCmap(self, cmapPreferences=((3, 10), (0, 6), (0, 4), (3, 1), (0, 3), (0, 2), (0, 1), (0, 0))):
+		"""Return the 'best' unicode cmap dictionary available in the font,
+		or None, if no unicode cmap subtable is available.
+
+		By default it will search for the following (platformID, platEncID)
+		pairs:
+			(3, 10), (0, 6), (0, 4), (3, 1), (0, 3), (0, 2), (0, 1), (0, 0)
+		This can be customized via the cmapPreferences argument.
+		"""
+		for platformID, platEncID in cmapPreferences:
+			cmapSubtable = self.getcmap(platformID, platEncID)
+			if cmapSubtable is not None:
+				return cmapSubtable.cmap
+		return None  # None of the requested cmap subtables were found
+
+	def buildReversed(self):
+		"""Returns a reverse cmap such as {'one':{0x31}, 'A':{0x41,0x391}}.
+
+		The values are sets of Unicode codepoints because
+		some fonts map different codepoints to the same glyph.
+		For example, U+0041 LATIN CAPITAL LETTER A and U+0391
+		GREEK CAPITAL LETTER ALPHA are sometimes the same glyph.
+		"""
+		result = {}
+		for subtable in self.tables:
+			if subtable.isUnicode():
+				for codepoint, name in subtable.cmap.items():
+					result.setdefault(name, set()).add(codepoint)
+		return result
+
 	def decompile(self, data, ttFont):
 		tableVersion, numSubTables = struct.unpack(">HH", data[:4])
 		self.tableVersion = int(tableVersion)
@@ -33,14 +82,14 @@
 				format, reserved, length = struct.unpack(">HHL", data[offset:offset+8])
 			elif format in [14]:
 				format, length = struct.unpack(">HL", data[offset:offset+6])
-				
+
 			if not length:
-				print("Error: cmap subtable is reported as having zero length: platformID %s, platEncID %s,  format %s offset %s. Skipping table." % (platformID, platEncID,format, offset))
+				log.error(
+					"cmap subtable is reported as having zero length: platformID %s, "
+					"platEncID %s, format %s offset %s. Skipping table.",
+					platformID, platEncID, format, offset)
 				continue
-			if format not in cmap_classes:
-				table = cmap_format_unknown(format)
-			else:
-				table = cmap_classes[format](format)
+			table = CmapSubtable.newSubtable(format)
 			table.platformID = platformID
 			table.platEncID = platEncID
 			# Note that by default we decompile only the subtable header info;
@@ -48,11 +97,12 @@
 			# subtable is referenced.
 			table.decompileHeader(data[offset:offset+int(length)], ttFont)
 			if offset in seenOffsets:
+				table.data = None # Mark as decompiled
 				table.cmap = tables[seenOffsets[offset]].cmap
 			else:
 				seenOffsets[offset] = i
 			tables.append(table)
-	
+
 	def compile(self, ttFont):
 		self.tables.sort()    # sort according to the spec; see CmapSubtable.__lt__()
 		numSubTables = len(self.tables)
@@ -73,13 +123,13 @@
 					tableData = tableData + chunk
 			data = data + struct.pack(">HHl", table.platformID, table.platEncID, offset)
 		return data + tableData
-	
+
 	def toXML(self, writer, ttFont):
 		writer.simpletag("tableVersion", version=self.tableVersion)
 		writer.newline()
 		for table in self.tables:
 			table.toXML(writer, ttFont)
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		if name == "tableVersion":
 			self.tableVersion = safeEval(attrs["version"])
@@ -89,10 +139,7 @@
 		if not hasattr(self, "tables"):
 			self.tables = []
 		format = safeEval(name[12:])
-		if format not in cmap_classes:
-			table = cmap_format_unknown(format)
-		else:
-			table = cmap_classes[format](format)
+		table = CmapSubtable.newSubtable(format)
 		table.platformID = safeEval(attrs["platformID"])
 		table.platEncID = safeEval(attrs["platEncID"])
 		table.fromXML(name, attrs, content, ttFont)
@@ -100,7 +147,18 @@
 
 
 class CmapSubtable(object):
-	
+
+	@staticmethod
+	def getSubtableClass(format):
+		"""Return the subtable class for a format."""
+		return cmap_classes.get(format, cmap_format_unknown)
+
+	@staticmethod
+	def newSubtable(format):
+		"""Return a new instance of a subtable for format."""
+		subtableClass = CmapSubtable.getSubtableClass(format)
+		return subtableClass(format)
+
 	def __init__(self, format):
 		self.format = format
 		self.data = None
@@ -113,11 +171,11 @@
 		if self.data is None:
 			raise AttributeError(attr)
 		self.decompile(None, None) # use saved data.
-		self.data = None # Once this table has been decompiled, make sure we don't
-						# just return the original data. Also avoids recursion when
-						# called with an attribute that the cmap subtable doesn't have.
+		self.data = None	# Once this table has been decompiled, make sure we don't
+							# just return the original data. Also avoids recursion when
+							# called with an attribute that the cmap subtable doesn't have.
 		return getattr(self, attr)
-	
+
 	def decompileHeader(self, data, ttFont):
 		format, length, language = struct.unpack(">HHH", data[:6])
 		assert len(data) == length, "corrupt cmap table format %d (data length: %d, header length: %d)" % (format, len(data), length)
@@ -139,9 +197,21 @@
 		writer.endtag(self.__class__.__name__)
 		writer.newline()
 
+	def getEncoding(self, default=None):
+		"""Returns the Python encoding name for this cmap subtable based on its platformID,
+		platEncID, and language.  If encoding for these values is not known, by default
+		None is returned.  That can be overriden by passing a value to the default
+		argument.
+
+		Note that if you want to choose a "preferred" cmap subtable, most of the time
+		self.isUnicode() is what you want as that one only returns true for the modern,
+		commonly used, Unicode-compatible triplets, not the legacy ones.
+		"""
+		return getEncoding(self.platformID, self.platEncID, self.language, default)
+
 	def isUnicode(self):
 		return (self.platformID == 0 or
-			(self.platformID == 3 and self.platEncID in [1, 10]))
+			(self.platformID == 3 and self.platEncID in [0, 1, 10]))
 
 	def isSymbol(self):
 		return self.platformID == 3 and self.platEncID == 0
@@ -153,7 +223,7 @@
 			if isUnicode:
 				writer.comment(Unicode[code])
 			writer.newline()
-	
+
 	def __lt__(self, other):
 		if not isinstance(other, CmapSubtable):
 			return NotImplemented
@@ -173,40 +243,35 @@
 
 
 class cmap_format_0(CmapSubtable):
-	
+
 	def decompile(self, data, ttFont):
 		# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
 		# If not, someone is calling  the subtable decompile() directly, and must provide both args.
 		if data is not None and ttFont is not None:
-			self.decompileHeader(data[offset:offset+int(length)], ttFont)
+			self.decompileHeader(data, ttFont)
 		else:
 			assert (data is None and ttFont is None), "Need both data and ttFont arguments"
 		data = self.data # decompileHeader assigns the data after the header to self.data
 		assert 262 == self.length, "Format 0 cmap subtable not 262 bytes"
-		glyphIdArray = array.array("B")
-		glyphIdArray.fromstring(self.data)
-		self.cmap = cmap = {}
-		lenArray = len(glyphIdArray)
-		charCodes = list(range(lenArray))
-		names = map(self.ttFont.getGlyphName, glyphIdArray)
-		list(map(operator.setitem, [cmap]*lenArray, charCodes, names))
+		gids = array.array("B")
+		gids.fromstring(self.data)
+		charCodes = list(range(len(gids)))
+		self.cmap = _make_map(self.ttFont, charCodes, gids)
 
-	
 	def compile(self, ttFont):
 		if self.data:
 			return struct.pack(">HHH", 0, 262, self.language) + self.data
 
-		charCodeList = sorted(self.cmap.items())
-		charCodes = [entry[0] for entry in charCodeList]
-		valueList = [entry[1] for entry in charCodeList]
-		assert charCodes == list(range(256))
-		valueList = map(ttFont.getGlyphID, valueList)
+		cmap = self.cmap
+		assert set(cmap.keys()).issubset(range(256))
+		getGlyphID = ttFont.getGlyphID
+		valueList = [getGlyphID(cmap[i]) if i in cmap else 0 for i in range(256)]
 
-		glyphIdArray = array.array("B", valueList)
-		data = struct.pack(">HHH", 0, 262, self.language) + glyphIdArray.tostring()
+		gids = array.array("B", valueList)
+		data = struct.pack(">HHH", 0, 262, self.language) + gids.tostring()
 		assert len(data) == 262
 		return data
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		self.language = safeEval(attrs["language"])
 		if not hasattr(self, "cmap"):
@@ -229,9 +294,9 @@
 		self.idDelta = None
 		self.idRangeOffset = None
 		self.glyphIndexArray = []
-		
+
 class cmap_format_2(CmapSubtable):
-	
+
 	def setIDDelta(self, subHeader):
 		subHeader.idDelta = 0
 		# find the minGI which is not zero.
@@ -241,13 +306,13 @@
 				minGI = gid
 		# The lowest gid in glyphIndexArray, after subtracting idDelta, must be 1.
 		# idDelta is a short, and must be between -32K and 32K. minGI can be between 1 and 64K.
-		# We would like to pick an idDelta such that the first glyphArray GID is 1, 
+		# We would like to pick an idDelta such that the first glyphArray GID is 1,
 		# so that we are more likely to be able to combine glypharray GID subranges.
 		# This means that we have a problem when minGI is > 32K
 		# Since the final gi is reconstructed from the glyphArray GID by:
 		#    (short)finalGID = (gid +  idDelta) % 0x10000),
 		# we can get from a glypharray GID of 1 to a final GID of 65K by subtracting 2, and casting the
-		# negative number to an unsigned short. 
+		# negative number to an unsigned short.
 
 		if  (minGI > 1):
 			if  minGI > 0x7FFF:
@@ -257,15 +322,14 @@
 			idDelta = subHeader.idDelta
 			for i in range(subHeader.entryCount):
 				gid = subHeader.glyphIndexArray[i]
-				if gid > 0: 
-					subHeader.glyphIndexArray[i] = gid - idDelta 
-
+				if gid > 0:
+					subHeader.glyphIndexArray[i] = gid - idDelta
 
 	def decompile(self, data, ttFont):
 		# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
 		# If not, someone is calling  the subtable decompile() directly, and must provide both args.
 		if data is not None and ttFont is not None:
-			self.decompileHeader(data[offset:offset+int(length)], ttFont)
+			self.decompileHeader(data, ttFont)
 		else:
 			assert (data is None and ttFont is None), "Need both data and ttFont arguments"
 
@@ -280,7 +344,7 @@
 			allKeys.byteswap()
 		subHeaderKeys = [ key//8 for key in allKeys]
 		maxSubHeaderindex = max(subHeaderKeys)
-	
+
 		#Load subHeaders
 		subHeaderList = []
 		pos = 0
@@ -296,14 +360,14 @@
 				giList.byteswap()
 			subHeader.glyphIndexArray = giList
 			subHeaderList.append(subHeader)
-		# How this gets processed. 
+		# How this gets processed.
 		# Charcodes may be one or two bytes.
 		# The first byte of a charcode is mapped through the  subHeaderKeys, to select
 		# a subHeader. For any subheader but 0, the next byte is then mapped through the
-		# selected subheader. If subheader Index 0 is selected, then the byte itself is 
+		# selected subheader. If subheader Index 0 is selected, then the byte itself is
 		# mapped through the subheader, and there is no second byte.
 		# Then assume that the subsequent byte is the first byte of the next charcode,and repeat.
-		# 
+		#
 		# Each subheader references a range in the glyphIndexArray whose length is entryCount.
 		# The range in glyphIndexArray referenced by a sunheader may overlap with the range in glyphIndexArray
 		# referenced by another subheader.
@@ -315,7 +379,7 @@
 		# firstChar and EntryCount values. If the byte value is outside the subrange, then the glyphIndex is zero
 		# (e.g. glyph not in font).
 		# If the byte index is in the subrange, then an offset index is calculated as (byteIndex - firstChar).
-		# The index to glyphIndex mapping is a subrange of the glyphIndexArray. You find the start of the subrange by 
+		# The index to glyphIndex mapping is a subrange of the glyphIndexArray. You find the start of the subrange by
 		# counting idRangeOffset bytes from the idRangeOffset word. The first value in this subrange is the
 		# glyphIndex for the index firstChar. The offset index should then be used in this array to get the glyphIndex.
 		# Example for Logocut-Medium
@@ -329,12 +393,12 @@
 		# [257], [1]=2  	from charcode [129, 65]
 		# [258], [2]=3  	from charcode [129, 66]
 		# [259], [3]=4  	from charcode [129, 67]
-		# So, the glyphIndex = 3 from the array. Then if idDelta is not zero and the glyph ID is not zero, 
+		# So, the glyphIndex = 3 from the array. Then if idDelta is not zero and the glyph ID is not zero,
 		# add it to the glyphID to get the final glyphIndex
 		# value. In this case the final glyph index = 3+ 42 -> 45 for the final glyphIndex. Whew!
-		
+
 		self.data = b""
-		self.cmap = cmap = {}
+		cmap = {}
 		notdefGI = 0
 		for firstByte in range(256):
 			subHeadindex = subHeaderKeys[firstByte]
@@ -363,21 +427,13 @@
 							continue
 						cmap[charCode] = gi
 				# If not subHeader.entryCount, then all char codes with this first byte are
-				# mapped to .notdef. We can skip this subtable, and leave the glyphs un-encoded, which is the 
+				# mapped to .notdef. We can skip this subtable, and leave the glyphs un-encoded, which is the
 				# same as mapping it to .notdef.
-		# cmap values are GID's.
-		glyphOrder = self.ttFont.getGlyphOrder()
+
 		gids = list(cmap.values())
 		charCodes = list(cmap.keys())
-		lenCmap = len(gids)
-		try:
-			names = list(map(operator.getitem, [glyphOrder]*lenCmap, gids ))
-		except IndexError:
-			getGlyphName = self.ttFont.getGlyphName
-			names = list(map(getGlyphName, gids ))
-		list(map(operator.setitem, [cmap]*lenCmap, charCodes, names))
-	
-		
+		self.cmap = _make_map(self.ttFont, charCodes, gids)
+
 	def compile(self, ttFont):
 		if self.data:
 			return struct.pack(">HHH", self.format, self.length, self.language) + self.data
@@ -388,7 +444,7 @@
 		charCodes = [item[0] for item in items]
 		names = [item[1] for item in items]
 		nameMap = ttFont.getReverseGlyphMap()
-		lenCharCodes = len(charCodes) 
+		lenCharCodes = len(charCodes)
 		try:
 			gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
 		except KeyError:
@@ -413,8 +469,8 @@
 					gids.append(gid)
 
 		# Process the (char code to gid) item list  in char code order.
-		# By definition, all one byte char codes map to subheader 0. 
-		# For all the two byte char codes, we assume that the first byte maps maps to the empty subhead (with an entry count of 0, 
+		# By definition, all one byte char codes map to subheader 0.
+		# For all the two byte char codes, we assume that the first byte maps maps to the empty subhead (with an entry count of 0,
 		# which defines all char codes in its range to map to notdef) unless proven otherwise.
 		# Note that since the char code items are processed in char code order, all the char codes with the
 		# same first byte are in sequential order.
@@ -433,8 +489,7 @@
 			subHeader.idDelta = 0
 			subHeader.idRangeOffset = 0
 			subHeaderList.append(subHeader)
-			
-		
+
 		lastFirstByte = -1
 		items = zip(charCodes, gids)
 		for charCode, gid in items:
@@ -471,7 +526,7 @@
 					subHeader.glyphIndexArray.append(notdefGI)
 				subHeader.glyphIndexArray.append(gid)
 				subHeader.entryCount = subHeader.entryCount + codeDiff + 1
-					
+
 		# fix GI's and iDelta of last subheader that we we added to the subheader array.
 		self.setIDDelta(subHeader)
 
@@ -488,12 +543,12 @@
 				subHeaderKeys[index] = emptySubheadIndex
 		# Since this is the last subheader, the GlyphIndex Array starts two bytes after the start of the
 		# idRangeOffset word of this subHeader. We can safely point to the first entry in the GlyphIndexArray,
-		# since the first subrange of the GlyphIndexArray is for subHeader 0, which always starts with 
+		# since the first subrange of the GlyphIndexArray is for subHeader 0, which always starts with
 		# charcode 0 and GID 0.
-		
-		idRangeOffset = (len(subHeaderList)-1)*8  + 2 # offset to beginning of glyphIDArray from first subheader idRangeOffset.
+
+		idRangeOffset = (len(subHeaderList)-1)*8 + 2 # offset to beginning of glyphIDArray from first subheader idRangeOffset.
 		subheadRangeLen = len(subHeaderList) -1 # skip last special empty-set subheader; we've already hardocodes its idRangeOffset to 2.
-		for index in range(subheadRangeLen): 
+		for index in range(subheadRangeLen):
 			subHeader = subHeaderList[index]
 			subHeader.idRangeOffset = 0
 			for j  in range(index):
@@ -502,7 +557,7 @@
 					subHeader.idRangeOffset = prevSubhead.idRangeOffset - (index-j)*8
 					subHeader.glyphIndexArray = []
 					break
-			if subHeader.idRangeOffset == 0: # didn't find one. 
+			if subHeader.idRangeOffset == 0: # didn't find one.
 				subHeader.idRangeOffset = idRangeOffset
 				idRangeOffset = (idRangeOffset - 8) + subHeader.entryCount*2 # one less subheader, one more subArray.
 			else:
@@ -524,7 +579,6 @@
 		assert (len(data) == length), "Error: cmap format 2 is not same length as calculated! actual: " + str(len(data))+ " calc : " + str(length)
 		return data
 
-
 	def fromXML(self, name, attrs, content, ttFont):
 		self.language = safeEval(attrs["language"])
 		if not hasattr(self, "cmap"):
@@ -556,17 +610,17 @@
 	# to do well with the fonts I tested: none became bigger, many became smaller.
 	if startCode == endCode:
 		return [], [endCode]
-	
+
 	lastID = cmap[startCode]
 	lastCode = startCode
 	inOrder = None
 	orderedBegin = None
 	subRanges = []
-	
+
 	# Gather subranges in which the glyph IDs are consecutive.
 	for code in range(startCode + 1, endCode + 1):
 		glyphID = cmap[code]
-		
+
 		if glyphID - 1 == lastID:
 			if inOrder is None or not inOrder:
 				inOrder = 1
@@ -576,14 +630,14 @@
 				inOrder = 0
 				subRanges.append((orderedBegin, lastCode))
 				orderedBegin = None
-				
+
 		lastID = glyphID
 		lastCode = code
-	
+
 	if inOrder:
 		subRanges.append((orderedBegin, lastCode))
 	assert lastCode == endCode
-	
+
 	# Now filter out those new subranges that would only make the data bigger.
 	# A new segment cost 8 bytes, not using a new segment costs 2 bytes per
 	# character.
@@ -598,15 +652,15 @@
 		if (e - b + 1) > threshold:
 			newRanges.append((b, e))
 	subRanges = newRanges
-	
+
 	if not subRanges:
 		return [], [endCode]
-	
+
 	if subRanges[0][0] != startCode:
 		subRanges.insert(0, (startCode, subRanges[0][0] - 1))
 	if subRanges[-1][1] != endCode:
 		subRanges.append((subRanges[-1][1] + 1, endCode))
-	
+
 	# Fill the "holes" in the segments list -- those are the segments in which
 	# the glyph IDs are _not_ consecutive.
 	i = 1
@@ -615,7 +669,7 @@
 			subRanges.insert(i, (subRanges[i-1][1] + 1, subRanges[i][0] - 1))
 			i = i + 1
 		i = i + 1
-	
+
 	# Transform the ranges into startCode/endCode lists.
 	start = []
 	end = []
@@ -623,18 +677,18 @@
 		start.append(b)
 		end.append(e)
 	start.pop(0)
-	
+
 	assert len(start) + 1 == len(end)
 	return start, end
 
 
 class cmap_format_4(CmapSubtable):
-	
+
 	def decompile(self, data, ttFont):
 		# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
 		# If not, someone is calling  the subtable decompile() directly, and must provide both args.
 		if data is not None and ttFont is not None:
-			self.decompileHeader(self.data[offset:offset+int(length)], ttFont)
+			self.decompileHeader(data, ttFont)
 		else:
 			assert (data is None and ttFont is None), "Need both data and ttFont arguments"
 
@@ -643,14 +697,14 @@
 					struct.unpack(">4H", data[:8])
 		data = data[8:]
 		segCount = segCountX2 // 2
-		
+
 		allCodes = array.array("H")
 		allCodes.fromstring(data)
 		self.data = data = None
 
 		if sys.byteorder != "big":
 			allCodes.byteswap()
-		
+
 		# divide the data
 		endCode = allCodes[:segCount]
 		allCodes = allCodes[segCount+1:]  # the +1 is skipping the reservedPad field
@@ -686,21 +740,12 @@
 						glyphID = 0  # missing glyph
 					gids.append(glyphID & 0xFFFF)
 
-		self.cmap = cmap = {}
-		lenCmap = len(gids)
-		glyphOrder = self.ttFont.getGlyphOrder()
-		try:
-			names = list(map(operator.getitem, [glyphOrder]*lenCmap, gids ))
-		except IndexError:
-			getGlyphName = self.ttFont.getGlyphName
-			names = list(map(getGlyphName, gids ))
-		list(map(operator.setitem, [cmap]*lenCmap, charCodes, names))
-
+		self.cmap = _make_map(self.ttFont, charCodes, gids)
 
 	def compile(self, ttFont):
 		if self.data:
 			return struct.pack(">HHH", self.format, self.length, self.language) + self.data
-		
+
 		charCodes = list(self.cmap.keys())
 		lenCharCodes = len(charCodes)
 		if lenCharCodes == 0:
@@ -730,11 +775,11 @@
 									gid = ttFont.getGlyphID(name)
 							except:
 								raise KeyError(name)
-	
+
 						gids.append(gid)
 			cmap = {}  # code:glyphID mapping
 			list(map(operator.setitem, [cmap]*len(charCodes), charCodes, gids))
-		
+
 			# Build startCode and endCode lists.
 			# Split the char codes in ranges of consecutive char codes, then split
 			# each range in more ranges of consecutive/not consecutive glyph IDs.
@@ -751,10 +796,12 @@
 				endCode.extend(end)
 				startCode.append(charCode)
 				lastCode = charCode
-			endCode.append(lastCode)
+			start, end = splitRange(startCode[-1], lastCode, cmap)
+			startCode.extend(start)
+			endCode.extend(end)
 			startCode.append(0xffff)
 			endCode.append(0xffff)
-		
+
 		# build up rest of cruft
 		idDelta = []
 		idRangeOffset = []
@@ -773,12 +820,12 @@
 				glyphIndexArray.extend(indices)
 		idDelta.append(1)  # 0xffff + 1 == (tadaa!) 0. So this end code maps to .notdef
 		idRangeOffset.append(0)
-		
+
 		# Insane.
 		segCount = len(endCode)
 		segCountX2 = segCount * 2
 		searchRange, entrySelector, rangeShift = getSearchRange(segCount, 2)
-		
+
 		charCodeArray = array.array("H", endCode + [0] + startCode)
 		idDeltaArray = array.array("H", idDelta)
 		restArray = array.array("H", idRangeOffset + glyphIndexArray)
@@ -789,10 +836,10 @@
 		data = charCodeArray.tostring() + idDeltaArray.tostring() + restArray.tostring()
 
 		length = struct.calcsize(cmap_format_4_format) + len(data)
-		header = struct.pack(cmap_format_4_format, self.format, length, self.language, 
+		header = struct.pack(cmap_format_4_format, self.format, length, self.language,
 				segCountX2, searchRange, entrySelector, rangeShift)
 		return header + data
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		self.language = safeEval(attrs["language"])
 		if not hasattr(self, "cmap"):
@@ -809,12 +856,12 @@
 
 
 class cmap_format_6(CmapSubtable):
-	
+
 	def decompile(self, data, ttFont):
 		# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
 		# If not, someone is calling  the subtable decompile() directly, and must provide both args.
 		if data is not None and ttFont is not None:
-			self.decompileHeader(data[offset:offset+int(length)], ttFont)
+			self.decompileHeader(data, ttFont)
 		else:
 			assert (data is None and ttFont is None), "Need both data and ttFont arguments"
 
@@ -823,45 +870,38 @@
 		firstCode = int(firstCode)
 		data = data[4:]
 		#assert len(data) == 2 * entryCount  # XXX not true in Apple's Helvetica!!!
-		glyphIndexArray = array.array("H")
-		glyphIndexArray.fromstring(data[:2 * int(entryCount)])
+		gids = array.array("H")
+		gids.fromstring(data[:2 * int(entryCount)])
 		if sys.byteorder != "big":
-			glyphIndexArray.byteswap()
+			gids.byteswap()
 		self.data = data = None
 
-		self.cmap = cmap = {}
+		charCodes = list(range(firstCode, firstCode + len(gids)))
+		self.cmap = _make_map(self.ttFont, charCodes, gids)
 
-		lenArray = len(glyphIndexArray)
-		charCodes = list(range(firstCode, firstCode + lenArray))
-		glyphOrder = self.ttFont.getGlyphOrder()
-		try:
-			names = list(map(operator.getitem, [glyphOrder]*lenArray, glyphIndexArray ))
-		except IndexError:
-			getGlyphName = self.ttFont.getGlyphName
-			names = list(map(getGlyphName, glyphIndexArray ))
-		list(map(operator.setitem, [cmap]*lenArray, charCodes, names))
-	
 	def compile(self, ttFont):
 		if self.data:
 			return struct.pack(">HHH", self.format, self.length, self.language) + self.data
 		cmap = self.cmap
-		codes = list(cmap.keys())
+		codes = sorted(cmap.keys())
 		if codes: # yes, there are empty cmap tables.
 			codes = list(range(codes[0], codes[-1] + 1))
 			firstCode = codes[0]
-			valueList = [cmap.get(code, ".notdef") for code in codes]
-			valueList = map(ttFont.getGlyphID, valueList)
-			glyphIndexArray = array.array("H", valueList)
+			valueList = [
+				ttFont.getGlyphID(cmap[code]) if code in cmap else 0
+				for code in codes
+			]
+			gids = array.array("H", valueList)
 			if sys.byteorder != "big":
-				glyphIndexArray.byteswap()
-			data = glyphIndexArray.tostring()
+				gids.byteswap()
+			data = gids.tostring()
 		else:
 			data = b""
 			firstCode = 0
-		header = struct.pack(">HHHHH", 
+		header = struct.pack(">HHHHH",
 				6, len(data) + 10, self.language, firstCode, len(codes))
 		return header + data
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		self.language = safeEval(attrs["language"])
 		if not hasattr(self, "cmap"):
@@ -878,7 +918,7 @@
 
 
 class cmap_format_12_or_13(CmapSubtable):
-	
+
 	def __init__(self, format):
 		self.format = format
 		self.reserved = 0
@@ -887,7 +927,7 @@
 
 	def decompileHeader(self, data, ttFont):
 		format, reserved, length, language, nGroups = struct.unpack(">HHLLL", data[:16])
-		assert len(data) == (16 + nGroups*12) == (length), "corrupt cmap table format %d (data length: %d, header length: %d)" % (format, len(data), length)
+		assert len(data) == (16 + nGroups*12) == (length), "corrupt cmap table format %d (data length: %d, header length: %d)" % (self.format, len(data), length)
 		self.format = format
 		self.reserved = reserved
 		self.length = length
@@ -900,7 +940,7 @@
 		# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
 		# If not, someone is calling  the subtable decompile() directly, and must provide both args.
 		if data is not None and ttFont is not None:
-			self.decompileHeader(data[offset:offset+int(length)], ttFont)
+			self.decompileHeader(data, ttFont)
 		else:
 			assert (data is None and ttFont is None), "Need both data and ttFont arguments"
 
@@ -915,21 +955,13 @@
 			charCodes.extend(list(range(startCharCode, endCharCode +1)))
 			gids.extend(self._computeGIDs(glyphID, lenGroup))
 		self.data = data = None
-		self.cmap = cmap = {}
-		lenCmap = len(gids)
-		glyphOrder = self.ttFont.getGlyphOrder()
-		try:
-			names = list(map(operator.getitem, [glyphOrder]*lenCmap, gids ))
-		except IndexError:
-			getGlyphName = self.ttFont.getGlyphName
-			names = list(map(getGlyphName, gids ))
-		list(map(operator.setitem, [cmap]*lenCmap, charCodes, names))
-	
+		self.cmap = _make_map(self.ttFont, charCodes, gids)
+
 	def compile(self, ttFont):
 		if self.data:
 			return struct.pack(">HHLLL", self.format, self.reserved, self.length, self.language, self.nGroups) + self.data
 		charCodes = list(self.cmap.keys())
-		lenCharCodes = len(charCodes) 
+		lenCharCodes = len(charCodes)
 		names = list(self.cmap.values())
 		nameMap = ttFont.getReverseGlyphMap()
 		try:
@@ -954,7 +986,7 @@
 							raise KeyError(name)
 
 					gids.append(gid)
-		
+
 		cmap = {}  # code:glyphID mapping
 		list(map(operator.setitem, [cmap]*len(charCodes), charCodes, gids))
 
@@ -981,9 +1013,9 @@
 		nGroups = nGroups + 1
 		data = bytesjoin(dataList)
 		lengthSubtable = len(data) +16
-		assert len(data) == (nGroups*12) == (lengthSubtable-16) 
-		return struct.pack(">HHLLL", self.format, self.reserved , lengthSubtable, self.language, nGroups) + data
-	
+		assert len(data) == (nGroups*12) == (lengthSubtable-16)
+		return struct.pack(">HHLLL", self.format, self.reserved, lengthSubtable, self.language, nGroups) + data
+
 	def toXML(self, writer, ttFont):
 		writer.begintag(self.__class__.__name__, [
 				("platformID", self.platformID),
@@ -999,7 +1031,7 @@
 		self._writeCodes(codes, writer)
 		writer.endtag(self.__class__.__name__)
 		writer.newline()
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		self.format = safeEval(attrs["format"])
 		self.reserved = safeEval(attrs["reserved"])
@@ -1020,9 +1052,11 @@
 
 
 class cmap_format_12(cmap_format_12_or_13):
-	def __init__(self, format):
+
+	_format_step = 1
+
+	def __init__(self, format=12):
 		cmap_format_12_or_13.__init__(self, format)
-		self._format_step = 1
 
 	def _computeGIDs(self, startingGlyph, numberOfGlyphs):
 		return list(range(startingGlyph, startingGlyph + numberOfGlyphs))
@@ -1032,9 +1066,11 @@
 
 
 class cmap_format_13(cmap_format_12_or_13):
-	def __init__(self, format):
+
+	_format_step = 0
+
+	def __init__(self, format=13):
 		cmap_format_12_or_13.__init__(self, format)
-		self._format_step = 0
 
 	def _computeGIDs(self, startingGlyph, numberOfGlyphs):
 		return [startingGlyph] * numberOfGlyphs
@@ -1065,21 +1101,21 @@
 		self.language = 0xFF # has no language.
 
 	def decompile(self, data, ttFont):
-		if data is not None and ttFont is not None and ttFont.lazy:
+		if data is not None and ttFont is not None:
 			self.decompileHeader(data, ttFont)
 		else:
 			assert (data is None and ttFont is None), "Need both data and ttFont arguments"
 		data = self.data
-		
+
 		self.cmap = {} # so that clients that expect this to exist in a cmap table won't fail.
 		uvsDict = {}
 		recOffset = 0
 		for n in range(self.numVarSelectorRecords):
-			uvs, defOVSOffset, nonDefUVSOffset =  struct.unpack(">3sLL", data[recOffset:recOffset +11])		
+			uvs, defOVSOffset, nonDefUVSOffset =  struct.unpack(">3sLL", data[recOffset:recOffset +11])
 			recOffset += 11
 			varUVS = cvtToUVS(uvs)
 			if defOVSOffset:
-				startOffset = defOVSOffset  - 10
+				startOffset = defOVSOffset - 10
 				numValues, = struct.unpack(">L", data[startOffset:startOffset+4])
 				startOffset +=4
 				for r in range(numValues):
@@ -1094,9 +1130,9 @@
 						uvsDict[varUVS].extend(localUVList)
 					except KeyError:
 						uvsDict[varUVS] = list(localUVList)
-				
+
 			if nonDefUVSOffset:
-				startOffset = nonDefUVSOffset  - 10
+				startOffset = nonDefUVSOffset - 10
 				numRecs, = struct.unpack(">L", data[startOffset:startOffset+4])
 				startOffset +=4
 				localUVList = []
@@ -1110,9 +1146,9 @@
 					uvsDict[varUVS].extend(localUVList)
 				except KeyError:
 					uvsDict[varUVS] = localUVList
-					
+
 		self.uvsDict = uvsDict
-							
+
 	def toXML(self, writer, ttFont):
 		writer.begintag(self.__class__.__name__, [
 				("platformID", self.platformID),
@@ -1144,8 +1180,8 @@
 		if not hasattr(self, "cmap"):
 			self.cmap = {} # so that clients that expect this to exist in a cmap table won't fail.
 		if not hasattr(self, "uvsDict"):
-			self.uvsDict  = {}
-			uvsDict = self.uvsDict 
+			self.uvsDict = {}
+			uvsDict = self.uvsDict
 
 		for element in content:
 			if not isinstance(element, tuple):
@@ -1162,11 +1198,10 @@
 				uvsDict[uvs].append( [uv, gname])
 			except KeyError:
 				uvsDict[uvs] = [ [uv, gname] ]
-			
 
 	def compile(self, ttFont):
 		if self.data:
-			return struct.pack(">HLL", self.format, self.length , self.numVarSelectorRecords) + self.data
+			return struct.pack(">HLL", self.format, self.length, self.numVarSelectorRecords) + self.data
 
 		uvsDict = self.uvsDict
 		uvsList = sorted(uvsDict.keys())
@@ -1193,7 +1228,7 @@
 						lastUV = defEntry
 						defRecs.append(rec)
 						cnt = 0
-					
+
 				rec = struct.pack(">3sB", cvtFromUVS(lastUV), cnt)
 				defRecs.append(rec)
 
@@ -1218,20 +1253,19 @@
 					data.append(ndrec)
 			else:
 				nonDefUVSOffset = 0
-				
+
 			vrec = struct.pack(">3sLL", cvtFromUVS(uvs), defOVSOffset, nonDefUVSOffset)
 			varSelectorRecords.append(vrec)
-				
+
 		data = bytesjoin(varSelectorRecords) + bytesjoin(data)
 		self.length = 10 + len(data)
-		headerdata = struct.pack(">HLL", self.format, self.length , self.numVarSelectorRecords)
-		self.data = headerdata + data
-	
-		return self.data
-		
-		
+		headerdata = struct.pack(">HLL", self.format, self.length, self.numVarSelectorRecords)
+
+		return headerdata + data
+
+
 class cmap_format_unknown(CmapSubtable):
-	
+
 	def toXML(self, writer, ttFont):
 		cmapName = self.__class__.__name__[:12] + str(self.format)
 		writer.begintag(cmapName, [
@@ -1242,20 +1276,20 @@
 		writer.dumphex(self.data)
 		writer.endtag(cmapName)
 		writer.newline()
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		self.data = readHex(content)
 		self.cmap = {}
-	
+
 	def decompileHeader(self, data, ttFont):
 		self.language = 0  # dummy value
 		self.data = data
-	
+
 	def decompile(self, data, ttFont):
 		# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
 		# If not, someone is calling  the subtable decompile() directly, and must provide both args.
 		if data is not None and ttFont is not None:
-			self.decompileHeader(data[offset:offset+int(length)], ttFont)
+			self.decompileHeader(data, ttFont)
 		else:
 			assert (data is None and ttFont is None), "Need both data and ttFont arguments"
 
@@ -1273,4 +1307,4 @@
 		12: cmap_format_12,
 		13: cmap_format_13,
 		14: cmap_format_14,
-		}
+}
diff --git a/Lib/fontTools/ttLib/tables/_c_v_a_r.py b/Lib/fontTools/ttLib/tables/_c_v_a_r.py
new file mode 100644
index 0000000..c4ebbc9
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_c_v_a_r.py
@@ -0,0 +1,84 @@
+from __future__ import \
+    print_function, division, absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+from . import DefaultTable
+from fontTools.misc import sstruct
+from fontTools.ttLib.tables.TupleVariation import \
+    compileTupleVariationStore, decompileTupleVariationStore, TupleVariation
+
+
+# https://www.microsoft.com/typography/otspec/cvar.htm
+# https://www.microsoft.com/typography/otspec/otvarcommonformats.htm
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6cvar.html
+
+CVAR_HEADER_FORMAT = """
+    > # big endian
+    majorVersion:        H
+    minorVersion:        H
+    tupleVariationCount: H
+    offsetToData:        H
+"""
+
+CVAR_HEADER_SIZE = sstruct.calcsize(CVAR_HEADER_FORMAT)
+
+
+class table__c_v_a_r(DefaultTable.DefaultTable):
+    dependencies = ["cvt ", "fvar"]
+
+    def __init__(self, tag=None):
+        DefaultTable.DefaultTable.__init__(self, tag)
+        self.majorVersion, self.minorVersion = 1, 0
+        self.variations = []
+
+    def compile(self, ttFont, useSharedPoints=False):
+        tupleVariationCount, tuples, data = compileTupleVariationStore(
+            variations=[v for v in self.variations if v.hasImpact()],
+            pointCount=len(ttFont["cvt "].values),
+            axisTags=[axis.axisTag for axis in ttFont["fvar"].axes],
+            sharedTupleIndices={},
+            useSharedPoints=useSharedPoints)
+        header = {
+            "majorVersion": self.majorVersion,
+            "minorVersion": self.minorVersion,
+            "tupleVariationCount": tupleVariationCount,
+            "offsetToData": CVAR_HEADER_SIZE + len(tuples),
+        }
+        return bytesjoin([
+            sstruct.pack(CVAR_HEADER_FORMAT, header),
+            tuples,
+            data
+        ])
+
+    def decompile(self, data, ttFont):
+        axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
+        header = {}
+        sstruct.unpack(CVAR_HEADER_FORMAT, data[0:CVAR_HEADER_SIZE], header)
+        self.majorVersion = header["majorVersion"]
+        self.minorVersion = header["minorVersion"]
+        assert self.majorVersion == 1, self.majorVersion
+        self.variations = decompileTupleVariationStore(
+            tableTag=self.tableTag, axisTags=axisTags,
+            tupleVariationCount=header["tupleVariationCount"],
+            pointCount=len(ttFont["cvt "].values), sharedTuples=None,
+            data=data, pos=CVAR_HEADER_SIZE, dataPos=header["offsetToData"])
+
+    def fromXML(self, name, attrs, content, ttFont):
+        if name == "version":
+            self.majorVersion = int(attrs.get("major", "1"))
+            self.minorVersion = int(attrs.get("minor", "0"))
+        elif name == "tuple":
+            valueCount = len(ttFont["cvt "].values)
+            var = TupleVariation({}, [None] * valueCount)
+            self.variations.append(var)
+            for tupleElement in content:
+                if isinstance(tupleElement, tuple):
+                    tupleName, tupleAttrs, tupleContent = tupleElement
+                    var.fromXML(tupleName, tupleAttrs, tupleContent)
+
+    def toXML(self, writer, ttFont):
+        axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
+        writer.simpletag("version",
+                         major=self.majorVersion, minor=self.minorVersion)
+        writer.newline()
+        for var in self.variations:
+            var.toXML(writer, axisTags)
diff --git a/Lib/fontTools/ttLib/tables/_c_v_t.py b/Lib/fontTools/ttLib/tables/_c_v_t.py
index f9f8186..4fbee7b 100644
--- a/Lib/fontTools/ttLib/tables/_c_v_t.py
+++ b/Lib/fontTools/ttLib/tables/_c_v_t.py
@@ -6,26 +6,26 @@
 import array
 
 class table__c_v_t(DefaultTable.DefaultTable):
-	
+
 	def decompile(self, data, ttFont):
 		values = array.array("h")
 		values.fromstring(data)
 		if sys.byteorder != "big":
 			values.byteswap()
 		self.values = values
-	
+
 	def compile(self, ttFont):
 		values = self.values[:]
 		if sys.byteorder != "big":
 			values.byteswap()
 		return values.tostring()
-	
+
 	def toXML(self, writer, ttFont):
 		for i in range(len(self.values)):
 			value = self.values[i]
 			writer.simpletag("cv", value=value, index=i)
 			writer.newline()
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		if not hasattr(self, "values"):
 			self.values = array.array("h")
@@ -35,16 +35,15 @@
 			for i in range(1 + index - len(self.values)):
 				self.values.append(0)
 			self.values[index] = value
-	
+
 	def __len__(self):
 		return len(self.values)
-	
+
 	def __getitem__(self, index):
 		return self.values[index]
-	
+
 	def __setitem__(self, index, value):
 		self.values[index] = value
-	
+
 	def __delitem__(self, index):
 		del self.values[index]
-
diff --git a/Lib/fontTools/ttLib/tables/_f_e_a_t.py b/Lib/fontTools/ttLib/tables/_f_e_a_t.py
new file mode 100644
index 0000000..3715271
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_f_e_a_t.py
@@ -0,0 +1,7 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from .otBase import BaseTTXConverter
+
+
+class table__f_e_a_t(BaseTTXConverter):
+	pass
diff --git a/Lib/fontTools/ttLib/tables/_f_p_g_m.py b/Lib/fontTools/ttLib/tables/_f_p_g_m.py
index e4bd5f7..6536dba 100644
--- a/Lib/fontTools/ttLib/tables/_f_p_g_m.py
+++ b/Lib/fontTools/ttLib/tables/_f_p_g_m.py
@@ -4,24 +4,47 @@
 from . import ttProgram
 
 class table__f_p_g_m(DefaultTable.DefaultTable):
-	
+
 	def decompile(self, data, ttFont):
 		program = ttProgram.Program()
 		program.fromBytecode(data)
 		self.program = program
-	
+
 	def compile(self, ttFont):
 		return self.program.getBytecode()
-	
+
 	def toXML(self, writer, ttFont):
 		self.program.toXML(writer, ttFont)
-		writer.newline()
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		program = ttProgram.Program()
 		program.fromXML(name, attrs, content, ttFont)
 		self.program = program
-	
-	def __len__(self):
-		return len(self.program)
 
+	def __bool__(self):
+		"""
+		>>> fpgm = table__f_p_g_m()
+		>>> bool(fpgm)
+		False
+		>>> p = ttProgram.Program()
+		>>> fpgm.program = p
+		>>> bool(fpgm)
+		False
+		>>> bc = bytearray([0])
+		>>> p.fromBytecode(bc)
+		>>> bool(fpgm)
+		True
+		>>> p.bytecode.pop()
+		0
+		>>> bool(fpgm)
+		False
+		"""
+		return hasattr(self, 'program') and bool(self.program)
+
+	__nonzero__ = __bool__
+
+
+if __name__ == "__main__":
+	import sys
+	import doctest
+	sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/ttLib/tables/_f_v_a_r.py b/Lib/fontTools/ttLib/tables/_f_v_a_r.py
new file mode 100644
index 0000000..3bc46e2
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_f_v_a_r.py
@@ -0,0 +1,219 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc import sstruct
+from fontTools.misc.fixedTools import fixedToFloat, floatToFixed
+from fontTools.misc.textTools import safeEval, num2binary, binary2num
+from fontTools.ttLib import TTLibError
+from . import DefaultTable
+import struct
+
+
+# Apple's documentation of 'fvar':
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6fvar.html
+
+FVAR_HEADER_FORMAT = """
+    > # big endian
+    version:        L
+    offsetToData:   H
+    countSizePairs: H
+    axisCount:      H
+    axisSize:       H
+    instanceCount:  H
+    instanceSize:   H
+"""
+
+FVAR_AXIS_FORMAT = """
+    > # big endian
+    axisTag:        4s
+    minValue:       16.16F
+    defaultValue:   16.16F
+    maxValue:       16.16F
+    flags:          H
+    axisNameID:         H
+"""
+
+FVAR_INSTANCE_FORMAT = """
+    > # big endian
+    subfamilyNameID:     H
+    flags:      H
+"""
+
+class table__f_v_a_r(DefaultTable.DefaultTable):
+    dependencies = ["name"]
+
+    def __init__(self, tag=None):
+        DefaultTable.DefaultTable.__init__(self, tag)
+        self.axes = []
+        self.instances = []
+
+    def compile(self, ttFont):
+        instanceSize = sstruct.calcsize(FVAR_INSTANCE_FORMAT) + (len(self.axes) * 4)
+        includePostScriptNames = any(instance.postscriptNameID != 0xFFFF
+                                     for instance in self.instances)
+        if includePostScriptNames:
+            instanceSize += 2
+        header = {
+            "version": 0x00010000,
+            "offsetToData": sstruct.calcsize(FVAR_HEADER_FORMAT),
+            "countSizePairs": 2,
+            "axisCount": len(self.axes),
+            "axisSize": sstruct.calcsize(FVAR_AXIS_FORMAT),
+            "instanceCount": len(self.instances),
+            "instanceSize": instanceSize,
+        }
+        result = [sstruct.pack(FVAR_HEADER_FORMAT, header)]
+        result.extend([axis.compile() for axis in self.axes])
+        axisTags = [axis.axisTag for axis in self.axes]
+        for instance in self.instances:
+            result.append(instance.compile(axisTags, includePostScriptNames))
+        return bytesjoin(result)
+
+    def decompile(self, data, ttFont):
+        header = {}
+        headerSize = sstruct.calcsize(FVAR_HEADER_FORMAT)
+        header = sstruct.unpack(FVAR_HEADER_FORMAT, data[0:headerSize])
+        if header["version"] != 0x00010000:
+            raise TTLibError("unsupported 'fvar' version %04x" % header["version"])
+        pos = header["offsetToData"]
+        axisSize = header["axisSize"]
+        for _ in range(header["axisCount"]):
+            axis = Axis()
+            axis.decompile(data[pos:pos+axisSize])
+            self.axes.append(axis)
+            pos += axisSize
+        instanceSize = header["instanceSize"]
+        axisTags = [axis.axisTag for axis in self.axes]
+        for _ in range(header["instanceCount"]):
+            instance = NamedInstance()
+            instance.decompile(data[pos:pos+instanceSize], axisTags)
+            self.instances.append(instance)
+            pos += instanceSize
+
+    def toXML(self, writer, ttFont):
+        for axis in self.axes:
+            axis.toXML(writer, ttFont)
+        for instance in self.instances:
+            instance.toXML(writer, ttFont)
+
+    def fromXML(self, name, attrs, content, ttFont):
+        if name == "Axis":
+            axis = Axis()
+            axis.fromXML(name, attrs, content, ttFont)
+            self.axes.append(axis)
+        elif name == "NamedInstance":
+            instance = NamedInstance()
+            instance.fromXML(name, attrs, content, ttFont)
+            self.instances.append(instance)
+
+class Axis(object):
+    def __init__(self):
+        self.axisTag = None
+        self.axisNameID = 0
+        self.flags = 0
+        self.minValue = -1.0
+        self.defaultValue = 0.0
+        self.maxValue = 1.0
+
+    def compile(self):
+        return sstruct.pack(FVAR_AXIS_FORMAT, self)
+
+    def decompile(self, data):
+        sstruct.unpack2(FVAR_AXIS_FORMAT, data, self)
+
+    def toXML(self, writer, ttFont):
+        name = ttFont["name"].getDebugName(self.axisNameID)
+        if name is not None:
+            writer.newline()
+            writer.comment(name)
+            writer.newline()
+        writer.begintag("Axis")
+        writer.newline()
+        for tag, value in [("AxisTag", self.axisTag),
+                           ("Flags", "0x%X" % self.flags),
+                           ("MinValue", str(self.minValue)),
+                           ("DefaultValue", str(self.defaultValue)),
+                           ("MaxValue", str(self.maxValue)),
+                           ("AxisNameID", str(self.axisNameID))]:
+            writer.begintag(tag)
+            writer.write(value)
+            writer.endtag(tag)
+            writer.newline()
+        writer.endtag("Axis")
+        writer.newline()
+
+    def fromXML(self, name, _attrs, content, ttFont):
+        assert(name == "Axis")
+        for tag, _, value in filter(lambda t: type(t) is tuple, content):
+            value = ''.join(value)
+            if tag == "AxisTag":
+                self.axisTag = Tag(value)
+            elif tag in {"Flags", "MinValue", "DefaultValue", "MaxValue",
+                         "AxisNameID"}:
+                setattr(self, tag[0].lower() + tag[1:], safeEval(value))
+
+
+class NamedInstance(object):
+    def __init__(self):
+        self.subfamilyNameID = 0
+        self.postscriptNameID = 0xFFFF
+        self.flags = 0
+        self.coordinates = {}
+
+    def compile(self, axisTags, includePostScriptName):
+        result = [sstruct.pack(FVAR_INSTANCE_FORMAT, self)]
+        for axis in axisTags:
+            fixedCoord = floatToFixed(self.coordinates[axis], 16)
+            result.append(struct.pack(">l", fixedCoord))
+        if includePostScriptName:
+            result.append(struct.pack(">H", self.postscriptNameID))
+        return bytesjoin(result)
+
+    def decompile(self, data, axisTags):
+        sstruct.unpack2(FVAR_INSTANCE_FORMAT, data, self)
+        pos = sstruct.calcsize(FVAR_INSTANCE_FORMAT)
+        for axis in axisTags:
+            value = struct.unpack(">l", data[pos : pos + 4])[0]
+            self.coordinates[axis] = fixedToFloat(value, 16)
+            pos += 4
+        if pos + 2 <= len(data):
+          self.postscriptNameID = struct.unpack(">H", data[pos : pos + 2])[0]
+        else:
+          self.postscriptNameID = 0xFFFF
+
+    def toXML(self, writer, ttFont):
+        name = ttFont["name"].getDebugName(self.subfamilyNameID)
+        if name is not None:
+            writer.newline()
+            writer.comment(name)
+            writer.newline()
+        psname = ttFont["name"].getDebugName(self.postscriptNameID)
+        if psname is not None:
+            writer.comment(u"PostScript: " + psname)
+            writer.newline()
+        if self.postscriptNameID  == 0xFFFF:
+           writer.begintag("NamedInstance", flags=("0x%X" % self.flags),
+                           subfamilyNameID=self.subfamilyNameID)
+        else:
+            writer.begintag("NamedInstance", flags=("0x%X" % self.flags),
+                            subfamilyNameID=self.subfamilyNameID,
+                            postscriptNameID=self.postscriptNameID, )
+        writer.newline()
+        for axis in ttFont["fvar"].axes:
+            writer.simpletag("coord", axis=axis.axisTag,
+                             value=self.coordinates[axis.axisTag])
+            writer.newline()
+        writer.endtag("NamedInstance")
+        writer.newline()
+
+    def fromXML(self, name, attrs, content, ttFont):
+        assert(name == "NamedInstance")
+        self.subfamilyNameID = safeEval(attrs["subfamilyNameID"])
+        self.flags = safeEval(attrs.get("flags", "0"))
+        if "postscriptNameID" in attrs:
+            self.postscriptNameID = safeEval(attrs["postscriptNameID"])
+        else:
+            self.postscriptNameID = 0xFFFF
+
+        for tag, elementAttrs, _ in filter(lambda t: type(t) is tuple, content):
+            if tag == "coord":
+                self.coordinates[elementAttrs["axis"]] = safeEval(elementAttrs["value"])
diff --git a/Lib/fontTools/ttLib/tables/_g_a_s_p.py b/Lib/fontTools/ttLib/tables/_g_a_s_p.py
index 54fef90..dce3569 100644
--- a/Lib/fontTools/ttLib/tables/_g_a_s_p.py
+++ b/Lib/fontTools/ttLib/tables/_g_a_s_p.py
@@ -11,7 +11,7 @@
 GASP_GRIDFIT = 0x0001
 
 class table__g_a_s_p(DefaultTable.DefaultTable):
-	
+
 	def decompile(self, data, ttFont):
 		self.version, numRanges = struct.unpack(">HH", data[:4])
 		assert 0 <= self.version <= 1, "unknown 'gasp' format: %s" % self.version
@@ -22,7 +22,7 @@
 			self.gaspRange[int(rangeMaxPPEM)] = int(rangeGaspBehavior)
 			data = data[4:]
 		assert not data, "too much data"
-	
+
 	def compile(self, ttFont):
 		version = 0 # ignore self.version
 		numRanges = len(self.gaspRange)
@@ -34,7 +34,7 @@
 				version = 1
 		data = struct.pack(">HH", version, numRanges) + data
 		return data
-	
+
 	def toXML(self, writer, ttFont):
 		items = sorted(self.gaspRange.items())
 		for rangeMaxPPEM, rangeGaspBehavior in items:
@@ -42,11 +42,10 @@
 					("rangeMaxPPEM", rangeMaxPPEM),
 					("rangeGaspBehavior", rangeGaspBehavior)])
 			writer.newline()
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		if name != "gaspRange":
 			return
 		if not hasattr(self, "gaspRange"):
 			self.gaspRange = {}
 		self.gaspRange[safeEval(attrs["rangeMaxPPEM"])] = safeEval(attrs["rangeGaspBehavior"])
-
diff --git a/Lib/fontTools/ttLib/tables/_g_c_i_d.py b/Lib/fontTools/ttLib/tables/_g_c_i_d.py
new file mode 100644
index 0000000..f8b57e2
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_g_c_i_d.py
@@ -0,0 +1,8 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from .otBase import BaseTTXConverter
+
+
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6gcid.html
+class table__g_c_i_d(BaseTTXConverter):
+    pass
diff --git a/Lib/fontTools/ttLib/tables/_g_l_y_f.py b/Lib/fontTools/ttLib/tables/_g_l_y_f.py
index 970980b..58c1eb2 100644
--- a/Lib/fontTools/ttLib/tables/_g_l_y_f.py
+++ b/Lib/fontTools/ttLib/tables/_g_l_y_f.py
@@ -1,36 +1,58 @@
 """_g_l_y_f.py -- Converter classes for the 'glyf' table."""
 
-
 from __future__ import print_function, division, absolute_import
+from collections import namedtuple
 from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
 from fontTools import ttLib
-from fontTools.misc.textTools import safeEval
+from fontTools import version
+from fontTools.misc.textTools import safeEval, pad
 from fontTools.misc.arrayTools import calcBounds, calcIntBounds, pointInRect
 from fontTools.misc.bezierTools import calcQuadraticBounds
-from fontTools.misc.fixedTools import fixedToFloat as fi2fl, floatToFixed as fl2fi
+from fontTools.misc.fixedTools import (
+	fixedToFloat as fi2fl,
+	floatToFixed as fl2fi,
+	otRound,
+)
+from numbers import Number
 from . import DefaultTable
 from . import ttProgram
 import sys
 import struct
 import array
-import warnings
+import logging
+import os
+from fontTools.misc import xmlWriter
+from fontTools.misc.filenames import userNameToFileName
+
+log = logging.getLogger(__name__)
+
+# We compute the version the same as is computed in ttlib/__init__
+# so that we can write 'ttLibVersion' attribute of the glyf TTX files
+# when glyf is written to separate files.
+version = ".".join(version.split('.')[:2])
 
 #
-# The Apple and MS rasterizers behave differently for 
+# The Apple and MS rasterizers behave differently for
 # scaled composite components: one does scale first and then translate
 # and the other does it vice versa. MS defined some flags to indicate
 # the difference, but it seems nobody actually _sets_ those flags.
 #
 # Funny thing: Apple seems to _only_ do their thing in the
-# WE_HAVE_A_SCALE (eg. Chicago) case, and not when it's WE_HAVE_AN_X_AND_Y_SCALE 
+# WE_HAVE_A_SCALE (eg. Chicago) case, and not when it's WE_HAVE_AN_X_AND_Y_SCALE
 # (eg. Charcoal)...
 #
 SCALE_COMPONENT_OFFSET_DEFAULT = 0   # 0 == MS, 1 == Apple
 
 
 class table__g_l_y_f(DefaultTable.DefaultTable):
-	
+
+	# this attribute controls the amount of padding applied to glyph data upon compile.
+	# Glyph lenghts are aligned to multiples of the specified value. 
+	# Allowed values are (0, 1, 2, 4). '0' means no padding; '1' (default) also means
+	# no padding, except for when padding would allow to use short loca offsets.
+	padding = 1
+
 	def decompile(self, data, ttFont):
 		loca = ttFont['loca']
 		last = int(loca[0])
@@ -50,17 +72,21 @@
 			glyph = Glyph(glyphdata)
 			self.glyphs[glyphName] = glyph
 			last = next
-		if len(data) > next:
-			warnings.warn("too much 'glyf' table data")
+		if len(data) - next >= 4:
+			log.warning(
+				"too much 'glyf' table data: expected %d, received %d bytes",
+				next, len(data))
 		if noname:
-			warnings.warn('%s glyphs have no name' % i)
-		if not ttFont.lazy:
+			log.warning('%s glyphs have no name', noname)
+		if ttFont.lazy is False: # Be lazy for None and True
 			for glyph in self.glyphs.values():
 				glyph.expand(self)
-	
+
 	def compile(self, ttFont):
 		if not hasattr(self, "glyphOrder"):
 			self.glyphOrder = ttFont.getGlyphOrder()
+		padding = self.padding
+		assert padding in (0, 1, 2, 4)
 		locations = []
 		currentLocation = 0
 		dataList = []
@@ -68,49 +94,94 @@
 		for glyphName in self.glyphOrder:
 			glyph = self.glyphs[glyphName]
 			glyphData = glyph.compile(self, recalcBBoxes)
+			if padding > 1:
+				glyphData = pad(glyphData, size=padding)
 			locations.append(currentLocation)
 			currentLocation = currentLocation + len(glyphData)
 			dataList.append(glyphData)
 		locations.append(currentLocation)
+
+		if padding == 1 and currentLocation < 0x20000:
+			# See if we can pad any odd-lengthed glyphs to allow loca
+			# table to use the short offsets.
+			indices = [i for i,glyphData in enumerate(dataList) if len(glyphData) % 2 == 1]
+			if indices and currentLocation + len(indices) < 0x20000:
+				# It fits.  Do it.
+				for i in indices:
+					dataList[i] += b'\0'
+				currentLocation = 0
+				for i,glyphData in enumerate(dataList):
+					locations[i] = currentLocation
+					currentLocation += len(glyphData)
+				locations[len(dataList)] = currentLocation
+
 		data = bytesjoin(dataList)
 		if 'loca' in ttFont:
 			ttFont['loca'].set(locations)
-		ttFont['maxp'].numGlyphs = len(self.glyphs)
+		if 'maxp' in ttFont:
+			ttFont['maxp'].numGlyphs = len(self.glyphs)
 		return data
-	
-	def toXML(self, writer, ttFont, progress=None):
-		writer.newline()
+
+	def toXML(self, writer, ttFont, splitGlyphs=False):
+		notice = (
+			"The xMin, yMin, xMax and yMax values\n"
+			"will be recalculated by the compiler.")
 		glyphNames = ttFont.getGlyphNames()
-		writer.comment("The xMin, yMin, xMax and yMax values\nwill be recalculated by the compiler.")
-		writer.newline()
-		writer.newline()
-		counter = 0
-		progressStep = 10
+		if not splitGlyphs:
+			writer.newline()
+			writer.comment(notice)
+			writer.newline()
+			writer.newline()
 		numGlyphs = len(glyphNames)
+		if splitGlyphs:
+			path, ext = os.path.splitext(writer.file.name)
+			existingGlyphFiles = set()
 		for glyphName in glyphNames:
-			if not counter % progressStep and progress is not None:
-				progress.setLabel("Dumping 'glyf' table... (%s)" % glyphName)
-				progress.increment(progressStep / numGlyphs)
-			counter = counter + 1
 			glyph = self[glyphName]
 			if glyph.numberOfContours:
-				writer.begintag('TTGlyph', [
-						("name", glyphName),
-						("xMin", glyph.xMin),
-						("yMin", glyph.yMin),
-						("xMax", glyph.xMax),
-						("yMax", glyph.yMax),
-						])
-				writer.newline()
-				glyph.toXML(writer, ttFont)
-				writer.endtag('TTGlyph')
-				writer.newline()
+				if splitGlyphs:
+					glyphPath = userNameToFileName(
+						tounicode(glyphName, 'utf-8'),
+						existingGlyphFiles,
+						prefix=path + ".",
+						suffix=ext)
+					existingGlyphFiles.add(glyphPath.lower())
+					glyphWriter = xmlWriter.XMLWriter(
+						glyphPath, idlefunc=writer.idlefunc,
+						newlinestr=writer.newlinestr)
+					glyphWriter.begintag("ttFont", ttLibVersion=version)
+					glyphWriter.newline()
+					glyphWriter.begintag("glyf")
+					glyphWriter.newline()
+					glyphWriter.comment(notice)
+					glyphWriter.newline()
+					writer.simpletag("TTGlyph", src=os.path.basename(glyphPath))
+				else:
+					glyphWriter = writer
+				glyphWriter.begintag('TTGlyph', [
+							("name", glyphName),
+							("xMin", glyph.xMin),
+							("yMin", glyph.yMin),
+							("xMax", glyph.xMax),
+							("yMax", glyph.yMax),
+							])
+				glyphWriter.newline()
+				glyph.toXML(glyphWriter, ttFont)
+				glyphWriter.endtag('TTGlyph')
+				glyphWriter.newline()
+				if splitGlyphs:
+					glyphWriter.endtag("glyf")
+					glyphWriter.newline()
+					glyphWriter.endtag("ttFont")
+					glyphWriter.newline()
+					glyphWriter.close()
 			else:
 				writer.simpletag('TTGlyph', name=glyphName)
 				writer.comment("contains no outline data")
-				writer.newline()
+				if not splitGlyphs:
+					writer.newline()
 			writer.newline()
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		if name != "TTGlyph":
 			return
@@ -119,8 +190,7 @@
 		if not hasattr(self, "glyphOrder"):
 			self.glyphOrder = ttFont.getGlyphOrder()
 		glyphName = attrs["name"]
-		if ttFont.verbose:
-			ttLib.debugmsg("unpacking glyph '%s'" % glyphName)
+		log.debug("unpacking glyph '%s'", glyphName)
 		glyph = Glyph()
 		for attr in ['xMin', 'yMin', 'xMax', 'yMax']:
 			setattr(glyph, attr, safeEval(attrs.get(attr, '0')))
@@ -132,39 +202,43 @@
 			glyph.fromXML(name, attrs, content, ttFont)
 		if not ttFont.recalcBBoxes:
 			glyph.compact(self, 0)
-	
+
 	def setGlyphOrder(self, glyphOrder):
 		self.glyphOrder = glyphOrder
-	
+
 	def getGlyphName(self, glyphID):
 		return self.glyphOrder[glyphID]
-	
+
 	def getGlyphID(self, glyphName):
 		# XXX optimize with reverse dict!!!
 		return self.glyphOrder.index(glyphName)
-	
+
+	def removeHinting(self):
+		for glyph in self.glyphs.values():
+			glyph.removeHinting()
+
 	def keys(self):
 		return self.glyphs.keys()
-	
+
 	def has_key(self, glyphName):
 		return glyphName in self.glyphs
-	
+
 	__contains__ = has_key
-	
+
 	def __getitem__(self, glyphName):
 		glyph = self.glyphs[glyphName]
 		glyph.expand(self)
 		return glyph
-	
+
 	def __setitem__(self, glyphName, glyph):
 		self.glyphs[glyphName] = glyph
 		if glyphName not in self.glyphOrder:
 			self.glyphOrder.append(glyphName)
-	
+
 	def __delitem__(self, glyphName):
 		del self.glyphs[glyphName]
 		self.glyphOrder.remove(glyphName)
-	
+
 	def __len__(self):
 		assert len(self.glyphOrder) == len(self.glyphs)
 		return len(self.glyphs)
@@ -189,54 +263,129 @@
 flagReserved1 = 0x40
 flagReserved2 = 0x80
 
+_flagSignBytes = {
+	0: 2,
+	flagXsame: 0,
+	flagXShort|flagXsame: +1,
+	flagXShort: -1,
+	flagYsame: 0,
+	flagYShort|flagYsame: +1,
+	flagYShort: -1,
+}
 
-ARG_1_AND_2_ARE_WORDS      = 0x0001  # if set args are words otherwise they are bytes 
-ARGS_ARE_XY_VALUES         = 0x0002  # if set args are xy values, otherwise they are points 
-ROUND_XY_TO_GRID           = 0x0004  # for the xy values if above is true 
-WE_HAVE_A_SCALE            = 0x0008  # Sx = Sy, otherwise scale == 1.0 
-NON_OVERLAPPING            = 0x0010  # set to same value for all components (obsolete!)
-MORE_COMPONENTS            = 0x0020  # indicates at least one more glyph after this one 
-WE_HAVE_AN_X_AND_Y_SCALE   = 0x0040  # Sx, Sy 
-WE_HAVE_A_TWO_BY_TWO       = 0x0080  # t00, t01, t10, t11 
-WE_HAVE_INSTRUCTIONS       = 0x0100  # instructions follow 
-USE_MY_METRICS             = 0x0200  # apply these metrics to parent glyph 
-OVERLAP_COMPOUND           = 0x0400  # used by Apple in GX fonts 
-SCALED_COMPONENT_OFFSET    = 0x0800  # composite designed to have the component offset scaled (designed for Apple) 
-UNSCALED_COMPONENT_OFFSET  = 0x1000  # composite designed not to have the component offset scaled (designed for MS) 
+def flagBest(x, y, onCurve):
+	"""For a given x,y delta pair, returns the flag that packs this pair
+	most efficiently, as well as the number of byte cost of such flag."""
+
+	flag = flagOnCurve if onCurve else 0
+	cost = 0
+	# do x
+	if x == 0:
+		flag = flag | flagXsame
+	elif -255 <= x <= 255:
+		flag = flag | flagXShort
+		if x > 0:
+			flag = flag | flagXsame
+		cost += 1
+	else:
+		cost += 2
+	# do y
+	if y == 0:
+		flag = flag | flagYsame
+	elif -255 <= y <= 255:
+		flag = flag | flagYShort
+		if y > 0:
+			flag = flag | flagYsame
+		cost += 1
+	else:
+		cost += 2
+	return flag, cost
+
+def flagFits(newFlag, oldFlag, mask):
+	newBytes = _flagSignBytes[newFlag & mask]
+	oldBytes = _flagSignBytes[oldFlag & mask]
+	return newBytes == oldBytes or abs(newBytes) > abs(oldBytes)
+
+def flagSupports(newFlag, oldFlag):
+	return ((oldFlag & flagOnCurve) == (newFlag & flagOnCurve) and
+		flagFits(newFlag, oldFlag, flagXsame|flagXShort) and
+		flagFits(newFlag, oldFlag, flagYsame|flagYShort))
+
+def flagEncodeCoord(flag, mask, coord, coordBytes):
+	byteCount = _flagSignBytes[flag & mask]
+	if byteCount == 1:
+		coordBytes.append(coord)
+	elif byteCount == -1:
+		coordBytes.append(-coord)
+	elif byteCount == 2:
+		coordBytes.append((coord >> 8) & 0xFF)
+		coordBytes.append(coord & 0xFF)
+
+def flagEncodeCoords(flag, x, y, xBytes, yBytes):
+	flagEncodeCoord(flag, flagXsame|flagXShort, x, xBytes)
+	flagEncodeCoord(flag, flagYsame|flagYShort, y, yBytes)
+
+
+ARG_1_AND_2_ARE_WORDS		= 0x0001  # if set args are words otherwise they are bytes
+ARGS_ARE_XY_VALUES		= 0x0002  # if set args are xy values, otherwise they are points
+ROUND_XY_TO_GRID		= 0x0004  # for the xy values if above is true
+WE_HAVE_A_SCALE			= 0x0008  # Sx = Sy, otherwise scale == 1.0
+NON_OVERLAPPING			= 0x0010  # set to same value for all components (obsolete!)
+MORE_COMPONENTS			= 0x0020  # indicates at least one more glyph after this one
+WE_HAVE_AN_X_AND_Y_SCALE	= 0x0040  # Sx, Sy
+WE_HAVE_A_TWO_BY_TWO		= 0x0080  # t00, t01, t10, t11
+WE_HAVE_INSTRUCTIONS		= 0x0100  # instructions follow
+USE_MY_METRICS			= 0x0200  # apply these metrics to parent glyph
+OVERLAP_COMPOUND		= 0x0400  # used by Apple in GX fonts
+SCALED_COMPONENT_OFFSET		= 0x0800  # composite designed to have the component offset scaled (designed for Apple)
+UNSCALED_COMPONENT_OFFSET	= 0x1000  # composite designed not to have the component offset scaled (designed for MS)
+
+
+CompositeMaxpValues = namedtuple('CompositeMaxpValues', ['nPoints', 'nContours', 'maxComponentDepth'])
 
 
 class Glyph(object):
-	
+
 	def __init__(self, data=""):
 		if not data:
 			# empty char
 			self.numberOfContours = 0
 			return
 		self.data = data
-	
+
 	def compact(self, glyfTable, recalcBBoxes=True):
 		data = self.compile(glyfTable, recalcBBoxes)
 		self.__dict__.clear()
 		self.data = data
-	
+
 	def expand(self, glyfTable):
 		if not hasattr(self, "data"):
 			# already unpacked
 			return
 		if not self.data:
 			# empty char
+			del self.data
 			self.numberOfContours = 0
 			return
 		dummy, data = sstruct.unpack2(glyphHeaderFormat, self.data, self)
 		del self.data
+		# Some fonts (eg. Neirizi.ttf) have a 0 for numberOfContours in
+		# some glyphs; decompileCoordinates assumes that there's at least
+		# one, so short-circuit here.
+		if self.numberOfContours == 0:
+			return
 		if self.isComposite():
 			self.decompileComponents(data, glyfTable)
 		else:
 			self.decompileCoordinates(data)
-	
+
 	def compile(self, glyfTable, recalcBBoxes=True):
 		if hasattr(self, "data"):
-			return self.data
+			if recalcBBoxes:
+				# must unpack glyph in order to recalculate bounding box
+				self.expand(glyfTable)
+			else:
+				return self.data
 		if self.numberOfContours == 0:
 			return ""
 		if recalcBBoxes:
@@ -246,24 +395,13 @@
 			data = data + self.compileComponents(glyfTable)
 		else:
 			data = data + self.compileCoordinates()
-		# From the spec: "Note that the local offsets should be word-aligned"
-		# From a later MS spec: "Note that the local offsets should be long-aligned"
-		# Let's be modern and align on 4-byte boundaries.
-		if len(data) % 4:
-			# add pad bytes
-			nPadBytes = 4 - (len(data) % 4)
-			data = data + b"\0" * nPadBytes
 		return data
-	
+
 	def toXML(self, writer, ttFont):
 		if self.isComposite():
 			for compo in self.components:
 				compo.toXML(writer, ttFont)
-			if hasattr(self, "program"):
-				writer.begintag("instructions")
-				self.program.toXML(writer, ttFont)
-				writer.endtag("instructions")
-				writer.newline()
+			haveInstructions = hasattr(self, "program")
 		else:
 			last = 0
 			for i in range(self.numberOfContours):
@@ -271,19 +409,24 @@
 				writer.newline()
 				for j in range(last, self.endPtsOfContours[i] + 1):
 					writer.simpletag("pt", [
-							("x", self.coordinates[j][0]), 
+							("x", self.coordinates[j][0]),
 							("y", self.coordinates[j][1]),
 							("on", self.flags[j] & flagOnCurve)])
 					writer.newline()
 				last = self.endPtsOfContours[i] + 1
 				writer.endtag("contour")
 				writer.newline()
-			if self.numberOfContours:
+			haveInstructions = self.numberOfContours > 0
+		if haveInstructions:
+			if self.program:
 				writer.begintag("instructions")
+				writer.newline()
 				self.program.toXML(writer, ttFont)
 				writer.endtag("instructions")
-				writer.newline()
-	
+			else:
+				writer.simpletag("instructions")
+			writer.newline()
+
 	def fromXML(self, name, attrs, content, ttFont):
 		if name == "contour":
 			if self.numberOfContours < 0:
@@ -324,7 +467,7 @@
 					continue
 				name, attrs, content = element
 				self.program.fromXML(name, attrs, content, ttFont)
-	
+
 	def getCompositeMaxpValues(self, glyfTable, maxComponentDepth=1):
 		assert self.isComposite()
 		nContours = 0
@@ -340,12 +483,12 @@
 						glyfTable, maxComponentDepth + 1)
 			nPoints = nPoints + nP
 			nContours = nContours + nC
-		return nPoints, nContours, maxComponentDepth
-	
+		return CompositeMaxpValues(nPoints, nContours, maxComponentDepth)
+
 	def getMaxpValues(self):
 		assert self.numberOfContours > 0
 		return len(self.coordinates), len(self.endPtsOfContours)
-	
+
 	def decompileComponents(self, data, glyfTable):
 		self.components = []
 		more = 1
@@ -361,17 +504,20 @@
 			self.program = ttProgram.Program()
 			self.program.fromBytecode(data[:numInstructions])
 			data = data[numInstructions:]
-			assert len(data) < 4, "bad composite data"
-	
+			if len(data) >= 4:
+				log.warning(
+					"too much glyph data at the end of composite glyph: %d excess bytes",
+					len(data))
+
 	def decompileCoordinates(self, data):
 		endPtsOfContours = array.array("h")
 		endPtsOfContours.fromstring(data[:2*self.numberOfContours])
 		if sys.byteorder != "big":
 			endPtsOfContours.byteswap()
 		self.endPtsOfContours = endPtsOfContours.tolist()
-		
+
 		data = data[2*self.numberOfContours:]
-		
+
 		instructionLength, = struct.unpack(">h", data[:2])
 		data = data[2:]
 		self.program = ttProgram.Program()
@@ -380,7 +526,7 @@
 		nCoordinates = self.endPtsOfContours[-1] + 1
 		flags, xCoordinates, yCoordinates = \
 				self.decompileCoordinatesRaw(nCoordinates, data)
-		
+
 		# fill in repetitions and apply signs
 		self.coordinates = coordinates = GlyphCoordinates.zeros(nCoordinates)
 		xIndex = 0
@@ -453,11 +599,12 @@
 		xDataLen = struct.calcsize(xFormat)
 		yDataLen = struct.calcsize(yFormat)
 		if len(data) - (xDataLen + yDataLen) >= 4:
-			warnings.warn("too much glyph data: %d excess bytes" % (len(data) - (xDataLen + yDataLen)))
+			log.warning(
+				"too much glyph data: %d excess bytes", len(data) - (xDataLen + yDataLen))
 		xCoordinates = struct.unpack(xFormat, data[:xDataLen])
 		yCoordinates = struct.unpack(yFormat, data[xDataLen:xDataLen+yDataLen])
 		return flags, xCoordinates, yCoordinates
-	
+
 	def compileComponents(self, glyfTable):
 		data = b""
 		lastcomponent = len(self.components) - 1
@@ -473,33 +620,41 @@
 			instructions = self.program.getBytecode()
 			data = data + struct.pack(">h", len(instructions)) + instructions
 		return data
-			
-	
+
 	def compileCoordinates(self):
 		assert len(self.coordinates) == len(self.flags)
-		data = b""
+		data = []
 		endPtsOfContours = array.array("h", self.endPtsOfContours)
 		if sys.byteorder != "big":
 			endPtsOfContours.byteswap()
-		data = data + endPtsOfContours.tostring()
+		data.append(endPtsOfContours.tostring())
 		instructions = self.program.getBytecode()
-		data = data + struct.pack(">h", len(instructions)) + instructions
-		nCoordinates = len(self.coordinates)
-		
-		coordinates = self.coordinates.copy()
-		coordinates.absoluteToRelative()
-		flags = self.flags
+		data.append(struct.pack(">h", len(instructions)))
+		data.append(instructions)
+
+		deltas = self.coordinates.copy()
+		if deltas.isFloat():
+			# Warn?
+			deltas.toInt()
+		deltas.absoluteToRelative()
+
+		# TODO(behdad): Add a configuration option for this?
+		deltas = self.compileDeltasGreedy(self.flags, deltas)
+		#deltas = self.compileDeltasOptimal(self.flags, deltas)
+
+		data.extend(deltas)
+		return bytesjoin(data)
+
+	def compileDeltasGreedy(self, flags, deltas):
+		# Implements greedy algorithm for packing coordinate deltas:
+		# uses shortest representation one coordinate at a time.
 		compressedflags = []
 		xPoints = []
 		yPoints = []
-		xFormat = ">"
-		yFormat = ">"
 		lastflag = None
 		repeat = 0
-		for i in range(len(coordinates)):
+		for flag,(x,y) in zip(flags, deltas):
 			# Oh, the horrors of TrueType
-			flag = flags[i]
-			x, y = coordinates[i]
 			# do x
 			if x == 0:
 				flag = flag | flagXsame
@@ -509,11 +664,9 @@
 					flag = flag | flagXsame
 				else:
 					x = -x
-				xPoints.append(x)
-				xFormat = xFormat + 'B'
+				xPoints.append(bytechr(x))
 			else:
-				xPoints.append(x)
-				xFormat = xFormat + 'h'
+				xPoints.append(struct.pack(">h", x))
 			# do y
 			if y == 0:
 				flag = flag | flagYsame
@@ -523,11 +676,9 @@
 					flag = flag | flagYsame
 				else:
 					y = -y
-				yPoints.append(y)
-				yFormat = yFormat + 'B'
+				yPoints.append(bytechr(y))
 			else:
-				yPoints.append(y)
-				yFormat = yFormat + 'h'
+				yPoints.append(struct.pack(">h", y))
 			# handle repeating flags
 			if flag == lastflag and repeat != 255:
 				repeat = repeat + 1
@@ -540,15 +691,67 @@
 				repeat = 0
 				compressedflags.append(flag)
 			lastflag = flag
-		data = data + array.array("B", compressedflags).tostring()
-		if coordinates.isFloat():
-			# Warn?
-			xPoints = [int(round(x)) for x in xPoints]
-			yPoints = [int(round(y)) for y in xPoints]
-		data = data + struct.pack(*(xFormat,)+tuple(xPoints))
-		data = data + struct.pack(*(yFormat,)+tuple(yPoints))
-		return data
-	
+		compressedFlags = array.array("B", compressedflags).tostring()
+		compressedXs = bytesjoin(xPoints)
+		compressedYs = bytesjoin(yPoints)
+		return (compressedFlags, compressedXs, compressedYs)
+
+	def compileDeltasOptimal(self, flags, deltas):
+		# Implements optimal, dynaic-programming, algorithm for packing coordinate
+		# deltas.  The savings are negligible :(.
+		candidates = []
+		bestTuple = None
+		bestCost = 0
+		repeat = 0
+		for flag,(x,y) in zip(flags, deltas):
+			# Oh, the horrors of TrueType
+			flag, coordBytes = flagBest(x, y, flag)
+			bestCost += 1 + coordBytes
+			newCandidates = [(bestCost, bestTuple, flag, coordBytes),
+							(bestCost+1, bestTuple, (flag|flagRepeat), coordBytes)]
+			for lastCost,lastTuple,lastFlag,coordBytes in candidates:
+				if lastCost + coordBytes <= bestCost + 1 and (lastFlag & flagRepeat) and (lastFlag < 0xff00) and flagSupports(lastFlag, flag):
+					if (lastFlag & 0xFF) == (flag|flagRepeat) and lastCost == bestCost + 1:
+						continue
+					newCandidates.append((lastCost + coordBytes, lastTuple, lastFlag+256, coordBytes))
+			candidates = newCandidates
+			bestTuple = min(candidates, key=lambda t:t[0])
+			bestCost = bestTuple[0]
+
+		flags = []
+		while bestTuple:
+			cost, bestTuple, flag, coordBytes = bestTuple
+			flags.append(flag)
+		flags.reverse()
+
+		compressedFlags = array.array("B")
+		compressedXs = array.array("B")
+		compressedYs = array.array("B")
+		coords = iter(deltas)
+		ff = []
+		for flag in flags:
+			repeatCount, flag = flag >> 8, flag & 0xFF
+			compressedFlags.append(flag)
+			if flag & flagRepeat:
+				assert(repeatCount > 0)
+				compressedFlags.append(repeatCount)
+			else:
+				assert(repeatCount == 0)
+			for i in range(1 + repeatCount):
+				x,y = next(coords)
+				flagEncodeCoords(flag, x, y, compressedXs, compressedYs)
+				ff.append(flag)
+		try:
+			next(coords)
+			raise Exception("internal error")
+		except StopIteration:
+			pass
+		compressedFlags = compressedFlags.tostring()
+		compressedXs = compressedXs.tostring()
+		compressedYs = compressedYs.tostring()
+
+		return (compressedFlags, compressedXs, compressedYs)
+
 	def recalcBounds(self, glyfTable):
 		coords, endPts, flags = self.getCoordinates(glyfTable)
 		if len(coords) > 0:
@@ -564,7 +767,7 @@
 
 				# Collect on-curve points
 				onCurveCoords = [coords[j] for j in range(len(coords))
-						 if flags[j] & flagOnCurve]
+								if flags[j] & flagOnCurve]
 				# Add implicit on-curve points
 				start = 0
 				for end in endPts:
@@ -586,7 +789,7 @@
 							bbox = calcBounds([coords[last], coords[next]])
 							if not pointInRect(coords[j], bbox):
 								# Ouch!
-								warnings.warn("Outline has curve with implicit extrema.")
+								log.warning("Outline has curve with implicit extrema.")
 								# Ouch!  Find analytical curve bounds.
 								pthis = coords[j]
 								plast = coords[last]
@@ -606,19 +809,19 @@
 				self.xMin, self.yMin, self.xMax, self.yMax = calcIntBounds(coords)
 		else:
 			self.xMin, self.yMin, self.xMax, self.yMax = (0, 0, 0, 0)
-	
+
 	def isComposite(self):
 		"""Can be called on compact or expanded glyph."""
-		if hasattr(self, "data"):
+		if hasattr(self, "data") and self.data:
 			return struct.unpack(">h", self.data[:2])[0] == -1
 		else:
 			return self.numberOfContours == -1
-	
+
 	def __getitem__(self, componentIndex):
 		if not self.isComposite():
 			raise ttLib.TTLibError("can't use glyph as sequence")
 		return self.components[componentIndex]
-	
+
 	def getCoordinates(self, glyfTable):
 		if self.numberOfContours > 0:
 			return self.coordinates, self.endPtsOfContours, self.flags
@@ -637,7 +840,7 @@
 					move = x1-x2, y1-y2
 				else:
 					move = compo.x, compo.y
-				
+
 				coordinates = GlyphCoordinates(coordinates)
 				if not hasattr(compo, "transform"):
 					coordinates.translate(move)
@@ -696,14 +899,16 @@
 
 		return components
 
-	def removeHinting(self):
+	def trim(self, remove_hinting=False):
+		""" Remove padding and, if requested, hinting, from a glyph.
+			This works on both expanded and compacted glyphs, without
+			expanding it."""
 		if not hasattr(self, "data"):
-			self.program = ttProgram.Program()
-			self.program.fromBytecode([])
+			if remove_hinting:
+				self.program = ttProgram.Program()
+				self.program.fromBytecode([])
+			# No padding to trim.
 			return
-
-		# Remove instructions without expanding glyph
-
 		if not self.data:
 			return
 		numContours = struct.unpack(">h", self.data[:2])[0]
@@ -713,48 +918,52 @@
 			i += 2 * numContours # endPtsOfContours
 			nCoordinates = ((data[i-2] << 8) | data[i-1]) + 1
 			instructionLen = (data[i] << 8) | data[i+1]
-			# Zero it
-			data[i] = data [i+1] = 0
-			i += 2
-			if instructionLen:
-				# Splice it out
-				data = data[:i] + data[i+instructionLen:]
-				if instructionLen % 4:
-					# We now have to go ahead and drop
-					# the old padding.  Otherwise with
-					# padding we have to add, we may
-					# end up with more than 3 bytes of
-					# padding.
-					coordBytes = 0
-					j = 0
-					while True:
-						flag = data[i]
-						i = i + 1
-						repeat = 1
-						if flag & flagRepeat:
-							repeat = data[i] + 1
-							i = i + 1
-						xBytes = yBytes = 0
-						if flag & flagXShort:
-							xBytes = 1
-						elif not (flag & flagXsame):
-							xBytes = 2
-						if flag & flagYShort:
-							yBytes = 1
-						elif not (flag & flagYsame):
-							yBytes = 2
-						coordBytes += (xBytes + yBytes) * repeat
-						j += repeat
-						if j >= nCoordinates:
-							break
-					assert j == nCoordinates, "bad glyph flags"
-					data = data[:i + coordBytes]
+			if remove_hinting:
+				# Zero instruction length
+				data[i] = data [i+1] = 0
+				i += 2
+				if instructionLen:
+					# Splice it out
+					data = data[:i] + data[i+instructionLen:]
+				instructionLen = 0
+			else:
+				i += 2 + instructionLen
+
+			coordBytes = 0
+			j = 0
+			while True:
+				flag = data[i]
+				i = i + 1
+				repeat = 1
+				if flag & flagRepeat:
+					repeat = data[i] + 1
+					i = i + 1
+				xBytes = yBytes = 0
+				if flag & flagXShort:
+					xBytes = 1
+				elif not (flag & flagXsame):
+					xBytes = 2
+				if flag & flagYShort:
+					yBytes = 1
+				elif not (flag & flagYsame):
+					yBytes = 2
+				coordBytes += (xBytes + yBytes) * repeat
+				j += repeat
+				if j >= nCoordinates:
+					break
+			assert j == nCoordinates, "bad glyph flags"
+			i += coordBytes
+			# Remove padding
+			data = data[:i]
 		else:
 			more = 1
+			we_have_instructions = False
 			while more:
 				flags =(data[i] << 8) | data[i+1]
-				# Turn instruction flag off
-				flags &= ~WE_HAVE_INSTRUCTIONS
+				if remove_hinting:
+					flags &= ~WE_HAVE_INSTRUCTIONS
+				if flags & WE_HAVE_INSTRUCTIONS:
+					we_have_instructions = True
 				data[i+0] = flags >> 8
 				data[i+1] = flags & 0xFF
 				i += 4
@@ -766,36 +975,77 @@
 				elif flags & WE_HAVE_AN_X_AND_Y_SCALE: i += 4
 				elif flags & WE_HAVE_A_TWO_BY_TWO: i += 8
 				more = flags & MORE_COMPONENTS
-
-			# Cut off
+			if we_have_instructions:
+				instructionLen = (data[i] << 8) | data[i+1]
+				i += 2 + instructionLen
+			# Remove padding
 			data = data[:i]
 
-		data = data.tostring()
+		self.data = data.tostring()
 
-		if len(data) % 4:
-			# add pad bytes
-			nPadBytes = 4 - (len(data) % 4)
-			data = data + b"\0" * nPadBytes
+	def removeHinting(self):
+		self.trim (remove_hinting=True)
 
-		self.data = data
+	def draw(self, pen, glyfTable, offset=0):
 
-	def __ne__(self, other):
-		return not self.__eq__(other)
+		if self.isComposite():
+			for component in self.components:
+				glyphName, transform = component.getComponentInfo()
+				pen.addComponent(glyphName, transform)
+			return
+
+		coordinates, endPts, flags = self.getCoordinates(glyfTable)
+		if offset:
+			coordinates = coordinates.copy()
+			coordinates.translate((offset, 0))
+		start = 0
+		for end in endPts:
+			end = end + 1
+			contour = coordinates[start:end]
+			cFlags = flags[start:end]
+			start = end
+			if 1 not in cFlags:
+				# There is not a single on-curve point on the curve,
+				# use pen.qCurveTo's special case by specifying None
+				# as the on-curve point.
+				contour.append(None)
+				pen.qCurveTo(*contour)
+			else:
+				# Shuffle the points so that contour the is guaranteed
+				# to *end* in an on-curve point, which we'll use for
+				# the moveTo.
+				firstOnCurve = cFlags.index(1) + 1
+				contour = contour[firstOnCurve:] + contour[:firstOnCurve]
+				cFlags = cFlags[firstOnCurve:] + cFlags[:firstOnCurve]
+				pen.moveTo(contour[-1])
+				while contour:
+					nextOnCurve = cFlags.index(1) + 1
+					if nextOnCurve == 1:
+						pen.lineTo(contour[0])
+					else:
+						pen.qCurveTo(*contour[:nextOnCurve])
+					contour = contour[nextOnCurve:]
+					cFlags = cFlags[nextOnCurve:]
+			pen.closePath()
+
 	def __eq__(self, other):
 		if type(self) != type(other):
 			return NotImplemented
 		return self.__dict__ == other.__dict__
 
+	def __ne__(self, other):
+		result = self.__eq__(other)
+		return result if result is NotImplemented else not result
 
 class GlyphComponent(object):
-	
+
 	def __init__(self):
 		pass
-	
+
 	def getComponentInfo(self):
 		"""Return the base glyph name and a transform."""
 		# XXX Ignoring self.firstPt & self.lastpt for now: I need to implement
-		# something equivalent in fontTools.objects.glyph (I'd rather not 
+		# something equivalent in fontTools.objects.glyph (I'd rather not
 		# convert it to an absolute offset, since it is valuable information).
 		# This method will now raise "AttributeError: x" on glyphs that use
 		# this TT feature.
@@ -805,15 +1055,14 @@
 		else:
 			trans = (1, 0, 0, 1, self.x, self.y)
 		return self.glyphName, trans
-	
+
 	def decompile(self, data, glyfTable):
 		flags, glyphID = struct.unpack(">HH", data[:4])
 		self.flags = int(flags)
 		glyphID = int(glyphID)
 		self.glyphName = glyfTable.getGlyphName(int(glyphID))
-		#print ">>", reprflag(self.flags)
 		data = data[4:]
-		
+
 		if self.flags & ARG_1_AND_2_ARE_WORDS:
 			if self.flags & ARGS_ARE_XY_VALUES:
 				self.x, self.y = struct.unpack(">hh", data[:4])
@@ -828,7 +1077,7 @@
 				x, y = struct.unpack(">BB", data[:2])
 				self.firstPt, self.secondPt = int(x), int(y)
 			data = data[2:]
-		
+
 		if self.flags & WE_HAVE_A_SCALE:
 			scale, = struct.unpack(">h", data[:2])
 			self.transform = [[fi2fl(scale,14), 0], [0, fi2fl(scale,14)]]  # fixed 2.14
@@ -838,30 +1087,30 @@
 			self.transform = [[fi2fl(xscale,14), 0], [0, fi2fl(yscale,14)]]  # fixed 2.14
 			data = data[4:]
 		elif self.flags & WE_HAVE_A_TWO_BY_TWO:
-			(xscale, scale01, 
+			(xscale, scale01,
 					scale10, yscale) = struct.unpack(">hhhh", data[:8])
 			self.transform = [[fi2fl(xscale,14), fi2fl(scale01,14)],
-					  [fi2fl(scale10,14), fi2fl(yscale,14)]] # fixed 2.14
+							[fi2fl(scale10,14), fi2fl(yscale,14)]] # fixed 2.14
 			data = data[8:]
 		more = self.flags & MORE_COMPONENTS
 		haveInstructions = self.flags & WE_HAVE_INSTRUCTIONS
-		self.flags = self.flags & (ROUND_XY_TO_GRID | USE_MY_METRICS | 
+		self.flags = self.flags & (ROUND_XY_TO_GRID | USE_MY_METRICS |
 				SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET |
-				NON_OVERLAPPING)
+				NON_OVERLAPPING | OVERLAP_COMPOUND)
 		return more, haveInstructions, data
-	
+
 	def compile(self, more, haveInstructions, glyfTable):
 		data = b""
-		
+
 		# reset all flags we will calculate ourselves
-		flags = self.flags & (ROUND_XY_TO_GRID | USE_MY_METRICS | 
+		flags = self.flags & (ROUND_XY_TO_GRID | USE_MY_METRICS |
 				SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET |
-				NON_OVERLAPPING)
+				NON_OVERLAPPING | OVERLAP_COMPOUND)
 		if more:
 			flags = flags | MORE_COMPONENTS
 		if haveInstructions:
 			flags = flags | WE_HAVE_INSTRUCTIONS
-		
+
 		if hasattr(self, "firstPt"):
 			if (0 <= self.firstPt <= 255) and (0 <= self.secondPt <= 255):
 				data = data + struct.pack(">BB", self.firstPt, self.secondPt)
@@ -869,39 +1118,41 @@
 				data = data + struct.pack(">HH", self.firstPt, self.secondPt)
 				flags = flags | ARG_1_AND_2_ARE_WORDS
 		else:
+			x = otRound(self.x)
+			y = otRound(self.y)
 			flags = flags | ARGS_ARE_XY_VALUES
-			if (-128 <= self.x <= 127) and (-128 <= self.y <= 127):
-				data = data + struct.pack(">bb", self.x, self.y)
+			if (-128 <= x <= 127) and (-128 <= y <= 127):
+				data = data + struct.pack(">bb", x, y)
 			else:
-				data = data + struct.pack(">hh", self.x, self.y)
+				data = data + struct.pack(">hh", x, y)
 				flags = flags | ARG_1_AND_2_ARE_WORDS
-		
+
 		if hasattr(self, "transform"):
 			transform = [[fl2fi(x,14) for x in row] for row in self.transform]
 			if transform[0][1] or transform[1][0]:
 				flags = flags | WE_HAVE_A_TWO_BY_TWO
-				data = data + struct.pack(">hhhh", 
+				data = data + struct.pack(">hhhh",
 						transform[0][0], transform[0][1],
 						transform[1][0], transform[1][1])
 			elif transform[0][0] != transform[1][1]:
 				flags = flags | WE_HAVE_AN_X_AND_Y_SCALE
-				data = data + struct.pack(">hh", 
+				data = data + struct.pack(">hh",
 						transform[0][0], transform[1][1])
 			else:
 				flags = flags | WE_HAVE_A_SCALE
-				data = data + struct.pack(">h", 
+				data = data + struct.pack(">h",
 						transform[0][0])
-		
+
 		glyphID = glyfTable.getGlyphID(self.glyphName)
 		return struct.pack(">HH", flags, glyphID) + data
-	
+
 	def toXML(self, writer, ttFont):
 		attrs = [("glyphName", self.glyphName)]
 		if not hasattr(self, "firstPt"):
 			attrs = attrs + [("x", self.x), ("y", self.y)]
 		else:
 			attrs = attrs + [("firstPt", self.firstPt), ("secondPt", self.secondPt)]
-		
+
 		if hasattr(self, "transform"):
 			transform = self.transform
 			if transform[0][1] or transform[1][0]:
@@ -918,7 +1169,7 @@
 		attrs = attrs + [("flags", hex(self.flags))]
 		writer.simpletag("component", attrs)
 		writer.newline()
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		self.glyphName = attrs["glyphName"]
 		if "firstPt" in attrs:
@@ -941,29 +1192,41 @@
 			scale = safeEval(attrs["scale"])
 			self.transform = [[scale, 0], [0, scale]]
 		self.flags = safeEval(attrs["flags"])
-	
-	def __ne__(self, other):
-		return not self.__eq__(other)
+
 	def __eq__(self, other):
 		if type(self) != type(other):
 			return NotImplemented
 		return self.__dict__ == other.__dict__
 
+	def __ne__(self, other):
+		result = self.__eq__(other)
+		return result if result is NotImplemented else not result
+
 class GlyphCoordinates(object):
 
-	def __init__(self, iterable=[]):
-		self._a = array.array("h")
+	def __init__(self, iterable=[], typecode="h"):
+		self._a = array.array(typecode)
 		self.extend(iterable)
 
+	@property
+	def array(self):
+		return self._a
+
 	def isFloat(self):
-		return self._a.typecode == 'f'
+		return self._a.typecode == 'd'
 
 	def _ensureFloat(self):
 		if self.isFloat():
 			return
-		self._a = array.array("f", self._a)
+		# The conversion to list() is to work around Jython bug
+		self._a = array.array("d", list(self._a))
 
 	def _checkFloat(self, p):
+		if self.isFloat():
+			return p
+		if any(v > 0x7FFF or v < -0x8000 for v in p):
+			self._ensureFloat()
+			return p
 		if any(isinstance(v, float) for v in p):
 			p = [int(v) if int(v) == v else v for v in p]
 			if any(isinstance(v, float) for v in p):
@@ -975,7 +1238,7 @@
 		return GlyphCoordinates([(0,0)] * count)
 
 	def copy(self):
-		c = GlyphCoordinates()
+		c = GlyphCoordinates(typecode=self._a.typecode)
 		c._a.extend(self._a)
 		return c
 
@@ -992,13 +1255,17 @@
 		if isinstance(k, slice):
 			indices = range(*k.indices(len(self)))
 			# XXX This only works if len(v) == len(indices)
-			# TODO Implement __delitem__
 			for j,i in enumerate(indices):
 				self[i] = v[j]
 			return
 		v = self._checkFloat(v)
 		self._a[2*k],self._a[2*k+1] = v
 
+	def __delitem__(self, i):
+		i = (2*i) % len(self._a)
+		del self._a[i]
+		del self._a[i]
+
 	def __repr__(self):
 		return 'GlyphCoordinates(['+','.join(str(c) for c in self)+'])'
 
@@ -1011,12 +1278,21 @@
 			p = self._checkFloat(p)
 			self._a.extend(p)
 
+	def toInt(self):
+		if not self.isFloat():
+			return
+		a = array.array("h")
+		for n in self._a:
+			a.append(otRound(n))
+		self._a = a
+
 	def relativeToAbsolute(self):
 		a = self._a
 		x,y = 0,0
 		for i in range(len(a) // 2):
-			a[2*i  ] = x = a[2*i  ] + x
-			a[2*i+1] = y = a[2*i+1] + y
+			x = a[2*i  ] + x
+			y = a[2*i+1] + y
+			self[i] = (x, y)
 
 	def absoluteToRelative(self):
 		a = self._a
@@ -1026,17 +1302,30 @@
 			dy = a[2*i+1] - y
 			x = a[2*i  ]
 			y = a[2*i+1]
-			a[2*i  ] = dx
-			a[2*i+1] = dy
+			self[i] = (dx, dy)
 
 	def translate(self, p):
-		(x,y) = p
+		"""
+		>>> GlyphCoordinates([(1,2)]).translate((.5,0))
+		"""
+		(x,y) = self._checkFloat(p)
 		a = self._a
 		for i in range(len(a) // 2):
-			a[2*i  ] += x
-			a[2*i+1] += y
+			self[i] = (a[2*i] + x, a[2*i+1] + y)
+
+	def scale(self, p):
+		"""
+		>>> GlyphCoordinates([(1,2)]).scale((.5,0))
+		"""
+		(x,y) = self._checkFloat(p)
+		a = self._a
+		for i in range(len(a) // 2):
+			self[i] = (a[2*i] * x, a[2*i+1] * y)
 
 	def transform(self, t):
+		"""
+		>>> GlyphCoordinates([(1,2)]).transform(((.5,0),(.2,.5)))
+		"""
 		a = self._a
 		for i in range(len(a) // 2):
 			x = a[2*i  ]
@@ -1045,13 +1334,197 @@
 			py = x * t[0][1] + y * t[1][1]
 			self[i] = (px, py)
 
-	def __ne__(self, other):
-		return not self.__eq__(other)
 	def __eq__(self, other):
+		"""
+		>>> g = GlyphCoordinates([(1,2)])
+		>>> g2 = GlyphCoordinates([(1.0,2)])
+		>>> g3 = GlyphCoordinates([(1.5,2)])
+		>>> g == g2
+		True
+		>>> g == g3
+		False
+		>>> g2 == g3
+		False
+		"""
 		if type(self) != type(other):
 			return NotImplemented
 		return self._a == other._a
 
+	def __ne__(self, other):
+		"""
+		>>> g = GlyphCoordinates([(1,2)])
+		>>> g2 = GlyphCoordinates([(1.0,2)])
+		>>> g3 = GlyphCoordinates([(1.5,2)])
+		>>> g != g2
+		False
+		>>> g != g3
+		True
+		>>> g2 != g3
+		True
+		"""
+		result = self.__eq__(other)
+		return result if result is NotImplemented else not result
+
+	# Math operations
+
+	def __pos__(self):
+		"""
+		>>> g = GlyphCoordinates([(1,2)])
+		>>> g
+		GlyphCoordinates([(1, 2)])
+		>>> g2 = +g
+		>>> g2
+		GlyphCoordinates([(1, 2)])
+		>>> g2.translate((1,0))
+		>>> g2
+		GlyphCoordinates([(2, 2)])
+		>>> g
+		GlyphCoordinates([(1, 2)])
+		"""
+		return self.copy()
+	def __neg__(self):
+		"""
+		>>> g = GlyphCoordinates([(1,2)])
+		>>> g
+		GlyphCoordinates([(1, 2)])
+		>>> g2 = -g
+		>>> g2
+		GlyphCoordinates([(-1, -2)])
+		>>> g
+		GlyphCoordinates([(1, 2)])
+		"""
+		r = self.copy()
+		a = r._a
+		for i in range(len(a)):
+			a[i] = -a[i]
+		return r
+	def __round__(self):
+		"""
+		Note: This is Python 3 only.  Python 2 does not call __round__.
+		As such, we cannot test this method either. :(
+		"""
+		r = self.copy()
+		r.toInt()
+		return r
+
+	def __add__(self, other): return self.copy().__iadd__(other)
+	def __sub__(self, other): return self.copy().__isub__(other)
+	def __mul__(self, other): return self.copy().__imul__(other)
+	def __truediv__(self, other): return self.copy().__itruediv__(other)
+
+	__radd__ = __add__
+	__rmul__ = __mul__
+	def __rsub__(self, other): return other + (-self)
+
+	def __iadd__(self, other):
+		"""
+		>>> g = GlyphCoordinates([(1,2)])
+		>>> g += (.5,0)
+		>>> g
+		GlyphCoordinates([(1.5, 2.0)])
+		>>> g2 = GlyphCoordinates([(3,4)])
+		>>> g += g2
+		>>> g
+		GlyphCoordinates([(4.5, 6.0)])
+		"""
+		if isinstance(other, tuple):
+			assert len(other) ==  2
+			self.translate(other)
+			return self
+		if isinstance(other, GlyphCoordinates):
+			if other.isFloat(): self._ensureFloat()
+			other = other._a
+			a = self._a
+			assert len(a) == len(other)
+			for i in range(len(a) // 2):
+				self[i] = (a[2*i] + other[2*i], a[2*i+1] + other[2*i+1])
+			return self
+		return NotImplemented
+
+	def __isub__(self, other):
+		"""
+		>>> g = GlyphCoordinates([(1,2)])
+		>>> g -= (.5,0)
+		>>> g
+		GlyphCoordinates([(0.5, 2.0)])
+		>>> g2 = GlyphCoordinates([(3,4)])
+		>>> g -= g2
+		>>> g
+		GlyphCoordinates([(-2.5, -2.0)])
+		"""
+		if isinstance(other, tuple):
+			assert len(other) ==  2
+			self.translate((-other[0],-other[1]))
+			return self
+		if isinstance(other, GlyphCoordinates):
+			if other.isFloat(): self._ensureFloat()
+			other = other._a
+			a = self._a
+			assert len(a) == len(other)
+			for i in range(len(a) // 2):
+				self[i] = (a[2*i] - other[2*i], a[2*i+1] - other[2*i+1])
+			return self
+		return NotImplemented
+
+	def __imul__(self, other):
+		"""
+		>>> g = GlyphCoordinates([(1,2)])
+		>>> g *= (2,.5)
+		>>> g *= 2
+		>>> g
+		GlyphCoordinates([(4.0, 2.0)])
+		>>> g = GlyphCoordinates([(1,2)])
+		>>> g *= 2
+		>>> g
+		GlyphCoordinates([(2, 4)])
+		"""
+		if isinstance(other, Number):
+			other = (other, other)
+		if isinstance(other, tuple):
+			if other == (1,1):
+				return self
+			assert len(other) ==  2
+			self.scale(other)
+			return self
+		return NotImplemented
+
+	def __itruediv__(self, other):
+		"""
+		>>> g = GlyphCoordinates([(1,3)])
+		>>> g /= (.5,1.5)
+		>>> g /= 2
+		>>> g
+		GlyphCoordinates([(1.0, 1.0)])
+		"""
+		if isinstance(other, Number):
+			other = (other, other)
+		if isinstance(other, tuple):
+			if other == (1,1):
+				return self
+			assert len(other) ==  2
+			self.scale((1./other[0],1./other[1]))
+			return self
+		return NotImplemented
+
+	def __bool__(self):
+		"""
+		>>> g = GlyphCoordinates([])
+		>>> bool(g)
+		False
+		>>> g = GlyphCoordinates([(0,0), (0.,0)])
+		>>> bool(g)
+		True
+		>>> g = GlyphCoordinates([(0,0), (1,0)])
+		>>> bool(g)
+		True
+		>>> g = GlyphCoordinates([(0,.5), (0,0)])
+		>>> bool(g)
+		True
+		"""
+		return bool(self._a)
+
+	__nonzero__ = __bool__
+
 
 def reprflag(flag):
 	bin = ""
@@ -1066,3 +1539,7 @@
 	bin = (14 - len(bin)) * "0" + bin
 	return bin
 
+
+if __name__ == "__main__":
+	import doctest, sys
+	sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/ttLib/tables/_g_v_a_r.py b/Lib/fontTools/ttLib/tables/_g_v_a_r.py
new file mode 100644
index 0000000..9f97c31
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_g_v_a_r.py
@@ -0,0 +1,229 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools import ttLib
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import safeEval
+from fontTools.ttLib import TTLibError
+from . import DefaultTable
+import array
+import itertools
+import logging
+import struct
+import sys
+import fontTools.ttLib.tables.TupleVariation as tv
+
+
+log = logging.getLogger(__name__)
+TupleVariation = tv.TupleVariation
+
+
+# https://www.microsoft.com/typography/otspec/gvar.htm
+# https://www.microsoft.com/typography/otspec/otvarcommonformats.htm
+#
+# Apple's documentation of 'gvar':
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6gvar.html
+#
+# FreeType2 source code for parsing 'gvar':
+# http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/src/truetype/ttgxvar.c
+
+GVAR_HEADER_FORMAT = """
+	> # big endian
+	version:			H
+	reserved:			H
+	axisCount:			H
+	sharedTupleCount:		H
+	offsetToSharedTuples:		I
+	glyphCount:			H
+	flags:				H
+	offsetToGlyphVariationData:	I
+"""
+
+GVAR_HEADER_SIZE = sstruct.calcsize(GVAR_HEADER_FORMAT)
+
+
+class table__g_v_a_r(DefaultTable.DefaultTable):
+	dependencies = ["fvar", "glyf"]
+
+	def __init__(self, tag=None):
+		DefaultTable.DefaultTable.__init__(self, tag)
+		self.version, self.reserved = 1, 0
+		self.variations = {}
+
+	def compile(self, ttFont):
+		axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
+		sharedTuples =  tv.compileSharedTuples(
+			axisTags, itertools.chain(*self.variations.values()))
+		sharedTupleIndices = {coord:i for i, coord in enumerate(sharedTuples)}
+		sharedTupleSize = sum([len(c) for c in sharedTuples])
+		compiledGlyphs = self.compileGlyphs_(
+			ttFont, axisTags, sharedTupleIndices)
+		offset = 0
+		offsets = []
+		for glyph in compiledGlyphs:
+			offsets.append(offset)
+			offset += len(glyph)
+		offsets.append(offset)
+		compiledOffsets, tableFormat = self.compileOffsets_(offsets)
+
+		header = {}
+		header["version"] = self.version
+		header["reserved"] = self.reserved
+		header["axisCount"] = len(axisTags)
+		header["sharedTupleCount"] = len(sharedTuples)
+		header["offsetToSharedTuples"] = GVAR_HEADER_SIZE + len(compiledOffsets)
+		header["glyphCount"] = len(compiledGlyphs)
+		header["flags"] = tableFormat
+		header["offsetToGlyphVariationData"] = header["offsetToSharedTuples"] + sharedTupleSize
+		compiledHeader = sstruct.pack(GVAR_HEADER_FORMAT, header)
+
+		result = [compiledHeader, compiledOffsets]
+		result.extend(sharedTuples)
+		result.extend(compiledGlyphs)
+		return bytesjoin(result)
+
+	def compileGlyphs_(self, ttFont, axisTags, sharedCoordIndices):
+		result = []
+		for glyphName in ttFont.getGlyphOrder():
+			glyph = ttFont["glyf"][glyphName]
+			pointCount = self.getNumPoints_(glyph)
+			variations = self.variations.get(glyphName, [])
+			result.append(compileGlyph_(variations, pointCount,
+			                            axisTags, sharedCoordIndices))
+		return result
+
+	def decompile(self, data, ttFont):
+		axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
+		glyphs = ttFont.getGlyphOrder()
+		sstruct.unpack(GVAR_HEADER_FORMAT, data[0:GVAR_HEADER_SIZE], self)
+		assert len(glyphs) == self.glyphCount
+		assert len(axisTags) == self.axisCount
+		offsets = self.decompileOffsets_(data[GVAR_HEADER_SIZE:], tableFormat=(self.flags & 1), glyphCount=self.glyphCount)
+		sharedCoords = tv.decompileSharedTuples(
+			axisTags, self.sharedTupleCount, data, self.offsetToSharedTuples)
+		self.variations = {}
+		offsetToData = self.offsetToGlyphVariationData
+		for i in range(self.glyphCount):
+			glyphName = glyphs[i]
+			glyph = ttFont["glyf"][glyphName]
+			numPointsInGlyph = self.getNumPoints_(glyph)
+			gvarData = data[offsetToData + offsets[i] : offsetToData + offsets[i + 1]]
+			self.variations[glyphName] = decompileGlyph_(
+				numPointsInGlyph, sharedCoords, axisTags, gvarData)
+
+	@staticmethod
+	def decompileOffsets_(data, tableFormat, glyphCount):
+		if tableFormat == 0:
+			# Short format: array of UInt16
+			offsets = array.array("H")
+			offsetsSize = (glyphCount + 1) * 2
+		else:
+			# Long format: array of UInt32
+			offsets = array.array("I")
+			offsetsSize = (glyphCount + 1) * 4
+		offsets.fromstring(data[0 : offsetsSize])
+		if sys.byteorder != "big":
+			offsets.byteswap()
+
+		# In the short format, offsets need to be multiplied by 2.
+		# This is not documented in Apple's TrueType specification,
+		# but can be inferred from the FreeType implementation, and
+		# we could verify it with two sample GX fonts.
+		if tableFormat == 0:
+			offsets = [off * 2 for off in offsets]
+
+		return offsets
+
+	@staticmethod
+	def compileOffsets_(offsets):
+		"""Packs a list of offsets into a 'gvar' offset table.
+
+		Returns a pair (bytestring, tableFormat). Bytestring is the
+		packed offset table. Format indicates whether the table
+		uses short (tableFormat=0) or long (tableFormat=1) integers.
+		The returned tableFormat should get packed into the flags field
+		of the 'gvar' header.
+		"""
+		assert len(offsets) >= 2
+		for i in range(1, len(offsets)):
+			assert offsets[i - 1] <= offsets[i]
+		if max(offsets) <= 0xffff * 2:
+			packed = array.array("H", [n >> 1 for n in offsets])
+			tableFormat = 0
+		else:
+			packed = array.array("I", offsets)
+			tableFormat = 1
+		if sys.byteorder != "big":
+			packed.byteswap()
+		return (packed.tostring(), tableFormat)
+
+	def toXML(self, writer, ttFont):
+		writer.simpletag("version", value=self.version)
+		writer.newline()
+		writer.simpletag("reserved", value=self.reserved)
+		writer.newline()
+		axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
+		for glyphName in ttFont.getGlyphOrder():
+			variations = self.variations.get(glyphName)
+			if not variations:
+				continue
+			writer.begintag("glyphVariations", glyph=glyphName)
+			writer.newline()
+			for gvar in variations:
+				gvar.toXML(writer, axisTags)
+			writer.endtag("glyphVariations")
+			writer.newline()
+
+	def fromXML(self, name, attrs, content, ttFont):
+		if name == "version":
+			self.version = safeEval(attrs["value"])
+		elif name == "reserved":
+			self.reserved = safeEval(attrs["value"])
+		elif name == "glyphVariations":
+			if not hasattr(self, "variations"):
+				self.variations = {}
+			glyphName = attrs["glyph"]
+			glyph = ttFont["glyf"][glyphName]
+			numPointsInGlyph = self.getNumPoints_(glyph)
+			glyphVariations = []
+			for element in content:
+				if isinstance(element, tuple):
+					name, attrs, content = element
+					if name == "tuple":
+						gvar = TupleVariation({}, [None] * numPointsInGlyph)
+						glyphVariations.append(gvar)
+						for tupleElement in content:
+							if isinstance(tupleElement, tuple):
+								tupleName, tupleAttrs, tupleContent = tupleElement
+								gvar.fromXML(tupleName, tupleAttrs, tupleContent)
+			self.variations[glyphName] = glyphVariations
+
+	@staticmethod
+	def getNumPoints_(glyph):
+		NUM_PHANTOM_POINTS = 4
+		if glyph.isComposite():
+			return len(glyph.components) + NUM_PHANTOM_POINTS
+		else:
+			# Empty glyphs (eg. space, nonmarkingreturn) have no "coordinates" attribute.
+			return len(getattr(glyph, "coordinates", [])) + NUM_PHANTOM_POINTS
+
+
+def compileGlyph_(variations, pointCount, axisTags, sharedCoordIndices):
+	tupleVariationCount, tuples, data = tv.compileTupleVariationStore(
+		variations, pointCount, axisTags, sharedCoordIndices)
+	if tupleVariationCount == 0:
+		return b""
+	result = (struct.pack(">HH", tupleVariationCount, 4 + len(tuples)) +
+	          tuples + data)
+	if len(result) % 2 != 0:
+		result = result + b"\0"  # padding
+	return result
+
+
+def decompileGlyph_(pointCount, sharedTuples, axisTags, data):
+	if len(data) < 4:
+		return []
+	tupleVariationCount, offsetToData = struct.unpack(">HH", data[:4])
+	dataPos = offsetToData
+	return tv.decompileTupleVariationStore("gvar", axisTags,
+                                           tupleVariationCount, pointCount,
+                                           sharedTuples, data, 4, offsetToData)
diff --git a/Lib/fontTools/ttLib/tables/_h_d_m_x.py b/Lib/fontTools/ttLib/tables/_h_d_m_x.py
index 06fca7d..e073325 100644
--- a/Lib/fontTools/ttLib/tables/_h_d_m_x.py
+++ b/Lib/fontTools/ttLib/tables/_h_d_m_x.py
@@ -2,6 +2,7 @@
 from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
 from . import DefaultTable
+import array
 
 hdmxHeaderFormat = """
 	>   # big endian!
@@ -10,8 +11,31 @@
 	recordSize:	l
 """
 
+try:
+	from collections.abc import Mapping
+except:
+	from UserDict import DictMixin as Mapping
+
+class _GlyphnamedList(Mapping):
+
+	def __init__(self, reverseGlyphOrder, data):
+		self._array = data
+		self._map = dict(reverseGlyphOrder)
+
+	def __getitem__(self, k):
+		return self._array[self._map[k]]
+
+	def __len__(self):
+		return len(self._map)
+
+	def __iter__(self):
+		return iter(self._map)
+
+	def keys(self):
+		return self._map.keys()
+
 class table__h_d_m_x(DefaultTable.DefaultTable):
-	
+
 	def decompile(self, data, ttFont):
 		numGlyphs = ttFont['maxp'].numGlyphs
 		glyphOrder = ttFont.getGlyphOrder()
@@ -20,13 +44,11 @@
 		for i in range(self.numRecords):
 			ppem = byteord(data[0])
 			maxSize = byteord(data[1])
-			widths = {}
-			for glyphID in range(numGlyphs):
-				widths[glyphOrder[glyphID]] = byteord(data[glyphID+2])
+			widths = _GlyphnamedList(ttFont.getReverseGlyphMap(), array.array("B", data[2:2+numGlyphs]))
 			self.hdmx[ppem] = widths
 			data = data[self.recordSize:]
 		assert len(data) == 0, "too much hdmx data"
-	
+
 	def compile(self, ttFont):
 		self.version = 0
 		numGlyphs = ttFont['maxp'].numGlyphs
@@ -43,7 +65,7 @@
 				data = data + bytechr(width)
 			data = data + pad
 		return data
-	
+
 	def toXML(self, writer, ttFont):
 		writer.begintag("hdmxData")
 		writer.newline()
@@ -72,7 +94,7 @@
 			writer.newline()
 		writer.endtag("hdmxData")
 		writer.newline()
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		if name != "hdmxData":
 			return
@@ -97,4 +119,3 @@
 			assert len(line) == len(ppems), "illegal hdmx format"
 			for i in range(len(ppems)):
 				hdmx[ppems[i]][glyphName] = line[i]
-
diff --git a/Lib/fontTools/ttLib/tables/_h_e_a_d.py b/Lib/fontTools/ttLib/tables/_h_e_a_d.py
index bf4116d..9275d41 100644
--- a/Lib/fontTools/ttLib/tables/_h_e_a_d.py
+++ b/Lib/fontTools/ttLib/tables/_h_e_a_d.py
@@ -2,11 +2,14 @@
 from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
 from fontTools.misc.textTools import safeEval, num2binary, binary2num
+from fontTools.misc.timeTools import timestampFromString, timestampToString, timestampNow
+from fontTools.misc.timeTools import epoch_diff as mac_epoch_diff # For backward compat
 from . import DefaultTable
-import time
-import calendar
+import logging
 
 
+log = logging.getLogger(__name__)
+
 headFormat = """
 		>	# big endian
 		tableVersion:       16.16F
@@ -29,21 +32,43 @@
 """
 
 class table__h_e_a_d(DefaultTable.DefaultTable):
-	
-	dependencies = ['maxp', 'loca']
-	
+
+	dependencies = ['maxp', 'loca', 'CFF ']
+
 	def decompile(self, data, ttFont):
 		dummy, rest = sstruct.unpack2(headFormat, data, self)
 		if rest:
 			# this is quite illegal, but there seem to be fonts out there that do this
+			log.warning("extra bytes at the end of 'head' table")
 			assert rest == "\0\0"
-	
+
+		# For timestamp fields, ignore the top four bytes.  Some fonts have
+		# bogus values there.  Since till 2038 those bytes only can be zero,
+		# ignore them.
+		#
+		# https://github.com/behdad/fonttools/issues/99#issuecomment-66776810
+		for stamp in 'created', 'modified':
+			value = getattr(self, stamp)
+			if value > 0xFFFFFFFF:
+				log.warning("'%s' timestamp out of range; ignoring top bytes", stamp)
+				value &= 0xFFFFFFFF
+				setattr(self, stamp, value)
+			if value < 0x7C259DC0: # January 1, 1970 00:00:00
+				log.warning("'%s' timestamp seems very low; regarding as unix timestamp", stamp)
+				value += 0x7C259DC0
+				setattr(self, stamp, value)
+
 	def compile(self, ttFont):
+		if ttFont.recalcBBoxes:
+			# For TT-flavored fonts, xMin, yMin, xMax and yMax are set in table__m_a_x_p.recalc().
+			if 'CFF ' in ttFont:
+				topDict = ttFont['CFF '].cff.topDictIndex[0]
+				self.xMin, self.yMin, self.xMax, self.yMax = topDict.FontBBox
 		if ttFont.recalcTimestamp:
-			self.modified = int(time.time() - mac_epoch_diff)
+			self.modified = timestampNow()
 		data = sstruct.pack(headFormat, self)
 		return data
-	
+
 	def toXML(self, writer, ttFont):
 		writer.comment("Most of this table will be recalculated by the compiler")
 		writer.newline()
@@ -51,10 +76,7 @@
 		for name in names:
 			value = getattr(self, name)
 			if name in ("created", "modified"):
-				try:
-					value = time.asctime(time.gmtime(max(0, value + mac_epoch_diff)))
-				except ValueError:
-					value = time.asctime(time.gmtime(0))
+				value = timestampToString(value)
 			if name in ("magicNumber", "checkSumAdjustment"):
 				if value < 0:
 					value = value + 0x100000000
@@ -65,17 +87,13 @@
 				value = num2binary(value, 16)
 			writer.simpletag(name, value=value)
 			writer.newline()
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		value = attrs["value"]
 		if name in ("created", "modified"):
-			value = calendar.timegm(time.strptime(value)) - mac_epoch_diff
+			value = timestampFromString(value)
 		elif name in ("macStyle", "flags"):
 			value = binary2num(value)
 		else:
 			value = safeEval(value)
 		setattr(self, name, value)
-
-
-# Difference between the original Mac epoch (1904) to the epoch on this machine.
-mac_epoch_diff = calendar.timegm((1904, 1, 1, 0, 0, 0, 0, 0, 0))
diff --git a/Lib/fontTools/ttLib/tables/_h_h_e_a.py b/Lib/fontTools/ttLib/tables/_h_h_e_a.py
index f8b7eb3..bde9073 100644
--- a/Lib/fontTools/ttLib/tables/_h_h_e_a.py
+++ b/Lib/fontTools/ttLib/tables/_h_h_e_a.py
@@ -2,12 +2,15 @@
 from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
 from fontTools.misc.textTools import safeEval
+from fontTools.misc.fixedTools import (
+	ensureVersionIsLong as fi2ve, versionToFixed as ve2fi)
 from . import DefaultTable
+import math
 
 
 hheaFormat = """
 		>  # big endian
-		tableVersion:           16.16F
+		tableVersion:           L
 		ascent:                 h
 		descent:                h
 		lineGap:                h
@@ -28,30 +31,29 @@
 
 
 class table__h_h_e_a(DefaultTable.DefaultTable):
-	
-	dependencies = ['hmtx', 'glyf']
-	
+
+	# Note: Keep in sync with table__v_h_e_a
+
+	dependencies = ['hmtx', 'glyf', 'CFF ']
+
 	def decompile(self, data, ttFont):
 		sstruct.unpack(hheaFormat, data, self)
-	
+
 	def compile(self, ttFont):
-		if ttFont.isLoaded('glyf') and ttFont.recalcBBoxes:
+		if ttFont.recalcBBoxes and (ttFont.isLoaded('glyf') or ttFont.isLoaded('CFF ')):
 			self.recalc(ttFont)
+		self.tableVersion = fi2ve(self.tableVersion)
 		return sstruct.pack(hheaFormat, self)
-	
+
 	def recalc(self, ttFont):
-		hmtxTable = ttFont['hmtx']
+		if 'hmtx' in ttFont:
+			hmtxTable = ttFont['hmtx']
+			self.advanceWidthMax = max(adv for adv, _ in hmtxTable.metrics.values())
+
+		boundsWidthDict = {}
 		if 'glyf' in ttFont:
 			glyfTable = ttFont['glyf']
-			INFINITY = 100000
-			advanceWidthMax = 0
-			minLeftSideBearing = +INFINITY  # arbitrary big number
-			minRightSideBearing = +INFINITY # arbitrary big number
-			xMaxExtent = -INFINITY          # arbitrary big negative number
-			
 			for name in ttFont.getGlyphOrder():
-				width, lsb = hmtxTable[name]
-				advanceWidthMax = max(advanceWidthMax, width)
 				g = glyfTable[name]
 				if g.numberOfContours == 0:
 					continue
@@ -59,33 +61,49 @@
 					# Composite glyph without extents set.
 					# Calculate those.
 					g.recalcBounds(glyfTable)
+				boundsWidthDict[name] = g.xMax - g.xMin
+		elif 'CFF ' in ttFont:
+			topDict = ttFont['CFF '].cff.topDictIndex[0]
+			charStrings = topDict.CharStrings
+			for name in ttFont.getGlyphOrder():
+				cs = charStrings[name]
+				bounds = cs.calcBounds(charStrings)
+				if bounds is not None:
+					boundsWidthDict[name] = int(
+						math.ceil(bounds[2]) - math.floor(bounds[0]))
+
+		if boundsWidthDict:
+			minLeftSideBearing = float('inf')
+			minRightSideBearing = float('inf')
+			xMaxExtent = -float('inf')
+			for name, boundsWidth in boundsWidthDict.items():
+				advanceWidth, lsb = hmtxTable[name]
+				rsb = advanceWidth - lsb - boundsWidth
+				extent = lsb + boundsWidth
 				minLeftSideBearing = min(minLeftSideBearing, lsb)
-				rsb = width - lsb - (g.xMax - g.xMin)
 				minRightSideBearing = min(minRightSideBearing, rsb)
-				extent = lsb + (g.xMax - g.xMin)
 				xMaxExtent = max(xMaxExtent, extent)
-
-			if xMaxExtent == -INFINITY:
-				# No glyph has outlines.
-				minLeftSideBearing = 0
-				minRightSideBearing = 0
-				xMaxExtent = 0
-
-			self.advanceWidthMax = advanceWidthMax
 			self.minLeftSideBearing = minLeftSideBearing
 			self.minRightSideBearing = minRightSideBearing
 			self.xMaxExtent = xMaxExtent
-		else:
-			# XXX CFF recalc...
-			pass
-	
+
+		else:  # No glyph has outlines.
+			self.minLeftSideBearing = 0
+			self.minRightSideBearing = 0
+			self.xMaxExtent = 0
+
 	def toXML(self, writer, ttFont):
 		formatstring, names, fixes = sstruct.getformat(hheaFormat)
 		for name in names:
 			value = getattr(self, name)
+			if name == "tableVersion":
+				value = fi2ve(value)
+				value = "0x%08x" % value
 			writer.simpletag(name, value=value)
 			writer.newline()
-	
-	def fromXML(self, name, attrs, content, ttFont):
-		setattr(self, name, safeEval(attrs["value"]))
 
+	def fromXML(self, name, attrs, content, ttFont):
+		if name == "tableVersion":
+			setattr(self, name, ve2fi(attrs["value"]))
+			return
+		setattr(self, name, safeEval(attrs["value"]))
diff --git a/Lib/fontTools/ttLib/tables/_h_m_t_x.py b/Lib/fontTools/ttLib/tables/_h_m_t_x.py
index c7b5ee9..e6b8ea9 100644
--- a/Lib/fontTools/ttLib/tables/_h_m_t_x.py
+++ b/Lib/fontTools/ttLib/tables/_h_m_t_x.py
@@ -1,28 +1,40 @@
 from __future__ import print_function, division, absolute_import
 from fontTools.misc.py23 import *
+from fontTools.misc.fixedTools import otRound
+from fontTools import ttLib
 from fontTools.misc.textTools import safeEval
 from . import DefaultTable
 import sys
+import struct
 import array
-import warnings
+import logging
+
+
+log = logging.getLogger(__name__)
 
 
 class table__h_m_t_x(DefaultTable.DefaultTable):
-	
+
 	headerTag = 'hhea'
 	advanceName = 'width'
 	sideBearingName = 'lsb'
 	numberOfMetricsName = 'numberOfHMetrics'
-	
+	longMetricFormat = 'Hh'
+
 	def decompile(self, data, ttFont):
 		numGlyphs = ttFont['maxp'].numGlyphs
 		numberOfMetrics = int(getattr(ttFont[self.headerTag], self.numberOfMetricsName))
 		if numberOfMetrics > numGlyphs:
-			numberOfMetrics = numGlyphs # We warn later.
-		# Note: advanceWidth is unsigned, but we read/write as signed.
-		metrics = array.array("h", data[:4 * numberOfMetrics])
-		if sys.byteorder != "big":
-			metrics.byteswap()
+			log.warning("The %s.%s exceeds the maxp.numGlyphs" % (
+				self.headerTag, self.numberOfMetricsName))
+			numberOfMetrics = numGlyphs
+		if len(data) < 4 * numberOfMetrics:
+			raise ttLib.TTLibError("not enough '%s' table data" % self.tableTag)
+		# Note: advanceWidth is unsigned, but some font editors might
+		# read/write as signed. We can't be sure whether it was a mistake
+		# or not, so we read as unsigned but also issue a warning...
+		metricsFmt = ">" + self.longMetricFormat * numberOfMetrics
+		metrics = struct.unpack(metricsFmt, data[:4 * numberOfMetrics])
 		data = data[4 * numberOfMetrics:]
 		numberOfSideBearings = numGlyphs - numberOfMetrics
 		sideBearings = array.array("h", data[:2 * numberOfSideBearings])
@@ -31,21 +43,33 @@
 		if sys.byteorder != "big":
 			sideBearings.byteswap()
 		if data:
-			warnings.warn("too much 'hmtx'/'vmtx' table data")
+			log.warning("too much '%s' table data" % self.tableTag)
 		self.metrics = {}
 		glyphOrder = ttFont.getGlyphOrder()
 		for i in range(numberOfMetrics):
 			glyphName = glyphOrder[i]
-			self.metrics[glyphName] = list(metrics[i*2:i*2+2])
+			advanceWidth, lsb = metrics[i*2:i*2+2]
+			if advanceWidth > 32767:
+				log.warning(
+					"Glyph %r has a huge advance %s (%d); is it intentional or "
+					"an (invalid) negative value?", glyphName, self.advanceName,
+					advanceWidth)
+			self.metrics[glyphName] = (advanceWidth, lsb)
 		lastAdvance = metrics[-2]
 		for i in range(numberOfSideBearings):
 			glyphName = glyphOrder[i + numberOfMetrics]
-			self.metrics[glyphName] = [lastAdvance, sideBearings[i]]
-	
+			self.metrics[glyphName] = (lastAdvance, sideBearings[i])
+
 	def compile(self, ttFont):
 		metrics = []
+		hasNegativeAdvances = False
 		for glyphName in ttFont.getGlyphOrder():
-			metrics.append(self.metrics[glyphName])
+			advanceWidth, sideBearing = self.metrics[glyphName]
+			if advanceWidth < 0:
+				log.error("Glyph %r has negative advance %s" % (
+					glyphName, self.advanceName))
+				hasNegativeAdvances = True
+			metrics.append([advanceWidth, sideBearing])
 		lastAdvance = metrics[-1][0]
 		lastIndex = len(metrics)
 		while metrics[lastIndex-2][0] == lastAdvance:
@@ -55,48 +79,53 @@
 				lastIndex = 1
 				break
 		additionalMetrics = metrics[lastIndex:]
-		additionalMetrics = [sb for advance, sb in additionalMetrics]
+		additionalMetrics = [otRound(sb) for _, sb in additionalMetrics]
 		metrics = metrics[:lastIndex]
-		setattr(ttFont[self.headerTag], self.numberOfMetricsName, len(metrics))
-		
+		numberOfMetrics = len(metrics)
+		setattr(ttFont[self.headerTag], self.numberOfMetricsName, numberOfMetrics)
+
 		allMetrics = []
-		for item in metrics:
-			allMetrics.extend(item)
-		allMetrics = array.array("h", allMetrics)
-		if sys.byteorder != "big":
-			allMetrics.byteswap()
-		data = allMetrics.tostring()
-		
+		for advance, sb in metrics:
+			allMetrics.extend([otRound(advance), otRound(sb)])
+		metricsFmt = ">" + self.longMetricFormat * numberOfMetrics
+		try:
+			data = struct.pack(metricsFmt, *allMetrics)
+		except struct.error as e:
+			if "out of range" in str(e) and hasNegativeAdvances:
+				raise ttLib.TTLibError(
+					"'%s' table can't contain negative advance %ss"
+					% (self.tableTag, self.advanceName))
+			else:
+				raise
 		additionalMetrics = array.array("h", additionalMetrics)
 		if sys.byteorder != "big":
 			additionalMetrics.byteswap()
 		data = data + additionalMetrics.tostring()
 		return data
-	
+
 	def toXML(self, writer, ttFont):
 		names = sorted(self.metrics.keys())
 		for glyphName in names:
 			advance, sb = self.metrics[glyphName]
 			writer.simpletag("mtx", [
-					("name", glyphName), 
-					(self.advanceName, advance), 
+					("name", glyphName),
+					(self.advanceName, advance),
 					(self.sideBearingName, sb),
 					])
 			writer.newline()
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		if not hasattr(self, "metrics"):
 			self.metrics = {}
 		if name == "mtx":
-			self.metrics[attrs["name"]] = [safeEval(attrs[self.advanceName]), 
-					safeEval(attrs[self.sideBearingName])]
+			self.metrics[attrs["name"]] = (safeEval(attrs[self.advanceName]),
+					safeEval(attrs[self.sideBearingName]))
 
 	def __delitem__(self, glyphName):
 		del self.metrics[glyphName]
-	
+
 	def __getitem__(self, glyphName):
 		return self.metrics[glyphName]
-	
+
 	def __setitem__(self, glyphName, advance_sb_pair):
 		self.metrics[glyphName] = tuple(advance_sb_pair)
-
diff --git a/Lib/fontTools/ttLib/tables/_k_e_r_n.py b/Lib/fontTools/ttLib/tables/_k_e_r_n.py
index 928298b..6e21a4b 100644
--- a/Lib/fontTools/ttLib/tables/_k_e_r_n.py
+++ b/Lib/fontTools/ttLib/tables/_k_e_r_n.py
@@ -2,20 +2,27 @@
 from fontTools.misc.py23 import *
 from fontTools.ttLib import getSearchRange
 from fontTools.misc.textTools import safeEval, readHex
-from fontTools.misc.fixedTools import fixedToFloat as fi2fl, floatToFixed as fl2fi
+from fontTools.misc.fixedTools import (
+	fixedToFloat as fi2fl,
+	floatToFixed as fl2fi)
 from . import DefaultTable
 import struct
-import warnings
+import sys
+import array
+import logging
+
+
+log = logging.getLogger(__name__)
 
 
 class table__k_e_r_n(DefaultTable.DefaultTable):
-	
+
 	def getkern(self, format):
 		for subtable in self.kernTables:
-			if subtable.version == format:
+			if subtable.format == format:
 				return subtable
 		return None  # not found
-	
+
 	def decompile(self, data, ttFont):
 		version, nTables = struct.unpack(">HH", data[:4])
 		apple = False
@@ -28,25 +35,27 @@
 		else:
 			self.version = version
 			data = data[4:]
-		tablesIndex = []
 		self.kernTables = []
 		for i in range(nTables):
 			if self.version == 1.0:
 				# Apple
-				length, coverage, tupleIndex = struct.unpack(">lHH", data[:8])
-				version = coverage & 0xff
+				length, coverage, subtableFormat = struct.unpack(
+					">LBB", data[:6])
 			else:
-				version, length = struct.unpack(">HH", data[:4])
-			length = int(length)
-			if version not in kern_classes:
-				subtable = KernTable_format_unkown(version)
+				# in OpenType spec the "version" field refers to the common
+				# subtable header; the actual subtable format is stored in
+				# the 8-15 mask bits of "coverage" field.
+				# This "version" is always 0 so we ignore it here
+				_, length, subtableFormat, coverage = struct.unpack(
+					">HHBB", data[:6])
+			if subtableFormat not in kern_classes:
+				subtable = KernTable_format_unkown(subtableFormat)
 			else:
-				subtable = kern_classes[version]()
-			subtable.apple = apple
+				subtable = kern_classes[subtableFormat](apple)
 			subtable.decompile(data[:length], ttFont)
 			self.kernTables.append(subtable)
 			data = data[length:]
-	
+
 	def compile(self, ttFont):
 		if hasattr(self, "kernTables"):
 			nTables = len(self.kernTables)
@@ -54,20 +63,20 @@
 			nTables = 0
 		if self.version == 1.0:
 			# AAT Apple's "new" format.
-			data = struct.pack(">ll", fl2fi(self.version, 16), nTables)
+			data = struct.pack(">LL", fl2fi(self.version, 16), nTables)
 		else:
 			data = struct.pack(">HH", self.version, nTables)
 		if hasattr(self, "kernTables"):
 			for subtable in self.kernTables:
 				data = data + subtable.compile(ttFont)
 		return data
-	
+
 	def toXML(self, writer, ttFont):
 		writer.simpletag("version", value=self.version)
 		writer.newline()
 		for subtable in self.kernTables:
 			subtable.toXML(writer, ttFont)
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		if name == "version":
 			self.version = safeEval(attrs["value"])
@@ -80,69 +89,142 @@
 		if format not in kern_classes:
 			subtable = KernTable_format_unkown(format)
 		else:
-			subtable = kern_classes[format]()
+			apple = self.version == 1.0
+			subtable = kern_classes[format](apple)
 		self.kernTables.append(subtable)
 		subtable.fromXML(name, attrs, content, ttFont)
 
 
 class KernTable_format_0(object):
-	
+
+	# 'version' is kept for backward compatibility
+	version = format = 0
+
+	def __init__(self, apple=False):
+		self.apple = apple
+
 	def decompile(self, data, ttFont):
-		version, length, coverage = (0,0,0)
 		if not self.apple:
-			version, length, coverage = struct.unpack(">HHH", data[:6])
+			version, length, subtableFormat, coverage = struct.unpack(
+				">HHBB", data[:6])
+			if version != 0:
+				from fontTools.ttLib import TTLibError
+				raise TTLibError(
+					"unsupported kern subtable version: %d" % version)
+			tupleIndex = None
+			# Should we also assert length == len(data)?
 			data = data[6:]
 		else:
-			version, length, coverage = struct.unpack(">LHH", data[:8])
+			length, coverage, subtableFormat, tupleIndex = struct.unpack(
+				">LBBH", data[:8])
 			data = data[8:]
-		self.version, self.coverage = int(version), int(coverage)
-		
+		assert self.format == subtableFormat, "unsupported format"
+		self.coverage = coverage
+		self.tupleIndex = tupleIndex
+
 		self.kernTable = kernTable = {}
-		
-		nPairs, searchRange, entrySelector, rangeShift = struct.unpack(">HHHH", data[:8])
+
+		nPairs, searchRange, entrySelector, rangeShift = struct.unpack(
+			">HHHH", data[:8])
 		data = data[8:]
-		
+
+		nPairs = min(nPairs, len(data) // 6)
+		datas = array.array("H", data[:6 * nPairs])
+		if sys.byteorder != "big":  # pragma: no cover
+			datas.byteswap()
+		it = iter(datas)
+		glyphOrder = ttFont.getGlyphOrder()
 		for k in range(nPairs):
-			if len(data) < 6:
-				# buggy kern table
-				data = b""
-				break
-			left, right, value = struct.unpack(">HHh", data[:6])
-			data = data[6:]
-			left, right = int(left), int(right)
-			kernTable[(ttFont.getGlyphName(left), ttFont.getGlyphName(right))] = value
-		if len(data):
-			warnings.warn("excess data in 'kern' subtable: %d bytes" % len(data))
-	
+			left, right, value = next(it), next(it), next(it)
+			if value >= 32768:
+				value -= 65536
+			try:
+				kernTable[(glyphOrder[left], glyphOrder[right])] = value
+			except IndexError:
+				# Slower, but will not throw an IndexError on an invalid
+				# glyph id.
+				kernTable[(
+					ttFont.getGlyphName(left),
+					ttFont.getGlyphName(right))] = value
+		if len(data) > 6 * nPairs + 4:  # Ignore up to 4 bytes excess
+			log.warning(
+				"excess data in 'kern' subtable: %d bytes",
+				len(data) - 6 * nPairs)
+
 	def compile(self, ttFont):
 		nPairs = len(self.kernTable)
 		searchRange, entrySelector, rangeShift = getSearchRange(nPairs, 6)
-		data = struct.pack(">HHHH", nPairs, searchRange, entrySelector, rangeShift)
-		
+		data = struct.pack(
+			">HHHH", nPairs, searchRange, entrySelector, rangeShift)
+
 		# yeehee! (I mean, turn names into indices)
-		getGlyphID = ttFont.getGlyphID
-		kernTable = sorted((getGlyphID(left), getGlyphID(right), value) for ((left,right),value) in self.kernTable.items())
+		try:
+			reverseOrder = ttFont.getReverseGlyphMap()
+			kernTable = sorted(
+				(reverseOrder[left], reverseOrder[right], value)
+				for ((left, right), value) in self.kernTable.items())
+		except KeyError:
+			# Slower, but will not throw KeyError on invalid glyph id.
+			getGlyphID = ttFont.getGlyphID
+			kernTable = sorted(
+				(getGlyphID(left), getGlyphID(right), value)
+				for ((left, right), value) in self.kernTable.items())
+
 		for left, right, value in kernTable:
 			data = data + struct.pack(">HHh", left, right, value)
-		return struct.pack(">HHH", self.version, len(data) + 6, self.coverage) + data
-	
+
+		if not self.apple:
+			version = 0
+			length = len(data) + 6
+			header = struct.pack(
+				">HHBB", version, length, self.format, self.coverage)
+		else:
+			if self.tupleIndex is None:
+				# sensible default when compiling a TTX from an old fonttools
+				# or when inserting a Windows-style format 0 subtable into an
+				# Apple version=1.0 kern table
+				log.warning("'tupleIndex' is None; default to 0")
+				self.tupleIndex = 0
+			length = len(data) + 8
+			header = struct.pack(
+				">LBBH", length, self.coverage, self.format, self.tupleIndex)
+		return header + data
+
 	def toXML(self, writer, ttFont):
-		writer.begintag("kernsubtable", coverage=self.coverage, format=0)
+		attrs = dict(coverage=self.coverage, format=self.format)
+		if self.apple:
+			if self.tupleIndex is None:
+				log.warning("'tupleIndex' is None; default to 0")
+				attrs["tupleIndex"] = 0
+			else:
+				attrs["tupleIndex"] = self.tupleIndex
+		writer.begintag("kernsubtable", **attrs)
 		writer.newline()
 		items = sorted(self.kernTable.items())
 		for (left, right), value in items:
 			writer.simpletag("pair", [
-					("l", left),
-					("r", right),
-					("v", value)
-					])
+				("l", left),
+				("r", right),
+				("v", value)
+			])
 			writer.newline()
 		writer.endtag("kernsubtable")
 		writer.newline()
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		self.coverage = safeEval(attrs["coverage"])
-		self.version = safeEval(attrs["format"])
+		subtableFormat = safeEval(attrs["format"])
+		if self.apple:
+			if "tupleIndex" in attrs:
+				self.tupleIndex = safeEval(attrs["tupleIndex"])
+			else:
+				# previous fontTools versions didn't export tupleIndex
+				log.warning(
+					"Apple kern subtable is missing 'tupleIndex' attribute")
+				self.tupleIndex = None
+		else:
+			self.tupleIndex = None
+		assert subtableFormat == self.format, "unsupported format"
 		if not hasattr(self, "kernTable"):
 			self.kernTable = {}
 		for element in content:
@@ -150,47 +232,28 @@
 				continue
 			name, attrs, content = element
 			self.kernTable[(attrs["l"], attrs["r"])] = safeEval(attrs["v"])
-	
+
 	def __getitem__(self, pair):
 		return self.kernTable[pair]
-	
+
 	def __setitem__(self, pair, value):
 		self.kernTable[pair] = value
-	
+
 	def __delitem__(self, pair):
 		del self.kernTable[pair]
 
 
-class KernTable_format_2(object):
-	
-	def decompile(self, data, ttFont):
-		self.data = data
-	
-	def compile(self, ttFont):
-		return self.data
-	
-	def toXML(self, writer):
-		writer.begintag("kernsubtable", format=2)
-		writer.newline()
-		writer.dumphex(self.data)
-		writer.endtag("kernsubtable")
-		writer.newline()
-	
-	def fromXML(self, name, attrs, content, ttFont):
-		self.decompile(readHex(content), ttFont)
-
-
 class KernTable_format_unkown(object):
-	
+
 	def __init__(self, format):
 		self.format = format
-	
+
 	def decompile(self, data, ttFont):
 		self.data = data
-	
+
 	def compile(self, ttFont):
 		return self.data
-	
+
 	def toXML(self, writer, ttFont):
 		writer.begintag("kernsubtable", format=self.format)
 		writer.newline()
@@ -199,10 +262,9 @@
 		writer.dumphex(self.data)
 		writer.endtag("kernsubtable")
 		writer.newline()
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		self.decompile(readHex(content), ttFont)
 
 
-
-kern_classes = {0: KernTable_format_0, 2: KernTable_format_2}
+kern_classes = {0: KernTable_format_0}
diff --git a/Lib/fontTools/ttLib/tables/_l_c_a_r.py b/Lib/fontTools/ttLib/tables/_l_c_a_r.py
new file mode 100644
index 0000000..4b9c854
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_l_c_a_r.py
@@ -0,0 +1,7 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from .otBase import BaseTTXConverter
+
+
+class table__l_c_a_r(BaseTTXConverter):
+	pass
diff --git a/Lib/fontTools/ttLib/tables/_l_o_c_a.py b/Lib/fontTools/ttLib/tables/_l_o_c_a.py
index 1ce9cab..2fcd528 100644
--- a/Lib/fontTools/ttLib/tables/_l_o_c_a.py
+++ b/Lib/fontTools/ttLib/tables/_l_o_c_a.py
@@ -3,12 +3,16 @@
 from . import DefaultTable
 import sys
 import array
-import warnings
+import logging
+
+
+log = logging.getLogger(__name__)
+
 
 class table__l_o_c_a(DefaultTable.DefaultTable):
-	
+
 	dependencies = ['glyf']
-	
+
 	def decompile(self, data, ttFont):
 		longFormat = ttFont['head'].indexToLocFormat
 		if longFormat:
@@ -25,16 +29,17 @@
 				l.append(locations[i] * 2)
 			locations = l
 		if len(locations) < (ttFont['maxp'].numGlyphs + 1):
-			warnings.warn("corrupt 'loca' table, or wrong numGlyphs in 'maxp': %d %d" % (len(locations) - 1, ttFont['maxp'].numGlyphs))
+			log.warning("corrupt 'loca' table, or wrong numGlyphs in 'maxp': %d %d",
+				len(locations) - 1, ttFont['maxp'].numGlyphs)
 		self.locations = locations
-	
+
 	def compile(self, ttFont):
 		try:
 			max_location = max(self.locations)
 		except AttributeError:
 			self.set([])
 			max_location = 0
-		if max_location < 0x20000:
+		if max_location < 0x20000 and all(l % 2 == 0 for l in self.locations):
 			locations = array.array("H")
 			for i in range(len(self.locations)):
 				locations.append(self.locations[i] // 2)
@@ -45,17 +50,16 @@
 		if sys.byteorder != "big":
 			locations.byteswap()
 		return locations.tostring()
-	
+
 	def set(self, locations):
 		self.locations = array.array("I", locations)
-	
+
 	def toXML(self, writer, ttFont):
 		writer.comment("The 'loca' table will be calculated by the compiler")
 		writer.newline()
-	
+
 	def __getitem__(self, index):
 		return self.locations[index]
-	
+
 	def __len__(self):
 		return len(self.locations)
-
diff --git a/Lib/fontTools/ttLib/tables/_l_t_a_g.py b/Lib/fontTools/ttLib/tables/_l_t_a_g.py
new file mode 100644
index 0000000..59f8e72
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_l_t_a_g.py
@@ -0,0 +1,65 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.textTools import safeEval
+from . import DefaultTable
+import struct
+
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6ltag.html
+
+class table__l_t_a_g(DefaultTable.DefaultTable):
+	def __init__(self, tag=None):
+		DefaultTable.DefaultTable.__init__(self, tag)
+		self.version, self.flags = 1, 0
+		self.tags = []
+
+	def addTag(self, tag):
+		"""Add 'tag' to the list of langauge tags if not already there.
+
+		Returns the integer index of 'tag' in the list of all tags.
+		"""
+		try:
+			return self.tags.index(tag)
+		except ValueError:
+			self.tags.append(tag)
+			return len(self.tags) - 1
+
+	def decompile(self, data, ttFont):
+		self.version, self.flags, numTags = struct.unpack(">LLL", data[:12])
+		assert self.version == 1
+		self.tags = []
+		for i in range(numTags):
+			pos = 12 + i * 4
+			offset, length = struct.unpack(">HH", data[pos:pos+4])
+			tag = data[offset:offset+length].decode("ascii")
+			self.tags.append(tag)
+
+	def compile(self, ttFont):
+		dataList = [struct.pack(">LLL", self.version, self.flags, len(self.tags))]
+		stringPool = ""
+		for tag in self.tags:
+			offset = stringPool.find(tag)
+			if offset < 0:
+				offset = len(stringPool)
+				stringPool = stringPool + tag
+			offset = offset + 12 + len(self.tags) * 4
+			dataList.append(struct.pack(">HH", offset, len(tag)))
+		dataList.append(tobytes(stringPool))
+		return bytesjoin(dataList)
+
+	def toXML(self, writer, ttFont):
+		writer.simpletag("version", value=self.version)
+		writer.newline()
+		writer.simpletag("flags", value=self.flags)
+		writer.newline()
+		for tag in self.tags:
+			writer.simpletag("LanguageTag", tag=tag)
+			writer.newline()
+
+	def fromXML(self, name, attrs, content, ttFont):
+		if not hasattr(self, "tags"):
+			self.tags = []
+		if name == "LanguageTag":
+			self.tags.append(attrs["tag"])
+		elif "value" in attrs:
+			value =  safeEval(attrs["value"])
+			setattr(self, name, value)
diff --git a/Lib/fontTools/ttLib/tables/_m_a_x_p.py b/Lib/fontTools/ttLib/tables/_m_a_x_p.py
index 1089d64..a94a9cf 100644
--- a/Lib/fontTools/ttLib/tables/_m_a_x_p.py
+++ b/Lib/fontTools/ttLib/tables/_m_a_x_p.py
@@ -29,16 +29,16 @@
 
 
 class table__m_a_x_p(DefaultTable.DefaultTable):
-	
+
 	dependencies = ['glyf']
-	
+
 	def decompile(self, data, ttFont):
 		dummy, data = sstruct.unpack2(maxpFormat_0_5, data, self)
 		self.numGlyphs = int(self.numGlyphs)
 		if self.tableVersion != 0x00005000:
 			dummy, data = sstruct.unpack2(maxpFormat_1_0_add, data, self)
 		assert len(data) == 0
-	
+
 	def compile(self, ttFont):
 		if 'glyf' in ttFont:
 			if ttFont.isLoaded('glyf') and ttFont.recalcBBoxes:
@@ -52,7 +52,7 @@
 		if self.tableVersion == 0x00010000:
 			data = data + sstruct.pack(maxpFormat_1_0_add, self)
 		return data
-	
+
 	def recalc(self, ttFont):
 		"""Recalculate the font bounding box, and most other maxp values except
 		for the TT instructions values. Also recalculate the value of bit 1
@@ -73,12 +73,12 @@
 		maxCompositeContours = 0
 		maxComponentElements = 0
 		maxComponentDepth = 0
-		allXMaxIsLsb = 1
+		allXMinIsLsb = 1
 		for glyphName in ttFont.getGlyphOrder():
 			g = glyfTable[glyphName]
 			if g.numberOfContours:
 				if hmtxTable[glyphName][1] != g.xMin:
-					allXMaxIsLsb = 0
+					allXMinIsLsb = 0
 				xMin = min(xMin, g.xMin)
 				yMin = min(yMin, g.yMin)
 				xMax = max(xMax, g.xMax)
@@ -99,27 +99,27 @@
 			headTable.xMax = 0
 			headTable.yMax = 0
 		else:
-		    headTable.xMin = xMin
-		    headTable.yMin = yMin
-		    headTable.xMax = xMax
-		    headTable.yMax = yMax
+			headTable.xMin = xMin
+			headTable.yMin = yMin
+			headTable.xMax = xMax
+			headTable.yMax = yMax
 		self.maxPoints = maxPoints
 		self.maxContours = maxContours
 		self.maxCompositePoints = maxCompositePoints
 		self.maxCompositeContours = maxCompositeContours
 		self.maxComponentDepth = maxComponentDepth
-		if allXMaxIsLsb:
+		if allXMinIsLsb:
 			headTable.flags = headTable.flags | 0x2
 		else:
 			headTable.flags = headTable.flags & ~0x2
-	
+
 	def testrepr(self):
 		items = sorted(self.__dict__.items())
 		print(". . . . . . . . .")
 		for combo in items:
 			print("  %s: %s" % combo)
 		print(". . . . . . . . .")
-	
+
 	def toXML(self, writer, ttFont):
 		if self.tableVersion != 0x00005000:
 			writer.comment("Most of this table will be recalculated by the compiler")
@@ -134,8 +134,6 @@
 				value = hex(value)
 			writer.simpletag(name, value=value)
 			writer.newline()
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		setattr(self, name, safeEval(attrs["value"]))
-		
-
diff --git a/Lib/fontTools/ttLib/tables/_m_e_t_a.py b/Lib/fontTools/ttLib/tables/_m_e_t_a.py
new file mode 100644
index 0000000..cc19fe6
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_m_e_t_a.py
@@ -0,0 +1,99 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import readHex
+from fontTools.ttLib import TTLibError
+from . import DefaultTable
+
+# Apple's documentation of 'meta':
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6meta.html
+
+META_HEADER_FORMAT = """
+    > # big endian
+    version:     L
+    flags:       L
+    dataOffset:  L
+    numDataMaps: L
+"""
+
+
+DATA_MAP_FORMAT = """
+    > # big endian
+    tag:         4s
+    dataOffset:  L
+    dataLength:  L
+"""
+
+
+class table__m_e_t_a(DefaultTable.DefaultTable):
+    def __init__(self, tag=None):
+        DefaultTable.DefaultTable.__init__(self, tag)
+        self.data = {}
+
+    def decompile(self, data, ttFont):
+        headerSize = sstruct.calcsize(META_HEADER_FORMAT)
+        header = sstruct.unpack(META_HEADER_FORMAT, data[0 : headerSize])
+        if header["version"] != 1:
+            raise TTLibError("unsupported 'meta' version %d" %
+                             header["version"])
+        dataMapSize = sstruct.calcsize(DATA_MAP_FORMAT)
+        for i in range(header["numDataMaps"]):
+            dataMapOffset = headerSize + i * dataMapSize
+            dataMap = sstruct.unpack(
+                DATA_MAP_FORMAT,
+                data[dataMapOffset : dataMapOffset + dataMapSize])
+            tag = dataMap["tag"]
+            offset = dataMap["dataOffset"]
+            self.data[tag] = data[offset : offset + dataMap["dataLength"]]
+            if tag in ["dlng", "slng"]:
+                self.data[tag] = self.data[tag].decode("utf-8")
+
+    def compile(self, ttFont):
+        keys = sorted(self.data.keys())
+        headerSize = sstruct.calcsize(META_HEADER_FORMAT)
+        dataOffset = headerSize + len(keys) * sstruct.calcsize(DATA_MAP_FORMAT)
+        header = sstruct.pack(META_HEADER_FORMAT, {
+                "version": 1,
+                "flags": 0,
+                "dataOffset": dataOffset,
+                "numDataMaps": len(keys)
+        })
+        dataMaps = []
+        dataBlocks = []
+        for tag in keys:
+            if tag in ["dlng", "slng"]:
+                data = self.data[tag].encode("utf-8")
+            else:
+                data = self.data[tag]
+            dataMaps.append(sstruct.pack(DATA_MAP_FORMAT, {
+                "tag": tag,
+                "dataOffset": dataOffset,
+                "dataLength": len(data)
+            }))
+            dataBlocks.append(data)
+            dataOffset += len(data)
+        return bytesjoin([header] + dataMaps + dataBlocks)
+
+    def toXML(self, writer, ttFont):
+        for tag in sorted(self.data.keys()):
+            if tag in ["dlng", "slng"]:
+                writer.begintag("text", tag=tag)
+                writer.newline()
+                writer.write(self.data[tag])
+                writer.newline()
+                writer.endtag("text")
+                writer.newline()
+            else:
+                writer.begintag("hexdata", tag=tag)
+                writer.newline()
+                writer.dumphex(self.data[tag])
+                writer.endtag("hexdata")
+                writer.newline()
+
+    def fromXML(self, name, attrs, content, ttFont):
+        if name == "hexdata":
+            self.data[attrs["tag"]] = readHex(content)
+        elif name == "text" and attrs["tag"] in ["dlng", "slng"]:
+            self.data[attrs["tag"]] = strjoin(content).strip()
+        else:
+            raise TTLibError("can't handle '%s' element" % name)
diff --git a/Lib/fontTools/ttLib/tables/_m_o_r_t.py b/Lib/fontTools/ttLib/tables/_m_o_r_t.py
new file mode 100644
index 0000000..b87b425
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_m_o_r_t.py
@@ -0,0 +1,8 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from .otBase import BaseTTXConverter
+
+
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6mort.html
+class table__m_o_r_t(BaseTTXConverter):
+    pass
diff --git a/Lib/fontTools/ttLib/tables/_m_o_r_x.py b/Lib/fontTools/ttLib/tables/_m_o_r_x.py
new file mode 100644
index 0000000..1619d8d
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_m_o_r_x.py
@@ -0,0 +1,8 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from .otBase import BaseTTXConverter
+
+
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html
+class table__m_o_r_x(BaseTTXConverter):
+    pass
diff --git a/Lib/fontTools/ttLib/tables/_n_a_m_e.py b/Lib/fontTools/ttLib/tables/_n_a_m_e.py
index 53fde4d..a30291c 100644
--- a/Lib/fontTools/ttLib/tables/_n_a_m_e.py
+++ b/Lib/fontTools/ttLib/tables/_n_a_m_e.py
@@ -1,9 +1,17 @@
+# -*- coding: utf-8 -*-
 from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
 from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
 from fontTools.misc.textTools import safeEval
+from fontTools.misc.encodingTools import getEncoding
+from fontTools.ttLib import newTable
 from . import DefaultTable
 import struct
+import logging
+
+
+log = logging.getLogger(__name__)
 
 nameRecordFormat = """
 		>	# big endian
@@ -19,22 +27,27 @@
 
 
 class table__n_a_m_e(DefaultTable.DefaultTable):
-	
+	dependencies = ["ltag"]
+
 	def decompile(self, data, ttFont):
-		format, n, stringOffset = struct.unpack(">HHH", data[:6])
+		format, n, stringOffset = struct.unpack(b">HHH", data[:6])
 		expectedStringOffset = 6 + n * nameRecordSize
 		if stringOffset != expectedStringOffset:
-			# XXX we need a warn function
-			print("Warning: 'name' table stringOffset incorrect. Expected: %s; Actual: %s" % (expectedStringOffset, stringOffset))
+			log.error(
+				"'name' table stringOffset incorrect. Expected: %s; Actual: %s",
+				expectedStringOffset, stringOffset)
 		stringData = data[stringOffset:]
 		data = data[6:]
 		self.names = []
 		for i in range(n):
 			if len(data) < 12:
-				# compensate for buggy font
-				break
+				log.error('skipping malformed name record #%d', i)
+				continue
 			name, data = sstruct.unpack2(nameRecordFormat, data, NameRecord())
 			name.string = stringData[name.offset:name.offset+name.length]
+			if name.offset + name.length > len(stringData):
+				log.error('skipping malformed name record #%d', i)
+				continue
 			assert len(name.string) == name.length
 			#if (name.platEncID, name.platformID) in ((0, 0), (1, 3)):
 			#	if len(name.string) % 2:
@@ -42,33 +55,35 @@
 			#		print name.__dict__
 			del name.offset, name.length
 			self.names.append(name)
-	
+
 	def compile(self, ttFont):
 		if not hasattr(self, "names"):
 			# only happens when there are NO name table entries read
 			# from the TTX file
 			self.names = []
-		self.names.sort()  # sort according to the spec; see NameRecord.__lt__()
+		names = self.names
+		names.sort() # sort according to the spec; see NameRecord.__lt__()
 		stringData = b""
 		format = 0
-		n = len(self.names)
+		n = len(names)
 		stringOffset = 6 + n * sstruct.calcsize(nameRecordFormat)
-		data = struct.pack(">HHH", format, n, stringOffset)
+		data = struct.pack(b">HHH", format, n, stringOffset)
 		lastoffset = 0
 		done = {}  # remember the data so we can reuse the "pointers"
-		for name in self.names:
-			if name.string in done:
-				name.offset, name.length = done[name.string]
+		for name in names:
+			string = name.toBytes()
+			if string in done:
+				name.offset, name.length = done[string]
 			else:
-				name.offset, name.length = done[name.string] = len(stringData), len(name.string)
-				stringData = stringData + name.string
+				name.offset, name.length = done[string] = len(stringData), len(string)
+				stringData = bytesjoin([stringData, string])
 			data = data + sstruct.pack(nameRecordFormat, name)
 		return data + stringData
-	
+
 	def toXML(self, writer, ttFont):
 		for name in self.names:
 			name.toXML(writer, ttFont)
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		if name != "namerecord":
 			return # ignore unknown tags
@@ -77,56 +92,347 @@
 		name = NameRecord()
 		self.names.append(name)
 		name.fromXML(name, attrs, content, ttFont)
-	
+
 	def getName(self, nameID, platformID, platEncID, langID=None):
 		for namerecord in self.names:
-			if (	namerecord.nameID == nameID and 
-					namerecord.platformID == platformID and 
+			if (	namerecord.nameID == nameID and
+					namerecord.platformID == platformID and
 					namerecord.platEncID == platEncID):
 				if langID is None or namerecord.langID == langID:
 					return namerecord
 		return None # not found
 
+	def getDebugName(self, nameID):
+		englishName = someName = None
+		for name in self.names:
+			if name.nameID != nameID:
+				continue
+			try:
+				unistr = name.toUnicode()
+			except UnicodeDecodeError:
+				continue
+
+			someName = unistr
+			if (name.platformID, name.langID) in ((1, 0), (3, 0x409)):
+				englishName = unistr
+				break
+		if englishName:
+			return englishName
+		elif someName:
+			return someName
+		else:
+			return None
+
+	def setName(self, string, nameID, platformID, platEncID, langID):
+		""" Set the 'string' for the name record identified by 'nameID', 'platformID',
+		'platEncID' and 'langID'. If a record with that nameID doesn't exist, create it
+		and append to the name table.
+
+		'string' can be of type `str` (`unicode` in PY2) or `bytes`. In the latter case,
+		it is assumed to be already encoded with the correct plaform-specific encoding
+		identified by the (platformID, platEncID, langID) triplet. A warning is issued
+		to prevent unexpected results.
+		"""
+		if not hasattr(self, 'names'):
+			self.names = []
+		if not isinstance(string, unicode):
+			if isinstance(string, bytes):
+				log.warning(
+					"name string is bytes, ensure it's correctly encoded: %r", string)
+			else:
+				raise TypeError(
+					"expected unicode or bytes, found %s: %r" % (
+						type(string).__name__, string))
+		namerecord = self.getName(nameID, platformID, platEncID, langID)
+		if namerecord:
+			namerecord.string = string
+		else:
+			self.names.append(makeName(string, nameID, platformID, platEncID, langID))
+
+	def _findUnusedNameID(self, minNameID=256):
+		"""Finds an unused name id.
+
+		The nameID is assigned in the range between 'minNameID' and 32767 (inclusive),
+		following the last nameID in the name table.
+		"""
+		names = getattr(self, 'names', [])
+		nameID = 1 + max([n.nameID for n in names] + [minNameID - 1])
+		if nameID > 32767:
+			raise ValueError("nameID must be less than 32768")
+		return nameID
+
+	def addMultilingualName(self, names, ttFont=None, nameID=None):
+		"""Add a multilingual name, returning its name ID
+
+		'names' is a dictionary with the name in multiple languages,
+		such as {'en': 'Pale', 'de': 'Blaß', 'de-CH': 'Blass'}.
+		The keys can be arbitrary IETF BCP 47 language codes;
+		the values are Unicode strings.
+
+		'ttFont' is the TTFont to which the names are added, or None.
+		If present, the font's 'ltag' table can get populated
+		to store exotic language codes, which allows encoding
+		names that otherwise cannot get encoded at all.
+
+		'nameID' is the name ID to be used, or None to let the library
+		pick an unused name ID.
+		"""
+		if not hasattr(self, 'names'):
+			self.names = []
+		if nameID is None:
+			nameID = self._findUnusedNameID()
+		# TODO: Should minimize BCP 47 language codes.
+		# https://github.com/fonttools/fonttools/issues/930
+		for lang, name in sorted(names.items()):
+			# Apple platforms have been recognizing Windows names
+			# since early OSX (~2001), so we only add names
+			# for the Macintosh platform when we cannot not make
+			# a Windows name. This can happen for exotic BCP47
+			# language tags that have no Windows language code.
+			windowsName = _makeWindowsName(name, nameID, lang)
+			if windowsName is not None:
+				self.names.append(windowsName)
+			else:
+				macName = _makeMacName(name, nameID, lang, ttFont)
+				if macName is not None:
+					self.names.append(macName)
+		return nameID
+
+	def addName(self, string, platforms=((1, 0, 0), (3, 1, 0x409)), minNameID=255):
+		""" Add a new name record containing 'string' for each (platformID, platEncID,
+		langID) tuple specified in the 'platforms' list.
+
+		The nameID is assigned in the range between 'minNameID'+1 and 32767 (inclusive),
+		following the last nameID in the name table.
+		If no 'platforms' are specified, two English name records are added, one for the
+		Macintosh (platformID=0), and one for the Windows platform (3).
+
+		The 'string' must be a Unicode string, so it can be encoded with different,
+		platform-specific encodings.
+
+		Return the new nameID.
+		"""
+		assert len(platforms) > 0, \
+			"'platforms' must contain at least one (platformID, platEncID, langID) tuple"
+		if not hasattr(self, 'names'):
+			self.names = []
+		if not isinstance(string, unicode):
+			raise TypeError(
+				"expected %s, found %s: %r" % (
+					unicode.__name__, type(string).__name__,string ))
+		nameID = self._findUnusedNameID(minNameID + 1)
+		for platformID, platEncID, langID in platforms:
+			self.names.append(makeName(string, nameID, platformID, platEncID, langID))
+		return nameID
+
+
+def makeName(string, nameID, platformID, platEncID, langID):
+	name = NameRecord()
+	name.string, name.nameID, name.platformID, name.platEncID, name.langID = (
+		string, nameID, platformID, platEncID, langID)
+	return name
+
+
+def _makeWindowsName(name, nameID, language):
+	"""Create a NameRecord for the Microsoft Windows platform
+
+	'language' is an arbitrary IETF BCP 47 language identifier such
+	as 'en', 'de-CH', 'de-AT-1901', or 'fa-Latn'. If Microsoft Windows
+	does not support the desired language, the result will be None.
+	Future versions of fonttools might return a NameRecord for the
+	OpenType 'name' table format 1, but this is not implemented yet.
+	"""
+	langID = _WINDOWS_LANGUAGE_CODES.get(language.lower())
+	if langID is not None:
+		return makeName(name, nameID, 3, 1, langID)
+	else:
+		log.warning("cannot add Windows name in language %s "
+		            "because fonttools does not yet support "
+		            "name table format 1" % language)
+		return None
+
+
+def _makeMacName(name, nameID, language, font=None):
+	"""Create a NameRecord for Apple platforms
+
+	'language' is an arbitrary IETF BCP 47 language identifier such
+	as 'en', 'de-CH', 'de-AT-1901', or 'fa-Latn'. When possible, we
+	create a Macintosh NameRecord that is understood by old applications
+	(platform ID 1 and an old-style Macintosh language enum). If this
+	is not possible, we create a Unicode NameRecord (platform ID 0)
+	whose language points to the font’s 'ltag' table. The latter
+	can encode any string in any language, but legacy applications
+	might not recognize the format (in which case they will ignore
+	those names).
+
+	'font' should be the TTFont for which you want to create a name.
+	If 'font' is None, we only return NameRecords for legacy Macintosh;
+	in that case, the result will be None for names that need to
+	be encoded with an 'ltag' table.
+
+	See the section “The language identifier” in Apple’s specification:
+	https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6name.html
+	"""
+	macLang = _MAC_LANGUAGE_CODES.get(language.lower())
+	macScript = _MAC_LANGUAGE_TO_SCRIPT.get(macLang)
+	if macLang is not None and macScript is not None:
+		encoding = getEncoding(1, macScript, macLang, default="ascii")
+		# Check if we can actually encode this name. If we can't,
+		# for example because we have no support for the legacy
+		# encoding, or because the name string contains Unicode
+		# characters that the legacy encoding cannot represent,
+		# we fall back to encoding the name in Unicode and put
+		# the language tag into the ltag table.
+		try:
+			_ = tobytes(name, encoding, errors="strict")
+			return makeName(name, nameID, 1, macScript, macLang)
+		except UnicodeEncodeError:
+			pass
+	if font is not None:
+		ltag = font.tables.get("ltag")
+		if ltag is None:
+			ltag = font["ltag"] = newTable("ltag")
+		# 0 = Unicode; 4 = “Unicode 2.0 or later semantics (non-BMP characters allowed)”
+		# “The preferred platform-specific code for Unicode would be 3 or 4.”
+		# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6name.html
+		return makeName(name, nameID, 0, 4, ltag.addTag(language))
+	else:
+		log.warning("cannot store language %s into 'ltag' table "
+		            "without having access to the TTFont object" %
+		            language)
+		return None
+
 
 class NameRecord(object):
-	
+
+	def getEncoding(self, default='ascii'):
+		"""Returns the Python encoding name for this name entry based on its platformID,
+		platEncID, and langID.  If encoding for these values is not known, by default
+		'ascii' is returned.  That can be overriden by passing a value to the default
+		argument.
+		"""
+		return getEncoding(self.platformID, self.platEncID, self.langID, default)
+
+	def encodingIsUnicodeCompatible(self):
+		return self.getEncoding(None) in ['utf_16_be', 'ucs2be', 'ascii', 'latin1']
+
+	def __str__(self):
+		return self.toStr(errors='backslashreplace')
+
 	def isUnicode(self):
 		return (self.platformID == 0 or
 			(self.platformID == 3 and self.platEncID in [0, 1, 10]))
 
+	def toUnicode(self, errors='strict'):
+		"""
+		If self.string is a Unicode string, return it; otherwise try decoding the
+		bytes in self.string to a Unicode string using the encoding of this
+		entry as returned by self.getEncoding(); Note that  self.getEncoding()
+		returns 'ascii' if the encoding is unknown to the library.
+
+		Certain heuristics are performed to recover data from bytes that are
+		ill-formed in the chosen encoding, or that otherwise look misencoded
+		(mostly around bad UTF-16BE encoded bytes, or bytes that look like UTF-16BE
+		but marked otherwise).  If the bytes are ill-formed and the heuristics fail,
+		the error is handled according to the errors parameter to this function, which is
+		passed to the underlying decode() function; by default it throws a
+		UnicodeDecodeError exception.
+
+		Note: The mentioned heuristics mean that roundtripping a font to XML and back
+		to binary might recover some misencoded data whereas just loading the font
+		and saving it back will not change them.
+		"""
+		def isascii(b):
+			return (b >= 0x20 and b <= 0x7E) or b in [0x09, 0x0A, 0x0D]
+		encoding = self.getEncoding()
+		string = self.string
+
+		if encoding == 'utf_16_be' and len(string) % 2 == 1:
+			# Recover badly encoded UTF-16 strings that have an odd number of bytes:
+			# - If the last byte is zero, drop it.  Otherwise,
+			# - If all the odd bytes are zero and all the even bytes are ASCII,
+			#   prepend one zero byte.  Otherwise,
+			# - If first byte is zero and all other bytes are ASCII, insert zero
+			#   bytes between consecutive ASCII bytes.
+			#
+			# (Yes, I've seen all of these in the wild... sigh)
+			if byteord(string[-1]) == 0:
+				string = string[:-1]
+			elif all(byteord(b) == 0 if i % 2 else isascii(byteord(b)) for i,b in enumerate(string)):
+				string = b'\0' + string
+			elif byteord(string[0]) == 0 and all(isascii(byteord(b)) for b in string[1:]):
+				string = bytesjoin(b'\0'+bytechr(byteord(b)) for b in string[1:])
+
+		string = tounicode(string, encoding=encoding, errors=errors)
+
+		# If decoded strings still looks like UTF-16BE, it suggests a double-encoding.
+		# Fix it up.
+		if all(ord(c) == 0 if i % 2 == 0 else isascii(ord(c)) for i,c in enumerate(string)):
+			# If string claims to be Mac encoding, but looks like UTF-16BE with ASCII text,
+			# narrow it down.
+			string = ''.join(c for c in string[1::2])
+
+		return string
+
+	def toBytes(self, errors='strict'):
+		""" If self.string is a bytes object, return it; otherwise try encoding
+		the Unicode string in self.string to bytes using the encoding of this
+		entry as returned by self.getEncoding(); Note that self.getEncoding()
+		returns 'ascii' if the encoding is unknown to the library.
+
+		If the Unicode string cannot be encoded to bytes in the chosen encoding,
+		the error is handled according to the errors parameter to this function,
+		which is passed to the underlying encode() function; by default it throws a
+		UnicodeEncodeError exception.
+		"""
+		return tobytes(self.string, encoding=self.getEncoding(), errors=errors)
+
+	def toStr(self, errors='strict'):
+		if str == bytes:
+			# python 2
+			return self.toBytes(errors)
+		else:
+			# python 3
+			return self.toUnicode(errors)
+
 	def toXML(self, writer, ttFont):
-		writer.begintag("namerecord", [
+		try:
+			unistr = self.toUnicode()
+		except UnicodeDecodeError:
+			unistr = None
+		attrs = [
 				("nameID", self.nameID),
 				("platformID", self.platformID),
 				("platEncID", self.platEncID),
 				("langID", hex(self.langID)),
-						])
+			]
+
+		if unistr is None or not self.encodingIsUnicodeCompatible():
+			attrs.append(("unicode", unistr is not None))
+
+		writer.begintag("namerecord", attrs)
 		writer.newline()
-		if self.isUnicode():
-			if len(self.string) % 2:
-				# no, shouldn't happen, but some of the Apple
-				# tools cause this anyway :-(
-				writer.write16bit(self.string + b"\0", strip=True)
-			else:
-				writer.write16bit(self.string, strip=True)
+		if unistr is not None:
+			writer.write(unistr)
 		else:
-			writer.write8bit(self.string, strip=True)
+			writer.write8bit(self.string)
 		writer.newline()
 		writer.endtag("namerecord")
 		writer.newline()
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		self.nameID = safeEval(attrs["nameID"])
 		self.platformID = safeEval(attrs["platformID"])
 		self.platEncID = safeEval(attrs["platEncID"])
 		self.langID =  safeEval(attrs["langID"])
 		s = strjoin(content).strip()
-		if self.isUnicode():
-			self.string = s.encode("utf_16_be")
+		encoding = self.getEncoding()
+		if self.encodingIsUnicodeCompatible() or safeEval(attrs.get("unicode", "False")):
+			self.string = s.encode(encoding)
 		else:
 			# This is the inverse of write8bit...
 			self.string = s.encode("latin1")
-	
+
 	def __lt__(self, other):
 		if type(self) != type(other):
 			return NotImplemented
@@ -147,7 +453,497 @@
 			getattr(other, "string", None),
 		)
 		return selfTuple < otherTuple
-	
+
 	def __repr__(self):
 		return "<NameRecord NameID=%d; PlatformID=%d; LanguageID=%d>" % (
 				self.nameID, self.platformID, self.langID)
+
+
+# Windows language ID → IETF BCP-47 language tag
+#
+# While Microsoft indicates a region/country for all its language
+# IDs, we follow Unicode practice by omitting “most likely subtags”
+# as per Unicode CLDR. For example, English is simply “en” and not
+# “en-Latn” because according to Unicode, the default script
+# for English is Latin.
+#
+# http://www.unicode.org/cldr/charts/latest/supplemental/likely_subtags.html
+# http://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
+_WINDOWS_LANGUAGES = {
+    0x0436: 'af',
+    0x041C: 'sq',
+    0x0484: 'gsw',
+    0x045E: 'am',
+    0x1401: 'ar-DZ',
+    0x3C01: 'ar-BH',
+    0x0C01: 'ar',
+    0x0801: 'ar-IQ',
+    0x2C01: 'ar-JO',
+    0x3401: 'ar-KW',
+    0x3001: 'ar-LB',
+    0x1001: 'ar-LY',
+    0x1801: 'ary',
+    0x2001: 'ar-OM',
+    0x4001: 'ar-QA',
+    0x0401: 'ar-SA',
+    0x2801: 'ar-SY',
+    0x1C01: 'aeb',
+    0x3801: 'ar-AE',
+    0x2401: 'ar-YE',
+    0x042B: 'hy',
+    0x044D: 'as',
+    0x082C: 'az-Cyrl',
+    0x042C: 'az',
+    0x046D: 'ba',
+    0x042D: 'eu',
+    0x0423: 'be',
+    0x0845: 'bn',
+    0x0445: 'bn-IN',
+    0x201A: 'bs-Cyrl',
+    0x141A: 'bs',
+    0x047E: 'br',
+    0x0402: 'bg',
+    0x0403: 'ca',
+    0x0C04: 'zh-HK',
+    0x1404: 'zh-MO',
+    0x0804: 'zh',
+    0x1004: 'zh-SG',
+    0x0404: 'zh-TW',
+    0x0483: 'co',
+    0x041A: 'hr',
+    0x101A: 'hr-BA',
+    0x0405: 'cs',
+    0x0406: 'da',
+    0x048C: 'prs',
+    0x0465: 'dv',
+    0x0813: 'nl-BE',
+    0x0413: 'nl',
+    0x0C09: 'en-AU',
+    0x2809: 'en-BZ',
+    0x1009: 'en-CA',
+    0x2409: 'en-029',
+    0x4009: 'en-IN',
+    0x1809: 'en-IE',
+    0x2009: 'en-JM',
+    0x4409: 'en-MY',
+    0x1409: 'en-NZ',
+    0x3409: 'en-PH',
+    0x4809: 'en-SG',
+    0x1C09: 'en-ZA',
+    0x2C09: 'en-TT',
+    0x0809: 'en-GB',
+    0x0409: 'en',
+    0x3009: 'en-ZW',
+    0x0425: 'et',
+    0x0438: 'fo',
+    0x0464: 'fil',
+    0x040B: 'fi',
+    0x080C: 'fr-BE',
+    0x0C0C: 'fr-CA',
+    0x040C: 'fr',
+    0x140C: 'fr-LU',
+    0x180C: 'fr-MC',
+    0x100C: 'fr-CH',
+    0x0462: 'fy',
+    0x0456: 'gl',
+    0x0437: 'ka',
+    0x0C07: 'de-AT',
+    0x0407: 'de',
+    0x1407: 'de-LI',
+    0x1007: 'de-LU',
+    0x0807: 'de-CH',
+    0x0408: 'el',
+    0x046F: 'kl',
+    0x0447: 'gu',
+    0x0468: 'ha',
+    0x040D: 'he',
+    0x0439: 'hi',
+    0x040E: 'hu',
+    0x040F: 'is',
+    0x0470: 'ig',
+    0x0421: 'id',
+    0x045D: 'iu',
+    0x085D: 'iu-Latn',
+    0x083C: 'ga',
+    0x0434: 'xh',
+    0x0435: 'zu',
+    0x0410: 'it',
+    0x0810: 'it-CH',
+    0x0411: 'ja',
+    0x044B: 'kn',
+    0x043F: 'kk',
+    0x0453: 'km',
+    0x0486: 'quc',
+    0x0487: 'rw',
+    0x0441: 'sw',
+    0x0457: 'kok',
+    0x0412: 'ko',
+    0x0440: 'ky',
+    0x0454: 'lo',
+    0x0426: 'lv',
+    0x0427: 'lt',
+    0x082E: 'dsb',
+    0x046E: 'lb',
+    0x042F: 'mk',
+    0x083E: 'ms-BN',
+    0x043E: 'ms',
+    0x044C: 'ml',
+    0x043A: 'mt',
+    0x0481: 'mi',
+    0x047A: 'arn',
+    0x044E: 'mr',
+    0x047C: 'moh',
+    0x0450: 'mn',
+    0x0850: 'mn-CN',
+    0x0461: 'ne',
+    0x0414: 'nb',
+    0x0814: 'nn',
+    0x0482: 'oc',
+    0x0448: 'or',
+    0x0463: 'ps',
+    0x0415: 'pl',
+    0x0416: 'pt',
+    0x0816: 'pt-PT',
+    0x0446: 'pa',
+    0x046B: 'qu-BO',
+    0x086B: 'qu-EC',
+    0x0C6B: 'qu',
+    0x0418: 'ro',
+    0x0417: 'rm',
+    0x0419: 'ru',
+    0x243B: 'smn',
+    0x103B: 'smj-NO',
+    0x143B: 'smj',
+    0x0C3B: 'se-FI',
+    0x043B: 'se',
+    0x083B: 'se-SE',
+    0x203B: 'sms',
+    0x183B: 'sma-NO',
+    0x1C3B: 'sms',
+    0x044F: 'sa',
+    0x1C1A: 'sr-Cyrl-BA',
+    0x0C1A: 'sr',
+    0x181A: 'sr-Latn-BA',
+    0x081A: 'sr-Latn',
+    0x046C: 'nso',
+    0x0432: 'tn',
+    0x045B: 'si',
+    0x041B: 'sk',
+    0x0424: 'sl',
+    0x2C0A: 'es-AR',
+    0x400A: 'es-BO',
+    0x340A: 'es-CL',
+    0x240A: 'es-CO',
+    0x140A: 'es-CR',
+    0x1C0A: 'es-DO',
+    0x300A: 'es-EC',
+    0x440A: 'es-SV',
+    0x100A: 'es-GT',
+    0x480A: 'es-HN',
+    0x080A: 'es-MX',
+    0x4C0A: 'es-NI',
+    0x180A: 'es-PA',
+    0x3C0A: 'es-PY',
+    0x280A: 'es-PE',
+    0x500A: 'es-PR',
+
+    # Microsoft has defined two different language codes for
+    # “Spanish with modern sorting” and “Spanish with traditional
+    # sorting”. This makes sense for collation APIs, and it would be
+    # possible to express this in BCP 47 language tags via Unicode
+    # extensions (eg., “es-u-co-trad” is “Spanish with traditional
+    # sorting”). However, for storing names in fonts, this distinction
+    # does not make sense, so we use “es” in both cases.
+    0x0C0A: 'es',
+    0x040A: 'es',
+
+    0x540A: 'es-US',
+    0x380A: 'es-UY',
+    0x200A: 'es-VE',
+    0x081D: 'sv-FI',
+    0x041D: 'sv',
+    0x045A: 'syr',
+    0x0428: 'tg',
+    0x085F: 'tzm',
+    0x0449: 'ta',
+    0x0444: 'tt',
+    0x044A: 'te',
+    0x041E: 'th',
+    0x0451: 'bo',
+    0x041F: 'tr',
+    0x0442: 'tk',
+    0x0480: 'ug',
+    0x0422: 'uk',
+    0x042E: 'hsb',
+    0x0420: 'ur',
+    0x0843: 'uz-Cyrl',
+    0x0443: 'uz',
+    0x042A: 'vi',
+    0x0452: 'cy',
+    0x0488: 'wo',
+    0x0485: 'sah',
+    0x0478: 'ii',
+    0x046A: 'yo',
+}
+
+
+_MAC_LANGUAGES = {
+    0: 'en',
+    1: 'fr',
+    2: 'de',
+    3: 'it',
+    4: 'nl',
+    5: 'sv',
+    6: 'es',
+    7: 'da',
+    8: 'pt',
+    9: 'no',
+    10: 'he',
+    11: 'ja',
+    12: 'ar',
+    13: 'fi',
+    14: 'el',
+    15: 'is',
+    16: 'mt',
+    17: 'tr',
+    18: 'hr',
+    19: 'zh-Hant',
+    20: 'ur',
+    21: 'hi',
+    22: 'th',
+    23: 'ko',
+    24: 'lt',
+    25: 'pl',
+    26: 'hu',
+    27: 'es',
+    28: 'lv',
+    29: 'se',
+    30: 'fo',
+    31: 'fa',
+    32: 'ru',
+    33: 'zh',
+    34: 'nl-BE',
+    35: 'ga',
+    36: 'sq',
+    37: 'ro',
+    38: 'cz',
+    39: 'sk',
+    40: 'sl',
+    41: 'yi',
+    42: 'sr',
+    43: 'mk',
+    44: 'bg',
+    45: 'uk',
+    46: 'be',
+    47: 'uz',
+    48: 'kk',
+    49: 'az-Cyrl',
+    50: 'az-Arab',
+    51: 'hy',
+    52: 'ka',
+    53: 'mo',
+    54: 'ky',
+    55: 'tg',
+    56: 'tk',
+    57: 'mn-CN',
+    58: 'mn',
+    59: 'ps',
+    60: 'ks',
+    61: 'ku',
+    62: 'sd',
+    63: 'bo',
+    64: 'ne',
+    65: 'sa',
+    66: 'mr',
+    67: 'bn',
+    68: 'as',
+    69: 'gu',
+    70: 'pa',
+    71: 'or',
+    72: 'ml',
+    73: 'kn',
+    74: 'ta',
+    75: 'te',
+    76: 'si',
+    77: 'my',
+    78: 'km',
+    79: 'lo',
+    80: 'vi',
+    81: 'id',
+    82: 'tl',
+    83: 'ms',
+    84: 'ms-Arab',
+    85: 'am',
+    86: 'ti',
+    87: 'om',
+    88: 'so',
+    89: 'sw',
+    90: 'rw',
+    91: 'rn',
+    92: 'ny',
+    93: 'mg',
+    94: 'eo',
+    128: 'cy',
+    129: 'eu',
+    130: 'ca',
+    131: 'la',
+    132: 'qu',
+    133: 'gn',
+    134: 'ay',
+    135: 'tt',
+    136: 'ug',
+    137: 'dz',
+    138: 'jv',
+    139: 'su',
+    140: 'gl',
+    141: 'af',
+    142: 'br',
+    143: 'iu',
+    144: 'gd',
+    145: 'gv',
+    146: 'ga',
+    147: 'to',
+    148: 'el-polyton',
+    149: 'kl',
+    150: 'az',
+    151: 'nn',
+}
+
+
+_WINDOWS_LANGUAGE_CODES = {lang.lower(): code for code, lang in _WINDOWS_LANGUAGES.items()}
+_MAC_LANGUAGE_CODES = {lang.lower(): code for code, lang in _MAC_LANGUAGES.items()}
+
+
+# MacOS language ID → MacOS script ID
+#
+# Note that the script ID is not sufficient to determine what encoding
+# to use in TrueType files. For some languages, MacOS used a modification
+# of a mainstream script. For example, an Icelandic name would be stored
+# with smRoman in the TrueType naming table, but the actual encoding
+# is a special Icelandic version of the normal Macintosh Roman encoding.
+# As another example, Inuktitut uses an 8-bit encoding for Canadian Aboriginal
+# Syllables but MacOS had run out of available script codes, so this was
+# done as a (pretty radical) “modification” of Ethiopic.
+#
+# http://unicode.org/Public/MAPPINGS/VENDORS/APPLE/Readme.txt
+_MAC_LANGUAGE_TO_SCRIPT = {
+    0: 0,  # langEnglish → smRoman
+    1: 0,  # langFrench → smRoman
+    2: 0,  # langGerman → smRoman
+    3: 0,  # langItalian → smRoman
+    4: 0,  # langDutch → smRoman
+    5: 0,  # langSwedish → smRoman
+    6: 0,  # langSpanish → smRoman
+    7: 0,  # langDanish → smRoman
+    8: 0,  # langPortuguese → smRoman
+    9: 0,  # langNorwegian → smRoman
+    10: 5,  # langHebrew → smHebrew
+    11: 1,  # langJapanese → smJapanese
+    12: 4,  # langArabic → smArabic
+    13: 0,  # langFinnish → smRoman
+    14: 6,  # langGreek → smGreek
+    15: 0,  # langIcelandic → smRoman (modified)
+    16: 0,  # langMaltese → smRoman
+    17: 0,  # langTurkish → smRoman (modified)
+    18: 0,  # langCroatian → smRoman (modified)
+    19: 2,  # langTradChinese → smTradChinese
+    20: 4,  # langUrdu → smArabic
+    21: 9,  # langHindi → smDevanagari
+    22: 21,  # langThai → smThai
+    23: 3,  # langKorean → smKorean
+    24: 29,  # langLithuanian → smCentralEuroRoman
+    25: 29,  # langPolish → smCentralEuroRoman
+    26: 29,  # langHungarian → smCentralEuroRoman
+    27: 29,  # langEstonian → smCentralEuroRoman
+    28: 29,  # langLatvian → smCentralEuroRoman
+    29: 0,  # langSami → smRoman
+    30: 0,  # langFaroese → smRoman (modified)
+    31: 4,  # langFarsi → smArabic (modified)
+    32: 7,  # langRussian → smCyrillic
+    33: 25,  # langSimpChinese → smSimpChinese
+    34: 0,  # langFlemish → smRoman
+    35: 0,  # langIrishGaelic → smRoman (modified)
+    36: 0,  # langAlbanian → smRoman
+    37: 0,  # langRomanian → smRoman (modified)
+    38: 29,  # langCzech → smCentralEuroRoman
+    39: 29,  # langSlovak → smCentralEuroRoman
+    40: 0,  # langSlovenian → smRoman (modified)
+    41: 5,  # langYiddish → smHebrew
+    42: 7,  # langSerbian → smCyrillic
+    43: 7,  # langMacedonian → smCyrillic
+    44: 7,  # langBulgarian → smCyrillic
+    45: 7,  # langUkrainian → smCyrillic (modified)
+    46: 7,  # langByelorussian → smCyrillic
+    47: 7,  # langUzbek → smCyrillic
+    48: 7,  # langKazakh → smCyrillic
+    49: 7,  # langAzerbaijani → smCyrillic
+    50: 4,  # langAzerbaijanAr → smArabic
+    51: 24,  # langArmenian → smArmenian
+    52: 23,  # langGeorgian → smGeorgian
+    53: 7,  # langMoldavian → smCyrillic
+    54: 7,  # langKirghiz → smCyrillic
+    55: 7,  # langTajiki → smCyrillic
+    56: 7,  # langTurkmen → smCyrillic
+    57: 27,  # langMongolian → smMongolian
+    58: 7,  # langMongolianCyr → smCyrillic
+    59: 4,  # langPashto → smArabic
+    60: 4,  # langKurdish → smArabic
+    61: 4,  # langKashmiri → smArabic
+    62: 4,  # langSindhi → smArabic
+    63: 26,  # langTibetan → smTibetan
+    64: 9,  # langNepali → smDevanagari
+    65: 9,  # langSanskrit → smDevanagari
+    66: 9,  # langMarathi → smDevanagari
+    67: 13,  # langBengali → smBengali
+    68: 13,  # langAssamese → smBengali
+    69: 11,  # langGujarati → smGujarati
+    70: 10,  # langPunjabi → smGurmukhi
+    71: 12,  # langOriya → smOriya
+    72: 17,  # langMalayalam → smMalayalam
+    73: 16,  # langKannada → smKannada
+    74: 14,  # langTamil → smTamil
+    75: 15,  # langTelugu → smTelugu
+    76: 18,  # langSinhalese → smSinhalese
+    77: 19,  # langBurmese → smBurmese
+    78: 20,  # langKhmer → smKhmer
+    79: 22,  # langLao → smLao
+    80: 30,  # langVietnamese → smVietnamese
+    81: 0,  # langIndonesian → smRoman
+    82: 0,  # langTagalog → smRoman
+    83: 0,  # langMalayRoman → smRoman
+    84: 4,  # langMalayArabic → smArabic
+    85: 28,  # langAmharic → smEthiopic
+    86: 28,  # langTigrinya → smEthiopic
+    87: 28,  # langOromo → smEthiopic
+    88: 0,  # langSomali → smRoman
+    89: 0,  # langSwahili → smRoman
+    90: 0,  # langKinyarwanda → smRoman
+    91: 0,  # langRundi → smRoman
+    92: 0,  # langNyanja → smRoman
+    93: 0,  # langMalagasy → smRoman
+    94: 0,  # langEsperanto → smRoman
+    128: 0,  # langWelsh → smRoman (modified)
+    129: 0,  # langBasque → smRoman
+    130: 0,  # langCatalan → smRoman
+    131: 0,  # langLatin → smRoman
+    132: 0,  # langQuechua → smRoman
+    133: 0,  # langGuarani → smRoman
+    134: 0,  # langAymara → smRoman
+    135: 7,  # langTatar → smCyrillic
+    136: 4,  # langUighur → smArabic
+    137: 26,  # langDzongkha → smTibetan
+    138: 0,  # langJavaneseRom → smRoman
+    139: 0,  # langSundaneseRom → smRoman
+    140: 0,  # langGalician → smRoman
+    141: 0,  # langAfrikaans → smRoman
+    142: 0,  # langBreton → smRoman (modified)
+    143: 28,  # langInuktitut → smEthiopic (modified)
+    144: 0,  # langScottishGaelic → smRoman (modified)
+    145: 0,  # langManxGaelic → smRoman (modified)
+    146: 0,  # langIrishGaelicScript → smRoman (modified)
+    147: 0,  # langTongan → smRoman
+    148: 6,  # langGreekAncient → smRoman
+    149: 0,  # langGreenlandic → smRoman
+    150: 0,  # langAzerbaijanRoman → smRoman
+    151: 0,   # langNynorsk → smRoman
+}
diff --git a/Lib/fontTools/ttLib/tables/_o_p_b_d.py b/Lib/fontTools/ttLib/tables/_o_p_b_d.py
new file mode 100644
index 0000000..60bb6c5
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_o_p_b_d.py
@@ -0,0 +1,8 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from .otBase import BaseTTXConverter
+
+
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6opbd.html
+class table__o_p_b_d(BaseTTXConverter):
+    pass
diff --git a/Lib/fontTools/ttLib/tables/_p_o_s_t.py b/Lib/fontTools/ttLib/tables/_p_o_s_t.py
index 248983f..ede62da 100644
--- a/Lib/fontTools/ttLib/tables/_p_o_s_t.py
+++ b/Lib/fontTools/ttLib/tables/_p_o_s_t.py
@@ -13,7 +13,7 @@
 postFormat = """
 	>
 	formatType:			16.16F
-	italicAngle:		16.16F		# italic angle in degrees			
+	italicAngle:		16.16F		# italic angle in degrees
 	underlinePosition:	h
 	underlineThickness:	h
 	isFixedPitch:		L
@@ -27,7 +27,7 @@
 
 
 class table__p_o_s_t(DefaultTable.DefaultTable):
-	
+
 	def decompile(self, data, ttFont):
 		sstruct.unpack(postFormat, data[:postFormatSize], self)
 		data = data[postFormatSize:]
@@ -42,7 +42,7 @@
 		else:
 			# supported format
 			raise ttLib.TTLibError("'post' table format %f not supported" % self.formatType)
-	
+
 	def compile(self, ttFont):
 		data = sstruct.pack(postFormat, self)
 		if self.formatType == 1.0:
@@ -57,7 +57,7 @@
 			# supported format
 			raise ttLib.TTLibError("'post' table format %f not supported" % self.formatType)
 		return data
-	
+
 	def getGlyphOrder(self):
 		"""This function will get called by a ttLib.TTFont instance.
 		Do not call this function yourself, use TTFont().getGlyphOrder()
@@ -68,10 +68,10 @@
 		glyphOrder = self.glyphOrder
 		del self.glyphOrder
 		return glyphOrder
-	
+
 	def decode_format_1_0(self, data, ttFont):
 		self.glyphOrder = standardGlyphOrder[:ttFont["maxp"].numGlyphs]
-	
+
 	def decode_format_2_0(self, data, ttFont):
 		numGlyphs, = struct.unpack(">H", data[:2])
 		numGlyphs = int(numGlyphs)
@@ -88,44 +88,47 @@
 			indices.byteswap()
 		data = data[2*numGlyphs:]
 		self.extraNames = extraNames = unpackPStrings(data)
-		self.glyphOrder = glyphOrder = [None] * int(ttFont['maxp'].numGlyphs)
+		self.glyphOrder = glyphOrder = [""] * int(ttFont['maxp'].numGlyphs)
 		for glyphID in range(numGlyphs):
 			index = indices[glyphID]
 			if index > 257:
-				name = extraNames[index-258]
+				try:
+					name = extraNames[index-258]
+				except IndexError:
+					name = ""
 			else:
 				# fetch names from standard list
 				name = standardGlyphOrder[index]
 			glyphOrder[glyphID] = name
-		#AL990511: code added to handle the case of new glyphs without
-		#          entries into the 'post' table
-		if numGlyphs < ttFont['maxp'].numGlyphs:
-			for i in range(numGlyphs, ttFont['maxp'].numGlyphs):
-				glyphOrder[i] = "glyph#%.5d" % i
-				self.extraNames.append(glyphOrder[i])
 		self.build_psNameMapping(ttFont)
-	
+
 	def build_psNameMapping(self, ttFont):
 		mapping = {}
 		allNames = {}
 		for i in range(ttFont['maxp'].numGlyphs):
 			glyphName = psName = self.glyphOrder[i]
+			if glyphName == "":
+				glyphName = "glyph%.5d" % i
 			if glyphName in allNames:
 				# make up a new glyphName that's unique
 				n = allNames[glyphName]
+				while (glyphName + "#" + str(n)) in allNames:
+					n += 1
 				allNames[glyphName] = n + 1
-				glyphName = glyphName + "#" + repr(n)
-				self.glyphOrder[i] = glyphName
+				glyphName = glyphName + "#" + str(n)
+
+			self.glyphOrder[i] = glyphName
+			allNames[glyphName] = 1
+			if glyphName != psName:
 				mapping[glyphName] = psName
-			else:
-				allNames[glyphName] = 1
+
 		self.mapping = mapping
-	
+
 	def decode_format_3_0(self, data, ttFont):
 		# Setting self.glyphOrder to None will cause the TTFont object
 		# try and construct glyph names from a Unicode cmap table.
 		self.glyphOrder = None
-	
+
 	def decode_format_4_0(self, data, ttFont):
 		from fontTools import agl
 		numGlyphs = ttFont['maxp'].numGlyphs
@@ -151,7 +154,8 @@
 		assert len(glyphOrder) == numGlyphs
 		indices = array.array("H")
 		extraDict = {}
-		extraNames = self.extraNames
+		extraNames = self.extraNames = [
+			n for n in self.extraNames if n not in standardGlyphOrder]
 		for i in range(len(extraNames)):
 			extraDict[extraNames[i]] = i
 		for glyphID in range(numGlyphs):
@@ -172,7 +176,7 @@
 		if sys.byteorder != "big":
 			indices.byteswap()
 		return struct.pack(">H", numGlyphs) + indices.tostring() + packPStrings(extraNames)
-	
+
 	def encode_format_4_0(self, ttFont):
 		from fontTools import agl
 		numGlyphs = ttFont['maxp'].numGlyphs
@@ -229,7 +233,7 @@
 			writer.dumphex(self.data)
 			writer.endtag("hexdata")
 			writer.newline()
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
 		if name not in ("psNames", "extraNames", "hexdata"):
 			setattr(self, name, safeEval(attrs["value"]))
@@ -269,4 +273,3 @@
 	for s in strings:
 		data = data + bytechr(len(s)) + tobytes(s, encoding="latin1")
 	return data
-
diff --git a/Lib/fontTools/ttLib/tables/_p_r_e_p.py b/Lib/fontTools/ttLib/tables/_p_r_e_p.py
index fc92665..f4e8925 100644
--- a/Lib/fontTools/ttLib/tables/_p_r_e_p.py
+++ b/Lib/fontTools/ttLib/tables/_p_r_e_p.py
@@ -1,7 +1,8 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
 from fontTools import ttLib
 
 superclass = ttLib.getTableClass("fpgm")
 
 class table__p_r_e_p(superclass):
 	pass
-
diff --git a/Lib/fontTools/ttLib/tables/_p_r_o_p.py b/Lib/fontTools/ttLib/tables/_p_r_o_p.py
new file mode 100644
index 0000000..7da18ea
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_p_r_o_p.py
@@ -0,0 +1,8 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from .otBase import BaseTTXConverter
+
+
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6prop.html
+class table__p_r_o_p(BaseTTXConverter):
+    pass
diff --git a/Lib/fontTools/ttLib/tables/_s_b_i_x.py b/Lib/fontTools/ttLib/tables/_s_b_i_x.py
index 23cb6df..6efd1f2 100644
--- a/Lib/fontTools/ttLib/tables/_s_b_i_x.py
+++ b/Lib/fontTools/ttLib/tables/_s_b_i_x.py
@@ -1,136 +1,112 @@
 from __future__ import print_function, division, absolute_import
 from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
-from fontTools.misc.textTools import readHex
+from fontTools.misc.textTools import safeEval, num2binary, binary2num
 from . import DefaultTable
-from .sbixBitmap import *
-from .sbixBitmapSet import *
-import struct
+from .sbixGlyph import *
+from .sbixStrike import *
 
-"""
-sbix Table organization:
-
-USHORT        version?
-USHORT        version?
-USHORT        count                    number of bitmap sets
-offsetEntry   offsetEntry[count]       offsetEntries
-(Variable)    storage for bitmap sets
-
-
-offsetEntry:
-
-ULONG         offset                   offset from table start to bitmap set
-
-
-bitmap set:
-
-USHORT        size                     height and width in pixels
-USHORT        resolution               ?
-offsetRecord  offsetRecord[]
-(Variable)    storage for bitmaps
-
-
-offsetRecord:
-
-ULONG         bitmapOffset             offset from start of bitmap set to individual bitmap
-
-
-bitmap:
-
-ULONG         reserved                 00 00 00 00
-char[4]       format                   data type, e.g. "png "
-(Variable)    bitmap data
-"""
 
 sbixHeaderFormat = """
 	>
-	usVal1:          H    # 00 01
-	usVal2:          H    #       00 01
-	numSets:         L    # 00 00 00 02 # number of bitmap sets
+	version:       H	# Version number (set to 1)
+	flags:         H	# The only two bits used in the flags field are bits 0
+						# and 1. For historical reasons, bit 0 must always be 1.
+						# Bit 1 is a sbixDrawOutlines flag and is interpreted as
+						# follows:
+						#     0: Draw only 'sbix' bitmaps
+						#     1: Draw both 'sbix' bitmaps and outlines, in that
+						#        order
+	numStrikes:    L	# Number of bitmap strikes to follow
 """
 sbixHeaderFormatSize = sstruct.calcsize(sbixHeaderFormat)
 
 
-sbixBitmapSetOffsetFormat = """
+sbixStrikeOffsetFormat = """
 	>
-	offset:          L    # 00 00 00 10 # offset from table start to each bitmap set
+	strikeOffset:  L	# Offset from begining of table to data for the
+						# individual strike
 """
-sbixBitmapSetOffsetFormatSize = sstruct.calcsize(sbixBitmapSetOffsetFormat)
+sbixStrikeOffsetFormatSize = sstruct.calcsize(sbixStrikeOffsetFormat)
 
 
 class table__s_b_i_x(DefaultTable.DefaultTable):
-	def __init__(self, tag):
-		self.tableTag = tag
-		self.usVal1 = 1
-		self.usVal2 = 1
-		self.numSets = 0
-		self.bitmapSets = {}
-		self.bitmapSetOffsets = []
+
+	def __init__(self, tag=None):
+		DefaultTable.DefaultTable.__init__(self, tag)
+		self.version = 1
+		self.flags = 1
+		self.numStrikes = 0
+		self.strikes = {}
+		self.strikeOffsets = []
 
 	def decompile(self, data, ttFont):
 		# read table header
 		sstruct.unpack(sbixHeaderFormat, data[ : sbixHeaderFormatSize], self)
-		# collect offsets to individual bitmap sets in self.bitmapSetOffsets
-		for i in range(self.numSets):
-			myOffset = sbixHeaderFormatSize + i * sbixBitmapSetOffsetFormatSize
-			offsetEntry = sbixBitmapSetOffset()
-			sstruct.unpack(sbixBitmapSetOffsetFormat, \
-				data[myOffset : myOffset+sbixBitmapSetOffsetFormatSize], \
-				offsetEntry)
-			self.bitmapSetOffsets.append(offsetEntry.offset)
+		# collect offsets to individual strikes in self.strikeOffsets
+		for i in range(self.numStrikes):
+			current_offset = sbixHeaderFormatSize + i * sbixStrikeOffsetFormatSize
+			offset_entry = sbixStrikeOffset()
+			sstruct.unpack(sbixStrikeOffsetFormat, \
+				data[current_offset:current_offset+sbixStrikeOffsetFormatSize], \
+				offset_entry)
+			self.strikeOffsets.append(offset_entry.strikeOffset)
 
-		# decompile BitmapSets
-		for i in range(self.numSets-1, -1, -1):
-			myBitmapSet = BitmapSet(rawdata=data[self.bitmapSetOffsets[i]:])
-			data = data[:self.bitmapSetOffsets[i]]
-			myBitmapSet.decompile(ttFont)
-			#print "  BitmapSet length: %xh" % len(bitmapSetData)
-			#print "Number of Bitmaps:", myBitmapSet.numBitmaps
-			if myBitmapSet.size in self.bitmapSets:
+		# decompile Strikes
+		for i in range(self.numStrikes-1, -1, -1):
+			current_strike = Strike(rawdata=data[self.strikeOffsets[i]:])
+			data = data[:self.strikeOffsets[i]]
+			current_strike.decompile(ttFont)
+			#print "  Strike length: %xh" % len(bitmapSetData)
+			#print "Number of Glyph entries:", len(current_strike.glyphs)
+			if current_strike.ppem in self.strikes:
 				from fontTools import ttLib
-				raise ttLib.TTLibError("Pixel 'size' must be unique for each BitmapSet")
-			self.bitmapSets[myBitmapSet.size] = myBitmapSet
+				raise ttLib.TTLibError("Pixel 'ppem' must be unique for each Strike")
+			self.strikes[current_strike.ppem] = current_strike
 
-		# after the bitmaps have been extracted, we don't need the offsets anymore
-		del self.bitmapSetOffsets
+		# after the glyph data records have been extracted, we don't need the offsets anymore
+		del self.strikeOffsets
+		del self.numStrikes
 
 	def compile(self, ttFont):
-		sbixData = ""
-		self.numSets = len(self.bitmapSets)
+		sbixData = b""
+		self.numStrikes = len(self.strikes)
 		sbixHeader = sstruct.pack(sbixHeaderFormat, self)
 
-		# calculate offset to start of first bitmap set
-		setOffset = sbixHeaderFormatSize + sbixBitmapSetOffsetFormatSize * self.numSets
+		# calculate offset to start of first strike
+		setOffset = sbixHeaderFormatSize + sbixStrikeOffsetFormatSize * self.numStrikes
 
-		for si in sorted(self.bitmapSets.keys()):
-			myBitmapSet = self.bitmapSets[si]
-			myBitmapSet.compile(ttFont)
-			# append offset to this bitmap set to table header
-			myBitmapSet.offset = setOffset
-			sbixHeader += sstruct.pack(sbixBitmapSetOffsetFormat, myBitmapSet)
-			setOffset += sbixBitmapSetHeaderFormatSize + len(myBitmapSet.data)
-			sbixData += myBitmapSet.data
+		for si in sorted(self.strikes.keys()):
+			current_strike = self.strikes[si]
+			current_strike.compile(ttFont)
+			# append offset to this strike to table header
+			current_strike.strikeOffset = setOffset
+			sbixHeader += sstruct.pack(sbixStrikeOffsetFormat, current_strike)
+			setOffset += len(current_strike.data)
+			sbixData += current_strike.data
 
 		return sbixHeader + sbixData
 
 	def toXML(self, xmlWriter, ttFont):
-		xmlWriter.simpletag("usVal1", value=self.usVal1)
+		xmlWriter.simpletag("version", value=self.version)
 		xmlWriter.newline()
-		xmlWriter.simpletag("usVal2", value=self.usVal2)
+		xmlWriter.simpletag("flags", value=num2binary(self.flags, 16))
 		xmlWriter.newline()
-		for i in sorted(self.bitmapSets.keys()):
-			self.bitmapSets[i].toXML(xmlWriter, ttFont)
+		for i in sorted(self.strikes.keys()):
+			self.strikes[i].toXML(xmlWriter, ttFont)
 
 	def fromXML(self, name, attrs, content, ttFont):
-		if name in ["usVal1", "usVal2"]:
-			setattr(self, name, int(attrs["value"]))
-		elif name == "bitmapSet":
-			myBitmapSet = BitmapSet()
+		if name =="version":
+			setattr(self, name, safeEval(attrs["value"]))
+		elif name == "flags":
+			setattr(self, name, binary2num(attrs["value"]))
+		elif name == "strike":
+			current_strike = Strike()
 			for element in content:
 				if isinstance(element, tuple):
 					name, attrs, content = element
-					myBitmapSet.fromXML(name, attrs, content, ttFont)
-			self.bitmapSets[myBitmapSet.size] = myBitmapSet
+					current_strike.fromXML(name, attrs, content, ttFont)
+			self.strikes[current_strike.ppem] = current_strike
 		else:
 			from fontTools import ttLib
 			raise ttLib.TTLibError("can't handle '%s' element" % name)
@@ -138,5 +114,5 @@
 
 # Helper classes
 
-class sbixBitmapSetOffset(object):
+class sbixStrikeOffset(object):
 	pass
diff --git a/Lib/fontTools/ttLib/tables/_t_r_a_k.py b/Lib/fontTools/ttLib/tables/_t_r_a_k.py
new file mode 100644
index 0000000..b5820b2
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_t_r_a_k.py
@@ -0,0 +1,314 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc import sstruct
+from fontTools.misc.fixedTools import fixedToFloat as fi2fl, floatToFixed as fl2fi
+from fontTools.misc.textTools import safeEval
+from fontTools.ttLib import TTLibError
+from . import DefaultTable
+import struct
+try:
+	from collections.abc import MutableMapping
+except ImportError:
+	from UserDict import DictMixin as MutableMapping
+
+
+# Apple's documentation of 'trak':
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6trak.html
+
+TRAK_HEADER_FORMAT = """
+	> # big endian
+	version:     16.16F
+	format:      H
+	horizOffset: H
+	vertOffset:  H
+	reserved:    H
+"""
+
+TRAK_HEADER_FORMAT_SIZE = sstruct.calcsize(TRAK_HEADER_FORMAT)
+
+
+TRACK_DATA_FORMAT = """
+	> # big endian
+	nTracks:         H
+	nSizes:          H
+	sizeTableOffset: L
+"""
+
+TRACK_DATA_FORMAT_SIZE = sstruct.calcsize(TRACK_DATA_FORMAT)
+
+
+TRACK_TABLE_ENTRY_FORMAT = """
+	> # big endian
+	track:      16.16F
+	nameIndex:       H
+	offset:          H
+"""
+
+TRACK_TABLE_ENTRY_FORMAT_SIZE = sstruct.calcsize(TRACK_TABLE_ENTRY_FORMAT)
+
+
+# size values are actually '16.16F' fixed-point values, but here I do the
+# fixedToFloat conversion manually instead of relying on sstruct
+SIZE_VALUE_FORMAT = ">l"
+SIZE_VALUE_FORMAT_SIZE = struct.calcsize(SIZE_VALUE_FORMAT)
+
+# per-Size values are in 'FUnits', i.e. 16-bit signed integers
+PER_SIZE_VALUE_FORMAT = ">h"
+PER_SIZE_VALUE_FORMAT_SIZE = struct.calcsize(PER_SIZE_VALUE_FORMAT)
+
+
+class table__t_r_a_k(DefaultTable.DefaultTable):
+	dependencies = ['name']
+
+	def compile(self, ttFont):
+		dataList = []
+		offset = TRAK_HEADER_FORMAT_SIZE
+		for direction in ('horiz', 'vert'):
+			trackData = getattr(self, direction + 'Data', TrackData())
+			offsetName = direction + 'Offset'
+			# set offset to 0 if None or empty
+			if not trackData:
+				setattr(self, offsetName, 0)
+				continue
+			# TrackData table format must be longword aligned
+			alignedOffset = (offset + 3) & ~3
+			padding, offset = b"\x00"*(alignedOffset - offset), alignedOffset
+			setattr(self, offsetName, offset)
+
+			data = trackData.compile(offset)
+			offset += len(data)
+			dataList.append(padding + data)
+
+		self.reserved = 0
+		tableData = bytesjoin([sstruct.pack(TRAK_HEADER_FORMAT, self)] + dataList)
+		return tableData
+
+	def decompile(self, data, ttFont):
+		sstruct.unpack(TRAK_HEADER_FORMAT, data[:TRAK_HEADER_FORMAT_SIZE], self)
+		for direction in ('horiz', 'vert'):
+			trackData = TrackData()
+			offset = getattr(self, direction + 'Offset')
+			if offset != 0:
+				trackData.decompile(data, offset)
+			setattr(self, direction + 'Data', trackData)
+
+	def toXML(self, writer, ttFont):
+		writer.simpletag('version', value=self.version)
+		writer.newline()
+		writer.simpletag('format', value=self.format)
+		writer.newline()
+		for direction in ('horiz', 'vert'):
+			dataName = direction + 'Data'
+			writer.begintag(dataName)
+			writer.newline()
+			trackData = getattr(self, dataName, TrackData())
+			trackData.toXML(writer, ttFont)
+			writer.endtag(dataName)
+			writer.newline()
+
+	def fromXML(self, name, attrs, content, ttFont):
+		if name == 'version':
+			self.version = safeEval(attrs['value'])
+		elif name == 'format':
+			self.format = safeEval(attrs['value'])
+		elif name in ('horizData', 'vertData'):
+			trackData = TrackData()
+			setattr(self, name, trackData)
+			for element in content:
+				if not isinstance(element, tuple):
+					continue
+				name, attrs, content_ = element
+				trackData.fromXML(name, attrs, content_, ttFont)
+
+
+class TrackData(MutableMapping):
+
+	def __init__(self, initialdata={}):
+		self._map = dict(initialdata)
+
+	def compile(self, offset):
+		nTracks = len(self)
+		sizes = self.sizes()
+		nSizes = len(sizes)
+
+		# offset to the start of the size subtable
+		offset += TRACK_DATA_FORMAT_SIZE + TRACK_TABLE_ENTRY_FORMAT_SIZE*nTracks
+		trackDataHeader = sstruct.pack(
+			TRACK_DATA_FORMAT,
+			{'nTracks': nTracks, 'nSizes': nSizes, 'sizeTableOffset': offset})
+
+		entryDataList = []
+		perSizeDataList = []
+		# offset to per-size tracking values
+		offset += SIZE_VALUE_FORMAT_SIZE*nSizes
+		# sort track table entries by track value
+		for track, entry in sorted(self.items()):
+			assert entry.nameIndex is not None
+			entry.track = track
+			entry.offset = offset
+			entryDataList += [sstruct.pack(TRACK_TABLE_ENTRY_FORMAT, entry)]
+			# sort per-size values by size
+			for size, value in sorted(entry.items()):
+				perSizeDataList += [struct.pack(PER_SIZE_VALUE_FORMAT, value)]
+			offset += PER_SIZE_VALUE_FORMAT_SIZE*nSizes
+		# sort size values
+		sizeDataList = [struct.pack(SIZE_VALUE_FORMAT, fl2fi(sv, 16)) for sv in sorted(sizes)]
+
+		data = bytesjoin([trackDataHeader] + entryDataList + sizeDataList + perSizeDataList)
+		return data
+
+	def decompile(self, data, offset):
+		# initial offset is from the start of trak table to the current TrackData
+		trackDataHeader = data[offset:offset+TRACK_DATA_FORMAT_SIZE]
+		if len(trackDataHeader) != TRACK_DATA_FORMAT_SIZE:
+			raise TTLibError('not enough data to decompile TrackData header')
+		sstruct.unpack(TRACK_DATA_FORMAT, trackDataHeader, self)
+		offset += TRACK_DATA_FORMAT_SIZE
+
+		nSizes = self.nSizes
+		sizeTableOffset = self.sizeTableOffset
+		sizeTable = []
+		for i in range(nSizes):
+			sizeValueData = data[sizeTableOffset:sizeTableOffset+SIZE_VALUE_FORMAT_SIZE]
+			if len(sizeValueData) < SIZE_VALUE_FORMAT_SIZE:
+				raise TTLibError('not enough data to decompile TrackData size subtable')
+			sizeValue, = struct.unpack(SIZE_VALUE_FORMAT, sizeValueData)
+			sizeTable.append(fi2fl(sizeValue, 16))
+			sizeTableOffset += SIZE_VALUE_FORMAT_SIZE
+
+		for i in range(self.nTracks):
+			entry = TrackTableEntry()
+			entryData = data[offset:offset+TRACK_TABLE_ENTRY_FORMAT_SIZE]
+			if len(entryData) < TRACK_TABLE_ENTRY_FORMAT_SIZE:
+				raise TTLibError('not enough data to decompile TrackTableEntry record')
+			sstruct.unpack(TRACK_TABLE_ENTRY_FORMAT, entryData, entry)
+			perSizeOffset = entry.offset
+			for j in range(nSizes):
+				size = sizeTable[j]
+				perSizeValueData = data[perSizeOffset:perSizeOffset+PER_SIZE_VALUE_FORMAT_SIZE]
+				if len(perSizeValueData) < PER_SIZE_VALUE_FORMAT_SIZE:
+					raise TTLibError('not enough data to decompile per-size track values')
+				perSizeValue, = struct.unpack(PER_SIZE_VALUE_FORMAT, perSizeValueData)
+				entry[size] = perSizeValue
+				perSizeOffset += PER_SIZE_VALUE_FORMAT_SIZE
+			self[entry.track] = entry
+			offset += TRACK_TABLE_ENTRY_FORMAT_SIZE
+
+	def toXML(self, writer, ttFont):
+		nTracks = len(self)
+		nSizes = len(self.sizes())
+		writer.comment("nTracks=%d, nSizes=%d" % (nTracks, nSizes))
+		writer.newline()
+		for track, entry in sorted(self.items()):
+			assert entry.nameIndex is not None
+			entry.track = track
+			entry.toXML(writer, ttFont)
+
+	def fromXML(self, name, attrs, content, ttFont):
+		if name != 'trackEntry':
+			return
+		entry = TrackTableEntry()
+		entry.fromXML(name, attrs, content, ttFont)
+		self[entry.track] = entry
+
+	def sizes(self):
+		if not self:
+			return frozenset()
+		tracks = list(self.tracks())
+		sizes = self[tracks.pop(0)].sizes()
+		for track in tracks:
+			entrySizes = self[track].sizes()
+			if sizes != entrySizes:
+				raise TTLibError(
+					"'trak' table entries must specify the same sizes: "
+					"%s != %s" % (sorted(sizes), sorted(entrySizes)))
+		return frozenset(sizes)
+
+	def __getitem__(self, track):
+		return self._map[track]
+
+	def __delitem__(self, track):
+		del self._map[track]
+
+	def __setitem__(self, track, entry):
+		self._map[track] = entry
+
+	def __len__(self):
+		return len(self._map)
+
+	def __iter__(self):
+		return iter(self._map)
+
+	def keys(self):
+		return self._map.keys()
+
+	tracks = keys
+
+	def __repr__(self):
+		return "TrackData({})".format(self._map if self else "")
+
+
+class TrackTableEntry(MutableMapping):
+
+	def __init__(self, values={}, nameIndex=None):
+		self.nameIndex = nameIndex
+		self._map = dict(values)
+
+	def toXML(self, writer, ttFont):
+		name = ttFont["name"].getDebugName(self.nameIndex)
+		writer.begintag(
+			"trackEntry",
+			(('value', self.track), ('nameIndex', self.nameIndex)))
+		writer.newline()
+		if name:
+			writer.comment(name)
+			writer.newline()
+		for size, perSizeValue in sorted(self.items()):
+			writer.simpletag("track", size=size, value=perSizeValue)
+			writer.newline()
+		writer.endtag("trackEntry")
+		writer.newline()
+
+	def fromXML(self, name, attrs, content, ttFont):
+		self.track = safeEval(attrs['value'])
+		self.nameIndex = safeEval(attrs['nameIndex'])
+		for element in content:
+			if not isinstance(element, tuple):
+				continue
+			name, attrs, _ = element
+			if name != 'track':
+				continue
+			size = safeEval(attrs['size'])
+			self[size] = safeEval(attrs['value'])
+
+	def __getitem__(self, size):
+		return self._map[size]
+
+	def __delitem__(self, size):
+		del self._map[size]
+
+	def __setitem__(self, size, value):
+		self._map[size] = value
+
+	def __len__(self):
+		return len(self._map)
+
+	def __iter__(self):
+		return iter(self._map)
+
+	def keys(self):
+		return self._map.keys()
+
+	sizes = keys
+
+	def __repr__(self):
+		return "TrackTableEntry({}, nameIndex={})".format(self._map, self.nameIndex)
+
+	def __eq__(self, other):
+		if not isinstance(other, self.__class__):
+			return NotImplemented
+		return self.nameIndex == other.nameIndex and dict(self) == dict(other)
+
+	def __ne__(self, other):
+		result = self.__eq__(other)
+		return result if result is NotImplemented else not result
diff --git a/Lib/fontTools/ttLib/tables/_v_h_e_a.py b/Lib/fontTools/ttLib/tables/_v_h_e_a.py
index 8131ad3..312c5ec 100644
--- a/Lib/fontTools/ttLib/tables/_v_h_e_a.py
+++ b/Lib/fontTools/ttLib/tables/_v_h_e_a.py
@@ -2,77 +2,116 @@
 from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
 from fontTools.misc.textTools import safeEval
+from fontTools.misc.fixedTools import (
+	ensureVersionIsLong as fi2ve, versionToFixed as ve2fi)
 from . import DefaultTable
+import math
+
 
 vheaFormat = """
 		>	# big endian
-		tableVersion:			16.16F
-		ascent:					h
-		descent:				h
-		lineGap:				h
-		advanceHeightMax:		H
-		minTopSideBearing:		h
+		tableVersion:		L
+		ascent:			h
+		descent:		h
+		lineGap:		h
+		advanceHeightMax:	H
+		minTopSideBearing:	h
 		minBottomSideBearing:	h
-		yMaxExtent:				h
-		caretSlopeRise:			h
-		caretSlopeRun:			h
-		reserved0:				h
-		reserved1:				h
-		reserved2:				h
-		reserved3:				h
-		reserved4:				h
-		metricDataFormat:		h
-		numberOfVMetrics:		H
+		yMaxExtent:		h
+		caretSlopeRise:		h
+		caretSlopeRun:		h
+		caretOffset:		h
+		reserved1:		h
+		reserved2:		h
+		reserved3:		h
+		reserved4:		h
+		metricDataFormat:	h
+		numberOfVMetrics:	H
 """
 
 class table__v_h_e_a(DefaultTable.DefaultTable):
-	
-	dependencies = ['vmtx', 'glyf']
-	
+
+	# Note: Keep in sync with table__h_h_e_a
+
+	dependencies = ['vmtx', 'glyf', 'CFF ']
+
 	def decompile(self, data, ttFont):
 		sstruct.unpack(vheaFormat, data, self)
-	
+
 	def compile(self, ttFont):
-		self.recalc(ttFont)
+		if ttFont.recalcBBoxes and (ttFont.isLoaded('glyf') or ttFont.isLoaded('CFF ')):
+			self.recalc(ttFont)
+		self.tableVersion = fi2ve(self.tableVersion)
 		return sstruct.pack(vheaFormat, self)
-	
+
 	def recalc(self, ttFont):
-		vtmxTable = ttFont['vmtx']
+		if 'vmtx' in ttFont:
+			vmtxTable = ttFont['vmtx']
+			self.advanceHeightMax = max(adv for adv, _ in vmtxTable.metrics.values())
+
+		boundsHeightDict = {}
 		if 'glyf' in ttFont:
-			if not ttFont.isLoaded('glyf'):
-				return
 			glyfTable = ttFont['glyf']
-			advanceHeightMax = -100000    # arbitrary big negative number
-			minTopSideBearing = 100000    # arbitrary big number
-			minBottomSideBearing = 100000 # arbitrary big number
-			yMaxExtent = -100000          # arbitrary big negative number
-			
 			for name in ttFont.getGlyphOrder():
-				height, tsb = vtmxTable[name]
 				g = glyfTable[name]
-				if g.numberOfContours <= 0:
+				if g.numberOfContours == 0:
 					continue
-				advanceHeightMax = max(advanceHeightMax, height)
+				if g.numberOfContours < 0 and not hasattr(g, "yMax"):
+					# Composite glyph without extents set.
+					# Calculate those.
+					g.recalcBounds(glyfTable)
+				boundsHeightDict[name] = g.yMax - g.yMin
+		elif 'CFF ' in ttFont:
+			topDict = ttFont['CFF '].cff.topDictIndex[0]
+			charStrings = topDict.CharStrings
+			for name in ttFont.getGlyphOrder():
+				cs = charStrings[name]
+				bounds = cs.calcBounds(charStrings)
+				if bounds is not None:
+					boundsHeightDict[name] = int(
+						math.ceil(bounds[3]) - math.floor(bounds[1]))
+
+		if boundsHeightDict:
+			minTopSideBearing = float('inf')
+			minBottomSideBearing = float('inf')
+			yMaxExtent = -float('inf')
+			for name, boundsHeight in boundsHeightDict.items():
+				advanceHeight, tsb = vmtxTable[name]
+				bsb = advanceHeight - tsb - boundsHeight
+				extent = tsb + boundsHeight
 				minTopSideBearing = min(minTopSideBearing, tsb)
-				rsb = height - tsb - (g.yMax - g.yMin)
-				minBottomSideBearing = min(minBottomSideBearing, rsb)
-				extent = tsb + (g.yMax - g.yMin)
+				minBottomSideBearing = min(minBottomSideBearing, bsb)
 				yMaxExtent = max(yMaxExtent, extent)
-			self.advanceHeightMax = advanceHeightMax
 			self.minTopSideBearing = minTopSideBearing
 			self.minBottomSideBearing = minBottomSideBearing
 			self.yMaxExtent = yMaxExtent
-		else:
-			# XXX CFF recalc...
-			pass
-	
+
+		else:  # No glyph has outlines.
+			self.minTopSideBearing = 0
+			self.minBottomSideBearing = 0
+			self.yMaxExtent = 0
+
 	def toXML(self, writer, ttFont):
 		formatstring, names, fixes = sstruct.getformat(vheaFormat)
 		for name in names:
 			value = getattr(self, name)
+			if name == "tableVersion":
+				value = fi2ve(value)
+				value = "0x%08x" % value
 			writer.simpletag(name, value=value)
 			writer.newline()
-	
+
 	def fromXML(self, name, attrs, content, ttFont):
+		if name == "tableVersion":
+			setattr(self, name, ve2fi(attrs["value"]))
+			return
 		setattr(self, name, safeEval(attrs["value"]))
 
+	# reserved0 is caretOffset for legacy reasons
+	@property
+	def reserved0(self):
+		return self.caretOffset
+
+	@reserved0.setter
+	def reserved0(self, value):
+		self.caretOffset = value
diff --git a/Lib/fontTools/ttLib/tables/_v_m_t_x.py b/Lib/fontTools/ttLib/tables/_v_m_t_x.py
index c204de6..5573225 100644
--- a/Lib/fontTools/ttLib/tables/_v_m_t_x.py
+++ b/Lib/fontTools/ttLib/tables/_v_m_t_x.py
@@ -1,9 +1,11 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
 from fontTools import ttLib
 
 superclass = ttLib.getTableClass("hmtx")
 
 class table__v_m_t_x(superclass):
-	
+
 	headerTag = 'vhea'
 	advanceName = 'height'
 	sideBearingName = 'tsb'
diff --git a/Lib/fontTools/ttLib/tables/asciiTable.py b/Lib/fontTools/ttLib/tables/asciiTable.py
index e5f3136..87266a0 100644
--- a/Lib/fontTools/ttLib/tables/asciiTable.py
+++ b/Lib/fontTools/ttLib/tables/asciiTable.py
@@ -4,7 +4,7 @@
 
 
 class asciiTable(DefaultTable.DefaultTable):
-	
+
 	def toXML(self, writer, ttFont):
 		data = tostr(self.data)
 		# removing null bytes. XXX needed??
@@ -12,12 +12,11 @@
 		data = strjoin(data)
 		writer.begintag("source")
 		writer.newline()
-		writer.write_noindent(data.replace("\r", "\n"))
+		writer.write_noindent(data)
 		writer.newline()
 		writer.endtag("source")
 		writer.newline()
-	
-	def fromXML(self, name, attrs, content, ttFont):
-		lines = strjoin(content).replace("\r", "\n").split("\n")
-		self.data = tobytes("\r".join(lines[1:-1]))
 
+	def fromXML(self, name, attrs, content, ttFont):
+		lines = strjoin(content).split("\n")
+		self.data = tobytes("\n".join(lines[1:-1]))
diff --git a/Lib/fontTools/ttLib/tables/grUtils.py b/Lib/fontTools/ttLib/tables/grUtils.py
new file mode 100644
index 0000000..1ce2c9e
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/grUtils.py
@@ -0,0 +1,79 @@
+import struct, warnings
+try:
+    import lz4
+except: 
+    lz4 = None
+
+#old scheme for VERSION < 0.9 otherwise use lz4.block
+
+def decompress(data):
+    (compression,) = struct.unpack(">L", data[4:8])
+    scheme = compression >> 27
+    size = compression & 0x07ffffff
+    if scheme == 0:
+        pass
+    elif scheme == 1 and lz4:
+        res = lz4.decompress(struct.pack("<L", size) + data[8:])
+        if len(res) != size:
+            warnings.warn("Table decompression failed.")
+        else:
+            data = res
+    else:
+        warnings.warn("Table is compressed with an unsupported compression scheme")
+    return (data, scheme)
+
+def compress(scheme, data):
+    hdr = data[:4] + struct.pack(">L", (scheme << 27) + (len(data) & 0x07ffffff))
+    if scheme == 0 :
+        return data
+    elif scheme == 1 and lz4:
+        res = lz4.compress(hdr + data)
+        return res
+    else:
+        warnings.warn("Table failed to compress by unsupported compression scheme")
+    return data
+
+def _entries(attrs, sameval):
+    ak = 0
+    vals = []
+    lastv = 0
+    for k,v in attrs:
+        if len(vals) and (k != ak + 1 or (sameval and v != lastv)) :
+            yield (ak - len(vals) + 1, len(vals), vals)
+            vals = []
+        ak = k
+        vals.append(v)
+        lastv = v
+    yield (ak - len(vals) + 1, len(vals), vals)
+
+def entries(attributes, sameval = False):
+    g = _entries(sorted(attributes.iteritems(), key=lambda x:int(x[0])), sameval)
+    return g
+
+def bininfo(num, size=1):
+    if num == 0:
+        return struct.pack(">4H", 0, 0, 0, 0)
+    srange = 1;
+    select = 0
+    while srange <= num:
+        srange *= 2
+        select += 1
+    select -= 1
+    srange /= 2
+    srange *= size
+    shift = num * size - srange
+    return struct.pack(">4H", num, srange, select, shift)
+
+def num2tag(n):
+    if n < 0x200000:
+        return str(n)
+    else:
+        return struct.unpack('4s', struct.pack('>L', n))[0].replace(b'\000', b'').decode()
+
+def tag2num(n):
+    try:
+        return int(n)
+    except ValueError:
+        n = (n+"    ")[:4]
+        return struct.unpack('>L', n.encode('ascii'))[0]
+
diff --git a/Lib/fontTools/ttLib/tables/otBase.py b/Lib/fontTools/ttLib/tables/otBase.py
index cbe574a..2064e24 100644
--- a/Lib/fontTools/ttLib/tables/otBase.py
+++ b/Lib/fontTools/ttLib/tables/otBase.py
@@ -1,7 +1,12 @@
 from __future__ import print_function, division, absolute_import
 from fontTools.misc.py23 import *
 from .DefaultTable import DefaultTable
+import sys
+import array
 import struct
+import logging
+
+log = logging.getLogger(__name__)
 
 class OverflowErrorRecord(object):
 	def __init__(self, overflowTuple):
@@ -23,43 +28,27 @@
 
 
 class BaseTTXConverter(DefaultTable):
-	
+
 	"""Generic base class for TTX table converters. It functions as an
 	adapter between the TTX (ttLib actually) table model and the model
 	we use for OpenType tables, which is necessarily subtly different.
 	"""
-	
+
 	def decompile(self, data, font):
 		from . import otTables
-		cachingStats = None if True else {}
-		class GlobalState(object):
-			def __init__(self, tableType, cachingStats):
-				self.tableType = tableType
-				self.cachingStats = cachingStats
-		globalState = GlobalState(tableType=self.tableTag,
-					  cachingStats=cachingStats)
-		reader = OTTableReader(data, globalState)
+		reader = OTTableReader(data, tableTag=self.tableTag)
 		tableClass = getattr(otTables, self.tableTag)
 		self.table = tableClass()
 		self.table.decompile(reader, font)
-		if cachingStats:
-			stats = sorted([(v, k) for k, v in cachingStats.items()])
-			stats.reverse()
-			print("cachingsstats for ", self.tableTag)
-			for v, k in stats:
-				if v < 2:
-					break
-				print(v, k)
-			print("---", len(stats))
-	
+
 	def compile(self, font):
-		""" Create a top-level OTFWriter for the GPOS/GSUB table.
+		""" Create a top-level OTTableWriter for the GPOS/GSUB table.
 			Call the compile method for the the table
 				for each 'converter' record in the table converter list
-					call converter's write method for each item in the value. 
+					call converter's write method for each item in the value.
 						- For simple items, the write method adds a string to the
-						writer's self.items list. 
-						- For Struct/Table/Subtable items, it add first adds new writer to the 
+						writer's self.items list.
+						- For Struct/Table/Subtable items, it add first adds new writer to the
 						to the writer's self.items, then calls the item's compile method.
 						This creates a tree of writers, rooted at the GUSB/GPOS writer, with
 						each writer representing a table, and the writer.items list containing
@@ -71,17 +60,13 @@
 				Traverse the flat list of tables again, calling getData each get the data in the table, now that
 				pos's and offset are known.
 
-				If a lookup subtable overflows an offset, we have to start all over. 
+				If a lookup subtable overflows an offset, we have to start all over.
 		"""
-		class GlobalState(object):
-			def __init__(self, tableType):
-				self.tableType = tableType
-		globalState = GlobalState(tableType=self.tableTag)
 		overflowRecord = None
 
 		while True:
 			try:
-				writer = OTTableWriter(globalState)
+				writer = OTTableWriter(tableTag=self.tableTag)
 				self.table.compile(writer, font)
 				return writer.getAllData()
 
@@ -91,7 +76,7 @@
 					raise # Oh well...
 
 				overflowRecord = e.value
-				print("Attempting to fix OTLOffsetOverflowError", e)
+				log.info("Attempting to fix OTLOffsetOverflowError %s", e)
 				lastItem = overflowRecord
 
 				ok = 0
@@ -106,7 +91,7 @@
 
 	def toXML(self, writer, font):
 		self.table.toXML2(writer, font)
-	
+
 	def fromXML(self, name, attrs, content, font):
 		from . import otTables
 		if not hasattr(self, "table"):
@@ -119,21 +104,29 @@
 
 	"""Helper class to retrieve data from an OpenType table."""
 
-	__slots__ = ('data', 'offset', 'pos', 'globalState', 'localState')
+	__slots__ = ('data', 'offset', 'pos', 'localState', 'tableTag')
 
-	def __init__(self, data, globalState={}, localState=None, offset=0):
+	def __init__(self, data, localState=None, offset=0, tableTag=None):
 		self.data = data
 		self.offset = offset
 		self.pos = offset
-		self.globalState = globalState
 		self.localState = localState
+		self.tableTag = tableTag
+
+	def advance(self, count):
+		self.pos += count
+
+	def seek(self, pos):
+		self.pos = pos
+
+	def copy(self):
+		other = self.__class__(self.data, self.localState, self.offset, self.tableTag)
+		other.pos = self.pos
+		return other
 
 	def getSubReader(self, offset):
 		offset = self.offset + offset
-		cachingStats = self.globalState.cachingStats
-		if cachingStats is not None:
-			cachingStats[offset] = cachingStats.get(offset, 0) + 1
-		return self.__class__(self.data, self.globalState, self.localState, offset)
+		return self.__class__(self.data, self.localState, offset, self.tableTag)
 
 	def readUShort(self):
 		pos = self.pos
@@ -142,6 +135,22 @@
 		self.pos = newpos
 		return value
 
+	def readUShortArray(self, count):
+		pos = self.pos
+		newpos = pos + count * 2
+		value = array.array("H", self.data[pos:newpos])
+		if sys.byteorder != "big":
+			value.byteswap()
+		self.pos = newpos
+		return value
+
+	def readInt8(self):
+		pos = self.pos
+		newpos = pos + 1
+		value, = struct.unpack(">b", self.data[pos:newpos])
+		self.pos = newpos
+		return value
+
 	def readShort(self):
 		pos = self.pos
 		newpos = pos + 2
@@ -156,6 +165,13 @@
 		self.pos = newpos
 		return value
 
+	def readUInt8(self):
+		pos = self.pos
+		newpos = pos + 1
+		value, = struct.unpack(">B", self.data[pos:newpos])
+		self.pos = newpos
+		return value
+
 	def readUInt24(self):
 		pos = self.pos
 		newpos = pos + 3
@@ -169,12 +185,19 @@
 		value, = struct.unpack(">L", self.data[pos:newpos])
 		self.pos = newpos
 		return value
-	
+
 	def readTag(self):
 		pos = self.pos
 		newpos = pos + 4
 		value = Tag(self.data[pos:newpos])
-		assert len(value) == 4
+		assert len(value) == 4, value
+		self.pos = newpos
+		return value
+
+	def readData(self, count):
+		pos = self.pos
+		newpos = pos + count
+		value = self.data[pos:newpos]
 		self.pos = newpos
 		return value
 
@@ -184,18 +207,22 @@
 		self.localState = state
 
 	def __getitem__(self, name):
-		return self.localState[name]
+		return self.localState and self.localState[name]
+
+	def __contains__(self, name):
+		return self.localState and name in self.localState
 
 
 class OTTableWriter(object):
-	
+
 	"""Helper class to gather and assemble data for OpenType tables."""
-	
-	def __init__(self, globalState, localState=None):
+
+	def __init__(self, localState=None, tableTag=None):
 		self.items = []
 		self.pos = None
-		self.globalState = globalState
 		self.localState = localState
+		self.tableTag = tableTag
+		self.longOffset = False
 		self.parent = None
 
 	def __setitem__(self, name, value):
@@ -206,50 +233,23 @@
 	def __getitem__(self, name):
 		return self.localState[name]
 
+	def __delitem__(self, name):
+		del self.localState[name]
+
 	# assembler interface
-	
-	def getAllData(self):
-		"""Assemble all data, including all subtables."""
-		self._doneWriting()
-		tables, extTables = self._gatherTables()
-		tables.reverse()
-		extTables.reverse()
-		# Gather all data in two passes: the absolute positions of all
-		# subtable are needed before the actual data can be assembled.
-		pos = 0
-		for table in tables:
-			table.pos = pos
-			pos = pos + table.getDataLength()
 
-		for table in extTables:
-			table.pos = pos
-			pos = pos + table.getDataLength()
-
-
-		data = []
-		for table in tables:
-			tableData = table.getData()
-			data.append(tableData)
-
-		for table in extTables:
-			tableData = table.getData()
-			data.append(tableData)
-
-		return bytesjoin(data)
-	
 	def getDataLength(self):
 		"""Return the length of this table in bytes, without subtables."""
 		l = 0
 		for item in self.items:
-			if hasattr(item, "getData") or hasattr(item, "getCountData"):
-				if item.longOffset:
-					l = l + 4  # sizeof(ULong)
-				else:
-					l = l + 2  # sizeof(UShort)
+			if hasattr(item, "getCountData"):
+				l += item.size
+			elif hasattr(item, "getData"):
+				l += 4 if item.longOffset else 2
 			else:
 				l = l + len(item)
 		return l
-	
+
 	def getData(self):
 		"""Assemble the data for this writer/table, without subtables."""
 		items = list(self.items)  # make a shallow copy
@@ -257,7 +257,7 @@
 		numItems = len(items)
 		for i in range(numItems):
 			item = items[i]
-			
+
 			if hasattr(item, "getData"):
 				if item.longOffset:
 					items[i] = packULong(item.pos - pos)
@@ -266,55 +266,26 @@
 						items[i] = packUShort(item.pos - pos)
 					except struct.error:
 						# provide data to fix overflow problem.
-						# If the overflow is to a lookup, or from a lookup to a subtable,
-						# just report the current item.  Otherwise...
-						if self.name not in [ 'LookupList', 'Lookup']:
-							# overflow is within a subTable. Life is more complicated.
-							# If we split the sub-table just before the current item, we may still suffer overflow.
-							# This is because duplicate table merging is done only within an Extension subTable tree;
-							# when we split the subtable in two, some items may no longer be duplicates. 
-							# Get worst case by adding up all the item lengths, depth first traversal.
-							# and then report the first item that overflows a short.
-							def getDeepItemLength(table):
-								if hasattr(table, "getDataLength"):
-									length = 0
-									for item in table.items:
-										length = length + getDeepItemLength(item)
-								else:
-									length = len(table)
-								return length
-	
-							length = self.getDataLength()
-							if hasattr(self, "sortCoverageLast") and item.name == "Coverage":
-								# Coverage is first in the item list, but last in the table list,
-								# The original overflow is really in the item list. Skip the Coverage 
-								# table in the following test.
-								items = items[i+1:]
-	
-							for j in range(len(items)):
-								item = items[j]
-								length = length + getDeepItemLength(item)
-								if length > 65535:
-									break
 						overflowErrorRecord = self.getOverflowErrorRecord(item)
-						
-						
+
 						raise OTLOffsetOverflowError(overflowErrorRecord)
 
 		return bytesjoin(items)
-	
+
 	def __hash__(self):
 		# only works after self._doneWriting() has been called
 		return hash(self.items)
-	
+
 	def __ne__(self, other):
-		return not self.__eq__(other)
+		result = self.__eq__(other)
+		return result if result is NotImplemented else not result
+
 	def __eq__(self, other):
 		if type(self) != type(other):
 			return NotImplemented
-		return self.items == other.items
-	
-	def _doneWriting(self, internedTables=None):
+		return self.longOffset == other.longOffset and self.items == other.items
+
+	def _doneWriting(self, internedTables):
 		# Convert CountData references to data string items
 		# collapse duplicate table references to a unique entry
 		# "tables" are OTTableWriter objects.
@@ -322,54 +293,51 @@
 		# For Extension Lookup types, we can
 		# eliminate duplicates only within the tree under the Extension Lookup,
 		# as offsets may exceed 64K even between Extension LookupTable subtables.
-		if internedTables is None:
+		isExtension = hasattr(self, "Extension")
+
+		# Certain versions of Uniscribe reject the font if the GSUB/GPOS top-level
+		# arrays (ScriptList, FeatureList, LookupList) point to the same, possibly
+		# empty, array.  So, we don't share those.
+		# See: https://github.com/behdad/fonttools/issues/518
+		dontShare = hasattr(self, 'DontShare')
+
+		if isExtension:
 			internedTables = {}
+
 		items = self.items
-		iRange = list(range(len(items)))
-		
-		if hasattr(self, "Extension"):
-			newTree = 1
-		else:
-			newTree = 0
-		for i in iRange:
+		for i in range(len(items)):
 			item = items[i]
 			if hasattr(item, "getCountData"):
 				items[i] = item.getCountData()
 			elif hasattr(item, "getData"):
-				if newTree:
-					item._doneWriting()
-				else:
-					item._doneWriting(internedTables)
-					internedItem = internedTables.get(item)
-					if internedItem:
-						items[i] = item = internedItem
-					else:
-						internedTables[item] = item
+				item._doneWriting(internedTables)
+				if not dontShare:
+					items[i] = item = internedTables.setdefault(item, item)
 		self.items = tuple(items)
-	
-	def _gatherTables(self, tables=None, extTables=None, done=None):
+
+	def _gatherTables(self, tables, extTables, done):
 		# Convert table references in self.items tree to a flat
 		# list of tables in depth-first traversal order.
 		# "tables" are OTTableWriter objects.
-		# We do the traversal in reverse order at each level, in order to 
+		# We do the traversal in reverse order at each level, in order to
 		# resolve duplicate references to be the last reference in the list of tables.
 		# For extension lookups, duplicate references can be merged only within the
 		# writer tree under the  extension lookup.
-		if tables is None: # init call for first time.
-			tables = []
-			extTables = []
-			done = {}
 
-		done[self] = 1
+		done[id(self)] = True
 
 		numItems = len(self.items)
 		iRange = list(range(numItems))
 		iRange.reverse()
 
-		if hasattr(self, "Extension"):
-			appendExtensions = 1
-		else:
-			appendExtensions = 0
+		isExtension = hasattr(self, "Extension")
+		dontShare = hasattr(self, 'DontShare')
+
+		selfTables = tables
+
+		if isExtension:
+			assert extTables is not None, "Program or XML editing error. Extension subtables cannot contain extensions subtables"
+			tables, extTables, done = extTables, None, {}
 
 		# add Coverage table if it is sorted last.
 		sortCoverageLast = 0
@@ -380,7 +348,7 @@
 				if hasattr(item, "name") and (item.name == "Coverage"):
 					sortCoverageLast = 1
 					break
-			if item not in done:
+			if id(item) not in done:
 				item._gatherTables(tables, extTables, done)
 			else:
 				# We're a new parent of item
@@ -395,70 +363,104 @@
 				# we've already 'gathered' it above
 				continue
 
-			if appendExtensions:
-				assert extTables is not None, "Program or XML editing error. Extension subtables cannot contain extensions subtables"
-				newDone = {}
-				item._gatherTables(extTables, None, newDone)
-
-			elif item not in done:
+			if id(item) not in done:
 				item._gatherTables(tables, extTables, done)
 			else:
-				# We're a new parent of item
+				# Item is already written out by other parent
 				pass
 
+		selfTables.append(self)
 
-		tables.append(self)
-		return tables, extTables
-	
+	def getAllData(self):
+		"""Assemble all data, including all subtables."""
+		internedTables = {}
+		self._doneWriting(internedTables)
+		tables = []
+		extTables = []
+		done = {}
+		self._gatherTables(tables, extTables, done)
+		tables.reverse()
+		extTables.reverse()
+		# Gather all data in two passes: the absolute positions of all
+		# subtable are needed before the actual data can be assembled.
+		pos = 0
+		for table in tables:
+			table.pos = pos
+			pos = pos + table.getDataLength()
+
+		for table in extTables:
+			table.pos = pos
+			pos = pos + table.getDataLength()
+
+		data = []
+		for table in tables:
+			tableData = table.getData()
+			data.append(tableData)
+
+		for table in extTables:
+			tableData = table.getData()
+			data.append(tableData)
+
+		return bytesjoin(data)
+
 	# interface for gathering data, as used by table.compile()
-	
+
 	def getSubWriter(self):
-		subwriter = self.__class__(self.globalState, self.localState)
+		subwriter = self.__class__(self.localState, self.tableTag)
 		subwriter.parent = self # because some subtables have idential values, we discard
 					# the duplicates under the getAllData method. Hence some
 					# subtable writers can have more than one parent writer.
 					# But we just care about first one right now.
 		return subwriter
-	
+
 	def writeUShort(self, value):
-		assert 0 <= value < 0x10000
+		assert 0 <= value < 0x10000, value
 		self.items.append(struct.pack(">H", value))
-	
+
 	def writeShort(self, value):
+		assert -32768 <= value < 32768, value
 		self.items.append(struct.pack(">h", value))
 
+	def writeUInt8(self, value):
+		assert 0 <= value < 256, value
+		self.items.append(struct.pack(">B", value))
+
+	def writeInt8(self, value):
+		assert -128 <= value < 128, value
+		self.items.append(struct.pack(">b", value))
+
 	def writeUInt24(self, value):
-		assert 0 <= value < 0x1000000
+		assert 0 <= value < 0x1000000, value
 		b = struct.pack(">L", value)
 		self.items.append(b[1:])
-	
+
 	def writeLong(self, value):
 		self.items.append(struct.pack(">l", value))
-	
+
 	def writeULong(self, value):
 		self.items.append(struct.pack(">L", value))
-	
+
 	def writeTag(self, tag):
 		tag = Tag(tag).tobytes()
-		assert len(tag) == 4
+		assert len(tag) == 4, tag
 		self.items.append(tag)
-	
+
 	def writeSubTable(self, subWriter):
 		self.items.append(subWriter)
-	
-	def writeCountReference(self, table, name):
-		ref = CountReference(table, name)
+
+	def writeCountReference(self, table, name, size=2, value=None):
+		ref = CountReference(table, name, size=size, value=value)
 		self.items.append(ref)
 		return ref
-	
+
 	def writeStruct(self, format, values):
 		data = struct.pack(*(format,) + values)
 		self.items.append(data)
-	
+
 	def writeData(self, data):
 		self.items.append(data)
 
-	def	getOverflowErrorRecord(self, item):
+	def getOverflowErrorRecord(self, item):
 		LookupListIndex = SubTableIndex = itemName = itemIndex = None
 		if self.name == 'LookupList':
 			LookupListIndex = item.repeatIndex
@@ -466,7 +468,7 @@
 			LookupListIndex = self.repeatIndex
 			SubTableIndex = item.repeatIndex
 		else:
-			itemName = item.name
+			itemName = getattr(item, 'name', '<none>')
 			if hasattr(item, 'repeatIndex'):
 				itemIndex = item.repeatIndex
 			if self.name == 'SubTable':
@@ -476,10 +478,10 @@
 				LookupListIndex = self.parent.parent.repeatIndex
 				SubTableIndex = self.parent.repeatIndex
 			else: # who knows how far below the SubTable level we are! Climb back up to the nearest subtable.
-				itemName = ".".join([self.name, item.name])
+				itemName = ".".join([self.name, itemName])
 				p1 = self.parent
 				while p1 and p1.name not in ['ExtSubTable', 'SubTable']:
-					itemName = ".".join([p1.name, item.name])
+					itemName = ".".join([p1.name, itemName])
 					p1 = p1.parent
 				if p1:
 					if p1.name == 'ExtSubTable':
@@ -489,14 +491,17 @@
 						LookupListIndex = p1.parent.repeatIndex
 						SubTableIndex = p1.repeatIndex
 
-		return OverflowErrorRecord( (self.globalState.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) )
+		return OverflowErrorRecord( (self.tableTag, LookupListIndex, SubTableIndex, itemName, itemIndex) )
 
 
 class CountReference(object):
 	"""A reference to a Count value, not a count of references."""
-	def __init__(self, table, name):
+	def __init__(self, table, name, size=None, value=None):
 		self.table = table
 		self.name = name
+		self.size = size
+		if value is not None:
+			self.setValue(value)
 	def setValue(self, value):
 		table = self.table
 		name = self.name
@@ -505,13 +510,17 @@
 		else:
 			assert table[name] == value, (name, table[name], value)
 	def getCountData(self):
-		return packUShort(self.table[self.name])
+		v = self.table[self.name]
+		if v is None: v = 0
+		return {1:packUInt8, 2:packUShort, 4:packULong}[self.size](v)
 
 
+def packUInt8 (value):
+	return struct.pack(">B", value)
+
 def packUShort(value):
 	return struct.pack(">H", value)
 
-
 def packULong(value):
 	assert 0 <= value < 0x100000000, value
 	return struct.pack(">L", value)
@@ -519,6 +528,8 @@
 
 class BaseTable(object):
 
+	"""Generic base class for all OpenType (sub)tables."""
+
 	def __getattr__(self, attr):
 		reader = self.__dict__.get("reader")
 		if reader:
@@ -530,49 +541,6 @@
 
 		raise AttributeError(attr)
 
-	"""Generic base class for all OpenType (sub)tables."""
-	
-	def getConverters(self):
-		return self.converters
-	
-	def getConverterByName(self, name):
-		return self.convertersByName[name]
-	
-	def decompile(self, reader, font):
-		self.readFormat(reader)
-		table = {}
-		self.__rawTable = table  # for debugging
-		converters = self.getConverters()
-		for conv in converters:
-			if conv.name == "SubTable":
-				conv = conv.getConverter(reader.globalState.tableType,
-						table["LookupType"])
-			if conv.name == "ExtSubTable":
-				conv = conv.getConverter(reader.globalState.tableType,
-						table["ExtensionLookupType"])
-			if conv.name == "FeatureParams":
-				conv = conv.getConverter(reader["FeatureTag"])
-			if conv.repeat:
-				l = []
-				if conv.repeat in table:
-					countValue = table[conv.repeat]
-				else:
-					# conv.repeat is a propagated count
-					countValue = reader[conv.repeat]
-				for i in range(countValue + conv.aux):
-					l.append(conv.read(reader, font, table))
-				table[conv.name] = l
-			else:
-				if conv.aux and not eval(conv.aux, None, table):
-					continue
-				table[conv.name] = conv.read(reader, font, table)
-				if conv.isPropagated:
-					reader[conv.name] = table[conv.name]
-
-		self.postRead(table, font)
-
-		del self.__rawTable  # succeeded, get rid of debugging info
-
 	def ensureDecompiled(self):
 		reader = self.__dict__.get("reader")
 		if reader:
@@ -581,30 +549,135 @@
 			del self.font
 			self.decompile(reader, font)
 
+	@classmethod
+	def getRecordSize(cls, reader):
+		totalSize = 0
+		for conv in cls.converters:
+			size = conv.getRecordSize(reader)
+			if size is NotImplemented: return NotImplemented
+			countValue = 1
+			if conv.repeat:
+				if conv.repeat in reader:
+					countValue = reader[conv.repeat]
+				else:
+					return NotImplemented
+			totalSize += size * countValue
+		return totalSize
+
+	def getConverters(self):
+		return self.converters
+
+	def getConverterByName(self, name):
+		return self.convertersByName[name]
+
+	def populateDefaults(self, propagator=None):
+		for conv in self.getConverters():
+			if conv.repeat:
+				if not hasattr(self, conv.name):
+					setattr(self, conv.name, [])
+				countValue = len(getattr(self, conv.name)) - conv.aux
+				try:
+					count_conv = self.getConverterByName(conv.repeat)
+					setattr(self, conv.repeat, countValue)
+				except KeyError:
+					# conv.repeat is a propagated count
+					if propagator and conv.repeat in propagator:
+						propagator[conv.repeat].setValue(countValue)
+			else:
+				if conv.aux and not eval(conv.aux, None, self.__dict__):
+					continue
+				if hasattr(self, conv.name):
+					continue # Warn if it should NOT be present?!
+				if hasattr(conv, 'writeNullOffset'):
+					setattr(self, conv.name, None) # Warn?
+				#elif not conv.isCount:
+				#	# Warn?
+				#	pass
+
+	def decompile(self, reader, font):
+		self.readFormat(reader)
+		table = {}
+		self.__rawTable = table  # for debugging
+		for conv in self.getConverters():
+			if conv.name == "SubTable":
+				conv = conv.getConverter(reader.tableTag,
+						table["LookupType"])
+			if conv.name == "ExtSubTable":
+				conv = conv.getConverter(reader.tableTag,
+						table["ExtensionLookupType"])
+			if conv.name == "FeatureParams":
+				conv = conv.getConverter(reader["FeatureTag"])
+			if conv.name == "SubStruct":
+				conv = conv.getConverter(reader.tableTag,
+				                         table["MorphType"])
+			try:
+				if conv.repeat:
+					if isinstance(conv.repeat, int):
+						countValue = conv.repeat
+					elif conv.repeat in table:
+						countValue = table[conv.repeat]
+					else:
+						# conv.repeat is a propagated count
+						countValue = reader[conv.repeat]
+					countValue += conv.aux
+					table[conv.name] = conv.readArray(reader, font, table, countValue)
+				else:
+					if conv.aux and not eval(conv.aux, None, table):
+						continue
+					table[conv.name] = conv.read(reader, font, table)
+					if conv.isPropagated:
+						reader[conv.name] = table[conv.name]
+			except Exception as e:
+				name = conv.name
+				e.args = e.args + (name,)
+				raise
+
+		if hasattr(self, 'postRead'):
+			self.postRead(table, font)
+		else:
+			self.__dict__.update(table)
+
+		del self.__rawTable  # succeeded, get rid of debugging info
+
 	def compile(self, writer, font):
 		self.ensureDecompiled()
-		table = self.preWrite(font)
+		if hasattr(self, 'preWrite'):
+			table = self.preWrite(font)
+		else:
+			table = self.__dict__.copy()
+
 
 		if hasattr(self, 'sortCoverageLast'):
 			writer.sortCoverageLast = 1
 
+		if hasattr(self, 'DontShare'):
+			writer.DontShare = True
+
 		if hasattr(self.__class__, 'LookupType'):
 			writer['LookupType'].setValue(self.__class__.LookupType)
 
 		self.writeFormat(writer)
 		for conv in self.getConverters():
-			value = table.get(conv.name)
+			value = table.get(conv.name) # TODO Handle defaults instead of defaulting to None!
 			if conv.repeat:
 				if value is None:
 					value = []
 				countValue = len(value) - conv.aux
-				if conv.repeat in table:
-					CountReference(table, conv.repeat).setValue(countValue)
+				if isinstance(conv.repeat, int):
+					assert len(value) == conv.repeat, 'expected %d values, got %d' % (conv.repeat, len(value))
+				elif conv.repeat in table:
+					CountReference(table, conv.repeat, value=countValue)
 				else:
 					# conv.repeat is a propagated count
 					writer[conv.repeat].setValue(countValue)
-				for i in range(len(value)):
-					conv.write(writer, font, table, value[i], i)
+				values = value
+				for i, value in enumerate(values):
+					try:
+						conv.write(writer, font, table, value, i)
+					except Exception as e:
+						name = value.__class__.__name__ if value is not None else conv.name
+						e.args = e.args + (name+'['+str(i)+']',)
+						raise
 			elif conv.isCount:
 				# Special-case Count values.
 				# Assumption: a Count field will *always* precede
@@ -613,33 +686,36 @@
 				# table. We will later store it here.
 				# We add a reference: by the time the data is assembled
 				# the Count value will be filled in.
-				ref = writer.writeCountReference(table, conv.name)
+				ref = writer.writeCountReference(table, conv.name, conv.staticSize)
 				table[conv.name] = None
 				if conv.isPropagated:
 					writer[conv.name] = ref
 			elif conv.isLookupType:
-				ref = writer.writeCountReference(table, conv.name)
-				table[conv.name] = None
+				# We make sure that subtables have the same lookup type,
+				# and that the type is the same as the one set on the
+				# Lookup object, if any is set.
+				if conv.name not in table:
+					table[conv.name] = None
+				ref = writer.writeCountReference(table, conv.name, conv.staticSize, table[conv.name])
 				writer['LookupType'] = ref
 			else:
 				if conv.aux and not eval(conv.aux, None, table):
 					continue
-				conv.write(writer, font, table, value)
+				try:
+					conv.write(writer, font, table, value)
+				except Exception as e:
+					name = value.__class__.__name__ if value is not None else conv.name
+					e.args = e.args + (name,)
+					raise
 				if conv.isPropagated:
 					writer[conv.name] = value
-	
+
 	def readFormat(self, reader):
 		pass
-	
+
 	def writeFormat(self, writer):
 		pass
-	
-	def postRead(self, table, font):
-		self.__dict__.update(table)
-	
-	def preWrite(self, font):
-		return self.__dict__.copy()
-	
+
 	def toXML(self, xmlWriter, font, attrs=None, name=None):
 		tableName = name if name else self.__class__.__name__
 		if attrs is None:
@@ -651,14 +727,14 @@
 		self.toXML2(xmlWriter, font)
 		xmlWriter.endtag(tableName)
 		xmlWriter.newline()
-	
+
 	def toXML2(self, xmlWriter, font):
 		# Simpler variant of toXML, *only* for the top level tables (like GPOS, GSUB).
 		# This is because in TTX our parent writes our main tag, and in otBase.py we
 		# do it ourselves. I think I'm getting schizophrenic...
 		for conv in self.getConverters():
 			if conv.repeat:
-				value = getattr(self, conv.name)
+				value = getattr(self, conv.name, [])
 				for i in range(len(value)):
 					item = value[i]
 					conv.xmlWrite(xmlWriter, font, item, conv.name,
@@ -666,9 +742,9 @@
 			else:
 				if conv.aux and not eval(conv.aux, None, vars(self)):
 					continue
-				value = getattr(self, conv.name)
+				value = getattr(self, conv.name, None) # TODO Handle defaults instead of defaulting to None!
 				conv.xmlWrite(xmlWriter, font, value, conv.name, [])
-	
+
 	def fromXML(self, name, attrs, content, font):
 		try:
 			conv = self.getConverterByName(name)
@@ -683,9 +759,11 @@
 			seq.append(value)
 		else:
 			setattr(self, conv.name, value)
-	
+
 	def __ne__(self, other):
-		return not self.__eq__(other)
+		result = self.__eq__(other)
+		return result if result is NotImplemented else not result
+
 	def __eq__(self, other):
 		if type(self) != type(other):
 			return NotImplemented
@@ -697,25 +775,28 @@
 
 
 class FormatSwitchingBaseTable(BaseTable):
-	
+
 	"""Minor specialization of BaseTable, for tables that have multiple
 	formats, eg. CoverageFormat1 vs. CoverageFormat2."""
-	
+
+	@classmethod
+	def getRecordSize(cls, reader):
+		return NotImplemented
+
 	def getConverters(self):
-		return self.converters[self.Format]
-	
+		return self.converters.get(self.Format, [])
+
 	def getConverterByName(self, name):
 		return self.convertersByName[self.Format][name]
-	
+
 	def readFormat(self, reader):
 		self.Format = reader.readUShort()
-		assert self.Format != 0, (self, reader.pos, len(reader.data))
-	
+
 	def writeFormat(self, writer):
 		writer.writeUShort(self.Format)
 
 	def toXML(self, xmlWriter, font, attrs=None, name=None):
-		BaseTable.toXML(self, xmlWriter, font, attrs, name=self.__class__.__name__)
+		BaseTable.toXML(self, xmlWriter, font, attrs, name)
 
 
 #
@@ -727,24 +808,24 @@
 #
 
 valueRecordFormat = [
-#	Mask	 Name            isDevice  signed
-	(0x0001, "XPlacement",   0,        1),
-	(0x0002, "YPlacement",   0,        1),
-	(0x0004, "XAdvance",     0,        1),
-	(0x0008, "YAdvance",     0,        1),
-	(0x0010, "XPlaDevice",   1,        0),
-	(0x0020, "YPlaDevice",   1,        0),
-	(0x0040, "XAdvDevice",   1,        0),
-	(0x0080, "YAdvDevice",   1,        0),
-# 	reserved:
-	(0x0100, "Reserved1",    0,        0),
-	(0x0200, "Reserved2",    0,        0),
-	(0x0400, "Reserved3",    0,        0),
-	(0x0800, "Reserved4",    0,        0),
-	(0x1000, "Reserved5",    0,        0),
-	(0x2000, "Reserved6",    0,        0),
-	(0x4000, "Reserved7",    0,        0),
-	(0x8000, "Reserved8",    0,        0),
+#	Mask	 Name		isDevice signed
+	(0x0001, "XPlacement",	0,	1),
+	(0x0002, "YPlacement",	0,	1),
+	(0x0004, "XAdvance",	0,	1),
+	(0x0008, "YAdvance",	0,	1),
+	(0x0010, "XPlaDevice",	1,	0),
+	(0x0020, "YPlaDevice",	1,	0),
+	(0x0040, "XAdvDevice",	1,	0),
+	(0x0080, "YAdvDevice",	1,	0),
+#	reserved:
+	(0x0100, "Reserved1",	0,	0),
+	(0x0200, "Reserved2",	0,	0),
+	(0x0400, "Reserved3",	0,	0),
+	(0x0800, "Reserved4",	0,	0),
+	(0x1000, "Reserved5",	0,	0),
+	(0x2000, "Reserved6",	0,	0),
+	(0x4000, "Reserved7",	0,	0),
+	(0x8000, "Reserved8",	0,	0),
 ]
 
 def _buildDict():
@@ -757,7 +838,7 @@
 
 
 class ValueRecordFactory(object):
-	
+
 	"""Given a format code, this object convert ValueRecords."""
 
 	def __init__(self, valueFormat):
@@ -766,7 +847,10 @@
 			if valueFormat & mask:
 				format.append((name, isDevice, signed))
 		self.format = format
-	
+
+	def __len__(self):
+		return len(self.format)
+
 	def readValueRecord(self, reader, font):
 		format = self.format
 		if not format:
@@ -787,7 +871,7 @@
 					value = None
 			setattr(valueRecord, name, value)
 		return valueRecord
-	
+
 	def writeValueRecord(self, writer, font, valueRecord):
 		for name, isDevice, signed in self.format:
 			value = getattr(valueRecord, name, 0)
@@ -805,15 +889,28 @@
 
 
 class ValueRecord(object):
-	
+
 	# see ValueRecordFactory
-	
+
+	def __init__(self, valueFormat=None, src=None):
+		if valueFormat is not None:
+			for mask, name, isDevice, signed in valueRecordFormat:
+				if valueFormat & mask:
+					setattr(self, name, None if isDevice else 0)
+			if src is not None:
+				for key,val in src.__dict__.items():
+					if not hasattr(self, key):
+						continue
+					setattr(self, key, val)
+		elif src is not None:
+			self.__dict__ = src.__dict__.copy()
+
 	def getFormat(self):
 		format = 0
 		for name in self.__dict__.keys():
 			format = format | valueRecordFormatDict[name][0]
 		return format
-	
+
 	def toXML(self, xmlWriter, font, valueName, attrs=None):
 		if attrs is None:
 			simpleItems = []
@@ -833,13 +930,13 @@
 			xmlWriter.newline()
 			for name, deviceRecord in deviceItems:
 				if deviceRecord is not None:
-					deviceRecord.toXML(xmlWriter, font)
+					deviceRecord.toXML(xmlWriter, font, name=name)
 			xmlWriter.endtag(valueName)
 			xmlWriter.newline()
 		else:
 			xmlWriter.simpletag(valueName, simpleItems)
 			xmlWriter.newline()
-	
+
 	def fromXML(self, name, attrs, content, font):
 		from . import otTables
 		for k, v in attrs.items():
@@ -855,9 +952,11 @@
 				name2, attrs2, content2 = elem2
 				value.fromXML(name2, attrs2, content2, font)
 			setattr(self, name, value)
-	
+
 	def __ne__(self, other):
-		return not self.__eq__(other)
+		result = self.__eq__(other)
+		return result if result is NotImplemented else not result
+
 	def __eq__(self, other):
 		if type(self) != type(other):
 			return NotImplemented
diff --git a/Lib/fontTools/ttLib/tables/otConverters.py b/Lib/fontTools/ttLib/tables/otConverters.py
index d6ac461..ac65945 100644
--- a/Lib/fontTools/ttLib/tables/otConverters.py
+++ b/Lib/fontTools/ttLib/tables/otConverters.py
@@ -1,8 +1,22 @@
 from __future__ import print_function, division, absolute_import
 from fontTools.misc.py23 import *
-from fontTools.misc.textTools import safeEval
-from fontTools.misc.fixedTools import fixedToFloat as fi2fl, floatToFixed as fl2fi
-from .otBase import ValueRecordFactory
+from fontTools.misc.fixedTools import (
+	fixedToFloat as fi2fl, floatToFixed as fl2fi, ensureVersionIsLong as fi2ve,
+	versionToFixed as ve2fi)
+from fontTools.misc.textTools import pad, safeEval
+from fontTools.ttLib import getSearchRange
+from .otBase import (CountReference, FormatSwitchingBaseTable,
+                     OTTableReader, OTTableWriter, ValueRecordFactory)
+from .otTables import (lookupTypes, AATStateTable, AATState, AATAction,
+                       ContextualMorphAction, LigatureMorphAction,
+                       MorxSubtable)
+from functools import partial
+import struct
+import logging
+
+
+log = logging.getLogger(__name__)
+istuple = lambda t: isinstance(t, tuple)
 
 
 def buildConverters(tableSpec, tableNamespace):
@@ -16,24 +30,37 @@
 		if name.startswith("ValueFormat"):
 			assert tp == "uint16"
 			converterClass = ValueFormat
-		elif name.endswith("Count") or name.endswith("LookupType"):
-			assert tp == "uint16"
-			converterClass = ComputedUShort
+		elif name.endswith("Count") or name in ("StructLength", "MorphType"):
+			converterClass = {
+				"uint8": ComputedUInt8,
+				"uint16": ComputedUShort,
+				"uint32": ComputedULong,
+			}[tp]
 		elif name == "SubTable":
 			converterClass = SubTable
 		elif name == "ExtSubTable":
 			converterClass = ExtSubTable
+		elif name == "SubStruct":
+			converterClass = SubStruct
 		elif name == "FeatureParams":
 			converterClass = FeatureParams
+		elif name in ("CIDGlyphMapping", "GlyphCIDMapping"):
+			converterClass = StructWithLength
 		else:
-			if not tp in converterMapping:
+			if not tp in converterMapping and '(' not in tp:
 				tableName = tp
 				converterClass = Struct
 			else:
-				converterClass = converterMapping[tp]
-		tableClass = tableNamespace.get(tableName)
-		conv = converterClass(name, repeat, aux, tableClass)
-		if name in ["SubTable", "ExtSubTable"]:
+				converterClass = eval(tp, tableNamespace, converterMapping)
+		if tp in ('MortChain', 'MortSubtable', 'MorxChain'):
+			tableClass = tableNamespace.get(tp)
+		else:
+			tableClass = tableNamespace.get(tableName)
+		if tableClass is not None:
+			conv = converterClass(name, repeat, aux, tableClass=tableClass)
+		else:
+			conv = converterClass(name, repeat, aux)
+		if name in ["SubTable", "ExtSubTable", "SubStruct"]:
 			conv.lookupTypes = tableNamespace['lookupTypes']
 			# also create reverse mapping
 			for t in conv.lookupTypes.values():
@@ -50,32 +77,104 @@
 	return converters, convertersByName
 
 
+class _MissingItem(tuple):
+	__slots__ = ()
+
+
+try:
+	from collections import UserList
+except ImportError:
+	from UserList import UserList
+
+
+class _LazyList(UserList):
+
+	def __getslice__(self, i, j):
+		return self.__getitem__(slice(i, j))
+
+	def __getitem__(self, k):
+		if isinstance(k, slice):
+			indices = range(*k.indices(len(self)))
+			return [self[i] for i in indices]
+		item = self.data[k]
+		if isinstance(item, _MissingItem):
+			self.reader.seek(self.pos + item[0] * self.recordSize)
+			item = self.conv.read(self.reader, self.font, {})
+			self.data[k] = item
+		return item
+
+	def __add__(self, other):
+		if isinstance(other, _LazyList):
+			other = list(other)
+		elif isinstance(other, list):
+			pass
+		else:
+			return NotImplemented
+		return list(self) + other
+
+	def __radd__(self, other):
+		if not isinstance(other, list):
+			return NotImplemented
+		return other + list(self)
+
+
 class BaseConverter(object):
-	
+
 	"""Base class for converter objects. Apart from the constructor, this
 	is an abstract class."""
-	
-	def __init__(self, name, repeat, aux, tableClass):
+
+	def __init__(self, name, repeat, aux, tableClass=None):
 		self.name = name
 		self.repeat = repeat
 		self.aux = aux
 		self.tableClass = tableClass
-		self.isCount = name.endswith("Count")
-		self.isLookupType = name.endswith("LookupType")
-		self.isPropagated = name in ["ClassCount", "Class2Count", "FeatureTag"]
-	
+		self.isCount = name.endswith("Count") or name in ['DesignAxisRecordSize', 'ValueRecordSize']
+		self.isLookupType = name.endswith("LookupType") or name == "MorphType"
+		self.isPropagated = name in ["ClassCount", "Class2Count", "FeatureTag", "SettingsCount", "VarRegionCount", "MappingCount", "RegionAxisCount", 'DesignAxisCount', 'DesignAxisRecordSize', 'AxisValueCount', 'ValueRecordSize']
+
+	def readArray(self, reader, font, tableDict, count):
+		"""Read an array of values from the reader."""
+		lazy = font.lazy and count > 8
+		if lazy:
+			recordSize = self.getRecordSize(reader)
+			if recordSize is NotImplemented:
+				lazy = False
+		if not lazy:
+			l = []
+			for i in range(count):
+				l.append(self.read(reader, font, tableDict))
+			return l
+		else:
+			l = _LazyList()
+			l.reader = reader.copy()
+			l.pos = l.reader.pos
+			l.font = font
+			l.conv = self
+			l.recordSize = recordSize
+			l.extend(_MissingItem([i]) for i in range(count))
+			reader.advance(count * recordSize)
+			return l
+
+	def getRecordSize(self, reader):
+		if hasattr(self, 'staticSize'): return self.staticSize
+		return NotImplemented
+
 	def read(self, reader, font, tableDict):
 		"""Read a value from the reader."""
 		raise NotImplementedError(self)
-	
+
+	def writeArray(self, writer, font, tableDict, values):
+		for i, value in enumerate(values):
+			self.write(writer, font, tableDict, value, i)
+
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		"""Write a value to the writer."""
 		raise NotImplementedError(self)
-	
+
 	def xmlRead(self, attrs, content, font):
 		"""Read a value from XML."""
 		raise NotImplementedError(self)
-	
+
 	def xmlWrite(self, xmlWriter, font, value, name, attrs):
 		"""Write a value to XML."""
 		raise NotImplementedError(self)
@@ -93,130 +192,298 @@
 		return int(attrs["value"], 0)
 
 class Long(IntValue):
+	staticSize = 4
 	def read(self, reader, font, tableDict):
 		return reader.readLong()
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		writer.writeLong(value)
 
-class Version(BaseConverter):
+class ULong(IntValue):
+	staticSize = 4
 	def read(self, reader, font, tableDict):
-		value = reader.readLong()
-		assert (value >> 16) == 1, "Unsupported version 0x%08x" % value
-		return  fi2fl(value, 16)
+		return reader.readULong()
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
-		if value < 0x10000:
-			value = fl2fi(value, 16)
-		value = int(round(value))
-		assert (value >> 16) == 1, "Unsupported version 0x%08x" % value
-		writer.writeLong(value)
-	def xmlRead(self, attrs, content, font):
-		value = attrs["value"]
-		value = float(int(value, 0)) if value.startswith("0") else float(value)
-		if value >= 0x10000:
-			value = fi2fl(value, 16)
-		return value
+		writer.writeULong(value)
+
+class Flags32(ULong):
 	def xmlWrite(self, xmlWriter, font, value, name, attrs):
-		if value >= 0x10000:
-			value = fi2fl(value, 16)
-		if value % 1 != 0:
-			# Write as hex
-			value = "0x%08x" % fl2fi(value, 16)
-		xmlWriter.simpletag(name, attrs + [("value", value)])
+		xmlWriter.simpletag(name, attrs + [("value", "0x%08X" % value)])
 		xmlWriter.newline()
 
 class Short(IntValue):
+	staticSize = 2
 	def read(self, reader, font, tableDict):
 		return reader.readShort()
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		writer.writeShort(value)
 
 class UShort(IntValue):
+	staticSize = 2
 	def read(self, reader, font, tableDict):
 		return reader.readUShort()
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		writer.writeUShort(value)
 
+class Int8(IntValue):
+	staticSize = 1
+	def read(self, reader, font, tableDict):
+		return reader.readInt8()
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
+		writer.writeInt8(value)
+
+class UInt8(IntValue):
+	staticSize = 1
+	def read(self, reader, font, tableDict):
+		return reader.readUInt8()
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
+		writer.writeUInt8(value)
+
 class UInt24(IntValue):
+	staticSize = 3
 	def read(self, reader, font, tableDict):
 		return reader.readUInt24()
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		writer.writeUInt24(value)
 
-class ComputedUShort(UShort):
+class ComputedInt(IntValue):
 	def xmlWrite(self, xmlWriter, font, value, name, attrs):
-		xmlWriter.comment("%s=%s" % (name, value))
-		xmlWriter.newline()
+		if value is not None:
+			xmlWriter.comment("%s=%s" % (name, value))
+			xmlWriter.newline()
+
+class ComputedUInt8(ComputedInt, UInt8):
+	pass
+class ComputedUShort(ComputedInt, UShort):
+	pass
+class ComputedULong(ComputedInt, ULong):
+	pass
 
 class Tag(SimpleValue):
+	staticSize = 4
 	def read(self, reader, font, tableDict):
 		return reader.readTag()
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		writer.writeTag(value)
 
 class GlyphID(SimpleValue):
+	staticSize = 2
+	def readArray(self, reader, font, tableDict, count):
+		glyphOrder = font.getGlyphOrder()
+		gids = reader.readUShortArray(count)
+		try:
+			l = [glyphOrder[gid] for gid in gids]
+		except IndexError:
+			# Slower, but will not throw an IndexError on an invalid glyph id.
+			l = [font.getGlyphName(gid) for gid in gids]
+		return l
 	def read(self, reader, font, tableDict):
-		value = reader.readUShort()
-		value =  font.getGlyphName(value)
-		return value
-
+		return font.getGlyphName(reader.readUShort())
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
-		value =  font.getGlyphID(value)
-		writer.writeUShort(value)
+		writer.writeUShort(font.getGlyphID(value))
+
+
+class NameID(UShort):
+	def xmlWrite(self, xmlWriter, font, value, name, attrs):
+		xmlWriter.simpletag(name, attrs + [("value", value)])
+		if font and value:
+			nameTable = font.get("name")
+			if nameTable:
+				name = nameTable.getDebugName(value)
+				xmlWriter.write("  ")
+				if name:
+					xmlWriter.comment(name)
+				else:
+					xmlWriter.comment("missing from name table")
+					log.warning("name id %d missing from name table" % value)
+		xmlWriter.newline()
+
 
 class FloatValue(SimpleValue):
 	def xmlRead(self, attrs, content, font):
 		return float(attrs["value"])
 
 class DeciPoints(FloatValue):
+	staticSize = 2
 	def read(self, reader, font, tableDict):
-		value = reader.readUShort()
-		return value / 10
+		return reader.readUShort() / 10
 
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
-		writer.writeUShort(int(round(value * 10)))
+		writer.writeUShort(round(value * 10))
+
+class Fixed(FloatValue):
+	staticSize = 4
+	def read(self, reader, font, tableDict):
+		return  fi2fl(reader.readLong(), 16)
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
+		writer.writeLong(fl2fi(value, 16))
+
+class F2Dot14(FloatValue):
+	staticSize = 2
+	def read(self, reader, font, tableDict):
+		return  fi2fl(reader.readShort(), 14)
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
+		writer.writeShort(fl2fi(value, 14))
+
+class Version(BaseConverter):
+	staticSize = 4
+	def read(self, reader, font, tableDict):
+		value = reader.readLong()
+		assert (value >> 16) == 1, "Unsupported version 0x%08x" % value
+		return value
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
+		value = fi2ve(value)
+		assert (value >> 16) == 1, "Unsupported version 0x%08x" % value
+		writer.writeLong(value)
+	def xmlRead(self, attrs, content, font):
+		value = attrs["value"]
+		value = ve2fi(value)
+		return value
+	def xmlWrite(self, xmlWriter, font, value, name, attrs):
+		value = fi2ve(value)
+		value = "0x%08x" % value
+		xmlWriter.simpletag(name, attrs + [("value", value)])
+		xmlWriter.newline()
+
+	@staticmethod
+	def fromFloat(v):
+		return fl2fi(v, 16)
+
+
+class Char64(SimpleValue):
+	"""An ASCII string with up to 64 characters.
+
+	Unused character positions are filled with 0x00 bytes.
+	Used in Apple AAT fonts in the `gcid` table.
+	"""
+	staticSize = 64
+
+	def read(self, reader, font, tableDict):
+		data = reader.readData(self.staticSize)
+		zeroPos = data.find(b"\0")
+		if zeroPos >= 0:
+			data = data[:zeroPos]
+		s = tounicode(data, encoding="ascii", errors="replace")
+		if s != tounicode(data, encoding="ascii", errors="ignore"):
+			log.warning('replaced non-ASCII characters in "%s"' %
+			            s)
+		return s
+
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
+		data = tobytes(value, encoding="ascii", errors="replace")
+		if data != tobytes(value, encoding="ascii", errors="ignore"):
+			log.warning('replacing non-ASCII characters in "%s"' %
+			            value)
+		if len(data) > self.staticSize:
+			log.warning('truncating overlong "%s" to %d bytes' %
+			            (value, self.staticSize))
+		data = (data + b"\0" * self.staticSize)[:self.staticSize]
+		writer.writeData(data)
+
 
 class Struct(BaseConverter):
-	
+
+	def getRecordSize(self, reader):
+		return self.tableClass and self.tableClass.getRecordSize(reader)
+
 	def read(self, reader, font, tableDict):
 		table = self.tableClass()
 		table.decompile(reader, font)
 		return table
-	
+
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		value.compile(writer, font)
-	
+
 	def xmlWrite(self, xmlWriter, font, value, name, attrs):
 		if value is None:
 			if attrs:
 				# If there are attributes (probably index), then
 				# don't drop this even if it's NULL.  It will mess
 				# up the array indices of the containing element.
-				xmlWriter.simpletag(name, attrs + [("empty", True)])
+				xmlWriter.simpletag(name, attrs + [("empty", 1)])
 				xmlWriter.newline()
 			else:
 				pass # NULL table, ignore
 		else:
 			value.toXML(xmlWriter, font, attrs, name=name)
-	
+
 	def xmlRead(self, attrs, content, font):
-		table = self.tableClass()
-		if attrs.get("empty"):
+		if "empty" in attrs and safeEval(attrs["empty"]):
 			return None
+		table = self.tableClass()
 		Format = attrs.get("Format")
 		if Format is not None:
 			table.Format = int(Format)
+
+		noPostRead = not hasattr(table, 'postRead')
+		if noPostRead:
+			# TODO Cache table.hasPropagated.
+			cleanPropagation = False
+			for conv in table.getConverters():
+				if conv.isPropagated:
+					cleanPropagation = True
+					if not hasattr(font, '_propagator'):
+						font._propagator = {}
+					propagator = font._propagator
+					assert conv.name not in propagator, (conv.name, propagator)
+					setattr(table, conv.name, None)
+					propagator[conv.name] = CountReference(table.__dict__, conv.name)
+
 		for element in content:
 			if isinstance(element, tuple):
 				name, attrs, content = element
 				table.fromXML(name, attrs, content, font)
 			else:
 				pass
+
+		table.populateDefaults(propagator=getattr(font, '_propagator', None))
+
+		if noPostRead:
+			if cleanPropagation:
+				for conv in table.getConverters():
+					if conv.isPropagated:
+						propagator = font._propagator
+						del propagator[conv.name]
+						if not propagator:
+							del font._propagator
+
 		return table
 
+	def __repr__(self):
+		return "Struct of " + repr(self.tableClass)
+
+
+class StructWithLength(Struct):
+	def read(self, reader, font, tableDict):
+		pos = reader.pos
+		table = self.tableClass()
+		table.decompile(reader, font)
+		reader.seek(pos + table.StructLength)
+		return table
+
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
+		for convIndex, conv in enumerate(value.getConverters()):
+			if conv.name == "StructLength":
+				break
+		lengthIndex = len(writer.items) + convIndex
+		if isinstance(value, FormatSwitchingBaseTable):
+			lengthIndex += 1  # implicit Format field
+		deadbeef = {1:0xDE, 2:0xDEAD, 4:0xDEADBEEF}[conv.staticSize]
+
+		before = writer.getDataLength()
+		value.StructLength = deadbeef
+		value.compile(writer, font)
+		length = writer.getDataLength() - before
+		lengthWriter = writer.getSubWriter()
+		conv.write(lengthWriter, font, tableDict, length)
+		assert(writer.items[lengthIndex] ==
+		       b"\xde\xad\xbe\xef"[:conv.staticSize])
+		writer.items[lengthIndex] = lengthWriter.getAllData()
+
 
 class Table(Struct):
 
 	longOffset = False
+	staticSize = 2
 
 	def readOffset(self, reader):
 		return reader.readUShort()
@@ -226,16 +493,11 @@
 			writer.writeULong(0)
 		else:
 			writer.writeUShort(0)
-	
+
 	def read(self, reader, font, tableDict):
 		offset = self.readOffset(reader)
 		if offset == 0:
 			return None
-		if offset <= 3:
-			# XXX hack to work around buggy pala.ttf
-			print("*** Warning: offset is not 0, yet suspiciously low (%s). table: %s" \
-					% (offset, self.tableClass.__name__))
-			return None
 		table = self.tableClass()
 		reader = reader.getSubReader(offset)
 		if font.lazy:
@@ -244,7 +506,7 @@
 		else:
 			table.decompile(reader, font)
 		return table
-	
+
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		if value is None:
 			self.writeNullOffset(writer)
@@ -260,23 +522,37 @@
 class LTable(Table):
 
 	longOffset = True
+	staticSize = 4
 
 	def readOffset(self, reader):
 		return reader.readULong()
 
 
+# TODO Clean / merge the SubTable and SubStruct
+
+class SubStruct(Struct):
+	def getConverter(self, tableType, lookupType):
+		tableClass = self.lookupTypes[tableType][lookupType]
+		return self.__class__(self.name, self.repeat, self.aux, tableClass)
+
+	def xmlWrite(self, xmlWriter, font, value, name, attrs):
+		super(SubStruct, self).xmlWrite(xmlWriter, font, value, None, attrs)
+
 class SubTable(Table):
 	def getConverter(self, tableType, lookupType):
 		tableClass = self.lookupTypes[tableType][lookupType]
 		return self.__class__(self.name, self.repeat, self.aux, tableClass)
 
+	def xmlWrite(self, xmlWriter, font, value, name, attrs):
+		super(SubTable, self).xmlWrite(xmlWriter, font, value, None, attrs)
 
 class ExtSubTable(LTable, SubTable):
-	
+
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
-		writer.Extension = 1 # actually, mere presence of the field flags it as an Ext Subtable writer.
+		writer.Extension = True # actually, mere presence of the field flags it as an Ext Subtable writer.
 		Table.write(self, writer, font, tableDict, value, repeatIndex)
 
+
 class FeatureParams(Table):
 	def getConverter(self, featureTag):
 		tableClass = self.featureParamTypes.get(featureTag, self.defaultFeatureParams)
@@ -284,7 +560,8 @@
 
 
 class ValueFormat(IntValue):
-	def __init__(self, name, repeat, aux, tableClass):
+	staticSize = 2
+	def __init__(self, name, repeat, aux, tableClass=None):
 		BaseConverter.__init__(self, name, repeat, aux, tableClass)
 		self.which = "ValueFormat" + ("2" if name[-1] == "2" else "1")
 	def read(self, reader, font, tableDict):
@@ -297,6 +574,8 @@
 
 
 class ValueRecord(ValueFormat):
+	def getRecordSize(self, reader):
+		return 2 * len(reader[self.which])
 	def read(self, reader, font, tableDict):
 		return reader[self.which].readValueRecord(reader, font)
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
@@ -313,8 +592,859 @@
 		return value
 
 
+class AATLookup(BaseConverter):
+	BIN_SEARCH_HEADER_SIZE = 10
+
+	def __init__(self, name, repeat, aux, tableClass):
+		BaseConverter.__init__(self, name, repeat, aux, tableClass)
+		if issubclass(self.tableClass, SimpleValue):
+			self.converter = self.tableClass(name='Value', repeat=None, aux=None)
+		else:
+			self.converter = Table(name='Value', repeat=None, aux=None, tableClass=self.tableClass)
+
+	def read(self, reader, font, tableDict):
+		format = reader.readUShort()
+		if format == 0:
+			return self.readFormat0(reader, font)
+		elif format == 2:
+			return self.readFormat2(reader, font)
+		elif format == 4:
+			return self.readFormat4(reader, font)
+		elif format == 6:
+			return self.readFormat6(reader, font)
+		elif format == 8:
+			return self.readFormat8(reader, font)
+		else:
+			assert False, "unsupported lookup format: %d" % format
+
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
+		values = list(sorted([(font.getGlyphID(glyph), val)
+		                      for glyph, val in value.items()]))
+		# TODO: Also implement format 4.
+		formats = list(sorted(filter(None, [
+			self.buildFormat0(writer, font, values),
+			self.buildFormat2(writer, font, values),
+			self.buildFormat6(writer, font, values),
+			self.buildFormat8(writer, font, values),
+		])))
+		# We use the format ID as secondary sort key to make the output
+		# deterministic when multiple formats have same encoded size.
+		dataSize, lookupFormat, writeMethod = formats[0]
+		pos = writer.getDataLength()
+		writeMethod()
+		actualSize = writer.getDataLength() - pos
+		assert actualSize == dataSize, (
+			"AATLookup format %d claimed to write %d bytes, but wrote %d" %
+			(lookupFormat, dataSize, actualSize))
+
+	@staticmethod
+	def writeBinSearchHeader(writer, numUnits, unitSize):
+		writer.writeUShort(unitSize)
+		writer.writeUShort(numUnits)
+		searchRange, entrySelector, rangeShift = \
+			getSearchRange(n=numUnits, itemSize=unitSize)
+		writer.writeUShort(searchRange)
+		writer.writeUShort(entrySelector)
+		writer.writeUShort(rangeShift)
+
+	def buildFormat0(self, writer, font, values):
+		numGlyphs = len(font.getGlyphOrder())
+		if len(values) != numGlyphs:
+			return None
+		valueSize = self.converter.staticSize
+		return (2 + numGlyphs * valueSize, 0,
+			lambda: self.writeFormat0(writer, font, values))
+
+	def writeFormat0(self, writer, font, values):
+		writer.writeUShort(0)
+		for glyphID_, value in values:
+			self.converter.write(
+				writer, font, tableDict=None,
+				value=value, repeatIndex=None)
+
+	def buildFormat2(self, writer, font, values):
+		segStart, segValue = values[0]
+		segEnd = segStart
+		segments = []
+		for glyphID, curValue in values[1:]:
+			if glyphID != segEnd + 1 or curValue != segValue:
+				segments.append((segStart, segEnd, segValue))
+				segStart = segEnd = glyphID
+				segValue = curValue
+			else:
+				segEnd = glyphID
+		segments.append((segStart, segEnd, segValue))
+		valueSize = self.converter.staticSize
+		numUnits, unitSize = len(segments) + 1, valueSize + 4
+		return (2 + self.BIN_SEARCH_HEADER_SIZE + numUnits * unitSize, 2,
+		        lambda: self.writeFormat2(writer, font, segments))
+
+	def writeFormat2(self, writer, font, segments):
+		writer.writeUShort(2)
+		valueSize = self.converter.staticSize
+		numUnits, unitSize = len(segments), valueSize + 4
+		self.writeBinSearchHeader(writer, numUnits, unitSize)
+		for firstGlyph, lastGlyph, value in segments:
+			writer.writeUShort(lastGlyph)
+			writer.writeUShort(firstGlyph)
+			self.converter.write(
+				writer, font, tableDict=None,
+				value=value, repeatIndex=None)
+		writer.writeUShort(0xFFFF)
+		writer.writeUShort(0xFFFF)
+		writer.writeData(b'\x00' * valueSize)
+
+	def buildFormat6(self, writer, font, values):
+		valueSize = self.converter.staticSize
+		numUnits, unitSize = len(values), valueSize + 2
+		return (2 + self.BIN_SEARCH_HEADER_SIZE + (numUnits + 1) * unitSize, 6,
+			lambda: self.writeFormat6(writer, font, values))
+
+	def writeFormat6(self, writer, font, values):
+		writer.writeUShort(6)
+		valueSize = self.converter.staticSize
+		numUnits, unitSize = len(values), valueSize + 2
+		self.writeBinSearchHeader(writer, numUnits, unitSize)
+		for glyphID, value in values:
+			writer.writeUShort(glyphID)
+			self.converter.write(
+				writer, font, tableDict=None,
+				value=value, repeatIndex=None)
+		writer.writeUShort(0xFFFF)
+		writer.writeData(b'\x00' * valueSize)
+
+	def buildFormat8(self, writer, font, values):
+		minGlyphID, maxGlyphID = values[0][0], values[-1][0]
+		if len(values) != maxGlyphID - minGlyphID + 1:
+			return None
+		valueSize = self.converter.staticSize
+		return (6 + len(values) * valueSize, 8,
+                        lambda: self.writeFormat8(writer, font, values))
+
+	def writeFormat8(self, writer, font, values):
+		firstGlyphID = values[0][0]
+		writer.writeUShort(8)
+		writer.writeUShort(firstGlyphID)
+		writer.writeUShort(len(values))
+		for _, value in values:
+			self.converter.write(
+				writer, font, tableDict=None,
+				value=value, repeatIndex=None)
+
+	def readFormat0(self, reader, font):
+		numGlyphs = len(font.getGlyphOrder())
+		data = self.converter.readArray(
+			reader, font, tableDict=None, count=numGlyphs)
+		return {font.getGlyphName(k): value
+		        for k, value in enumerate(data)}
+
+	def readFormat2(self, reader, font):
+		mapping = {}
+		pos = reader.pos - 2  # start of table is at UShort for format
+		unitSize, numUnits = reader.readUShort(), reader.readUShort()
+		assert unitSize >= 4 + self.converter.staticSize, unitSize
+		for i in range(numUnits):
+			reader.seek(pos + i * unitSize + 12)
+			last = reader.readUShort()
+			first = reader.readUShort()
+			value = self.converter.read(reader, font, tableDict=None)
+			if last != 0xFFFF:
+				for k in range(first, last + 1):
+					mapping[font.getGlyphName(k)] = value
+		return mapping
+
+	def readFormat4(self, reader, font):
+		mapping = {}
+		pos = reader.pos - 2  # start of table is at UShort for format
+		unitSize = reader.readUShort()
+		assert unitSize >= 6, unitSize
+		for i in range(reader.readUShort()):
+			reader.seek(pos + i * unitSize + 12)
+			last = reader.readUShort()
+			first = reader.readUShort()
+			offset = reader.readUShort()
+			if last != 0xFFFF:
+				dataReader = reader.getSubReader(0)  # relative to current position
+				dataReader.seek(pos + offset)  # relative to start of table
+				data = self.converter.readArray(
+					dataReader, font, tableDict=None,
+					count=last - first + 1)
+				for k, v in enumerate(data):
+					mapping[font.getGlyphName(first + k)] = v
+		return mapping
+
+	def readFormat6(self, reader, font):
+		mapping = {}
+		pos = reader.pos - 2  # start of table is at UShort for format
+		unitSize = reader.readUShort()
+		assert unitSize >= 2 + self.converter.staticSize, unitSize
+		for i in range(reader.readUShort()):
+			reader.seek(pos + i * unitSize + 12)
+			glyphID = reader.readUShort()
+			value = self.converter.read(
+				reader, font, tableDict=None)
+			if glyphID != 0xFFFF:
+				mapping[font.getGlyphName(glyphID)] = value
+		return mapping
+
+	def readFormat8(self, reader, font):
+		first = reader.readUShort()
+		count = reader.readUShort()
+		data = self.converter.readArray(
+			reader, font, tableDict=None, count=count)
+		return {font.getGlyphName(first + k): value
+		        for (k, value) in enumerate(data)}
+
+	def xmlRead(self, attrs, content, font):
+		value = {}
+		for element in content:
+			if isinstance(element, tuple):
+				name, a, eltContent = element
+				if name == "Lookup":
+					value[a["glyph"]] = self.converter.xmlRead(a, eltContent, font)
+		return value
+
+	def xmlWrite(self, xmlWriter, font, value, name, attrs):
+		xmlWriter.begintag(name, attrs)
+		xmlWriter.newline()
+		for glyph, value in sorted(value.items()):
+			self.converter.xmlWrite(
+				xmlWriter, font, value=value,
+				name="Lookup", attrs=[("glyph", glyph)])
+		xmlWriter.endtag(name)
+		xmlWriter.newline()
+
+
+# The AAT 'ankr' table has an unusual structure: An offset to an AATLookup
+# followed by an offset to a glyph data table. Other than usual, the
+# offsets in the AATLookup are not relative to the beginning of
+# the beginning of the 'ankr' table, but relative to the glyph data table.
+# So, to find the anchor data for a glyph, one needs to add the offset
+# to the data table to the offset found in the AATLookup, and then use
+# the sum of these two offsets to find the actual data.
+class AATLookupWithDataOffset(BaseConverter):
+	def read(self, reader, font, tableDict):
+		lookupOffset = reader.readULong()
+		dataOffset = reader.readULong()
+		lookupReader = reader.getSubReader(lookupOffset)
+		lookup = AATLookup('DataOffsets', None, None, UShort)
+		offsets = lookup.read(lookupReader, font, tableDict)
+		result = {}
+		for glyph, offset in offsets.items():
+			dataReader = reader.getSubReader(offset + dataOffset)
+			item = self.tableClass()
+			item.decompile(dataReader, font)
+			result[glyph] = item
+		return result
+
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
+		# We do not work with OTTableWriter sub-writers because
+		# the offsets in our AATLookup are relative to our data
+		# table, for which we need to provide an offset value itself.
+		# It might have been possible to somehow make a kludge for
+		# performing this indirect offset computation directly inside
+		# OTTableWriter. But this would have made the internal logic
+		# of OTTableWriter even more complex than it already is,
+		# so we decided to roll our own offset computation for the
+		# contents of the AATLookup and associated data table.
+		offsetByGlyph, offsetByData, dataLen = {}, {}, 0
+		compiledData = []
+		for glyph in sorted(value, key=font.getGlyphID):
+			subWriter = OTTableWriter()
+			value[glyph].compile(subWriter, font)
+			data = subWriter.getAllData()
+			offset = offsetByData.get(data, None)
+			if offset == None:
+				offset = dataLen
+				dataLen = dataLen + len(data)
+				offsetByData[data] = offset
+				compiledData.append(data)
+			offsetByGlyph[glyph] = offset
+		# For calculating the offsets to our AATLookup and data table,
+		# we can use the regular OTTableWriter infrastructure.
+		lookupWriter = writer.getSubWriter()
+		lookupWriter.longOffset = True
+		lookup = AATLookup('DataOffsets', None, None, UShort)
+		lookup.write(lookupWriter, font, tableDict, offsetByGlyph, None)
+
+		dataWriter = writer.getSubWriter()
+		dataWriter.longOffset = True
+		writer.writeSubTable(lookupWriter)
+		writer.writeSubTable(dataWriter)
+		for d in compiledData:
+			dataWriter.writeData(d)
+
+	def xmlRead(self, attrs, content, font):
+		lookup = AATLookup('DataOffsets', None, None, self.tableClass)
+		return lookup.xmlRead(attrs, content, font)
+
+	def xmlWrite(self, xmlWriter, font, value, name, attrs):
+		lookup = AATLookup('DataOffsets', None, None, self.tableClass)
+		lookup.xmlWrite(xmlWriter, font, value, name, attrs)
+
+
+class MorxSubtableConverter(BaseConverter):
+	_PROCESSING_ORDERS = {
+		# bits 30 and 28 of morx.CoverageFlags; see morx spec
+		(False, False): "LayoutOrder",
+		(True, False): "ReversedLayoutOrder",
+		(False, True): "LogicalOrder",
+		(True, True): "ReversedLogicalOrder",
+	}
+
+	_PROCESSING_ORDERS_REVERSED = {
+		val: key for key, val in _PROCESSING_ORDERS.items()
+	}
+
+	def __init__(self, name, repeat, aux):
+		BaseConverter.__init__(self, name, repeat, aux)
+
+	def _setTextDirectionFromCoverageFlags(self, flags, subtable):
+		if (flags & 0x20) != 0:
+			subtable.TextDirection = "Any"
+		elif (flags & 0x80) != 0:
+			subtable.TextDirection = "Vertical"
+		else:
+			subtable.TextDirection = "Horizontal"
+
+	def read(self, reader, font, tableDict):
+		pos = reader.pos
+		m = MorxSubtable()
+		m.StructLength = reader.readULong()
+		flags = reader.readUInt8()
+		orderKey = ((flags & 0x40) != 0, (flags & 0x10) != 0)
+		m.ProcessingOrder = self._PROCESSING_ORDERS[orderKey]
+		self._setTextDirectionFromCoverageFlags(flags, m)
+		m.Reserved = reader.readUShort()
+		m.Reserved |= (flags & 0xF) << 16
+		m.MorphType = reader.readUInt8()
+		m.SubFeatureFlags = reader.readULong()
+		tableClass = lookupTypes["morx"].get(m.MorphType)
+		if tableClass is None:
+			assert False, ("unsupported 'morx' lookup type %s" %
+			               m.MorphType)
+		# To decode AAT ligatures, we need to know the subtable size.
+		# The easiest way to pass this along is to create a new reader
+		# that works on just the subtable as its data.
+		headerLength = reader.pos - pos
+		data = reader.data[
+			reader.pos
+			: reader.pos + m.StructLength - headerLength]
+		assert len(data) == m.StructLength - headerLength
+		subReader = OTTableReader(data=data, tableTag=reader.tableTag)
+		m.SubStruct = tableClass()
+		m.SubStruct.decompile(subReader, font)
+		reader.seek(pos + m.StructLength)
+		return m
+
+	def xmlWrite(self, xmlWriter, font, value, name, attrs):
+		xmlWriter.begintag(name, attrs)
+		xmlWriter.newline()
+		xmlWriter.comment("StructLength=%d" % value.StructLength)
+		xmlWriter.newline()
+		xmlWriter.simpletag("TextDirection", value=value.TextDirection)
+		xmlWriter.newline()
+		xmlWriter.simpletag("ProcessingOrder",
+		                    value=value.ProcessingOrder)
+		xmlWriter.newline()
+		if value.Reserved != 0:
+			xmlWriter.simpletag("Reserved",
+			                    value="0x%04x" % value.Reserved)
+			xmlWriter.newline()
+		xmlWriter.comment("MorphType=%d" % value.MorphType)
+		xmlWriter.newline()
+		xmlWriter.simpletag("SubFeatureFlags",
+		                    value="0x%08x" % value.SubFeatureFlags)
+		xmlWriter.newline()
+		value.SubStruct.toXML(xmlWriter, font)
+		xmlWriter.endtag(name)
+		xmlWriter.newline()
+
+	def xmlRead(self, attrs, content, font):
+		m = MorxSubtable()
+		covFlags = 0
+		m.Reserved = 0
+		for eltName, eltAttrs, eltContent in filter(istuple, content):
+			if eltName == "CoverageFlags":
+				# Only in XML from old versions of fonttools.
+				covFlags = safeEval(eltAttrs["value"])
+				orderKey = ((covFlags & 0x40) != 0,
+				            (covFlags & 0x10) != 0)
+				m.ProcessingOrder = self._PROCESSING_ORDERS[
+					orderKey]
+				self._setTextDirectionFromCoverageFlags(
+					covFlags, m)
+			elif eltName == "ProcessingOrder":
+				m.ProcessingOrder = eltAttrs["value"]
+				assert m.ProcessingOrder in self._PROCESSING_ORDERS_REVERSED, "unknown ProcessingOrder: %s" % m.ProcessingOrder
+			elif eltName == "TextDirection":
+				m.TextDirection = eltAttrs["value"]
+				assert m.TextDirection in {"Horizontal", "Vertical", "Any"}, "unknown TextDirection %s" % m.TextDirection
+			elif eltName == "Reserved":
+				m.Reserved = safeEval(eltAttrs["value"])
+			elif eltName == "SubFeatureFlags":
+				m.SubFeatureFlags = safeEval(eltAttrs["value"])
+			elif eltName.endswith("Morph"):
+				m.fromXML(eltName, eltAttrs, eltContent, font)
+			else:
+				assert False, eltName
+		m.Reserved = (covFlags & 0xF) << 16 | m.Reserved
+		return m
+
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
+		covFlags = (value.Reserved & 0x000F0000) >> 16
+		reverseOrder, logicalOrder = self._PROCESSING_ORDERS_REVERSED[
+			value.ProcessingOrder]
+		covFlags |= 0x80 if value.TextDirection == "Vertical" else 0
+		covFlags |= 0x40 if reverseOrder else 0
+		covFlags |= 0x20 if value.TextDirection == "Any" else 0
+		covFlags |= 0x10 if logicalOrder else 0
+		value.CoverageFlags = covFlags
+		lengthIndex = len(writer.items)
+		before = writer.getDataLength()
+		value.StructLength = 0xdeadbeef
+		# The high nibble of value.Reserved is actuallly encoded
+		# into coverageFlags, so we need to clear it here.
+		origReserved = value.Reserved # including high nibble
+		value.Reserved = value.Reserved & 0xFFFF # without high nibble
+		value.compile(writer, font)
+		value.Reserved = origReserved  # restore original value
+		assert writer.items[lengthIndex] == b"\xde\xad\xbe\xef"
+		length = writer.getDataLength() - before
+		writer.items[lengthIndex] = struct.pack(">L", length)
+
+
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6Tables.html#ExtendedStateHeader
+# TODO: Untangle the implementation of the various lookup-specific formats.
+class STXHeader(BaseConverter):
+	def __init__(self, name, repeat, aux, tableClass):
+		BaseConverter.__init__(self, name, repeat, aux, tableClass)
+		assert issubclass(self.tableClass, AATAction)
+		self.classLookup = AATLookup("GlyphClasses", None, None, UShort)
+		if issubclass(self.tableClass, ContextualMorphAction):
+			self.perGlyphLookup = AATLookup("PerGlyphLookup",
+			                                None, None, GlyphID)
+		else:
+			self.perGlyphLookup = None
+
+	def read(self, reader, font, tableDict):
+		table = AATStateTable()
+		pos = reader.pos
+		classTableReader = reader.getSubReader(0)
+		stateArrayReader = reader.getSubReader(0)
+		entryTableReader = reader.getSubReader(0)
+		actionReader = None
+		ligaturesReader = None
+		table.GlyphClassCount = reader.readULong()
+		classTableReader.seek(pos + reader.readULong())
+		stateArrayReader.seek(pos + reader.readULong())
+		entryTableReader.seek(pos + reader.readULong())
+		if self.perGlyphLookup is not None:
+			perGlyphTableReader = reader.getSubReader(0)
+			perGlyphTableReader.seek(pos + reader.readULong())
+		if issubclass(self.tableClass, LigatureMorphAction):
+			actionReader = reader.getSubReader(0)
+			actionReader.seek(pos + reader.readULong())
+			ligComponentReader = reader.getSubReader(0)
+			ligComponentReader.seek(pos + reader.readULong())
+			ligaturesReader = reader.getSubReader(0)
+			ligaturesReader.seek(pos + reader.readULong())
+			numLigComponents = (ligaturesReader.pos
+			                    - ligComponentReader.pos) // 2
+			assert numLigComponents >= 0
+			table.LigComponents = \
+				ligComponentReader.readUShortArray(numLigComponents)
+			table.Ligatures = self._readLigatures(ligaturesReader, font)
+		table.GlyphClasses = self.classLookup.read(classTableReader,
+		                                           font, tableDict)
+		numStates = int((entryTableReader.pos - stateArrayReader.pos)
+		                 / (table.GlyphClassCount * 2))
+		for stateIndex in range(numStates):
+			state = AATState()
+			table.States.append(state)
+			for glyphClass in range(table.GlyphClassCount):
+				entryIndex = stateArrayReader.readUShort()
+				state.Transitions[glyphClass] = \
+					self._readTransition(entryTableReader,
+					                     entryIndex, font,
+					                     actionReader)
+		if self.perGlyphLookup is not None:
+			table.PerGlyphLookups = self._readPerGlyphLookups(
+				table, perGlyphTableReader, font)
+		return table
+
+	def _readTransition(self, reader, entryIndex, font, actionReader):
+		transition = self.tableClass()
+		entryReader = reader.getSubReader(
+			reader.pos + entryIndex * transition.staticSize)
+		transition.decompile(entryReader, font, actionReader)
+		return transition
+
+	def _readLigatures(self, reader, font):
+		limit = len(reader.data)
+		numLigatureGlyphs = (limit - reader.pos) // 2
+		return [font.getGlyphName(g)
+		        for g in reader.readUShortArray(numLigatureGlyphs)]
+
+	def _countPerGlyphLookups(self, table):
+		# Somewhat annoyingly, the morx table does not encode
+		# the size of the per-glyph table. So we need to find
+		# the maximum value that MorphActions use as index
+		# into this table.
+		numLookups = 0
+		for state in table.States:
+			for t in state.Transitions.values():
+				if isinstance(t, ContextualMorphAction):
+					if t.MarkIndex != 0xFFFF:
+						numLookups = max(
+							numLookups,
+							t.MarkIndex + 1)
+					if t.CurrentIndex != 0xFFFF:
+						numLookups = max(
+							numLookups,
+							t.CurrentIndex + 1)
+		return numLookups
+
+	def _readPerGlyphLookups(self, table, reader, font):
+		pos = reader.pos
+		lookups = []
+		for _ in range(self._countPerGlyphLookups(table)):
+			lookupReader = reader.getSubReader(0)
+			lookupReader.seek(pos + reader.readULong())
+			lookups.append(
+				self.perGlyphLookup.read(lookupReader, font, {}))
+		return lookups
+
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
+		glyphClassWriter = OTTableWriter()
+		self.classLookup.write(glyphClassWriter, font, tableDict,
+		                       value.GlyphClasses, repeatIndex=None)
+		glyphClassData = pad(glyphClassWriter.getAllData(), 4)
+		glyphClassCount = max(value.GlyphClasses.values()) + 1
+		glyphClassTableOffset = 16  # size of STXHeader
+		if self.perGlyphLookup is not None:
+			glyphClassTableOffset += 4
+
+		actionData, actionIndex = None, None
+		if issubclass(self.tableClass, LigatureMorphAction):
+			glyphClassTableOffset += 12
+			actionData, actionIndex = \
+				self._compileLigActions(value, font)
+			actionData = pad(actionData, 4)
+
+		stateArrayData, entryTableData = self._compileStates(
+			font, value.States, glyphClassCount, actionIndex)
+		stateArrayOffset = glyphClassTableOffset + len(glyphClassData)
+		entryTableOffset = stateArrayOffset + len(stateArrayData)
+		perGlyphOffset = entryTableOffset + len(entryTableData)
+		perGlyphData = \
+			pad(self._compilePerGlyphLookups(value, font), 4)
+		ligComponentsData = self._compileLigComponents(value, font)
+		ligaturesData = self._compileLigatures(value, font)
+		if actionData is None:
+			actionOffset = None
+			ligComponentsOffset = None
+			ligaturesOffset = None
+		else:
+			assert len(perGlyphData) == 0
+			actionOffset = entryTableOffset + len(entryTableData)
+			ligComponentsOffset = actionOffset + len(actionData)
+			ligaturesOffset = ligComponentsOffset + len(ligComponentsData)
+		writer.writeULong(glyphClassCount)
+		writer.writeULong(glyphClassTableOffset)
+		writer.writeULong(stateArrayOffset)
+		writer.writeULong(entryTableOffset)
+		if self.perGlyphLookup is not None:
+			writer.writeULong(perGlyphOffset)
+		if actionOffset is not None:
+			writer.writeULong(actionOffset)
+			writer.writeULong(ligComponentsOffset)
+			writer.writeULong(ligaturesOffset)
+		writer.writeData(glyphClassData)
+		writer.writeData(stateArrayData)
+		writer.writeData(entryTableData)
+		writer.writeData(perGlyphData)
+		if actionData is not None:
+			writer.writeData(actionData)
+		if ligComponentsData is not None:
+			writer.writeData(ligComponentsData)
+		if ligaturesData is not None:
+			writer.writeData(ligaturesData)
+
+	def _compileStates(self, font, states, glyphClassCount, actionIndex):
+		stateArrayWriter = OTTableWriter()
+		entries, entryIDs = [], {}
+		for state in states:
+			for glyphClass in range(glyphClassCount):
+				transition = state.Transitions[glyphClass]
+				entryWriter = OTTableWriter()
+				transition.compile(entryWriter, font,
+				                   actionIndex)
+				entryData = entryWriter.getAllData()
+				assert len(entryData)  == transition.staticSize, ( \
+					"%s has staticSize %d, "
+					"but actually wrote %d bytes" % (
+						repr(transition),
+						transition.staticSize,
+						len(entryData)))
+				entryIndex = entryIDs.get(entryData)
+				if entryIndex is None:
+					entryIndex = len(entries)
+					entryIDs[entryData] = entryIndex
+					entries.append(entryData)
+				stateArrayWriter.writeUShort(entryIndex)
+		stateArrayData = pad(stateArrayWriter.getAllData(), 4)
+		entryTableData = pad(bytesjoin(entries), 4)
+		return stateArrayData, entryTableData
+
+	def _compilePerGlyphLookups(self, table, font):
+		if self.perGlyphLookup is None:
+			return b""
+		numLookups = self._countPerGlyphLookups(table)
+		assert len(table.PerGlyphLookups) == numLookups, (
+			"len(AATStateTable.PerGlyphLookups) is %d, "
+			"but the actions inside the table refer to %d" %
+				(len(table.PerGlyphLookups), numLookups))
+		writer = OTTableWriter()
+		for lookup in table.PerGlyphLookups:
+			lookupWriter = writer.getSubWriter()
+			lookupWriter.longOffset = True
+			self.perGlyphLookup.write(lookupWriter, font,
+			                          {}, lookup, None)
+			writer.writeSubTable(lookupWriter)
+		return writer.getAllData()
+
+	def _compileLigActions(self, table, font):
+		assert issubclass(self.tableClass, LigatureMorphAction)
+		actions = set()
+		for state in table.States:
+			for _glyphClass, trans in state.Transitions.items():
+				actions.add(trans.compileLigActions())
+		result, actionIndex = b"", {}
+		# Sort the compiled actions in decreasing order of
+		# length, so that the longer sequence come before the
+		# shorter ones.  For each compiled action ABCD, its
+		# suffixes BCD, CD, and D do not be encoded separately
+		# (in case they occur); instead, we can just store an
+		# index that points into the middle of the longer
+		# sequence. Every compiled AAT ligature sequence is
+		# terminated with an end-of-sequence flag, which can
+		# only be set on the last element of the sequence.
+		# Therefore, it is sufficient to consider just the
+		# suffixes.
+		for a in sorted(actions, key=lambda x:(-len(x), x)):
+			if a not in actionIndex:
+				for i in range(0, len(a), 4):
+					suffix = a[i:]
+					suffixIndex = (len(result) + i) // 4
+					actionIndex.setdefault(
+						suffix, suffixIndex)
+				result += a
+		return (result, actionIndex)
+
+	def _compileLigComponents(self, table, font):
+		if not hasattr(table, "LigComponents"):
+			return None
+		writer = OTTableWriter()
+		for component in table.LigComponents:
+			writer.writeUShort(component)
+		return writer.getAllData()
+
+	def _compileLigatures(self, table, font):
+		if not hasattr(table, "Ligatures"):
+			return None
+		writer = OTTableWriter()
+		for glyphName in table.Ligatures:
+			writer.writeUShort(font.getGlyphID(glyphName))
+		return writer.getAllData()
+
+	def xmlWrite(self, xmlWriter, font, value, name, attrs):
+		xmlWriter.begintag(name, attrs)
+		xmlWriter.newline()
+		xmlWriter.comment("GlyphClassCount=%s" %value.GlyphClassCount)
+		xmlWriter.newline()
+		for g, klass in sorted(value.GlyphClasses.items()):
+			xmlWriter.simpletag("GlyphClass", glyph=g, value=klass)
+			xmlWriter.newline()
+		for stateIndex, state in enumerate(value.States):
+			xmlWriter.begintag("State", index=stateIndex)
+			xmlWriter.newline()
+			for glyphClass, trans in sorted(state.Transitions.items()):
+				trans.toXML(xmlWriter, font=font,
+				            attrs={"onGlyphClass": glyphClass},
+				            name="Transition")
+			xmlWriter.endtag("State")
+			xmlWriter.newline()
+		for i, lookup in enumerate(value.PerGlyphLookups):
+			xmlWriter.begintag("PerGlyphLookup", index=i)
+			xmlWriter.newline()
+			for glyph, val in sorted(lookup.items()):
+				xmlWriter.simpletag("Lookup", glyph=glyph,
+				                    value=val)
+				xmlWriter.newline()
+			xmlWriter.endtag("PerGlyphLookup")
+			xmlWriter.newline()
+		if hasattr(value, "LigComponents"):
+			xmlWriter.begintag("LigComponents")
+			xmlWriter.newline()
+			for i, val in enumerate(getattr(value, "LigComponents")):
+				xmlWriter.simpletag("LigComponent", index=i,
+				                    value=val)
+				xmlWriter.newline()
+			xmlWriter.endtag("LigComponents")
+			xmlWriter.newline()
+		self._xmlWriteLigatures(xmlWriter, font, value, name, attrs)
+		xmlWriter.endtag(name)
+		xmlWriter.newline()
+
+	def _xmlWriteLigatures(self, xmlWriter, font, value, name, attrs):
+		if not hasattr(value, "Ligatures"):
+			return
+		xmlWriter.begintag("Ligatures")
+		xmlWriter.newline()
+		for i, g in enumerate(getattr(value, "Ligatures")):
+			xmlWriter.simpletag("Ligature", index=i, glyph=g)
+			xmlWriter.newline()
+		xmlWriter.endtag("Ligatures")
+		xmlWriter.newline()
+
+	def xmlRead(self, attrs, content, font):
+		table = AATStateTable()
+		for eltName, eltAttrs, eltContent in filter(istuple, content):
+			if eltName == "GlyphClass":
+				glyph = eltAttrs["glyph"]
+				value = eltAttrs["value"]
+				table.GlyphClasses[glyph] = safeEval(value)
+			elif eltName == "State":
+				state = self._xmlReadState(eltAttrs, eltContent, font)
+				table.States.append(state)
+			elif eltName == "PerGlyphLookup":
+				lookup = self.perGlyphLookup.xmlRead(
+					eltAttrs, eltContent, font)
+				table.PerGlyphLookups.append(lookup)
+			elif eltName == "LigComponents":
+				table.LigComponents = \
+					self._xmlReadLigComponents(
+						eltAttrs, eltContent, font)
+			elif eltName == "Ligatures":
+				table.Ligatures = \
+					self._xmlReadLigatures(
+						eltAttrs, eltContent, font)
+		table.GlyphClassCount = max(table.GlyphClasses.values()) + 1
+		return table
+
+	def _xmlReadState(self, attrs, content, font):
+		state = AATState()
+		for eltName, eltAttrs, eltContent in filter(istuple, content):
+			if eltName == "Transition":
+				glyphClass = safeEval(eltAttrs["onGlyphClass"])
+				transition = self.tableClass()
+				transition.fromXML(eltName, eltAttrs,
+				                   eltContent, font)
+				state.Transitions[glyphClass] = transition
+		return state
+
+	def _xmlReadLigComponents(self, attrs, content, font):
+		ligComponents = []
+		for eltName, eltAttrs, _eltContent in filter(istuple, content):
+			if eltName == "LigComponent":
+				ligComponents.append(
+					safeEval(eltAttrs["value"]))
+		return ligComponents
+
+	def _xmlReadLigatures(self, attrs, content, font):
+		ligs = []
+		for eltName, eltAttrs, _eltContent in filter(istuple, content):
+			if eltName == "Ligature":
+				ligs.append(eltAttrs["glyph"])
+		return ligs
+
+
+class CIDGlyphMap(BaseConverter):
+	def read(self, reader, font, tableDict):
+		numCIDs = reader.readUShort()
+		result = {}
+		for cid, glyphID in enumerate(reader.readUShortArray(numCIDs)):
+			if glyphID != 0xFFFF:
+				result[cid] = font.getGlyphName(glyphID)
+		return result
+
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
+		items = {cid: font.getGlyphID(glyph)
+		         for cid, glyph in value.items()}
+		count = max(items) + 1 if items else 0
+		writer.writeUShort(count)
+		for cid in range(count):
+			writer.writeUShort(items.get(cid, 0xFFFF))
+
+	def xmlRead(self, attrs, content, font):
+		result = {}
+		for eName, eAttrs, _eContent in filter(istuple, content):
+			if eName == "CID":
+				result[safeEval(eAttrs["cid"])] = \
+					eAttrs["glyph"].strip()
+		return result
+
+	def xmlWrite(self, xmlWriter, font, value, name, attrs):
+		xmlWriter.begintag(name, attrs)
+		xmlWriter.newline()
+		for cid, glyph in sorted(value.items()):
+			if glyph is not None and glyph != 0xFFFF:
+				xmlWriter.simpletag(
+					"CID", cid=cid, glyph=glyph)
+				xmlWriter.newline()
+		xmlWriter.endtag(name)
+		xmlWriter.newline()
+
+
+class GlyphCIDMap(BaseConverter):
+	def read(self, reader, font, tableDict):
+		glyphOrder = font.getGlyphOrder()
+		count = reader.readUShort()
+		cids = reader.readUShortArray(count)
+		if count > len(glyphOrder):
+			log.warning("GlyphCIDMap has %d elements, "
+			            "but the font has only %d glyphs; "
+			            "ignoring the rest" %
+			             (count, len(glyphOrder)))
+		result = {}
+		for glyphID in range(min(len(cids), len(glyphOrder))):
+			cid = cids[glyphID]
+			if cid != 0xFFFF:
+				result[glyphOrder[glyphID]] = cid
+		return result
+
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
+		items = {font.getGlyphID(g): cid
+		         for g, cid in value.items()
+		         if cid is not None and cid != 0xFFFF}
+		count = max(items) + 1 if items else 0
+		writer.writeUShort(count)
+		for glyphID in range(count):
+			writer.writeUShort(items.get(glyphID, 0xFFFF))
+
+	def xmlRead(self, attrs, content, font):
+		result = {}
+		for eName, eAttrs, _eContent in filter(istuple, content):
+			if eName == "CID":
+				result[eAttrs["glyph"]] = \
+					safeEval(eAttrs["value"])
+		return result
+
+	def xmlWrite(self, xmlWriter, font, value, name, attrs):
+		xmlWriter.begintag(name, attrs)
+		xmlWriter.newline()
+		for glyph, cid in sorted(value.items()):
+			if cid is not None and cid != 0xFFFF:
+				xmlWriter.simpletag(
+					"CID", glyph=glyph, value=cid)
+				xmlWriter.newline()
+		xmlWriter.endtag(name)
+		xmlWriter.newline()
+
+
 class DeltaValue(BaseConverter):
-	
+
 	def read(self, reader, font, tableDict):
 		StartSize = tableDict["StartSize"]
 		EndSize = tableDict["EndSize"]
@@ -325,7 +1455,7 @@
 		minusOffset = 1 << nBits
 		mask = (1 << nBits) - 1
 		signMask = 1 << (nBits - 1)
-		
+
 		DeltaValue = []
 		tmp, shift = 0, 0
 		for i in range(nItems):
@@ -337,7 +1467,7 @@
 				value = value - minusOffset
 			DeltaValue.append(value)
 		return DeltaValue
-	
+
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		StartSize = tableDict["StartSize"]
 		EndSize = tableDict["EndSize"]
@@ -348,7 +1478,7 @@
 		nBits = 1 << DeltaFormat
 		assert len(DeltaValue) == nItems
 		mask = (1 << nBits) - 1
-		
+
 		tmp, shift = 0, 16
 		for value in DeltaValue:
 			shift = shift - nBits
@@ -358,28 +1488,138 @@
 				tmp, shift = 0, 16
 		if shift != 16:
 			writer.writeUShort(tmp)
-	
+
 	def xmlWrite(self, xmlWriter, font, value, name, attrs):
 		xmlWriter.simpletag(name, attrs + [("value", value)])
 		xmlWriter.newline()
-	
+
+	def xmlRead(self, attrs, content, font):
+		return safeEval(attrs["value"])
+
+
+class VarIdxMapValue(BaseConverter):
+
+	def read(self, reader, font, tableDict):
+		fmt = tableDict['EntryFormat']
+		nItems = tableDict['MappingCount']
+
+		innerBits = 1 + (fmt & 0x000F)
+		innerMask = (1<<innerBits) - 1
+		outerMask = 0xFFFFFFFF - innerMask
+		outerShift = 16 - innerBits
+
+		entrySize = 1 + ((fmt & 0x0030) >> 4)
+		read = {
+			1: reader.readUInt8,
+			2: reader.readUShort,
+			3: reader.readUInt24,
+			4: reader.readULong,
+		}[entrySize]
+
+		mapping = []
+		for i in range(nItems):
+			raw = read()
+			idx = ((raw & outerMask) << outerShift) | (raw & innerMask)
+			mapping.append(idx)
+
+		return mapping
+
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
+		fmt = tableDict['EntryFormat']
+		mapping = value
+		writer['MappingCount'].setValue(len(mapping))
+
+		innerBits = 1 + (fmt & 0x000F)
+		innerMask = (1<<innerBits) - 1
+		outerShift = 16 - innerBits
+
+		entrySize = 1 + ((fmt & 0x0030) >> 4)
+		write = {
+			1: writer.writeUInt8,
+			2: writer.writeUShort,
+			3: writer.writeUInt24,
+			4: writer.writeULong,
+		}[entrySize]
+
+		for idx in mapping:
+			raw = ((idx & 0xFFFF0000) >> outerShift) | (idx & innerMask)
+			write(raw)
+
+
+class VarDataValue(BaseConverter):
+
+	def read(self, reader, font, tableDict):
+		values = []
+
+		regionCount = tableDict["VarRegionCount"]
+		shortCount = tableDict["NumShorts"]
+
+		for i in range(min(regionCount, shortCount)):
+			values.append(reader.readShort())
+		for i in range(min(regionCount, shortCount), regionCount):
+			values.append(reader.readInt8())
+		for i in range(regionCount, shortCount):
+			reader.readInt8()
+
+		return values
+
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
+		regionCount = tableDict["VarRegionCount"]
+		shortCount = tableDict["NumShorts"]
+
+		for i in range(min(regionCount, shortCount)):
+			writer.writeShort(value[i])
+		for i in range(min(regionCount, shortCount), regionCount):
+			writer.writeInt8(value[i])
+		for i in range(regionCount, shortCount):
+			writer.writeInt8(0)
+
+	def xmlWrite(self, xmlWriter, font, value, name, attrs):
+		xmlWriter.simpletag(name, attrs + [("value", value)])
+		xmlWriter.newline()
+
 	def xmlRead(self, attrs, content, font):
 		return safeEval(attrs["value"])
 
 
 converterMapping = {
-	# type         class
-	"int16":       Short,
-	"uint16":      UShort,
-	"uint24":      UInt24,
-	"Version":     Version,
-	"Tag":         Tag,
-	"GlyphID":     GlyphID,
-	"DeciPoints":  DeciPoints,
-	"struct":      Struct,
-	"Offset":      Table,
-	"LOffset":     LTable,
-	"ValueRecord": ValueRecord,
-	"DeltaValue":  DeltaValue,
-}
+	# type		class
+	"int8":		Int8,
+	"int16":	Short,
+	"uint8":	UInt8,
+	"uint8":	UInt8,
+	"uint16":	UShort,
+	"uint24":	UInt24,
+	"uint32":	ULong,
+	"char64":	Char64,
+	"Flags32":	Flags32,
+	"Version":	Version,
+	"Tag":		Tag,
+	"GlyphID":	GlyphID,
+	"NameID":	NameID,
+	"DeciPoints":	DeciPoints,
+	"Fixed":	Fixed,
+	"F2Dot14":	F2Dot14,
+	"struct":	Struct,
+	"Offset":	Table,
+	"LOffset":	LTable,
+	"ValueRecord":	ValueRecord,
+	"DeltaValue":	DeltaValue,
+	"VarIdxMapValue":	VarIdxMapValue,
+	"VarDataValue":	VarDataValue,
 
+	# AAT
+	"CIDGlyphMap":	CIDGlyphMap,
+	"GlyphCIDMap":	GlyphCIDMap,
+	"MortChain":	StructWithLength,
+	"MortSubtable": StructWithLength,
+	"MorxChain":	StructWithLength,
+	"MorxSubtable": MorxSubtableConverter,
+
+	# "Template" types
+	"AATLookup":	lambda C: partial(AATLookup, tableClass=C),
+	"AATLookupWithDataOffset":	lambda C: partial(AATLookupWithDataOffset, tableClass=C),
+	"STXHeader":	lambda C: partial(STXHeader, tableClass=C),
+	"OffsetTo":	lambda C: partial(Table, tableClass=C),
+	"LOffsetTo":	lambda C: partial(LTable, tableClass=C),
+}
diff --git a/Lib/fontTools/ttLib/tables/otData.py b/Lib/fontTools/ttLib/tables/otData.py
old mode 100644
new mode 100755
index 10046d8..a0d0552
--- a/Lib/fontTools/ttLib/tables/otData.py
+++ b/Lib/fontTools/ttLib/tables/otData.py
@@ -1,9 +1,15 @@
+# coding: utf-8
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+
 otData = [
 
 	#
 	# common
 	#
 
+	('LookupOrder', []),
+
 	('ScriptList', [
 		('uint16', 'ScriptCount', None, None, 'Number of ScriptRecords'),
 		('struct', 'ScriptRecord', 'ScriptCount', 0, 'Array of ScriptRecords -listed alphabetically by ScriptTag'),
@@ -54,23 +60,23 @@
 	('FeatureParamsSize', [
 		('DeciPoints', 'DesignSize', None, None, 'The design size in 720/inch units (decipoints).'),
 		('uint16', 'SubfamilyID', None, None, 'Serves as an identifier that associates fonts in a subfamily.'),
-		('uint16', 'SubfamilyNameID', None, None, 'Subfamily NameID.'),
+		('NameID', 'SubfamilyNameID', None, None, 'Subfamily NameID.'),
 		('DeciPoints', 'RangeStart', None, None, 'Small end of recommended usage range (exclusive) in 720/inch units.'),
 		('DeciPoints', 'RangeEnd', None, None, 'Large end of recommended usage range (inclusive) in 720/inch units.'),
 	]),
 
 	('FeatureParamsStylisticSet', [
 		('uint16', 'Version', None, None, 'Set to 0.'),
-		('uint16', 'UINameID', None, None, 'UI NameID.'),
+		('NameID', 'UINameID', None, None, 'UI NameID.'),
 	]),
 
 	('FeatureParamsCharacterVariants', [
 		('uint16', 'Format', None, None, 'Set to 0.'),
-		('uint16', 'FeatUILabelNameID', None, None, 'Feature UI label NameID.'),
-		('uint16', 'FeatUITooltipTextNameID', None, None, 'Feature UI tooltip text NameID.'),
-		('uint16', 'SampleTextNameID', None, None, 'Sample text NameID.'),
+		('NameID', 'FeatUILabelNameID', None, None, 'Feature UI label NameID.'),
+		('NameID', 'FeatUITooltipTextNameID', None, None, 'Feature UI tooltip text NameID.'),
+		('NameID', 'SampleTextNameID', None, None, 'Sample text NameID.'),
 		('uint16', 'NumNamedParameters', None, None, 'Number of named parameters.'),
-		('uint16', 'FirstParamUILabelNameID', None, None, 'First NameID of UI feature parameters.'),
+		('NameID', 'FirstParamUILabelNameID', None, None, 'First NameID of UI feature parameters.'),
 		('uint16', 'CharCount', None, None, 'Count of characters this feature provides glyph variants for.'),
 		('uint24', 'Character', 'CharCount', 0, 'Unicode characters for which this feature provides glyph variants.'),
 	]),
@@ -129,7 +135,7 @@
 		('uint16', 'StartSize', None, None, 'Smallest size to correct-in ppem'),
 		('uint16', 'EndSize', None, None, 'Largest size to correct-in ppem'),
 		('uint16', 'DeltaFormat', None, None, 'Format of DeltaValue array data: 1, 2, or 3'),
-		('DeltaValue', 'DeltaValue', '', 0, 'Array of compressed data'),
+		('DeltaValue', 'DeltaValue', '', 'DeltaFormat in (1,2,3)', 'Array of compressed data'),
 	]),
 
 
@@ -138,10 +144,11 @@
 	#
 
 	('GPOS', [
-		('Version', 'Version', None, None, 'Version of the GPOS table-initially = 0x00010000'),
+		('Version', 'Version', None, None, 'Version of the GPOS table- 0x00010000 or 0x00010001'),
 		('Offset', 'ScriptList', None, None, 'Offset to ScriptList table-from beginning of GPOS table'),
 		('Offset', 'FeatureList', None, None, 'Offset to FeatureList table-from beginning of GPOS table'),
 		('Offset', 'LookupList', None, None, 'Offset to LookupList table-from beginning of GPOS table'),
+		('LOffset', 'FeatureVariations', None, 'Version >= 0x00010001', 'Offset to FeatureVariations table-from beginning of GPOS table'),
 	]),
 
 	('SinglePosFormat1', [
@@ -390,16 +397,16 @@
 		('LOffset', 'ExtSubTable', None, None, 'Offset to SubTable'),
 	]),
 
-	('ValueRecord', [
-		('int16', 'XPlacement', None, None, 'Horizontal adjustment for placement-in design units'),
-		('int16', 'YPlacement', None, None, 'Vertical adjustment for placement-in design units'),
-		('int16', 'XAdvance', None, None, 'Horizontal adjustment for advance-in design units (only used for horizontal writing)'),
-		('int16', 'YAdvance', None, None, 'Vertical adjustment for advance-in design units (only used for vertical writing)'),
-		('Offset', 'XPlaDevice', None, None, 'Offset to Device table for horizontal placement-measured from beginning of PosTable (may be NULL)'),
-		('Offset', 'YPlaDevice', None, None, 'Offset to Device table for vertical placement-measured from beginning of PosTable (may be NULL)'),
-		('Offset', 'XAdvDevice', None, None, 'Offset to Device table for horizontal advance-measured from beginning of PosTable (may be NULL)'),
-		('Offset', 'YAdvDevice', None, None, 'Offset to Device table for vertical advance-measured from beginning of PosTable (may be NULL)'),
-	]),
+#	('ValueRecord', [
+#		('int16', 'XPlacement', None, None, 'Horizontal adjustment for placement-in design units'),
+#		('int16', 'YPlacement', None, None, 'Vertical adjustment for placement-in design units'),
+#		('int16', 'XAdvance', None, None, 'Horizontal adjustment for advance-in design units (only used for horizontal writing)'),
+#		('int16', 'YAdvance', None, None, 'Vertical adjustment for advance-in design units (only used for vertical writing)'),
+#		('Offset', 'XPlaDevice', None, None, 'Offset to Device table for horizontal placement-measured from beginning of PosTable (may be NULL)'),
+#		('Offset', 'YPlaDevice', None, None, 'Offset to Device table for vertical placement-measured from beginning of PosTable (may be NULL)'),
+#		('Offset', 'XAdvDevice', None, None, 'Offset to Device table for horizontal advance-measured from beginning of PosTable (may be NULL)'),
+#		('Offset', 'YAdvDevice', None, None, 'Offset to Device table for vertical advance-measured from beginning of PosTable (may be NULL)'),
+#	]),
 
 	('AnchorFormat1', [
 		('uint16', 'AnchorFormat', None, None, 'Format identifier-format = 1'),
@@ -438,16 +445,17 @@
 	#
 
 	('GSUB', [
-		('Version', 'Version', None, None, 'Version of the GSUB table-initially set to 0x00010000'),
+		('Version', 'Version', None, None, 'Version of the GSUB table- 0x00010000 or 0x00010001'),
 		('Offset', 'ScriptList', None, None, 'Offset to ScriptList table-from beginning of GSUB table'),
 		('Offset', 'FeatureList', None, None, 'Offset to FeatureList table-from beginning of GSUB table'),
 		('Offset', 'LookupList', None, None, 'Offset to LookupList table-from beginning of GSUB table'),
+		('LOffset', 'FeatureVariations', None, 'Version >= 0x00010001', 'Offset to FeatureVariations table-from beginning of GSUB table'),
 	]),
 
 	('SingleSubstFormat1', [
 		('uint16', 'SubstFormat', None, None, 'Format identifier-format = 1'),
 		('Offset', 'Coverage', None, None, 'Offset to Coverage table-from beginning of Substitution table'),
-		('int16', 'DeltaGlyphID', None, None, 'Add to original GlyphID to get substitute GlyphID'),
+		('uint16', 'DeltaGlyphID', None, None, 'Add to original GlyphID modulo 65536 to get substitute GlyphID'),
 	]),
 
 	('SingleSubstFormat2', [
@@ -634,12 +642,13 @@
 	#
 
 	('GDEF', [
-		('Version', 'Version', None, None, 'Version of the GDEF table-initially 0x00010000'),
+		('Version', 'Version', None, None, 'Version of the GDEF table- 0x00010000, 0x00010002, or 0x00010003'),
 		('Offset', 'GlyphClassDef', None, None, 'Offset to class definition table for glyph type-from beginning of GDEF header (may be NULL)'),
 		('Offset', 'AttachList', None, None, 'Offset to list of glyphs with attachment points-from beginning of GDEF header (may be NULL)'),
 		('Offset', 'LigCaretList', None, None, 'Offset to list of positioning points for ligature carets-from beginning of GDEF header (may be NULL)'),
 		('Offset', 'MarkAttachClassDef', None, None, 'Offset to class definition table for mark attachment type-from beginning of GDEF header (may be NULL)'),
-		('Offset', 'MarkGlyphSetsDef', None, 'int(round(Version*0x10000)) >= 0x00010002', 'Offset to the table of mark set definitions-from beginning of GDEF header (may be NULL)'),
+		('Offset', 'MarkGlyphSetsDef', None, 'Version >= 0x00010002', 'Offset to the table of mark set definitions-from beginning of GDEF header (may be NULL)'),
+		('LOffset', 'VarStore', None, 'Version >= 0x00010003', 'Offset to variation store (may be NULL)'),
 	]),
 
 	('AttachList', [
@@ -831,6 +840,193 @@
 		('Offset', 'Lookup', 'LookupCount', 0, 'Array of offsets to GPOS-type lookup tables-from beginning of JstfMax table-in design order'),
 	]),
 
+
+	#
+	# STAT
+	#
+	('STAT', [
+		('Version', 'Version', None, None, 'Version of the table-initially set to 0x00010000, currently 0x00010002.'),
+		('uint16', 'DesignAxisRecordSize', None, None, 'Size in bytes of each design axis record'),
+		('uint16', 'DesignAxisCount', None, None, 'Number of design axis records'),
+		('LOffsetTo(AxisRecordArray)', 'DesignAxisRecord', None, None, 'Offset in bytes from the beginning of the STAT table to the start of the design axes array'),
+		('uint16', 'AxisValueCount', None, None, 'Number of axis value tables'),
+		('LOffsetTo(AxisValueArray)', 'AxisValueArray', None, None, 'Offset in bytes from the beginning of the STAT table to the start of the axes value offset array'),
+		('NameID', 'ElidedFallbackNameID', None, 'Version >= 0x00010001', 'NameID to use when all style attributes are elided.'),
+	]),
+
+	('AxisRecordArray', [
+		('AxisRecord', 'Axis', 'DesignAxisCount', 0, 'Axis records'),
+	]),
+
+	('AxisRecord', [
+		('Tag', 'AxisTag', None, None, 'A tag identifying the axis of design variation'),
+		('NameID', 'AxisNameID', None, None, 'The name ID for entries in the "name" table that provide a display string for this axis'),
+		('uint16', 'AxisOrdering', None, None, 'A value that applications can use to determine primary sorting of face names, or for ordering of descriptors when composing family or face names'),
+		('uint8', 'MoreBytes', 'DesignAxisRecordSize', -8, 'Extra bytes.  Set to empty array.'),
+	]),
+
+	('AxisValueArray', [
+		('Offset', 'AxisValue', 'AxisValueCount', 0, 'Axis values'),
+	]),
+
+	('AxisValueFormat1', [
+		('uint16', 'Format', None, None, 'Format, = 1'),
+		('uint16', 'AxisIndex', None, None, 'Index into the axis record array identifying the axis of design variation to which the axis value record applies.'),
+		('uint16', 'Flags', None, None, 'Flags.'),
+		('NameID', 'ValueNameID', None, None, ''),
+		('Fixed', 'Value', None, None, ''),
+	]),
+
+	('AxisValueFormat2', [
+		('uint16', 'Format', None, None, 'Format, = 2'),
+		('uint16', 'AxisIndex', None, None, 'Index into the axis record array identifying the axis of design variation to which the axis value record applies.'),
+		('uint16', 'Flags', None, None, 'Flags.'),
+		('NameID', 'ValueNameID', None, None, ''),
+		('Fixed', 'NominalValue', None, None, ''),
+		('Fixed', 'RangeMinValue', None, None, ''),
+		('Fixed', 'RangeMaxValue', None, None, ''),
+	]),
+
+	('AxisValueFormat3', [
+		('uint16', 'Format', None, None, 'Format, = 3'),
+		('uint16', 'AxisIndex', None, None, 'Index into the axis record array identifying the axis of design variation to which the axis value record applies.'),
+		('uint16', 'Flags', None, None, 'Flags.'),
+		('NameID', 'ValueNameID', None, None, ''),
+		('Fixed', 'Value', None, None, ''),
+		('Fixed', 'LinkedValue', None, None, ''),
+	]),
+
+	('AxisValueFormat4', [
+		('uint16', 'Format', None, None, 'Format, = 4'),
+		('uint16', 'AxisCount', None, None, 'The total number of axes contributing to this axis-values combination.'),
+		('uint16', 'Flags', None, None, 'Flags.'),
+		('NameID', 'ValueNameID', None, None, ''),
+		('struct', 'AxisValueRecord', 'AxisCount', 0, 'Array of AxisValue records that provide the combination of axis values, one for each contributing axis. '),
+	]),
+
+	('AxisValueRecord', [
+			('uint16', 'AxisIndex', None, None, 'Index into the axis record array identifying the axis of design variation to which the axis value record applies.'),
+			('Fixed', 'Value', None, None, 'A numeric value for this attribute value.'),
+	]),
+
+
+	#
+	# Variation fonts
+	#
+
+	# GSUB/GPOS FeatureVariations
+
+	('FeatureVariations', [
+		('Version', 'Version', None, None, 'Version of the table-initially set to 0x00010000'),
+		('uint32', 'FeatureVariationCount', None, None, 'Number of records in the FeatureVariationRecord array'),
+		('struct', 'FeatureVariationRecord', 'FeatureVariationCount', 0, 'Array of FeatureVariationRecord'),
+	]),
+
+	('FeatureVariationRecord', [
+		('LOffset', 'ConditionSet', None, None, 'Offset to a ConditionSet table, from beginning of the FeatureVariations table.'),
+		('LOffset', 'FeatureTableSubstitution', None, None, 'Offset to a FeatureTableSubstitution table, from beginning of the FeatureVariations table'),
+	]),
+
+	('ConditionSet', [
+		('uint16', 'ConditionCount', None, None, 'Number of condition tables in the ConditionTable array'),
+		('LOffset', 'ConditionTable', 'ConditionCount', 0, 'Array of condition tables.'),
+	]),
+
+	('ConditionTableFormat1', [
+		('uint16', 'Format', None, None, 'Format, = 1'),
+		('uint16', 'AxisIndex', None, None, 'Index for the variation axis within the fvar table, base 0.'),
+		('F2Dot14', 'FilterRangeMinValue', None, None, 'Minimum normalized axis value of the font variation instances that satisfy this condition.'),
+		('F2Dot14', 'FilterRangeMaxValue', None, None, 'Maximum value that satisfies this condition.'),
+	]),
+
+	('FeatureTableSubstitution', [
+		('Version', 'Version', None, None, 'Version of the table-initially set to 0x00010000'),
+		('uint16', 'SubstitutionCount', None, None, 'Number of records in the FeatureVariationRecords array'),
+		('FeatureTableSubstitutionRecord', 'SubstitutionRecord', 'SubstitutionCount', 0, 'Array of FeatureTableSubstitutionRecord'),
+	]),
+
+	('FeatureTableSubstitutionRecord', [
+		('uint16', 'FeatureIndex', None, None, 'The feature table index to match.'),
+		('LOffset', 'Feature', None, None, 'Offset to an alternate feature table, from start of the FeatureTableSubstitution table.'),
+	]),
+
+	# VariationStore
+
+	('VarRegionAxis', [
+		('F2Dot14', 'StartCoord', None, None, ''),
+		('F2Dot14', 'PeakCoord', None, None, ''),
+		('F2Dot14', 'EndCoord', None, None, ''),
+	]),
+
+	('VarRegion', [
+		('struct', 'VarRegionAxis', 'RegionAxisCount', 0, ''),
+	]),
+
+	('VarRegionList', [
+		('uint16', 'RegionAxisCount', None, None, ''),
+		('uint16', 'RegionCount', None, None, ''),
+		('VarRegion', 'Region', 'RegionCount', 0, ''),
+	]),
+
+	('VarData', [
+		('uint16', 'ItemCount', None, None, ''),
+		('uint16', 'NumShorts', None, None, ''),
+		('uint16', 'VarRegionCount', None, None, ''),
+		('uint16', 'VarRegionIndex', 'VarRegionCount', 0, ''),
+		('VarDataValue', 'Item', 'ItemCount', 0, ''),
+	]),
+
+	('VarStore', [
+		('uint16', 'Format', None, None, 'Set to 1.'),
+		('LOffset', 'VarRegionList', None, None, ''),
+		('uint16', 'VarDataCount', None, None, ''),
+		('LOffset', 'VarData', 'VarDataCount', 0, ''),
+	]),
+
+	# Variation helpers
+
+	('VarIdxMap', [
+		('uint16', 'EntryFormat', None, None, ''), # Automatically computed
+		('uint16', 'MappingCount', None, None, ''), # Automatically computed
+		('VarIdxMapValue', 'mapping', '', 0, 'Array of compressed data'),
+	]),
+
+	# Glyph advance variations
+
+	('HVAR', [
+		('Version', 'Version', None, None, 'Version of the HVAR table-initially = 0x00010000'),
+		('LOffset', 'VarStore', None, None, ''),
+		('LOffsetTo(VarIdxMap)', 'AdvWidthMap', None, None, ''),
+		('LOffsetTo(VarIdxMap)', 'LsbMap', None, None, ''),
+		('LOffsetTo(VarIdxMap)', 'RsbMap', None, None, ''),
+	]),
+	('VVAR', [
+		('Version', 'Version', None, None, 'Version of the VVAR table-initially = 0x00010000'),
+		('LOffset', 'VarStore', None, None, ''),
+		('LOffsetTo(VarIdxMap)', 'AdvHeightMap', None, None, ''),
+		('LOffsetTo(VarIdxMap)', 'TsbMap', None, None, ''),
+		('LOffsetTo(VarIdxMap)', 'BsbMap', None, None, ''),
+		('LOffsetTo(VarIdxMap)', 'VOrgMap', None, None, 'Vertical origin mapping.'),
+	]),
+
+	# Font-wide metrics variations
+
+	('MetricsValueRecord', [
+		('Tag', 'ValueTag', None, None, '4-byte font-wide measure identifier'),
+		('uint32', 'VarIdx', None, None, 'Combined outer-inner variation index'),
+		('uint8', 'MoreBytes', 'ValueRecordSize', -8, 'Extra bytes.  Set to empty array.'),
+	]),
+
+	('MVAR', [
+		('Version', 'Version', None, None, 'Version of the MVAR table-initially = 0x00010000'),
+		('uint16', 'Reserved', None, None, 'Set to 0'),
+		('uint16', 'ValueRecordSize', None, None, ''),
+		('uint16', 'ValueRecordCount', None, None, ''),
+		('Offset', 'VarStore', None, None, ''),
+		('MetricsValueRecord', 'ValueRecord', 'ValueRecordCount', 0, ''),
+	]),
+
+
 	#
 	# math
 	#
@@ -979,4 +1175,342 @@
 		('uint16', 'PartFlags', None, None, 'Part qualifiers. PartFlags enumeration currently uses only one bit: 0x0001 fExtender: If set, the part can be skipped or repeated. 0xFFFE Reserved'),
 	]),
 
+
+	##
+	## Apple Advanced Typography (AAT) tables
+	##
+
+	('AATLookupSegment', [
+		('uint16', 'lastGlyph', None, None, 'Last glyph index in this segment.'),
+		('uint16', 'firstGlyph', None, None, 'First glyph index in this segment.'),
+		('uint16', 'value', None, None, 'A 16-bit offset from the start of the table to the data.'),
+	]),
+
+
+	#
+	# ankr
+	#
+
+	('ankr', [
+		('struct', 'AnchorPoints', None, None, 'Anchor points table.'),
+        ]),
+
+	('AnchorPointsFormat0', [
+		('uint16', 'Format', None, None, 'Format of the anchor points table, = 0.'),
+		('uint16', 'Flags', None, None, 'Flags. Currenty unused, set to zero.'),
+		('AATLookupWithDataOffset(AnchorGlyphData)', 'Anchors', None, None, 'Table of with anchor overrides for each glyph.'),
+	]),
+
+	('AnchorGlyphData', [
+		('uint32', 'AnchorPointCount', None, None, 'Number of anchor points for this glyph.'),
+		('struct', 'AnchorPoint', 'AnchorPointCount', 0, 'Individual anchor points.'),
+	]),
+
+	('AnchorPoint', [
+		('int16', 'XCoordinate', None, None, 'X coordinate of this anchor point.'),
+		('int16', 'YCoordinate', None, None, 'Y coordinate of this anchor point.'),
+	]),
+
+	#
+	# bsln
+	#
+
+	('bsln', [
+		('Version', 'Version', None, None, 'Version number of the AAT baseline table (0x00010000 for the initial version).'),
+		('struct', 'Baseline', None, None, 'Baseline table.'),
+	]),
+
+	('BaselineFormat0', [
+		('uint16', 'Format', None, None, 'Format of the baseline table, = 0.'),
+		('uint16', 'DefaultBaseline', None, None, 'Default baseline value for all glyphs. This value can be from 0 through 31.'),
+		('uint16', 'Delta', 32, 0, u'These are the FUnit distance deltas from the font’s natural baseline to the other baselines used in the font. A total of 32 deltas must be assigned.'),
+	]),
+
+	('BaselineFormat1', [
+		('uint16', 'Format', None, None, 'Format of the baseline table, = 1.'),
+		('uint16', 'DefaultBaseline', None, None, 'Default baseline value for all glyphs. This value can be from 0 through 31.'),
+		('uint16', 'Delta', 32, 0, u'These are the FUnit distance deltas from the font’s natural baseline to the other baselines used in the font. A total of 32 deltas must be assigned.'),
+		('AATLookup(uint16)', 'BaselineValues', None, None, 'Lookup table that maps glyphs to their baseline values.'),
+	]),
+
+	('BaselineFormat2', [
+		('uint16', 'Format', None, None, 'Format of the baseline table, = 1.'),
+		('uint16', 'DefaultBaseline', None, None, 'Default baseline value for all glyphs. This value can be from 0 through 31.'),
+		('GlyphID', 'StandardGlyph', None, None, 'Glyph index of the glyph in this font to be used to set the baseline values. This glyph must contain a set of control points (whose numbers are contained in the following field) that determines baseline distances.'),
+		('uint16', 'ControlPoint', 32, 0, 'Array of 32 control point numbers, associated with the standard glyph. A value of 0xFFFF means there is no corresponding control point in the standard glyph.'),
+	]),
+
+	('BaselineFormat3', [
+		('uint16', 'Format', None, None, 'Format of the baseline table, = 1.'),
+		('uint16', 'DefaultBaseline', None, None, 'Default baseline value for all glyphs. This value can be from 0 through 31.'),
+		('GlyphID', 'StandardGlyph', None, None, 'Glyph index of the glyph in this font to be used to set the baseline values. This glyph must contain a set of control points (whose numbers are contained in the following field) that determines baseline distances.'),
+		('uint16', 'ControlPoint', 32, 0, 'Array of 32 control point numbers, associated with the standard glyph. A value of 0xFFFF means there is no corresponding control point in the standard glyph.'),
+		('AATLookup(uint16)', 'BaselineValues', None, None, 'Lookup table that maps glyphs to their baseline values.'),
+	]),
+
+
+	#
+	# cidg
+	#
+
+	('cidg', [
+		('struct', 'CIDGlyphMapping', None, None, 'CID-to-glyph mapping table.'),
+        ]),
+
+	('CIDGlyphMappingFormat0', [
+		('uint16', 'Format', None, None, 'Format of the CID-to-glyph mapping table, = 0.'),
+		('uint16', 'DataFormat', None, None, 'Currenty unused, set to zero.'),
+		('uint32', 'StructLength', None, None, 'Size of the table in bytes.'),
+		('uint16', 'Registry', None, None, 'The registry ID.'),
+		('char64', 'RegistryName', None, None, 'The registry name in ASCII; unused bytes should be set to 0.'),
+		('uint16', 'Order', None, None, 'The order ID.'),
+		('char64', 'OrderName', None, None, 'The order name in ASCII; unused bytes should be set to 0.'),
+		('uint16', 'SupplementVersion', None, None, 'The supplement version.'),
+		('CIDGlyphMap', 'Mapping', None, None, 'A mapping from CIDs to the glyphs in the font, starting with CID 0. If a CID from the identified collection has no glyph in the font, 0xFFFF is used'),
+	]),
+
+
+	#
+	# feat
+	#
+
+	('feat', [
+		('Version', 'Version', None, None, 'Version of the feat table-initially set to 0x00010000.'),
+		('FeatureNames', 'FeatureNames', None, None, 'The feature names.'),
+	]),
+
+	('FeatureNames', [
+		('uint16', 'FeatureNameCount', None, None, 'Number of entries in the feature name array.'),
+		('uint16', 'Reserved1', None, None, 'Reserved (set to zero).'),
+		('uint32', 'Reserved2', None, None, 'Reserved (set to zero).'),
+		('FeatureName', 'FeatureName', 'FeatureNameCount', 0, 'The feature name array.'),
+	]),
+
+	('FeatureName', [
+		('uint16', 'FeatureType', None, None, 'Feature type.'),
+		('uint16', 'SettingsCount', None, None, 'The number of records in the setting name array.'),
+		('LOffset', 'Settings', None, None, 'Offset to setting table for this feature.'),
+		('uint16', 'FeatureFlags', None, None, 'Single-bit flags associated with the feature type.'),
+		('NameID', 'FeatureNameID', None, None, 'The name table index for the feature name.'),
+	]),
+
+	('Settings', [
+		('Setting', 'Setting', 'SettingsCount', 0, 'The setting array.'),
+	]),
+
+	('Setting', [
+		('uint16', 'SettingValue', None, None, 'The setting.'),
+		('NameID', 'SettingNameID', None, None, 'The name table index for the setting name.'),
+	]),
+
+
+	#
+	# gcid
+	#
+
+	('gcid', [
+		('struct', 'GlyphCIDMapping', None, None, 'Glyph to CID mapping table.'),
+        ]),
+
+	('GlyphCIDMappingFormat0', [
+		('uint16', 'Format', None, None, 'Format of the glyph-to-CID mapping table, = 0.'),
+		('uint16', 'DataFormat', None, None, 'Currenty unused, set to zero.'),
+		('uint32', 'StructLength', None, None, 'Size of the table in bytes.'),
+		('uint16', 'Registry', None, None, 'The registry ID.'),
+		('char64', 'RegistryName', None, None, 'The registry name in ASCII; unused bytes should be set to 0.'),
+		('uint16', 'Order', None, None, 'The order ID.'),
+		('char64', 'OrderName', None, None, 'The order name in ASCII; unused bytes should be set to 0.'),
+		('uint16', 'SupplementVersion', None, None, 'The supplement version.'),
+		('GlyphCIDMap', 'Mapping', None, None, 'The CIDs for the glyphs in the font, starting with glyph 0. If a glyph does not correspond to a CID in the identified collection, 0xFFFF is used'),
+	]),
+
+
+	#
+	# lcar
+	#
+
+	('lcar', [
+		('Version', 'Version', None, None, 'Version number of the ligature caret table (0x00010000 for the initial version).'),
+		('struct', 'LigatureCarets', None, None, 'Ligature carets table.'),
+        ]),
+
+	('LigatureCaretsFormat0', [
+		('uint16', 'Format', None, None, 'Format of the ligature caret table. Format 0 indicates division points are distances in font units, Format 1 indicates division points are indexes of control points.'),
+		('AATLookup(LigCaretDistances)', 'Carets', None, None, 'Lookup table associating ligature glyphs with their caret positions, in font unit distances.'),
+	]),
+
+	('LigatureCaretsFormat1', [
+		('uint16', 'Format', None, None, 'Format of the ligature caret table. Format 0 indicates division points are distances in font units, Format 1 indicates division points are indexes of control points.'),
+		('AATLookup(LigCaretPoints)', 'Carets', None, None, 'Lookup table associating ligature glyphs with their caret positions, as control points.'),
+	]),
+
+	('LigCaretDistances', [
+		('uint16', 'DivsionPointCount', None, None, 'Number of division points.'),
+		('int16', 'DivisionPoint', 'DivsionPointCount', 0, 'Distance in font units through which a subdivision is made orthogonally to the baseline.'),
+	]),
+
+	('LigCaretPoints', [
+		('uint16', 'DivsionPointCount', None, None, 'Number of division points.'),
+		('int16', 'DivisionPoint', 'DivsionPointCount', 0, 'The number of the control point through which a subdivision is made orthogonally to the baseline.'),
+	]),
+
+
+	#
+	# mort
+	#
+
+	('mort', [
+		('Version', 'Version', None, None, 'Version of the mort table.'),
+		('uint32', 'MorphChainCount', None, None, 'Number of metamorphosis chains.'),
+		('MortChain', 'MorphChain', 'MorphChainCount', 0, 'Array of metamorphosis chains.'),
+	]),
+
+	('MortChain', [
+		('Flags32', 'DefaultFlags', None, None, 'The default specification for subtables.'),
+		('uint32', 'StructLength', None, None, 'Total byte count, including this header; must be a multiple of 4.'),
+		('uint16', 'MorphFeatureCount', None, None, 'Number of metamorphosis feature entries.'),
+		('uint16', 'MorphSubtableCount', None, None, 'The number of subtables in the chain.'),
+		('struct', 'MorphFeature', 'MorphFeatureCount', 0, 'Array of metamorphosis features.'),
+		('MortSubtable', 'MorphSubtable', 'MorphSubtableCount', 0, 'Array of metamorphosis subtables.'),
+	]),
+
+	('MortSubtable', [
+		('uint16', 'StructLength', None, None, 'Total subtable length, including this header.'),
+		('uint8', 'CoverageFlags', None, None, 'Most significant byte of coverage flags.'),
+		('uint8', 'MorphType', None, None, 'Subtable type.'),
+		('Flags32', 'SubFeatureFlags', None, None, 'The 32-bit mask identifying which subtable this is (the subtable being executed if the AND of this value and the processed defaultFlags is nonzero).'),
+		('SubStruct', 'SubStruct', None, None, 'SubTable.'),
+	]),
+
+	#
+	# morx
+	#
+
+	('morx', [
+		('uint16', 'Version', None, None, 'Version of the morx table.'),
+		('uint16', 'Reserved', None, None, 'Reserved (set to zero).'),
+		('uint32', 'MorphChainCount', None, None, 'Number of extended metamorphosis chains.'),
+		('MorxChain', 'MorphChain', 'MorphChainCount', 0, 'Array of extended metamorphosis chains.'),
+	]),
+
+	('MorxChain', [
+		('Flags32', 'DefaultFlags', None, None, 'The default specification for subtables.'),
+		('uint32', 'StructLength', None, None, 'Total byte count, including this header; must be a multiple of 4.'),
+		('uint32', 'MorphFeatureCount', None, None, 'Number of feature subtable entries.'),
+		('uint32', 'MorphSubtableCount', None, None, 'The number of subtables in the chain.'),
+		('MorphFeature', 'MorphFeature', 'MorphFeatureCount', 0, 'Array of metamorphosis features.'),
+		('MorxSubtable', 'MorphSubtable', 'MorphSubtableCount', 0, 'Array of extended metamorphosis subtables.'),
+	]),
+
+	('MorphFeature', [
+		('uint16', 'FeatureType', None, None, 'The type of feature.'),
+		('uint16', 'FeatureSetting', None, None, "The feature's setting (aka selector)."),
+		('Flags32', 'EnableFlags', None, None, 'Flags for the settings that this feature and setting enables.'),
+		('Flags32', 'DisableFlags', None, None, 'Complement of flags for the settings that this feature and setting disable.'),
+	]),
+
+	# Apple TrueType Reference Manual, chapter “The ‘morx’ table”,
+	# section “Metamorphosis Subtables”.
+	# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html
+	('MorxSubtable', [
+		('uint32', 'StructLength', None, None, 'Total subtable length, including this header.'),
+		('uint8', 'CoverageFlags', None, None, 'Most significant byte of coverage flags.'),
+		('uint16', 'Reserved', None, None, 'Unused.'),
+		('uint8', 'MorphType', None, None, 'Subtable type.'),
+		('Flags32', 'SubFeatureFlags', None, None, 'The 32-bit mask identifying which subtable this is (the subtable being executed if the AND of this value and the processed defaultFlags is nonzero).'),
+		('SubStruct', 'SubStruct', None, None, 'SubTable.'),
+        ]),
+
+	('StateHeader', [
+		('uint32', 'ClassCount', None, None, 'Number of classes, which is the number of 16-bit entry indices in a single line in the state array.'),
+		('uint32', 'MorphClass', None, None, 'Offset from the start of this state table header to the start of the class table.'),
+		('uint32', 'StateArrayOffset', None, None, 'Offset from the start of this state table header to the start of the state array.'),
+		('uint32', 'EntryTableOffset', None, None, 'Offset from the start of this state table header to the start of the entry table.'),
+	]),
+
+	('RearrangementMorph', [
+		('STXHeader(RearrangementMorphAction)', 'StateTable', None, None, 'Finite-state transducer table for indic rearrangement.'),
+	]),
+
+	('ContextualMorph', [
+		('STXHeader(ContextualMorphAction)', 'StateTable', None, None, 'Finite-state transducer for contextual glyph substitution.'),
+	]),
+
+	('LigatureMorph', [
+		('STXHeader(LigatureMorphAction)', 'StateTable', None, None, 'Finite-state transducer for ligature substitution.'),
+	]),
+
+	('NoncontextualMorph', [
+		('AATLookup(GlyphID)', 'Substitution', None, None, 'The noncontextual glyph substitution table.'),
+        ]),
+
+	('InsertionMorph', [
+		('struct', 'StateHeader', None, None, 'Header.'),
+		# TODO: Add missing parts.
+	]),
+
+	('MorphClass', [
+		('uint16', 'FirstGlyph', None, None, 'Glyph index of the first glyph in the class table.'),
+		#('uint16', 'GlyphCount', None, None, 'Number of glyphs in class table.'),
+		#('uint8', 'GlyphClass', 'GlyphCount', 0, 'The class codes (indexed by glyph index minus firstGlyph). Class codes range from 0 to the value of stateSize minus 1.'),
+	]),
+
+	# If the 'morx' table version is 3 or greater, then the last subtable in the chain is followed by a subtableGlyphCoverageArray, as described below.
+	#		('Offset', 'MarkGlyphSetsDef', None, 'round(Version*0x10000) >= 0x00010002', 'Offset to the table of mark set definitions-from beginning of GDEF header (may be NULL)'),
+
+
+	#
+	# prop
+	#
+
+	('prop', [
+		('Fixed', 'Version', None, None, 'Version number of the AAT glyphs property table. Version 1.0 is the initial table version. Version 2.0, which is recognized by macOS 8.5 and later, adds support for the “attaches on right” bit. Version 3.0, which gets recognized by macOS X and iOS, adds support for the additional directional properties defined in Unicode 3.0.'),
+		('struct', 'GlyphProperties', None, None, 'Glyph properties.'),
+	]),
+
+	('GlyphPropertiesFormat0', [
+		('uint16', 'Format', None, None, 'Format, = 0.'),
+		('uint16', 'DefaultProperties', None, None, 'Default properties applied to a glyph. Since there is no lookup table in prop format 0, the default properties get applied to every glyph in the font.'),
+        ]),
+
+	('GlyphPropertiesFormat1', [
+		('uint16', 'Format', None, None, 'Format, = 1.'),
+		('uint16', 'DefaultProperties', None, None, 'Default properties applied to a glyph if that glyph is not present in the Properties lookup table.'),
+		('AATLookup(uint16)', 'Properties', None, None, 'Lookup data associating glyphs with their properties.'),
+        ]),
+
+
+	#
+	# opbd
+	#
+
+	('opbd', [
+		('Version', 'Version', None, None, 'Version number of the optical bounds table (0x00010000 for the initial version).'),
+		('struct', 'OpticalBounds', None, None, 'Optical bounds table.'),
+	]),
+
+	('OpticalBoundsFormat0', [
+		('uint16', 'Format', None, None, 'Format of the optical bounds table, = 0.'),
+		('AATLookup(OpticalBoundsDeltas)', 'OpticalBoundsDeltas', None, None, 'Lookup table associating glyphs with their optical bounds, given as deltas in font units.'),
+	]),
+
+	('OpticalBoundsFormat1', [
+		('uint16', 'Format', None, None, 'Format of the optical bounds table, = 1.'),
+		('AATLookup(OpticalBoundsPoints)', 'OpticalBoundsPoints', None, None, 'Lookup table associating glyphs with their optical bounds, given as references to control points.'),
+	]),
+
+	('OpticalBoundsDeltas', [
+		('int16', 'Left', None, None, 'Delta value for the left-side optical edge.'),
+		('int16', 'Top', None, None, 'Delta value for the top-side optical edge.'),
+		('int16', 'Right', None, None, 'Delta value for the right-side optical edge.'),
+		('int16', 'Bottom', None, None, 'Delta value for the bottom-side optical edge.'),
+	]),
+
+	('OpticalBoundsPoints', [
+		('int16', 'Left', None, None, 'Control point index for the left-side optical edge, or -1 if this glyph has none.'),
+		('int16', 'Top', None, None, 'Control point index for the top-side optical edge, or -1 if this glyph has none.'),
+		('int16', 'Right', None, None, 'Control point index for the right-side optical edge, or -1 if this glyph has none.'),
+		('int16', 'Bottom', None, None, 'Control point index for the bottom-side optical edge, or -1 if this glyph has none.'),
+	]),
+
 ]
diff --git a/Lib/fontTools/ttLib/tables/otTables.py b/Lib/fontTools/ttLib/tables/otTables.py
index 2afc2cc..d00c233 100644
--- a/Lib/fontTools/ttLib/tables/otTables.py
+++ b/Lib/fontTools/ttLib/tables/otTables.py
@@ -1,18 +1,422 @@
+# coding: utf-8
 """fontTools.ttLib.tables.otTables -- A collection of classes representing the various
 OpenType subtables.
 
 Most are constructed upon import from data in otData.py, all are populated with
 converter objects from otConverters.py.
 """
-from __future__ import print_function, division, absolute_import
+from __future__ import print_function, division, absolute_import, unicode_literals
 from fontTools.misc.py23 import *
-from .otBase import BaseTable, FormatSwitchingBaseTable
+from fontTools.misc.textTools import safeEval
+from .otBase import BaseTable, FormatSwitchingBaseTable, ValueRecord
 import operator
-import warnings
+import logging
+import struct
 
 
-class LookupOrder(BaseTable):
-	"""Dummy class; this table isn't defined, but is used, and is always NULL."""
+log = logging.getLogger(__name__)
+
+
+class AATStateTable(object):
+	def __init__(self):
+		self.GlyphClasses = {}  # GlyphID --> GlyphClass
+		self.States = []  # List of AATState, indexed by state number
+		self.PerGlyphLookups = []  # [{GlyphID:GlyphID}, ...]
+
+
+class AATState(object):
+	def __init__(self):
+		self.Transitions = {}  # GlyphClass --> AATAction
+
+
+class AATAction(object):
+	_FLAGS = None
+
+	def _writeFlagsToXML(self, xmlWriter):
+		flags = [f for f in self._FLAGS if self.__dict__[f]]
+		if flags:
+			xmlWriter.simpletag("Flags", value=",".join(flags))
+			xmlWriter.newline()
+		if self.ReservedFlags != 0:
+			xmlWriter.simpletag(
+				"ReservedFlags",
+				value='0x%04X' % self.ReservedFlags)
+			xmlWriter.newline()
+
+	def _setFlag(self, flag):
+		assert flag in self._FLAGS, "unsupported flag %s" % flag
+		self.__dict__[flag] = True
+
+
+class RearrangementMorphAction(AATAction):
+	staticSize = 4
+	_FLAGS = ["MarkFirst", "DontAdvance", "MarkLast"]
+
+	_VERBS = {
+		0: "no change",
+		1: "Ax ⇒ xA",
+		2: "xD ⇒ Dx",
+		3: "AxD ⇒ DxA",
+		4: "ABx ⇒ xAB",
+		5: "ABx ⇒ xBA",
+		6: "xCD ⇒ CDx",
+		7: "xCD ⇒ DCx",
+		8: "AxCD ⇒ CDxA",
+		9: "AxCD ⇒ DCxA",
+		10: "ABxD ⇒ DxAB",
+		11: "ABxD ⇒ DxBA",
+		12: "ABxCD ⇒ CDxAB",
+		13: "ABxCD ⇒ CDxBA",
+		14: "ABxCD ⇒ DCxAB",
+		15: "ABxCD ⇒ DCxBA",
+        }
+
+	def __init__(self):
+		self.NewState = 0
+		self.Verb = 0
+		self.MarkFirst = False
+		self.DontAdvance = False
+		self.MarkLast = False
+		self.ReservedFlags = 0
+
+	def compile(self, writer, font, actionIndex):
+		assert actionIndex is None
+		writer.writeUShort(self.NewState)
+		assert self.Verb >= 0 and self.Verb <= 15, self.Verb
+		flags = self.Verb | self.ReservedFlags
+		if self.MarkFirst: flags |= 0x8000
+		if self.DontAdvance: flags |= 0x4000
+		if self.MarkLast: flags |= 0x2000
+		writer.writeUShort(flags)
+
+	def decompile(self, reader, font, actionReader):
+		assert actionReader is None
+		self.NewState = reader.readUShort()
+		flags = reader.readUShort()
+		self.Verb = flags & 0xF
+		self.MarkFirst = bool(flags & 0x8000)
+		self.DontAdvance = bool(flags & 0x4000)
+		self.MarkLast = bool(flags & 0x2000)
+		self.ReservedFlags = flags & 0x1FF0
+
+	def toXML(self, xmlWriter, font, attrs, name):
+		xmlWriter.begintag(name, **attrs)
+		xmlWriter.newline()
+		xmlWriter.simpletag("NewState", value=self.NewState)
+		xmlWriter.newline()
+		self._writeFlagsToXML(xmlWriter)
+		xmlWriter.simpletag("Verb", value=self.Verb)
+		verbComment = self._VERBS.get(self.Verb)
+		if verbComment is not None:
+			xmlWriter.comment(verbComment)
+		xmlWriter.newline()
+		xmlWriter.endtag(name)
+		xmlWriter.newline()
+
+	def fromXML(self, name, attrs, content, font):
+		self.NewState = self.Verb = self.ReservedFlags = 0
+		self.MarkFirst = self.DontAdvance = self.MarkLast = False
+		content = [t for t in content if isinstance(t, tuple)]
+		for eltName, eltAttrs, eltContent in content:
+			if eltName == "NewState":
+				self.NewState = safeEval(eltAttrs["value"])
+			elif eltName == "Verb":
+				self.Verb = safeEval(eltAttrs["value"])
+			elif eltName == "ReservedFlags":
+				self.ReservedFlags = safeEval(eltAttrs["value"])
+			elif eltName == "Flags":
+				for flag in eltAttrs["value"].split(","):
+					self._setFlag(flag.strip())
+
+
+class ContextualMorphAction(AATAction):
+	staticSize = 8
+	_FLAGS = ["SetMark", "DontAdvance"]
+
+	def __init__(self):
+		self.NewState = 0
+		self.SetMark, self.DontAdvance = False, False
+		self.ReservedFlags = 0
+		self.MarkIndex, self.CurrentIndex = 0xFFFF, 0xFFFF
+
+	def compile(self, writer, font, actionIndex):
+		assert actionIndex is None
+		writer.writeUShort(self.NewState)
+		flags = self.ReservedFlags
+		if self.SetMark: flags |= 0x8000
+		if self.DontAdvance: flags |= 0x4000
+		writer.writeUShort(flags)
+		writer.writeUShort(self.MarkIndex)
+		writer.writeUShort(self.CurrentIndex)
+
+	def decompile(self, reader, font, actionReader):
+		assert actionReader is None
+		self.NewState = reader.readUShort()
+		flags = reader.readUShort()
+		self.SetMark = bool(flags & 0x8000)
+		self.DontAdvance = bool(flags & 0x4000)
+		self.ReservedFlags = flags & 0x3FFF
+		self.MarkIndex = reader.readUShort()
+		self.CurrentIndex = reader.readUShort()
+
+	def toXML(self, xmlWriter, font, attrs, name):
+		xmlWriter.begintag(name, **attrs)
+		xmlWriter.newline()
+		xmlWriter.simpletag("NewState", value=self.NewState)
+		xmlWriter.newline()
+		self._writeFlagsToXML(xmlWriter)
+		xmlWriter.simpletag("MarkIndex", value=self.MarkIndex)
+		xmlWriter.newline()
+		xmlWriter.simpletag("CurrentIndex",
+		                    value=self.CurrentIndex)
+		xmlWriter.newline()
+		xmlWriter.endtag(name)
+		xmlWriter.newline()
+
+	def fromXML(self, name, attrs, content, font):
+		self.NewState = self.ReservedFlags = 0
+		self.SetMark = self.DontAdvance = False
+		self.MarkIndex, self.CurrentIndex = 0xFFFF, 0xFFFF
+		content = [t for t in content if isinstance(t, tuple)]
+		for eltName, eltAttrs, eltContent in content:
+			if eltName == "NewState":
+				self.NewState = safeEval(eltAttrs["value"])
+			elif eltName == "Flags":
+				for flag in eltAttrs["value"].split(","):
+					self._setFlag(flag.strip())
+			elif eltName == "ReservedFlags":
+				self.ReservedFlags = safeEval(eltAttrs["value"])
+			elif eltName == "MarkIndex":
+				self.MarkIndex = safeEval(eltAttrs["value"])
+			elif eltName == "CurrentIndex":
+				self.CurrentIndex = safeEval(eltAttrs["value"])
+
+
+class LigAction(object):
+	def __init__(self):
+		self.Store = False
+		# GlyphIndexDelta is a (possibly negative) delta that gets
+		# added to the glyph ID at the top of the AAT runtime
+		# execution stack. It is *not* a byte offset into the
+		# morx table. The result of the addition, which is performed
+		# at run time by the shaping engine, is an index into
+		# the ligature components table. See 'morx' specification.
+		# In the AAT specification, this field is called Offset;
+		# but its meaning is quite different from other offsets
+		# in either AAT or OpenType, so we use a different name.
+		self.GlyphIndexDelta = 0
+
+
+class LigatureMorphAction(AATAction):
+	staticSize = 6
+	_FLAGS = ["SetComponent", "DontAdvance"]
+
+	def __init__(self):
+		self.NewState = 0
+		self.SetComponent, self.DontAdvance = False, False
+		self.ReservedFlags = 0
+		self.Actions = []
+
+	def compile(self, writer, font, actionIndex):
+		assert actionIndex is not None
+		writer.writeUShort(self.NewState)
+		flags = self.ReservedFlags
+		if self.SetComponent: flags |= 0x8000
+		if self.DontAdvance: flags |= 0x4000
+		if len(self.Actions) > 0: flags |= 0x2000
+		writer.writeUShort(flags)
+		if len(self.Actions) > 0:
+			actions = self.compileLigActions()
+			writer.writeUShort(actionIndex[actions])
+		else:
+			writer.writeUShort(0)
+
+	def decompile(self, reader, font, actionReader):
+		assert actionReader is not None
+		self.NewState = reader.readUShort()
+		flags = reader.readUShort()
+		self.SetComponent = bool(flags & 0x8000)
+		self.DontAdvance = bool(flags & 0x4000)
+		performAction = bool(flags & 0x2000)
+		# As of 2017-09-12, the 'morx' specification says that
+		# the reserved bitmask in ligature subtables is 0x3FFF.
+		# However, the specification also defines a flag 0x2000,
+		# so the reserved value should actually be 0x1FFF.
+		# TODO: Report this specification bug to Apple.
+		self.ReservedFlags = flags & 0x1FFF
+		actionIndex = reader.readUShort()
+		if performAction:
+			self.Actions = self._decompileLigActions(
+				actionReader, actionIndex)
+		else:
+			self.Actions = []
+
+	def compileLigActions(self):
+		result = []
+		for i, action in enumerate(self.Actions):
+			last = (i == len(self.Actions) - 1)
+			value = action.GlyphIndexDelta & 0x3FFFFFFF
+			value |= 0x80000000 if last else 0
+			value |= 0x40000000 if action.Store else 0
+			result.append(struct.pack(">L", value))
+		return bytesjoin(result)
+
+	def _decompileLigActions(self, actionReader, actionIndex):
+		actions = []
+		last = False
+		reader = actionReader.getSubReader(
+			actionReader.pos + actionIndex * 4)
+		while not last:
+			value = reader.readULong()
+			last = bool(value & 0x80000000)
+			action = LigAction()
+			actions.append(action)
+			action.Store = bool(value & 0x40000000)
+			delta = value & 0x3FFFFFFF
+			if delta >= 0x20000000: # sign-extend 30-bit value
+				delta = -0x40000000 + delta
+			action.GlyphIndexDelta = delta
+		return actions
+
+	def fromXML(self, name, attrs, content, font):
+		self.NewState = self.ReservedFlags = 0
+		self.SetComponent = self.DontAdvance = False
+		self.ReservedFlags = 0
+		self.Actions = []
+		content = [t for t in content if isinstance(t, tuple)]
+		for eltName, eltAttrs, eltContent in content:
+			if eltName == "NewState":
+				self.NewState = safeEval(eltAttrs["value"])
+			elif eltName == "Flags":
+				for flag in eltAttrs["value"].split(","):
+					self._setFlag(flag.strip())
+			elif eltName == "ReservedFlags":
+				self.ReservedFlags = safeEval(eltAttrs["value"])
+			elif eltName == "Action":
+				action = LigAction()
+				flags = eltAttrs.get("Flags", "").split(",")
+				flags = [f.strip() for f in flags]
+				action.Store = "Store" in flags
+				action.GlyphIndexDelta = safeEval(
+					eltAttrs["GlyphIndexDelta"])
+				self.Actions.append(action)
+
+	def toXML(self, xmlWriter, font, attrs, name):
+		xmlWriter.begintag(name, **attrs)
+		xmlWriter.newline()
+		xmlWriter.simpletag("NewState", value=self.NewState)
+		xmlWriter.newline()
+		self._writeFlagsToXML(xmlWriter)
+		for action in self.Actions:
+			attribs = [("GlyphIndexDelta", action.GlyphIndexDelta)]
+			if action.Store:
+				attribs.append(("Flags", "Store"))
+			xmlWriter.simpletag("Action", attribs)
+			xmlWriter.newline()
+		xmlWriter.endtag(name)
+		xmlWriter.newline()
+
+
+class InsertionMorphAction(AATAction):
+	staticSize = 8
+
+	_FLAGS = ["SetMark", "DontAdvance",
+	          "CurrentIsKashidaLike", "MarkedIsKashidaLike",
+	          "CurrentInsertBefore", "MarkedInsertBefore"]
+
+	def __init__(self):
+		self.NewState = 0
+		for flag in self._FLAGS:
+			setattr(self, flag, False)
+		self.ReservedFlags = 0
+		self.CurrentInsertionAction, self.MarkedInsertionAction = [], []
+
+	def compile(self, writer, font, actionIndex):
+		assert actionIndex is not None
+		writer.writeUShort(self.NewState)
+		flags = self.ReservedFlags
+		if self.SetMark: flags |= 0x8000
+		if self.DontAdvance: flags |= 0x4000
+		if self.CurrentIsKashidaLike: flags |= 0x2000
+		if self.MarkedIsKashidaLike: flags |= 0x1000
+		if self.CurrentInsertBefore: flags |= 0x0800
+		if self.MarkedInsertBefore: flags |= 0x0400
+		flags |= len(self.CurrentInsertionAction) << 5
+		flags |= len(self.MarkedInsertionAction)
+		writer.writeUShort(flags)
+		if len(self.CurrentInsertionAction) > 0:
+			currentIndex = actionIndex[
+				tuple(self.CurrentInsertionAction)]
+		else:
+			currentIndex = 0xFFFF
+		writer.writeUShort(currentIndex)
+		if len(self.MarkedInsertionAction) > 0:
+			markedIndex = actionIndex[
+				tuple(self.MarkedInsertionAction)]
+		else:
+			markedIndex = 0xFFFF
+		writer.writeUShort(markedIndex)
+
+	def decompile(self, reader, font, actionReader):
+		assert actionReader is not None
+		self.NewState = reader.readUShort()
+		flags = reader.readUShort()
+		self.SetMark = bool(flags & 0x8000)
+		self.DontAdvance = bool(flags & 0x4000)
+		self.CurrentIsKashidaLike = bool(flags & 0x2000)
+		self.MarkedIsKashidaLike = bool(flags & 0x1000)
+		self.CurrentInsertBefore = bool(flags & 0x0800)
+		self.MarkedInsertBefore = bool(flags & 0x0400)
+		self.CurrentInsertionAction = self._decompileInsertionAction(
+			actionReader, font,
+			index=reader.readUShort(),
+			count=((flags & 0x03E0) >> 5))
+		self.MarkedInsertionAction = self._decompileInsertionAction(
+			actionReader, font,
+			index=reader.readUShort(),
+			count=(flags & 0x001F))
+
+	def _decompileInsertionAction(self, actionReader, font, index, count):
+		if index == 0xFFFF or count == 0:
+			return []
+		reader = actionReader.getSubReader(
+			actionReader.pos + index * 2)
+		return [font.getGlyphName(glyphID)
+		        for glyphID in reader.readUShortArray(count)]
+
+	def toXML(self, xmlWriter, font, attrs, name):
+		xmlWriter.begintag(name, **attrs)
+		xmlWriter.newline()
+		xmlWriter.simpletag("NewState", value=self.NewState)
+		xmlWriter.newline()
+		self._writeFlagsToXML(xmlWriter)
+		for g in self.CurrentInsertionAction:
+			xmlWriter.simpletag("CurrentInsertionAction", glyph=g)
+			xmlWriter.newline()
+		for g in self.MarkedInsertionAction:
+			xmlWriter.simpletag("MarkedInsertionAction", glyph=g)
+			xmlWriter.newline()
+		xmlWriter.endtag(name)
+		xmlWriter.newline()
+
+	def fromXML(self, name, attrs, content, font):
+		self.__init__()
+		content = [t for t in content if isinstance(t, tuple)]
+		for eltName, eltAttrs, eltContent in content:
+			if eltName == "NewState":
+				self.NewState = safeEval(eltAttrs["value"])
+			elif eltName == "Flags":
+				for flag in eltAttrs["value"].split(","):
+					self._setFlag(flag.strip())
+			elif eltName == "CurrentInsertionAction":
+				self.CurrentInsertionAction.append(
+					eltAttrs["glyph"])
+			elif eltName == "MarkedInsertionAction":
+				self.MarkedInsertionAction.append(
+					eltAttrs["glyph"])
+			else:
+				assert False, eltName
+
 
 class FeatureParams(BaseTable):
 
@@ -33,9 +437,13 @@
 	pass
 
 class Coverage(FormatSwitchingBaseTable):
-	
+
 	# manual implementation to get rid of glyphID dependencies
-	
+
+	def populateDefaults(self, propagator=None):
+		if not hasattr(self, 'glyphs'):
+			self.glyphs = []
+
 	def postRead(self, rawTable, font):
 		if self.Format == 1:
 			# TODO only allow glyphs that are valid?
@@ -49,7 +457,7 @@
 			# this when writing font out.
 			sorted_ranges = sorted(ranges, key=lambda a: a.StartCoverageIndex)
 			if ranges != sorted_ranges:
-				warnings.warn("GSUB/GPOS Coverage is not sorted by glyph ids.")
+				log.warning("GSUB/GPOS Coverage is not sorted by glyph ids.")
 				ranges = sorted_ranges
 			del sorted_ranges
 			for r in ranges:
@@ -60,22 +468,22 @@
 				try:
 					startID = font.getGlyphID(start, requireReal=True)
 				except KeyError:
-					warnings.warn("Coverage table has start glyph ID out of range: %s." % start)
+					log.warning("Coverage table has start glyph ID out of range: %s.", start)
 					continue
 				try:
 					endID = font.getGlyphID(end, requireReal=True) + 1
 				except KeyError:
 					# Apparently some tools use 65535 to "match all" the range
 					if end != 'glyph65535':
-						warnings.warn("Coverage table has end glyph ID out of range: %s." % end)
+						log.warning("Coverage table has end glyph ID out of range: %s.", end)
 					# NOTE: We clobber out-of-range things here.  There are legit uses for those,
 					# but none that we have seen in the wild.
 					endID = len(glyphOrder)
 				glyphs.extend(glyphOrder[glyphID] for glyphID in range(startID, endID))
 		else:
-			assert 0, "unknown format: %s" % self.Format
-		del self.Format # Don't need this anymore
-	
+			self.glyphs = []
+			log.warning("Unknown Coverage format: %s", self.Format)
+
 	def preWrite(self, font):
 		glyphs = getattr(self, "glyphs", None)
 		if glyphs is None:
@@ -87,7 +495,7 @@
 			# find out whether Format 2 is more compact or not
 			glyphIDs = [getGlyphID(glyphName) for glyphName in glyphs ]
 			brokenOrder = sorted(glyphIDs) != glyphIDs
-			
+
 			last = glyphIDs[0]
 			ranges = [[last]]
 			for glyphID in glyphIDs[1:]:
@@ -96,7 +504,7 @@
 					ranges.append([glyphID])
 				last = glyphID
 			ranges[-1].append(last)
-			
+
 			if brokenOrder or len(ranges) * 3 < len(glyphs):  # 3 words vs. 1 word
 				# Format 2 is more compact
 				index = 0
@@ -110,7 +518,7 @@
 					ranges[i] = r
 					index = index + end - start + 1
 				if brokenOrder:
-					warnings.warn("GSUB/GPOS Coverage is not sorted by glyph ids.")
+					log.warning("GSUB/GPOS Coverage is not sorted by glyph ids.")
 					ranges.sort(key=lambda a: a.StartID)
 				for r in ranges:
 					del r.StartID
@@ -120,12 +528,12 @@
 			#	fallthrough; Format 1 is more compact
 		self.Format = format
 		return rawTable
-	
+
 	def toXML2(self, xmlWriter, font):
 		for glyphName in getattr(self, "glyphs", []):
 			xmlWriter.simpletag("Glyph", value=glyphName)
 			xmlWriter.newline()
-	
+
 	def fromXML(self, name, attrs, content, font):
 		glyphs = getattr(self, "glyphs", None)
 		if glyphs is None:
@@ -134,13 +542,90 @@
 		glyphs.append(attrs["value"])
 
 
-def doModulo(value):
-	if value < 0:
-		return value + 65536
-	return value
+class VarIdxMap(BaseTable):
+
+	def populateDefaults(self, propagator=None):
+		if not hasattr(self, 'mapping'):
+			self.mapping = {}
+
+	def postRead(self, rawTable, font):
+		assert (rawTable['EntryFormat'] & 0xFFC0) == 0
+		glyphOrder = font.getGlyphOrder()
+		mapList = rawTable['mapping']
+		mapList.extend([mapList[-1]] * (len(glyphOrder) - len(mapList)))
+		self.mapping = dict(zip(glyphOrder, mapList))
+
+	def preWrite(self, font):
+		mapping = getattr(self, "mapping", None)
+		if mapping is None:
+			mapping = self.mapping = {}
+
+		glyphOrder = font.getGlyphOrder()
+		mapping = [mapping[g] for g in glyphOrder]
+		while len(mapping) > 1 and mapping[-2] == mapping[-1]:
+			del mapping[-1]
+
+		rawTable = { 'mapping': mapping }
+		rawTable['MappingCount'] = len(mapping)
+
+		ored = 0
+		for idx in mapping:
+			ored |= idx
+
+		inner = ored & 0xFFFF
+		innerBits = 0
+		while inner:
+			innerBits += 1
+			inner >>= 1
+		innerBits = max(innerBits, 1)
+		assert innerBits <= 16
+
+		ored = (ored >> (16-innerBits)) | (ored & ((1<<innerBits)-1))
+		if   ored <= 0x000000FF:
+			entrySize = 1
+		elif ored <= 0x0000FFFF:
+			entrySize = 2
+		elif ored <= 0x00FFFFFF:
+			entrySize = 3
+		else:
+			entrySize = 4
+
+		entryFormat = ((entrySize - 1) << 4) | (innerBits - 1)
+
+		rawTable['EntryFormat'] = entryFormat
+		return rawTable
+
+	def toXML2(self, xmlWriter, font):
+		for glyph, value in sorted(getattr(self, "mapping", {}).items()):
+			attrs = (
+				('glyph', glyph),
+				('outer', value >> 16),
+				('inner', value & 0xFFFF),
+			)
+			xmlWriter.simpletag("Map", attrs)
+			xmlWriter.newline()
+
+	def fromXML(self, name, attrs, content, font):
+		mapping = getattr(self, "mapping", None)
+		if mapping is None:
+			mapping = {}
+			self.mapping = mapping
+		try:
+			glyph = attrs['glyph']
+		except: # https://github.com/fonttools/fonttools/commit/21cbab8ce9ded3356fef3745122da64dcaf314e9#commitcomment-27649836
+			glyph = font.getGlyphOrder()[attrs['index']]
+		outer = safeEval(attrs['outer'])
+		inner = safeEval(attrs['inner'])
+		assert inner <= 0xFFFF
+		mapping[glyph] = (outer << 16) | inner
+
 
 class SingleSubst(FormatSwitchingBaseTable):
 
+	def populateDefaults(self, propagator=None):
+		if not hasattr(self, 'mapping'):
+			self.mapping = {}
+
 	def postRead(self, rawTable, font):
 		mapping = {}
 		input = _getGlyphsFromCoverageTable(rawTable["Coverage"])
@@ -148,8 +633,7 @@
 		if self.Format == 1:
 			delta = rawTable["DeltaGlyphID"]
 			inputGIDS =  [ font.getGlyphID(name) for name in input ]
-			outGIDS = [ glyphID + delta for glyphID in inputGIDS ]
-			outGIDS = map(doModulo, outGIDS)
+			outGIDS = [ (glyphID + delta) % 65536 for glyphID in inputGIDS ]
 			outNames = [ font.getGlyphName(glyphID) for glyphID in outGIDS ]
 			list(map(operator.setitem, [mapping]*lenMapping, input, outNames))
 		elif self.Format == 2:
@@ -160,8 +644,7 @@
 		else:
 			assert 0, "unknown format: %s" % self.Format
 		self.mapping = mapping
-		del self.Format # Don't need this anymore
-	
+
 	def preWrite(self, font):
 		mapping = getattr(self, "mapping", None)
 		if mapping is None:
@@ -176,16 +659,16 @@
 		delta = None
 		for inID, outID in gidItems:
 			if delta is None:
-				delta = outID - inID
-				if delta < -32768:
-					delta += 65536
-				elif delta > 32767:
-					delta -= 65536
-			else:
-				if delta != outID - inID:
+				delta = (outID - inID) % 65536
+
+			if (inID + delta) % 65536 != outID:
 					break
 		else:
-			format = 1
+			if delta is None:
+				# the mapping is empty, better use format 2
+				format = 2
+			else:
+				format = 1
 
 		rawTable = {}
 		self.Format = format
@@ -200,14 +683,14 @@
 		else:
 			rawTable["Substitute"] = subst
 		return rawTable
-	
+
 	def toXML2(self, xmlWriter, font):
 		items = sorted(self.mapping.items())
 		for inGlyph, outGlyph in items:
 			xmlWriter.simpletag("Substitution",
 					[("in", inGlyph), ("out", outGlyph)])
 			xmlWriter.newline()
-	
+
 	def fromXML(self, name, attrs, content, font):
 		mapping = getattr(self, "mapping", None)
 		if mapping is None:
@@ -216,8 +699,89 @@
 		mapping[attrs["in"]] = attrs["out"]
 
 
+class MultipleSubst(FormatSwitchingBaseTable):
+
+	def populateDefaults(self, propagator=None):
+		if not hasattr(self, 'mapping'):
+			self.mapping = {}
+
+	def postRead(self, rawTable, font):
+		mapping = {}
+		if self.Format == 1:
+			glyphs = _getGlyphsFromCoverageTable(rawTable["Coverage"])
+			subst = [s.Substitute for s in rawTable["Sequence"]]
+			mapping = dict(zip(glyphs, subst))
+		else:
+			assert 0, "unknown format: %s" % self.Format
+		self.mapping = mapping
+
+	def preWrite(self, font):
+		mapping = getattr(self, "mapping", None)
+		if mapping is None:
+			mapping = self.mapping = {}
+		cov = Coverage()
+		cov.glyphs = sorted(list(mapping.keys()), key=font.getGlyphID)
+		self.Format = 1
+		rawTable = {
+                        "Coverage": cov,
+                        "Sequence": [self.makeSequence_(mapping[glyph])
+                                     for glyph in cov.glyphs],
+                }
+		return rawTable
+
+	def toXML2(self, xmlWriter, font):
+		items = sorted(self.mapping.items())
+		for inGlyph, outGlyphs in items:
+			out = ",".join(outGlyphs)
+			xmlWriter.simpletag("Substitution",
+					[("in", inGlyph), ("out", out)])
+			xmlWriter.newline()
+
+	def fromXML(self, name, attrs, content, font):
+		mapping = getattr(self, "mapping", None)
+		if mapping is None:
+			mapping = {}
+			self.mapping = mapping
+
+		# TTX v3.0 and earlier.
+		if name == "Coverage":
+			self.old_coverage_ = []
+			for element in content:
+				if not isinstance(element, tuple):
+					continue
+				element_name, element_attrs, _ = element
+				if element_name == "Glyph":
+					self.old_coverage_.append(element_attrs["value"])
+			return
+		if name == "Sequence":
+			index = int(attrs.get("index", len(mapping)))
+			glyph = self.old_coverage_[index]
+			glyph_mapping = mapping[glyph] = []
+			for element in content:
+				if not isinstance(element, tuple):
+					continue
+				element_name, element_attrs, _ = element
+				if element_name == "Substitute":
+					glyph_mapping.append(element_attrs["value"])
+			return
+
+                # TTX v3.1 and later.
+		outGlyphs = attrs["out"].split(",") if attrs["out"] else []
+		mapping[attrs["in"]] = [g.strip() for g in outGlyphs]
+
+	@staticmethod
+	def makeSequence_(g):
+		seq = Sequence()
+		seq.Substitute = g
+		return seq
+
+
 class ClassDef(FormatSwitchingBaseTable):
-	
+
+	def populateDefaults(self, propagator=None):
+		if not hasattr(self, 'classDefs'):
+			self.classDefs = {}
+
 	def postRead(self, rawTable, font):
 		classDefs = {}
 		glyphOrder = font.getGlyphOrder()
@@ -228,17 +792,19 @@
 			try:
 				startID = font.getGlyphID(start, requireReal=True)
 			except KeyError:
-				warnings.warn("ClassDef table has start glyph ID out of range: %s." % start)
+				log.warning("ClassDef table has start glyph ID out of range: %s.", start)
 				startID = len(glyphOrder)
 			endID = startID + len(classList)
 			if endID > len(glyphOrder):
-				warnings.warn("ClassDef table has entries for out of range glyph IDs: %s,%s." % (start, len(classList)))
+				log.warning("ClassDef table has entries for out of range glyph IDs: %s,%s.",
+					start, len(classList))
 				# NOTE: We clobber out-of-range things here.  There are legit uses for those,
 				# but none that we have seen in the wild.
 				endID = len(glyphOrder)
 
 			for glyphID, cls in zip(range(startID, endID), classList):
-				classDefs[glyphOrder[glyphID]] = cls
+				if cls:
+					classDefs[glyphOrder[glyphID]] = cls
 
 		elif self.Format == 2:
 			records = rawTable["ClassRangeRecord"]
@@ -249,37 +815,37 @@
 				try:
 					startID = font.getGlyphID(start, requireReal=True)
 				except KeyError:
-					warnings.warn("ClassDef table has start glyph ID out of range: %s." % start)
+					log.warning("ClassDef table has start glyph ID out of range: %s.", start)
 					continue
 				try:
 					endID = font.getGlyphID(end, requireReal=True) + 1
 				except KeyError:
 					# Apparently some tools use 65535 to "match all" the range
 					if end != 'glyph65535':
-						warnings.warn("ClassDef table has end glyph ID out of range: %s." % end)
+						log.warning("ClassDef table has end glyph ID out of range: %s.", end)
 					# NOTE: We clobber out-of-range things here.  There are legit uses for those,
 					# but none that we have seen in the wild.
 					endID = len(glyphOrder)
 				for glyphID in range(startID, endID):
-					classDefs[glyphOrder[glyphID]] = cls
+					if cls:
+						classDefs[glyphOrder[glyphID]] = cls
 		else:
-			assert 0, "unknown format: %s" % self.Format
+			log.warning("Unknown ClassDef format: %s", self.Format)
 		self.classDefs = classDefs
-		del self.Format # Don't need this anymore
-	
-	def preWrite(self, font):
+
+	def _getClassRanges(self, font):
 		classDefs = getattr(self, "classDefs", None)
 		if classDefs is None:
-			classDefs = self.classDefs = {}
-		items = list(classDefs.items())
-		format = 2
-		rawTable = {"ClassRangeRecord": []}
+			self.classDefs = {}
+			return
 		getGlyphID = font.getGlyphID
-		for i in range(len(items)):
-			glyphName, cls = items[i]
-			items[i] = getGlyphID(glyphName), glyphName, cls
-		items.sort()
+		items = []
+		for glyphName, cls in classDefs.items():
+			if not cls:
+				continue
+			items.append((getGlyphID(glyphName), glyphName, cls))
 		if items:
+			items.sort()
 			last, lastName, lastCls = items[0]
 			ranges = [[lastCls, last, lastName]]
 			for glyphID, glyphName, cls in items[1:]:
@@ -290,7 +856,13 @@
 				lastName = glyphName
 				lastCls = cls
 			ranges[-1].extend([last, lastName])
+			return ranges
 
+	def preWrite(self, font):
+		format = 2
+		rawTable = {"ClassRangeRecord": []}
+		ranges = self._getClassRanges(font)
+		if ranges:
 			startGlyph = ranges[0][1]
 			endGlyph = ranges[-1][3]
 			glyphCount = endGlyph - startGlyph + 1
@@ -316,13 +888,13 @@
 				rawTable = {"StartGlyph": startGlyphName, "ClassValueArray": classes}
 		self.Format = format
 		return rawTable
-	
+
 	def toXML2(self, xmlWriter, font):
 		items = sorted(self.classDefs.items())
 		for glyphName, cls in items:
 			xmlWriter.simpletag("ClassDef", [("glyph", glyphName), ("class", cls)])
 			xmlWriter.newline()
-	
+
 	def fromXML(self, name, attrs, content, font):
 		classDefs = getattr(self, "classDefs", None)
 		if classDefs is None:
@@ -332,21 +904,23 @@
 
 
 class AlternateSubst(FormatSwitchingBaseTable):
-	
+
+	def populateDefaults(self, propagator=None):
+		if not hasattr(self, 'alternates'):
+			self.alternates = {}
+
 	def postRead(self, rawTable, font):
 		alternates = {}
 		if self.Format == 1:
 			input = _getGlyphsFromCoverageTable(rawTable["Coverage"])
 			alts = rawTable["AlternateSet"]
-			if len(input) != len(alts):
-				assert len(input) == len(alts)
-			for i in range(len(input)):
-				alternates[input[i]] = alts[i].Alternate
+			assert len(input) == len(alts)
+			for inp,alt in zip(input,alts):
+				alternates[inp] = alt.Alternate
 		else:
 			assert 0, "unknown format: %s" % self.Format
 		self.alternates = alternates
-		del self.Format # Don't need this anymore
-	
+
 	def preWrite(self, font):
 		self.Format = 1
 		alternates = getattr(self, "alternates", None)
@@ -361,7 +935,7 @@
 		cov.glyphs = [ item[1] for item in items]
 		alternates = []
 		setList = [ item[-1] for item in items]
-		for  set in setList:
+		for set in setList:
 			alts = AlternateSet()
 			alts.Alternate = set
 			alternates.append(alts)
@@ -370,9 +944,9 @@
 		# Also useful in that when splitting a sub-table because of an offset overflow
 		# I don't need to calculate the change in the subtable offset due to the change in the coverage table size.
 		# Allows packing more rules in subtable.
-		self.sortCoverageLast = 1 
+		self.sortCoverageLast = 1
 		return {"Coverage": cov, "AlternateSet": alternates}
-	
+
 	def toXML2(self, xmlWriter, font):
 		items = sorted(self.alternates.items())
 		for glyphName, alternates in items:
@@ -383,7 +957,7 @@
 				xmlWriter.newline()
 			xmlWriter.endtag("AlternateSet")
 			xmlWriter.newline()
-	
+
 	def fromXML(self, name, attrs, content, font):
 		alternates = getattr(self, "alternates", None)
 		if alternates is None:
@@ -400,7 +974,11 @@
 
 
 class LigatureSubst(FormatSwitchingBaseTable):
-	
+
+	def populateDefaults(self, propagator=None):
+		if not hasattr(self, 'ligatures'):
+			self.ligatures = {}
+
 	def postRead(self, rawTable, font):
 		ligatures = {}
 		if self.Format == 1:
@@ -412,13 +990,27 @@
 		else:
 			assert 0, "unknown format: %s" % self.Format
 		self.ligatures = ligatures
-		del self.Format # Don't need this anymore
-	
+
 	def preWrite(self, font):
 		self.Format = 1
 		ligatures = getattr(self, "ligatures", None)
 		if ligatures is None:
 			ligatures = self.ligatures = {}
+
+		if ligatures and isinstance(next(iter(ligatures)), tuple):
+			# New high-level API in v3.1 and later.  Note that we just support compiling this
+			# for now.  We don't load to this API, and don't do XML with it.
+
+			# ligatures is map from components-sequence to lig-glyph
+			newLigatures = dict()
+			for comps,lig in sorted(ligatures.items(), key=lambda item: (-len(item[0]), item[0])):
+				ligature = Ligature()
+				ligature.Component = comps[1:]
+				ligature.CompCount = len(comps)
+				ligature.LigGlyph = lig
+				newLigatures.setdefault(comps[0], []).append(ligature)
+			ligatures = newLigatures
+
 		items = list(ligatures.items())
 		for i in range(len(items)):
 			glyphName, set = items[i]
@@ -438,9 +1030,9 @@
 		# Useful in that when splitting a sub-table because of an offset overflow
 		# I don't need to calculate the change in subtabl offset due to the coverage table size.
 		# Allows packing more rules in subtable.
-		self.sortCoverageLast = 1 
+		self.sortCoverageLast = 1
 		return {"Coverage": cov, "LigatureSet": ligSets}
-	
+
 	def toXML2(self, xmlWriter, font):
 		items = sorted(self.ligatures.items())
 		for glyphName, ligSets in items:
@@ -452,7 +1044,7 @@
 				xmlWriter.newline()
 			xmlWriter.endtag("LigatureSet")
 			xmlWriter.newline()
-	
+
 	def fromXML(self, name, attrs, content, font):
 		ligatures = getattr(self, "ligatures", None)
 		if ligatures is None:
@@ -467,11 +1059,11 @@
 			name, attrs, content = element
 			lig = Ligature()
 			lig.LigGlyph = attrs["glyph"]
-			lig.Component = attrs["components"].split(",")
+			components = attrs["components"]
+			lig.Component = components.split(",") if components else []
 			ligs.append(lig)
 
 
-#
 # For each subtable format there is a class. However, we don't really distinguish
 # between "field name" and "format name": often these are the same. Yet there's
 # a whole bunch of fields with different names. The following dict is a mapping
@@ -512,7 +1104,7 @@
 
 def fixLookupOverFlows(ttf, overflowRecord):
 	""" Either the offset from the LookupList to a lookup overflowed, or
-	an offset from a lookup to a subtable overflowed. 
+	an offset from a lookup to a subtable overflowed.
 	The table layout is:
 	GPSO/GUSB
 		Script List
@@ -531,7 +1123,7 @@
 					SubTable[n] and contents
 	If the offset to a lookup overflowed (SubTableIndex is None)
 		we must promote the *previous*	lookup to an Extension type.
-	If the offset from a lookup to subtable overflowed, then we must promote it 
+	If the offset from a lookup to subtable overflowed, then we must promote it
 		to an Extension Lookup type.
 	"""
 	ok = 0
@@ -553,7 +1145,8 @@
 		if lookupIndex < 0:
 			return ok
 		lookup = lookups[lookupIndex]
-		
+
+	lookup.LookupType = extType
 	for si in range(len(lookup.SubTable)):
 		subTable = lookup.SubTable[si]
 		extSubTableClass = lookupTypes[overflowRecord.tableType][extType]
@@ -569,7 +1162,7 @@
 	newSubTable.Format = oldSubTable.Format
 	if hasattr(oldSubTable, 'sortCoverageLast'):
 		newSubTable.sortCoverageLast = oldSubTable.sortCoverageLast
-	
+
 	oldAlts = sorted(oldSubTable.alternates.items())
 	oldLen = len(oldAlts)
 
@@ -579,10 +1172,10 @@
 		newLen = oldLen//2
 
 	elif overflowRecord.itemName == 'AlternateSet':
-		# We just need to back up by two items 
+		# We just need to back up by two items
 		# from the overflowed AlternateSet index to make sure the offset
 		# to the Coverage table doesn't overflow.
-		newLen  = overflowRecord.itemIndex - 1
+		newLen = overflowRecord.itemIndex - 1
 
 	newSubTable.alternates = {}
 	for i in range(newLen, oldLen):
@@ -591,7 +1184,6 @@
 		newSubTable.alternates[key] = item[1]
 		del oldSubTable.alternates[key]
 
-
 	return ok
 
 
@@ -607,10 +1199,10 @@
 		newLen = oldLen//2
 
 	elif overflowRecord.itemName == 'LigatureSet':
-		# We just need to back up by two items 
+		# We just need to back up by two items
 		# from the overflowed AlternateSet index to make sure the offset
 		# to the Coverage table doesn't overflow.
-		newLen  = overflowRecord.itemIndex - 1
+		newLen = overflowRecord.itemIndex - 1
 
 	newSubTable.ligatures = {}
 	for i in range(newLen, oldLen):
@@ -622,6 +1214,73 @@
 	return ok
 
 
+def splitPairPos(oldSubTable, newSubTable, overflowRecord):
+	st = oldSubTable
+	ok = False
+	newSubTable.Format = oldSubTable.Format
+	if oldSubTable.Format == 1 and len(oldSubTable.PairSet) > 1:
+		for name in 'ValueFormat1', 'ValueFormat2':
+			setattr(newSubTable, name, getattr(oldSubTable, name))
+
+		# Move top half of coverage to new subtable
+
+		newSubTable.Coverage = oldSubTable.Coverage.__class__()
+
+		coverage = oldSubTable.Coverage.glyphs
+		records = oldSubTable.PairSet
+
+		oldCount = len(oldSubTable.PairSet) // 2
+
+		oldSubTable.Coverage.glyphs = coverage[:oldCount]
+		oldSubTable.PairSet = records[:oldCount]
+
+		newSubTable.Coverage.glyphs = coverage[oldCount:]
+		newSubTable.PairSet = records[oldCount:]
+
+		oldSubTable.PairSetCount = len(oldSubTable.PairSet)
+		newSubTable.PairSetCount = len(newSubTable.PairSet)
+
+		ok = True
+
+	elif oldSubTable.Format == 2 and len(oldSubTable.Class1Record) > 1:
+		if not hasattr(oldSubTable, 'Class2Count'):
+			oldSubTable.Class2Count = len(oldSubTable.Class1Record[0].Class2Record)
+		for name in 'Class2Count', 'ClassDef2', 'ValueFormat1', 'ValueFormat2':
+			setattr(newSubTable, name, getattr(oldSubTable, name))
+
+		# The two subtables will still have the same ClassDef2 and the table
+		# sharing will still cause the sharing to overflow.  As such, disable
+		# sharing on the one that is serialized second (that's oldSubTable).
+		oldSubTable.DontShare = True
+
+		# Move top half of class numbers to new subtable
+
+		newSubTable.Coverage = oldSubTable.Coverage.__class__()
+		newSubTable.ClassDef1 = oldSubTable.ClassDef1.__class__()
+
+		coverage = oldSubTable.Coverage.glyphs
+		classDefs = oldSubTable.ClassDef1.classDefs
+		records = oldSubTable.Class1Record
+
+		oldCount = len(oldSubTable.Class1Record) // 2
+		newGlyphs = set(k for k,v in classDefs.items() if v >= oldCount)
+
+		oldSubTable.Coverage.glyphs = [g for g in coverage if g not in newGlyphs]
+		oldSubTable.ClassDef1.classDefs = {k:v for k,v in classDefs.items() if v < oldCount}
+		oldSubTable.Class1Record = records[:oldCount]
+
+		newSubTable.Coverage.glyphs = [g for g in coverage if g in newGlyphs]
+		newSubTable.ClassDef1.classDefs = {k:(v-oldCount) for k,v in classDefs.items() if v > oldCount}
+		newSubTable.Class1Record = records[oldCount:]
+
+		oldSubTable.Class1Count = len(oldSubTable.Class1Record)
+		newSubTable.Class1Count = len(newSubTable.Class1Record)
+
+		ok = True
+
+	return ok
+
+
 splitTable = {	'GSUB': {
 #					1: splitSingleSubst,
 #					2: splitMultipleSubst,
@@ -634,7 +1293,7 @@
 					},
 				'GPOS': {
 #					1: splitSinglePos,
-#					2: splitPairPos,
+					2: splitPairPos,
 #					3: splitCursivePos,
 #					4: splitMarkBasePos,
 #					5: splitMarkLigPos,
@@ -647,7 +1306,7 @@
 			}
 
 def fixSubTableOverFlows(ttf, overflowRecord):
-	""" 
+	"""
 	An offset has overflowed within a sub-table. We need to divide this subtable into smaller parts.
 	"""
 	ok = 0
@@ -656,6 +1315,11 @@
 	subIndex = overflowRecord.SubTableIndex
 	subtable = lookup.SubTable[subIndex]
 
+	# First, try not sharing anything for this subtable...
+	if not hasattr(subtable, "DontShare"):
+		subtable.DontShare = True
+		return True
+
 	if hasattr(subtable, 'ExtSubTable'):
 		# We split the subtable of the Extension table, and add a new Extension table
 		# to contain the new subtable.
@@ -663,7 +1327,7 @@
 		subTableType = subtable.ExtSubTable.__class__.LookupType
 		extSubTable = subtable
 		subtable = extSubTable.ExtSubTable
-		newExtSubTableClass = lookupTypes[overflowRecord.tableType][subtable.__class__.LookupType]
+		newExtSubTableClass = lookupTypes[overflowRecord.tableType][extSubTable.__class__.LookupType]
 		newExtSubTable = newExtSubTableClass()
 		newExtSubTable.Format = extSubTable.Format
 		lookup.SubTable.insert(subIndex + 1, newExtSubTable)
@@ -694,10 +1358,10 @@
 def _buildClasses():
 	import re
 	from .otData import otData
-	
+
 	formatPat = re.compile("([A-Za-z0-9]+)Format(\d+)$")
 	namespace = globals()
-	
+
 	# populate module with classes
 	for name, table in otData:
 		baseClass = BaseTable
@@ -709,13 +1373,15 @@
 		if name not in namespace:
 			# the class doesn't exist yet, so the base implementation is used.
 			cls = type(name, (baseClass,), {})
+			if name in ('GSUB', 'GPOS'):
+				cls.DontShare = True
 			namespace[name] = cls
-	
+
 	for base, alts in _equivalents.items():
 		base = namespace[base]
 		for alt in alts:
-			namespace[alt] = type(alt, (base,), {})
-	
+			namespace[alt] = base
+
 	global lookupTypes
 	lookupTypes = {
 		'GSUB': {
@@ -739,6 +1405,17 @@
 			8: ChainContextPos,
 			9: ExtensionPos,
 		},
+		'mort': {
+			4: NoncontextualMorph,
+		},
+		'morx': {
+			0: RearrangementMorph,
+			1: ContextualMorph,
+			2: LigatureMorph,
+			# 3: Reserved,
+			4: NoncontextualMorph,
+			# 5: InsertionMorph,
+		},
 	}
 	lookupTypes['JSTF'] = lookupTypes['GPOS']  # JSTF contains GPOS
 	for lookupEnum in lookupTypes.values():
@@ -753,7 +1430,7 @@
 		featureParamTypes['ss%02d' % i] = FeatureParamsStylisticSet
 	for i in range(1, 99+1):
 		featureParamTypes['cv%02d' % i] = FeatureParamsCharacterVariants
-	
+
 	# add converters to classes
 	from .otConverters import buildConverters
 	for name, table in otData:
@@ -769,9 +1446,11 @@
 			converters, convertersByName = buildConverters(table[1:], namespace)
 			cls.converters[format] = converters
 			cls.convertersByName[format] = convertersByName
+			# XXX Add staticSize?
 		else:
 			cls = namespace[name]
 			cls.converters, cls.convertersByName = buildConverters(table, namespace)
+			# XXX Add staticSize?
 
 
 _buildClasses()
diff --git a/Lib/fontTools/ttLib/tables/sbixBitmap.py b/Lib/fontTools/ttLib/tables/sbixBitmap.py
deleted file mode 100644
index 96da4e1..0000000
--- a/Lib/fontTools/ttLib/tables/sbixBitmap.py
+++ /dev/null
@@ -1,104 +0,0 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
-from fontTools.misc import sstruct
-from fontTools.misc.textTools import readHex
-import struct
-
-
-sbixBitmapHeaderFormat = """
-	>
-	usReserved1:     H    # 00 00
-	usReserved2:     H    #       00 00
-	imageFormatTag:  4s   # e.g. "png "
-"""
-
-sbixBitmapHeaderFormatSize = sstruct.calcsize(sbixBitmapHeaderFormat)
-
-
-class Bitmap(object):
-	def __init__(self, glyphName=None, referenceGlyphName=None, usReserved1=0, usReserved2=0, imageFormatTag=None, imageData=None, rawdata=None, gid=0):
-		self.gid = gid
-		self.glyphName = glyphName
-		self.referenceGlyphName = referenceGlyphName
-		self.usReserved1 = usReserved1
-		self.usReserved2 = usReserved2
-		self.rawdata = rawdata
-		self.imageFormatTag = imageFormatTag
-		self.imageData = imageData
-
-	def decompile(self, ttFont):
-		self.glyphName = ttFont.getGlyphName(self.gid)
-		if self.rawdata is None:
-			from fontTools import ttLib
-			raise ttLib.TTLibError("No table data to decompile")
-		if len(self.rawdata) > 0:
-			if len(self.rawdata) < sbixBitmapHeaderFormatSize:
-				from fontTools import ttLib
-				#print "Bitmap %i header too short: Expected %x, got %x." % (self.gid, sbixBitmapHeaderFormatSize, len(self.rawdata))
-				raise ttLib.TTLibError("Bitmap header too short.")
-
-			sstruct.unpack(sbixBitmapHeaderFormat, self.rawdata[:sbixBitmapHeaderFormatSize], self)
-
-			if self.imageFormatTag == "dupe":
-				# bitmap is a reference to another glyph's bitmap
-				gid, = struct.unpack(">H", self.rawdata[sbixBitmapHeaderFormatSize:])
-				self.referenceGlyphName = ttFont.getGlyphName(gid)
-			else:
-				self.imageData = self.rawdata[sbixBitmapHeaderFormatSize:]
-				self.referenceGlyphName = None
-		# clean up
-		del self.rawdata
-		del self.gid
-
-	def compile(self, ttFont):
-		if self.glyphName is None:
-			from fontTools import ttLib
-			raise ttLib.TTLibError("Can't compile bitmap without glyph name")
-			# TODO: if ttFont has no maxp, cmap etc., ignore glyph names and compile by index?
-			# (needed if you just want to compile the sbix table on its own)
-		self.gid = struct.pack(">H", ttFont.getGlyphID(self.glyphName))
-		if self.imageFormatTag is None:
-			self.rawdata = ""
-		else:
-			self.rawdata = sstruct.pack(sbixBitmapHeaderFormat, self) + self.imageData
-
-	def toXML(self, xmlWriter, ttFont):
-		if self.imageFormatTag == None:
-			# TODO: ignore empty bitmaps?
-			# a bitmap entry is required for each glyph,
-			# but empty ones can be calculated at compile time
-			xmlWriter.simpletag("bitmap", glyphname=self.glyphName)
-			xmlWriter.newline()
-			return
-		xmlWriter.begintag("bitmap", format=self.imageFormatTag, glyphname=self.glyphName)
-		xmlWriter.newline()
-		#xmlWriter.simpletag("usReserved1", value=self.usReserved1)
-		#xmlWriter.newline()
-		#xmlWriter.simpletag("usReserved2", value=self.usReserved2)
-		#xmlWriter.newline()
-		if self.imageFormatTag == "dupe":
-			# format == "dupe" is apparently a reference to another glyph id.
-			xmlWriter.simpletag("ref", glyphname=self.referenceGlyphName)
-		else:
-			xmlWriter.begintag("hexdata")
-			xmlWriter.newline()
-			xmlWriter.dumphex(self.imageData)
-			xmlWriter.endtag("hexdata")
-		xmlWriter.newline()
-		xmlWriter.endtag("bitmap")
-		xmlWriter.newline()
-
-	def fromXML(self, name, attrs, content, ttFont):
-		#if name in ["usReserved1", "usReserved2"]:
-		#	setattr(self, name, int(attrs["value"]))
-		#elif
-		if name == "ref":
-			# bitmap is a "dupe", i.e. a reference to another bitmap.
-			# in this case imageData contains the glyph id of the reference glyph
-			# get glyph id from glyphname
-			self.imageData = struct.pack(">H", ttFont.getGlyphID(attrs["glyphname"]))
-		elif name == "hexdata":
-			self.imageData = readHex(content)
-		else:
-			from fontTools import ttLib
-			raise ttLib.TTLibError("can't handle '%s' element" % name)
diff --git a/Lib/fontTools/ttLib/tables/sbixBitmapSet.py b/Lib/fontTools/ttLib/tables/sbixBitmapSet.py
deleted file mode 100644
index b5786ec..0000000
--- a/Lib/fontTools/ttLib/tables/sbixBitmapSet.py
+++ /dev/null
@@ -1,138 +0,0 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
-from fontTools.misc import sstruct
-from fontTools.misc.textTools import readHex
-from .sbixBitmap import *
-import struct
-
-sbixBitmapSetHeaderFormat = """
-	>
-	size:            H    # 00 28
-	resolution:      H    #       00 48
-"""
-
-sbixBitmapOffsetEntryFormat = """
-	>
-	ulOffset:        L    # 00 00 07 E0 # Offset from start of first offset entry to each bitmap
-"""
-
-sbixBitmapSetHeaderFormatSize = sstruct.calcsize(sbixBitmapSetHeaderFormat)
-sbixBitmapOffsetEntryFormatSize = sstruct.calcsize(sbixBitmapOffsetEntryFormat)
-
-
-class BitmapSet(object):
-	def __init__(self, rawdata=None, size=0, resolution=72):
-		self.data = rawdata
-		self.size = size
-		self.resolution = resolution
-		self.bitmaps = {}
-
-	def decompile(self, ttFont):
-		if self.data is None:
-			from fontTools import ttLib
-			raise ttLib.TTLibError
-		if len(self.data) < sbixBitmapSetHeaderFormatSize:
-			from fontTools import ttLib
-			raise(ttLib.TTLibError, "BitmapSet header too short: Expected %x, got %x.") \
-				% (sbixBitmapSetHeaderFormatSize, len(self.data))
-
-		# read BitmapSet header from raw data
-		sstruct.unpack(sbixBitmapSetHeaderFormat, self.data[:sbixBitmapSetHeaderFormatSize], self)
-
-		# calculate number of bitmaps
-		firstBitmapOffset, = struct.unpack(">L", \
-			self.data[sbixBitmapSetHeaderFormatSize : sbixBitmapSetHeaderFormatSize + sbixBitmapOffsetEntryFormatSize])
-		self.numBitmaps = (firstBitmapOffset - sbixBitmapSetHeaderFormatSize) // sbixBitmapOffsetEntryFormatSize - 1
-		# ^ -1 because there's one more offset than bitmaps
-
-		# build offset list for single bitmap offsets
-		self.bitmapOffsets = []
-		for i in range(self.numBitmaps + 1): # + 1 because there's one more offset than bitmaps
-			start = i * sbixBitmapOffsetEntryFormatSize + sbixBitmapSetHeaderFormatSize
-			myOffset, = struct.unpack(">L", self.data[start : start + sbixBitmapOffsetEntryFormatSize])
-			self.bitmapOffsets.append(myOffset)
-
-		# iterate through offset list and slice raw data into bitmaps
-		for i in range(self.numBitmaps):
-			myBitmap = Bitmap(rawdata=self.data[self.bitmapOffsets[i] : self.bitmapOffsets[i+1]], gid=i)
-			myBitmap.decompile(ttFont)
-			self.bitmaps[myBitmap.glyphName] = myBitmap
-		del self.bitmapOffsets
-		del self.data
-
-	def compile(self, ttFont):
-		self.bitmapOffsets = ""
-		self.bitmapData = ""
-
-		glyphOrder = ttFont.getGlyphOrder()
-
-		# first bitmap starts right after the header
-		bitmapOffset = sbixBitmapSetHeaderFormatSize + sbixBitmapOffsetEntryFormatSize * (len(glyphOrder) + 1)
-		for glyphName in glyphOrder:
-			if glyphName in self.bitmaps:
-				# we have a bitmap for this glyph
-				myBitmap = self.bitmaps[glyphName]
-			else:
-				# must add empty bitmap for this glyph
-				myBitmap = Bitmap(glyphName=glyphName)
-			myBitmap.compile(ttFont)
-			myBitmap.ulOffset = bitmapOffset
-			self.bitmapData += myBitmap.rawdata
-			bitmapOffset += len(myBitmap.rawdata)
-			self.bitmapOffsets += sstruct.pack(sbixBitmapOffsetEntryFormat, myBitmap)
-
-		# add last "offset", really the end address of the last bitmap
-		dummy = Bitmap()
-		dummy.ulOffset = bitmapOffset
-		self.bitmapOffsets += sstruct.pack(sbixBitmapOffsetEntryFormat, dummy)
-
-		# bitmap sets are padded to 4 byte boundaries
-		dataLength = len(self.bitmapOffsets) + len(self.bitmapData)
-		if dataLength % 4 != 0:
-			padding = 4 - (dataLength % 4)
-		else:
-			padding = 0
-
-		# pack header
-		self.data = sstruct.pack(sbixBitmapSetHeaderFormat, self)
-		# add offset, image data and padding after header
-		self.data += self.bitmapOffsets + self.bitmapData + "\0" * padding
-
-	def toXML(self, xmlWriter, ttFont):
-		xmlWriter.begintag("bitmapSet")
-		xmlWriter.newline()
-		xmlWriter.simpletag("size", value=self.size)
-		xmlWriter.newline()
-		xmlWriter.simpletag("resolution", value=self.resolution)
-		xmlWriter.newline()
-		glyphOrder = ttFont.getGlyphOrder()
-		for i in range(len(glyphOrder)):
-			if glyphOrder[i] in self.bitmaps:
-				self.bitmaps[glyphOrder[i]].toXML(xmlWriter, ttFont)
-				# TODO: what if there are more bitmaps than glyphs?
-		xmlWriter.endtag("bitmapSet")
-		xmlWriter.newline()
-
-	def fromXML(self, name, attrs, content, ttFont):
-		if name in ["size", "resolution"]:
-			setattr(self, name, int(attrs["value"]))
-		elif name == "bitmap":
-			if "format" in attrs:
-				myFormat = attrs["format"]
-			else:
-				myFormat = None
-			if "glyphname" in attrs:
-				myGlyphName = attrs["glyphname"]
-			else:
-				from fontTools import ttLib
-				raise ttLib.TTLibError("Bitmap must have a glyph name.")
-			myBitmap = Bitmap(glyphName=myGlyphName, imageFormatTag=myFormat)
-			for element in content:
-				if isinstance(element, tuple):
-					name, attrs, content = element
-					myBitmap.fromXML(name, attrs, content, ttFont)
-					myBitmap.compile(ttFont)
-			self.bitmaps[myBitmap.glyphName] = myBitmap
-		else:
-			from fontTools import ttLib
-			raise ttLib.TTLibError("can't handle '%s' element" % name)
diff --git a/Lib/fontTools/ttLib/tables/sbixGlyph.py b/Lib/fontTools/ttLib/tables/sbixGlyph.py
new file mode 100644
index 0000000..c053908
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/sbixGlyph.py
@@ -0,0 +1,119 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import readHex, safeEval
+import struct
+
+
+sbixGlyphHeaderFormat = """
+	>
+	originOffsetX: h	# The x-value of the point in the glyph relative to its
+						# lower-left corner which corresponds to the origin of
+						# the glyph on the screen, that is the point on the
+						# baseline at the left edge of the glyph.
+	originOffsetY: h	# The y-value of the point in the glyph relative to its
+						# lower-left corner which corresponds to the origin of
+						# the glyph on the screen, that is the point on the
+						# baseline at the left edge of the glyph.
+	graphicType:  4s	# e.g. "png "
+"""
+
+sbixGlyphHeaderFormatSize = sstruct.calcsize(sbixGlyphHeaderFormat)
+
+
+class Glyph(object):
+	def __init__(self, glyphName=None, referenceGlyphName=None, originOffsetX=0, originOffsetY=0, graphicType=None, imageData=None, rawdata=None, gid=0):
+		self.gid = gid
+		self.glyphName = glyphName
+		self.referenceGlyphName = referenceGlyphName
+		self.originOffsetX = originOffsetX
+		self.originOffsetY = originOffsetY
+		self.rawdata = rawdata
+		self.graphicType = graphicType
+		self.imageData = imageData
+
+		# fix self.graphicType if it is null terminated or too short
+		if self.graphicType is not None:
+			if self.graphicType[-1] == "\0":
+				self.graphicType = self.graphicType[:-1]
+			if len(self.graphicType) > 4:
+				from fontTools import ttLib
+				raise ttLib.TTLibError("Glyph.graphicType must not be longer than 4 characters.")
+			elif len(self.graphicType) < 4:
+				# pad with spaces
+				self.graphicType += "    "[:(4 - len(self.graphicType))]
+
+	def decompile(self, ttFont):
+		self.glyphName = ttFont.getGlyphName(self.gid)
+		if self.rawdata is None:
+			from fontTools import ttLib
+			raise ttLib.TTLibError("No table data to decompile")
+		if len(self.rawdata) > 0:
+			if len(self.rawdata) < sbixGlyphHeaderFormatSize:
+				from fontTools import ttLib
+				#print "Glyph %i header too short: Expected %x, got %x." % (self.gid, sbixGlyphHeaderFormatSize, len(self.rawdata))
+				raise ttLib.TTLibError("Glyph header too short.")
+
+			sstruct.unpack(sbixGlyphHeaderFormat, self.rawdata[:sbixGlyphHeaderFormatSize], self)
+
+			if self.graphicType == "dupe":
+				# this glyph is a reference to another glyph's image data
+				gid, = struct.unpack(">H", self.rawdata[sbixGlyphHeaderFormatSize:])
+				self.referenceGlyphName = ttFont.getGlyphName(gid)
+			else:
+				self.imageData = self.rawdata[sbixGlyphHeaderFormatSize:]
+				self.referenceGlyphName = None
+		# clean up
+		del self.rawdata
+		del self.gid
+
+	def compile(self, ttFont):
+		if self.glyphName is None:
+			from fontTools import ttLib
+			raise ttLib.TTLibError("Can't compile Glyph without glyph name")
+			# TODO: if ttFont has no maxp, cmap etc., ignore glyph names and compile by index?
+			# (needed if you just want to compile the sbix table on its own)
+		self.gid = struct.pack(">H", ttFont.getGlyphID(self.glyphName))
+		if self.graphicType is None:
+			self.rawdata = b""
+		else:
+			self.rawdata = sstruct.pack(sbixGlyphHeaderFormat, self) + self.imageData
+
+	def toXML(self, xmlWriter, ttFont):
+		if self.graphicType == None:
+			# TODO: ignore empty glyphs?
+			# a glyph data entry is required for each glyph,
+			# but empty ones can be calculated at compile time
+			xmlWriter.simpletag("glyph", name=self.glyphName)
+			xmlWriter.newline()
+			return
+		xmlWriter.begintag("glyph",
+			graphicType=self.graphicType,
+			name=self.glyphName,
+			originOffsetX=self.originOffsetX,
+			originOffsetY=self.originOffsetY,
+		)
+		xmlWriter.newline()
+		if self.graphicType == "dupe":
+			# graphicType == "dupe" is a reference to another glyph id.
+			xmlWriter.simpletag("ref", glyphname=self.referenceGlyphName)
+		else:
+			xmlWriter.begintag("hexdata")
+			xmlWriter.newline()
+			xmlWriter.dumphex(self.imageData)
+			xmlWriter.endtag("hexdata")
+		xmlWriter.newline()
+		xmlWriter.endtag("glyph")
+		xmlWriter.newline()
+
+	def fromXML(self, name, attrs, content, ttFont):
+		if name == "ref":
+			# glyph is a "dupe", i.e. a reference to another glyph's image data.
+			# in this case imageData contains the glyph id of the reference glyph
+			# get glyph id from glyphname
+			self.imageData = struct.pack(">H", ttFont.getGlyphID(safeEval("'''" + attrs["glyphname"] + "'''")))
+		elif name == "hexdata":
+			self.imageData = readHex(content)
+		else:
+			from fontTools import ttLib
+			raise ttLib.TTLibError("can't handle '%s' element" % name)
diff --git a/Lib/fontTools/ttLib/tables/sbixStrike.py b/Lib/fontTools/ttLib/tables/sbixStrike.py
new file mode 100644
index 0000000..6c1ac77
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/sbixStrike.py
@@ -0,0 +1,150 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import readHex
+from .sbixGlyph import *
+import struct
+
+sbixStrikeHeaderFormat = """
+	>
+	ppem:          H	# The PPEM for which this strike was designed (e.g., 9,
+						# 12, 24)
+	resolution:    H	# The screen resolution (in dpi) for which this strike
+						# was designed (e.g., 72)
+"""
+
+sbixGlyphDataOffsetFormat = """
+	>
+	glyphDataOffset:   L	# Offset from the beginning of the strike data record
+							# to data for the individual glyph
+"""
+
+sbixStrikeHeaderFormatSize = sstruct.calcsize(sbixStrikeHeaderFormat)
+sbixGlyphDataOffsetFormatSize = sstruct.calcsize(sbixGlyphDataOffsetFormat)
+
+
+class Strike(object):
+	def __init__(self, rawdata=None, ppem=0, resolution=72):
+		self.data = rawdata
+		self.ppem = ppem
+		self.resolution = resolution
+		self.glyphs = {}
+
+	def decompile(self, ttFont):
+		if self.data is None:
+			from fontTools import ttLib
+			raise ttLib.TTLibError
+		if len(self.data) < sbixStrikeHeaderFormatSize:
+			from fontTools import ttLib
+			raise(ttLib.TTLibError, "Strike header too short: Expected %x, got %x.") \
+				% (sbixStrikeHeaderFormatSize, len(self.data))
+
+		# read Strike header from raw data
+		sstruct.unpack(sbixStrikeHeaderFormat, self.data[:sbixStrikeHeaderFormatSize], self)
+
+		# calculate number of glyphs
+		firstGlyphDataOffset, = struct.unpack(">L", \
+			self.data[sbixStrikeHeaderFormatSize:sbixStrikeHeaderFormatSize + sbixGlyphDataOffsetFormatSize])
+		self.numGlyphs = (firstGlyphDataOffset - sbixStrikeHeaderFormatSize) // sbixGlyphDataOffsetFormatSize - 1
+		# ^ -1 because there's one more offset than glyphs
+
+		# build offset list for single glyph data offsets
+		self.glyphDataOffsets = []
+		for i in range(self.numGlyphs + 1): # + 1 because there's one more offset than glyphs
+			start = i * sbixGlyphDataOffsetFormatSize + sbixStrikeHeaderFormatSize
+			current_offset, = struct.unpack(">L", self.data[start:start + sbixGlyphDataOffsetFormatSize])
+			self.glyphDataOffsets.append(current_offset)
+
+		# iterate through offset list and slice raw data into glyph data records
+		for i in range(self.numGlyphs):
+			current_glyph = Glyph(rawdata=self.data[self.glyphDataOffsets[i]:self.glyphDataOffsets[i+1]], gid=i)
+			current_glyph.decompile(ttFont)
+			self.glyphs[current_glyph.glyphName] = current_glyph
+		del self.glyphDataOffsets
+		del self.numGlyphs
+		del self.data
+
+	def compile(self, ttFont):
+		self.glyphDataOffsets = b""
+		self.bitmapData = b""
+
+		glyphOrder = ttFont.getGlyphOrder()
+
+		# first glyph starts right after the header
+		currentGlyphDataOffset = sbixStrikeHeaderFormatSize + sbixGlyphDataOffsetFormatSize * (len(glyphOrder) + 1)
+		for glyphName in glyphOrder:
+			if glyphName in self.glyphs:
+				# we have glyph data for this glyph
+				current_glyph = self.glyphs[glyphName]
+			else:
+				# must add empty glyph data record for this glyph
+				current_glyph = Glyph(glyphName=glyphName)
+			current_glyph.compile(ttFont)
+			current_glyph.glyphDataOffset = currentGlyphDataOffset
+			self.bitmapData += current_glyph.rawdata
+			currentGlyphDataOffset += len(current_glyph.rawdata)
+			self.glyphDataOffsets += sstruct.pack(sbixGlyphDataOffsetFormat, current_glyph)
+
+		# add last "offset", really the end address of the last glyph data record
+		dummy = Glyph()
+		dummy.glyphDataOffset = currentGlyphDataOffset
+		self.glyphDataOffsets += sstruct.pack(sbixGlyphDataOffsetFormat, dummy)
+
+		# pack header
+		self.data = sstruct.pack(sbixStrikeHeaderFormat, self)
+		# add offsets and image data after header
+		self.data += self.glyphDataOffsets + self.bitmapData
+
+	def toXML(self, xmlWriter, ttFont):
+		xmlWriter.begintag("strike")
+		xmlWriter.newline()
+		xmlWriter.simpletag("ppem", value=self.ppem)
+		xmlWriter.newline()
+		xmlWriter.simpletag("resolution", value=self.resolution)
+		xmlWriter.newline()
+		glyphOrder = ttFont.getGlyphOrder()
+		for i in range(len(glyphOrder)):
+			if glyphOrder[i] in self.glyphs:
+				self.glyphs[glyphOrder[i]].toXML(xmlWriter, ttFont)
+				# TODO: what if there are more glyph data records than (glyf table) glyphs?
+		xmlWriter.endtag("strike")
+		xmlWriter.newline()
+
+	def fromXML(self, name, attrs, content, ttFont):
+		if name in ["ppem", "resolution"]:
+			setattr(self, name, safeEval(attrs["value"]))
+		elif name == "glyph":
+			if "graphicType" in attrs:
+				myFormat = safeEval("'''" + attrs["graphicType"] + "'''")
+			else:
+				myFormat = None
+			if "glyphname" in attrs:
+				myGlyphName = safeEval("'''" + attrs["glyphname"] + "'''")
+			elif "name" in attrs:
+				myGlyphName = safeEval("'''" + attrs["name"] + "'''")
+			else:
+				from fontTools import ttLib
+				raise ttLib.TTLibError("Glyph must have a glyph name.")
+			if "originOffsetX" in attrs:
+				myOffsetX = safeEval(attrs["originOffsetX"])
+			else:
+				myOffsetX = 0
+			if "originOffsetY" in attrs:
+				myOffsetY = safeEval(attrs["originOffsetY"])
+			else:
+				myOffsetY = 0
+			current_glyph = Glyph(
+				glyphName=myGlyphName,
+				graphicType=myFormat,
+				originOffsetX=myOffsetX,
+				originOffsetY=myOffsetY,
+			)
+			for element in content:
+				if isinstance(element, tuple):
+					name, attrs, content = element
+					current_glyph.fromXML(name, attrs, content, ttFont)
+					current_glyph.compile(ttFont)
+			self.glyphs[current_glyph.glyphName] = current_glyph
+		else:
+			from fontTools import ttLib
+			raise ttLib.TTLibError("can't handle '%s' element" % name)
diff --git a/Lib/fontTools/ttLib/tables/ttProgram.py b/Lib/fontTools/ttLib/tables/ttProgram.py
index 03f778e..182982f 100644
--- a/Lib/fontTools/ttLib/tables/ttProgram.py
+++ b/Lib/fontTools/ttLib/tables/ttProgram.py
@@ -5,146 +5,148 @@
 from fontTools.misc.textTools import num2binary, binary2num, readHex
 import array
 import re
+import logging
+
+
+log = logging.getLogger(__name__)
 
 # first, the list of instructions that eat bytes or words from the instruction stream
 
 streamInstructions = [
-#	------  -----------  -----  ------------------------ ---  ------  ----------------------------------  --------------
-#	opcode     mnemonic argBits         descriptive name pops pushes        eats from instruction stream          pushes
-#	------  -----------  -----  ------------------------ ---  ------  ----------------------------------  --------------
-	(0x40,    'NPUSHB',     0,             'PushNBytes',  0, -1), #                      n, b1, b2,...bn      b1,b2...bn
-	(0x41,    'NPUSHW',     0,             'PushNWords',  0, -1), #                       n, w1, w2,...w      w1,w2...wn
-	(0xb0,     'PUSHB',     3,              'PushBytes',  0, -1), #                          b0, b1,..bn  b0, b1, ...,bn
-	(0xb8,     'PUSHW',     3,              'PushWords',  0, -1), #                           w0,w1,..wn   w0 ,w1, ...wn
-#	------  -----------  -----  ------------------------ ---  ------  ----------------------------------  --------------
+#
+#	opcode  mnemonic     argBits    descriptive name      pops pushes         eats from instruction stream          pushes
+#
+	(0x40,	'NPUSHB',	0,	'PushNBytes',		0, -1),	#                      n, b1, b2,...bn      b1,b2...bn
+	(0x41,	'NPUSHW',	0,	'PushNWords',		0, -1),	#                       n, w1, w2,...w      w1,w2...wn
+	(0xb0,	'PUSHB',	3,	'PushBytes',		0, -1),	#                          b0, b1,..bn  b0, b1, ...,bn
+	(0xb8,	'PUSHW',	3,	'PushWords',		0, -1),	#                           w0,w1,..wn   w0 ,w1, ...wn
 ]
 
 
-# next, the list of "normal" instructions
+# next,	the list of "normal" instructions
 
 instructions = [
-#	------  -----------  -----  ------------------------ ---  ------  ----------------------------------  --------------
-#	opcode     mnemonic  argBits        descriptive name pops pushes                                pops          pushes
-#	------  -----------  -----  ------------------------ ---  ------  ----------------------------------  --------------
-	(0x7f,        'AA',     0,            'AdjustAngle',  1,  0), #                                    p               -
-	(0x64,       'ABS',     0,               'Absolute',  1,  1), #                                    n             |n|
-	(0x60,       'ADD',     0,                    'Add',  2,  1), #                               n2, n1       (n1 + n2)
-	(0x27,  'ALIGNPTS',     0,               'AlignPts',  2,  0), #                               p2, p1               -
-	(0x3c,   'ALIGNRP',     0,        'AlignRelativePt', -1,  0), #             p1, p2, ... , ploopvalue               -
-	(0x5a,       'AND',     0,             'LogicalAnd',  2,  1), #                               e2, e1               b
-	(0x2b,      'CALL',     0,           'CallFunction',  1,  0), #                                    f               -
-	(0x67,   'CEILING',     0,                'Ceiling',  1,  1), #                                    n         ceil(n)
-	(0x25,    'CINDEX',     0,        'CopyXToTopStack',  1,  1), #                                    k              ek
-	(0x22,     'CLEAR',     0,             'ClearStack', -1,  0), #               all items on the stack               -
-	(0x4f,     'DEBUG',     0,              'DebugCall',  1,  0), #                                    n               -
-	(0x73,   'DELTAC1',     0,       'DeltaExceptionC1', -1,  0), #    argn, cn, argn-1,cn-1, , arg1, c1               -
-	(0x74,   'DELTAC2',     0,       'DeltaExceptionC2', -1,  0), #    argn, cn, argn-1,cn-1, , arg1, c1               -
-	(0x75,   'DELTAC3',     0,       'DeltaExceptionC3', -1,  0), #    argn, cn, argn-1,cn-1, , arg1, c1               -
-	(0x5d,   'DELTAP1',     0,       'DeltaExceptionP1', -1,  0), #   argn, pn, argn-1, pn-1, , arg1, p1               -
-	(0x71,   'DELTAP2',     0,       'DeltaExceptionP2', -1,  0), #   argn, pn, argn-1, pn-1, , arg1, p1               -
-	(0x72,   'DELTAP3',     0,       'DeltaExceptionP3', -1,  0), #   argn, pn, argn-1, pn-1, , arg1, p1               -
-	(0x24,     'DEPTH',     0,          'GetDepthStack',  0,  1), #                                    -               n
-	(0x62,       'DIV',     0,                 'Divide',  2,  1), #                               n2, n1   (n1 * 64)/ n2
-	(0x20,       'DUP',     0,      'DuplicateTopStack',  1,  2), #                                    e            e, e
-	(0x59,       'EIF',     0,                  'EndIf',  0,  0), #                                    -               -
-	(0x1b,      'ELSE',     0,                   'Else',  0,  0), #                                    -               -
-	(0x2d,      'ENDF',     0,  'EndFunctionDefinition',  0,  0), #                                    -               -
-	(0x54,        'EQ',     0,                  'Equal',  2,  1), #                               e2, e1               b
-	(0x57,      'EVEN',     0,                   'Even',  1,  1), #                                    e               b
-	(0x2c,      'FDEF',     0,     'FunctionDefinition',  1,  0), #                                    f               -
-	(0x4e,   'FLIPOFF',     0,         'SetAutoFlipOff',  0,  0), #                                    -               -
-	(0x4d,    'FLIPON',     0,          'SetAutoFlipOn',  0,  0), #                                    -               -
-	(0x80,    'FLIPPT',     0,              'FlipPoint', -1,  0), #              p1, p2, ..., ploopvalue               -
-	(0x82, 'FLIPRGOFF',     0,           'FlipRangeOff',  2,  0), #                                 h, l               -
-	(0x81,  'FLIPRGON',     0,            'FlipRangeOn',  2,  0), #                                 h, l               -
-	(0x66,     'FLOOR',     0,                  'Floor',  1,  1), #                                    n        floor(n)
-	(0x46,        'GC',     1,      'GetCoordOnPVector',  1,  1), #                                    p               c
-	(0x88,   'GETINFO',     0,                'GetInfo',  1,  1), #                             selector          result
-	(0x0d,       'GFV',     0,             'GetFVector',  0,  2), #                                    -          px, py
-	(0x0c,       'GPV',     0,             'GetPVector',  0,  2), #                                    -          px, py
-	(0x52,        'GT',     0,            'GreaterThan',  2,  1), #                               e2, e1               b
-	(0x53,      'GTEQ',     0,     'GreaterThanOrEqual',  2,  1), #                               e2, e1               b
-	(0x89,      'IDEF',     0,  'InstructionDefinition',  1,  0), #                                    f               -
-	(0x58,        'IF',     0,                     'If',  1,  0), #                                    e               -
-	(0x8e,  'INSTCTRL',     0,    'SetInstrExecControl',  2,  0), #                                 s, v               -
-	(0x39,        'IP',     0,         'InterpolatePts', -1,  0), #             p1, p2, ... , ploopvalue               -
-	(0x0f,     'ISECT',     0,      'MovePtToIntersect',  5,  0), #                    a1, a0, b1, b0, p               -
-	(0x30,       'IUP',     1,      'InterpolateUntPts',  0,  0), #                                    -               -
-	(0x1c,      'JMPR',     0,                   'Jump',  1,  0), #                               offset               -
-	(0x79,      'JROF',     0,    'JumpRelativeOnFalse',  2,  0), #                            e, offset               -
-	(0x78,      'JROT',     0,     'JumpRelativeOnTrue',  2,  0), #                            e, offset               -
-	(0x2a,  'LOOPCALL',     0,    'LoopAndCallFunction',  2,  0), #                             f, count               -
-	(0x50,        'LT',     0,               'LessThan',  2,  1), #                               e2, e1               b
-	(0x51,      'LTEQ',     0,        'LessThenOrEqual',  2,  1), #                               e2, e1               b
-	(0x8b,       'MAX',     0,                'Maximum',  2,  1), #                               e2, e1     max(e1, e2)
-	(0x49,        'MD',     1,        'MeasureDistance',  2,  1), #                                p2,p1               d
-	(0x2e,      'MDAP',     1,        'MoveDirectAbsPt',  1,  0), #                                    p               -
-	(0xc0,      'MDRP',     5,        'MoveDirectRelPt',  1,  0), #                                    p               -
-	(0x3e,      'MIAP',     1,      'MoveIndirectAbsPt',  2,  0), #                                 n, p               -
-	(0x8c,       'MIN',     0,                'Minimum',  2,  1), #                               e2, e1     min(e1, e2)
-	(0x26,    'MINDEX',     0,        'MoveXToTopStack',  1,  1), #                                    k              ek
-	(0xe0,      'MIRP',     5,      'MoveIndirectRelPt',  2,  0), #                                 n, p               -
-	(0x4b,     'MPPEM',     0,      'MeasurePixelPerEm',  0,  1), #                                    -            ppem
-	(0x4c,       'MPS',     0,       'MeasurePointSize',  0,  1), #                                    -       pointSize
-	(0x3a,     'MSIRP',     1,    'MoveStackIndirRelPt',  2,  0), #                                 d, p               -
-	(0x63,       'MUL',     0,               'Multiply',  2,  1), #                               n2, n1    (n1 * n2)/64
-	(0x65,       'NEG',     0,                 'Negate',  1,  1), #                                    n              -n
-	(0x55,       'NEQ',     0,               'NotEqual',  2,  1), #                               e2, e1               b
-	(0x5c,       'NOT',     0,             'LogicalNot',  1,  1), #                                    e       ( not e )
-	(0x6c,    'NROUND',     2,                'NoRound',  1,  1), #                                   n1              n2
-	(0x56,       'ODD',     0,                    'Odd',  1,  1), #                                    e               b
-	(0x5b,        'OR',     0,              'LogicalOr',  2,  1), #                               e2, e1               b
-	(0x21,       'POP',     0,            'PopTopStack',  1,  0), #                                    e               -
-	(0x45,      'RCVT',     0,                'ReadCVT',  1,  1), #                             location           value
-	(0x7d,      'RDTG',     0,        'RoundDownToGrid',  0,  0), #                                    -               -
-	(0x7a,      'ROFF',     0,               'RoundOff',  0,  0), #                                    -               -
-	(0x8a,      'ROLL',     0,      'RollTopThreeStack',  3,  3), #                                a,b,c           b,a,c
-	(0x68,     'ROUND',     2,                  'Round',  1,  1), #                                   n1              n2
-	(0x43,        'RS',     0,              'ReadStore',  1,  1), #                                    n               v
-	(0x3d,      'RTDG',     0,      'RoundToDoubleGrid',  0,  0), #                                    -               -
-	(0x18,       'RTG',     0,            'RoundToGrid',  0,  0), #                                    -               -
-	(0x19,      'RTHG',     0,        'RoundToHalfGrid',  0,  0), #                                    -               -
-	(0x7c,      'RUTG',     0,          'RoundUpToGrid',  0,  0), #                                    -               -
-	(0x77,  'S45ROUND',     0,    'SuperRound45Degrees',  1,  0), #                                    n               -
-	(0x7e,     'SANGW',     0,         'SetAngleWeight',  1,  0), #                               weight               -
-	(0x85,  'SCANCTRL',     0,  'ScanConversionControl',  1,  0), #                                    n               -
-	(0x8d,  'SCANTYPE',     0,               'ScanType',  1,  0), #                                    n               -
-	(0x48,      'SCFS',     0,    'SetCoordFromStackFP',  2,  0), #                                 c, p               -
-	(0x1d,    'SCVTCI',     0,            'SetCVTCutIn',  1,  0), #                                    n               -
-	(0x5e,       'SDB',     0,   'SetDeltaBaseInGState',  1,  0), #                                    n               -
-	(0x86,    'SDPVTL',     1,   'SetDualPVectorToLine',  2,  0), #                               p2, p1               -
-	(0x5f,       'SDS',     0,  'SetDeltaShiftInGState',  1,  0), #                                    n               -
-	(0x0b,     'SFVFS',     0,    'SetFVectorFromStack',  2,  0), #                                 y, x               -
-	(0x04,    'SFVTCA',     1,       'SetFVectorToAxis',  0,  0), #                                    -               -
-	(0x08,     'SFVTL',     1,       'SetFVectorToLine',  2,  0), #                               p2, p1               -
-	(0x0e,    'SFVTPV',     0,    'SetFVectorToPVector',  0,  0), #                                    -               -
-	(0x34,       'SHC',     1,   'ShiftContourByLastPt',  1,  0), #                                    c               -
-	(0x32,       'SHP',     1,  'ShiftPointByLastPoint', -1,  0), #              p1, p2, ..., ploopvalue               -
-	(0x38,     'SHPIX',     0,       'ShiftZoneByPixel', -1,  0), #           d, p1, p2, ..., ploopvalue               -
-	(0x36,       'SHZ',     1,   'ShiftZoneByLastPoint',  1,  0), #                                    e               -
-	(0x17,     'SLOOP',     0,        'SetLoopVariable',  1,  0), #                                    n               -
-	(0x1a,       'SMD',     0,     'SetMinimumDistance',  1,  0), #                             distance               -
-	(0x0a,     'SPVFS',     0,    'SetPVectorFromStack',  2,  0), #                                 y, x               -
-	(0x02,    'SPVTCA',     1,       'SetPVectorToAxis',  0,  0), #                                    -               -
-	(0x06,     'SPVTL',     1,       'SetPVectorToLine',  2,  0), #                               p2, p1               -
-	(0x76,    'SROUND',     0,             'SuperRound',  1,  0), #                                    n               -
-	(0x10,      'SRP0',     0,           'SetRefPoint0',  1,  0), #                                    p               -
-	(0x11,      'SRP1',     0,           'SetRefPoint1',  1,  0), #                                    p               -
-	(0x12,      'SRP2',     0,           'SetRefPoint2',  1,  0), #                                    p               -
-	(0x1f,       'SSW',     0,         'SetSingleWidth',  1,  0), #                                    n               -
-	(0x1e,     'SSWCI',     0,    'SetSingleWidthCutIn',  1,  0), #                                    n               -
-	(0x61,       'SUB',     0,               'Subtract',  2,  1), #                               n2, n1       (n1 - n2)
-	(0x00,     'SVTCA',     1,      'SetFPVectorToAxis',  0,  0), #                                    -               -
-	(0x23,      'SWAP',     0,           'SwapTopStack',  2,  2), #                               e2, e1          e1, e2
-	(0x13,      'SZP0',     0,        'SetZonePointer0',  1,  0), #                                    n               -
-	(0x14,      'SZP1',     0,        'SetZonePointer1',  1,  0), #                                    n               -
-	(0x15,      'SZP2',     0,        'SetZonePointer2',  1,  0), #                                    n               -
-	(0x16,      'SZPS',     0,        'SetZonePointerS',  1,  0), #                                    n               -
-	(0x29,       'UTP',     0,              'UnTouchPt',  1,  0), #                                    p               -
-	(0x70,     'WCVTF',     0,       'WriteCVTInFUnits',  2,  0), #                                 n, l               -
-	(0x44,     'WCVTP',     0,       'WriteCVTInPixels',  2,  0), #                                 v, l               -
-	(0x42,        'WS',     0,             'WriteStore',  2,  0), #                                 v, l               -
-#	------  -----------  -----  ------------------------ ---  ------  ----------------------------------  --------------
+#
+#,	opcode  mnemonic     argBits    descriptive name      pops pushes         eats from instruction stream          pushes
+#
+	(0x7f,	'AA',		0,	'AdjustAngle',		1, 0),	#                                    p               -
+	(0x64,	'ABS',		0,	'Absolute',		1, 1),	#                                    n             |n|
+	(0x60,	'ADD',		0,	'Add',			2, 1),	#                               n2, n1       (n1 + n2)
+	(0x27,	'ALIGNPTS',	0,	'AlignPts',		2, 0),	#                               p2, p1               -
+	(0x3c,	'ALIGNRP',	0,	'AlignRelativePt',	-1, 0),	#             p1, p2, ... , ploopvalue               -
+	(0x5a,	'AND',		0,	'LogicalAnd',		2, 1),	#                               e2, e1               b
+	(0x2b,	'CALL',		0,	'CallFunction',		1, 0),	#                                    f               -
+	(0x67,	'CEILING',	0,	'Ceiling',		1, 1),	#                                    n         ceil(n)
+	(0x25,	'CINDEX',	0,	'CopyXToTopStack',	1, 1),	#                                    k              ek
+	(0x22,	'CLEAR',	0,	'ClearStack',		-1, 0),	#               all items on the stack               -
+	(0x4f,	'DEBUG',	0,	'DebugCall',		1, 0),	#                                    n               -
+	(0x73,	'DELTAC1',	0,	'DeltaExceptionC1',	-1, 0),	#    argn, cn, argn-1,cn-1, , arg1, c1               -
+	(0x74,	'DELTAC2',	0,	'DeltaExceptionC2',	-1, 0),	#    argn, cn, argn-1,cn-1, , arg1, c1               -
+	(0x75,	'DELTAC3',	0,	'DeltaExceptionC3',	-1, 0),	#    argn, cn, argn-1,cn-1, , arg1, c1               -
+	(0x5d,	'DELTAP1',	0,	'DeltaExceptionP1',	-1, 0),	#   argn, pn, argn-1, pn-1, , arg1, p1               -
+	(0x71,	'DELTAP2',	0,	'DeltaExceptionP2',	-1, 0),	#   argn, pn, argn-1, pn-1, , arg1, p1               -
+	(0x72,	'DELTAP3',	0,	'DeltaExceptionP3',	-1, 0),	#   argn, pn, argn-1, pn-1, , arg1, p1               -
+	(0x24,	'DEPTH',	0,	'GetDepthStack',	0, 1),	#                                    -               n
+	(0x62,	'DIV',		0,	'Divide',		2, 1),	#                               n2, n1   (n1 * 64)/ n2
+	(0x20,	'DUP',		0,	'DuplicateTopStack',	1, 2),	#                                    e            e, e
+	(0x59,	'EIF',		0,	'EndIf',		0, 0),	#                                    -               -
+	(0x1b,	'ELSE',		0,	'Else',			0, 0),	#                                    -               -
+	(0x2d,	'ENDF',		0,	'EndFunctionDefinition', 0, 0),	#                                    -               -
+	(0x54,	'EQ',		0,	'Equal',		2, 1),	#                               e2, e1               b
+	(0x57,	'EVEN',		0,	'Even',			1, 1),	#                                    e               b
+	(0x2c,	'FDEF',		0,	'FunctionDefinition',	1, 0),	#                                    f               -
+	(0x4e,	'FLIPOFF',	0,	'SetAutoFlipOff',	0, 0),	#                                    -               -
+	(0x4d,	'FLIPON',	0,	'SetAutoFlipOn',	0, 0),	#                                    -               -
+	(0x80,	'FLIPPT',	0,	'FlipPoint',		-1, 0),	#              p1, p2, ..., ploopvalue               -
+	(0x82,	'FLIPRGOFF',	0,	'FlipRangeOff',		2, 0),	#                                 h, l               -
+	(0x81,	'FLIPRGON',	0,	'FlipRangeOn',		2, 0),	#                                 h, l               -
+	(0x66,	'FLOOR',	0,	'Floor',		1, 1),	#                                    n        floor(n)
+	(0x46,	'GC',		1,	'GetCoordOnPVector',	1, 1),	#                                    p               c
+	(0x88,	'GETINFO',	0,	'GetInfo',		1, 1),	#                             selector          result
+	(0x0d,	'GFV',		0,	'GetFVector',		0, 2),	#                                    -          px, py
+	(0x0c,	'GPV',		0,	'GetPVector',		0, 2),	#                                    -          px, py
+	(0x52,	'GT',		0,	'GreaterThan',		2, 1),	#                               e2, e1               b
+	(0x53,	'GTEQ',		0,	'GreaterThanOrEqual',	2, 1),	#                               e2, e1               b
+	(0x89,	'IDEF',		0,	'InstructionDefinition', 1, 0),	#                                    f               -
+	(0x58,	'IF',		0,	'If',			1, 0),	#                                    e               -
+	(0x8e,	'INSTCTRL',	0,	'SetInstrExecControl',	2, 0),	#                                 s, v               -
+	(0x39,	'IP',		0,	'InterpolatePts',	-1, 0),	#             p1, p2, ... , ploopvalue               -
+	(0x0f,	'ISECT',	0,	'MovePtToIntersect',	5, 0),	#                    a1, a0, b1, b0, p               -
+	(0x30,	'IUP',		1,	'InterpolateUntPts',	0, 0),	#                                    -               -
+	(0x1c,	'JMPR',		0,	'Jump',			1, 0),	#                               offset               -
+	(0x79,	'JROF',		0,	'JumpRelativeOnFalse',	2, 0),	#                            e, offset               -
+	(0x78,	'JROT',		0,	'JumpRelativeOnTrue',	2, 0),	#                            e, offset               -
+	(0x2a,	'LOOPCALL',	0,	'LoopAndCallFunction',	2, 0),	#                             f, count               -
+	(0x50,	'LT',		0,	'LessThan',		2, 1),	#                               e2, e1               b
+	(0x51,	'LTEQ',		0,	'LessThenOrEqual',	2, 1),	#                               e2, e1               b
+	(0x8b,	'MAX',		0,	'Maximum',		2, 1),	#                               e2, e1     max(e1, e2)
+	(0x49,	'MD',		1,	'MeasureDistance',	2, 1),	#                                p2,p1               d
+	(0x2e,	'MDAP',		1,	'MoveDirectAbsPt',	1, 0),	#                                    p               -
+	(0xc0,	'MDRP',		5,	'MoveDirectRelPt',	1, 0),	#                                    p               -
+	(0x3e,	'MIAP',		1,	'MoveIndirectAbsPt',	2, 0),	#                                 n, p               -
+	(0x8c,	'MIN',		0,	'Minimum',		2, 1),	#                               e2, e1     min(e1, e2)
+	(0x26,	'MINDEX',	0,	'MoveXToTopStack',	1, 1),	#                                    k              ek
+	(0xe0,	'MIRP',		5,	'MoveIndirectRelPt',	2, 0),	#                                 n, p               -
+	(0x4b,	'MPPEM',	0,	'MeasurePixelPerEm',	0, 1),	#                                    -            ppem
+	(0x4c,	'MPS',		0,	'MeasurePointSize',	0, 1),	#                                    -       pointSize
+	(0x3a,	'MSIRP',	1,	'MoveStackIndirRelPt',	2, 0),	#                                 d, p               -
+	(0x63,	'MUL',		0,	'Multiply',		2, 1),	#                               n2, n1    (n1 * n2)/64
+	(0x65,	'NEG',		0,	'Negate',		1, 1),	#                                    n              -n
+	(0x55,	'NEQ',		0,	'NotEqual',		2, 1),	#                               e2, e1               b
+	(0x5c,	'NOT',		0,	'LogicalNot',		1, 1),	#                                    e       ( not e )
+	(0x6c,	'NROUND',	2,	'NoRound',		1, 1),	#                                   n1              n2
+	(0x56,	'ODD',		0,	'Odd',			1, 1),	#                                    e               b
+	(0x5b,	'OR',		0,	'LogicalOr',		2, 1),	#                               e2, e1               b
+	(0x21,	'POP',		0,	'PopTopStack',		1, 0),	#                                    e               -
+	(0x45,	'RCVT',		0,	'ReadCVT',		1, 1),	#                             location           value
+	(0x7d,	'RDTG',		0,	'RoundDownToGrid',	0, 0),	#                                    -               -
+	(0x7a,	'ROFF',		0,	'RoundOff',		0, 0),	#                                    -               -
+	(0x8a,	'ROLL',		0,	'RollTopThreeStack',	3, 3),	#                                a,b,c           b,a,c
+	(0x68,	'ROUND',	2,	'Round',		1, 1),	#                                   n1              n2
+	(0x43,	'RS',		0,	'ReadStore',		1, 1),	#                                    n               v
+	(0x3d,	'RTDG',		0,	'RoundToDoubleGrid',	0, 0),	#                                    -               -
+	(0x18,	'RTG',		0,	'RoundToGrid',		0, 0),	#                                    -               -
+	(0x19,	'RTHG',		0,	'RoundToHalfGrid',	0, 0),	#                                    -               -
+	(0x7c,	'RUTG',		0,	'RoundUpToGrid',	0, 0),	#                                    -               -
+	(0x77,	'S45ROUND',	0,	'SuperRound45Degrees',	1, 0),	#                                    n               -
+	(0x7e,	'SANGW',	0,	'SetAngleWeight',	1, 0),	#                               weight               -
+	(0x85,	'SCANCTRL',	0,	'ScanConversionControl', 1, 0),	#                                    n               -
+	(0x8d,	'SCANTYPE',	0,	'ScanType',		1, 0),	#                                    n               -
+	(0x48,	'SCFS',		0,	'SetCoordFromStackFP',	2, 0),	#                                 c, p               -
+	(0x1d,	'SCVTCI',	0,	'SetCVTCutIn',		1, 0),	#                                    n               -
+	(0x5e,	'SDB',		0,	'SetDeltaBaseInGState',	1, 0),	#                                    n               -
+	(0x86,	'SDPVTL',	1,	'SetDualPVectorToLine',	2, 0),	#                               p2, p1               -
+	(0x5f,	'SDS',		0,	'SetDeltaShiftInGState',1, 0),	#                                    n               -
+	(0x0b,	'SFVFS',	0,	'SetFVectorFromStack',	2, 0),	#                                 y, x               -
+	(0x04,	'SFVTCA',	1,	'SetFVectorToAxis',	0, 0),	#                                    -               -
+	(0x08,	'SFVTL',	1,	'SetFVectorToLine',	2, 0),	#                               p2, p1               -
+	(0x0e,	'SFVTPV',	0,	'SetFVectorToPVector',	0, 0),	#                                    -               -
+	(0x34,	'SHC',		1,	'ShiftContourByLastPt',	1, 0),	#                                    c               -
+	(0x32,	'SHP',		1,	'ShiftPointByLastPoint',-1, 0),	#              p1, p2, ..., ploopvalue               -
+	(0x38,	'SHPIX',	0,	'ShiftZoneByPixel',	-1, 0),	#           d, p1, p2, ..., ploopvalue               -
+	(0x36,	'SHZ',		1,	'ShiftZoneByLastPoint',	1, 0),	#                                    e               -
+	(0x17,	'SLOOP',	0,	'SetLoopVariable',	1, 0),	#                                    n               -
+	(0x1a,	'SMD',		0,	'SetMinimumDistance',	1, 0),	#                             distance               -
+	(0x0a,	'SPVFS',	0,	'SetPVectorFromStack',	2, 0),	#                                 y, x               -
+	(0x02,	'SPVTCA',	1,	'SetPVectorToAxis',	0, 0),	#                                    -               -
+	(0x06,	'SPVTL',	1,	'SetPVectorToLine',	2, 0),	#                               p2, p1               -
+	(0x76,	'SROUND',	0,	'SuperRound',		1, 0),	#                                    n               -
+	(0x10,	'SRP0',		0,	'SetRefPoint0',		1, 0),	#                                    p               -
+	(0x11,	'SRP1',		0,	'SetRefPoint1',		1, 0),	#                                    p               -
+	(0x12,	'SRP2',		0,	'SetRefPoint2',		1, 0),	#                                    p               -
+	(0x1f,	'SSW',		0,	'SetSingleWidth',	1, 0),	#                                    n               -
+	(0x1e,	'SSWCI',	0,	'SetSingleWidthCutIn',	1, 0),	#                                    n               -
+	(0x61,	'SUB',		0,	'Subtract',		2, 1),	#                               n2, n1       (n1 - n2)
+	(0x00,	'SVTCA',	1,	'SetFPVectorToAxis',	0, 0),	#                                    -               -
+	(0x23,	'SWAP',		0,	'SwapTopStack',		2, 2),	#                               e2, e1          e1, e2
+	(0x13,	'SZP0',		0,	'SetZonePointer0',	1, 0),	#                                    n               -
+	(0x14,	'SZP1',		0,	'SetZonePointer1',	1, 0),	#                                    n               -
+	(0x15,	'SZP2',		0,	'SetZonePointer2',	1, 0),	#                                    n               -
+	(0x16,	'SZPS',		0,	'SetZonePointerS',	1, 0),	#                                    n               -
+	(0x29,	'UTP',		0,	'UnTouchPt',		1, 0),	#                                    p               -
+	(0x70,	'WCVTF',	0,	'WriteCVTInFUnits',	2, 0),	#                                 n, l               -
+	(0x44,	'WCVTP',	0,	'WriteCVTInPixels',	2, 0),	#                                 v, l               -
+	(0x42,	'WS',		0,	'WriteStore',		2, 0),	#                                 v, l               -
 ]
 
 
@@ -163,13 +165,13 @@
 	mnemonicDict = {}
 	for op, mnemonic, argBits, name, pops, pushes in instructionList:
 		assert _mnemonicPat.match(mnemonic)
-		mnemonicDict[mnemonic] = op, argBits
+		mnemonicDict[mnemonic] = op, argBits, name
 		if argBits:
 			argoffset = op
 			for i in range(1 << argBits):
-				opcodeDict[op+i] = mnemonic, argBits, argoffset
+				opcodeDict[op+i] = mnemonic, argBits, argoffset, name
 		else:
-				opcodeDict[op] = mnemonic, 0, 0
+				opcodeDict[op] = mnemonic, 0, 0, name
 	return opcodeDict, mnemonicDict
 
 streamOpcodeDict, streamMnemonicDict = _makeDict(streamInstructions)
@@ -190,8 +192,10 @@
 _tokenRE = re.compile(_token)
 _whiteRE = re.compile(r"\s*")
 
-_pushCountPat = re.compile(r"[A-Z][A-Z0-9]*\s*\[.*?\]\s*/\* ([0-9]*).*?\*/")
+_pushCountPat = re.compile(r"[A-Z][A-Z0-9]*\s*\[.*?\]\s*/\* ([0-9]+).*?\*/")
 
+_indentRE = re.compile("^FDEF|IF|ELSE\[ \]\t.+")
+_unindentRE = re.compile("^ELSE|ENDF|EIF\[ \]\t.+")
 
 def _skipWhite(data, pos):
 	m = _whiteRE.match(data, pos)
@@ -201,63 +205,94 @@
 
 
 class Program(object):
-	
+
 	def __init__(self):
 		pass
-	
+
 	def fromBytecode(self, bytecode):
 		self.bytecode = array.array("B", bytecode)
 		if hasattr(self, "assembly"):
 			del self.assembly
-	
+
 	def fromAssembly(self, assembly):
 		self.assembly = assembly
 		if hasattr(self, "bytecode"):
 			del self.bytecode
-	
+
 	def getBytecode(self):
 		if not hasattr(self, "bytecode"):
 			self._assemble()
 		return self.bytecode.tostring()
-	
-	def getAssembly(self):
+
+	def getAssembly(self, preserve=True):
 		if not hasattr(self, "assembly"):
-			self._disassemble()
+			self._disassemble(preserve=preserve)
 		return self.assembly
-	
+
 	def toXML(self, writer, ttFont):
 		if not hasattr (ttFont, "disassembleInstructions") or ttFont.disassembleInstructions:
-			assembly = self.getAssembly()
-			writer.begintag("assembly")
-			writer.newline()
-			i = 0
-			nInstr = len(assembly)
-			while i < nInstr:
-				instr = assembly[i]
-				writer.write(instr)
+			try:
+				assembly = self.getAssembly()
+			except:
+				import traceback
+				tmp = StringIO()
+				traceback.print_exc(file=tmp)
+				msg = "An exception occurred during the decompilation of glyph program:\n\n"
+				msg += tmp.getvalue()
+				log.error(msg)
+				writer.begintag("bytecode")
 				writer.newline()
-				m = _pushCountPat.match(instr)
-				i = i + 1
-				if m:
-					nValues = int(m.group(1))
-					line = []
-					j = 0
-					for j in range(nValues):
-						if j and not (j % 25):
-							writer.write(' '.join(line))
-							writer.newline()
-							line = []
-						line.append(assembly[i+j])
-					writer.write(' '.join(line))
+				writer.comment(msg.strip())
+				writer.newline()
+				writer.dumphex(self.getBytecode())
+				writer.endtag("bytecode")
+				writer.newline()
+			else:
+				if not assembly:
+					return
+				writer.begintag("assembly")
+				writer.newline()
+				i = 0
+				indent = 0
+				nInstr = len(assembly)
+				while i < nInstr:
+					instr = assembly[i]
+					if _unindentRE.match(instr):
+						indent -= 1
+					writer.write(writer.indentwhite * indent)
+					writer.write(instr)
 					writer.newline()
-					i = i + j + 1
-			writer.endtag("assembly")
+					m = _pushCountPat.match(instr)
+					i = i + 1
+					if m:
+						nValues = int(m.group(1))
+						line = []
+						j = 0
+						for j in range(nValues):
+							if j and not (j % 25):
+								writer.write(writer.indentwhite * indent)
+								writer.write(' '.join(line))
+								writer.newline()
+								line = []
+							line.append(assembly[i+j])
+						writer.write(writer.indentwhite * indent)
+						writer.write(' '.join(line))
+						writer.newline()
+						i = i + j + 1
+					if _indentRE.match(instr):
+						indent += 1
+				writer.endtag("assembly")
+				writer.newline()
 		else:
+			bytecode = self.getBytecode()
+			if not bytecode:
+				return
 			writer.begintag("bytecode")
 			writer.newline()
-			writer.dumphex(self.getBytecode())
+			writer.dumphex(bytecode)
 			writer.endtag("bytecode")
-	
+			writer.newline()
+
 	def fromXML(self, name, attrs, content, ttFont):
 		if name == "assembly":
 			self.fromAssembly(strjoin(content))
@@ -266,9 +301,9 @@
 		else:
 			assert name == "bytecode"
 			self.fromBytecode(readHex(content))
-	
+
 	def _assemble(self):
-		assembly = self.assembly
+		assembly = getattr(self, 'assembly', [])
 		if isinstance(assembly, type([])):
 			assembly = ' '.join(assembly)
 		bytecode = []
@@ -282,15 +317,16 @@
 			dummy, mnemonic, arg, number, comment = m.groups()
 			pos = m.regs[0][1]
 			if comment:
+				pos = _skipWhite(assembly, pos)
 				continue
-			
+
 			arg = arg.strip()
 			if mnemonic.startswith("INSTR"):
 				# Unknown instruction
 				op = int(mnemonic[5:])
 				push(op)
 			elif mnemonic not in ("PUSH", "NPUSHB", "NPUSHW", "PUSHB", "PUSHW"):
-				op, argBits = mnemonicDict[mnemonic]
+				op, argBits, name = mnemonicDict[mnemonic]
 				if len(arg) != argBits:
 					raise tt_instructions_error("Incorrect number of argument bits (%s[%s])" % (mnemonic, arg))
 				if arg:
@@ -331,11 +367,11 @@
 						# Write words
 						if nWords:
 							if nWords <= 8:
-								op, argBits = streamMnemonicDict["PUSHW"]
+								op, argBits, name = streamMnemonicDict["PUSHW"]
 								op = op + nWords - 1
 								push(op)
 							else:
-								op, argBits = streamMnemonicDict["NPUSHW"]
+								op, argBits, name = streamMnemonicDict["NPUSHW"]
 								push(op)
 								push(nWords)
 							for value in args[:nWords]:
@@ -347,11 +383,11 @@
 						if nBytes:
 							pass
 							if nBytes <= 8:
-								op, argBits = streamMnemonicDict["PUSHB"]
+								op, argBits, name = streamMnemonicDict["PUSHB"]
 								op = op + nBytes - 1
 								push(op)
 							else:
-								op, argBits = streamMnemonicDict["NPUSHB"]
+								op, argBits, name = streamMnemonicDict["NPUSHB"]
 								push(op)
 								push(nBytes)
 							for value in args[nWords:nWords+nBytes]:
@@ -364,7 +400,7 @@
 				else:
 					# Write exactly what we've been asked to
 					words = mnemonic[-1] == "W"
-					op, argBits = streamMnemonicDict[mnemonic]
+					op, argBits, name = streamMnemonicDict[mnemonic]
 					if mnemonic[0] != "N":
 						assert nArgs <= 8, nArgs
 						op = op + nArgs - 1
@@ -384,20 +420,20 @@
 							push(value)
 
 			pos = _skipWhite(assembly, pos)
-		
+
 		if bytecode:
 			assert max(bytecode) < 256 and min(bytecode) >= 0
 		self.bytecode = array.array("B", bytecode)
-	
+
 	def _disassemble(self, preserve=False):
 		assembly = []
 		i = 0
-		bytecode = self.bytecode
+		bytecode = getattr(self, 'bytecode', [])
 		numBytecode = len(bytecode)
 		while i < numBytecode:
 			op = bytecode[i]
 			try:
-				mnemonic, argBits, argoffset = opcodeDict[op]
+				mnemonic, argBits, argoffset, name = opcodeDict[op]
 			except KeyError:
 				if op in streamOpcodeDict:
 					values = []
@@ -405,7 +441,7 @@
 					# Merge consecutive PUSH operations
 					while bytecode[i] in streamOpcodeDict:
 						op = bytecode[i]
-						mnemonic, argBits, argoffset = streamOpcodeDict[op]
+						mnemonic, argBits, argoffset, name = streamOpcodeDict[op]
 						words = mnemonic[-1] == "W"
 						if argBits:
 							nValues = op - argoffset + 1
@@ -434,28 +470,75 @@
 						mnemonic = "PUSH"
 					nValues = len(values)
 					if nValues == 1:
-						assembly.append("%s[ ]" % mnemonic)
+						assembly.append("%s[ ]	/* 1 value pushed */" % mnemonic)
 					else:
-						assembly.append("%s[ ]  /* %s values pushed */" % (mnemonic, nValues))
+						assembly.append("%s[ ]	/* %s values pushed */" % (mnemonic, nValues))
 					assembly.extend(values)
 				else:
 					assembly.append("INSTR%d[ ]" % op)
 					i = i + 1
 			else:
 				if argBits:
-					assembly.append(mnemonic + "[%s]" % num2binary(op - argoffset, argBits))
+					assembly.append(mnemonic + "[%s]	/* %s */" % (num2binary(op - argoffset, argBits), name))
 				else:
-					assembly.append(mnemonic + "[ ]")
+					assembly.append(mnemonic + "[ ]	/* %s */" % name)
 				i = i + 1
 		self.assembly = assembly
 
+	def __bool__(self):
+		"""
+		>>> p = Program()
+		>>> bool(p)
+		False
+		>>> bc = array.array("B", [0])
+		>>> p.fromBytecode(bc)
+		>>> bool(p)
+		True
+		>>> p.bytecode.pop()
+		0
+		>>> bool(p)
+		False
 
-if __name__ == "__main__":
-	bc = """@;:9876543210/.-,+*)(\'&%$#"! \037\036\035\034\033\032\031\030\027\026\025\024\023\022\021\020\017\016\015\014\013\012\011\010\007\006\005\004\003\002\001\000,\001\260\030CXEj\260\031C`\260F#D#\020 \260FN\360M/\260\000\022\033!#\0213Y-,\001\260\030CX\260\005+\260\000\023K\260\024PX\261\000@8Y\260\006+\033!#\0213Y-,\001\260\030CXN\260\003%\020\362!\260\000\022M\033 E\260\004%\260\004%#Jad\260(RX!#\020\326\033\260\003%\020\362!\260\000\022YY-,\260\032CX!!\033\260\002%\260\002%I\260\003%\260\003%Ja d\260\020PX!!!\033\260\003%\260\003%I\260\000PX\260\000PX\270\377\3428!\033\260\0208!Y\033\260\000RX\260\0368!\033\270\377\3608!YYYY-,\001\260\030CX\260\005+\260\000\023K\260\024PX\271\000\000\377\3008Y\260\006+\033!#\0213Y-,N\001\212\020\261F\031CD\260\000\024\261\000F\342\260\000\025\271\000\000\377\3608\000\260\000<\260(+\260\002%\020\260\000<-,\001\030\260\000/\260\001\024\362\260\001\023\260\001\025M\260\000\022-,\001\260\030CX\260\005+\260\000\023\271\000\000\377\3408\260\006+\033!#\0213Y-,\001\260\030CXEdj#Edi\260\031Cd``\260F#D#\020 \260F\360/\260\000\022\033!! \212 \212RX\0213\033!!YY-,\001\261\013\012C#Ce\012-,\000\261\012\013C#C\013-,\000\260F#p\261\001F>\001\260F#p\261\002FE:\261\002\000\010\015-,\260\022+\260\002%E\260\002%Ej\260@\213`\260\002%#D!!!-,\260\023+\260\002%E\260\002%Ej\270\377\300\214`\260\002%#D!!!-,\260\000\260\022+!!!-,\260\000\260\023+!!!-,\001\260\006C\260\007Ce\012-, i\260@a\260\000\213 \261,\300\212\214\270\020\000b`+\014d#da\\X\260\003aY-,\261\000\003%EhT\260\034KPZX\260\003%E\260\003%E`h \260\004%#D\260\004%#D\033\260\003% Eh \212#D\260\003%Eh`\260\003%#DY-,\260\003% Eh \212#D\260\003%Edhe`\260\004%\260\001`#D-,\260\011CX\207!\300\033\260\022CX\207E\260\021+\260G#D\260Gz\344\033\003\212E\030i \260G#D\212\212\207 \260\240QX\260\021+\260G#D\260Gz\344\033!\260Gz\344YYY\030-, \212E#Eh`D-,EjB-,\001\030/-,\001\260\030CX\260\004%\260\004%Id#Edi\260@\213a \260\200bj\260\002%\260\002%a\214\260\031C`\260F#D!\212\020\260F\366!\033!!!!Y-,\001\260\030CX\260\002%E\260\002%Ed`j\260\003%Eja \260\004%Ej \212\213e\260\004%#D\214\260\003%#D!!\033 EjD EjDY-,\001 E\260\000U\260\030CZXEh#Ei\260@\213a \260\200bj \212#a \260\003%\213e\260\004%#D\214\260\003%#D!!\033!!\260\031+Y-,\001\212\212Ed#EdadB-,\260\004%\260\004%\260\031+\260\030CX\260\004%\260\004%\260\003%\260\033+\001\260\002%C\260@T\260\002%C\260\000TZX\260\003% E\260@aDY\260\002%C\260\000T\260\002%C\260@TZX\260\004% E\260@`DYY!!!!-,\001KRXC\260\002%E#aD\033!!Y-,\001KRXC\260\002%E#`D\033!!Y-,KRXED\033!!Y-,\001 \260\003%#I\260@`\260 c \260\000RX#\260\002%8#\260\002%e8\000\212c8\033!!!!!Y\001-,KPXED\033!!Y-,\001\260\005%\020# \212\365\000\260\001`#\355\354-,\001\260\005%\020# \212\365\000\260\001a#\355\354-,\001\260\006%\020\365\000\355\354-,F#F`\212\212F# F\212`\212a\270\377\200b# \020#\212\261KK\212pE` \260\000PX\260\001a\270\377\272\213\033\260F\214Y\260\020`h\001:-, E\260\003%FRX\260\002%F ha\260\003%\260\003%?#!8\033!\021Y-, E\260\003%FPX\260\002%F ha\260\003%\260\003%?#!8\033!\021Y-,\000\260\007C\260\006C\013-,\212\020\354-,\260\014CX!\033 F\260\000RX\270\377\3608\033\260\0208YY-, \260\000UX\270\020\000c\260\003%Ed\260\003%Eda\260\000SX\260\002\033\260@a\260\003Y%EiSXED\033!!Y\033!\260\002%E\260\002%Ead\260(QXED\033!!YY-,!!\014d#d\213\270@\000b-,!\260\200QX\014d#d\213\270 \000b\033\262\000@/+Y\260\002`-,!\260\300QX\014d#d\213\270\025Ub\033\262\000\200/+Y\260\002`-,\014d#d\213\270@\000b`#!-,KSX\260\004%\260\004%Id#Edi\260@\213a \260\200bj\260\002%\260\002%a\214\260F#D!\212\020\260F\366!\033!\212\021#\022 9/Y-,\260\002%\260\002%Id\260\300TX\270\377\3708\260\0108\033!!Y-,\260\023CX\003\033\002Y-,\260\023CX\002\033\003Y-,\260\012+#\020 <\260\027+-,\260\002%\270\377\3608\260(+\212\020# \320#\260\020+\260\005CX\300\033<Y \020\021\260\000\022\001-,KS#KQZX8\033!!Y-,\001\260\002%\020\320#\311\001\260\001\023\260\000\024\020\260\001<\260\001\026-,\001\260\000\023\260\001\260\003%I\260\003\0278\260\001\023-,KS#KQZX E\212`D\033!!Y-, 9/-"""
-	
+		>>> p = Program()
+		>>> asm = ['SVTCA[0]']
+		>>> p.fromAssembly(asm)
+		>>> bool(p)
+		True
+		>>> p.assembly.pop()
+		'SVTCA[0]'
+		>>> bool(p)
+		False
+		"""
+		return ((hasattr(self, 'assembly') and len(self.assembly) > 0) or
+				(hasattr(self, 'bytecode') and len(self.bytecode) > 0))
+
+	__nonzero__ = __bool__
+
+	def __eq__(self, other):
+		if type(self) != type(other):
+			return NotImplemented
+		return self.__dict__ == other.__dict__
+
+	def __ne__(self, other):
+		result = self.__eq__(other)
+		return result if result is NotImplemented else not result
+
+
+def _test():
+	"""
+		>>> _test()
+		True
+	"""
+
+	bc = b"""@;:9876543210/.-,+*)(\'&%$#"! \037\036\035\034\033\032\031\030\027\026\025\024\023\022\021\020\017\016\015\014\013\012\011\010\007\006\005\004\003\002\001\000,\001\260\030CXEj\260\031C`\260F#D#\020 \260FN\360M/\260\000\022\033!#\0213Y-,\001\260\030CX\260\005+\260\000\023K\260\024PX\261\000@8Y\260\006+\033!#\0213Y-,\001\260\030CXN\260\003%\020\362!\260\000\022M\033 E\260\004%\260\004%#Jad\260(RX!#\020\326\033\260\003%\020\362!\260\000\022YY-,\260\032CX!!\033\260\002%\260\002%I\260\003%\260\003%Ja d\260\020PX!!!\033\260\003%\260\003%I\260\000PX\260\000PX\270\377\3428!\033\260\0208!Y\033\260\000RX\260\0368!\033\270\377\3608!YYYY-,\001\260\030CX\260\005+\260\000\023K\260\024PX\271\000\000\377\3008Y\260\006+\033!#\0213Y-,N\001\212\020\261F\031CD\260\000\024\261\000F\342\260\000\025\271\000\000\377\3608\000\260\000<\260(+\260\002%\020\260\000<-,\001\030\260\000/\260\001\024\362\260\001\023\260\001\025M\260\000\022-,\001\260\030CX\260\005+\260\000\023\271\000\000\377\3408\260\006+\033!#\0213Y-,\001\260\030CXEdj#Edi\260\031Cd``\260F#D#\020 \260F\360/\260\000\022\033!! \212 \212RX\0213\033!!YY-,\001\261\013\012C#Ce\012-,\000\261\012\013C#C\013-,\000\260F#p\261\001F>\001\260F#p\261\002FE:\261\002\000\010\015-,\260\022+\260\002%E\260\002%Ej\260@\213`\260\002%#D!!!-,\260\023+\260\002%E\260\002%Ej\270\377\300\214`\260\002%#D!!!-,\260\000\260\022+!!!-,\260\000\260\023+!!!-,\001\260\006C\260\007Ce\012-, i\260@a\260\000\213 \261,\300\212\214\270\020\000b`+\014d#da\\X\260\003aY-,\261\000\003%EhT\260\034KPZX\260\003%E\260\003%E`h \260\004%#D\260\004%#D\033\260\003% Eh \212#D\260\003%Eh`\260\003%#DY-,\260\003% Eh \212#D\260\003%Edhe`\260\004%\260\001`#D-,\260\011CX\207!\300\033\260\022CX\207E\260\021+\260G#D\260Gz\344\033\003\212E\030i \260G#D\212\212\207 \260\240QX\260\021+\260G#D\260Gz\344\033!\260Gz\344YYY\030-, \212E#Eh`D-,EjB-,\001\030/-,\001\260\030CX\260\004%\260\004%Id#Edi\260@\213a \260\200bj\260\002%\260\002%a\214\260\031C`\260F#D!\212\020\260F\366!\033!!!!Y-,\001\260\030CX\260\002%E\260\002%Ed`j\260\003%Eja \260\004%Ej \212\213e\260\004%#D\214\260\003%#D!!\033 EjD EjDY-,\001 E\260\000U\260\030CZXEh#Ei\260@\213a \260\200bj \212#a \260\003%\213e\260\004%#D\214\260\003%#D!!\033!!\260\031+Y-,\001\212\212Ed#EdadB-,\260\004%\260\004%\260\031+\260\030CX\260\004%\260\004%\260\003%\260\033+\001\260\002%C\260@T\260\002%C\260\000TZX\260\003% E\260@aDY\260\002%C\260\000T\260\002%C\260@TZX\260\004% E\260@`DYY!!!!-,\001KRXC\260\002%E#aD\033!!Y-,\001KRXC\260\002%E#`D\033!!Y-,KRXED\033!!Y-,\001 \260\003%#I\260@`\260 c \260\000RX#\260\002%8#\260\002%e8\000\212c8\033!!!!!Y\001-,KPXED\033!!Y-,\001\260\005%\020# \212\365\000\260\001`#\355\354-,\001\260\005%\020# \212\365\000\260\001a#\355\354-,\001\260\006%\020\365\000\355\354-,F#F`\212\212F# F\212`\212a\270\377\200b# \020#\212\261KK\212pE` \260\000PX\260\001a\270\377\272\213\033\260F\214Y\260\020`h\001:-, E\260\003%FRX\260\002%F ha\260\003%\260\003%?#!8\033!\021Y-, E\260\003%FPX\260\002%F ha\260\003%\260\003%?#!8\033!\021Y-,\000\260\007C\260\006C\013-,\212\020\354-,\260\014CX!\033 F\260\000RX\270\377\3608\033\260\0208YY-, \260\000UX\270\020\000c\260\003%Ed\260\003%Eda\260\000SX\260\002\033\260@a\260\003Y%EiSXED\033!!Y\033!\260\002%E\260\002%Ead\260(QXED\033!!YY-,!!\014d#d\213\270@\000b-,!\260\200QX\014d#d\213\270 \000b\033\262\000@/+Y\260\002`-,!\260\300QX\014d#d\213\270\025Ub\033\262\000\200/+Y\260\002`-,\014d#d\213\270@\000b`#!-,KSX\260\004%\260\004%Id#Edi\260@\213a \260\200bj\260\002%\260\002%a\214\260F#D!\212\020\260F\366!\033!\212\021#\022 9/Y-,\260\002%\260\002%Id\260\300TX\270\377\3708\260\0108\033!!Y-,\260\023CX\003\033\002Y-,\260\023CX\002\033\003Y-,\260\012+#\020 <\260\027+-,\260\002%\270\377\3608\260(+\212\020# \320#\260\020+\260\005CX\300\033<Y \020\021\260\000\022\001-,KS#KQZX8\033!!Y-,\001\260\002%\020\320#\311\001\260\001\023\260\000\024\020\260\001<\260\001\026-,\001\260\000\023\260\001\260\003%I\260\003\0278\260\001\023-,KS#KQZX E\212`D\033!!Y-, 9/-"""
+
 	p = Program()
 	p.fromBytecode(bc)
-	asm = p.getAssembly()
+	asm = p.getAssembly(preserve=True)
 	p.fromAssembly(asm)
 	print(bc == p.getBytecode())
 
+if __name__ == "__main__":
+	import sys
+	import doctest
+	sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/ttLib/ttCollection.py b/Lib/fontTools/ttLib/ttCollection.py
new file mode 100644
index 0000000..2507fe3
--- /dev/null
+++ b/Lib/fontTools/ttLib/ttCollection.py
@@ -0,0 +1,107 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.ttLib.ttFont import TTFont
+from fontTools.ttLib.sfnt import readTTCHeader, writeTTCHeader
+import struct
+import logging
+
+log = logging.getLogger(__name__)
+
+
+class TTCollection(object):
+
+	"""Object representing a TrueType Collection / OpenType Collection.
+	The main API is self.fonts being a list of TTFont instances.
+
+	If shareTables is True, then different fonts in the collection
+	might point to the same table object if the data for the table was
+	the same in the font file.  Note, however, that this might result
+	in suprises and incorrect behavior if the different fonts involved
+	have different GlyphOrder.  Use only if you know what you are doing.
+	"""
+
+	def __init__(self, file=None, shareTables=False, **kwargs):
+		fonts = self.fonts = []
+		if file is None:
+			return
+
+		assert 'fontNumber' not in kwargs, kwargs
+
+		if not hasattr(file, "read"):
+			file = open(file, "rb")
+
+		tableCache = {} if shareTables else None
+
+		header = readTTCHeader(file)
+		for i in range(header.numFonts):
+			font = TTFont(file, fontNumber=i, _tableCache=tableCache, **kwargs)
+			fonts.append(font)
+
+	def save(self, file, shareTables=True):
+		"""Save the font to disk. Similarly to the constructor,
+		the 'file' argument can be either a pathname or a writable
+		file object.
+		"""
+		if not hasattr(file, "write"):
+			final = None
+			file = open(file, "wb")
+		else:
+			# assume "file" is a writable file object
+			# write to a temporary stream to allow saving to unseekable streams
+			final = file
+			file = BytesIO()
+
+		tableCache = {} if shareTables else None
+
+		offsets_offset = writeTTCHeader(file, len(self.fonts))
+		offsets = []
+		for font in self.fonts:
+			offsets.append(file.tell())
+			font._save(file, tableCache=tableCache)
+			file.seek(0,2)
+
+		file.seek(offsets_offset)
+		file.write(struct.pack(">%dL" % len(self.fonts), *offsets))
+
+		if final:
+			final.write(file.getvalue())
+		file.close()
+
+	def saveXML(self, fileOrPath, newlinestr=None, writeVersion=True, **kwargs):
+
+		from fontTools.misc import xmlWriter
+		writer = xmlWriter.XMLWriter(fileOrPath, newlinestr=newlinestr)
+
+		if writeVersion:
+			from fontTools import version
+			version = ".".join(version.split('.')[:2])
+			writer.begintag("ttCollection", ttLibVersion=version)
+		else:
+			writer.begintag("ttCollection")
+		writer.newline()
+		writer.newline()
+
+		for font in self.fonts:
+			font._saveXML(writer, writeVersion=False, **kwargs)
+			writer.newline()
+
+		writer.endtag("ttCollection")
+		writer.newline()
+
+		writer.close()
+
+
+	def __getitem__(self, item):
+		return self.fonts[item]
+
+	def __setitem__(self, item, value):
+		self.fonts[item] = values
+
+	def __delitem__(self, item):
+		return self.fonts[item]
+
+	def __len__(self):
+		return len(self.fonts)
+
+	def __iter__(self):
+		return iter(self.fonts)
diff --git a/Lib/fontTools/ttLib/ttFont.py b/Lib/fontTools/ttLib/ttFont.py
new file mode 100644
index 0000000..87538fb
--- /dev/null
+++ b/Lib/fontTools/ttLib/ttFont.py
@@ -0,0 +1,1001 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc import xmlWriter
+from fontTools.misc.py23 import *
+from fontTools.misc.loggingTools import deprecateArgument
+from fontTools.ttLib.sfnt import SFNTReader, SFNTWriter
+import os
+import logging
+import itertools
+
+log = logging.getLogger(__name__)
+
+class TTFont(object):
+
+	"""The main font object. It manages file input and output, and offers
+	a convenient way of accessing tables.
+	Tables will be only decompiled when necessary, ie. when they're actually
+	accessed. This means that simple operations can be extremely fast.
+	"""
+
+	def __init__(self, file=None, res_name_or_index=None,
+			sfntVersion="\000\001\000\000", flavor=None, checkChecksums=False,
+			verbose=None, recalcBBoxes=True, allowVID=False, ignoreDecompileErrors=False,
+			recalcTimestamp=True, fontNumber=-1, lazy=None, quiet=None,
+			_tableCache=None):
+
+		"""The constructor can be called with a few different arguments.
+		When reading a font from disk, 'file' should be either a pathname
+		pointing to a file, or a readable file object.
+
+		It we're running on a Macintosh, 'res_name_or_index' maybe an sfnt
+		resource name or an sfnt resource index number or zero. The latter
+		case will cause TTLib to autodetect whether the file is a flat file
+		or a suitcase. (If it's a suitcase, only the first 'sfnt' resource
+		will be read!)
+
+		The 'checkChecksums' argument is used to specify how sfnt
+		checksums are treated upon reading a file from disk:
+			0: don't check (default)
+			1: check, print warnings if a wrong checksum is found
+			2: check, raise an exception if a wrong checksum is found.
+
+		The TTFont constructor can also be called without a 'file'
+		argument: this is the way to create a new empty font.
+		In this case you can optionally supply the 'sfntVersion' argument,
+		and a 'flavor' which can be None, 'woff', or 'woff2'.
+
+		If the recalcBBoxes argument is false, a number of things will *not*
+		be recalculated upon save/compile:
+			1) 'glyf' glyph bounding boxes
+			2) 'CFF ' font bounding box
+			3) 'head' font bounding box
+			4) 'hhea' min/max values
+			5) 'vhea' min/max values
+		(1) is needed for certain kinds of CJK fonts (ask Werner Lemberg ;-).
+		Additionally, upon importing an TTX file, this option cause glyphs
+		to be compiled right away. This should reduce memory consumption
+		greatly, and therefore should have some impact on the time needed
+		to parse/compile large fonts.
+
+		If the recalcTimestamp argument is false, the modified timestamp in the
+		'head' table will *not* be recalculated upon save/compile.
+
+		If the allowVID argument is set to true, then virtual GID's are
+		supported. Asking for a glyph ID with a glyph name or GID that is not in
+		the font will return a virtual GID.   This is valid for GSUB and cmap
+		tables. For SING glyphlets, the cmap table is used to specify Unicode
+		values for virtual GI's used in GSUB/GPOS rules. If the gid N is requested
+		and does not exist in the font, or the glyphname has the form glyphN
+		and does not exist in the font, then N is used as the virtual GID.
+		Else, the first virtual GID is assigned as 0x1000 -1; for subsequent new
+		virtual GIDs, the next is one less than the previous.
+
+		If ignoreDecompileErrors is set to True, exceptions raised in
+		individual tables during decompilation will be ignored, falling
+		back to the DefaultTable implementation, which simply keeps the
+		binary data.
+
+		If lazy is set to True, many data structures are loaded lazily, upon
+		access only.  If it is set to False, many data structures are loaded
+		immediately.  The default is lazy=None which is somewhere in between.
+		"""
+
+		for name in ("verbose", "quiet"):
+			val = locals().get(name)
+			if val is not None:
+				deprecateArgument(name, "configure logging instead")
+			setattr(self, name, val)
+
+		self.lazy = lazy
+		self.recalcBBoxes = recalcBBoxes
+		self.recalcTimestamp = recalcTimestamp
+		self.tables = {}
+		self.reader = None
+
+		# Permit the user to reference glyphs that are not int the font.
+		self.last_vid = 0xFFFE # Can't make it be 0xFFFF, as the world is full unsigned short integer counters that get incremented after the last seen GID value.
+		self.reverseVIDDict = {}
+		self.VIDDict = {}
+		self.allowVID = allowVID
+		self.ignoreDecompileErrors = ignoreDecompileErrors
+
+		if not file:
+			self.sfntVersion = sfntVersion
+			self.flavor = flavor
+			self.flavorData = None
+			return
+		if not hasattr(file, "read"):
+			closeStream = True
+			# assume file is a string
+			if res_name_or_index is not None:
+				# see if it contains 'sfnt' resources in the resource or data fork
+				from . import macUtils
+				if res_name_or_index == 0:
+					if macUtils.getSFNTResIndices(file):
+						# get the first available sfnt font.
+						file = macUtils.SFNTResourceReader(file, 1)
+					else:
+						file = open(file, "rb")
+				else:
+					file = macUtils.SFNTResourceReader(file, res_name_or_index)
+			else:
+				file = open(file, "rb")
+		else:
+			# assume "file" is a readable file object
+			closeStream = False
+			file.seek(0)
+
+		if not self.lazy:
+			# read input file in memory and wrap a stream around it to allow overwriting
+			file.seek(0)
+			tmp = BytesIO(file.read())
+			if hasattr(file, 'name'):
+				# save reference to input file name
+				tmp.name = file.name
+			if closeStream:
+				file.close()
+			file = tmp
+		self._tableCache = _tableCache
+		self.reader = SFNTReader(file, checkChecksums, fontNumber=fontNumber)
+		self.sfntVersion = self.reader.sfntVersion
+		self.flavor = self.reader.flavor
+		self.flavorData = self.reader.flavorData
+
+	def __enter__(self):
+		return self
+
+	def __exit__(self, type, value, traceback):
+		self.close()
+
+	def close(self):
+		"""If we still have a reader object, close it."""
+		if self.reader is not None:
+			self.reader.close()
+
+	def save(self, file, reorderTables=True):
+		"""Save the font to disk. Similarly to the constructor,
+		the 'file' argument can be either a pathname or a writable
+		file object.
+		"""
+		if not hasattr(file, "write"):
+			if self.lazy and self.reader.file.name == file:
+				raise TTLibError(
+					"Can't overwrite TTFont when 'lazy' attribute is True")
+			closeStream = True
+			file = open(file, "wb")
+		else:
+			# assume "file" is a writable file object
+			closeStream = False
+
+		tmp = BytesIO()
+
+		writer_reordersTables = self._save(tmp)
+
+		if (reorderTables is None or writer_reordersTables or
+				(reorderTables is False and self.reader is None)):
+			# don't reorder tables and save as is
+			file.write(tmp.getvalue())
+			tmp.close()
+		else:
+			if reorderTables is False:
+				# sort tables using the original font's order
+				tableOrder = list(self.reader.keys())
+			else:
+				# use the recommended order from the OpenType specification
+				tableOrder = None
+			tmp.flush()
+			tmp2 = BytesIO()
+			reorderFontTables(tmp, tmp2, tableOrder)
+			file.write(tmp2.getvalue())
+			tmp.close()
+			tmp2.close()
+
+		if closeStream:
+			file.close()
+
+	def _save(self, file, tableCache=None):
+		"""Internal function, to be shared by save() and TTCollection.save()"""
+
+		if self.recalcTimestamp and 'head' in self:
+			self['head']  # make sure 'head' is loaded so the recalculation is actually done
+
+		tags = list(self.keys())
+		if "GlyphOrder" in tags:
+			tags.remove("GlyphOrder")
+		numTables = len(tags)
+		# write to a temporary stream to allow saving to unseekable streams
+		writer = SFNTWriter(file, numTables, self.sfntVersion, self.flavor, self.flavorData)
+
+		done = []
+		for tag in tags:
+			self._writeTable(tag, writer, done, tableCache)
+
+		writer.close()
+
+		return writer.reordersTables()
+
+	def saveXML(self, fileOrPath, newlinestr=None, **kwargs):
+		"""Export the font as TTX (an XML-based text file), or as a series of text
+		files when splitTables is true. In the latter case, the 'fileOrPath'
+		argument should be a path to a directory.
+		The 'tables' argument must either be false (dump all tables) or a
+		list of tables to dump. The 'skipTables' argument may be a list of tables
+		to skip, but only when the 'tables' argument is false.
+		"""
+
+		writer = xmlWriter.XMLWriter(fileOrPath, newlinestr=newlinestr)
+		self._saveXML(writer, **kwargs)
+		writer.close()
+
+	def _saveXML(self, writer,
+		     writeVersion=True,
+		     quiet=None, tables=None, skipTables=None, splitTables=False,
+		     splitGlyphs=False, disassembleInstructions=True,
+		     bitmapGlyphDataFormat='raw'):
+
+		if quiet is not None:
+			deprecateArgument("quiet", "configure logging instead")
+
+		self.disassembleInstructions = disassembleInstructions
+		self.bitmapGlyphDataFormat = bitmapGlyphDataFormat
+		if not tables:
+			tables = list(self.keys())
+			if "GlyphOrder" not in tables:
+				tables = ["GlyphOrder"] + tables
+			if skipTables:
+				for tag in skipTables:
+					if tag in tables:
+						tables.remove(tag)
+		numTables = len(tables)
+
+		if writeVersion:
+			from fontTools import version
+			version = ".".join(version.split('.')[:2])
+			writer.begintag("ttFont", sfntVersion=repr(tostr(self.sfntVersion))[1:-1],
+					ttLibVersion=version)
+		else:
+			writer.begintag("ttFont", sfntVersion=repr(tostr(self.sfntVersion))[1:-1])
+		writer.newline()
+
+		# always splitTables if splitGlyphs is enabled
+		splitTables = splitTables or splitGlyphs
+
+		if not splitTables:
+			writer.newline()
+		else:
+			path, ext = os.path.splitext(writer.filename)
+			fileNameTemplate = path + ".%s" + ext
+
+		for i in range(numTables):
+			tag = tables[i]
+			if splitTables:
+				tablePath = fileNameTemplate % tagToIdentifier(tag)
+				tableWriter = xmlWriter.XMLWriter(tablePath,
+						newlinestr=writer.newlinestr)
+				tableWriter.begintag("ttFont", ttLibVersion=version)
+				tableWriter.newline()
+				tableWriter.newline()
+				writer.simpletag(tagToXML(tag), src=os.path.basename(tablePath))
+				writer.newline()
+			else:
+				tableWriter = writer
+			self._tableToXML(tableWriter, tag, splitGlyphs=splitGlyphs)
+			if splitTables:
+				tableWriter.endtag("ttFont")
+				tableWriter.newline()
+				tableWriter.close()
+		writer.endtag("ttFont")
+		writer.newline()
+
+	def _tableToXML(self, writer, tag, quiet=None, splitGlyphs=False):
+		if quiet is not None:
+			deprecateArgument("quiet", "configure logging instead")
+		if tag in self:
+			table = self[tag]
+			report = "Dumping '%s' table..." % tag
+		else:
+			report = "No '%s' table found." % tag
+		log.info(report)
+		if tag not in self:
+			return
+		xmlTag = tagToXML(tag)
+		attrs = dict()
+		if hasattr(table, "ERROR"):
+			attrs['ERROR'] = "decompilation error"
+		from .tables.DefaultTable import DefaultTable
+		if table.__class__ == DefaultTable:
+			attrs['raw'] = True
+		writer.begintag(xmlTag, **attrs)
+		writer.newline()
+		if tag == "glyf":
+			table.toXML(writer, self, splitGlyphs=splitGlyphs)
+		else:
+			table.toXML(writer, self)
+		writer.endtag(xmlTag)
+		writer.newline()
+		writer.newline()
+
+	def importXML(self, fileOrPath, quiet=None):
+		"""Import a TTX file (an XML-based text format), so as to recreate
+		a font object.
+		"""
+		if quiet is not None:
+			deprecateArgument("quiet", "configure logging instead")
+
+		if "maxp" in self and "post" in self:
+			# Make sure the glyph order is loaded, as it otherwise gets
+			# lost if the XML doesn't contain the glyph order, yet does
+			# contain the table which was originally used to extract the
+			# glyph names from (ie. 'post', 'cmap' or 'CFF ').
+			self.getGlyphOrder()
+
+		from fontTools.misc import xmlReader
+
+		reader = xmlReader.XMLReader(fileOrPath, self)
+		reader.read()
+
+	def isLoaded(self, tag):
+		"""Return true if the table identified by 'tag' has been
+		decompiled and loaded into memory."""
+		return tag in self.tables
+
+	def has_key(self, tag):
+		if self.isLoaded(tag):
+			return True
+		elif self.reader and tag in self.reader:
+			return True
+		elif tag == "GlyphOrder":
+			return True
+		else:
+			return False
+
+	__contains__ = has_key
+
+	def keys(self):
+		keys = list(self.tables.keys())
+		if self.reader:
+			for key in list(self.reader.keys()):
+				if key not in keys:
+					keys.append(key)
+
+		if "GlyphOrder" in keys:
+			keys.remove("GlyphOrder")
+		keys = sortedTagList(keys)
+		return ["GlyphOrder"] + keys
+
+	def __len__(self):
+		return len(list(self.keys()))
+
+	def __getitem__(self, tag):
+		tag = Tag(tag)
+		try:
+			return self.tables[tag]
+		except KeyError:
+			if tag == "GlyphOrder":
+				table = GlyphOrder(tag)
+				self.tables[tag] = table
+				return table
+			if self.reader is not None:
+				import traceback
+				log.debug("Reading '%s' table from disk", tag)
+				data = self.reader[tag]
+				if self._tableCache is not None:
+					table = self._tableCache.get((Tag(tag), data))
+					if table is not None:
+						return table
+				tableClass = getTableClass(tag)
+				table = tableClass(tag)
+				self.tables[tag] = table
+				log.debug("Decompiling '%s' table", tag)
+				try:
+					table.decompile(data, self)
+				except:
+					if not self.ignoreDecompileErrors:
+						raise
+					# fall back to DefaultTable, retaining the binary table data
+					log.exception(
+						"An exception occurred during the decompilation of the '%s' table", tag)
+					from .tables.DefaultTable import DefaultTable
+					file = StringIO()
+					traceback.print_exc(file=file)
+					table = DefaultTable(tag)
+					table.ERROR = file.getvalue()
+					self.tables[tag] = table
+					table.decompile(data, self)
+				if self._tableCache is not None:
+					self._tableCache[(Tag(tag), data)] = table
+				return table
+			else:
+				raise KeyError("'%s' table not found" % tag)
+
+	def __setitem__(self, tag, table):
+		self.tables[Tag(tag)] = table
+
+	def __delitem__(self, tag):
+		if tag not in self:
+			raise KeyError("'%s' table not found" % tag)
+		if tag in self.tables:
+			del self.tables[tag]
+		if self.reader and tag in self.reader:
+			del self.reader[tag]
+
+	def get(self, tag, default=None):
+		try:
+			return self[tag]
+		except KeyError:
+			return default
+
+	def setGlyphOrder(self, glyphOrder):
+		self.glyphOrder = glyphOrder
+
+	def getGlyphOrder(self):
+		try:
+			return self.glyphOrder
+		except AttributeError:
+			pass
+		if 'CFF ' in self:
+			cff = self['CFF ']
+			self.glyphOrder = cff.getGlyphOrder()
+		elif 'post' in self:
+			# TrueType font
+			glyphOrder = self['post'].getGlyphOrder()
+			if glyphOrder is None:
+				#
+				# No names found in the 'post' table.
+				# Try to create glyph names from the unicode cmap (if available)
+				# in combination with the Adobe Glyph List (AGL).
+				#
+				self._getGlyphNamesFromCmap()
+			else:
+				self.glyphOrder = glyphOrder
+		else:
+			self._getGlyphNamesFromCmap()
+		return self.glyphOrder
+
+	def _getGlyphNamesFromCmap(self):
+		#
+		# This is rather convoluted, but then again, it's an interesting problem:
+		# - we need to use the unicode values found in the cmap table to
+		#   build glyph names (eg. because there is only a minimal post table,
+		#   or none at all).
+		# - but the cmap parser also needs glyph names to work with...
+		# So here's what we do:
+		# - make up glyph names based on glyphID
+		# - load a temporary cmap table based on those names
+		# - extract the unicode values, build the "real" glyph names
+		# - unload the temporary cmap table
+		#
+		if self.isLoaded("cmap"):
+			# Bootstrapping: we're getting called by the cmap parser
+			# itself. This means self.tables['cmap'] contains a partially
+			# loaded cmap, making it impossible to get at a unicode
+			# subtable here. We remove the partially loaded cmap and
+			# restore it later.
+			# This only happens if the cmap table is loaded before any
+			# other table that does f.getGlyphOrder()  or f.getGlyphName().
+			cmapLoading = self.tables['cmap']
+			del self.tables['cmap']
+		else:
+			cmapLoading = None
+		# Make up glyph names based on glyphID, which will be used by the
+		# temporary cmap and by the real cmap in case we don't find a unicode
+		# cmap.
+		numGlyphs = int(self['maxp'].numGlyphs)
+		glyphOrder = [None] * numGlyphs
+		glyphOrder[0] = ".notdef"
+		for i in range(1, numGlyphs):
+			glyphOrder[i] = "glyph%.5d" % i
+		# Set the glyph order, so the cmap parser has something
+		# to work with (so we don't get called recursively).
+		self.glyphOrder = glyphOrder
+
+		# Make up glyph names based on the reversed cmap table. Because some
+		# glyphs (eg. ligatures or alternates) may not be reachable via cmap,
+		# this naming table will usually not cover all glyphs in the font.
+		# If the font has no Unicode cmap table, reversecmap will be empty.
+		if 'cmap' in self:
+			reversecmap = self['cmap'].buildReversed()
+		else:
+			reversecmap = {}
+		useCount = {}
+		for i in range(numGlyphs):
+			tempName = glyphOrder[i]
+			if tempName in reversecmap:
+				# If a font maps both U+0041 LATIN CAPITAL LETTER A and
+				# U+0391 GREEK CAPITAL LETTER ALPHA to the same glyph,
+				# we prefer naming the glyph as "A".
+				glyphName = self._makeGlyphName(min(reversecmap[tempName]))
+				numUses = useCount[glyphName] = useCount.get(glyphName, 0) + 1
+				if numUses > 1:
+					glyphName = "%s.alt%d" % (glyphName, numUses - 1)
+				glyphOrder[i] = glyphName
+
+		if 'cmap' in self:
+			# Delete the temporary cmap table from the cache, so it can
+			# be parsed again with the right names.
+			del self.tables['cmap']
+			self.glyphOrder = glyphOrder
+			if cmapLoading:
+				# restore partially loaded cmap, so it can continue loading
+				# using the proper names.
+				self.tables['cmap'] = cmapLoading
+
+	@staticmethod
+	def _makeGlyphName(codepoint):
+		from fontTools import agl  # Adobe Glyph List
+		if codepoint in agl.UV2AGL:
+			return agl.UV2AGL[codepoint]
+		elif codepoint <= 0xFFFF:
+			return "uni%04X" % codepoint
+		else:
+			return "u%X" % codepoint
+
+	def getGlyphNames(self):
+		"""Get a list of glyph names, sorted alphabetically."""
+		glyphNames = sorted(self.getGlyphOrder())
+		return glyphNames
+
+	def getGlyphNames2(self):
+		"""Get a list of glyph names, sorted alphabetically,
+		but not case sensitive.
+		"""
+		from fontTools.misc import textTools
+		return textTools.caselessSort(self.getGlyphOrder())
+
+	def getGlyphName(self, glyphID, requireReal=False):
+		try:
+			return self.getGlyphOrder()[glyphID]
+		except IndexError:
+			if requireReal or not self.allowVID:
+				# XXX The ??.W8.otf font that ships with OSX uses higher glyphIDs in
+				# the cmap table than there are glyphs. I don't think it's legal...
+				return "glyph%.5d" % glyphID
+			else:
+				# user intends virtual GID support
+				try:
+					glyphName = self.VIDDict[glyphID]
+				except KeyError:
+					glyphName  ="glyph%.5d" % glyphID
+					self.last_vid = min(glyphID, self.last_vid )
+					self.reverseVIDDict[glyphName] = glyphID
+					self.VIDDict[glyphID] = glyphName
+				return glyphName
+
+	def getGlyphID(self, glyphName, requireReal=False):
+		if not hasattr(self, "_reverseGlyphOrderDict"):
+			self._buildReverseGlyphOrderDict()
+		glyphOrder = self.getGlyphOrder()
+		d = self._reverseGlyphOrderDict
+		if glyphName not in d:
+			if glyphName in glyphOrder:
+				self._buildReverseGlyphOrderDict()
+				return self.getGlyphID(glyphName)
+			else:
+				if requireReal:
+					raise KeyError(glyphName)
+				elif not self.allowVID:
+					# Handle glyphXXX only
+					if glyphName[:5] == "glyph":
+						try:
+							return int(glyphName[5:])
+						except (NameError, ValueError):
+							raise KeyError(glyphName)
+				else:
+					# user intends virtual GID support
+					try:
+						glyphID = self.reverseVIDDict[glyphName]
+					except KeyError:
+						# if name is in glyphXXX format, use the specified name.
+						if glyphName[:5] == "glyph":
+							try:
+								glyphID = int(glyphName[5:])
+							except (NameError, ValueError):
+								glyphID = None
+						if glyphID is None:
+							glyphID = self.last_vid -1
+							self.last_vid = glyphID
+						self.reverseVIDDict[glyphName] = glyphID
+						self.VIDDict[glyphID] = glyphName
+					return glyphID
+
+		glyphID = d[glyphName]
+		if glyphName != glyphOrder[glyphID]:
+			self._buildReverseGlyphOrderDict()
+			return self.getGlyphID(glyphName)
+		return glyphID
+
+	def getReverseGlyphMap(self, rebuild=False):
+		if rebuild or not hasattr(self, "_reverseGlyphOrderDict"):
+			self._buildReverseGlyphOrderDict()
+		return self._reverseGlyphOrderDict
+
+	def _buildReverseGlyphOrderDict(self):
+		self._reverseGlyphOrderDict = d = {}
+		glyphOrder = self.getGlyphOrder()
+		for glyphID in range(len(glyphOrder)):
+			d[glyphOrder[glyphID]] = glyphID
+
+	def _writeTable(self, tag, writer, done, tableCache=None):
+		"""Internal helper function for self.save(). Keeps track of
+		inter-table dependencies.
+		"""
+		if tag in done:
+			return
+		tableClass = getTableClass(tag)
+		for masterTable in tableClass.dependencies:
+			if masterTable not in done:
+				if masterTable in self:
+					self._writeTable(masterTable, writer, done, tableCache)
+				else:
+					done.append(masterTable)
+		done.append(tag)
+		tabledata = self.getTableData(tag)
+		if tableCache is not None:
+			entry = tableCache.get((Tag(tag), tabledata))
+			if entry is not None:
+				log.debug("reusing '%s' table", tag)
+				writer.setEntry(tag, entry)
+				return
+		log.debug("writing '%s' table to disk", tag)
+		writer[tag] = tabledata
+		if tableCache is not None:
+			tableCache[(Tag(tag), tabledata)] = writer[tag]
+
+	def getTableData(self, tag):
+		"""Returns raw table data, whether compiled or directly read from disk.
+		"""
+		tag = Tag(tag)
+		if self.isLoaded(tag):
+			log.debug("compiling '%s' table", tag)
+			return self.tables[tag].compile(self)
+		elif self.reader and tag in self.reader:
+			log.debug("Reading '%s' table from disk", tag)
+			return self.reader[tag]
+		else:
+			raise KeyError(tag)
+
+	def getGlyphSet(self, preferCFF=True):
+		"""Return a generic GlyphSet, which is a dict-like object
+		mapping glyph names to glyph objects. The returned glyph objects
+		have a .draw() method that supports the Pen protocol, and will
+		have an attribute named 'width'.
+
+		If the font is CFF-based, the outlines will be taken from the 'CFF ' or
+		'CFF2' tables. Otherwise the outlines will be taken from the 'glyf' table.
+		If the font contains both a 'CFF '/'CFF2' and a 'glyf' table, you can use
+		the 'preferCFF' argument to specify which one should be taken. If the
+		font contains both a 'CFF ' and a 'CFF2' table, the latter is taken.
+		"""
+		glyphs = None
+		if (preferCFF and any(tb in self for tb in ["CFF ", "CFF2"]) or
+		   ("glyf" not in self and any(tb in self for tb in ["CFF ", "CFF2"]))):
+			table_tag = "CFF2" if "CFF2" in self else "CFF "
+			glyphs = _TTGlyphSet(self,
+			    list(self[table_tag].cff.values())[0].CharStrings, _TTGlyphCFF)
+
+		if glyphs is None and "glyf" in self:
+			glyphs = _TTGlyphSet(self, self["glyf"], _TTGlyphGlyf)
+
+		if glyphs is None:
+			raise TTLibError("Font contains no outlines")
+
+		return glyphs
+
+	def getBestCmap(self, cmapPreferences=((3, 10), (0, 6), (0, 4), (3, 1), (0, 3), (0, 2), (0, 1), (0, 0))):
+		"""Return the 'best' unicode cmap dictionary available in the font,
+		or None, if no unicode cmap subtable is available.
+
+		By default it will search for the following (platformID, platEncID)
+		pairs:
+			(3, 10), (0, 6), (0, 4), (3, 1), (0, 3), (0, 2), (0, 1), (0, 0)
+		This can be customized via the cmapPreferences argument.
+		"""
+		return self["cmap"].getBestCmap(cmapPreferences=cmapPreferences)
+
+
+class _TTGlyphSet(object):
+
+	"""Generic dict-like GlyphSet class that pulls metrics from hmtx and
+	glyph shape from TrueType or CFF.
+	"""
+
+	def __init__(self, ttFont, glyphs, glyphType):
+		self._glyphs = glyphs
+		self._hmtx = ttFont['hmtx']
+		self._vmtx = ttFont['vmtx'] if 'vmtx' in ttFont else None
+		self._glyphType = glyphType
+
+	def keys(self):
+		return list(self._glyphs.keys())
+
+	def has_key(self, glyphName):
+		return glyphName in self._glyphs
+
+	__contains__ = has_key
+
+	def __getitem__(self, glyphName):
+		horizontalMetrics = self._hmtx[glyphName]
+		verticalMetrics = self._vmtx[glyphName] if self._vmtx else None
+		return self._glyphType(
+			self, self._glyphs[glyphName], horizontalMetrics, verticalMetrics)
+
+	def __len__(self):
+		return len(self._glyphs)
+
+	def get(self, glyphName, default=None):
+		try:
+			return self[glyphName]
+		except KeyError:
+			return default
+
+class _TTGlyph(object):
+
+	"""Wrapper for a TrueType glyph that supports the Pen protocol, meaning
+	that it has a .draw() method that takes a pen object as its only
+	argument. Additionally there are 'width' and 'lsb' attributes, read from
+	the 'hmtx' table.
+
+	If the font contains a 'vmtx' table, there will also be 'height' and 'tsb'
+	attributes.
+	"""
+
+	def __init__(self, glyphset, glyph, horizontalMetrics, verticalMetrics=None):
+		self._glyphset = glyphset
+		self._glyph = glyph
+		self.width, self.lsb = horizontalMetrics
+		if verticalMetrics:
+			self.height, self.tsb = verticalMetrics
+		else:
+			self.height, self.tsb = None, None
+
+	def draw(self, pen):
+		"""Draw the glyph onto Pen. See fontTools.pens.basePen for details
+		how that works.
+		"""
+		self._glyph.draw(pen)
+
+class _TTGlyphCFF(_TTGlyph):
+	pass
+
+class _TTGlyphGlyf(_TTGlyph):
+
+	def draw(self, pen):
+		"""Draw the glyph onto Pen. See fontTools.pens.basePen for details
+		how that works.
+		"""
+		glyfTable = self._glyphset._glyphs
+		glyph = self._glyph
+		offset = self.lsb - glyph.xMin if hasattr(glyph, "xMin") else 0
+		glyph.draw(pen, glyfTable, offset)
+
+
+class GlyphOrder(object):
+
+	"""A pseudo table. The glyph order isn't in the font as a separate
+	table, but it's nice to present it as such in the TTX format.
+	"""
+
+	def __init__(self, tag=None):
+		pass
+
+	def toXML(self, writer, ttFont):
+		glyphOrder = ttFont.getGlyphOrder()
+		writer.comment("The 'id' attribute is only for humans; "
+				"it is ignored when parsed.")
+		writer.newline()
+		for i in range(len(glyphOrder)):
+			glyphName = glyphOrder[i]
+			writer.simpletag("GlyphID", id=i, name=glyphName)
+			writer.newline()
+
+	def fromXML(self, name, attrs, content, ttFont):
+		if not hasattr(self, "glyphOrder"):
+			self.glyphOrder = []
+			ttFont.setGlyphOrder(self.glyphOrder)
+		if name == "GlyphID":
+			self.glyphOrder.append(attrs["name"])
+
+
+def getTableModule(tag):
+	"""Fetch the packer/unpacker module for a table.
+	Return None when no module is found.
+	"""
+	from . import tables
+	pyTag = tagToIdentifier(tag)
+	try:
+		__import__("fontTools.ttLib.tables." + pyTag)
+	except ImportError as err:
+		# If pyTag is found in the ImportError message,
+		# means table is not implemented.  If it's not
+		# there, then some other module is missing, don't
+		# suppress the error.
+		if str(err).find(pyTag) >= 0:
+			return None
+		else:
+			raise err
+	else:
+		return getattr(tables, pyTag)
+
+
+def getTableClass(tag):
+	"""Fetch the packer/unpacker class for a table.
+	Return None when no class is found.
+	"""
+	module = getTableModule(tag)
+	if module is None:
+		from .tables.DefaultTable import DefaultTable
+		return DefaultTable
+	pyTag = tagToIdentifier(tag)
+	tableClass = getattr(module, "table_" + pyTag)
+	return tableClass
+
+
+def getClassTag(klass):
+	"""Fetch the table tag for a class object."""
+	name = klass.__name__
+	assert name[:6] == 'table_'
+	name = name[6:] # Chop 'table_'
+	return identifierToTag(name)
+
+
+def newTable(tag):
+	"""Return a new instance of a table."""
+	tableClass = getTableClass(tag)
+	return tableClass(tag)
+
+
+def _escapechar(c):
+	"""Helper function for tagToIdentifier()"""
+	import re
+	if re.match("[a-z0-9]", c):
+		return "_" + c
+	elif re.match("[A-Z]", c):
+		return c + "_"
+	else:
+		return hex(byteord(c))[2:]
+
+
+def tagToIdentifier(tag):
+	"""Convert a table tag to a valid (but UGLY) python identifier,
+	as well as a filename that's guaranteed to be unique even on a
+	caseless file system. Each character is mapped to two characters.
+	Lowercase letters get an underscore before the letter, uppercase
+	letters get an underscore after the letter. Trailing spaces are
+	trimmed. Illegal characters are escaped as two hex bytes. If the
+	result starts with a number (as the result of a hex escape), an
+	extra underscore is prepended. Examples:
+		'glyf' -> '_g_l_y_f'
+		'cvt ' -> '_c_v_t'
+		'OS/2' -> 'O_S_2f_2'
+	"""
+	import re
+	tag = Tag(tag)
+	if tag == "GlyphOrder":
+		return tag
+	assert len(tag) == 4, "tag should be 4 characters long"
+	while len(tag) > 1 and tag[-1] == ' ':
+		tag = tag[:-1]
+	ident = ""
+	for c in tag:
+		ident = ident + _escapechar(c)
+	if re.match("[0-9]", ident):
+		ident = "_" + ident
+	return ident
+
+
+def identifierToTag(ident):
+	"""the opposite of tagToIdentifier()"""
+	if ident == "GlyphOrder":
+		return ident
+	if len(ident) % 2 and ident[0] == "_":
+		ident = ident[1:]
+	assert not (len(ident) % 2)
+	tag = ""
+	for i in range(0, len(ident), 2):
+		if ident[i] == "_":
+			tag = tag + ident[i+1]
+		elif ident[i+1] == "_":
+			tag = tag + ident[i]
+		else:
+			# assume hex
+			tag = tag + chr(int(ident[i:i+2], 16))
+	# append trailing spaces
+	tag = tag + (4 - len(tag)) * ' '
+	return Tag(tag)
+
+
+def tagToXML(tag):
+	"""Similarly to tagToIdentifier(), this converts a TT tag
+	to a valid XML element name. Since XML element names are
+	case sensitive, this is a fairly simple/readable translation.
+	"""
+	import re
+	tag = Tag(tag)
+	if tag == "OS/2":
+		return "OS_2"
+	elif tag == "GlyphOrder":
+		return tag
+	if re.match("[A-Za-z_][A-Za-z_0-9]* *$", tag):
+		return tag.strip()
+	else:
+		return tagToIdentifier(tag)
+
+
+def xmlToTag(tag):
+	"""The opposite of tagToXML()"""
+	if tag == "OS_2":
+		return Tag("OS/2")
+	if len(tag) == 8:
+		return identifierToTag(tag)
+	else:
+		return Tag(tag + " " * (4 - len(tag)))
+
+
+
+# Table order as recommended in the OpenType specification 1.4
+TTFTableOrder = ["head", "hhea", "maxp", "OS/2", "hmtx", "LTSH", "VDMX",
+				"hdmx", "cmap", "fpgm", "prep", "cvt ", "loca", "glyf",
+				"kern", "name", "post", "gasp", "PCLT"]
+
+OTFTableOrder = ["head", "hhea", "maxp", "OS/2", "name", "cmap", "post",
+				"CFF "]
+
+def sortedTagList(tagList, tableOrder=None):
+	"""Return a sorted copy of tagList, sorted according to the OpenType
+	specification, or according to a custom tableOrder. If given and not
+	None, tableOrder needs to be a list of tag names.
+	"""
+	tagList = sorted(tagList)
+	if tableOrder is None:
+		if "DSIG" in tagList:
+			# DSIG should be last (XXX spec reference?)
+			tagList.remove("DSIG")
+			tagList.append("DSIG")
+		if "CFF " in tagList:
+			tableOrder = OTFTableOrder
+		else:
+			tableOrder = TTFTableOrder
+	orderedTables = []
+	for tag in tableOrder:
+		if tag in tagList:
+			orderedTables.append(tag)
+			tagList.remove(tag)
+	orderedTables.extend(tagList)
+	return orderedTables
+
+
+def reorderFontTables(inFile, outFile, tableOrder=None, checkChecksums=False):
+	"""Rewrite a font file, ordering the tables as recommended by the
+	OpenType specification 1.4.
+	"""
+	inFile.seek(0)
+	outFile.seek(0)
+	reader = SFNTReader(inFile, checkChecksums=checkChecksums)
+	writer = SFNTWriter(outFile, len(reader.tables), reader.sfntVersion, reader.flavor, reader.flavorData)
+	tables = list(reader.keys())
+	for tag in sortedTagList(tables, tableOrder):
+		writer[tag] = reader[tag]
+	writer.close()
+
+
+def maxPowerOfTwo(x):
+	"""Return the highest exponent of two, so that
+	(2 ** exponent) <= x.  Return 0 if x is 0.
+	"""
+	exponent = 0
+	while x:
+		x = x >> 1
+		exponent = exponent + 1
+	return max(exponent - 1, 0)
+
+
+def getSearchRange(n, itemSize=16):
+	"""Calculate searchRange, entrySelector, rangeShift.
+	"""
+	# itemSize defaults to 16, for backward compatibility
+	# with upstream fonttools.
+	exponent = maxPowerOfTwo(n)
+	searchRange = (2 ** exponent) * itemSize
+	entrySelector = exponent
+	rangeShift = max(0, n * itemSize - searchRange)
+	return searchRange, entrySelector, rangeShift
diff --git a/Lib/fontTools/ttLib/woff2.py b/Lib/fontTools/ttLib/woff2.py
new file mode 100644
index 0000000..1952682
--- /dev/null
+++ b/Lib/fontTools/ttLib/woff2.py
@@ -0,0 +1,1098 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+import sys
+import array
+import struct
+from collections import OrderedDict
+from fontTools.misc import sstruct
+from fontTools.misc.arrayTools import calcIntBounds
+from fontTools.misc.textTools import pad
+from fontTools.ttLib import (TTFont, TTLibError, getTableModule, getTableClass,
+	getSearchRange)
+from fontTools.ttLib.sfnt import (SFNTReader, SFNTWriter, DirectoryEntry,
+	WOFFFlavorData, sfntDirectoryFormat, sfntDirectorySize, SFNTDirectoryEntry,
+	sfntDirectoryEntrySize, calcChecksum)
+from fontTools.ttLib.tables import ttProgram
+import logging
+
+
+log = logging.getLogger(__name__)
+
+haveBrotli = False
+try:
+	import brotli
+	haveBrotli = True
+except ImportError:
+	pass
+
+
+class WOFF2Reader(SFNTReader):
+
+	flavor = "woff2"
+
+	def __init__(self, file, checkChecksums=1, fontNumber=-1):
+		if not haveBrotli:
+			log.error(
+				'The WOFF2 decoder requires the Brotli Python extension, available at: '
+				'https://github.com/google/brotli')
+			raise ImportError("No module named brotli")
+
+		self.file = file
+
+		signature = Tag(self.file.read(4))
+		if signature != b"wOF2":
+			raise TTLibError("Not a WOFF2 font (bad signature)")
+
+		self.file.seek(0)
+		self.DirectoryEntry = WOFF2DirectoryEntry
+		data = self.file.read(woff2DirectorySize)
+		if len(data) != woff2DirectorySize:
+			raise TTLibError('Not a WOFF2 font (not enough data)')
+		sstruct.unpack(woff2DirectoryFormat, data, self)
+
+		self.tables = OrderedDict()
+		offset = 0
+		for i in range(self.numTables):
+			entry = self.DirectoryEntry()
+			entry.fromFile(self.file)
+			tag = Tag(entry.tag)
+			self.tables[tag] = entry
+			entry.offset = offset
+			offset += entry.length
+
+		totalUncompressedSize = offset
+		compressedData = self.file.read(self.totalCompressedSize)
+		decompressedData = brotli.decompress(compressedData)
+		if len(decompressedData) != totalUncompressedSize:
+			raise TTLibError(
+				'unexpected size for decompressed font data: expected %d, found %d'
+				% (totalUncompressedSize, len(decompressedData)))
+		self.transformBuffer = BytesIO(decompressedData)
+
+		self.file.seek(0, 2)
+		if self.length != self.file.tell():
+			raise TTLibError("reported 'length' doesn't match the actual file size")
+
+		self.flavorData = WOFF2FlavorData(self)
+
+		# make empty TTFont to store data while reconstructing tables
+		self.ttFont = TTFont(recalcBBoxes=False, recalcTimestamp=False)
+
+	def __getitem__(self, tag):
+		"""Fetch the raw table data. Reconstruct transformed tables."""
+		entry = self.tables[Tag(tag)]
+		if not hasattr(entry, 'data'):
+			if tag in woff2TransformedTableTags:
+				entry.data = self.reconstructTable(tag)
+			else:
+				entry.data = entry.loadData(self.transformBuffer)
+		return entry.data
+
+	def reconstructTable(self, tag):
+		"""Reconstruct table named 'tag' from transformed data."""
+		if tag not in woff2TransformedTableTags:
+			raise TTLibError("transform for table '%s' is unknown" % tag)
+		entry = self.tables[Tag(tag)]
+		rawData = entry.loadData(self.transformBuffer)
+		if tag == 'glyf':
+			# no need to pad glyph data when reconstructing
+			padding = self.padding if hasattr(self, 'padding') else None
+			data = self._reconstructGlyf(rawData, padding)
+		elif tag == 'loca':
+			data = self._reconstructLoca()
+		else:
+			raise NotImplementedError
+		return data
+
+	def _reconstructGlyf(self, data, padding=None):
+		""" Return recostructed glyf table data, and set the corresponding loca's
+		locations. Optionally pad glyph offsets to the specified number of bytes.
+		"""
+		self.ttFont['loca'] = WOFF2LocaTable()
+		glyfTable = self.ttFont['glyf'] = WOFF2GlyfTable()
+		glyfTable.reconstruct(data, self.ttFont)
+		if padding:
+			glyfTable.padding = padding
+		data = glyfTable.compile(self.ttFont)
+		return data
+
+	def _reconstructLoca(self):
+		""" Return reconstructed loca table data. """
+		if 'loca' not in self.ttFont:
+			# make sure glyf is reconstructed first
+			self.tables['glyf'].data = self.reconstructTable('glyf')
+		locaTable = self.ttFont['loca']
+		data = locaTable.compile(self.ttFont)
+		if len(data) != self.tables['loca'].origLength:
+			raise TTLibError(
+				"reconstructed 'loca' table doesn't match original size: "
+				"expected %d, found %d"
+				% (self.tables['loca'].origLength, len(data)))
+		return data
+
+
+class WOFF2Writer(SFNTWriter):
+
+	flavor = "woff2"
+
+	def __init__(self, file, numTables, sfntVersion="\000\001\000\000",
+		         flavor=None, flavorData=None):
+		if not haveBrotli:
+			log.error(
+				'The WOFF2 encoder requires the Brotli Python extension, available at: '
+				'https://github.com/google/brotli')
+			raise ImportError("No module named brotli")
+
+		self.file = file
+		self.numTables = numTables
+		self.sfntVersion = Tag(sfntVersion)
+		self.flavorData = flavorData or WOFF2FlavorData()
+
+		self.directoryFormat = woff2DirectoryFormat
+		self.directorySize = woff2DirectorySize
+		self.DirectoryEntry = WOFF2DirectoryEntry
+
+		self.signature = Tag("wOF2")
+
+		self.nextTableOffset = 0
+		self.transformBuffer = BytesIO()
+
+		self.tables = OrderedDict()
+
+		# make empty TTFont to store data while normalising and transforming tables
+		self.ttFont = TTFont(recalcBBoxes=False, recalcTimestamp=False)
+
+	def __setitem__(self, tag, data):
+		"""Associate new entry named 'tag' with raw table data."""
+		if tag in self.tables:
+			raise TTLibError("cannot rewrite '%s' table" % tag)
+		if tag == 'DSIG':
+			# always drop DSIG table, since the encoding process can invalidate it
+			self.numTables -= 1
+			return
+
+		entry = self.DirectoryEntry()
+		entry.tag = Tag(tag)
+		entry.flags = getKnownTagIndex(entry.tag)
+		# WOFF2 table data are written to disk only on close(), after all tags
+		# have been specified
+		entry.data = data
+
+		self.tables[tag] = entry
+
+	def close(self):
+		""" All tags must have been specified. Now write the table data and directory.
+		"""
+		if len(self.tables) != self.numTables:
+			raise TTLibError("wrong number of tables; expected %d, found %d" % (self.numTables, len(self.tables)))
+
+		if self.sfntVersion in ("\x00\x01\x00\x00", "true"):
+			isTrueType = True
+		elif self.sfntVersion == "OTTO":
+			isTrueType = False
+		else:
+			raise TTLibError("Not a TrueType or OpenType font (bad sfntVersion)")
+
+		# The WOFF2 spec no longer requires the glyph offsets to be 4-byte aligned.
+		# However, the reference WOFF2 implementation still fails to reconstruct
+		# 'unpadded' glyf tables, therefore we need to 'normalise' them.
+		# See:
+		# https://github.com/khaledhosny/ots/issues/60
+		# https://github.com/google/woff2/issues/15
+		if isTrueType:
+			self._normaliseGlyfAndLoca(padding=4)
+		self._setHeadTransformFlag()
+
+		# To pass the legacy OpenType Sanitiser currently included in browsers,
+		# we must sort the table directory and data alphabetically by tag.
+		# See:
+		# https://github.com/google/woff2/pull/3
+		# https://lists.w3.org/Archives/Public/public-webfonts-wg/2015Mar/0000.html
+		# TODO(user): remove to match spec once browsers are on newer OTS
+		self.tables = OrderedDict(sorted(self.tables.items()))
+
+		self.totalSfntSize = self._calcSFNTChecksumsLengthsAndOffsets()
+
+		fontData = self._transformTables()
+		compressedFont = brotli.compress(fontData, mode=brotli.MODE_FONT)
+
+		self.totalCompressedSize = len(compressedFont)
+		self.length = self._calcTotalSize()
+		self.majorVersion, self.minorVersion = self._getVersion()
+		self.reserved = 0
+
+		directory = self._packTableDirectory()
+		self.file.seek(0)
+		self.file.write(pad(directory + compressedFont, size=4))
+		self._writeFlavorData()
+
+	def _normaliseGlyfAndLoca(self, padding=4):
+		""" Recompile glyf and loca tables, aligning glyph offsets to multiples of
+		'padding' size. Update the head table's 'indexToLocFormat' accordingly while
+		compiling loca.
+		"""
+		if self.sfntVersion == "OTTO":
+			return
+
+		# make up glyph names required to decompile glyf table
+		self._decompileTable('maxp')
+		numGlyphs = self.ttFont['maxp'].numGlyphs
+		glyphOrder = ['.notdef'] + ["glyph%.5d" % i for i in range(1, numGlyphs)]
+		self.ttFont.setGlyphOrder(glyphOrder)
+
+		for tag in ('head', 'loca', 'glyf'):
+			self._decompileTable(tag)
+		self.ttFont['glyf'].padding = padding
+		for tag in ('glyf', 'loca'):
+			self._compileTable(tag)
+
+	def _setHeadTransformFlag(self):
+		""" Set bit 11 of 'head' table flags to indicate that the font has undergone
+		a lossless modifying transform. Re-compile head table data."""
+		self._decompileTable('head')
+		self.ttFont['head'].flags |= (1 << 11)
+		self._compileTable('head')
+
+	def _decompileTable(self, tag):
+		""" Fetch table data, decompile it, and store it inside self.ttFont. """
+		tag = Tag(tag)
+		if tag not in self.tables:
+			raise TTLibError("missing required table: %s" % tag)
+		if self.ttFont.isLoaded(tag):
+			return
+		data = self.tables[tag].data
+		if tag == 'loca':
+			tableClass = WOFF2LocaTable
+		elif tag == 'glyf':
+			tableClass = WOFF2GlyfTable
+		else:
+			tableClass = getTableClass(tag)
+		table = tableClass(tag)
+		self.ttFont.tables[tag] = table
+		table.decompile(data, self.ttFont)
+
+	def _compileTable(self, tag):
+		""" Compile table and store it in its 'data' attribute. """
+		self.tables[tag].data = self.ttFont[tag].compile(self.ttFont)
+
+	def _calcSFNTChecksumsLengthsAndOffsets(self):
+		""" Compute the 'original' SFNT checksums, lengths and offsets for checksum
+		adjustment calculation. Return the total size of the uncompressed font.
+		"""
+		offset = sfntDirectorySize + sfntDirectoryEntrySize * len(self.tables)
+		for tag, entry in self.tables.items():
+			data = entry.data
+			entry.origOffset = offset
+			entry.origLength = len(data)
+			if tag == 'head':
+				entry.checkSum = calcChecksum(data[:8] + b'\0\0\0\0' + data[12:])
+			else:
+				entry.checkSum = calcChecksum(data)
+			offset += (entry.origLength + 3) & ~3
+		return offset
+
+	def _transformTables(self):
+		"""Return transformed font data."""
+		for tag, entry in self.tables.items():
+			if tag in woff2TransformedTableTags:
+				data = self.transformTable(tag)
+			else:
+				data = entry.data
+			entry.offset = self.nextTableOffset
+			entry.saveData(self.transformBuffer, data)
+			self.nextTableOffset += entry.length
+		self.writeMasterChecksum()
+		fontData = self.transformBuffer.getvalue()
+		return fontData
+
+	def transformTable(self, tag):
+		"""Return transformed table data."""
+		if tag not in woff2TransformedTableTags:
+			raise TTLibError("Transform for table '%s' is unknown" % tag)
+		if tag == "loca":
+			data = b""
+		elif tag == "glyf":
+			for tag in ('maxp', 'head', 'loca', 'glyf'):
+				self._decompileTable(tag)
+			glyfTable = self.ttFont['glyf']
+			data = glyfTable.transform(self.ttFont)
+		else:
+			raise NotImplementedError
+		return data
+
+	def _calcMasterChecksum(self):
+		"""Calculate checkSumAdjustment."""
+		tags = list(self.tables.keys())
+		checksums = []
+		for i in range(len(tags)):
+			checksums.append(self.tables[tags[i]].checkSum)
+
+		# Create a SFNT directory for checksum calculation purposes
+		self.searchRange, self.entrySelector, self.rangeShift = getSearchRange(self.numTables, 16)
+		directory = sstruct.pack(sfntDirectoryFormat, self)
+		tables = sorted(self.tables.items())
+		for tag, entry in tables:
+			sfntEntry = SFNTDirectoryEntry()
+			sfntEntry.tag = entry.tag
+			sfntEntry.checkSum = entry.checkSum
+			sfntEntry.offset = entry.origOffset
+			sfntEntry.length = entry.origLength
+			directory = directory + sfntEntry.toString()
+
+		directory_end = sfntDirectorySize + len(self.tables) * sfntDirectoryEntrySize
+		assert directory_end == len(directory)
+
+		checksums.append(calcChecksum(directory))
+		checksum = sum(checksums) & 0xffffffff
+		# BiboAfba!
+		checksumadjustment = (0xB1B0AFBA - checksum) & 0xffffffff
+		return checksumadjustment
+
+	def writeMasterChecksum(self):
+		"""Write checkSumAdjustment to the transformBuffer."""
+		checksumadjustment = self._calcMasterChecksum()
+		self.transformBuffer.seek(self.tables['head'].offset + 8)
+		self.transformBuffer.write(struct.pack(">L", checksumadjustment))
+
+	def _calcTotalSize(self):
+		"""Calculate total size of WOFF2 font, including any meta- and/or private data."""
+		offset = self.directorySize
+		for entry in self.tables.values():
+			offset += len(entry.toString())
+		offset += self.totalCompressedSize
+		offset = (offset + 3) & ~3
+		offset = self._calcFlavorDataOffsetsAndSize(offset)
+		return offset
+
+	def _calcFlavorDataOffsetsAndSize(self, start):
+		"""Calculate offsets and lengths for any meta- and/or private data."""
+		offset = start
+		data = self.flavorData
+		if data.metaData:
+			self.metaOrigLength = len(data.metaData)
+			self.metaOffset = offset
+			self.compressedMetaData = brotli.compress(
+				data.metaData, mode=brotli.MODE_TEXT)
+			self.metaLength = len(self.compressedMetaData)
+			offset += self.metaLength
+		else:
+			self.metaOffset = self.metaLength = self.metaOrigLength = 0
+			self.compressedMetaData = b""
+		if data.privData:
+			# make sure private data is padded to 4-byte boundary
+			offset = (offset + 3) & ~3
+			self.privOffset = offset
+			self.privLength = len(data.privData)
+			offset += self.privLength
+		else:
+			self.privOffset = self.privLength = 0
+		return offset
+
+	def _getVersion(self):
+		"""Return the WOFF2 font's (majorVersion, minorVersion) tuple."""
+		data = self.flavorData
+		if data.majorVersion is not None and data.minorVersion is not None:
+			return data.majorVersion, data.minorVersion
+		else:
+			# if None, return 'fontRevision' from 'head' table
+			if 'head' in self.tables:
+				return struct.unpack(">HH", self.tables['head'].data[4:8])
+			else:
+				return 0, 0
+
+	def _packTableDirectory(self):
+		"""Return WOFF2 table directory data."""
+		directory = sstruct.pack(self.directoryFormat, self)
+		for entry in self.tables.values():
+			directory = directory + entry.toString()
+		return directory
+
+	def _writeFlavorData(self):
+		"""Write metadata and/or private data using appropiate padding."""
+		compressedMetaData = self.compressedMetaData
+		privData = self.flavorData.privData
+		if compressedMetaData and privData:
+			compressedMetaData = pad(compressedMetaData, size=4)
+		if compressedMetaData:
+			self.file.seek(self.metaOffset)
+			assert self.file.tell() == self.metaOffset
+			self.file.write(compressedMetaData)
+		if privData:
+			self.file.seek(self.privOffset)
+			assert self.file.tell() == self.privOffset
+			self.file.write(privData)
+
+	def reordersTables(self):
+		return True
+
+
+# -- woff2 directory helpers and cruft
+
+woff2DirectoryFormat = """
+		> # big endian
+		signature:           4s   # "wOF2"
+		sfntVersion:         4s
+		length:              L    # total woff2 file size
+		numTables:           H    # number of tables
+		reserved:            H    # set to 0
+		totalSfntSize:       L    # uncompressed size
+		totalCompressedSize: L    # compressed size
+		majorVersion:        H    # major version of WOFF file
+		minorVersion:        H    # minor version of WOFF file
+		metaOffset:          L    # offset to metadata block
+		metaLength:          L    # length of compressed metadata
+		metaOrigLength:      L    # length of uncompressed metadata
+		privOffset:          L    # offset to private data block
+		privLength:          L    # length of private data block
+"""
+
+woff2DirectorySize = sstruct.calcsize(woff2DirectoryFormat)
+
+woff2KnownTags = (
+	"cmap", "head", "hhea", "hmtx", "maxp", "name", "OS/2", "post", "cvt ",
+	"fpgm", "glyf", "loca", "prep", "CFF ", "VORG", "EBDT", "EBLC", "gasp",
+	"hdmx", "kern", "LTSH", "PCLT", "VDMX", "vhea", "vmtx", "BASE", "GDEF",
+	"GPOS", "GSUB", "EBSC", "JSTF", "MATH", "CBDT", "CBLC", "COLR", "CPAL",
+	"SVG ", "sbix", "acnt", "avar", "bdat", "bloc", "bsln", "cvar", "fdsc",
+	"feat", "fmtx", "fvar", "gvar", "hsty", "just", "lcar", "mort", "morx",
+	"opbd", "prop", "trak", "Zapf", "Silf", "Glat", "Gloc", "Feat", "Sill")
+
+woff2FlagsFormat = """
+		> # big endian
+		flags: B  # table type and flags
+"""
+
+woff2FlagsSize = sstruct.calcsize(woff2FlagsFormat)
+
+woff2UnknownTagFormat = """
+		> # big endian
+		tag: 4s  # 4-byte tag (optional)
+"""
+
+woff2UnknownTagSize = sstruct.calcsize(woff2UnknownTagFormat)
+
+woff2UnknownTagIndex = 0x3F
+
+woff2Base128MaxSize = 5
+woff2DirectoryEntryMaxSize = woff2FlagsSize + woff2UnknownTagSize + 2 * woff2Base128MaxSize
+
+woff2TransformedTableTags = ('glyf', 'loca')
+
+woff2GlyfTableFormat = """
+		> # big endian
+		version:                  L  # = 0x00000000
+		numGlyphs:                H  # Number of glyphs
+		indexFormat:              H  # Offset format for loca table
+		nContourStreamSize:       L  # Size of nContour stream
+		nPointsStreamSize:        L  # Size of nPoints stream
+		flagStreamSize:           L  # Size of flag stream
+		glyphStreamSize:          L  # Size of glyph stream
+		compositeStreamSize:      L  # Size of composite stream
+		bboxStreamSize:           L  # Comnined size of bboxBitmap and bboxStream
+		instructionStreamSize:    L  # Size of instruction stream
+"""
+
+woff2GlyfTableFormatSize = sstruct.calcsize(woff2GlyfTableFormat)
+
+bboxFormat = """
+		>	# big endian
+		xMin:				h
+		yMin:				h
+		xMax:				h
+		yMax:				h
+"""
+
+
+def getKnownTagIndex(tag):
+	"""Return index of 'tag' in woff2KnownTags list. Return 63 if not found."""
+	for i in range(len(woff2KnownTags)):
+		if tag == woff2KnownTags[i]:
+			return i
+	return woff2UnknownTagIndex
+
+
+class WOFF2DirectoryEntry(DirectoryEntry):
+
+	def fromFile(self, file):
+		pos = file.tell()
+		data = file.read(woff2DirectoryEntryMaxSize)
+		left = self.fromString(data)
+		consumed = len(data) - len(left)
+		file.seek(pos + consumed)
+
+	def fromString(self, data):
+		if len(data) < 1:
+			raise TTLibError("can't read table 'flags': not enough data")
+		dummy, data = sstruct.unpack2(woff2FlagsFormat, data, self)
+		if self.flags & 0x3F == 0x3F:
+			# if bits [0..5] of the flags byte == 63, read a 4-byte arbitrary tag value
+			if len(data) < woff2UnknownTagSize:
+				raise TTLibError("can't read table 'tag': not enough data")
+			dummy, data = sstruct.unpack2(woff2UnknownTagFormat, data, self)
+		else:
+			# otherwise, tag is derived from a fixed 'Known Tags' table
+			self.tag = woff2KnownTags[self.flags & 0x3F]
+		self.tag = Tag(self.tag)
+		if self.flags & 0xC0 != 0:
+			raise TTLibError('bits 6-7 are reserved and must be 0')
+		self.origLength, data = unpackBase128(data)
+		self.length = self.origLength
+		if self.tag in woff2TransformedTableTags:
+			self.length, data = unpackBase128(data)
+			if self.tag == 'loca' and self.length != 0:
+				raise TTLibError(
+					"the transformLength of the 'loca' table must be 0")
+		# return left over data
+		return data
+
+	def toString(self):
+		data = bytechr(self.flags)
+		if (self.flags & 0x3F) == 0x3F:
+			data += struct.pack('>4s', self.tag.tobytes())
+		data += packBase128(self.origLength)
+		if self.tag in woff2TransformedTableTags:
+			data += packBase128(self.length)
+		return data
+
+
+class WOFF2LocaTable(getTableClass('loca')):
+	"""Same as parent class. The only difference is that it attempts to preserve
+	the 'indexFormat' as encoded in the WOFF2 glyf table.
+	"""
+
+	def __init__(self, tag=None):
+		self.tableTag = Tag(tag or 'loca')
+
+	def compile(self, ttFont):
+		try:
+			max_location = max(self.locations)
+		except AttributeError:
+			self.set([])
+			max_location = 0
+		if 'glyf' in ttFont and hasattr(ttFont['glyf'], 'indexFormat'):
+			# copile loca using the indexFormat specified in the WOFF2 glyf table
+			indexFormat = ttFont['glyf'].indexFormat
+			if indexFormat == 0:
+				if max_location >= 0x20000:
+					raise TTLibError("indexFormat is 0 but local offsets > 0x20000")
+				if not all(l % 2 == 0 for l in self.locations):
+					raise TTLibError("indexFormat is 0 but local offsets not multiples of 2")
+				locations = array.array("H")
+				for i in range(len(self.locations)):
+					locations.append(self.locations[i] // 2)
+			else:
+				locations = array.array("I", self.locations)
+			if sys.byteorder != "big":
+				locations.byteswap()
+			data = locations.tostring()
+		else:
+			# use the most compact indexFormat given the current glyph offsets
+			data = super(WOFF2LocaTable, self).compile(ttFont)
+		return data
+
+
+class WOFF2GlyfTable(getTableClass('glyf')):
+	"""Decoder/Encoder for WOFF2 'glyf' table transform."""
+
+	subStreams = (
+		'nContourStream', 'nPointsStream', 'flagStream', 'glyphStream',
+		'compositeStream', 'bboxStream', 'instructionStream')
+
+	def __init__(self, tag=None):
+		self.tableTag = Tag(tag or 'glyf')
+
+	def reconstruct(self, data, ttFont):
+		""" Decompile transformed 'glyf' data. """
+		inputDataSize = len(data)
+
+		if inputDataSize < woff2GlyfTableFormatSize:
+			raise TTLibError("not enough 'glyf' data")
+		dummy, data = sstruct.unpack2(woff2GlyfTableFormat, data, self)
+		offset = woff2GlyfTableFormatSize
+
+		for stream in self.subStreams:
+			size = getattr(self, stream + 'Size')
+			setattr(self, stream, data[:size])
+			data = data[size:]
+			offset += size
+
+		if offset != inputDataSize:
+			raise TTLibError(
+				"incorrect size of transformed 'glyf' table: expected %d, received %d bytes"
+				% (offset, inputDataSize))
+
+		bboxBitmapSize = ((self.numGlyphs + 31) >> 5) << 2
+		bboxBitmap = self.bboxStream[:bboxBitmapSize]
+		self.bboxBitmap = array.array('B', bboxBitmap)
+		self.bboxStream = self.bboxStream[bboxBitmapSize:]
+
+		self.nContourStream = array.array("h", self.nContourStream)
+		if sys.byteorder != "big":
+			self.nContourStream.byteswap()
+		assert len(self.nContourStream) == self.numGlyphs
+
+		if 'head' in ttFont:
+			ttFont['head'].indexToLocFormat = self.indexFormat
+		try:
+			self.glyphOrder = ttFont.getGlyphOrder()
+		except:
+			self.glyphOrder = None
+		if self.glyphOrder is None:
+			self.glyphOrder = [".notdef"]
+			self.glyphOrder.extend(["glyph%.5d" % i for i in range(1, self.numGlyphs)])
+		else:
+			if len(self.glyphOrder) != self.numGlyphs:
+				raise TTLibError(
+					"incorrect glyphOrder: expected %d glyphs, found %d" %
+					(len(self.glyphOrder), self.numGlyphs))
+
+		glyphs = self.glyphs = {}
+		for glyphID, glyphName in enumerate(self.glyphOrder):
+			glyph = self._decodeGlyph(glyphID)
+			glyphs[glyphName] = glyph
+
+	def transform(self, ttFont):
+		""" Return transformed 'glyf' data """
+		self.numGlyphs = len(self.glyphs)
+		if not hasattr(self, "glyphOrder"):
+			try:
+				self.glyphOrder = ttFont.getGlyphOrder()
+			except:
+				self.glyphOrder = None
+			if self.glyphOrder is None:
+				self.glyphOrder = [".notdef"]
+				self.glyphOrder.extend(["glyph%.5d" % i for i in range(1, self.numGlyphs)])
+		if len(self.glyphOrder) != self.numGlyphs:
+			raise TTLibError(
+				"incorrect glyphOrder: expected %d glyphs, found %d" %
+				(len(self.glyphOrder), self.numGlyphs))
+
+		if 'maxp' in ttFont:
+			ttFont['maxp'].numGlyphs = self.numGlyphs
+		self.indexFormat = ttFont['head'].indexToLocFormat
+
+		for stream in self.subStreams:
+			setattr(self, stream, b"")
+		bboxBitmapSize = ((self.numGlyphs + 31) >> 5) << 2
+		self.bboxBitmap = array.array('B', [0]*bboxBitmapSize)
+
+		for glyphID in range(self.numGlyphs):
+			self._encodeGlyph(glyphID)
+
+		self.bboxStream = self.bboxBitmap.tostring() + self.bboxStream
+		for stream in self.subStreams:
+			setattr(self, stream + 'Size', len(getattr(self, stream)))
+		self.version = 0
+		data = sstruct.pack(woff2GlyfTableFormat, self)
+		data += bytesjoin([getattr(self, s) for s in self.subStreams])
+		return data
+
+	def _decodeGlyph(self, glyphID):
+		glyph = getTableModule('glyf').Glyph()
+		glyph.numberOfContours = self.nContourStream[glyphID]
+		if glyph.numberOfContours == 0:
+			return glyph
+		elif glyph.isComposite():
+			self._decodeComponents(glyph)
+		else:
+			self._decodeCoordinates(glyph)
+		self._decodeBBox(glyphID, glyph)
+		return glyph
+
+	def _decodeComponents(self, glyph):
+		data = self.compositeStream
+		glyph.components = []
+		more = 1
+		haveInstructions = 0
+		while more:
+			component = getTableModule('glyf').GlyphComponent()
+			more, haveInstr, data = component.decompile(data, self)
+			haveInstructions = haveInstructions | haveInstr
+			glyph.components.append(component)
+		self.compositeStream = data
+		if haveInstructions:
+			self._decodeInstructions(glyph)
+
+	def _decodeCoordinates(self, glyph):
+		data = self.nPointsStream
+		endPtsOfContours = []
+		endPoint = -1
+		for i in range(glyph.numberOfContours):
+			ptsOfContour, data = unpack255UShort(data)
+			endPoint += ptsOfContour
+			endPtsOfContours.append(endPoint)
+		glyph.endPtsOfContours = endPtsOfContours
+		self.nPointsStream = data
+		self._decodeTriplets(glyph)
+		self._decodeInstructions(glyph)
+
+	def _decodeInstructions(self, glyph):
+		glyphStream = self.glyphStream
+		instructionStream = self.instructionStream
+		instructionLength, glyphStream = unpack255UShort(glyphStream)
+		glyph.program = ttProgram.Program()
+		glyph.program.fromBytecode(instructionStream[:instructionLength])
+		self.glyphStream = glyphStream
+		self.instructionStream = instructionStream[instructionLength:]
+
+	def _decodeBBox(self, glyphID, glyph):
+		haveBBox = bool(self.bboxBitmap[glyphID >> 3] & (0x80 >> (glyphID & 7)))
+		if glyph.isComposite() and not haveBBox:
+			raise TTLibError('no bbox values for composite glyph %d' % glyphID)
+		if haveBBox:
+			dummy, self.bboxStream = sstruct.unpack2(bboxFormat, self.bboxStream, glyph)
+		else:
+			glyph.recalcBounds(self)
+
+	def _decodeTriplets(self, glyph):
+
+		def withSign(flag, baseval):
+			assert 0 <= baseval and baseval < 65536, 'integer overflow'
+			return baseval if flag & 1 else -baseval
+
+		nPoints = glyph.endPtsOfContours[-1] + 1
+		flagSize = nPoints
+		if flagSize > len(self.flagStream):
+			raise TTLibError("not enough 'flagStream' data")
+		flagsData = self.flagStream[:flagSize]
+		self.flagStream = self.flagStream[flagSize:]
+		flags = array.array('B', flagsData)
+
+		triplets = array.array('B', self.glyphStream)
+		nTriplets = len(triplets)
+		assert nPoints <= nTriplets
+
+		x = 0
+		y = 0
+		glyph.coordinates = getTableModule('glyf').GlyphCoordinates.zeros(nPoints)
+		glyph.flags = array.array("B")
+		tripletIndex = 0
+		for i in range(nPoints):
+			flag = flags[i]
+			onCurve = not bool(flag >> 7)
+			flag &= 0x7f
+			if flag < 84:
+				nBytes = 1
+			elif flag < 120:
+				nBytes = 2
+			elif flag < 124:
+				nBytes = 3
+			else:
+				nBytes = 4
+			assert ((tripletIndex + nBytes) <= nTriplets)
+			if flag < 10:
+				dx = 0
+				dy = withSign(flag, ((flag & 14) << 7) + triplets[tripletIndex])
+			elif flag < 20:
+				dx = withSign(flag, (((flag - 10) & 14) << 7) + triplets[tripletIndex])
+				dy = 0
+			elif flag < 84:
+				b0 = flag - 20
+				b1 = triplets[tripletIndex]
+				dx = withSign(flag, 1 + (b0 & 0x30) + (b1 >> 4))
+				dy = withSign(flag >> 1, 1 + ((b0 & 0x0c) << 2) + (b1 & 0x0f))
+			elif flag < 120:
+				b0 = flag - 84
+				dx = withSign(flag, 1 + ((b0 // 12) << 8) + triplets[tripletIndex])
+				dy = withSign(flag >> 1,
+					1 + (((b0 % 12) >> 2) << 8) + triplets[tripletIndex + 1])
+			elif flag < 124:
+				b2 = triplets[tripletIndex + 1]
+				dx = withSign(flag, (triplets[tripletIndex] << 4) + (b2 >> 4))
+				dy = withSign(flag >> 1,
+					((b2 & 0x0f) << 8) + triplets[tripletIndex + 2])
+			else:
+				dx = withSign(flag,
+					(triplets[tripletIndex] << 8) + triplets[tripletIndex + 1])
+				dy = withSign(flag >> 1,
+					(triplets[tripletIndex + 2] << 8) + triplets[tripletIndex + 3])
+			tripletIndex += nBytes
+			x += dx
+			y += dy
+			glyph.coordinates[i] = (x, y)
+			glyph.flags.append(int(onCurve))
+		bytesConsumed = tripletIndex
+		self.glyphStream = self.glyphStream[bytesConsumed:]
+
+	def _encodeGlyph(self, glyphID):
+		glyphName = self.getGlyphName(glyphID)
+		glyph = self[glyphName]
+		self.nContourStream += struct.pack(">h", glyph.numberOfContours)
+		if glyph.numberOfContours == 0:
+			return
+		elif glyph.isComposite():
+			self._encodeComponents(glyph)
+		else:
+			self._encodeCoordinates(glyph)
+		self._encodeBBox(glyphID, glyph)
+
+	def _encodeComponents(self, glyph):
+		lastcomponent = len(glyph.components) - 1
+		more = 1
+		haveInstructions = 0
+		for i in range(len(glyph.components)):
+			if i == lastcomponent:
+				haveInstructions = hasattr(glyph, "program")
+				more = 0
+			component = glyph.components[i]
+			self.compositeStream += component.compile(more, haveInstructions, self)
+		if haveInstructions:
+			self._encodeInstructions(glyph)
+
+	def _encodeCoordinates(self, glyph):
+		lastEndPoint = -1
+		for endPoint in glyph.endPtsOfContours:
+			ptsOfContour = endPoint - lastEndPoint
+			self.nPointsStream += pack255UShort(ptsOfContour)
+			lastEndPoint = endPoint
+		self._encodeTriplets(glyph)
+		self._encodeInstructions(glyph)
+
+	def _encodeInstructions(self, glyph):
+		instructions = glyph.program.getBytecode()
+		self.glyphStream += pack255UShort(len(instructions))
+		self.instructionStream += instructions
+
+	def _encodeBBox(self, glyphID, glyph):
+		assert glyph.numberOfContours != 0, "empty glyph has no bbox"
+		if not glyph.isComposite():
+			# for simple glyphs, compare the encoded bounding box info with the calculated
+			# values, and if they match omit the bounding box info
+			currentBBox = glyph.xMin, glyph.yMin, glyph.xMax, glyph.yMax
+			calculatedBBox = calcIntBounds(glyph.coordinates)
+			if currentBBox == calculatedBBox:
+				return
+		self.bboxBitmap[glyphID >> 3] |= 0x80 >> (glyphID & 7)
+		self.bboxStream += sstruct.pack(bboxFormat, glyph)
+
+	def _encodeTriplets(self, glyph):
+		assert len(glyph.coordinates) == len(glyph.flags)
+		coordinates = glyph.coordinates.copy()
+		coordinates.absoluteToRelative()
+
+		flags = array.array('B')
+		triplets = array.array('B')
+		for i in range(len(coordinates)):
+			onCurve = glyph.flags[i]
+			x, y = coordinates[i]
+			absX = abs(x)
+			absY = abs(y)
+			onCurveBit = 0 if onCurve else 128
+			xSignBit = 0 if (x < 0) else 1
+			ySignBit = 0 if (y < 0) else 1
+			xySignBits = xSignBit + 2 * ySignBit
+
+			if x == 0 and absY < 1280:
+				flags.append(onCurveBit + ((absY & 0xf00) >> 7) + ySignBit)
+				triplets.append(absY & 0xff)
+			elif y == 0 and absX < 1280:
+				flags.append(onCurveBit + 10 + ((absX & 0xf00) >> 7) + xSignBit)
+				triplets.append(absX & 0xff)
+			elif absX < 65 and absY < 65:
+				flags.append(onCurveBit + 20 + ((absX - 1) & 0x30) + (((absY - 1) & 0x30) >> 2) + xySignBits)
+				triplets.append((((absX - 1) & 0xf) << 4) | ((absY - 1) & 0xf))
+			elif absX < 769 and absY < 769:
+				flags.append(onCurveBit + 84 + 12 * (((absX - 1) & 0x300) >> 8) + (((absY - 1) & 0x300) >> 6) + xySignBits)
+				triplets.append((absX - 1) & 0xff)
+				triplets.append((absY - 1) & 0xff)
+			elif absX < 4096 and absY < 4096:
+				flags.append(onCurveBit + 120 + xySignBits)
+				triplets.append(absX >> 4)
+				triplets.append(((absX & 0xf) << 4) | (absY >> 8))
+				triplets.append(absY & 0xff)
+			else:
+				flags.append(onCurveBit + 124 + xySignBits)
+				triplets.append(absX >> 8)
+				triplets.append(absX & 0xff)
+				triplets.append(absY >> 8)
+				triplets.append(absY & 0xff)
+
+		self.flagStream += flags.tostring()
+		self.glyphStream += triplets.tostring()
+
+
+class WOFF2FlavorData(WOFFFlavorData):
+
+	Flavor = 'woff2'
+
+	def __init__(self, reader=None):
+		if not haveBrotli:
+			raise ImportError("No module named brotli")
+		self.majorVersion = None
+		self.minorVersion = None
+		self.metaData = None
+		self.privData = None
+		if reader:
+			self.majorVersion = reader.majorVersion
+			self.minorVersion = reader.minorVersion
+			if reader.metaLength:
+				reader.file.seek(reader.metaOffset)
+				rawData = reader.file.read(reader.metaLength)
+				assert len(rawData) == reader.metaLength
+				data = brotli.decompress(rawData)
+				assert len(data) == reader.metaOrigLength
+				self.metaData = data
+			if reader.privLength:
+				reader.file.seek(reader.privOffset)
+				data = reader.file.read(reader.privLength)
+				assert len(data) == reader.privLength
+				self.privData = data
+
+
+def unpackBase128(data):
+	r""" Read one to five bytes from UIntBase128-encoded input string, and return
+	a tuple containing the decoded integer plus any leftover data.
+
+	>>> unpackBase128(b'\x3f\x00\x00') == (63, b"\x00\x00")
+	True
+	>>> unpackBase128(b'\x8f\xff\xff\xff\x7f')[0] == 4294967295
+	True
+	>>> unpackBase128(b'\x80\x80\x3f')  # doctest: +IGNORE_EXCEPTION_DETAIL
+	Traceback (most recent call last):
+	  File "<stdin>", line 1, in ?
+	TTLibError: UIntBase128 value must not start with leading zeros
+	>>> unpackBase128(b'\x8f\xff\xff\xff\xff\x7f')[0]  # doctest: +IGNORE_EXCEPTION_DETAIL
+	Traceback (most recent call last):
+	  File "<stdin>", line 1, in ?
+	TTLibError: UIntBase128-encoded sequence is longer than 5 bytes
+	>>> unpackBase128(b'\x90\x80\x80\x80\x00')[0]  # doctest: +IGNORE_EXCEPTION_DETAIL
+	Traceback (most recent call last):
+	  File "<stdin>", line 1, in ?
+	TTLibError: UIntBase128 value exceeds 2**32-1
+	"""
+	if len(data) == 0:
+		raise TTLibError('not enough data to unpack UIntBase128')
+	result = 0
+	if byteord(data[0]) == 0x80:
+		# font must be rejected if UIntBase128 value starts with 0x80
+		raise TTLibError('UIntBase128 value must not start with leading zeros')
+	for i in range(woff2Base128MaxSize):
+		if len(data) == 0:
+			raise TTLibError('not enough data to unpack UIntBase128')
+		code = byteord(data[0])
+		data = data[1:]
+		# if any of the top seven bits are set then we're about to overflow
+		if result & 0xFE000000:
+			raise TTLibError('UIntBase128 value exceeds 2**32-1')
+		# set current value = old value times 128 bitwise-or (byte bitwise-and 127)
+		result = (result << 7) | (code & 0x7f)
+		# repeat until the most significant bit of byte is false
+		if (code & 0x80) == 0:
+			# return result plus left over data
+			return result, data
+	# make sure not to exceed the size bound
+	raise TTLibError('UIntBase128-encoded sequence is longer than 5 bytes')
+
+
+def base128Size(n):
+	""" Return the length in bytes of a UIntBase128-encoded sequence with value n.
+
+	>>> base128Size(0)
+	1
+	>>> base128Size(24567)
+	3
+	>>> base128Size(2**32-1)
+	5
+	"""
+	assert n >= 0
+	size = 1
+	while n >= 128:
+		size += 1
+		n >>= 7
+	return size
+
+
+def packBase128(n):
+	r""" Encode unsigned integer in range 0 to 2**32-1 (inclusive) to a string of
+	bytes using UIntBase128 variable-length encoding. Produce the shortest possible
+	encoding.
+
+	>>> packBase128(63) == b"\x3f"
+	True
+	>>> packBase128(2**32-1) == b'\x8f\xff\xff\xff\x7f'
+	True
+	"""
+	if n < 0 or n >= 2**32:
+		raise TTLibError(
+			"UIntBase128 format requires 0 <= integer <= 2**32-1")
+	data = b''
+	size = base128Size(n)
+	for i in range(size):
+		b = (n >> (7 * (size - i - 1))) & 0x7f
+		if i < size - 1:
+			b |= 0x80
+		data += struct.pack('B', b)
+	return data
+
+
+def unpack255UShort(data):
+	""" Read one to three bytes from 255UInt16-encoded input string, and return a
+	tuple containing the decoded integer plus any leftover data.
+
+	>>> unpack255UShort(bytechr(252))[0]
+	252
+
+	Note that some numbers (e.g. 506) can have multiple encodings:
+	>>> unpack255UShort(struct.pack("BB", 254, 0))[0]
+	506
+	>>> unpack255UShort(struct.pack("BB", 255, 253))[0]
+	506
+	>>> unpack255UShort(struct.pack("BBB", 253, 1, 250))[0]
+	506
+	"""
+	code = byteord(data[:1])
+	data = data[1:]
+	if code == 253:
+		# read two more bytes as an unsigned short
+		if len(data) < 2:
+			raise TTLibError('not enough data to unpack 255UInt16')
+		result, = struct.unpack(">H", data[:2])
+		data = data[2:]
+	elif code == 254:
+		# read another byte, plus 253 * 2
+		if len(data) == 0:
+			raise TTLibError('not enough data to unpack 255UInt16')
+		result = byteord(data[:1])
+		result += 506
+		data = data[1:]
+	elif code == 255:
+		# read another byte, plus 253
+		if len(data) == 0:
+			raise TTLibError('not enough data to unpack 255UInt16')
+		result = byteord(data[:1])
+		result += 253
+		data = data[1:]
+	else:
+		# leave as is if lower than 253
+		result = code
+	# return result plus left over data
+	return result, data
+
+
+def pack255UShort(value):
+	r""" Encode unsigned integer in range 0 to 65535 (inclusive) to a bytestring
+	using 255UInt16 variable-length encoding.
+
+	>>> pack255UShort(252) == b'\xfc'
+	True
+	>>> pack255UShort(506) == b'\xfe\x00'
+	True
+	>>> pack255UShort(762) == b'\xfd\x02\xfa'
+	True
+	"""
+	if value < 0 or value > 0xFFFF:
+		raise TTLibError(
+			"255UInt16 format requires 0 <= integer <= 65535")
+	if value < 253:
+		return struct.pack(">B", value)
+	elif value < 506:
+		return struct.pack(">BB", 255, value - 253)
+	elif value < 762:
+		return struct.pack(">BB", 254, value - 506)
+	else:
+		return struct.pack(">BH", 253, value)
+
+
+if __name__ == "__main__":
+	import doctest
+	sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/ttx.py b/Lib/fontTools/ttx.py
index e0b5edd..0e3eaf3 100644
--- a/Lib/fontTools/ttx.py
+++ b/Lib/fontTools/ttx.py
@@ -1,21 +1,24 @@
 """\
 usage: ttx [options] inputfile1 [... inputfileN]
 
-    TTX %s -- From OpenType To XML And Back
+    TTX -- From OpenType To XML And Back
 
     If an input file is a TrueType or OpenType font file, it will be
-       dumped to an TTX file (an XML-based text format).
-    If an input file is a TTX file, it will be compiled to a TrueType
-       or OpenType font file.
+       decompiled to a TTX file (an XML-based text format).
+    If an input file is a TTX file, it will be compiled to whatever 
+       format the data is in, a TrueType or OpenType/CFF font file.
 
     Output files are created so they are unique: an existing file is
        never overwritten.
 
     General options:
-    -h Help: print this message
+    -h Help: print this message.
+    --version: show version and exit.
     -d <outputfolder> Specify a directory where the output files are
        to be created.
-    -o <outputfile> Specify a file to write the output to.
+    -o <outputfile> Specify a file to write the output to. A special
+       value of - would use the standard output.
+    -f Overwrite existing output file(s), ie. don't append numbers.
     -v Verbose: more messages will be written to stdout about what
        is being done.
     -q Quiet: No messages will be written to stdout about what
@@ -35,6 +38,10 @@
        to the individual table dumps. This file can be used as
        input to ttx, as long as the table files are in the
        same directory.
+    -g Split glyf table: Save the glyf data into separate TTX files
+       per glyph and write a small TTX for the glyf table which
+       contains references to the individual TTGlyph elements.
+       NOTE: specifying -g implies -s (no need for -s together with -g)
     -i Do NOT disassemble TT instructions: when this option is given,
        all TrueType programs (glyph programs, the font program and the
        pre-program) will be written to the TTX file as hex data
@@ -50,12 +57,17 @@
          -z bitwise
             * export each row as binary in an ASCII art style
          -z extfile
-            * export the data as external files with XML refences
+            * export the data as external files with XML references
        If no export format is specified 'raw' format is used.
     -e Don't ignore decompilation errors, but show a full traceback
        and abort.
-    -y <number> Select font number for TrueType Collection,
+    -y <number> Select font number for TrueType Collection (.ttc/.otc),
        starting from 0.
+    --unicodedata <UnicodeData.txt> Use custom database file to write
+       character names in the comments of the cmap TTX output.
+    --newline <value> Control how line endings are written in the XML
+       file. It can be 'LF', 'CR', or 'CRLF'. If not specified, the
+       default platform-specific line endings are used.
 
     Compile options:
     -m Merge with TrueType-input-file: specify a TrueType or OpenType
@@ -63,6 +75,13 @@
        valid when at most one TTX file is specified.
     -b Don't recalc glyph bounding boxes: use the values in the TTX
        file as-is.
+    --recalc-timestamp Set font 'modified' timestamp to current time.
+       By default, the modification time of the TTX file will be used.
+    --flavor <type> Specify flavor of output font file. May be 'woff'
+      or 'woff2'. Note that WOFF2 requires the Brotli Python extension,
+      available at https://github.com/google/brotli
+    --with-zopfli Use Zopfli instead of Zlib to compress WOFF. The Python
+      extension is available at https://pypi.python.org/pypi/zopfli
 """
 
 
@@ -70,48 +89,43 @@
 from fontTools.misc.py23 import *
 from fontTools.ttLib import TTFont, TTLibError
 from fontTools.misc.macCreatorType import getMacCreatorAndType
+from fontTools.unicode import setUnicodeData
+from fontTools.misc.timeTools import timestampSinceEpoch
+from fontTools.misc.loggingTools import Timer
+from fontTools.misc.cliTools import makeOutputFileName
 import os
 import sys
 import getopt
 import re
+import logging
 
-def usage():
-	from fontTools import version
-	print(__doc__ % version)
-	sys.exit(2)
 
-	
-numberAddedRE = re.compile("#\d+$")
+log = logging.getLogger("fontTools.ttx")
+
 opentypeheaderRE = re.compile('''sfntVersion=['"]OTTO["']''')
 
-def makeOutputFileName(input, outputDir, extension):
-	dirName, fileName = os.path.split(input)
-	fileName, ext = os.path.splitext(fileName)
-	if outputDir:
-		dirName = outputDir
-	fileName = numberAddedRE.split(fileName)[0]
-	output = os.path.join(dirName, fileName + extension)
-	n = 1
-	while os.path.exists(output):
-		output = os.path.join(dirName, fileName + "#" + repr(n) + extension)
-		n = n + 1
-	return output
-
 
 class Options(object):
 
 	listTables = False
 	outputDir = None
 	outputFile = None
+	overWrite = False
 	verbose = False
 	quiet = False
 	splitTables = False
+	splitGlyphs = False
 	disassembleInstructions = True
 	mergeFile = None
 	recalcBBoxes = True
 	allowVID = False
 	ignoreDecompileErrors = True
 	bitmapGlyphDataFormat = 'raw'
+	unicodedata = None
+	newlinestr = None
+	recalcTimestamp = False
+	flavor = None
+	useZopfli = False
 
 	def __init__(self, rawOptions, numFiles):
 		self.onlyTables = []
@@ -120,16 +134,20 @@
 		for option, value in rawOptions:
 			# general options
 			if option == "-h":
+				print(__doc__)
+				sys.exit(0)
+			elif option == "--version":
 				from fontTools import version
-				print(__doc__ % version)
+				print(version)
 				sys.exit(0)
 			elif option == "-d":
 				if not os.path.isdir(value):
-					print("The -d option value must be an existing directory")
-					sys.exit(2)
+					raise getopt.GetoptError("The -d option value must be an existing directory")
 				self.outputDir = value
 			elif option == "-o":
 				self.outputFile = value
+			elif option == "-f":
+				self.overWrite = True
 			elif option == "-v":
 				self.verbose = True
 			elif option == "-q":
@@ -138,18 +156,26 @@
 			elif option == "-l":
 				self.listTables = True
 			elif option == "-t":
+				# pad with space if table tag length is less than 4
+				value = value.ljust(4)
 				self.onlyTables.append(value)
 			elif option == "-x":
+				# pad with space if table tag length is less than 4
+				value = value.ljust(4)
 				self.skipTables.append(value)
 			elif option == "-s":
 				self.splitTables = True
+			elif option == "-g":
+				# -g implies (and forces) splitTables
+				self.splitGlyphs = True
+				self.splitTables = True
 			elif option == "-i":
 				self.disassembleInstructions = False
 			elif option == "-z":
 				validOptions = ('raw', 'row', 'bitwise', 'extfile')
 				if value not in validOptions:
-					print("-z does not allow %s as a format. Use %s" % (option, validOptions))
-					sys.exit(2)
+					raise getopt.GetoptError(
+						"-z does not allow %s as a format. Use %s" % (option, validOptions))
 				self.bitmapGlyphDataFormat = value
 			elif option == "-y":
 				self.fontNumber = int(value)
@@ -162,12 +188,42 @@
 				self.allowVID = True
 			elif option == "-e":
 				self.ignoreDecompileErrors = False
+			elif option == "--unicodedata":
+				self.unicodedata = value
+			elif option == "--newline":
+				validOptions = ('LF', 'CR', 'CRLF')
+				if value == "LF":
+					self.newlinestr = "\n"
+				elif value == "CR":
+					self.newlinestr = "\r"
+				elif value == "CRLF":
+					self.newlinestr = "\r\n"
+				else:
+					raise getopt.GetoptError(
+						"Invalid choice for --newline: %r (choose from %s)"
+						% (value, ", ".join(map(repr, validOptions))))
+			elif option == "--recalc-timestamp":
+				self.recalcTimestamp = True
+			elif option == "--flavor":
+				self.flavor = value
+			elif option == "--with-zopfli":
+				self.useZopfli = True
+		if self.verbose and self.quiet:
+			raise getopt.GetoptError("-q and -v options are mutually exclusive")
+		if self.verbose:
+			self.logLevel = logging.DEBUG
+		elif self.quiet:
+			self.logLevel = logging.WARNING
+		else:
+			self.logLevel = logging.INFO
+		if self.mergeFile and self.flavor:
+			raise getopt.GetoptError("-m and --flavor options are mutually exclusive")
 		if self.onlyTables and self.skipTables:
-			print("-t and -x options are mutually exclusive")
-			sys.exit(2)
+			raise getopt.GetoptError("-t and -x options are mutually exclusive")
 		if self.mergeFile and numFiles > 1:
-			print("Must specify exactly one TTX source file when using -m")
-			sys.exit(2)
+			raise getopt.GetoptError("Must specify exactly one TTX source file when using -m")
+		if self.flavor != 'woff' and self.useZopfli:
+			raise getopt.GetoptError("--with-zopfli option requires --flavor 'woff'")
 
 
 def ttList(input, output, options):
@@ -175,12 +231,18 @@
 	reader = ttf.reader
 	tags = sorted(reader.keys())
 	print('Listing table info for "%s":' % input)
-	format = "    %4s  %10s  %7s  %7s"
-	print(format % ("tag ", "  checksum", " length", " offset"))
-	print(format % ("----", "----------", "-------", "-------"))
+	format = "    %4s  %10s  %8s  %8s"
+	print(format % ("tag ", "  checksum", "  length", "  offset"))
+	print(format % ("----", "----------", "--------", "--------"))
 	for tag in tags:
 		entry = reader.tables[tag]
-		checkSum = int(entry.checkSum)
+		if ttf.flavor == "woff2":
+			# WOFF2 doesn't store table checksums, so they must be calculated
+			from fontTools.ttLib.sfnt import calcChecksum
+			data = entry.loadData(reader.transformBuffer)
+			checkSum = calcChecksum(data)
+		else:
+			checkSum = int(entry.checkSum)
 		if checkSum < 0:
 			checkSum = checkSum + 0x100000000
 		checksum = "0x%08X" % checkSum
@@ -189,35 +251,43 @@
 	ttf.close()
 
 
+@Timer(log, 'Done dumping TTX in %(time).3f seconds')
 def ttDump(input, output, options):
-	if not options.quiet:
-		print('Dumping "%s" to "%s"...' % (input, output))
-	ttf = TTFont(input, 0, verbose=options.verbose, allowVID=options.allowVID,
-			quiet=options.quiet,
+	log.info('Dumping "%s" to "%s"...', input, output)
+	if options.unicodedata:
+		setUnicodeData(options.unicodedata)
+	ttf = TTFont(input, 0, allowVID=options.allowVID,
 			ignoreDecompileErrors=options.ignoreDecompileErrors,
 			fontNumber=options.fontNumber)
 	ttf.saveXML(output,
-			quiet=options.quiet,
 			tables=options.onlyTables,
 			skipTables=options.skipTables,
 			splitTables=options.splitTables,
+			splitGlyphs=options.splitGlyphs,
 			disassembleInstructions=options.disassembleInstructions,
-			bitmapGlyphDataFormat=options.bitmapGlyphDataFormat)
+			bitmapGlyphDataFormat=options.bitmapGlyphDataFormat,
+			newlinestr=options.newlinestr)
 	ttf.close()
 
 
+@Timer(log, 'Done compiling TTX in %(time).3f seconds')
 def ttCompile(input, output, options):
-	if not options.quiet:
-		print('Compiling "%s" to "%s"...' % (input, output))
-	ttf = TTFont(options.mergeFile,
+	log.info('Compiling "%s" to "%s"...' % (input, output))
+	if options.useZopfli:
+		from fontTools.ttLib import sfnt
+		sfnt.USE_ZOPFLI = True
+	ttf = TTFont(options.mergeFile, flavor=options.flavor,
 			recalcBBoxes=options.recalcBBoxes,
-			verbose=options.verbose, allowVID=options.allowVID)
-	ttf.importXML(input, quiet=options.quiet)
-	ttf.save(output)
+			recalcTimestamp=options.recalcTimestamp,
+			allowVID=options.allowVID)
+	ttf.importXML(input)
 
-	if options.verbose:
-		import time
-		print("finished at", time.strftime("%H:%M:%S", time.localtime(time.time())))
+	if not options.recalcTimestamp and 'head' in ttf:
+		# use TTX file modification time for head "modified" timestamp
+		mtime = os.path.getmtime(input)
+		ttf['head'].modified = timestampSinceEpoch(mtime)
+
+	ttf.save(output)
 
 
 def guessFileType(fileName):
@@ -226,12 +296,15 @@
 		f = open(fileName, "rb")
 	except IOError:
 		return None
+	header = f.read(256)
+	f.close()
+	if header.startswith(b'\xef\xbb\xbf<?xml'):
+		header = header.lstrip(b'\xef\xbb\xbf')
 	cr, tp = getMacCreatorAndType(fileName)
 	if tp in ("sfnt", "FFIL"):
 		return "TTF"
 	if ext == ".dfont":
 		return "TTF"
-	header = f.read(256)
 	head = Tag(header[:4])
 	if head == "OTTO":
 		return "OTF"
@@ -241,7 +314,9 @@
 		return "TTF"
 	elif head == "wOFF":
 		return "WOFF"
-	elif head.lower() == "<?xm":
+	elif head == "wOF2":
+		return "WOFF2"
+	elif head == "<?xm":
 		# Use 'latin1' because that can't fail.
 		header = tostr(header, 'latin1')
 		if opentypeheaderRE.search(header):
@@ -252,39 +327,42 @@
 
 
 def parseOptions(args):
-	try:
-		rawOptions, files = getopt.getopt(args, "ld:o:vqht:x:sim:z:baey:")
-	except getopt.GetoptError:
-		usage()
-	
-	if not files:
-		usage()
-	
+	rawOptions, files = getopt.getopt(args, "ld:o:fvqht:x:sgim:z:baey:",
+			['unicodedata=', "recalc-timestamp", 'flavor=', 'version',
+			 'with-zopfli', 'newline='])
+
 	options = Options(rawOptions, len(files))
 	jobs = []
-	
+
+	if not files:
+		raise getopt.GetoptError('Must specify at least one input file')
+
 	for input in files:
+		if not os.path.isfile(input):
+			raise getopt.GetoptError('File not found: "%s"' % input)
 		tp = guessFileType(input)
-		if tp in ("OTF", "TTF", "TTC", "WOFF"):
+		if tp in ("OTF", "TTF", "TTC", "WOFF", "WOFF2"):
 			extension = ".ttx"
 			if options.listTables:
 				action = ttList
 			else:
 				action = ttDump
 		elif tp == "TTX":
-			extension = ".ttf"
+			extension = "."+options.flavor if options.flavor else ".ttf"
 			action = ttCompile
 		elif tp == "OTX":
-			extension = ".otf"
+			extension = "."+options.flavor if options.flavor else ".otf"
 			action = ttCompile
 		else:
-			print('Unknown file type: "%s"' % input)
-			continue
-		
+			raise getopt.GetoptError('Unknown file type: "%s"' % input)
+
 		if options.outputFile:
 			output = options.outputFile
 		else:
-			output = makeOutputFileName(input, options.outputDir, extension)
+			output = makeOutputFileName(input, options.outputDir, extension, options.overWrite)
+			# 'touch' output file to avoid race condition in choosing file names
+			if action != ttList:
+				open(output, 'a').close()
 		jobs.append((action, input, output))
 	return jobs, options
 
@@ -298,32 +376,42 @@
 	"""Force the DOS Prompt window to stay open so the user gets
 	a chance to see what's wrong."""
 	import msvcrt
-	print('(Hit any key to exit)')
+	print('(Hit any key to exit)', file=sys.stderr)
 	while not msvcrt.kbhit():
 		pass
 
 
-def main(args):
-	jobs, options = parseOptions(args)
+def main(args=None):
+	from fontTools import configLogger
+
+	if args is None:
+		args = sys.argv[1:]
+	try:
+		jobs, options = parseOptions(args)
+	except getopt.GetoptError as e:
+		print("%s\nERROR: %s" % (__doc__, e), file=sys.stderr)
+		sys.exit(2)
+
+	configLogger(level=options.logLevel)
+
 	try:
 		process(jobs, options)
 	except KeyboardInterrupt:
-		print("(Cancelled.)")
+		log.error("(Cancelled.)")
+		sys.exit(1)
 	except SystemExit:
 		if sys.platform == "win32":
 			waitForKeyPress()
-		else:
-			raise
+		raise
 	except TTLibError as e:
-		print("Error:",e)
+		log.error(e)
+		sys.exit(1)
 	except:
+		log.exception('Unhandled exception has occurred')
 		if sys.platform == "win32":
-			import traceback
-			traceback.print_exc()
 			waitForKeyPress()
-		else:
-			raise
-	
+		sys.exit(1)
+
 
 if __name__ == "__main__":
-	main(sys.argv[1:])
+	sys.exit(main())
diff --git a/Lib/fontTools/unicode.py b/Lib/fontTools/unicode.py
index b599051..50dfc53 100644
--- a/Lib/fontTools/unicode.py
+++ b/Lib/fontTools/unicode.py
@@ -30,7 +30,12 @@
 class _UnicodeBuiltin(object):
 
 	def __getitem__(self, charCode):
-		import unicodedata
+		try:
+			# use unicodedata backport to python2, if available:
+			# https://github.com/mikekap/unicodedata2
+			import unicodedata2 as unicodedata
+		except ImportError: 
+			import unicodedata
 		try:
 			return unicodedata.name(unichr(charCode))
 		except ValueError:
diff --git a/Lib/fontTools/unicodedata/Blocks.py b/Lib/fontTools/unicodedata/Blocks.py
new file mode 100644
index 0000000..cba4e9e
--- /dev/null
+++ b/Lib/fontTools/unicodedata/Blocks.py
@@ -0,0 +1,677 @@
+# -*- coding: utf-8 -*-
+#
+# NOTE: This file was auto-generated with MetaTools/buildUCD.py.
+# Source: https://unicode.org/Public/UNIDATA/Blocks.txt
+# License: http://unicode.org/copyright.html#License
+#
+# Blocks-10.0.0.txt
+# Date: 2017-04-12, 17:30:00 GMT [KW]
+# © 2017 Unicode®, Inc.
+# For terms of use, see http://www.unicode.org/terms_of_use.html
+#
+# Unicode Character Database
+# For documentation, see http://www.unicode.org/reports/tr44/
+#
+# Format:
+# Start Code..End Code; Block Name
+
+
+RANGES = [
+    0x0000,  # .. 0x007F ; Basic Latin
+    0x0080,  # .. 0x00FF ; Latin-1 Supplement
+    0x0100,  # .. 0x017F ; Latin Extended-A
+    0x0180,  # .. 0x024F ; Latin Extended-B
+    0x0250,  # .. 0x02AF ; IPA Extensions
+    0x02B0,  # .. 0x02FF ; Spacing Modifier Letters
+    0x0300,  # .. 0x036F ; Combining Diacritical Marks
+    0x0370,  # .. 0x03FF ; Greek and Coptic
+    0x0400,  # .. 0x04FF ; Cyrillic
+    0x0500,  # .. 0x052F ; Cyrillic Supplement
+    0x0530,  # .. 0x058F ; Armenian
+    0x0590,  # .. 0x05FF ; Hebrew
+    0x0600,  # .. 0x06FF ; Arabic
+    0x0700,  # .. 0x074F ; Syriac
+    0x0750,  # .. 0x077F ; Arabic Supplement
+    0x0780,  # .. 0x07BF ; Thaana
+    0x07C0,  # .. 0x07FF ; NKo
+    0x0800,  # .. 0x083F ; Samaritan
+    0x0840,  # .. 0x085F ; Mandaic
+    0x0860,  # .. 0x086F ; Syriac Supplement
+    0x0870,  # .. 0x089F ; No_Block
+    0x08A0,  # .. 0x08FF ; Arabic Extended-A
+    0x0900,  # .. 0x097F ; Devanagari
+    0x0980,  # .. 0x09FF ; Bengali
+    0x0A00,  # .. 0x0A7F ; Gurmukhi
+    0x0A80,  # .. 0x0AFF ; Gujarati
+    0x0B00,  # .. 0x0B7F ; Oriya
+    0x0B80,  # .. 0x0BFF ; Tamil
+    0x0C00,  # .. 0x0C7F ; Telugu
+    0x0C80,  # .. 0x0CFF ; Kannada
+    0x0D00,  # .. 0x0D7F ; Malayalam
+    0x0D80,  # .. 0x0DFF ; Sinhala
+    0x0E00,  # .. 0x0E7F ; Thai
+    0x0E80,  # .. 0x0EFF ; Lao
+    0x0F00,  # .. 0x0FFF ; Tibetan
+    0x1000,  # .. 0x109F ; Myanmar
+    0x10A0,  # .. 0x10FF ; Georgian
+    0x1100,  # .. 0x11FF ; Hangul Jamo
+    0x1200,  # .. 0x137F ; Ethiopic
+    0x1380,  # .. 0x139F ; Ethiopic Supplement
+    0x13A0,  # .. 0x13FF ; Cherokee
+    0x1400,  # .. 0x167F ; Unified Canadian Aboriginal Syllabics
+    0x1680,  # .. 0x169F ; Ogham
+    0x16A0,  # .. 0x16FF ; Runic
+    0x1700,  # .. 0x171F ; Tagalog
+    0x1720,  # .. 0x173F ; Hanunoo
+    0x1740,  # .. 0x175F ; Buhid
+    0x1760,  # .. 0x177F ; Tagbanwa
+    0x1780,  # .. 0x17FF ; Khmer
+    0x1800,  # .. 0x18AF ; Mongolian
+    0x18B0,  # .. 0x18FF ; Unified Canadian Aboriginal Syllabics Extended
+    0x1900,  # .. 0x194F ; Limbu
+    0x1950,  # .. 0x197F ; Tai Le
+    0x1980,  # .. 0x19DF ; New Tai Lue
+    0x19E0,  # .. 0x19FF ; Khmer Symbols
+    0x1A00,  # .. 0x1A1F ; Buginese
+    0x1A20,  # .. 0x1AAF ; Tai Tham
+    0x1AB0,  # .. 0x1AFF ; Combining Diacritical Marks Extended
+    0x1B00,  # .. 0x1B7F ; Balinese
+    0x1B80,  # .. 0x1BBF ; Sundanese
+    0x1BC0,  # .. 0x1BFF ; Batak
+    0x1C00,  # .. 0x1C4F ; Lepcha
+    0x1C50,  # .. 0x1C7F ; Ol Chiki
+    0x1C80,  # .. 0x1C8F ; Cyrillic Extended-C
+    0x1C90,  # .. 0x1CBF ; No_Block
+    0x1CC0,  # .. 0x1CCF ; Sundanese Supplement
+    0x1CD0,  # .. 0x1CFF ; Vedic Extensions
+    0x1D00,  # .. 0x1D7F ; Phonetic Extensions
+    0x1D80,  # .. 0x1DBF ; Phonetic Extensions Supplement
+    0x1DC0,  # .. 0x1DFF ; Combining Diacritical Marks Supplement
+    0x1E00,  # .. 0x1EFF ; Latin Extended Additional
+    0x1F00,  # .. 0x1FFF ; Greek Extended
+    0x2000,  # .. 0x206F ; General Punctuation
+    0x2070,  # .. 0x209F ; Superscripts and Subscripts
+    0x20A0,  # .. 0x20CF ; Currency Symbols
+    0x20D0,  # .. 0x20FF ; Combining Diacritical Marks for Symbols
+    0x2100,  # .. 0x214F ; Letterlike Symbols
+    0x2150,  # .. 0x218F ; Number Forms
+    0x2190,  # .. 0x21FF ; Arrows
+    0x2200,  # .. 0x22FF ; Mathematical Operators
+    0x2300,  # .. 0x23FF ; Miscellaneous Technical
+    0x2400,  # .. 0x243F ; Control Pictures
+    0x2440,  # .. 0x245F ; Optical Character Recognition
+    0x2460,  # .. 0x24FF ; Enclosed Alphanumerics
+    0x2500,  # .. 0x257F ; Box Drawing
+    0x2580,  # .. 0x259F ; Block Elements
+    0x25A0,  # .. 0x25FF ; Geometric Shapes
+    0x2600,  # .. 0x26FF ; Miscellaneous Symbols
+    0x2700,  # .. 0x27BF ; Dingbats
+    0x27C0,  # .. 0x27EF ; Miscellaneous Mathematical Symbols-A
+    0x27F0,  # .. 0x27FF ; Supplemental Arrows-A
+    0x2800,  # .. 0x28FF ; Braille Patterns
+    0x2900,  # .. 0x297F ; Supplemental Arrows-B
+    0x2980,  # .. 0x29FF ; Miscellaneous Mathematical Symbols-B
+    0x2A00,  # .. 0x2AFF ; Supplemental Mathematical Operators
+    0x2B00,  # .. 0x2BFF ; Miscellaneous Symbols and Arrows
+    0x2C00,  # .. 0x2C5F ; Glagolitic
+    0x2C60,  # .. 0x2C7F ; Latin Extended-C
+    0x2C80,  # .. 0x2CFF ; Coptic
+    0x2D00,  # .. 0x2D2F ; Georgian Supplement
+    0x2D30,  # .. 0x2D7F ; Tifinagh
+    0x2D80,  # .. 0x2DDF ; Ethiopic Extended
+    0x2DE0,  # .. 0x2DFF ; Cyrillic Extended-A
+    0x2E00,  # .. 0x2E7F ; Supplemental Punctuation
+    0x2E80,  # .. 0x2EFF ; CJK Radicals Supplement
+    0x2F00,  # .. 0x2FDF ; Kangxi Radicals
+    0x2FE0,  # .. 0x2FEF ; No_Block
+    0x2FF0,  # .. 0x2FFF ; Ideographic Description Characters
+    0x3000,  # .. 0x303F ; CJK Symbols and Punctuation
+    0x3040,  # .. 0x309F ; Hiragana
+    0x30A0,  # .. 0x30FF ; Katakana
+    0x3100,  # .. 0x312F ; Bopomofo
+    0x3130,  # .. 0x318F ; Hangul Compatibility Jamo
+    0x3190,  # .. 0x319F ; Kanbun
+    0x31A0,  # .. 0x31BF ; Bopomofo Extended
+    0x31C0,  # .. 0x31EF ; CJK Strokes
+    0x31F0,  # .. 0x31FF ; Katakana Phonetic Extensions
+    0x3200,  # .. 0x32FF ; Enclosed CJK Letters and Months
+    0x3300,  # .. 0x33FF ; CJK Compatibility
+    0x3400,  # .. 0x4DBF ; CJK Unified Ideographs Extension A
+    0x4DC0,  # .. 0x4DFF ; Yijing Hexagram Symbols
+    0x4E00,  # .. 0x9FFF ; CJK Unified Ideographs
+    0xA000,  # .. 0xA48F ; Yi Syllables
+    0xA490,  # .. 0xA4CF ; Yi Radicals
+    0xA4D0,  # .. 0xA4FF ; Lisu
+    0xA500,  # .. 0xA63F ; Vai
+    0xA640,  # .. 0xA69F ; Cyrillic Extended-B
+    0xA6A0,  # .. 0xA6FF ; Bamum
+    0xA700,  # .. 0xA71F ; Modifier Tone Letters
+    0xA720,  # .. 0xA7FF ; Latin Extended-D
+    0xA800,  # .. 0xA82F ; Syloti Nagri
+    0xA830,  # .. 0xA83F ; Common Indic Number Forms
+    0xA840,  # .. 0xA87F ; Phags-pa
+    0xA880,  # .. 0xA8DF ; Saurashtra
+    0xA8E0,  # .. 0xA8FF ; Devanagari Extended
+    0xA900,  # .. 0xA92F ; Kayah Li
+    0xA930,  # .. 0xA95F ; Rejang
+    0xA960,  # .. 0xA97F ; Hangul Jamo Extended-A
+    0xA980,  # .. 0xA9DF ; Javanese
+    0xA9E0,  # .. 0xA9FF ; Myanmar Extended-B
+    0xAA00,  # .. 0xAA5F ; Cham
+    0xAA60,  # .. 0xAA7F ; Myanmar Extended-A
+    0xAA80,  # .. 0xAADF ; Tai Viet
+    0xAAE0,  # .. 0xAAFF ; Meetei Mayek Extensions
+    0xAB00,  # .. 0xAB2F ; Ethiopic Extended-A
+    0xAB30,  # .. 0xAB6F ; Latin Extended-E
+    0xAB70,  # .. 0xABBF ; Cherokee Supplement
+    0xABC0,  # .. 0xABFF ; Meetei Mayek
+    0xAC00,  # .. 0xD7AF ; Hangul Syllables
+    0xD7B0,  # .. 0xD7FF ; Hangul Jamo Extended-B
+    0xD800,  # .. 0xDB7F ; High Surrogates
+    0xDB80,  # .. 0xDBFF ; High Private Use Surrogates
+    0xDC00,  # .. 0xDFFF ; Low Surrogates
+    0xE000,  # .. 0xF8FF ; Private Use Area
+    0xF900,  # .. 0xFAFF ; CJK Compatibility Ideographs
+    0xFB00,  # .. 0xFB4F ; Alphabetic Presentation Forms
+    0xFB50,  # .. 0xFDFF ; Arabic Presentation Forms-A
+    0xFE00,  # .. 0xFE0F ; Variation Selectors
+    0xFE10,  # .. 0xFE1F ; Vertical Forms
+    0xFE20,  # .. 0xFE2F ; Combining Half Marks
+    0xFE30,  # .. 0xFE4F ; CJK Compatibility Forms
+    0xFE50,  # .. 0xFE6F ; Small Form Variants
+    0xFE70,  # .. 0xFEFF ; Arabic Presentation Forms-B
+    0xFF00,  # .. 0xFFEF ; Halfwidth and Fullwidth Forms
+    0xFFF0,  # .. 0xFFFF ; Specials
+    0x10000,  # .. 0x1007F ; Linear B Syllabary
+    0x10080,  # .. 0x100FF ; Linear B Ideograms
+    0x10100,  # .. 0x1013F ; Aegean Numbers
+    0x10140,  # .. 0x1018F ; Ancient Greek Numbers
+    0x10190,  # .. 0x101CF ; Ancient Symbols
+    0x101D0,  # .. 0x101FF ; Phaistos Disc
+    0x10200,  # .. 0x1027F ; No_Block
+    0x10280,  # .. 0x1029F ; Lycian
+    0x102A0,  # .. 0x102DF ; Carian
+    0x102E0,  # .. 0x102FF ; Coptic Epact Numbers
+    0x10300,  # .. 0x1032F ; Old Italic
+    0x10330,  # .. 0x1034F ; Gothic
+    0x10350,  # .. 0x1037F ; Old Permic
+    0x10380,  # .. 0x1039F ; Ugaritic
+    0x103A0,  # .. 0x103DF ; Old Persian
+    0x103E0,  # .. 0x103FF ; No_Block
+    0x10400,  # .. 0x1044F ; Deseret
+    0x10450,  # .. 0x1047F ; Shavian
+    0x10480,  # .. 0x104AF ; Osmanya
+    0x104B0,  # .. 0x104FF ; Osage
+    0x10500,  # .. 0x1052F ; Elbasan
+    0x10530,  # .. 0x1056F ; Caucasian Albanian
+    0x10570,  # .. 0x105FF ; No_Block
+    0x10600,  # .. 0x1077F ; Linear A
+    0x10780,  # .. 0x107FF ; No_Block
+    0x10800,  # .. 0x1083F ; Cypriot Syllabary
+    0x10840,  # .. 0x1085F ; Imperial Aramaic
+    0x10860,  # .. 0x1087F ; Palmyrene
+    0x10880,  # .. 0x108AF ; Nabataean
+    0x108B0,  # .. 0x108DF ; No_Block
+    0x108E0,  # .. 0x108FF ; Hatran
+    0x10900,  # .. 0x1091F ; Phoenician
+    0x10920,  # .. 0x1093F ; Lydian
+    0x10940,  # .. 0x1097F ; No_Block
+    0x10980,  # .. 0x1099F ; Meroitic Hieroglyphs
+    0x109A0,  # .. 0x109FF ; Meroitic Cursive
+    0x10A00,  # .. 0x10A5F ; Kharoshthi
+    0x10A60,  # .. 0x10A7F ; Old South Arabian
+    0x10A80,  # .. 0x10A9F ; Old North Arabian
+    0x10AA0,  # .. 0x10ABF ; No_Block
+    0x10AC0,  # .. 0x10AFF ; Manichaean
+    0x10B00,  # .. 0x10B3F ; Avestan
+    0x10B40,  # .. 0x10B5F ; Inscriptional Parthian
+    0x10B60,  # .. 0x10B7F ; Inscriptional Pahlavi
+    0x10B80,  # .. 0x10BAF ; Psalter Pahlavi
+    0x10BB0,  # .. 0x10BFF ; No_Block
+    0x10C00,  # .. 0x10C4F ; Old Turkic
+    0x10C50,  # .. 0x10C7F ; No_Block
+    0x10C80,  # .. 0x10CFF ; Old Hungarian
+    0x10D00,  # .. 0x10E5F ; No_Block
+    0x10E60,  # .. 0x10E7F ; Rumi Numeral Symbols
+    0x10E80,  # .. 0x10FFF ; No_Block
+    0x11000,  # .. 0x1107F ; Brahmi
+    0x11080,  # .. 0x110CF ; Kaithi
+    0x110D0,  # .. 0x110FF ; Sora Sompeng
+    0x11100,  # .. 0x1114F ; Chakma
+    0x11150,  # .. 0x1117F ; Mahajani
+    0x11180,  # .. 0x111DF ; Sharada
+    0x111E0,  # .. 0x111FF ; Sinhala Archaic Numbers
+    0x11200,  # .. 0x1124F ; Khojki
+    0x11250,  # .. 0x1127F ; No_Block
+    0x11280,  # .. 0x112AF ; Multani
+    0x112B0,  # .. 0x112FF ; Khudawadi
+    0x11300,  # .. 0x1137F ; Grantha
+    0x11380,  # .. 0x113FF ; No_Block
+    0x11400,  # .. 0x1147F ; Newa
+    0x11480,  # .. 0x114DF ; Tirhuta
+    0x114E0,  # .. 0x1157F ; No_Block
+    0x11580,  # .. 0x115FF ; Siddham
+    0x11600,  # .. 0x1165F ; Modi
+    0x11660,  # .. 0x1167F ; Mongolian Supplement
+    0x11680,  # .. 0x116CF ; Takri
+    0x116D0,  # .. 0x116FF ; No_Block
+    0x11700,  # .. 0x1173F ; Ahom
+    0x11740,  # .. 0x1189F ; No_Block
+    0x118A0,  # .. 0x118FF ; Warang Citi
+    0x11900,  # .. 0x119FF ; No_Block
+    0x11A00,  # .. 0x11A4F ; Zanabazar Square
+    0x11A50,  # .. 0x11AAF ; Soyombo
+    0x11AB0,  # .. 0x11ABF ; No_Block
+    0x11AC0,  # .. 0x11AFF ; Pau Cin Hau
+    0x11B00,  # .. 0x11BFF ; No_Block
+    0x11C00,  # .. 0x11C6F ; Bhaiksuki
+    0x11C70,  # .. 0x11CBF ; Marchen
+    0x11CC0,  # .. 0x11CFF ; No_Block
+    0x11D00,  # .. 0x11D5F ; Masaram Gondi
+    0x11D60,  # .. 0x11FFF ; No_Block
+    0x12000,  # .. 0x123FF ; Cuneiform
+    0x12400,  # .. 0x1247F ; Cuneiform Numbers and Punctuation
+    0x12480,  # .. 0x1254F ; Early Dynastic Cuneiform
+    0x12550,  # .. 0x12FFF ; No_Block
+    0x13000,  # .. 0x1342F ; Egyptian Hieroglyphs
+    0x13430,  # .. 0x143FF ; No_Block
+    0x14400,  # .. 0x1467F ; Anatolian Hieroglyphs
+    0x14680,  # .. 0x167FF ; No_Block
+    0x16800,  # .. 0x16A3F ; Bamum Supplement
+    0x16A40,  # .. 0x16A6F ; Mro
+    0x16A70,  # .. 0x16ACF ; No_Block
+    0x16AD0,  # .. 0x16AFF ; Bassa Vah
+    0x16B00,  # .. 0x16B8F ; Pahawh Hmong
+    0x16B90,  # .. 0x16EFF ; No_Block
+    0x16F00,  # .. 0x16F9F ; Miao
+    0x16FA0,  # .. 0x16FDF ; No_Block
+    0x16FE0,  # .. 0x16FFF ; Ideographic Symbols and Punctuation
+    0x17000,  # .. 0x187FF ; Tangut
+    0x18800,  # .. 0x18AFF ; Tangut Components
+    0x18B00,  # .. 0x1AFFF ; No_Block
+    0x1B000,  # .. 0x1B0FF ; Kana Supplement
+    0x1B100,  # .. 0x1B12F ; Kana Extended-A
+    0x1B130,  # .. 0x1B16F ; No_Block
+    0x1B170,  # .. 0x1B2FF ; Nushu
+    0x1B300,  # .. 0x1BBFF ; No_Block
+    0x1BC00,  # .. 0x1BC9F ; Duployan
+    0x1BCA0,  # .. 0x1BCAF ; Shorthand Format Controls
+    0x1BCB0,  # .. 0x1CFFF ; No_Block
+    0x1D000,  # .. 0x1D0FF ; Byzantine Musical Symbols
+    0x1D100,  # .. 0x1D1FF ; Musical Symbols
+    0x1D200,  # .. 0x1D24F ; Ancient Greek Musical Notation
+    0x1D250,  # .. 0x1D2FF ; No_Block
+    0x1D300,  # .. 0x1D35F ; Tai Xuan Jing Symbols
+    0x1D360,  # .. 0x1D37F ; Counting Rod Numerals
+    0x1D380,  # .. 0x1D3FF ; No_Block
+    0x1D400,  # .. 0x1D7FF ; Mathematical Alphanumeric Symbols
+    0x1D800,  # .. 0x1DAAF ; Sutton SignWriting
+    0x1DAB0,  # .. 0x1DFFF ; No_Block
+    0x1E000,  # .. 0x1E02F ; Glagolitic Supplement
+    0x1E030,  # .. 0x1E7FF ; No_Block
+    0x1E800,  # .. 0x1E8DF ; Mende Kikakui
+    0x1E8E0,  # .. 0x1E8FF ; No_Block
+    0x1E900,  # .. 0x1E95F ; Adlam
+    0x1E960,  # .. 0x1EDFF ; No_Block
+    0x1EE00,  # .. 0x1EEFF ; Arabic Mathematical Alphabetic Symbols
+    0x1EF00,  # .. 0x1EFFF ; No_Block
+    0x1F000,  # .. 0x1F02F ; Mahjong Tiles
+    0x1F030,  # .. 0x1F09F ; Domino Tiles
+    0x1F0A0,  # .. 0x1F0FF ; Playing Cards
+    0x1F100,  # .. 0x1F1FF ; Enclosed Alphanumeric Supplement
+    0x1F200,  # .. 0x1F2FF ; Enclosed Ideographic Supplement
+    0x1F300,  # .. 0x1F5FF ; Miscellaneous Symbols and Pictographs
+    0x1F600,  # .. 0x1F64F ; Emoticons
+    0x1F650,  # .. 0x1F67F ; Ornamental Dingbats
+    0x1F680,  # .. 0x1F6FF ; Transport and Map Symbols
+    0x1F700,  # .. 0x1F77F ; Alchemical Symbols
+    0x1F780,  # .. 0x1F7FF ; Geometric Shapes Extended
+    0x1F800,  # .. 0x1F8FF ; Supplemental Arrows-C
+    0x1F900,  # .. 0x1F9FF ; Supplemental Symbols and Pictographs
+    0x1FA00,  # .. 0x1FFFF ; No_Block
+    0x20000,  # .. 0x2A6DF ; CJK Unified Ideographs Extension B
+    0x2A6E0,  # .. 0x2A6FF ; No_Block
+    0x2A700,  # .. 0x2B73F ; CJK Unified Ideographs Extension C
+    0x2B740,  # .. 0x2B81F ; CJK Unified Ideographs Extension D
+    0x2B820,  # .. 0x2CEAF ; CJK Unified Ideographs Extension E
+    0x2CEB0,  # .. 0x2EBEF ; CJK Unified Ideographs Extension F
+    0x2EBF0,  # .. 0x2F7FF ; No_Block
+    0x2F800,  # .. 0x2FA1F ; CJK Compatibility Ideographs Supplement
+    0x2FA20,  # .. 0xDFFFF ; No_Block
+    0xE0000,  # .. 0xE007F ; Tags
+    0xE0080,  # .. 0xE00FF ; No_Block
+    0xE0100,  # .. 0xE01EF ; Variation Selectors Supplement
+    0xE01F0,  # .. 0xEFFFF ; No_Block
+    0xF0000,  # .. 0xFFFFF ; Supplementary Private Use Area-A
+    0x100000,  # .. 0x10FFFF ; Supplementary Private Use Area-B
+]
+
+VALUES = [
+    'Basic Latin',                                     # 0000..007F
+    'Latin-1 Supplement',                              # 0080..00FF
+    'Latin Extended-A',                                # 0100..017F
+    'Latin Extended-B',                                # 0180..024F
+    'IPA Extensions',                                  # 0250..02AF
+    'Spacing Modifier Letters',                        # 02B0..02FF
+    'Combining Diacritical Marks',                     # 0300..036F
+    'Greek and Coptic',                                # 0370..03FF
+    'Cyrillic',                                        # 0400..04FF
+    'Cyrillic Supplement',                             # 0500..052F
+    'Armenian',                                        # 0530..058F
+    'Hebrew',                                          # 0590..05FF
+    'Arabic',                                          # 0600..06FF
+    'Syriac',                                          # 0700..074F
+    'Arabic Supplement',                               # 0750..077F
+    'Thaana',                                          # 0780..07BF
+    'NKo',                                             # 07C0..07FF
+    'Samaritan',                                       # 0800..083F
+    'Mandaic',                                         # 0840..085F
+    'Syriac Supplement',                               # 0860..086F
+    'No_Block',                                        # 0870..089F
+    'Arabic Extended-A',                               # 08A0..08FF
+    'Devanagari',                                      # 0900..097F
+    'Bengali',                                         # 0980..09FF
+    'Gurmukhi',                                        # 0A00..0A7F
+    'Gujarati',                                        # 0A80..0AFF
+    'Oriya',                                           # 0B00..0B7F
+    'Tamil',                                           # 0B80..0BFF
+    'Telugu',                                          # 0C00..0C7F
+    'Kannada',                                         # 0C80..0CFF
+    'Malayalam',                                       # 0D00..0D7F
+    'Sinhala',                                         # 0D80..0DFF
+    'Thai',                                            # 0E00..0E7F
+    'Lao',                                             # 0E80..0EFF
+    'Tibetan',                                         # 0F00..0FFF
+    'Myanmar',                                         # 1000..109F
+    'Georgian',                                        # 10A0..10FF
+    'Hangul Jamo',                                     # 1100..11FF
+    'Ethiopic',                                        # 1200..137F
+    'Ethiopic Supplement',                             # 1380..139F
+    'Cherokee',                                        # 13A0..13FF
+    'Unified Canadian Aboriginal Syllabics',           # 1400..167F
+    'Ogham',                                           # 1680..169F
+    'Runic',                                           # 16A0..16FF
+    'Tagalog',                                         # 1700..171F
+    'Hanunoo',                                         # 1720..173F
+    'Buhid',                                           # 1740..175F
+    'Tagbanwa',                                        # 1760..177F
+    'Khmer',                                           # 1780..17FF
+    'Mongolian',                                       # 1800..18AF
+    'Unified Canadian Aboriginal Syllabics Extended',  # 18B0..18FF
+    'Limbu',                                           # 1900..194F
+    'Tai Le',                                          # 1950..197F
+    'New Tai Lue',                                     # 1980..19DF
+    'Khmer Symbols',                                   # 19E0..19FF
+    'Buginese',                                        # 1A00..1A1F
+    'Tai Tham',                                        # 1A20..1AAF
+    'Combining Diacritical Marks Extended',            # 1AB0..1AFF
+    'Balinese',                                        # 1B00..1B7F
+    'Sundanese',                                       # 1B80..1BBF
+    'Batak',                                           # 1BC0..1BFF
+    'Lepcha',                                          # 1C00..1C4F
+    'Ol Chiki',                                        # 1C50..1C7F
+    'Cyrillic Extended-C',                             # 1C80..1C8F
+    'No_Block',                                        # 1C90..1CBF
+    'Sundanese Supplement',                            # 1CC0..1CCF
+    'Vedic Extensions',                                # 1CD0..1CFF
+    'Phonetic Extensions',                             # 1D00..1D7F
+    'Phonetic Extensions Supplement',                  # 1D80..1DBF
+    'Combining Diacritical Marks Supplement',          # 1DC0..1DFF
+    'Latin Extended Additional',                       # 1E00..1EFF
+    'Greek Extended',                                  # 1F00..1FFF
+    'General Punctuation',                             # 2000..206F
+    'Superscripts and Subscripts',                     # 2070..209F
+    'Currency Symbols',                                # 20A0..20CF
+    'Combining Diacritical Marks for Symbols',         # 20D0..20FF
+    'Letterlike Symbols',                              # 2100..214F
+    'Number Forms',                                    # 2150..218F
+    'Arrows',                                          # 2190..21FF
+    'Mathematical Operators',                          # 2200..22FF
+    'Miscellaneous Technical',                         # 2300..23FF
+    'Control Pictures',                                # 2400..243F
+    'Optical Character Recognition',                   # 2440..245F
+    'Enclosed Alphanumerics',                          # 2460..24FF
+    'Box Drawing',                                     # 2500..257F
+    'Block Elements',                                  # 2580..259F
+    'Geometric Shapes',                                # 25A0..25FF
+    'Miscellaneous Symbols',                           # 2600..26FF
+    'Dingbats',                                        # 2700..27BF
+    'Miscellaneous Mathematical Symbols-A',            # 27C0..27EF
+    'Supplemental Arrows-A',                           # 27F0..27FF
+    'Braille Patterns',                                # 2800..28FF
+    'Supplemental Arrows-B',                           # 2900..297F
+    'Miscellaneous Mathematical Symbols-B',            # 2980..29FF
+    'Supplemental Mathematical Operators',             # 2A00..2AFF
+    'Miscellaneous Symbols and Arrows',                # 2B00..2BFF
+    'Glagolitic',                                      # 2C00..2C5F
+    'Latin Extended-C',                                # 2C60..2C7F
+    'Coptic',                                          # 2C80..2CFF
+    'Georgian Supplement',                             # 2D00..2D2F
+    'Tifinagh',                                        # 2D30..2D7F
+    'Ethiopic Extended',                               # 2D80..2DDF
+    'Cyrillic Extended-A',                             # 2DE0..2DFF
+    'Supplemental Punctuation',                        # 2E00..2E7F
+    'CJK Radicals Supplement',                         # 2E80..2EFF
+    'Kangxi Radicals',                                 # 2F00..2FDF
+    'No_Block',                                        # 2FE0..2FEF
+    'Ideographic Description Characters',              # 2FF0..2FFF
+    'CJK Symbols and Punctuation',                     # 3000..303F
+    'Hiragana',                                        # 3040..309F
+    'Katakana',                                        # 30A0..30FF
+    'Bopomofo',                                        # 3100..312F
+    'Hangul Compatibility Jamo',                       # 3130..318F
+    'Kanbun',                                          # 3190..319F
+    'Bopomofo Extended',                               # 31A0..31BF
+    'CJK Strokes',                                     # 31C0..31EF
+    'Katakana Phonetic Extensions',                    # 31F0..31FF
+    'Enclosed CJK Letters and Months',                 # 3200..32FF
+    'CJK Compatibility',                               # 3300..33FF
+    'CJK Unified Ideographs Extension A',              # 3400..4DBF
+    'Yijing Hexagram Symbols',                         # 4DC0..4DFF
+    'CJK Unified Ideographs',                          # 4E00..9FFF
+    'Yi Syllables',                                    # A000..A48F
+    'Yi Radicals',                                     # A490..A4CF
+    'Lisu',                                            # A4D0..A4FF
+    'Vai',                                             # A500..A63F
+    'Cyrillic Extended-B',                             # A640..A69F
+    'Bamum',                                           # A6A0..A6FF
+    'Modifier Tone Letters',                           # A700..A71F
+    'Latin Extended-D',                                # A720..A7FF
+    'Syloti Nagri',                                    # A800..A82F
+    'Common Indic Number Forms',                       # A830..A83F
+    'Phags-pa',                                        # A840..A87F
+    'Saurashtra',                                      # A880..A8DF
+    'Devanagari Extended',                             # A8E0..A8FF
+    'Kayah Li',                                        # A900..A92F
+    'Rejang',                                          # A930..A95F
+    'Hangul Jamo Extended-A',                          # A960..A97F
+    'Javanese',                                        # A980..A9DF
+    'Myanmar Extended-B',                              # A9E0..A9FF
+    'Cham',                                            # AA00..AA5F
+    'Myanmar Extended-A',                              # AA60..AA7F
+    'Tai Viet',                                        # AA80..AADF
+    'Meetei Mayek Extensions',                         # AAE0..AAFF
+    'Ethiopic Extended-A',                             # AB00..AB2F
+    'Latin Extended-E',                                # AB30..AB6F
+    'Cherokee Supplement',                             # AB70..ABBF
+    'Meetei Mayek',                                    # ABC0..ABFF
+    'Hangul Syllables',                                # AC00..D7AF
+    'Hangul Jamo Extended-B',                          # D7B0..D7FF
+    'High Surrogates',                                 # D800..DB7F
+    'High Private Use Surrogates',                     # DB80..DBFF
+    'Low Surrogates',                                  # DC00..DFFF
+    'Private Use Area',                                # E000..F8FF
+    'CJK Compatibility Ideographs',                    # F900..FAFF
+    'Alphabetic Presentation Forms',                   # FB00..FB4F
+    'Arabic Presentation Forms-A',                     # FB50..FDFF
+    'Variation Selectors',                             # FE00..FE0F
+    'Vertical Forms',                                  # FE10..FE1F
+    'Combining Half Marks',                            # FE20..FE2F
+    'CJK Compatibility Forms',                         # FE30..FE4F
+    'Small Form Variants',                             # FE50..FE6F
+    'Arabic Presentation Forms-B',                     # FE70..FEFF
+    'Halfwidth and Fullwidth Forms',                   # FF00..FFEF
+    'Specials',                                        # FFF0..FFFF
+    'Linear B Syllabary',                              # 10000..1007F
+    'Linear B Ideograms',                              # 10080..100FF
+    'Aegean Numbers',                                  # 10100..1013F
+    'Ancient Greek Numbers',                           # 10140..1018F
+    'Ancient Symbols',                                 # 10190..101CF
+    'Phaistos Disc',                                   # 101D0..101FF
+    'No_Block',                                        # 10200..1027F
+    'Lycian',                                          # 10280..1029F
+    'Carian',                                          # 102A0..102DF
+    'Coptic Epact Numbers',                            # 102E0..102FF
+    'Old Italic',                                      # 10300..1032F
+    'Gothic',                                          # 10330..1034F
+    'Old Permic',                                      # 10350..1037F
+    'Ugaritic',                                        # 10380..1039F
+    'Old Persian',                                     # 103A0..103DF
+    'No_Block',                                        # 103E0..103FF
+    'Deseret',                                         # 10400..1044F
+    'Shavian',                                         # 10450..1047F
+    'Osmanya',                                         # 10480..104AF
+    'Osage',                                           # 104B0..104FF
+    'Elbasan',                                         # 10500..1052F
+    'Caucasian Albanian',                              # 10530..1056F
+    'No_Block',                                        # 10570..105FF
+    'Linear A',                                        # 10600..1077F
+    'No_Block',                                        # 10780..107FF
+    'Cypriot Syllabary',                               # 10800..1083F
+    'Imperial Aramaic',                                # 10840..1085F
+    'Palmyrene',                                       # 10860..1087F
+    'Nabataean',                                       # 10880..108AF
+    'No_Block',                                        # 108B0..108DF
+    'Hatran',                                          # 108E0..108FF
+    'Phoenician',                                      # 10900..1091F
+    'Lydian',                                          # 10920..1093F
+    'No_Block',                                        # 10940..1097F
+    'Meroitic Hieroglyphs',                            # 10980..1099F
+    'Meroitic Cursive',                                # 109A0..109FF
+    'Kharoshthi',                                      # 10A00..10A5F
+    'Old South Arabian',                               # 10A60..10A7F
+    'Old North Arabian',                               # 10A80..10A9F
+    'No_Block',                                        # 10AA0..10ABF
+    'Manichaean',                                      # 10AC0..10AFF
+    'Avestan',                                         # 10B00..10B3F
+    'Inscriptional Parthian',                          # 10B40..10B5F
+    'Inscriptional Pahlavi',                           # 10B60..10B7F
+    'Psalter Pahlavi',                                 # 10B80..10BAF
+    'No_Block',                                        # 10BB0..10BFF
+    'Old Turkic',                                      # 10C00..10C4F
+    'No_Block',                                        # 10C50..10C7F
+    'Old Hungarian',                                   # 10C80..10CFF
+    'No_Block',                                        # 10D00..10E5F
+    'Rumi Numeral Symbols',                            # 10E60..10E7F
+    'No_Block',                                        # 10E80..10FFF
+    'Brahmi',                                          # 11000..1107F
+    'Kaithi',                                          # 11080..110CF
+    'Sora Sompeng',                                    # 110D0..110FF
+    'Chakma',                                          # 11100..1114F
+    'Mahajani',                                        # 11150..1117F
+    'Sharada',                                         # 11180..111DF
+    'Sinhala Archaic Numbers',                         # 111E0..111FF
+    'Khojki',                                          # 11200..1124F
+    'No_Block',                                        # 11250..1127F
+    'Multani',                                         # 11280..112AF
+    'Khudawadi',                                       # 112B0..112FF
+    'Grantha',                                         # 11300..1137F
+    'No_Block',                                        # 11380..113FF
+    'Newa',                                            # 11400..1147F
+    'Tirhuta',                                         # 11480..114DF
+    'No_Block',                                        # 114E0..1157F
+    'Siddham',                                         # 11580..115FF
+    'Modi',                                            # 11600..1165F
+    'Mongolian Supplement',                            # 11660..1167F
+    'Takri',                                           # 11680..116CF
+    'No_Block',                                        # 116D0..116FF
+    'Ahom',                                            # 11700..1173F
+    'No_Block',                                        # 11740..1189F
+    'Warang Citi',                                     # 118A0..118FF
+    'No_Block',                                        # 11900..119FF
+    'Zanabazar Square',                                # 11A00..11A4F
+    'Soyombo',                                         # 11A50..11AAF
+    'No_Block',                                        # 11AB0..11ABF
+    'Pau Cin Hau',                                     # 11AC0..11AFF
+    'No_Block',                                        # 11B00..11BFF
+    'Bhaiksuki',                                       # 11C00..11C6F
+    'Marchen',                                         # 11C70..11CBF
+    'No_Block',                                        # 11CC0..11CFF
+    'Masaram Gondi',                                   # 11D00..11D5F
+    'No_Block',                                        # 11D60..11FFF
+    'Cuneiform',                                       # 12000..123FF
+    'Cuneiform Numbers and Punctuation',               # 12400..1247F
+    'Early Dynastic Cuneiform',                        # 12480..1254F
+    'No_Block',                                        # 12550..12FFF
+    'Egyptian Hieroglyphs',                            # 13000..1342F
+    'No_Block',                                        # 13430..143FF
+    'Anatolian Hieroglyphs',                           # 14400..1467F
+    'No_Block',                                        # 14680..167FF
+    'Bamum Supplement',                                # 16800..16A3F
+    'Mro',                                             # 16A40..16A6F
+    'No_Block',                                        # 16A70..16ACF
+    'Bassa Vah',                                       # 16AD0..16AFF
+    'Pahawh Hmong',                                    # 16B00..16B8F
+    'No_Block',                                        # 16B90..16EFF
+    'Miao',                                            # 16F00..16F9F
+    'No_Block',                                        # 16FA0..16FDF
+    'Ideographic Symbols and Punctuation',             # 16FE0..16FFF
+    'Tangut',                                          # 17000..187FF
+    'Tangut Components',                               # 18800..18AFF
+    'No_Block',                                        # 18B00..1AFFF
+    'Kana Supplement',                                 # 1B000..1B0FF
+    'Kana Extended-A',                                 # 1B100..1B12F
+    'No_Block',                                        # 1B130..1B16F
+    'Nushu',                                           # 1B170..1B2FF
+    'No_Block',                                        # 1B300..1BBFF
+    'Duployan',                                        # 1BC00..1BC9F
+    'Shorthand Format Controls',                       # 1BCA0..1BCAF
+    'No_Block',                                        # 1BCB0..1CFFF
+    'Byzantine Musical Symbols',                       # 1D000..1D0FF
+    'Musical Symbols',                                 # 1D100..1D1FF
+    'Ancient Greek Musical Notation',                  # 1D200..1D24F
+    'No_Block',                                        # 1D250..1D2FF
+    'Tai Xuan Jing Symbols',                           # 1D300..1D35F
+    'Counting Rod Numerals',                           # 1D360..1D37F
+    'No_Block',                                        # 1D380..1D3FF
+    'Mathematical Alphanumeric Symbols',               # 1D400..1D7FF
+    'Sutton SignWriting',                              # 1D800..1DAAF
+    'No_Block',                                        # 1DAB0..1DFFF
+    'Glagolitic Supplement',                           # 1E000..1E02F
+    'No_Block',                                        # 1E030..1E7FF
+    'Mende Kikakui',                                   # 1E800..1E8DF
+    'No_Block',                                        # 1E8E0..1E8FF
+    'Adlam',                                           # 1E900..1E95F
+    'No_Block',                                        # 1E960..1EDFF
+    'Arabic Mathematical Alphabetic Symbols',          # 1EE00..1EEFF
+    'No_Block',                                        # 1EF00..1EFFF
+    'Mahjong Tiles',                                   # 1F000..1F02F
+    'Domino Tiles',                                    # 1F030..1F09F
+    'Playing Cards',                                   # 1F0A0..1F0FF
+    'Enclosed Alphanumeric Supplement',                # 1F100..1F1FF
+    'Enclosed Ideographic Supplement',                 # 1F200..1F2FF
+    'Miscellaneous Symbols and Pictographs',           # 1F300..1F5FF
+    'Emoticons',                                       # 1F600..1F64F
+    'Ornamental Dingbats',                             # 1F650..1F67F
+    'Transport and Map Symbols',                       # 1F680..1F6FF
+    'Alchemical Symbols',                              # 1F700..1F77F
+    'Geometric Shapes Extended',                       # 1F780..1F7FF
+    'Supplemental Arrows-C',                           # 1F800..1F8FF
+    'Supplemental Symbols and Pictographs',            # 1F900..1F9FF
+    'No_Block',                                        # 1FA00..1FFFF
+    'CJK Unified Ideographs Extension B',              # 20000..2A6DF
+    'No_Block',                                        # 2A6E0..2A6FF
+    'CJK Unified Ideographs Extension C',              # 2A700..2B73F
+    'CJK Unified Ideographs Extension D',              # 2B740..2B81F
+    'CJK Unified Ideographs Extension E',              # 2B820..2CEAF
+    'CJK Unified Ideographs Extension F',              # 2CEB0..2EBEF
+    'No_Block',                                        # 2EBF0..2F7FF
+    'CJK Compatibility Ideographs Supplement',         # 2F800..2FA1F
+    'No_Block',                                        # 2FA20..DFFFF
+    'Tags',                                            # E0000..E007F
+    'No_Block',                                        # E0080..E00FF
+    'Variation Selectors Supplement',                  # E0100..E01EF
+    'No_Block',                                        # E01F0..EFFFF
+    'Supplementary Private Use Area-A',                # F0000..FFFFF
+    'Supplementary Private Use Area-B',                # 100000..10FFFF
+]
diff --git a/Lib/fontTools/unicodedata/OTTags.py b/Lib/fontTools/unicodedata/OTTags.py
new file mode 100644
index 0000000..3922680
--- /dev/null
+++ b/Lib/fontTools/unicodedata/OTTags.py
@@ -0,0 +1,41 @@
+# Data updated to OpenType 1.8.2 as of January 2018.
+
+# Complete list of OpenType script tags at:
+# https://www.microsoft.com/typography/otspec/scripttags.htm
+
+# Most of the script tags are the same as the ISO 15924 tag but lowercased,
+# so we only have to handle the exceptional cases:
+# - KATAKANA and HIRAGANA both map to 'kana';
+# - spaces at the end are preserved, unlike ISO 15924;
+# - we map special script codes for Inherited, Common and Unknown to DFLT.
+
+DEFAULT_SCRIPT = "DFLT"
+
+SCRIPT_EXCEPTIONS = {
+    "Hira": "kana",
+    "Hrkt": "kana",
+    "Laoo": "lao ",
+    "Yiii": "yi  ",
+    "Nkoo": "nko ",
+    "Vaii": "vai ",
+    "Zinh": DEFAULT_SCRIPT,
+    "Zyyy": DEFAULT_SCRIPT,
+    "Zzzz": DEFAULT_SCRIPT,
+}
+
+NEW_SCRIPT_TAGS = {
+    "Beng": ("bng2",),
+    "Deva": ("dev2",),
+    "Gujr": ("gjr2",),
+    "Guru": ("gur2",),
+    "Knda": ("knd2",),
+    "Mlym": ("mlm2",),
+    "Orya": ("ory2",),
+    "Taml": ("tml2",),
+    "Telu": ("tel2",),
+    "Mymr": ("mym2",),
+}
+
+NEW_SCRIPT_TAGS_REVERSED = {
+    value: key for key, values in NEW_SCRIPT_TAGS.items() for value in values
+}
diff --git a/Lib/fontTools/unicodedata/ScriptExtensions.py b/Lib/fontTools/unicodedata/ScriptExtensions.py
new file mode 100644
index 0000000..a92cc80
--- /dev/null
+++ b/Lib/fontTools/unicodedata/ScriptExtensions.py
@@ -0,0 +1,389 @@
+# -*- coding: utf-8 -*-
+#
+# NOTE: This file was auto-generated with MetaTools/buildUCD.py.
+# Source: https://unicode.org/Public/UNIDATA/ScriptExtensions.txt
+# License: http://unicode.org/copyright.html#License
+#
+# ScriptExtensions-10.0.0.txt
+# Date: 2017-05-31, 01:07:00 GMT [RP]
+# © 2017 Unicode®, Inc.
+# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
+# For terms of use, see http://www.unicode.org/terms_of_use.html
+#
+# Unicode Character Database
+#   For documentation, see http://www.unicode.org/reports/tr44/
+#
+# The Script_Extensions property indicates which characters are commonly used
+# with more than one script, but with a limited number of scripts.
+# For each code point, there is one or more property values.  Each such value is a Script property value.
+# For more information, see:
+#   UAX #24, Unicode Script Property: http://www.unicode.org/reports/tr24/
+#     Especially the sections:
+#       http://www.unicode.org/reports/tr24/#Assignment_Script_Values
+#       http://www.unicode.org/reports/tr24/#Assignment_ScriptX_Values
+#
+# Each Script_Extensions value in this file consists of a set
+# of one or more abbreviated Script property values. The ordering of the
+# values in that set is not material, but for stability in presentation
+# it is given here as alphabetical.
+#
+# The Script_Extensions values are presented in sorted order in the file.
+# They are sorted first by the number of Script property values in their sets,
+# and then alphabetically by first differing Script property value.
+#
+# Following each distinct Script_Extensions value is the list of code
+# points associated with that value, listed in code point order.
+#
+# All code points not explicitly listed for Script_Extensions
+# have as their value the corresponding Script property value
+#
+# @missing: 0000..10FFFF; <script>
+
+
+RANGES = [
+    0x0000,  # .. 0x0341 ; None
+    0x0342,  # .. 0x0342 ; {'Grek'}
+    0x0343,  # .. 0x0344 ; None
+    0x0345,  # .. 0x0345 ; {'Grek'}
+    0x0346,  # .. 0x0362 ; None
+    0x0363,  # .. 0x036F ; {'Latn'}
+    0x0370,  # .. 0x0482 ; None
+    0x0483,  # .. 0x0483 ; {'Cyrl', 'Perm'}
+    0x0484,  # .. 0x0484 ; {'Cyrl', 'Glag'}
+    0x0485,  # .. 0x0486 ; {'Cyrl', 'Latn'}
+    0x0487,  # .. 0x0487 ; {'Cyrl', 'Glag'}
+    0x0488,  # .. 0x0588 ; None
+    0x0589,  # .. 0x0589 ; {'Armn', 'Geor'}
+    0x058A,  # .. 0x060B ; None
+    0x060C,  # .. 0x060C ; {'Arab', 'Syrc', 'Thaa'}
+    0x060D,  # .. 0x061A ; None
+    0x061B,  # .. 0x061C ; {'Arab', 'Syrc', 'Thaa'}
+    0x061D,  # .. 0x061E ; None
+    0x061F,  # .. 0x061F ; {'Arab', 'Syrc', 'Thaa'}
+    0x0620,  # .. 0x063F ; None
+    0x0640,  # .. 0x0640 ; {'Adlm', 'Arab', 'Mand', 'Mani', 'Phlp', 'Syrc'}
+    0x0641,  # .. 0x064A ; None
+    0x064B,  # .. 0x0655 ; {'Arab', 'Syrc'}
+    0x0656,  # .. 0x065F ; None
+    0x0660,  # .. 0x0669 ; {'Arab', 'Thaa'}
+    0x066A,  # .. 0x066F ; None
+    0x0670,  # .. 0x0670 ; {'Arab', 'Syrc'}
+    0x0671,  # .. 0x0950 ; None
+    0x0951,  # .. 0x0951 ; {'Beng', 'Deva', 'Gran', 'Gujr', 'Guru', 'Knda', 'Latn', 'Mlym', 'Orya', 'Shrd', 'Taml', 'Telu'}
+    0x0952,  # .. 0x0952 ; {'Beng', 'Deva', 'Gran', 'Gujr', 'Guru', 'Knda', 'Latn', 'Mlym', 'Orya', 'Taml', 'Telu'}
+    0x0953,  # .. 0x0963 ; None
+    0x0964,  # .. 0x0964 ; {'Beng', 'Deva', 'Gran', 'Gujr', 'Guru', 'Knda', 'Mahj', 'Mlym', 'Orya', 'Sind', 'Sinh', 'Sylo', 'Takr', 'Taml', 'Telu', 'Tirh'}
+    0x0965,  # .. 0x0965 ; {'Beng', 'Deva', 'Gran', 'Gujr', 'Guru', 'Knda', 'Limb', 'Mahj', 'Mlym', 'Orya', 'Sind', 'Sinh', 'Sylo', 'Takr', 'Taml', 'Telu', 'Tirh'}
+    0x0966,  # .. 0x096F ; {'Deva', 'Kthi', 'Mahj'}
+    0x0970,  # .. 0x09E5 ; None
+    0x09E6,  # .. 0x09EF ; {'Beng', 'Cakm', 'Sylo'}
+    0x09F0,  # .. 0x0A65 ; None
+    0x0A66,  # .. 0x0A6F ; {'Guru', 'Mult'}
+    0x0A70,  # .. 0x0AE5 ; None
+    0x0AE6,  # .. 0x0AEF ; {'Gujr', 'Khoj'}
+    0x0AF0,  # .. 0x0BA9 ; None
+    0x0BAA,  # .. 0x0BAA ; {'Gran', 'Taml'}
+    0x0BAB,  # .. 0x0BB4 ; None
+    0x0BB5,  # .. 0x0BB5 ; {'Gran', 'Taml'}
+    0x0BB6,  # .. 0x0BE5 ; None
+    0x0BE6,  # .. 0x0BF2 ; {'Gran', 'Taml'}
+    0x0BF3,  # .. 0x103F ; None
+    0x1040,  # .. 0x1049 ; {'Cakm', 'Mymr', 'Tale'}
+    0x104A,  # .. 0x10FA ; None
+    0x10FB,  # .. 0x10FB ; {'Geor', 'Latn'}
+    0x10FC,  # .. 0x1734 ; None
+    0x1735,  # .. 0x1736 ; {'Buhd', 'Hano', 'Tagb', 'Tglg'}
+    0x1737,  # .. 0x1801 ; None
+    0x1802,  # .. 0x1803 ; {'Mong', 'Phag'}
+    0x1804,  # .. 0x1804 ; None
+    0x1805,  # .. 0x1805 ; {'Mong', 'Phag'}
+    0x1806,  # .. 0x1CCF ; None
+    0x1CD0,  # .. 0x1CD0 ; {'Deva', 'Gran'}
+    0x1CD1,  # .. 0x1CD1 ; {'Deva'}
+    0x1CD2,  # .. 0x1CD3 ; {'Deva', 'Gran'}
+    0x1CD4,  # .. 0x1CD6 ; {'Deva'}
+    0x1CD7,  # .. 0x1CD7 ; {'Deva', 'Shrd'}
+    0x1CD8,  # .. 0x1CD8 ; {'Deva'}
+    0x1CD9,  # .. 0x1CD9 ; {'Deva', 'Shrd'}
+    0x1CDA,  # .. 0x1CDA ; {'Deva', 'Knda', 'Mlym', 'Taml', 'Telu'}
+    0x1CDB,  # .. 0x1CDB ; {'Deva'}
+    0x1CDC,  # .. 0x1CDD ; {'Deva', 'Shrd'}
+    0x1CDE,  # .. 0x1CDF ; {'Deva'}
+    0x1CE0,  # .. 0x1CE0 ; {'Deva', 'Shrd'}
+    0x1CE1,  # .. 0x1CF1 ; {'Deva'}
+    0x1CF2,  # .. 0x1CF4 ; {'Deva', 'Gran'}
+    0x1CF5,  # .. 0x1CF5 ; {'Deva', 'Knda'}
+    0x1CF6,  # .. 0x1CF6 ; {'Deva'}
+    0x1CF7,  # .. 0x1CF7 ; {'Beng'}
+    0x1CF8,  # .. 0x1CF9 ; {'Deva', 'Gran'}
+    0x1CFA,  # .. 0x1DBF ; None
+    0x1DC0,  # .. 0x1DC1 ; {'Grek'}
+    0x1DC2,  # .. 0x20EF ; None
+    0x20F0,  # .. 0x20F0 ; {'Deva', 'Gran', 'Latn'}
+    0x20F1,  # .. 0x2E42 ; None
+    0x2E43,  # .. 0x2E43 ; {'Cyrl', 'Glag'}
+    0x2E44,  # .. 0x3000 ; None
+    0x3001,  # .. 0x3002 ; {'Bopo', 'Hang', 'Hani', 'Hira', 'Kana', 'Yiii'}
+    0x3003,  # .. 0x3003 ; {'Bopo', 'Hang', 'Hani', 'Hira', 'Kana'}
+    0x3004,  # .. 0x3005 ; None
+    0x3006,  # .. 0x3006 ; {'Hani'}
+    0x3007,  # .. 0x3007 ; None
+    0x3008,  # .. 0x3011 ; {'Bopo', 'Hang', 'Hani', 'Hira', 'Kana', 'Yiii'}
+    0x3012,  # .. 0x3012 ; None
+    0x3013,  # .. 0x3013 ; {'Bopo', 'Hang', 'Hani', 'Hira', 'Kana'}
+    0x3014,  # .. 0x301B ; {'Bopo', 'Hang', 'Hani', 'Hira', 'Kana', 'Yiii'}
+    0x301C,  # .. 0x301F ; {'Bopo', 'Hang', 'Hani', 'Hira', 'Kana'}
+    0x3020,  # .. 0x3029 ; None
+    0x302A,  # .. 0x302D ; {'Bopo', 'Hani'}
+    0x302E,  # .. 0x302F ; None
+    0x3030,  # .. 0x3030 ; {'Bopo', 'Hang', 'Hani', 'Hira', 'Kana'}
+    0x3031,  # .. 0x3035 ; {'Hira', 'Kana'}
+    0x3036,  # .. 0x3036 ; None
+    0x3037,  # .. 0x3037 ; {'Bopo', 'Hang', 'Hani', 'Hira', 'Kana'}
+    0x3038,  # .. 0x303B ; None
+    0x303C,  # .. 0x303D ; {'Hani', 'Hira', 'Kana'}
+    0x303E,  # .. 0x303F ; {'Hani'}
+    0x3040,  # .. 0x3098 ; None
+    0x3099,  # .. 0x309C ; {'Hira', 'Kana'}
+    0x309D,  # .. 0x309F ; None
+    0x30A0,  # .. 0x30A0 ; {'Hira', 'Kana'}
+    0x30A1,  # .. 0x30FA ; None
+    0x30FB,  # .. 0x30FB ; {'Bopo', 'Hang', 'Hani', 'Hira', 'Kana', 'Yiii'}
+    0x30FC,  # .. 0x30FC ; {'Hira', 'Kana'}
+    0x30FD,  # .. 0x318F ; None
+    0x3190,  # .. 0x319F ; {'Hani'}
+    0x31A0,  # .. 0x31BF ; None
+    0x31C0,  # .. 0x31E3 ; {'Hani'}
+    0x31E4,  # .. 0x321F ; None
+    0x3220,  # .. 0x3247 ; {'Hani'}
+    0x3248,  # .. 0x327F ; None
+    0x3280,  # .. 0x32B0 ; {'Hani'}
+    0x32B1,  # .. 0x32BF ; None
+    0x32C0,  # .. 0x32CB ; {'Hani'}
+    0x32CC,  # .. 0x3357 ; None
+    0x3358,  # .. 0x3370 ; {'Hani'}
+    0x3371,  # .. 0x337A ; None
+    0x337B,  # .. 0x337F ; {'Hani'}
+    0x3380,  # .. 0x33DF ; None
+    0x33E0,  # .. 0x33FE ; {'Hani'}
+    0x33FF,  # .. 0xA66E ; None
+    0xA66F,  # .. 0xA66F ; {'Cyrl', 'Glag'}
+    0xA670,  # .. 0xA82F ; None
+    0xA830,  # .. 0xA835 ; {'Deva', 'Gujr', 'Guru', 'Knda', 'Kthi', 'Mahj', 'Modi', 'Sind', 'Takr', 'Tirh'}
+    0xA836,  # .. 0xA839 ; {'Deva', 'Gujr', 'Guru', 'Kthi', 'Mahj', 'Modi', 'Sind', 'Takr', 'Tirh'}
+    0xA83A,  # .. 0xA8F0 ; None
+    0xA8F1,  # .. 0xA8F1 ; {'Beng', 'Deva'}
+    0xA8F2,  # .. 0xA8F2 ; None
+    0xA8F3,  # .. 0xA8F3 ; {'Deva', 'Taml'}
+    0xA8F4,  # .. 0xA92D ; None
+    0xA92E,  # .. 0xA92E ; {'Kali', 'Latn', 'Mymr'}
+    0xA92F,  # .. 0xA9CE ; None
+    0xA9CF,  # .. 0xA9CF ; {'Bugi', 'Java'}
+    0xA9D0,  # .. 0xFDF1 ; None
+    0xFDF2,  # .. 0xFDF2 ; {'Arab', 'Thaa'}
+    0xFDF3,  # .. 0xFDFC ; None
+    0xFDFD,  # .. 0xFDFD ; {'Arab', 'Thaa'}
+    0xFDFE,  # .. 0xFE44 ; None
+    0xFE45,  # .. 0xFE46 ; {'Bopo', 'Hang', 'Hani', 'Hira', 'Kana'}
+    0xFE47,  # .. 0xFF60 ; None
+    0xFF61,  # .. 0xFF65 ; {'Bopo', 'Hang', 'Hani', 'Hira', 'Kana', 'Yiii'}
+    0xFF66,  # .. 0xFF6F ; None
+    0xFF70,  # .. 0xFF70 ; {'Hira', 'Kana'}
+    0xFF71,  # .. 0xFF9D ; None
+    0xFF9E,  # .. 0xFF9F ; {'Hira', 'Kana'}
+    0xFFA0,  # .. 0x100FF ; None
+    0x10100,  # .. 0x10102 ; {'Cprt', 'Linb'}
+    0x10103,  # .. 0x10106 ; None
+    0x10107,  # .. 0x10133 ; {'Cprt', 'Lina', 'Linb'}
+    0x10134,  # .. 0x10136 ; None
+    0x10137,  # .. 0x1013F ; {'Cprt', 'Linb'}
+    0x10140,  # .. 0x102DF ; None
+    0x102E0,  # .. 0x102FB ; {'Arab', 'Copt'}
+    0x102FC,  # .. 0x11300 ; None
+    0x11301,  # .. 0x11301 ; {'Gran', 'Taml'}
+    0x11302,  # .. 0x11302 ; None
+    0x11303,  # .. 0x11303 ; {'Gran', 'Taml'}
+    0x11304,  # .. 0x1133B ; None
+    0x1133C,  # .. 0x1133C ; {'Gran', 'Taml'}
+    0x1133D,  # .. 0x1BC9F ; None
+    0x1BCA0,  # .. 0x1BCA3 ; {'Dupl'}
+    0x1BCA4,  # .. 0x1D35F ; None
+    0x1D360,  # .. 0x1D371 ; {'Hani'}
+    0x1D372,  # .. 0x1F24F ; None
+    0x1F250,  # .. 0x1F251 ; {'Hani'}
+    0x1F252,  # .. 0x10FFFF ; None
+]
+
+VALUES = [
+    None,                                                      # 0000..0341
+    {'Grek'},                                                  # 0342..0342
+    None,                                                      # 0343..0344
+    {'Grek'},                                                  # 0345..0345
+    None,                                                      # 0346..0362
+    {'Latn'},                                                  # 0363..036F
+    None,                                                      # 0370..0482
+    {'Cyrl', 'Perm'},                                          # 0483..0483
+    {'Cyrl', 'Glag'},                                          # 0484..0484
+    {'Cyrl', 'Latn'},                                          # 0485..0486
+    {'Cyrl', 'Glag'},                                          # 0487..0487
+    None,                                                      # 0488..0588
+    {'Armn', 'Geor'},                                          # 0589..0589
+    None,                                                      # 058A..060B
+    {'Arab', 'Syrc', 'Thaa'},                                  # 060C..060C
+    None,                                                      # 060D..061A
+    {'Arab', 'Syrc', 'Thaa'},                                  # 061B..061C
+    None,                                                      # 061D..061E
+    {'Arab', 'Syrc', 'Thaa'},                                  # 061F..061F
+    None,                                                      # 0620..063F
+    {'Adlm', 'Arab', 'Mand', 'Mani', 'Phlp', 'Syrc'},          # 0640..0640
+    None,                                                      # 0641..064A
+    {'Arab', 'Syrc'},                                          # 064B..0655
+    None,                                                      # 0656..065F
+    {'Arab', 'Thaa'},                                          # 0660..0669
+    None,                                                      # 066A..066F
+    {'Arab', 'Syrc'},                                          # 0670..0670
+    None,                                                      # 0671..0950
+    {'Beng', 'Deva', 'Gran', 'Gujr', 'Guru', 'Knda', 'Latn', 'Mlym', 'Orya', 'Shrd', 'Taml', 'Telu'},  # 0951..0951
+    {'Beng', 'Deva', 'Gran', 'Gujr', 'Guru', 'Knda', 'Latn', 'Mlym', 'Orya', 'Taml', 'Telu'},  # 0952..0952
+    None,                                                      # 0953..0963
+    {'Beng', 'Deva', 'Gran', 'Gujr', 'Guru', 'Knda', 'Mahj', 'Mlym', 'Orya', 'Sind', 'Sinh', 'Sylo', 'Takr', 'Taml', 'Telu', 'Tirh'},  # 0964..0964
+    {'Beng', 'Deva', 'Gran', 'Gujr', 'Guru', 'Knda', 'Limb', 'Mahj', 'Mlym', 'Orya', 'Sind', 'Sinh', 'Sylo', 'Takr', 'Taml', 'Telu', 'Tirh'},  # 0965..0965
+    {'Deva', 'Kthi', 'Mahj'},                                  # 0966..096F
+    None,                                                      # 0970..09E5
+    {'Beng', 'Cakm', 'Sylo'},                                  # 09E6..09EF
+    None,                                                      # 09F0..0A65
+    {'Guru', 'Mult'},                                          # 0A66..0A6F
+    None,                                                      # 0A70..0AE5
+    {'Gujr', 'Khoj'},                                          # 0AE6..0AEF
+    None,                                                      # 0AF0..0BA9
+    {'Gran', 'Taml'},                                          # 0BAA..0BAA
+    None,                                                      # 0BAB..0BB4
+    {'Gran', 'Taml'},                                          # 0BB5..0BB5
+    None,                                                      # 0BB6..0BE5
+    {'Gran', 'Taml'},                                          # 0BE6..0BF2
+    None,                                                      # 0BF3..103F
+    {'Cakm', 'Mymr', 'Tale'},                                  # 1040..1049
+    None,                                                      # 104A..10FA
+    {'Geor', 'Latn'},                                          # 10FB..10FB
+    None,                                                      # 10FC..1734
+    {'Buhd', 'Hano', 'Tagb', 'Tglg'},                          # 1735..1736
+    None,                                                      # 1737..1801
+    {'Mong', 'Phag'},                                          # 1802..1803
+    None,                                                      # 1804..1804
+    {'Mong', 'Phag'},                                          # 1805..1805
+    None,                                                      # 1806..1CCF
+    {'Deva', 'Gran'},                                          # 1CD0..1CD0
+    {'Deva'},                                                  # 1CD1..1CD1
+    {'Deva', 'Gran'},                                          # 1CD2..1CD3
+    {'Deva'},                                                  # 1CD4..1CD6
+    {'Deva', 'Shrd'},                                          # 1CD7..1CD7
+    {'Deva'},                                                  # 1CD8..1CD8
+    {'Deva', 'Shrd'},                                          # 1CD9..1CD9
+    {'Deva', 'Knda', 'Mlym', 'Taml', 'Telu'},                  # 1CDA..1CDA
+    {'Deva'},                                                  # 1CDB..1CDB
+    {'Deva', 'Shrd'},                                          # 1CDC..1CDD
+    {'Deva'},                                                  # 1CDE..1CDF
+    {'Deva', 'Shrd'},                                          # 1CE0..1CE0
+    {'Deva'},                                                  # 1CE1..1CF1
+    {'Deva', 'Gran'},                                          # 1CF2..1CF4
+    {'Deva', 'Knda'},                                          # 1CF5..1CF5
+    {'Deva'},                                                  # 1CF6..1CF6
+    {'Beng'},                                                  # 1CF7..1CF7
+    {'Deva', 'Gran'},                                          # 1CF8..1CF9
+    None,                                                      # 1CFA..1DBF
+    {'Grek'},                                                  # 1DC0..1DC1
+    None,                                                      # 1DC2..20EF
+    {'Deva', 'Gran', 'Latn'},                                  # 20F0..20F0
+    None,                                                      # 20F1..2E42
+    {'Cyrl', 'Glag'},                                          # 2E43..2E43
+    None,                                                      # 2E44..3000
+    {'Bopo', 'Hang', 'Hani', 'Hira', 'Kana', 'Yiii'},          # 3001..3002
+    {'Bopo', 'Hang', 'Hani', 'Hira', 'Kana'},                  # 3003..3003
+    None,                                                      # 3004..3005
+    {'Hani'},                                                  # 3006..3006
+    None,                                                      # 3007..3007
+    {'Bopo', 'Hang', 'Hani', 'Hira', 'Kana', 'Yiii'},          # 3008..3011
+    None,                                                      # 3012..3012
+    {'Bopo', 'Hang', 'Hani', 'Hira', 'Kana'},                  # 3013..3013
+    {'Bopo', 'Hang', 'Hani', 'Hira', 'Kana', 'Yiii'},          # 3014..301B
+    {'Bopo', 'Hang', 'Hani', 'Hira', 'Kana'},                  # 301C..301F
+    None,                                                      # 3020..3029
+    {'Bopo', 'Hani'},                                          # 302A..302D
+    None,                                                      # 302E..302F
+    {'Bopo', 'Hang', 'Hani', 'Hira', 'Kana'},                  # 3030..3030
+    {'Hira', 'Kana'},                                          # 3031..3035
+    None,                                                      # 3036..3036
+    {'Bopo', 'Hang', 'Hani', 'Hira', 'Kana'},                  # 3037..3037
+    None,                                                      # 3038..303B
+    {'Hani', 'Hira', 'Kana'},                                  # 303C..303D
+    {'Hani'},                                                  # 303E..303F
+    None,                                                      # 3040..3098
+    {'Hira', 'Kana'},                                          # 3099..309C
+    None,                                                      # 309D..309F
+    {'Hira', 'Kana'},                                          # 30A0..30A0
+    None,                                                      # 30A1..30FA
+    {'Bopo', 'Hang', 'Hani', 'Hira', 'Kana', 'Yiii'},          # 30FB..30FB
+    {'Hira', 'Kana'},                                          # 30FC..30FC
+    None,                                                      # 30FD..318F
+    {'Hani'},                                                  # 3190..319F
+    None,                                                      # 31A0..31BF
+    {'Hani'},                                                  # 31C0..31E3
+    None,                                                      # 31E4..321F
+    {'Hani'},                                                  # 3220..3247
+    None,                                                      # 3248..327F
+    {'Hani'},                                                  # 3280..32B0
+    None,                                                      # 32B1..32BF
+    {'Hani'},                                                  # 32C0..32CB
+    None,                                                      # 32CC..3357
+    {'Hani'},                                                  # 3358..3370
+    None,                                                      # 3371..337A
+    {'Hani'},                                                  # 337B..337F
+    None,                                                      # 3380..33DF
+    {'Hani'},                                                  # 33E0..33FE
+    None,                                                      # 33FF..A66E
+    {'Cyrl', 'Glag'},                                          # A66F..A66F
+    None,                                                      # A670..A82F
+    {'Deva', 'Gujr', 'Guru', 'Knda', 'Kthi', 'Mahj', 'Modi', 'Sind', 'Takr', 'Tirh'},  # A830..A835
+    {'Deva', 'Gujr', 'Guru', 'Kthi', 'Mahj', 'Modi', 'Sind', 'Takr', 'Tirh'},  # A836..A839
+    None,                                                      # A83A..A8F0
+    {'Beng', 'Deva'},                                          # A8F1..A8F1
+    None,                                                      # A8F2..A8F2
+    {'Deva', 'Taml'},                                          # A8F3..A8F3
+    None,                                                      # A8F4..A92D
+    {'Kali', 'Latn', 'Mymr'},                                  # A92E..A92E
+    None,                                                      # A92F..A9CE
+    {'Bugi', 'Java'},                                          # A9CF..A9CF
+    None,                                                      # A9D0..FDF1
+    {'Arab', 'Thaa'},                                          # FDF2..FDF2
+    None,                                                      # FDF3..FDFC
+    {'Arab', 'Thaa'},                                          # FDFD..FDFD
+    None,                                                      # FDFE..FE44
+    {'Bopo', 'Hang', 'Hani', 'Hira', 'Kana'},                  # FE45..FE46
+    None,                                                      # FE47..FF60
+    {'Bopo', 'Hang', 'Hani', 'Hira', 'Kana', 'Yiii'},          # FF61..FF65
+    None,                                                      # FF66..FF6F
+    {'Hira', 'Kana'},                                          # FF70..FF70
+    None,                                                      # FF71..FF9D
+    {'Hira', 'Kana'},                                          # FF9E..FF9F
+    None,                                                      # FFA0..100FF
+    {'Cprt', 'Linb'},                                          # 10100..10102
+    None,                                                      # 10103..10106
+    {'Cprt', 'Lina', 'Linb'},                                  # 10107..10133
+    None,                                                      # 10134..10136
+    {'Cprt', 'Linb'},                                          # 10137..1013F
+    None,                                                      # 10140..102DF
+    {'Arab', 'Copt'},                                          # 102E0..102FB
+    None,                                                      # 102FC..11300
+    {'Gran', 'Taml'},                                          # 11301..11301
+    None,                                                      # 11302..11302
+    {'Gran', 'Taml'},                                          # 11303..11303
+    None,                                                      # 11304..1133B
+    {'Gran', 'Taml'},                                          # 1133C..1133C
+    None,                                                      # 1133D..1BC9F
+    {'Dupl'},                                                  # 1BCA0..1BCA3
+    None,                                                      # 1BCA4..1D35F
+    {'Hani'},                                                  # 1D360..1D371
+    None,                                                      # 1D372..1F24F
+    {'Hani'},                                                  # 1F250..1F251
+    None,                                                      # 1F252..10FFFF
+]
diff --git a/Lib/fontTools/unicodedata/Scripts.py b/Lib/fontTools/unicodedata/Scripts.py
new file mode 100644
index 0000000..f39b430
--- /dev/null
+++ b/Lib/fontTools/unicodedata/Scripts.py
@@ -0,0 +1,3201 @@
+# -*- coding: utf-8 -*-
+#
+# NOTE: This file was auto-generated with MetaTools/buildUCD.py.
+# Source: https://unicode.org/Public/UNIDATA/Scripts.txt
+# License: http://unicode.org/copyright.html#License
+#
+# Scripts-10.0.0.txt
+# Date: 2017-03-11, 06:40:37 GMT
+# © 2017 Unicode®, Inc.
+# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
+# For terms of use, see http://www.unicode.org/terms_of_use.html
+#
+# Unicode Character Database
+#   For documentation, see http://www.unicode.org/reports/tr44/
+# For more information, see:
+#   UAX #24, Unicode Script Property: http://www.unicode.org/reports/tr24/
+#     Especially the sections:
+#       http://www.unicode.org/reports/tr24/#Assignment_Script_Values
+#       http://www.unicode.org/reports/tr24/#Assignment_ScriptX_Values
+#
+
+
+RANGES = [
+    0x0000,  # .. 0x0040 ; Common
+    0x0041,  # .. 0x005A ; Latin
+    0x005B,  # .. 0x0060 ; Common
+    0x0061,  # .. 0x007A ; Latin
+    0x007B,  # .. 0x00A9 ; Common
+    0x00AA,  # .. 0x00AA ; Latin
+    0x00AB,  # .. 0x00B9 ; Common
+    0x00BA,  # .. 0x00BA ; Latin
+    0x00BB,  # .. 0x00BF ; Common
+    0x00C0,  # .. 0x00D6 ; Latin
+    0x00D7,  # .. 0x00D7 ; Common
+    0x00D8,  # .. 0x00F6 ; Latin
+    0x00F7,  # .. 0x00F7 ; Common
+    0x00F8,  # .. 0x02B8 ; Latin
+    0x02B9,  # .. 0x02DF ; Common
+    0x02E0,  # .. 0x02E4 ; Latin
+    0x02E5,  # .. 0x02E9 ; Common
+    0x02EA,  # .. 0x02EB ; Bopomofo
+    0x02EC,  # .. 0x02FF ; Common
+    0x0300,  # .. 0x036F ; Inherited
+    0x0370,  # .. 0x0373 ; Greek
+    0x0374,  # .. 0x0374 ; Common
+    0x0375,  # .. 0x0377 ; Greek
+    0x0378,  # .. 0x0379 ; Unknown
+    0x037A,  # .. 0x037D ; Greek
+    0x037E,  # .. 0x037E ; Common
+    0x037F,  # .. 0x037F ; Greek
+    0x0380,  # .. 0x0383 ; Unknown
+    0x0384,  # .. 0x0384 ; Greek
+    0x0385,  # .. 0x0385 ; Common
+    0x0386,  # .. 0x0386 ; Greek
+    0x0387,  # .. 0x0387 ; Common
+    0x0388,  # .. 0x038A ; Greek
+    0x038B,  # .. 0x038B ; Unknown
+    0x038C,  # .. 0x038C ; Greek
+    0x038D,  # .. 0x038D ; Unknown
+    0x038E,  # .. 0x03A1 ; Greek
+    0x03A2,  # .. 0x03A2 ; Unknown
+    0x03A3,  # .. 0x03E1 ; Greek
+    0x03E2,  # .. 0x03EF ; Coptic
+    0x03F0,  # .. 0x03FF ; Greek
+    0x0400,  # .. 0x0484 ; Cyrillic
+    0x0485,  # .. 0x0486 ; Inherited
+    0x0487,  # .. 0x052F ; Cyrillic
+    0x0530,  # .. 0x0530 ; Unknown
+    0x0531,  # .. 0x0556 ; Armenian
+    0x0557,  # .. 0x0558 ; Unknown
+    0x0559,  # .. 0x055F ; Armenian
+    0x0560,  # .. 0x0560 ; Unknown
+    0x0561,  # .. 0x0587 ; Armenian
+    0x0588,  # .. 0x0588 ; Unknown
+    0x0589,  # .. 0x0589 ; Common
+    0x058A,  # .. 0x058A ; Armenian
+    0x058B,  # .. 0x058C ; Unknown
+    0x058D,  # .. 0x058F ; Armenian
+    0x0590,  # .. 0x0590 ; Unknown
+    0x0591,  # .. 0x05C7 ; Hebrew
+    0x05C8,  # .. 0x05CF ; Unknown
+    0x05D0,  # .. 0x05EA ; Hebrew
+    0x05EB,  # .. 0x05EF ; Unknown
+    0x05F0,  # .. 0x05F4 ; Hebrew
+    0x05F5,  # .. 0x05FF ; Unknown
+    0x0600,  # .. 0x0604 ; Arabic
+    0x0605,  # .. 0x0605 ; Common
+    0x0606,  # .. 0x060B ; Arabic
+    0x060C,  # .. 0x060C ; Common
+    0x060D,  # .. 0x061A ; Arabic
+    0x061B,  # .. 0x061B ; Common
+    0x061C,  # .. 0x061C ; Arabic
+    0x061D,  # .. 0x061D ; Unknown
+    0x061E,  # .. 0x061E ; Arabic
+    0x061F,  # .. 0x061F ; Common
+    0x0620,  # .. 0x063F ; Arabic
+    0x0640,  # .. 0x0640 ; Common
+    0x0641,  # .. 0x064A ; Arabic
+    0x064B,  # .. 0x0655 ; Inherited
+    0x0656,  # .. 0x066F ; Arabic
+    0x0670,  # .. 0x0670 ; Inherited
+    0x0671,  # .. 0x06DC ; Arabic
+    0x06DD,  # .. 0x06DD ; Common
+    0x06DE,  # .. 0x06FF ; Arabic
+    0x0700,  # .. 0x070D ; Syriac
+    0x070E,  # .. 0x070E ; Unknown
+    0x070F,  # .. 0x074A ; Syriac
+    0x074B,  # .. 0x074C ; Unknown
+    0x074D,  # .. 0x074F ; Syriac
+    0x0750,  # .. 0x077F ; Arabic
+    0x0780,  # .. 0x07B1 ; Thaana
+    0x07B2,  # .. 0x07BF ; Unknown
+    0x07C0,  # .. 0x07FA ; Nko
+    0x07FB,  # .. 0x07FF ; Unknown
+    0x0800,  # .. 0x082D ; Samaritan
+    0x082E,  # .. 0x082F ; Unknown
+    0x0830,  # .. 0x083E ; Samaritan
+    0x083F,  # .. 0x083F ; Unknown
+    0x0840,  # .. 0x085B ; Mandaic
+    0x085C,  # .. 0x085D ; Unknown
+    0x085E,  # .. 0x085E ; Mandaic
+    0x085F,  # .. 0x085F ; Unknown
+    0x0860,  # .. 0x086A ; Syriac
+    0x086B,  # .. 0x089F ; Unknown
+    0x08A0,  # .. 0x08B4 ; Arabic
+    0x08B5,  # .. 0x08B5 ; Unknown
+    0x08B6,  # .. 0x08BD ; Arabic
+    0x08BE,  # .. 0x08D3 ; Unknown
+    0x08D4,  # .. 0x08E1 ; Arabic
+    0x08E2,  # .. 0x08E2 ; Common
+    0x08E3,  # .. 0x08FF ; Arabic
+    0x0900,  # .. 0x0950 ; Devanagari
+    0x0951,  # .. 0x0952 ; Inherited
+    0x0953,  # .. 0x0963 ; Devanagari
+    0x0964,  # .. 0x0965 ; Common
+    0x0966,  # .. 0x097F ; Devanagari
+    0x0980,  # .. 0x0983 ; Bengali
+    0x0984,  # .. 0x0984 ; Unknown
+    0x0985,  # .. 0x098C ; Bengali
+    0x098D,  # .. 0x098E ; Unknown
+    0x098F,  # .. 0x0990 ; Bengali
+    0x0991,  # .. 0x0992 ; Unknown
+    0x0993,  # .. 0x09A8 ; Bengali
+    0x09A9,  # .. 0x09A9 ; Unknown
+    0x09AA,  # .. 0x09B0 ; Bengali
+    0x09B1,  # .. 0x09B1 ; Unknown
+    0x09B2,  # .. 0x09B2 ; Bengali
+    0x09B3,  # .. 0x09B5 ; Unknown
+    0x09B6,  # .. 0x09B9 ; Bengali
+    0x09BA,  # .. 0x09BB ; Unknown
+    0x09BC,  # .. 0x09C4 ; Bengali
+    0x09C5,  # .. 0x09C6 ; Unknown
+    0x09C7,  # .. 0x09C8 ; Bengali
+    0x09C9,  # .. 0x09CA ; Unknown
+    0x09CB,  # .. 0x09CE ; Bengali
+    0x09CF,  # .. 0x09D6 ; Unknown
+    0x09D7,  # .. 0x09D7 ; Bengali
+    0x09D8,  # .. 0x09DB ; Unknown
+    0x09DC,  # .. 0x09DD ; Bengali
+    0x09DE,  # .. 0x09DE ; Unknown
+    0x09DF,  # .. 0x09E3 ; Bengali
+    0x09E4,  # .. 0x09E5 ; Unknown
+    0x09E6,  # .. 0x09FD ; Bengali
+    0x09FE,  # .. 0x0A00 ; Unknown
+    0x0A01,  # .. 0x0A03 ; Gurmukhi
+    0x0A04,  # .. 0x0A04 ; Unknown
+    0x0A05,  # .. 0x0A0A ; Gurmukhi
+    0x0A0B,  # .. 0x0A0E ; Unknown
+    0x0A0F,  # .. 0x0A10 ; Gurmukhi
+    0x0A11,  # .. 0x0A12 ; Unknown
+    0x0A13,  # .. 0x0A28 ; Gurmukhi
+    0x0A29,  # .. 0x0A29 ; Unknown
+    0x0A2A,  # .. 0x0A30 ; Gurmukhi
+    0x0A31,  # .. 0x0A31 ; Unknown
+    0x0A32,  # .. 0x0A33 ; Gurmukhi
+    0x0A34,  # .. 0x0A34 ; Unknown
+    0x0A35,  # .. 0x0A36 ; Gurmukhi
+    0x0A37,  # .. 0x0A37 ; Unknown
+    0x0A38,  # .. 0x0A39 ; Gurmukhi
+    0x0A3A,  # .. 0x0A3B ; Unknown
+    0x0A3C,  # .. 0x0A3C ; Gurmukhi
+    0x0A3D,  # .. 0x0A3D ; Unknown
+    0x0A3E,  # .. 0x0A42 ; Gurmukhi
+    0x0A43,  # .. 0x0A46 ; Unknown
+    0x0A47,  # .. 0x0A48 ; Gurmukhi
+    0x0A49,  # .. 0x0A4A ; Unknown
+    0x0A4B,  # .. 0x0A4D ; Gurmukhi
+    0x0A4E,  # .. 0x0A50 ; Unknown
+    0x0A51,  # .. 0x0A51 ; Gurmukhi
+    0x0A52,  # .. 0x0A58 ; Unknown
+    0x0A59,  # .. 0x0A5C ; Gurmukhi
+    0x0A5D,  # .. 0x0A5D ; Unknown
+    0x0A5E,  # .. 0x0A5E ; Gurmukhi
+    0x0A5F,  # .. 0x0A65 ; Unknown
+    0x0A66,  # .. 0x0A75 ; Gurmukhi
+    0x0A76,  # .. 0x0A80 ; Unknown
+    0x0A81,  # .. 0x0A83 ; Gujarati
+    0x0A84,  # .. 0x0A84 ; Unknown
+    0x0A85,  # .. 0x0A8D ; Gujarati
+    0x0A8E,  # .. 0x0A8E ; Unknown
+    0x0A8F,  # .. 0x0A91 ; Gujarati
+    0x0A92,  # .. 0x0A92 ; Unknown
+    0x0A93,  # .. 0x0AA8 ; Gujarati
+    0x0AA9,  # .. 0x0AA9 ; Unknown
+    0x0AAA,  # .. 0x0AB0 ; Gujarati
+    0x0AB1,  # .. 0x0AB1 ; Unknown
+    0x0AB2,  # .. 0x0AB3 ; Gujarati
+    0x0AB4,  # .. 0x0AB4 ; Unknown
+    0x0AB5,  # .. 0x0AB9 ; Gujarati
+    0x0ABA,  # .. 0x0ABB ; Unknown
+    0x0ABC,  # .. 0x0AC5 ; Gujarati
+    0x0AC6,  # .. 0x0AC6 ; Unknown
+    0x0AC7,  # .. 0x0AC9 ; Gujarati
+    0x0ACA,  # .. 0x0ACA ; Unknown
+    0x0ACB,  # .. 0x0ACD ; Gujarati
+    0x0ACE,  # .. 0x0ACF ; Unknown
+    0x0AD0,  # .. 0x0AD0 ; Gujarati
+    0x0AD1,  # .. 0x0ADF ; Unknown
+    0x0AE0,  # .. 0x0AE3 ; Gujarati
+    0x0AE4,  # .. 0x0AE5 ; Unknown
+    0x0AE6,  # .. 0x0AF1 ; Gujarati
+    0x0AF2,  # .. 0x0AF8 ; Unknown
+    0x0AF9,  # .. 0x0AFF ; Gujarati
+    0x0B00,  # .. 0x0B00 ; Unknown
+    0x0B01,  # .. 0x0B03 ; Oriya
+    0x0B04,  # .. 0x0B04 ; Unknown
+    0x0B05,  # .. 0x0B0C ; Oriya
+    0x0B0D,  # .. 0x0B0E ; Unknown
+    0x0B0F,  # .. 0x0B10 ; Oriya
+    0x0B11,  # .. 0x0B12 ; Unknown
+    0x0B13,  # .. 0x0B28 ; Oriya
+    0x0B29,  # .. 0x0B29 ; Unknown
+    0x0B2A,  # .. 0x0B30 ; Oriya
+    0x0B31,  # .. 0x0B31 ; Unknown
+    0x0B32,  # .. 0x0B33 ; Oriya
+    0x0B34,  # .. 0x0B34 ; Unknown
+    0x0B35,  # .. 0x0B39 ; Oriya
+    0x0B3A,  # .. 0x0B3B ; Unknown
+    0x0B3C,  # .. 0x0B44 ; Oriya
+    0x0B45,  # .. 0x0B46 ; Unknown
+    0x0B47,  # .. 0x0B48 ; Oriya
+    0x0B49,  # .. 0x0B4A ; Unknown
+    0x0B4B,  # .. 0x0B4D ; Oriya
+    0x0B4E,  # .. 0x0B55 ; Unknown
+    0x0B56,  # .. 0x0B57 ; Oriya
+    0x0B58,  # .. 0x0B5B ; Unknown
+    0x0B5C,  # .. 0x0B5D ; Oriya
+    0x0B5E,  # .. 0x0B5E ; Unknown
+    0x0B5F,  # .. 0x0B63 ; Oriya
+    0x0B64,  # .. 0x0B65 ; Unknown
+    0x0B66,  # .. 0x0B77 ; Oriya
+    0x0B78,  # .. 0x0B81 ; Unknown
+    0x0B82,  # .. 0x0B83 ; Tamil
+    0x0B84,  # .. 0x0B84 ; Unknown
+    0x0B85,  # .. 0x0B8A ; Tamil
+    0x0B8B,  # .. 0x0B8D ; Unknown
+    0x0B8E,  # .. 0x0B90 ; Tamil
+    0x0B91,  # .. 0x0B91 ; Unknown
+    0x0B92,  # .. 0x0B95 ; Tamil
+    0x0B96,  # .. 0x0B98 ; Unknown
+    0x0B99,  # .. 0x0B9A ; Tamil
+    0x0B9B,  # .. 0x0B9B ; Unknown
+    0x0B9C,  # .. 0x0B9C ; Tamil
+    0x0B9D,  # .. 0x0B9D ; Unknown
+    0x0B9E,  # .. 0x0B9F ; Tamil
+    0x0BA0,  # .. 0x0BA2 ; Unknown
+    0x0BA3,  # .. 0x0BA4 ; Tamil
+    0x0BA5,  # .. 0x0BA7 ; Unknown
+    0x0BA8,  # .. 0x0BAA ; Tamil
+    0x0BAB,  # .. 0x0BAD ; Unknown
+    0x0BAE,  # .. 0x0BB9 ; Tamil
+    0x0BBA,  # .. 0x0BBD ; Unknown
+    0x0BBE,  # .. 0x0BC2 ; Tamil
+    0x0BC3,  # .. 0x0BC5 ; Unknown
+    0x0BC6,  # .. 0x0BC8 ; Tamil
+    0x0BC9,  # .. 0x0BC9 ; Unknown
+    0x0BCA,  # .. 0x0BCD ; Tamil
+    0x0BCE,  # .. 0x0BCF ; Unknown
+    0x0BD0,  # .. 0x0BD0 ; Tamil
+    0x0BD1,  # .. 0x0BD6 ; Unknown
+    0x0BD7,  # .. 0x0BD7 ; Tamil
+    0x0BD8,  # .. 0x0BE5 ; Unknown
+    0x0BE6,  # .. 0x0BFA ; Tamil
+    0x0BFB,  # .. 0x0BFF ; Unknown
+    0x0C00,  # .. 0x0C03 ; Telugu
+    0x0C04,  # .. 0x0C04 ; Unknown
+    0x0C05,  # .. 0x0C0C ; Telugu
+    0x0C0D,  # .. 0x0C0D ; Unknown
+    0x0C0E,  # .. 0x0C10 ; Telugu
+    0x0C11,  # .. 0x0C11 ; Unknown
+    0x0C12,  # .. 0x0C28 ; Telugu
+    0x0C29,  # .. 0x0C29 ; Unknown
+    0x0C2A,  # .. 0x0C39 ; Telugu
+    0x0C3A,  # .. 0x0C3C ; Unknown
+    0x0C3D,  # .. 0x0C44 ; Telugu
+    0x0C45,  # .. 0x0C45 ; Unknown
+    0x0C46,  # .. 0x0C48 ; Telugu
+    0x0C49,  # .. 0x0C49 ; Unknown
+    0x0C4A,  # .. 0x0C4D ; Telugu
+    0x0C4E,  # .. 0x0C54 ; Unknown
+    0x0C55,  # .. 0x0C56 ; Telugu
+    0x0C57,  # .. 0x0C57 ; Unknown
+    0x0C58,  # .. 0x0C5A ; Telugu
+    0x0C5B,  # .. 0x0C5F ; Unknown
+    0x0C60,  # .. 0x0C63 ; Telugu
+    0x0C64,  # .. 0x0C65 ; Unknown
+    0x0C66,  # .. 0x0C6F ; Telugu
+    0x0C70,  # .. 0x0C77 ; Unknown
+    0x0C78,  # .. 0x0C7F ; Telugu
+    0x0C80,  # .. 0x0C83 ; Kannada
+    0x0C84,  # .. 0x0C84 ; Unknown
+    0x0C85,  # .. 0x0C8C ; Kannada
+    0x0C8D,  # .. 0x0C8D ; Unknown
+    0x0C8E,  # .. 0x0C90 ; Kannada
+    0x0C91,  # .. 0x0C91 ; Unknown
+    0x0C92,  # .. 0x0CA8 ; Kannada
+    0x0CA9,  # .. 0x0CA9 ; Unknown
+    0x0CAA,  # .. 0x0CB3 ; Kannada
+    0x0CB4,  # .. 0x0CB4 ; Unknown
+    0x0CB5,  # .. 0x0CB9 ; Kannada
+    0x0CBA,  # .. 0x0CBB ; Unknown
+    0x0CBC,  # .. 0x0CC4 ; Kannada
+    0x0CC5,  # .. 0x0CC5 ; Unknown
+    0x0CC6,  # .. 0x0CC8 ; Kannada
+    0x0CC9,  # .. 0x0CC9 ; Unknown
+    0x0CCA,  # .. 0x0CCD ; Kannada
+    0x0CCE,  # .. 0x0CD4 ; Unknown
+    0x0CD5,  # .. 0x0CD6 ; Kannada
+    0x0CD7,  # .. 0x0CDD ; Unknown
+    0x0CDE,  # .. 0x0CDE ; Kannada
+    0x0CDF,  # .. 0x0CDF ; Unknown
+    0x0CE0,  # .. 0x0CE3 ; Kannada
+    0x0CE4,  # .. 0x0CE5 ; Unknown
+    0x0CE6,  # .. 0x0CEF ; Kannada
+    0x0CF0,  # .. 0x0CF0 ; Unknown
+    0x0CF1,  # .. 0x0CF2 ; Kannada
+    0x0CF3,  # .. 0x0CFF ; Unknown
+    0x0D00,  # .. 0x0D03 ; Malayalam
+    0x0D04,  # .. 0x0D04 ; Unknown
+    0x0D05,  # .. 0x0D0C ; Malayalam
+    0x0D0D,  # .. 0x0D0D ; Unknown
+    0x0D0E,  # .. 0x0D10 ; Malayalam
+    0x0D11,  # .. 0x0D11 ; Unknown
+    0x0D12,  # .. 0x0D44 ; Malayalam
+    0x0D45,  # .. 0x0D45 ; Unknown
+    0x0D46,  # .. 0x0D48 ; Malayalam
+    0x0D49,  # .. 0x0D49 ; Unknown
+    0x0D4A,  # .. 0x0D4F ; Malayalam
+    0x0D50,  # .. 0x0D53 ; Unknown
+    0x0D54,  # .. 0x0D63 ; Malayalam
+    0x0D64,  # .. 0x0D65 ; Unknown
+    0x0D66,  # .. 0x0D7F ; Malayalam
+    0x0D80,  # .. 0x0D81 ; Unknown
+    0x0D82,  # .. 0x0D83 ; Sinhala
+    0x0D84,  # .. 0x0D84 ; Unknown
+    0x0D85,  # .. 0x0D96 ; Sinhala
+    0x0D97,  # .. 0x0D99 ; Unknown
+    0x0D9A,  # .. 0x0DB1 ; Sinhala
+    0x0DB2,  # .. 0x0DB2 ; Unknown
+    0x0DB3,  # .. 0x0DBB ; Sinhala
+    0x0DBC,  # .. 0x0DBC ; Unknown
+    0x0DBD,  # .. 0x0DBD ; Sinhala
+    0x0DBE,  # .. 0x0DBF ; Unknown
+    0x0DC0,  # .. 0x0DC6 ; Sinhala
+    0x0DC7,  # .. 0x0DC9 ; Unknown
+    0x0DCA,  # .. 0x0DCA ; Sinhala
+    0x0DCB,  # .. 0x0DCE ; Unknown
+    0x0DCF,  # .. 0x0DD4 ; Sinhala
+    0x0DD5,  # .. 0x0DD5 ; Unknown
+    0x0DD6,  # .. 0x0DD6 ; Sinhala
+    0x0DD7,  # .. 0x0DD7 ; Unknown
+    0x0DD8,  # .. 0x0DDF ; Sinhala
+    0x0DE0,  # .. 0x0DE5 ; Unknown
+    0x0DE6,  # .. 0x0DEF ; Sinhala
+    0x0DF0,  # .. 0x0DF1 ; Unknown
+    0x0DF2,  # .. 0x0DF4 ; Sinhala
+    0x0DF5,  # .. 0x0E00 ; Unknown
+    0x0E01,  # .. 0x0E3A ; Thai
+    0x0E3B,  # .. 0x0E3E ; Unknown
+    0x0E3F,  # .. 0x0E3F ; Common
+    0x0E40,  # .. 0x0E5B ; Thai
+    0x0E5C,  # .. 0x0E80 ; Unknown
+    0x0E81,  # .. 0x0E82 ; Lao
+    0x0E83,  # .. 0x0E83 ; Unknown
+    0x0E84,  # .. 0x0E84 ; Lao
+    0x0E85,  # .. 0x0E86 ; Unknown
+    0x0E87,  # .. 0x0E88 ; Lao
+    0x0E89,  # .. 0x0E89 ; Unknown
+    0x0E8A,  # .. 0x0E8A ; Lao
+    0x0E8B,  # .. 0x0E8C ; Unknown
+    0x0E8D,  # .. 0x0E8D ; Lao
+    0x0E8E,  # .. 0x0E93 ; Unknown
+    0x0E94,  # .. 0x0E97 ; Lao
+    0x0E98,  # .. 0x0E98 ; Unknown
+    0x0E99,  # .. 0x0E9F ; Lao
+    0x0EA0,  # .. 0x0EA0 ; Unknown
+    0x0EA1,  # .. 0x0EA3 ; Lao
+    0x0EA4,  # .. 0x0EA4 ; Unknown
+    0x0EA5,  # .. 0x0EA5 ; Lao
+    0x0EA6,  # .. 0x0EA6 ; Unknown
+    0x0EA7,  # .. 0x0EA7 ; Lao
+    0x0EA8,  # .. 0x0EA9 ; Unknown
+    0x0EAA,  # .. 0x0EAB ; Lao
+    0x0EAC,  # .. 0x0EAC ; Unknown
+    0x0EAD,  # .. 0x0EB9 ; Lao
+    0x0EBA,  # .. 0x0EBA ; Unknown
+    0x0EBB,  # .. 0x0EBD ; Lao
+    0x0EBE,  # .. 0x0EBF ; Unknown
+    0x0EC0,  # .. 0x0EC4 ; Lao
+    0x0EC5,  # .. 0x0EC5 ; Unknown
+    0x0EC6,  # .. 0x0EC6 ; Lao
+    0x0EC7,  # .. 0x0EC7 ; Unknown
+    0x0EC8,  # .. 0x0ECD ; Lao
+    0x0ECE,  # .. 0x0ECF ; Unknown
+    0x0ED0,  # .. 0x0ED9 ; Lao
+    0x0EDA,  # .. 0x0EDB ; Unknown
+    0x0EDC,  # .. 0x0EDF ; Lao
+    0x0EE0,  # .. 0x0EFF ; Unknown
+    0x0F00,  # .. 0x0F47 ; Tibetan
+    0x0F48,  # .. 0x0F48 ; Unknown
+    0x0F49,  # .. 0x0F6C ; Tibetan
+    0x0F6D,  # .. 0x0F70 ; Unknown
+    0x0F71,  # .. 0x0F97 ; Tibetan
+    0x0F98,  # .. 0x0F98 ; Unknown
+    0x0F99,  # .. 0x0FBC ; Tibetan
+    0x0FBD,  # .. 0x0FBD ; Unknown
+    0x0FBE,  # .. 0x0FCC ; Tibetan
+    0x0FCD,  # .. 0x0FCD ; Unknown
+    0x0FCE,  # .. 0x0FD4 ; Tibetan
+    0x0FD5,  # .. 0x0FD8 ; Common
+    0x0FD9,  # .. 0x0FDA ; Tibetan
+    0x0FDB,  # .. 0x0FFF ; Unknown
+    0x1000,  # .. 0x109F ; Myanmar
+    0x10A0,  # .. 0x10C5 ; Georgian
+    0x10C6,  # .. 0x10C6 ; Unknown
+    0x10C7,  # .. 0x10C7 ; Georgian
+    0x10C8,  # .. 0x10CC ; Unknown
+    0x10CD,  # .. 0x10CD ; Georgian
+    0x10CE,  # .. 0x10CF ; Unknown
+    0x10D0,  # .. 0x10FA ; Georgian
+    0x10FB,  # .. 0x10FB ; Common
+    0x10FC,  # .. 0x10FF ; Georgian
+    0x1100,  # .. 0x11FF ; Hangul
+    0x1200,  # .. 0x1248 ; Ethiopic
+    0x1249,  # .. 0x1249 ; Unknown
+    0x124A,  # .. 0x124D ; Ethiopic
+    0x124E,  # .. 0x124F ; Unknown
+    0x1250,  # .. 0x1256 ; Ethiopic
+    0x1257,  # .. 0x1257 ; Unknown
+    0x1258,  # .. 0x1258 ; Ethiopic
+    0x1259,  # .. 0x1259 ; Unknown
+    0x125A,  # .. 0x125D ; Ethiopic
+    0x125E,  # .. 0x125F ; Unknown
+    0x1260,  # .. 0x1288 ; Ethiopic
+    0x1289,  # .. 0x1289 ; Unknown
+    0x128A,  # .. 0x128D ; Ethiopic
+    0x128E,  # .. 0x128F ; Unknown
+    0x1290,  # .. 0x12B0 ; Ethiopic
+    0x12B1,  # .. 0x12B1 ; Unknown
+    0x12B2,  # .. 0x12B5 ; Ethiopic
+    0x12B6,  # .. 0x12B7 ; Unknown
+    0x12B8,  # .. 0x12BE ; Ethiopic
+    0x12BF,  # .. 0x12BF ; Unknown
+    0x12C0,  # .. 0x12C0 ; Ethiopic
+    0x12C1,  # .. 0x12C1 ; Unknown
+    0x12C2,  # .. 0x12C5 ; Ethiopic
+    0x12C6,  # .. 0x12C7 ; Unknown
+    0x12C8,  # .. 0x12D6 ; Ethiopic
+    0x12D7,  # .. 0x12D7 ; Unknown
+    0x12D8,  # .. 0x1310 ; Ethiopic
+    0x1311,  # .. 0x1311 ; Unknown
+    0x1312,  # .. 0x1315 ; Ethiopic
+    0x1316,  # .. 0x1317 ; Unknown
+    0x1318,  # .. 0x135A ; Ethiopic
+    0x135B,  # .. 0x135C ; Unknown
+    0x135D,  # .. 0x137C ; Ethiopic
+    0x137D,  # .. 0x137F ; Unknown
+    0x1380,  # .. 0x1399 ; Ethiopic
+    0x139A,  # .. 0x139F ; Unknown
+    0x13A0,  # .. 0x13F5 ; Cherokee
+    0x13F6,  # .. 0x13F7 ; Unknown
+    0x13F8,  # .. 0x13FD ; Cherokee
+    0x13FE,  # .. 0x13FF ; Unknown
+    0x1400,  # .. 0x167F ; Canadian_Aboriginal
+    0x1680,  # .. 0x169C ; Ogham
+    0x169D,  # .. 0x169F ; Unknown
+    0x16A0,  # .. 0x16EA ; Runic
+    0x16EB,  # .. 0x16ED ; Common
+    0x16EE,  # .. 0x16F8 ; Runic
+    0x16F9,  # .. 0x16FF ; Unknown
+    0x1700,  # .. 0x170C ; Tagalog
+    0x170D,  # .. 0x170D ; Unknown
+    0x170E,  # .. 0x1714 ; Tagalog
+    0x1715,  # .. 0x171F ; Unknown
+    0x1720,  # .. 0x1734 ; Hanunoo
+    0x1735,  # .. 0x1736 ; Common
+    0x1737,  # .. 0x173F ; Unknown
+    0x1740,  # .. 0x1753 ; Buhid
+    0x1754,  # .. 0x175F ; Unknown
+    0x1760,  # .. 0x176C ; Tagbanwa
+    0x176D,  # .. 0x176D ; Unknown
+    0x176E,  # .. 0x1770 ; Tagbanwa
+    0x1771,  # .. 0x1771 ; Unknown
+    0x1772,  # .. 0x1773 ; Tagbanwa
+    0x1774,  # .. 0x177F ; Unknown
+    0x1780,  # .. 0x17DD ; Khmer
+    0x17DE,  # .. 0x17DF ; Unknown
+    0x17E0,  # .. 0x17E9 ; Khmer
+    0x17EA,  # .. 0x17EF ; Unknown
+    0x17F0,  # .. 0x17F9 ; Khmer
+    0x17FA,  # .. 0x17FF ; Unknown
+    0x1800,  # .. 0x1801 ; Mongolian
+    0x1802,  # .. 0x1803 ; Common
+    0x1804,  # .. 0x1804 ; Mongolian
+    0x1805,  # .. 0x1805 ; Common
+    0x1806,  # .. 0x180E ; Mongolian
+    0x180F,  # .. 0x180F ; Unknown
+    0x1810,  # .. 0x1819 ; Mongolian
+    0x181A,  # .. 0x181F ; Unknown
+    0x1820,  # .. 0x1877 ; Mongolian
+    0x1878,  # .. 0x187F ; Unknown
+    0x1880,  # .. 0x18AA ; Mongolian
+    0x18AB,  # .. 0x18AF ; Unknown
+    0x18B0,  # .. 0x18F5 ; Canadian_Aboriginal
+    0x18F6,  # .. 0x18FF ; Unknown
+    0x1900,  # .. 0x191E ; Limbu
+    0x191F,  # .. 0x191F ; Unknown
+    0x1920,  # .. 0x192B ; Limbu
+    0x192C,  # .. 0x192F ; Unknown
+    0x1930,  # .. 0x193B ; Limbu
+    0x193C,  # .. 0x193F ; Unknown
+    0x1940,  # .. 0x1940 ; Limbu
+    0x1941,  # .. 0x1943 ; Unknown
+    0x1944,  # .. 0x194F ; Limbu
+    0x1950,  # .. 0x196D ; Tai_Le
+    0x196E,  # .. 0x196F ; Unknown
+    0x1970,  # .. 0x1974 ; Tai_Le
+    0x1975,  # .. 0x197F ; Unknown
+    0x1980,  # .. 0x19AB ; New_Tai_Lue
+    0x19AC,  # .. 0x19AF ; Unknown
+    0x19B0,  # .. 0x19C9 ; New_Tai_Lue
+    0x19CA,  # .. 0x19CF ; Unknown
+    0x19D0,  # .. 0x19DA ; New_Tai_Lue
+    0x19DB,  # .. 0x19DD ; Unknown
+    0x19DE,  # .. 0x19DF ; New_Tai_Lue
+    0x19E0,  # .. 0x19FF ; Khmer
+    0x1A00,  # .. 0x1A1B ; Buginese
+    0x1A1C,  # .. 0x1A1D ; Unknown
+    0x1A1E,  # .. 0x1A1F ; Buginese
+    0x1A20,  # .. 0x1A5E ; Tai_Tham
+    0x1A5F,  # .. 0x1A5F ; Unknown
+    0x1A60,  # .. 0x1A7C ; Tai_Tham
+    0x1A7D,  # .. 0x1A7E ; Unknown
+    0x1A7F,  # .. 0x1A89 ; Tai_Tham
+    0x1A8A,  # .. 0x1A8F ; Unknown
+    0x1A90,  # .. 0x1A99 ; Tai_Tham
+    0x1A9A,  # .. 0x1A9F ; Unknown
+    0x1AA0,  # .. 0x1AAD ; Tai_Tham
+    0x1AAE,  # .. 0x1AAF ; Unknown
+    0x1AB0,  # .. 0x1ABE ; Inherited
+    0x1ABF,  # .. 0x1AFF ; Unknown
+    0x1B00,  # .. 0x1B4B ; Balinese
+    0x1B4C,  # .. 0x1B4F ; Unknown
+    0x1B50,  # .. 0x1B7C ; Balinese
+    0x1B7D,  # .. 0x1B7F ; Unknown
+    0x1B80,  # .. 0x1BBF ; Sundanese
+    0x1BC0,  # .. 0x1BF3 ; Batak
+    0x1BF4,  # .. 0x1BFB ; Unknown
+    0x1BFC,  # .. 0x1BFF ; Batak
+    0x1C00,  # .. 0x1C37 ; Lepcha
+    0x1C38,  # .. 0x1C3A ; Unknown
+    0x1C3B,  # .. 0x1C49 ; Lepcha
+    0x1C4A,  # .. 0x1C4C ; Unknown
+    0x1C4D,  # .. 0x1C4F ; Lepcha
+    0x1C50,  # .. 0x1C7F ; Ol_Chiki
+    0x1C80,  # .. 0x1C88 ; Cyrillic
+    0x1C89,  # .. 0x1CBF ; Unknown
+    0x1CC0,  # .. 0x1CC7 ; Sundanese
+    0x1CC8,  # .. 0x1CCF ; Unknown
+    0x1CD0,  # .. 0x1CD2 ; Inherited
+    0x1CD3,  # .. 0x1CD3 ; Common
+    0x1CD4,  # .. 0x1CE0 ; Inherited
+    0x1CE1,  # .. 0x1CE1 ; Common
+    0x1CE2,  # .. 0x1CE8 ; Inherited
+    0x1CE9,  # .. 0x1CEC ; Common
+    0x1CED,  # .. 0x1CED ; Inherited
+    0x1CEE,  # .. 0x1CF3 ; Common
+    0x1CF4,  # .. 0x1CF4 ; Inherited
+    0x1CF5,  # .. 0x1CF7 ; Common
+    0x1CF8,  # .. 0x1CF9 ; Inherited
+    0x1CFA,  # .. 0x1CFF ; Unknown
+    0x1D00,  # .. 0x1D25 ; Latin
+    0x1D26,  # .. 0x1D2A ; Greek
+    0x1D2B,  # .. 0x1D2B ; Cyrillic
+    0x1D2C,  # .. 0x1D5C ; Latin
+    0x1D5D,  # .. 0x1D61 ; Greek
+    0x1D62,  # .. 0x1D65 ; Latin
+    0x1D66,  # .. 0x1D6A ; Greek
+    0x1D6B,  # .. 0x1D77 ; Latin
+    0x1D78,  # .. 0x1D78 ; Cyrillic
+    0x1D79,  # .. 0x1DBE ; Latin
+    0x1DBF,  # .. 0x1DBF ; Greek
+    0x1DC0,  # .. 0x1DF9 ; Inherited
+    0x1DFA,  # .. 0x1DFA ; Unknown
+    0x1DFB,  # .. 0x1DFF ; Inherited
+    0x1E00,  # .. 0x1EFF ; Latin
+    0x1F00,  # .. 0x1F15 ; Greek
+    0x1F16,  # .. 0x1F17 ; Unknown
+    0x1F18,  # .. 0x1F1D ; Greek
+    0x1F1E,  # .. 0x1F1F ; Unknown
+    0x1F20,  # .. 0x1F45 ; Greek
+    0x1F46,  # .. 0x1F47 ; Unknown
+    0x1F48,  # .. 0x1F4D ; Greek
+    0x1F4E,  # .. 0x1F4F ; Unknown
+    0x1F50,  # .. 0x1F57 ; Greek
+    0x1F58,  # .. 0x1F58 ; Unknown
+    0x1F59,  # .. 0x1F59 ; Greek
+    0x1F5A,  # .. 0x1F5A ; Unknown
+    0x1F5B,  # .. 0x1F5B ; Greek
+    0x1F5C,  # .. 0x1F5C ; Unknown
+    0x1F5D,  # .. 0x1F5D ; Greek
+    0x1F5E,  # .. 0x1F5E ; Unknown
+    0x1F5F,  # .. 0x1F7D ; Greek
+    0x1F7E,  # .. 0x1F7F ; Unknown
+    0x1F80,  # .. 0x1FB4 ; Greek
+    0x1FB5,  # .. 0x1FB5 ; Unknown
+    0x1FB6,  # .. 0x1FC4 ; Greek
+    0x1FC5,  # .. 0x1FC5 ; Unknown
+    0x1FC6,  # .. 0x1FD3 ; Greek
+    0x1FD4,  # .. 0x1FD5 ; Unknown
+    0x1FD6,  # .. 0x1FDB ; Greek
+    0x1FDC,  # .. 0x1FDC ; Unknown
+    0x1FDD,  # .. 0x1FEF ; Greek
+    0x1FF0,  # .. 0x1FF1 ; Unknown
+    0x1FF2,  # .. 0x1FF4 ; Greek
+    0x1FF5,  # .. 0x1FF5 ; Unknown
+    0x1FF6,  # .. 0x1FFE ; Greek
+    0x1FFF,  # .. 0x1FFF ; Unknown
+    0x2000,  # .. 0x200B ; Common
+    0x200C,  # .. 0x200D ; Inherited
+    0x200E,  # .. 0x2064 ; Common
+    0x2065,  # .. 0x2065 ; Unknown
+    0x2066,  # .. 0x2070 ; Common
+    0x2071,  # .. 0x2071 ; Latin
+    0x2072,  # .. 0x2073 ; Unknown
+    0x2074,  # .. 0x207E ; Common
+    0x207F,  # .. 0x207F ; Latin
+    0x2080,  # .. 0x208E ; Common
+    0x208F,  # .. 0x208F ; Unknown
+    0x2090,  # .. 0x209C ; Latin
+    0x209D,  # .. 0x209F ; Unknown
+    0x20A0,  # .. 0x20BF ; Common
+    0x20C0,  # .. 0x20CF ; Unknown
+    0x20D0,  # .. 0x20F0 ; Inherited
+    0x20F1,  # .. 0x20FF ; Unknown
+    0x2100,  # .. 0x2125 ; Common
+    0x2126,  # .. 0x2126 ; Greek
+    0x2127,  # .. 0x2129 ; Common
+    0x212A,  # .. 0x212B ; Latin
+    0x212C,  # .. 0x2131 ; Common
+    0x2132,  # .. 0x2132 ; Latin
+    0x2133,  # .. 0x214D ; Common
+    0x214E,  # .. 0x214E ; Latin
+    0x214F,  # .. 0x215F ; Common
+    0x2160,  # .. 0x2188 ; Latin
+    0x2189,  # .. 0x218B ; Common
+    0x218C,  # .. 0x218F ; Unknown
+    0x2190,  # .. 0x2426 ; Common
+    0x2427,  # .. 0x243F ; Unknown
+    0x2440,  # .. 0x244A ; Common
+    0x244B,  # .. 0x245F ; Unknown
+    0x2460,  # .. 0x27FF ; Common
+    0x2800,  # .. 0x28FF ; Braille
+    0x2900,  # .. 0x2B73 ; Common
+    0x2B74,  # .. 0x2B75 ; Unknown
+    0x2B76,  # .. 0x2B95 ; Common
+    0x2B96,  # .. 0x2B97 ; Unknown
+    0x2B98,  # .. 0x2BB9 ; Common
+    0x2BBA,  # .. 0x2BBC ; Unknown
+    0x2BBD,  # .. 0x2BC8 ; Common
+    0x2BC9,  # .. 0x2BC9 ; Unknown
+    0x2BCA,  # .. 0x2BD2 ; Common
+    0x2BD3,  # .. 0x2BEB ; Unknown
+    0x2BEC,  # .. 0x2BEF ; Common
+    0x2BF0,  # .. 0x2BFF ; Unknown
+    0x2C00,  # .. 0x2C2E ; Glagolitic
+    0x2C2F,  # .. 0x2C2F ; Unknown
+    0x2C30,  # .. 0x2C5E ; Glagolitic
+    0x2C5F,  # .. 0x2C5F ; Unknown
+    0x2C60,  # .. 0x2C7F ; Latin
+    0x2C80,  # .. 0x2CF3 ; Coptic
+    0x2CF4,  # .. 0x2CF8 ; Unknown
+    0x2CF9,  # .. 0x2CFF ; Coptic
+    0x2D00,  # .. 0x2D25 ; Georgian
+    0x2D26,  # .. 0x2D26 ; Unknown
+    0x2D27,  # .. 0x2D27 ; Georgian
+    0x2D28,  # .. 0x2D2C ; Unknown
+    0x2D2D,  # .. 0x2D2D ; Georgian
+    0x2D2E,  # .. 0x2D2F ; Unknown
+    0x2D30,  # .. 0x2D67 ; Tifinagh
+    0x2D68,  # .. 0x2D6E ; Unknown
+    0x2D6F,  # .. 0x2D70 ; Tifinagh
+    0x2D71,  # .. 0x2D7E ; Unknown
+    0x2D7F,  # .. 0x2D7F ; Tifinagh
+    0x2D80,  # .. 0x2D96 ; Ethiopic
+    0x2D97,  # .. 0x2D9F ; Unknown
+    0x2DA0,  # .. 0x2DA6 ; Ethiopic
+    0x2DA7,  # .. 0x2DA7 ; Unknown
+    0x2DA8,  # .. 0x2DAE ; Ethiopic
+    0x2DAF,  # .. 0x2DAF ; Unknown
+    0x2DB0,  # .. 0x2DB6 ; Ethiopic
+    0x2DB7,  # .. 0x2DB7 ; Unknown
+    0x2DB8,  # .. 0x2DBE ; Ethiopic
+    0x2DBF,  # .. 0x2DBF ; Unknown
+    0x2DC0,  # .. 0x2DC6 ; Ethiopic
+    0x2DC7,  # .. 0x2DC7 ; Unknown
+    0x2DC8,  # .. 0x2DCE ; Ethiopic
+    0x2DCF,  # .. 0x2DCF ; Unknown
+    0x2DD0,  # .. 0x2DD6 ; Ethiopic
+    0x2DD7,  # .. 0x2DD7 ; Unknown
+    0x2DD8,  # .. 0x2DDE ; Ethiopic
+    0x2DDF,  # .. 0x2DDF ; Unknown
+    0x2DE0,  # .. 0x2DFF ; Cyrillic
+    0x2E00,  # .. 0x2E49 ; Common
+    0x2E4A,  # .. 0x2E7F ; Unknown
+    0x2E80,  # .. 0x2E99 ; Han
+    0x2E9A,  # .. 0x2E9A ; Unknown
+    0x2E9B,  # .. 0x2EF3 ; Han
+    0x2EF4,  # .. 0x2EFF ; Unknown
+    0x2F00,  # .. 0x2FD5 ; Han
+    0x2FD6,  # .. 0x2FEF ; Unknown
+    0x2FF0,  # .. 0x2FFB ; Common
+    0x2FFC,  # .. 0x2FFF ; Unknown
+    0x3000,  # .. 0x3004 ; Common
+    0x3005,  # .. 0x3005 ; Han
+    0x3006,  # .. 0x3006 ; Common
+    0x3007,  # .. 0x3007 ; Han
+    0x3008,  # .. 0x3020 ; Common
+    0x3021,  # .. 0x3029 ; Han
+    0x302A,  # .. 0x302D ; Inherited
+    0x302E,  # .. 0x302F ; Hangul
+    0x3030,  # .. 0x3037 ; Common
+    0x3038,  # .. 0x303B ; Han
+    0x303C,  # .. 0x303F ; Common
+    0x3040,  # .. 0x3040 ; Unknown
+    0x3041,  # .. 0x3096 ; Hiragana
+    0x3097,  # .. 0x3098 ; Unknown
+    0x3099,  # .. 0x309A ; Inherited
+    0x309B,  # .. 0x309C ; Common
+    0x309D,  # .. 0x309F ; Hiragana
+    0x30A0,  # .. 0x30A0 ; Common
+    0x30A1,  # .. 0x30FA ; Katakana
+    0x30FB,  # .. 0x30FC ; Common
+    0x30FD,  # .. 0x30FF ; Katakana
+    0x3100,  # .. 0x3104 ; Unknown
+    0x3105,  # .. 0x312E ; Bopomofo
+    0x312F,  # .. 0x3130 ; Unknown
+    0x3131,  # .. 0x318E ; Hangul
+    0x318F,  # .. 0x318F ; Unknown
+    0x3190,  # .. 0x319F ; Common
+    0x31A0,  # .. 0x31BA ; Bopomofo
+    0x31BB,  # .. 0x31BF ; Unknown
+    0x31C0,  # .. 0x31E3 ; Common
+    0x31E4,  # .. 0x31EF ; Unknown
+    0x31F0,  # .. 0x31FF ; Katakana
+    0x3200,  # .. 0x321E ; Hangul
+    0x321F,  # .. 0x321F ; Unknown
+    0x3220,  # .. 0x325F ; Common
+    0x3260,  # .. 0x327E ; Hangul
+    0x327F,  # .. 0x32CF ; Common
+    0x32D0,  # .. 0x32FE ; Katakana
+    0x32FF,  # .. 0x32FF ; Unknown
+    0x3300,  # .. 0x3357 ; Katakana
+    0x3358,  # .. 0x33FF ; Common
+    0x3400,  # .. 0x4DB5 ; Han
+    0x4DB6,  # .. 0x4DBF ; Unknown
+    0x4DC0,  # .. 0x4DFF ; Common
+    0x4E00,  # .. 0x9FEA ; Han
+    0x9FEB,  # .. 0x9FFF ; Unknown
+    0xA000,  # .. 0xA48C ; Yi
+    0xA48D,  # .. 0xA48F ; Unknown
+    0xA490,  # .. 0xA4C6 ; Yi
+    0xA4C7,  # .. 0xA4CF ; Unknown
+    0xA4D0,  # .. 0xA4FF ; Lisu
+    0xA500,  # .. 0xA62B ; Vai
+    0xA62C,  # .. 0xA63F ; Unknown
+    0xA640,  # .. 0xA69F ; Cyrillic
+    0xA6A0,  # .. 0xA6F7 ; Bamum
+    0xA6F8,  # .. 0xA6FF ; Unknown
+    0xA700,  # .. 0xA721 ; Common
+    0xA722,  # .. 0xA787 ; Latin
+    0xA788,  # .. 0xA78A ; Common
+    0xA78B,  # .. 0xA7AE ; Latin
+    0xA7AF,  # .. 0xA7AF ; Unknown
+    0xA7B0,  # .. 0xA7B7 ; Latin
+    0xA7B8,  # .. 0xA7F6 ; Unknown
+    0xA7F7,  # .. 0xA7FF ; Latin
+    0xA800,  # .. 0xA82B ; Syloti_Nagri
+    0xA82C,  # .. 0xA82F ; Unknown
+    0xA830,  # .. 0xA839 ; Common
+    0xA83A,  # .. 0xA83F ; Unknown
+    0xA840,  # .. 0xA877 ; Phags_Pa
+    0xA878,  # .. 0xA87F ; Unknown
+    0xA880,  # .. 0xA8C5 ; Saurashtra
+    0xA8C6,  # .. 0xA8CD ; Unknown
+    0xA8CE,  # .. 0xA8D9 ; Saurashtra
+    0xA8DA,  # .. 0xA8DF ; Unknown
+    0xA8E0,  # .. 0xA8FD ; Devanagari
+    0xA8FE,  # .. 0xA8FF ; Unknown
+    0xA900,  # .. 0xA92D ; Kayah_Li
+    0xA92E,  # .. 0xA92E ; Common
+    0xA92F,  # .. 0xA92F ; Kayah_Li
+    0xA930,  # .. 0xA953 ; Rejang
+    0xA954,  # .. 0xA95E ; Unknown
+    0xA95F,  # .. 0xA95F ; Rejang
+    0xA960,  # .. 0xA97C ; Hangul
+    0xA97D,  # .. 0xA97F ; Unknown
+    0xA980,  # .. 0xA9CD ; Javanese
+    0xA9CE,  # .. 0xA9CE ; Unknown
+    0xA9CF,  # .. 0xA9CF ; Common
+    0xA9D0,  # .. 0xA9D9 ; Javanese
+    0xA9DA,  # .. 0xA9DD ; Unknown
+    0xA9DE,  # .. 0xA9DF ; Javanese
+    0xA9E0,  # .. 0xA9FE ; Myanmar
+    0xA9FF,  # .. 0xA9FF ; Unknown
+    0xAA00,  # .. 0xAA36 ; Cham
+    0xAA37,  # .. 0xAA3F ; Unknown
+    0xAA40,  # .. 0xAA4D ; Cham
+    0xAA4E,  # .. 0xAA4F ; Unknown
+    0xAA50,  # .. 0xAA59 ; Cham
+    0xAA5A,  # .. 0xAA5B ; Unknown
+    0xAA5C,  # .. 0xAA5F ; Cham
+    0xAA60,  # .. 0xAA7F ; Myanmar
+    0xAA80,  # .. 0xAAC2 ; Tai_Viet
+    0xAAC3,  # .. 0xAADA ; Unknown
+    0xAADB,  # .. 0xAADF ; Tai_Viet
+    0xAAE0,  # .. 0xAAF6 ; Meetei_Mayek
+    0xAAF7,  # .. 0xAB00 ; Unknown
+    0xAB01,  # .. 0xAB06 ; Ethiopic
+    0xAB07,  # .. 0xAB08 ; Unknown
+    0xAB09,  # .. 0xAB0E ; Ethiopic
+    0xAB0F,  # .. 0xAB10 ; Unknown
+    0xAB11,  # .. 0xAB16 ; Ethiopic
+    0xAB17,  # .. 0xAB1F ; Unknown
+    0xAB20,  # .. 0xAB26 ; Ethiopic
+    0xAB27,  # .. 0xAB27 ; Unknown
+    0xAB28,  # .. 0xAB2E ; Ethiopic
+    0xAB2F,  # .. 0xAB2F ; Unknown
+    0xAB30,  # .. 0xAB5A ; Latin
+    0xAB5B,  # .. 0xAB5B ; Common
+    0xAB5C,  # .. 0xAB64 ; Latin
+    0xAB65,  # .. 0xAB65 ; Greek
+    0xAB66,  # .. 0xAB6F ; Unknown
+    0xAB70,  # .. 0xABBF ; Cherokee
+    0xABC0,  # .. 0xABED ; Meetei_Mayek
+    0xABEE,  # .. 0xABEF ; Unknown
+    0xABF0,  # .. 0xABF9 ; Meetei_Mayek
+    0xABFA,  # .. 0xABFF ; Unknown
+    0xAC00,  # .. 0xD7A3 ; Hangul
+    0xD7A4,  # .. 0xD7AF ; Unknown
+    0xD7B0,  # .. 0xD7C6 ; Hangul
+    0xD7C7,  # .. 0xD7CA ; Unknown
+    0xD7CB,  # .. 0xD7FB ; Hangul
+    0xD7FC,  # .. 0xF8FF ; Unknown
+    0xF900,  # .. 0xFA6D ; Han
+    0xFA6E,  # .. 0xFA6F ; Unknown
+    0xFA70,  # .. 0xFAD9 ; Han
+    0xFADA,  # .. 0xFAFF ; Unknown
+    0xFB00,  # .. 0xFB06 ; Latin
+    0xFB07,  # .. 0xFB12 ; Unknown
+    0xFB13,  # .. 0xFB17 ; Armenian
+    0xFB18,  # .. 0xFB1C ; Unknown
+    0xFB1D,  # .. 0xFB36 ; Hebrew
+    0xFB37,  # .. 0xFB37 ; Unknown
+    0xFB38,  # .. 0xFB3C ; Hebrew
+    0xFB3D,  # .. 0xFB3D ; Unknown
+    0xFB3E,  # .. 0xFB3E ; Hebrew
+    0xFB3F,  # .. 0xFB3F ; Unknown
+    0xFB40,  # .. 0xFB41 ; Hebrew
+    0xFB42,  # .. 0xFB42 ; Unknown
+    0xFB43,  # .. 0xFB44 ; Hebrew
+    0xFB45,  # .. 0xFB45 ; Unknown
+    0xFB46,  # .. 0xFB4F ; Hebrew
+    0xFB50,  # .. 0xFBC1 ; Arabic
+    0xFBC2,  # .. 0xFBD2 ; Unknown
+    0xFBD3,  # .. 0xFD3D ; Arabic
+    0xFD3E,  # .. 0xFD3F ; Common
+    0xFD40,  # .. 0xFD4F ; Unknown
+    0xFD50,  # .. 0xFD8F ; Arabic
+    0xFD90,  # .. 0xFD91 ; Unknown
+    0xFD92,  # .. 0xFDC7 ; Arabic
+    0xFDC8,  # .. 0xFDEF ; Unknown
+    0xFDF0,  # .. 0xFDFD ; Arabic
+    0xFDFE,  # .. 0xFDFF ; Unknown
+    0xFE00,  # .. 0xFE0F ; Inherited
+    0xFE10,  # .. 0xFE19 ; Common
+    0xFE1A,  # .. 0xFE1F ; Unknown
+    0xFE20,  # .. 0xFE2D ; Inherited
+    0xFE2E,  # .. 0xFE2F ; Cyrillic
+    0xFE30,  # .. 0xFE52 ; Common
+    0xFE53,  # .. 0xFE53 ; Unknown
+    0xFE54,  # .. 0xFE66 ; Common
+    0xFE67,  # .. 0xFE67 ; Unknown
+    0xFE68,  # .. 0xFE6B ; Common
+    0xFE6C,  # .. 0xFE6F ; Unknown
+    0xFE70,  # .. 0xFE74 ; Arabic
+    0xFE75,  # .. 0xFE75 ; Unknown
+    0xFE76,  # .. 0xFEFC ; Arabic
+    0xFEFD,  # .. 0xFEFE ; Unknown
+    0xFEFF,  # .. 0xFEFF ; Common
+    0xFF00,  # .. 0xFF00 ; Unknown
+    0xFF01,  # .. 0xFF20 ; Common
+    0xFF21,  # .. 0xFF3A ; Latin
+    0xFF3B,  # .. 0xFF40 ; Common
+    0xFF41,  # .. 0xFF5A ; Latin
+    0xFF5B,  # .. 0xFF65 ; Common
+    0xFF66,  # .. 0xFF6F ; Katakana
+    0xFF70,  # .. 0xFF70 ; Common
+    0xFF71,  # .. 0xFF9D ; Katakana
+    0xFF9E,  # .. 0xFF9F ; Common
+    0xFFA0,  # .. 0xFFBE ; Hangul
+    0xFFBF,  # .. 0xFFC1 ; Unknown
+    0xFFC2,  # .. 0xFFC7 ; Hangul
+    0xFFC8,  # .. 0xFFC9 ; Unknown
+    0xFFCA,  # .. 0xFFCF ; Hangul
+    0xFFD0,  # .. 0xFFD1 ; Unknown
+    0xFFD2,  # .. 0xFFD7 ; Hangul
+    0xFFD8,  # .. 0xFFD9 ; Unknown
+    0xFFDA,  # .. 0xFFDC ; Hangul
+    0xFFDD,  # .. 0xFFDF ; Unknown
+    0xFFE0,  # .. 0xFFE6 ; Common
+    0xFFE7,  # .. 0xFFE7 ; Unknown
+    0xFFE8,  # .. 0xFFEE ; Common
+    0xFFEF,  # .. 0xFFF8 ; Unknown
+    0xFFF9,  # .. 0xFFFD ; Common
+    0xFFFE,  # .. 0xFFFF ; Unknown
+    0x10000,  # .. 0x1000B ; Linear_B
+    0x1000C,  # .. 0x1000C ; Unknown
+    0x1000D,  # .. 0x10026 ; Linear_B
+    0x10027,  # .. 0x10027 ; Unknown
+    0x10028,  # .. 0x1003A ; Linear_B
+    0x1003B,  # .. 0x1003B ; Unknown
+    0x1003C,  # .. 0x1003D ; Linear_B
+    0x1003E,  # .. 0x1003E ; Unknown
+    0x1003F,  # .. 0x1004D ; Linear_B
+    0x1004E,  # .. 0x1004F ; Unknown
+    0x10050,  # .. 0x1005D ; Linear_B
+    0x1005E,  # .. 0x1007F ; Unknown
+    0x10080,  # .. 0x100FA ; Linear_B
+    0x100FB,  # .. 0x100FF ; Unknown
+    0x10100,  # .. 0x10102 ; Common
+    0x10103,  # .. 0x10106 ; Unknown
+    0x10107,  # .. 0x10133 ; Common
+    0x10134,  # .. 0x10136 ; Unknown
+    0x10137,  # .. 0x1013F ; Common
+    0x10140,  # .. 0x1018E ; Greek
+    0x1018F,  # .. 0x1018F ; Unknown
+    0x10190,  # .. 0x1019B ; Common
+    0x1019C,  # .. 0x1019F ; Unknown
+    0x101A0,  # .. 0x101A0 ; Greek
+    0x101A1,  # .. 0x101CF ; Unknown
+    0x101D0,  # .. 0x101FC ; Common
+    0x101FD,  # .. 0x101FD ; Inherited
+    0x101FE,  # .. 0x1027F ; Unknown
+    0x10280,  # .. 0x1029C ; Lycian
+    0x1029D,  # .. 0x1029F ; Unknown
+    0x102A0,  # .. 0x102D0 ; Carian
+    0x102D1,  # .. 0x102DF ; Unknown
+    0x102E0,  # .. 0x102E0 ; Inherited
+    0x102E1,  # .. 0x102FB ; Common
+    0x102FC,  # .. 0x102FF ; Unknown
+    0x10300,  # .. 0x10323 ; Old_Italic
+    0x10324,  # .. 0x1032C ; Unknown
+    0x1032D,  # .. 0x1032F ; Old_Italic
+    0x10330,  # .. 0x1034A ; Gothic
+    0x1034B,  # .. 0x1034F ; Unknown
+    0x10350,  # .. 0x1037A ; Old_Permic
+    0x1037B,  # .. 0x1037F ; Unknown
+    0x10380,  # .. 0x1039D ; Ugaritic
+    0x1039E,  # .. 0x1039E ; Unknown
+    0x1039F,  # .. 0x1039F ; Ugaritic
+    0x103A0,  # .. 0x103C3 ; Old_Persian
+    0x103C4,  # .. 0x103C7 ; Unknown
+    0x103C8,  # .. 0x103D5 ; Old_Persian
+    0x103D6,  # .. 0x103FF ; Unknown
+    0x10400,  # .. 0x1044F ; Deseret
+    0x10450,  # .. 0x1047F ; Shavian
+    0x10480,  # .. 0x1049D ; Osmanya
+    0x1049E,  # .. 0x1049F ; Unknown
+    0x104A0,  # .. 0x104A9 ; Osmanya
+    0x104AA,  # .. 0x104AF ; Unknown
+    0x104B0,  # .. 0x104D3 ; Osage
+    0x104D4,  # .. 0x104D7 ; Unknown
+    0x104D8,  # .. 0x104FB ; Osage
+    0x104FC,  # .. 0x104FF ; Unknown
+    0x10500,  # .. 0x10527 ; Elbasan
+    0x10528,  # .. 0x1052F ; Unknown
+    0x10530,  # .. 0x10563 ; Caucasian_Albanian
+    0x10564,  # .. 0x1056E ; Unknown
+    0x1056F,  # .. 0x1056F ; Caucasian_Albanian
+    0x10570,  # .. 0x105FF ; Unknown
+    0x10600,  # .. 0x10736 ; Linear_A
+    0x10737,  # .. 0x1073F ; Unknown
+    0x10740,  # .. 0x10755 ; Linear_A
+    0x10756,  # .. 0x1075F ; Unknown
+    0x10760,  # .. 0x10767 ; Linear_A
+    0x10768,  # .. 0x107FF ; Unknown
+    0x10800,  # .. 0x10805 ; Cypriot
+    0x10806,  # .. 0x10807 ; Unknown
+    0x10808,  # .. 0x10808 ; Cypriot
+    0x10809,  # .. 0x10809 ; Unknown
+    0x1080A,  # .. 0x10835 ; Cypriot
+    0x10836,  # .. 0x10836 ; Unknown
+    0x10837,  # .. 0x10838 ; Cypriot
+    0x10839,  # .. 0x1083B ; Unknown
+    0x1083C,  # .. 0x1083C ; Cypriot
+    0x1083D,  # .. 0x1083E ; Unknown
+    0x1083F,  # .. 0x1083F ; Cypriot
+    0x10840,  # .. 0x10855 ; Imperial_Aramaic
+    0x10856,  # .. 0x10856 ; Unknown
+    0x10857,  # .. 0x1085F ; Imperial_Aramaic
+    0x10860,  # .. 0x1087F ; Palmyrene
+    0x10880,  # .. 0x1089E ; Nabataean
+    0x1089F,  # .. 0x108A6 ; Unknown
+    0x108A7,  # .. 0x108AF ; Nabataean
+    0x108B0,  # .. 0x108DF ; Unknown
+    0x108E0,  # .. 0x108F2 ; Hatran
+    0x108F3,  # .. 0x108F3 ; Unknown
+    0x108F4,  # .. 0x108F5 ; Hatran
+    0x108F6,  # .. 0x108FA ; Unknown
+    0x108FB,  # .. 0x108FF ; Hatran
+    0x10900,  # .. 0x1091B ; Phoenician
+    0x1091C,  # .. 0x1091E ; Unknown
+    0x1091F,  # .. 0x1091F ; Phoenician
+    0x10920,  # .. 0x10939 ; Lydian
+    0x1093A,  # .. 0x1093E ; Unknown
+    0x1093F,  # .. 0x1093F ; Lydian
+    0x10940,  # .. 0x1097F ; Unknown
+    0x10980,  # .. 0x1099F ; Meroitic_Hieroglyphs
+    0x109A0,  # .. 0x109B7 ; Meroitic_Cursive
+    0x109B8,  # .. 0x109BB ; Unknown
+    0x109BC,  # .. 0x109CF ; Meroitic_Cursive
+    0x109D0,  # .. 0x109D1 ; Unknown
+    0x109D2,  # .. 0x109FF ; Meroitic_Cursive
+    0x10A00,  # .. 0x10A03 ; Kharoshthi
+    0x10A04,  # .. 0x10A04 ; Unknown
+    0x10A05,  # .. 0x10A06 ; Kharoshthi
+    0x10A07,  # .. 0x10A0B ; Unknown
+    0x10A0C,  # .. 0x10A13 ; Kharoshthi
+    0x10A14,  # .. 0x10A14 ; Unknown
+    0x10A15,  # .. 0x10A17 ; Kharoshthi
+    0x10A18,  # .. 0x10A18 ; Unknown
+    0x10A19,  # .. 0x10A33 ; Kharoshthi
+    0x10A34,  # .. 0x10A37 ; Unknown
+    0x10A38,  # .. 0x10A3A ; Kharoshthi
+    0x10A3B,  # .. 0x10A3E ; Unknown
+    0x10A3F,  # .. 0x10A47 ; Kharoshthi
+    0x10A48,  # .. 0x10A4F ; Unknown
+    0x10A50,  # .. 0x10A58 ; Kharoshthi
+    0x10A59,  # .. 0x10A5F ; Unknown
+    0x10A60,  # .. 0x10A7F ; Old_South_Arabian
+    0x10A80,  # .. 0x10A9F ; Old_North_Arabian
+    0x10AA0,  # .. 0x10ABF ; Unknown
+    0x10AC0,  # .. 0x10AE6 ; Manichaean
+    0x10AE7,  # .. 0x10AEA ; Unknown
+    0x10AEB,  # .. 0x10AF6 ; Manichaean
+    0x10AF7,  # .. 0x10AFF ; Unknown
+    0x10B00,  # .. 0x10B35 ; Avestan
+    0x10B36,  # .. 0x10B38 ; Unknown
+    0x10B39,  # .. 0x10B3F ; Avestan
+    0x10B40,  # .. 0x10B55 ; Inscriptional_Parthian
+    0x10B56,  # .. 0x10B57 ; Unknown
+    0x10B58,  # .. 0x10B5F ; Inscriptional_Parthian
+    0x10B60,  # .. 0x10B72 ; Inscriptional_Pahlavi
+    0x10B73,  # .. 0x10B77 ; Unknown
+    0x10B78,  # .. 0x10B7F ; Inscriptional_Pahlavi
+    0x10B80,  # .. 0x10B91 ; Psalter_Pahlavi
+    0x10B92,  # .. 0x10B98 ; Unknown
+    0x10B99,  # .. 0x10B9C ; Psalter_Pahlavi
+    0x10B9D,  # .. 0x10BA8 ; Unknown
+    0x10BA9,  # .. 0x10BAF ; Psalter_Pahlavi
+    0x10BB0,  # .. 0x10BFF ; Unknown
+    0x10C00,  # .. 0x10C48 ; Old_Turkic
+    0x10C49,  # .. 0x10C7F ; Unknown
+    0x10C80,  # .. 0x10CB2 ; Old_Hungarian
+    0x10CB3,  # .. 0x10CBF ; Unknown
+    0x10CC0,  # .. 0x10CF2 ; Old_Hungarian
+    0x10CF3,  # .. 0x10CF9 ; Unknown
+    0x10CFA,  # .. 0x10CFF ; Old_Hungarian
+    0x10D00,  # .. 0x10E5F ; Unknown
+    0x10E60,  # .. 0x10E7E ; Arabic
+    0x10E7F,  # .. 0x10FFF ; Unknown
+    0x11000,  # .. 0x1104D ; Brahmi
+    0x1104E,  # .. 0x11051 ; Unknown
+    0x11052,  # .. 0x1106F ; Brahmi
+    0x11070,  # .. 0x1107E ; Unknown
+    0x1107F,  # .. 0x1107F ; Brahmi
+    0x11080,  # .. 0x110C1 ; Kaithi
+    0x110C2,  # .. 0x110CF ; Unknown
+    0x110D0,  # .. 0x110E8 ; Sora_Sompeng
+    0x110E9,  # .. 0x110EF ; Unknown
+    0x110F0,  # .. 0x110F9 ; Sora_Sompeng
+    0x110FA,  # .. 0x110FF ; Unknown
+    0x11100,  # .. 0x11134 ; Chakma
+    0x11135,  # .. 0x11135 ; Unknown
+    0x11136,  # .. 0x11143 ; Chakma
+    0x11144,  # .. 0x1114F ; Unknown
+    0x11150,  # .. 0x11176 ; Mahajani
+    0x11177,  # .. 0x1117F ; Unknown
+    0x11180,  # .. 0x111CD ; Sharada
+    0x111CE,  # .. 0x111CF ; Unknown
+    0x111D0,  # .. 0x111DF ; Sharada
+    0x111E0,  # .. 0x111E0 ; Unknown
+    0x111E1,  # .. 0x111F4 ; Sinhala
+    0x111F5,  # .. 0x111FF ; Unknown
+    0x11200,  # .. 0x11211 ; Khojki
+    0x11212,  # .. 0x11212 ; Unknown
+    0x11213,  # .. 0x1123E ; Khojki
+    0x1123F,  # .. 0x1127F ; Unknown
+    0x11280,  # .. 0x11286 ; Multani
+    0x11287,  # .. 0x11287 ; Unknown
+    0x11288,  # .. 0x11288 ; Multani
+    0x11289,  # .. 0x11289 ; Unknown
+    0x1128A,  # .. 0x1128D ; Multani
+    0x1128E,  # .. 0x1128E ; Unknown
+    0x1128F,  # .. 0x1129D ; Multani
+    0x1129E,  # .. 0x1129E ; Unknown
+    0x1129F,  # .. 0x112A9 ; Multani
+    0x112AA,  # .. 0x112AF ; Unknown
+    0x112B0,  # .. 0x112EA ; Khudawadi
+    0x112EB,  # .. 0x112EF ; Unknown
+    0x112F0,  # .. 0x112F9 ; Khudawadi
+    0x112FA,  # .. 0x112FF ; Unknown
+    0x11300,  # .. 0x11303 ; Grantha
+    0x11304,  # .. 0x11304 ; Unknown
+    0x11305,  # .. 0x1130C ; Grantha
+    0x1130D,  # .. 0x1130E ; Unknown
+    0x1130F,  # .. 0x11310 ; Grantha
+    0x11311,  # .. 0x11312 ; Unknown
+    0x11313,  # .. 0x11328 ; Grantha
+    0x11329,  # .. 0x11329 ; Unknown
+    0x1132A,  # .. 0x11330 ; Grantha
+    0x11331,  # .. 0x11331 ; Unknown
+    0x11332,  # .. 0x11333 ; Grantha
+    0x11334,  # .. 0x11334 ; Unknown
+    0x11335,  # .. 0x11339 ; Grantha
+    0x1133A,  # .. 0x1133B ; Unknown
+    0x1133C,  # .. 0x11344 ; Grantha
+    0x11345,  # .. 0x11346 ; Unknown
+    0x11347,  # .. 0x11348 ; Grantha
+    0x11349,  # .. 0x1134A ; Unknown
+    0x1134B,  # .. 0x1134D ; Grantha
+    0x1134E,  # .. 0x1134F ; Unknown
+    0x11350,  # .. 0x11350 ; Grantha
+    0x11351,  # .. 0x11356 ; Unknown
+    0x11357,  # .. 0x11357 ; Grantha
+    0x11358,  # .. 0x1135C ; Unknown
+    0x1135D,  # .. 0x11363 ; Grantha
+    0x11364,  # .. 0x11365 ; Unknown
+    0x11366,  # .. 0x1136C ; Grantha
+    0x1136D,  # .. 0x1136F ; Unknown
+    0x11370,  # .. 0x11374 ; Grantha
+    0x11375,  # .. 0x113FF ; Unknown
+    0x11400,  # .. 0x11459 ; Newa
+    0x1145A,  # .. 0x1145A ; Unknown
+    0x1145B,  # .. 0x1145B ; Newa
+    0x1145C,  # .. 0x1145C ; Unknown
+    0x1145D,  # .. 0x1145D ; Newa
+    0x1145E,  # .. 0x1147F ; Unknown
+    0x11480,  # .. 0x114C7 ; Tirhuta
+    0x114C8,  # .. 0x114CF ; Unknown
+    0x114D0,  # .. 0x114D9 ; Tirhuta
+    0x114DA,  # .. 0x1157F ; Unknown
+    0x11580,  # .. 0x115B5 ; Siddham
+    0x115B6,  # .. 0x115B7 ; Unknown
+    0x115B8,  # .. 0x115DD ; Siddham
+    0x115DE,  # .. 0x115FF ; Unknown
+    0x11600,  # .. 0x11644 ; Modi
+    0x11645,  # .. 0x1164F ; Unknown
+    0x11650,  # .. 0x11659 ; Modi
+    0x1165A,  # .. 0x1165F ; Unknown
+    0x11660,  # .. 0x1166C ; Mongolian
+    0x1166D,  # .. 0x1167F ; Unknown
+    0x11680,  # .. 0x116B7 ; Takri
+    0x116B8,  # .. 0x116BF ; Unknown
+    0x116C0,  # .. 0x116C9 ; Takri
+    0x116CA,  # .. 0x116FF ; Unknown
+    0x11700,  # .. 0x11719 ; Ahom
+    0x1171A,  # .. 0x1171C ; Unknown
+    0x1171D,  # .. 0x1172B ; Ahom
+    0x1172C,  # .. 0x1172F ; Unknown
+    0x11730,  # .. 0x1173F ; Ahom
+    0x11740,  # .. 0x1189F ; Unknown
+    0x118A0,  # .. 0x118F2 ; Warang_Citi
+    0x118F3,  # .. 0x118FE ; Unknown
+    0x118FF,  # .. 0x118FF ; Warang_Citi
+    0x11900,  # .. 0x119FF ; Unknown
+    0x11A00,  # .. 0x11A47 ; Zanabazar_Square
+    0x11A48,  # .. 0x11A4F ; Unknown
+    0x11A50,  # .. 0x11A83 ; Soyombo
+    0x11A84,  # .. 0x11A85 ; Unknown
+    0x11A86,  # .. 0x11A9C ; Soyombo
+    0x11A9D,  # .. 0x11A9D ; Unknown
+    0x11A9E,  # .. 0x11AA2 ; Soyombo
+    0x11AA3,  # .. 0x11ABF ; Unknown
+    0x11AC0,  # .. 0x11AF8 ; Pau_Cin_Hau
+    0x11AF9,  # .. 0x11BFF ; Unknown
+    0x11C00,  # .. 0x11C08 ; Bhaiksuki
+    0x11C09,  # .. 0x11C09 ; Unknown
+    0x11C0A,  # .. 0x11C36 ; Bhaiksuki
+    0x11C37,  # .. 0x11C37 ; Unknown
+    0x11C38,  # .. 0x11C45 ; Bhaiksuki
+    0x11C46,  # .. 0x11C4F ; Unknown
+    0x11C50,  # .. 0x11C6C ; Bhaiksuki
+    0x11C6D,  # .. 0x11C6F ; Unknown
+    0x11C70,  # .. 0x11C8F ; Marchen
+    0x11C90,  # .. 0x11C91 ; Unknown
+    0x11C92,  # .. 0x11CA7 ; Marchen
+    0x11CA8,  # .. 0x11CA8 ; Unknown
+    0x11CA9,  # .. 0x11CB6 ; Marchen
+    0x11CB7,  # .. 0x11CFF ; Unknown
+    0x11D00,  # .. 0x11D06 ; Masaram_Gondi
+    0x11D07,  # .. 0x11D07 ; Unknown
+    0x11D08,  # .. 0x11D09 ; Masaram_Gondi
+    0x11D0A,  # .. 0x11D0A ; Unknown
+    0x11D0B,  # .. 0x11D36 ; Masaram_Gondi
+    0x11D37,  # .. 0x11D39 ; Unknown
+    0x11D3A,  # .. 0x11D3A ; Masaram_Gondi
+    0x11D3B,  # .. 0x11D3B ; Unknown
+    0x11D3C,  # .. 0x11D3D ; Masaram_Gondi
+    0x11D3E,  # .. 0x11D3E ; Unknown
+    0x11D3F,  # .. 0x11D47 ; Masaram_Gondi
+    0x11D48,  # .. 0x11D4F ; Unknown
+    0x11D50,  # .. 0x11D59 ; Masaram_Gondi
+    0x11D5A,  # .. 0x11FFF ; Unknown
+    0x12000,  # .. 0x12399 ; Cuneiform
+    0x1239A,  # .. 0x123FF ; Unknown
+    0x12400,  # .. 0x1246E ; Cuneiform
+    0x1246F,  # .. 0x1246F ; Unknown
+    0x12470,  # .. 0x12474 ; Cuneiform
+    0x12475,  # .. 0x1247F ; Unknown
+    0x12480,  # .. 0x12543 ; Cuneiform
+    0x12544,  # .. 0x12FFF ; Unknown
+    0x13000,  # .. 0x1342E ; Egyptian_Hieroglyphs
+    0x1342F,  # .. 0x143FF ; Unknown
+    0x14400,  # .. 0x14646 ; Anatolian_Hieroglyphs
+    0x14647,  # .. 0x167FF ; Unknown
+    0x16800,  # .. 0x16A38 ; Bamum
+    0x16A39,  # .. 0x16A3F ; Unknown
+    0x16A40,  # .. 0x16A5E ; Mro
+    0x16A5F,  # .. 0x16A5F ; Unknown
+    0x16A60,  # .. 0x16A69 ; Mro
+    0x16A6A,  # .. 0x16A6D ; Unknown
+    0x16A6E,  # .. 0x16A6F ; Mro
+    0x16A70,  # .. 0x16ACF ; Unknown
+    0x16AD0,  # .. 0x16AED ; Bassa_Vah
+    0x16AEE,  # .. 0x16AEF ; Unknown
+    0x16AF0,  # .. 0x16AF5 ; Bassa_Vah
+    0x16AF6,  # .. 0x16AFF ; Unknown
+    0x16B00,  # .. 0x16B45 ; Pahawh_Hmong
+    0x16B46,  # .. 0x16B4F ; Unknown
+    0x16B50,  # .. 0x16B59 ; Pahawh_Hmong
+    0x16B5A,  # .. 0x16B5A ; Unknown
+    0x16B5B,  # .. 0x16B61 ; Pahawh_Hmong
+    0x16B62,  # .. 0x16B62 ; Unknown
+    0x16B63,  # .. 0x16B77 ; Pahawh_Hmong
+    0x16B78,  # .. 0x16B7C ; Unknown
+    0x16B7D,  # .. 0x16B8F ; Pahawh_Hmong
+    0x16B90,  # .. 0x16EFF ; Unknown
+    0x16F00,  # .. 0x16F44 ; Miao
+    0x16F45,  # .. 0x16F4F ; Unknown
+    0x16F50,  # .. 0x16F7E ; Miao
+    0x16F7F,  # .. 0x16F8E ; Unknown
+    0x16F8F,  # .. 0x16F9F ; Miao
+    0x16FA0,  # .. 0x16FDF ; Unknown
+    0x16FE0,  # .. 0x16FE0 ; Tangut
+    0x16FE1,  # .. 0x16FE1 ; Nushu
+    0x16FE2,  # .. 0x16FFF ; Unknown
+    0x17000,  # .. 0x187EC ; Tangut
+    0x187ED,  # .. 0x187FF ; Unknown
+    0x18800,  # .. 0x18AF2 ; Tangut
+    0x18AF3,  # .. 0x1AFFF ; Unknown
+    0x1B000,  # .. 0x1B000 ; Katakana
+    0x1B001,  # .. 0x1B11E ; Hiragana
+    0x1B11F,  # .. 0x1B16F ; Unknown
+    0x1B170,  # .. 0x1B2FB ; Nushu
+    0x1B2FC,  # .. 0x1BBFF ; Unknown
+    0x1BC00,  # .. 0x1BC6A ; Duployan
+    0x1BC6B,  # .. 0x1BC6F ; Unknown
+    0x1BC70,  # .. 0x1BC7C ; Duployan
+    0x1BC7D,  # .. 0x1BC7F ; Unknown
+    0x1BC80,  # .. 0x1BC88 ; Duployan
+    0x1BC89,  # .. 0x1BC8F ; Unknown
+    0x1BC90,  # .. 0x1BC99 ; Duployan
+    0x1BC9A,  # .. 0x1BC9B ; Unknown
+    0x1BC9C,  # .. 0x1BC9F ; Duployan
+    0x1BCA0,  # .. 0x1BCA3 ; Common
+    0x1BCA4,  # .. 0x1CFFF ; Unknown
+    0x1D000,  # .. 0x1D0F5 ; Common
+    0x1D0F6,  # .. 0x1D0FF ; Unknown
+    0x1D100,  # .. 0x1D126 ; Common
+    0x1D127,  # .. 0x1D128 ; Unknown
+    0x1D129,  # .. 0x1D166 ; Common
+    0x1D167,  # .. 0x1D169 ; Inherited
+    0x1D16A,  # .. 0x1D17A ; Common
+    0x1D17B,  # .. 0x1D182 ; Inherited
+    0x1D183,  # .. 0x1D184 ; Common
+    0x1D185,  # .. 0x1D18B ; Inherited
+    0x1D18C,  # .. 0x1D1A9 ; Common
+    0x1D1AA,  # .. 0x1D1AD ; Inherited
+    0x1D1AE,  # .. 0x1D1E8 ; Common
+    0x1D1E9,  # .. 0x1D1FF ; Unknown
+    0x1D200,  # .. 0x1D245 ; Greek
+    0x1D246,  # .. 0x1D2FF ; Unknown
+    0x1D300,  # .. 0x1D356 ; Common
+    0x1D357,  # .. 0x1D35F ; Unknown
+    0x1D360,  # .. 0x1D371 ; Common
+    0x1D372,  # .. 0x1D3FF ; Unknown
+    0x1D400,  # .. 0x1D454 ; Common
+    0x1D455,  # .. 0x1D455 ; Unknown
+    0x1D456,  # .. 0x1D49C ; Common
+    0x1D49D,  # .. 0x1D49D ; Unknown
+    0x1D49E,  # .. 0x1D49F ; Common
+    0x1D4A0,  # .. 0x1D4A1 ; Unknown
+    0x1D4A2,  # .. 0x1D4A2 ; Common
+    0x1D4A3,  # .. 0x1D4A4 ; Unknown
+    0x1D4A5,  # .. 0x1D4A6 ; Common
+    0x1D4A7,  # .. 0x1D4A8 ; Unknown
+    0x1D4A9,  # .. 0x1D4AC ; Common
+    0x1D4AD,  # .. 0x1D4AD ; Unknown
+    0x1D4AE,  # .. 0x1D4B9 ; Common
+    0x1D4BA,  # .. 0x1D4BA ; Unknown
+    0x1D4BB,  # .. 0x1D4BB ; Common
+    0x1D4BC,  # .. 0x1D4BC ; Unknown
+    0x1D4BD,  # .. 0x1D4C3 ; Common
+    0x1D4C4,  # .. 0x1D4C4 ; Unknown
+    0x1D4C5,  # .. 0x1D505 ; Common
+    0x1D506,  # .. 0x1D506 ; Unknown
+    0x1D507,  # .. 0x1D50A ; Common
+    0x1D50B,  # .. 0x1D50C ; Unknown
+    0x1D50D,  # .. 0x1D514 ; Common
+    0x1D515,  # .. 0x1D515 ; Unknown
+    0x1D516,  # .. 0x1D51C ; Common
+    0x1D51D,  # .. 0x1D51D ; Unknown
+    0x1D51E,  # .. 0x1D539 ; Common
+    0x1D53A,  # .. 0x1D53A ; Unknown
+    0x1D53B,  # .. 0x1D53E ; Common
+    0x1D53F,  # .. 0x1D53F ; Unknown
+    0x1D540,  # .. 0x1D544 ; Common
+    0x1D545,  # .. 0x1D545 ; Unknown
+    0x1D546,  # .. 0x1D546 ; Common
+    0x1D547,  # .. 0x1D549 ; Unknown
+    0x1D54A,  # .. 0x1D550 ; Common
+    0x1D551,  # .. 0x1D551 ; Unknown
+    0x1D552,  # .. 0x1D6A5 ; Common
+    0x1D6A6,  # .. 0x1D6A7 ; Unknown
+    0x1D6A8,  # .. 0x1D7CB ; Common
+    0x1D7CC,  # .. 0x1D7CD ; Unknown
+    0x1D7CE,  # .. 0x1D7FF ; Common
+    0x1D800,  # .. 0x1DA8B ; SignWriting
+    0x1DA8C,  # .. 0x1DA9A ; Unknown
+    0x1DA9B,  # .. 0x1DA9F ; SignWriting
+    0x1DAA0,  # .. 0x1DAA0 ; Unknown
+    0x1DAA1,  # .. 0x1DAAF ; SignWriting
+    0x1DAB0,  # .. 0x1DFFF ; Unknown
+    0x1E000,  # .. 0x1E006 ; Glagolitic
+    0x1E007,  # .. 0x1E007 ; Unknown
+    0x1E008,  # .. 0x1E018 ; Glagolitic
+    0x1E019,  # .. 0x1E01A ; Unknown
+    0x1E01B,  # .. 0x1E021 ; Glagolitic
+    0x1E022,  # .. 0x1E022 ; Unknown
+    0x1E023,  # .. 0x1E024 ; Glagolitic
+    0x1E025,  # .. 0x1E025 ; Unknown
+    0x1E026,  # .. 0x1E02A ; Glagolitic
+    0x1E02B,  # .. 0x1E7FF ; Unknown
+    0x1E800,  # .. 0x1E8C4 ; Mende_Kikakui
+    0x1E8C5,  # .. 0x1E8C6 ; Unknown
+    0x1E8C7,  # .. 0x1E8D6 ; Mende_Kikakui
+    0x1E8D7,  # .. 0x1E8FF ; Unknown
+    0x1E900,  # .. 0x1E94A ; Adlam
+    0x1E94B,  # .. 0x1E94F ; Unknown
+    0x1E950,  # .. 0x1E959 ; Adlam
+    0x1E95A,  # .. 0x1E95D ; Unknown
+    0x1E95E,  # .. 0x1E95F ; Adlam
+    0x1E960,  # .. 0x1EDFF ; Unknown
+    0x1EE00,  # .. 0x1EE03 ; Arabic
+    0x1EE04,  # .. 0x1EE04 ; Unknown
+    0x1EE05,  # .. 0x1EE1F ; Arabic
+    0x1EE20,  # .. 0x1EE20 ; Unknown
+    0x1EE21,  # .. 0x1EE22 ; Arabic
+    0x1EE23,  # .. 0x1EE23 ; Unknown
+    0x1EE24,  # .. 0x1EE24 ; Arabic
+    0x1EE25,  # .. 0x1EE26 ; Unknown
+    0x1EE27,  # .. 0x1EE27 ; Arabic
+    0x1EE28,  # .. 0x1EE28 ; Unknown
+    0x1EE29,  # .. 0x1EE32 ; Arabic
+    0x1EE33,  # .. 0x1EE33 ; Unknown
+    0x1EE34,  # .. 0x1EE37 ; Arabic
+    0x1EE38,  # .. 0x1EE38 ; Unknown
+    0x1EE39,  # .. 0x1EE39 ; Arabic
+    0x1EE3A,  # .. 0x1EE3A ; Unknown
+    0x1EE3B,  # .. 0x1EE3B ; Arabic
+    0x1EE3C,  # .. 0x1EE41 ; Unknown
+    0x1EE42,  # .. 0x1EE42 ; Arabic
+    0x1EE43,  # .. 0x1EE46 ; Unknown
+    0x1EE47,  # .. 0x1EE47 ; Arabic
+    0x1EE48,  # .. 0x1EE48 ; Unknown
+    0x1EE49,  # .. 0x1EE49 ; Arabic
+    0x1EE4A,  # .. 0x1EE4A ; Unknown
+    0x1EE4B,  # .. 0x1EE4B ; Arabic
+    0x1EE4C,  # .. 0x1EE4C ; Unknown
+    0x1EE4D,  # .. 0x1EE4F ; Arabic
+    0x1EE50,  # .. 0x1EE50 ; Unknown
+    0x1EE51,  # .. 0x1EE52 ; Arabic
+    0x1EE53,  # .. 0x1EE53 ; Unknown
+    0x1EE54,  # .. 0x1EE54 ; Arabic
+    0x1EE55,  # .. 0x1EE56 ; Unknown
+    0x1EE57,  # .. 0x1EE57 ; Arabic
+    0x1EE58,  # .. 0x1EE58 ; Unknown
+    0x1EE59,  # .. 0x1EE59 ; Arabic
+    0x1EE5A,  # .. 0x1EE5A ; Unknown
+    0x1EE5B,  # .. 0x1EE5B ; Arabic
+    0x1EE5C,  # .. 0x1EE5C ; Unknown
+    0x1EE5D,  # .. 0x1EE5D ; Arabic
+    0x1EE5E,  # .. 0x1EE5E ; Unknown
+    0x1EE5F,  # .. 0x1EE5F ; Arabic
+    0x1EE60,  # .. 0x1EE60 ; Unknown
+    0x1EE61,  # .. 0x1EE62 ; Arabic
+    0x1EE63,  # .. 0x1EE63 ; Unknown
+    0x1EE64,  # .. 0x1EE64 ; Arabic
+    0x1EE65,  # .. 0x1EE66 ; Unknown
+    0x1EE67,  # .. 0x1EE6A ; Arabic
+    0x1EE6B,  # .. 0x1EE6B ; Unknown
+    0x1EE6C,  # .. 0x1EE72 ; Arabic
+    0x1EE73,  # .. 0x1EE73 ; Unknown
+    0x1EE74,  # .. 0x1EE77 ; Arabic
+    0x1EE78,  # .. 0x1EE78 ; Unknown
+    0x1EE79,  # .. 0x1EE7C ; Arabic
+    0x1EE7D,  # .. 0x1EE7D ; Unknown
+    0x1EE7E,  # .. 0x1EE7E ; Arabic
+    0x1EE7F,  # .. 0x1EE7F ; Unknown
+    0x1EE80,  # .. 0x1EE89 ; Arabic
+    0x1EE8A,  # .. 0x1EE8A ; Unknown
+    0x1EE8B,  # .. 0x1EE9B ; Arabic
+    0x1EE9C,  # .. 0x1EEA0 ; Unknown
+    0x1EEA1,  # .. 0x1EEA3 ; Arabic
+    0x1EEA4,  # .. 0x1EEA4 ; Unknown
+    0x1EEA5,  # .. 0x1EEA9 ; Arabic
+    0x1EEAA,  # .. 0x1EEAA ; Unknown
+    0x1EEAB,  # .. 0x1EEBB ; Arabic
+    0x1EEBC,  # .. 0x1EEEF ; Unknown
+    0x1EEF0,  # .. 0x1EEF1 ; Arabic
+    0x1EEF2,  # .. 0x1EFFF ; Unknown
+    0x1F000,  # .. 0x1F02B ; Common
+    0x1F02C,  # .. 0x1F02F ; Unknown
+    0x1F030,  # .. 0x1F093 ; Common
+    0x1F094,  # .. 0x1F09F ; Unknown
+    0x1F0A0,  # .. 0x1F0AE ; Common
+    0x1F0AF,  # .. 0x1F0B0 ; Unknown
+    0x1F0B1,  # .. 0x1F0BF ; Common
+    0x1F0C0,  # .. 0x1F0C0 ; Unknown
+    0x1F0C1,  # .. 0x1F0CF ; Common
+    0x1F0D0,  # .. 0x1F0D0 ; Unknown
+    0x1F0D1,  # .. 0x1F0F5 ; Common
+    0x1F0F6,  # .. 0x1F0FF ; Unknown
+    0x1F100,  # .. 0x1F10C ; Common
+    0x1F10D,  # .. 0x1F10F ; Unknown
+    0x1F110,  # .. 0x1F12E ; Common
+    0x1F12F,  # .. 0x1F12F ; Unknown
+    0x1F130,  # .. 0x1F16B ; Common
+    0x1F16C,  # .. 0x1F16F ; Unknown
+    0x1F170,  # .. 0x1F1AC ; Common
+    0x1F1AD,  # .. 0x1F1E5 ; Unknown
+    0x1F1E6,  # .. 0x1F1FF ; Common
+    0x1F200,  # .. 0x1F200 ; Hiragana
+    0x1F201,  # .. 0x1F202 ; Common
+    0x1F203,  # .. 0x1F20F ; Unknown
+    0x1F210,  # .. 0x1F23B ; Common
+    0x1F23C,  # .. 0x1F23F ; Unknown
+    0x1F240,  # .. 0x1F248 ; Common
+    0x1F249,  # .. 0x1F24F ; Unknown
+    0x1F250,  # .. 0x1F251 ; Common
+    0x1F252,  # .. 0x1F25F ; Unknown
+    0x1F260,  # .. 0x1F265 ; Common
+    0x1F266,  # .. 0x1F2FF ; Unknown
+    0x1F300,  # .. 0x1F6D4 ; Common
+    0x1F6D5,  # .. 0x1F6DF ; Unknown
+    0x1F6E0,  # .. 0x1F6EC ; Common
+    0x1F6ED,  # .. 0x1F6EF ; Unknown
+    0x1F6F0,  # .. 0x1F6F8 ; Common
+    0x1F6F9,  # .. 0x1F6FF ; Unknown
+    0x1F700,  # .. 0x1F773 ; Common
+    0x1F774,  # .. 0x1F77F ; Unknown
+    0x1F780,  # .. 0x1F7D4 ; Common
+    0x1F7D5,  # .. 0x1F7FF ; Unknown
+    0x1F800,  # .. 0x1F80B ; Common
+    0x1F80C,  # .. 0x1F80F ; Unknown
+    0x1F810,  # .. 0x1F847 ; Common
+    0x1F848,  # .. 0x1F84F ; Unknown
+    0x1F850,  # .. 0x1F859 ; Common
+    0x1F85A,  # .. 0x1F85F ; Unknown
+    0x1F860,  # .. 0x1F887 ; Common
+    0x1F888,  # .. 0x1F88F ; Unknown
+    0x1F890,  # .. 0x1F8AD ; Common
+    0x1F8AE,  # .. 0x1F8FF ; Unknown
+    0x1F900,  # .. 0x1F90B ; Common
+    0x1F90C,  # .. 0x1F90F ; Unknown
+    0x1F910,  # .. 0x1F93E ; Common
+    0x1F93F,  # .. 0x1F93F ; Unknown
+    0x1F940,  # .. 0x1F94C ; Common
+    0x1F94D,  # .. 0x1F94F ; Unknown
+    0x1F950,  # .. 0x1F96B ; Common
+    0x1F96C,  # .. 0x1F97F ; Unknown
+    0x1F980,  # .. 0x1F997 ; Common
+    0x1F998,  # .. 0x1F9BF ; Unknown
+    0x1F9C0,  # .. 0x1F9C0 ; Common
+    0x1F9C1,  # .. 0x1F9CF ; Unknown
+    0x1F9D0,  # .. 0x1F9E6 ; Common
+    0x1F9E7,  # .. 0x1FFFF ; Unknown
+    0x20000,  # .. 0x2A6D6 ; Han
+    0x2A6D7,  # .. 0x2A6FF ; Unknown
+    0x2A700,  # .. 0x2B734 ; Han
+    0x2B735,  # .. 0x2B73F ; Unknown
+    0x2B740,  # .. 0x2B81D ; Han
+    0x2B81E,  # .. 0x2B81F ; Unknown
+    0x2B820,  # .. 0x2CEA1 ; Han
+    0x2CEA2,  # .. 0x2CEAF ; Unknown
+    0x2CEB0,  # .. 0x2EBE0 ; Han
+    0x2EBE1,  # .. 0x2F7FF ; Unknown
+    0x2F800,  # .. 0x2FA1D ; Han
+    0x2FA1E,  # .. 0xE0000 ; Unknown
+    0xE0001,  # .. 0xE0001 ; Common
+    0xE0002,  # .. 0xE001F ; Unknown
+    0xE0020,  # .. 0xE007F ; Common
+    0xE0080,  # .. 0xE00FF ; Unknown
+    0xE0100,  # .. 0xE01EF ; Inherited
+    0xE01F0,  # .. 0x10FFFF ; Unknown
+]
+
+VALUES = [
+    'Zyyy',  # 0000..0040 ; Common
+    'Latn',  # 0041..005A ; Latin
+    'Zyyy',  # 005B..0060 ; Common
+    'Latn',  # 0061..007A ; Latin
+    'Zyyy',  # 007B..00A9 ; Common
+    'Latn',  # 00AA..00AA ; Latin
+    'Zyyy',  # 00AB..00B9 ; Common
+    'Latn',  # 00BA..00BA ; Latin
+    'Zyyy',  # 00BB..00BF ; Common
+    'Latn',  # 00C0..00D6 ; Latin
+    'Zyyy',  # 00D7..00D7 ; Common
+    'Latn',  # 00D8..00F6 ; Latin
+    'Zyyy',  # 00F7..00F7 ; Common
+    'Latn',  # 00F8..02B8 ; Latin
+    'Zyyy',  # 02B9..02DF ; Common
+    'Latn',  # 02E0..02E4 ; Latin
+    'Zyyy',  # 02E5..02E9 ; Common
+    'Bopo',  # 02EA..02EB ; Bopomofo
+    'Zyyy',  # 02EC..02FF ; Common
+    'Zinh',  # 0300..036F ; Inherited
+    'Grek',  # 0370..0373 ; Greek
+    'Zyyy',  # 0374..0374 ; Common
+    'Grek',  # 0375..0377 ; Greek
+    'Zzzz',  # 0378..0379 ; Unknown
+    'Grek',  # 037A..037D ; Greek
+    'Zyyy',  # 037E..037E ; Common
+    'Grek',  # 037F..037F ; Greek
+    'Zzzz',  # 0380..0383 ; Unknown
+    'Grek',  # 0384..0384 ; Greek
+    'Zyyy',  # 0385..0385 ; Common
+    'Grek',  # 0386..0386 ; Greek
+    'Zyyy',  # 0387..0387 ; Common
+    'Grek',  # 0388..038A ; Greek
+    'Zzzz',  # 038B..038B ; Unknown
+    'Grek',  # 038C..038C ; Greek
+    'Zzzz',  # 038D..038D ; Unknown
+    'Grek',  # 038E..03A1 ; Greek
+    'Zzzz',  # 03A2..03A2 ; Unknown
+    'Grek',  # 03A3..03E1 ; Greek
+    'Copt',  # 03E2..03EF ; Coptic
+    'Grek',  # 03F0..03FF ; Greek
+    'Cyrl',  # 0400..0484 ; Cyrillic
+    'Zinh',  # 0485..0486 ; Inherited
+    'Cyrl',  # 0487..052F ; Cyrillic
+    'Zzzz',  # 0530..0530 ; Unknown
+    'Armn',  # 0531..0556 ; Armenian
+    'Zzzz',  # 0557..0558 ; Unknown
+    'Armn',  # 0559..055F ; Armenian
+    'Zzzz',  # 0560..0560 ; Unknown
+    'Armn',  # 0561..0587 ; Armenian
+    'Zzzz',  # 0588..0588 ; Unknown
+    'Zyyy',  # 0589..0589 ; Common
+    'Armn',  # 058A..058A ; Armenian
+    'Zzzz',  # 058B..058C ; Unknown
+    'Armn',  # 058D..058F ; Armenian
+    'Zzzz',  # 0590..0590 ; Unknown
+    'Hebr',  # 0591..05C7 ; Hebrew
+    'Zzzz',  # 05C8..05CF ; Unknown
+    'Hebr',  # 05D0..05EA ; Hebrew
+    'Zzzz',  # 05EB..05EF ; Unknown
+    'Hebr',  # 05F0..05F4 ; Hebrew
+    'Zzzz',  # 05F5..05FF ; Unknown
+    'Arab',  # 0600..0604 ; Arabic
+    'Zyyy',  # 0605..0605 ; Common
+    'Arab',  # 0606..060B ; Arabic
+    'Zyyy',  # 060C..060C ; Common
+    'Arab',  # 060D..061A ; Arabic
+    'Zyyy',  # 061B..061B ; Common
+    'Arab',  # 061C..061C ; Arabic
+    'Zzzz',  # 061D..061D ; Unknown
+    'Arab',  # 061E..061E ; Arabic
+    'Zyyy',  # 061F..061F ; Common
+    'Arab',  # 0620..063F ; Arabic
+    'Zyyy',  # 0640..0640 ; Common
+    'Arab',  # 0641..064A ; Arabic
+    'Zinh',  # 064B..0655 ; Inherited
+    'Arab',  # 0656..066F ; Arabic
+    'Zinh',  # 0670..0670 ; Inherited
+    'Arab',  # 0671..06DC ; Arabic
+    'Zyyy',  # 06DD..06DD ; Common
+    'Arab',  # 06DE..06FF ; Arabic
+    'Syrc',  # 0700..070D ; Syriac
+    'Zzzz',  # 070E..070E ; Unknown
+    'Syrc',  # 070F..074A ; Syriac
+    'Zzzz',  # 074B..074C ; Unknown
+    'Syrc',  # 074D..074F ; Syriac
+    'Arab',  # 0750..077F ; Arabic
+    'Thaa',  # 0780..07B1 ; Thaana
+    'Zzzz',  # 07B2..07BF ; Unknown
+    'Nkoo',  # 07C0..07FA ; Nko
+    'Zzzz',  # 07FB..07FF ; Unknown
+    'Samr',  # 0800..082D ; Samaritan
+    'Zzzz',  # 082E..082F ; Unknown
+    'Samr',  # 0830..083E ; Samaritan
+    'Zzzz',  # 083F..083F ; Unknown
+    'Mand',  # 0840..085B ; Mandaic
+    'Zzzz',  # 085C..085D ; Unknown
+    'Mand',  # 085E..085E ; Mandaic
+    'Zzzz',  # 085F..085F ; Unknown
+    'Syrc',  # 0860..086A ; Syriac
+    'Zzzz',  # 086B..089F ; Unknown
+    'Arab',  # 08A0..08B4 ; Arabic
+    'Zzzz',  # 08B5..08B5 ; Unknown
+    'Arab',  # 08B6..08BD ; Arabic
+    'Zzzz',  # 08BE..08D3 ; Unknown
+    'Arab',  # 08D4..08E1 ; Arabic
+    'Zyyy',  # 08E2..08E2 ; Common
+    'Arab',  # 08E3..08FF ; Arabic
+    'Deva',  # 0900..0950 ; Devanagari
+    'Zinh',  # 0951..0952 ; Inherited
+    'Deva',  # 0953..0963 ; Devanagari
+    'Zyyy',  # 0964..0965 ; Common
+    'Deva',  # 0966..097F ; Devanagari
+    'Beng',  # 0980..0983 ; Bengali
+    'Zzzz',  # 0984..0984 ; Unknown
+    'Beng',  # 0985..098C ; Bengali
+    'Zzzz',  # 098D..098E ; Unknown
+    'Beng',  # 098F..0990 ; Bengali
+    'Zzzz',  # 0991..0992 ; Unknown
+    'Beng',  # 0993..09A8 ; Bengali
+    'Zzzz',  # 09A9..09A9 ; Unknown
+    'Beng',  # 09AA..09B0 ; Bengali
+    'Zzzz',  # 09B1..09B1 ; Unknown
+    'Beng',  # 09B2..09B2 ; Bengali
+    'Zzzz',  # 09B3..09B5 ; Unknown
+    'Beng',  # 09B6..09B9 ; Bengali
+    'Zzzz',  # 09BA..09BB ; Unknown
+    'Beng',  # 09BC..09C4 ; Bengali
+    'Zzzz',  # 09C5..09C6 ; Unknown
+    'Beng',  # 09C7..09C8 ; Bengali
+    'Zzzz',  # 09C9..09CA ; Unknown
+    'Beng',  # 09CB..09CE ; Bengali
+    'Zzzz',  # 09CF..09D6 ; Unknown
+    'Beng',  # 09D7..09D7 ; Bengali
+    'Zzzz',  # 09D8..09DB ; Unknown
+    'Beng',  # 09DC..09DD ; Bengali
+    'Zzzz',  # 09DE..09DE ; Unknown
+    'Beng',  # 09DF..09E3 ; Bengali
+    'Zzzz',  # 09E4..09E5 ; Unknown
+    'Beng',  # 09E6..09FD ; Bengali
+    'Zzzz',  # 09FE..0A00 ; Unknown
+    'Guru',  # 0A01..0A03 ; Gurmukhi
+    'Zzzz',  # 0A04..0A04 ; Unknown
+    'Guru',  # 0A05..0A0A ; Gurmukhi
+    'Zzzz',  # 0A0B..0A0E ; Unknown
+    'Guru',  # 0A0F..0A10 ; Gurmukhi
+    'Zzzz',  # 0A11..0A12 ; Unknown
+    'Guru',  # 0A13..0A28 ; Gurmukhi
+    'Zzzz',  # 0A29..0A29 ; Unknown
+    'Guru',  # 0A2A..0A30 ; Gurmukhi
+    'Zzzz',  # 0A31..0A31 ; Unknown
+    'Guru',  # 0A32..0A33 ; Gurmukhi
+    'Zzzz',  # 0A34..0A34 ; Unknown
+    'Guru',  # 0A35..0A36 ; Gurmukhi
+    'Zzzz',  # 0A37..0A37 ; Unknown
+    'Guru',  # 0A38..0A39 ; Gurmukhi
+    'Zzzz',  # 0A3A..0A3B ; Unknown
+    'Guru',  # 0A3C..0A3C ; Gurmukhi
+    'Zzzz',  # 0A3D..0A3D ; Unknown
+    'Guru',  # 0A3E..0A42 ; Gurmukhi
+    'Zzzz',  # 0A43..0A46 ; Unknown
+    'Guru',  # 0A47..0A48 ; Gurmukhi
+    'Zzzz',  # 0A49..0A4A ; Unknown
+    'Guru',  # 0A4B..0A4D ; Gurmukhi
+    'Zzzz',  # 0A4E..0A50 ; Unknown
+    'Guru',  # 0A51..0A51 ; Gurmukhi
+    'Zzzz',  # 0A52..0A58 ; Unknown
+    'Guru',  # 0A59..0A5C ; Gurmukhi
+    'Zzzz',  # 0A5D..0A5D ; Unknown
+    'Guru',  # 0A5E..0A5E ; Gurmukhi
+    'Zzzz',  # 0A5F..0A65 ; Unknown
+    'Guru',  # 0A66..0A75 ; Gurmukhi
+    'Zzzz',  # 0A76..0A80 ; Unknown
+    'Gujr',  # 0A81..0A83 ; Gujarati
+    'Zzzz',  # 0A84..0A84 ; Unknown
+    'Gujr',  # 0A85..0A8D ; Gujarati
+    'Zzzz',  # 0A8E..0A8E ; Unknown
+    'Gujr',  # 0A8F..0A91 ; Gujarati
+    'Zzzz',  # 0A92..0A92 ; Unknown
+    'Gujr',  # 0A93..0AA8 ; Gujarati
+    'Zzzz',  # 0AA9..0AA9 ; Unknown
+    'Gujr',  # 0AAA..0AB0 ; Gujarati
+    'Zzzz',  # 0AB1..0AB1 ; Unknown
+    'Gujr',  # 0AB2..0AB3 ; Gujarati
+    'Zzzz',  # 0AB4..0AB4 ; Unknown
+    'Gujr',  # 0AB5..0AB9 ; Gujarati
+    'Zzzz',  # 0ABA..0ABB ; Unknown
+    'Gujr',  # 0ABC..0AC5 ; Gujarati
+    'Zzzz',  # 0AC6..0AC6 ; Unknown
+    'Gujr',  # 0AC7..0AC9 ; Gujarati
+    'Zzzz',  # 0ACA..0ACA ; Unknown
+    'Gujr',  # 0ACB..0ACD ; Gujarati
+    'Zzzz',  # 0ACE..0ACF ; Unknown
+    'Gujr',  # 0AD0..0AD0 ; Gujarati
+    'Zzzz',  # 0AD1..0ADF ; Unknown
+    'Gujr',  # 0AE0..0AE3 ; Gujarati
+    'Zzzz',  # 0AE4..0AE5 ; Unknown
+    'Gujr',  # 0AE6..0AF1 ; Gujarati
+    'Zzzz',  # 0AF2..0AF8 ; Unknown
+    'Gujr',  # 0AF9..0AFF ; Gujarati
+    'Zzzz',  # 0B00..0B00 ; Unknown
+    'Orya',  # 0B01..0B03 ; Oriya
+    'Zzzz',  # 0B04..0B04 ; Unknown
+    'Orya',  # 0B05..0B0C ; Oriya
+    'Zzzz',  # 0B0D..0B0E ; Unknown
+    'Orya',  # 0B0F..0B10 ; Oriya
+    'Zzzz',  # 0B11..0B12 ; Unknown
+    'Orya',  # 0B13..0B28 ; Oriya
+    'Zzzz',  # 0B29..0B29 ; Unknown
+    'Orya',  # 0B2A..0B30 ; Oriya
+    'Zzzz',  # 0B31..0B31 ; Unknown
+    'Orya',  # 0B32..0B33 ; Oriya
+    'Zzzz',  # 0B34..0B34 ; Unknown
+    'Orya',  # 0B35..0B39 ; Oriya
+    'Zzzz',  # 0B3A..0B3B ; Unknown
+    'Orya',  # 0B3C..0B44 ; Oriya
+    'Zzzz',  # 0B45..0B46 ; Unknown
+    'Orya',  # 0B47..0B48 ; Oriya
+    'Zzzz',  # 0B49..0B4A ; Unknown
+    'Orya',  # 0B4B..0B4D ; Oriya
+    'Zzzz',  # 0B4E..0B55 ; Unknown
+    'Orya',  # 0B56..0B57 ; Oriya
+    'Zzzz',  # 0B58..0B5B ; Unknown
+    'Orya',  # 0B5C..0B5D ; Oriya
+    'Zzzz',  # 0B5E..0B5E ; Unknown
+    'Orya',  # 0B5F..0B63 ; Oriya
+    'Zzzz',  # 0B64..0B65 ; Unknown
+    'Orya',  # 0B66..0B77 ; Oriya
+    'Zzzz',  # 0B78..0B81 ; Unknown
+    'Taml',  # 0B82..0B83 ; Tamil
+    'Zzzz',  # 0B84..0B84 ; Unknown
+    'Taml',  # 0B85..0B8A ; Tamil
+    'Zzzz',  # 0B8B..0B8D ; Unknown
+    'Taml',  # 0B8E..0B90 ; Tamil
+    'Zzzz',  # 0B91..0B91 ; Unknown
+    'Taml',  # 0B92..0B95 ; Tamil
+    'Zzzz',  # 0B96..0B98 ; Unknown
+    'Taml',  # 0B99..0B9A ; Tamil
+    'Zzzz',  # 0B9B..0B9B ; Unknown
+    'Taml',  # 0B9C..0B9C ; Tamil
+    'Zzzz',  # 0B9D..0B9D ; Unknown
+    'Taml',  # 0B9E..0B9F ; Tamil
+    'Zzzz',  # 0BA0..0BA2 ; Unknown
+    'Taml',  # 0BA3..0BA4 ; Tamil
+    'Zzzz',  # 0BA5..0BA7 ; Unknown
+    'Taml',  # 0BA8..0BAA ; Tamil
+    'Zzzz',  # 0BAB..0BAD ; Unknown
+    'Taml',  # 0BAE..0BB9 ; Tamil
+    'Zzzz',  # 0BBA..0BBD ; Unknown
+    'Taml',  # 0BBE..0BC2 ; Tamil
+    'Zzzz',  # 0BC3..0BC5 ; Unknown
+    'Taml',  # 0BC6..0BC8 ; Tamil
+    'Zzzz',  # 0BC9..0BC9 ; Unknown
+    'Taml',  # 0BCA..0BCD ; Tamil
+    'Zzzz',  # 0BCE..0BCF ; Unknown
+    'Taml',  # 0BD0..0BD0 ; Tamil
+    'Zzzz',  # 0BD1..0BD6 ; Unknown
+    'Taml',  # 0BD7..0BD7 ; Tamil
+    'Zzzz',  # 0BD8..0BE5 ; Unknown
+    'Taml',  # 0BE6..0BFA ; Tamil
+    'Zzzz',  # 0BFB..0BFF ; Unknown
+    'Telu',  # 0C00..0C03 ; Telugu
+    'Zzzz',  # 0C04..0C04 ; Unknown
+    'Telu',  # 0C05..0C0C ; Telugu
+    'Zzzz',  # 0C0D..0C0D ; Unknown
+    'Telu',  # 0C0E..0C10 ; Telugu
+    'Zzzz',  # 0C11..0C11 ; Unknown
+    'Telu',  # 0C12..0C28 ; Telugu
+    'Zzzz',  # 0C29..0C29 ; Unknown
+    'Telu',  # 0C2A..0C39 ; Telugu
+    'Zzzz',  # 0C3A..0C3C ; Unknown
+    'Telu',  # 0C3D..0C44 ; Telugu
+    'Zzzz',  # 0C45..0C45 ; Unknown
+    'Telu',  # 0C46..0C48 ; Telugu
+    'Zzzz',  # 0C49..0C49 ; Unknown
+    'Telu',  # 0C4A..0C4D ; Telugu
+    'Zzzz',  # 0C4E..0C54 ; Unknown
+    'Telu',  # 0C55..0C56 ; Telugu
+    'Zzzz',  # 0C57..0C57 ; Unknown
+    'Telu',  # 0C58..0C5A ; Telugu
+    'Zzzz',  # 0C5B..0C5F ; Unknown
+    'Telu',  # 0C60..0C63 ; Telugu
+    'Zzzz',  # 0C64..0C65 ; Unknown
+    'Telu',  # 0C66..0C6F ; Telugu
+    'Zzzz',  # 0C70..0C77 ; Unknown
+    'Telu',  # 0C78..0C7F ; Telugu
+    'Knda',  # 0C80..0C83 ; Kannada
+    'Zzzz',  # 0C84..0C84 ; Unknown
+    'Knda',  # 0C85..0C8C ; Kannada
+    'Zzzz',  # 0C8D..0C8D ; Unknown
+    'Knda',  # 0C8E..0C90 ; Kannada
+    'Zzzz',  # 0C91..0C91 ; Unknown
+    'Knda',  # 0C92..0CA8 ; Kannada
+    'Zzzz',  # 0CA9..0CA9 ; Unknown
+    'Knda',  # 0CAA..0CB3 ; Kannada
+    'Zzzz',  # 0CB4..0CB4 ; Unknown
+    'Knda',  # 0CB5..0CB9 ; Kannada
+    'Zzzz',  # 0CBA..0CBB ; Unknown
+    'Knda',  # 0CBC..0CC4 ; Kannada
+    'Zzzz',  # 0CC5..0CC5 ; Unknown
+    'Knda',  # 0CC6..0CC8 ; Kannada
+    'Zzzz',  # 0CC9..0CC9 ; Unknown
+    'Knda',  # 0CCA..0CCD ; Kannada
+    'Zzzz',  # 0CCE..0CD4 ; Unknown
+    'Knda',  # 0CD5..0CD6 ; Kannada
+    'Zzzz',  # 0CD7..0CDD ; Unknown
+    'Knda',  # 0CDE..0CDE ; Kannada
+    'Zzzz',  # 0CDF..0CDF ; Unknown
+    'Knda',  # 0CE0..0CE3 ; Kannada
+    'Zzzz',  # 0CE4..0CE5 ; Unknown
+    'Knda',  # 0CE6..0CEF ; Kannada
+    'Zzzz',  # 0CF0..0CF0 ; Unknown
+    'Knda',  # 0CF1..0CF2 ; Kannada
+    'Zzzz',  # 0CF3..0CFF ; Unknown
+    'Mlym',  # 0D00..0D03 ; Malayalam
+    'Zzzz',  # 0D04..0D04 ; Unknown
+    'Mlym',  # 0D05..0D0C ; Malayalam
+    'Zzzz',  # 0D0D..0D0D ; Unknown
+    'Mlym',  # 0D0E..0D10 ; Malayalam
+    'Zzzz',  # 0D11..0D11 ; Unknown
+    'Mlym',  # 0D12..0D44 ; Malayalam
+    'Zzzz',  # 0D45..0D45 ; Unknown
+    'Mlym',  # 0D46..0D48 ; Malayalam
+    'Zzzz',  # 0D49..0D49 ; Unknown
+    'Mlym',  # 0D4A..0D4F ; Malayalam
+    'Zzzz',  # 0D50..0D53 ; Unknown
+    'Mlym',  # 0D54..0D63 ; Malayalam
+    'Zzzz',  # 0D64..0D65 ; Unknown
+    'Mlym',  # 0D66..0D7F ; Malayalam
+    'Zzzz',  # 0D80..0D81 ; Unknown
+    'Sinh',  # 0D82..0D83 ; Sinhala
+    'Zzzz',  # 0D84..0D84 ; Unknown
+    'Sinh',  # 0D85..0D96 ; Sinhala
+    'Zzzz',  # 0D97..0D99 ; Unknown
+    'Sinh',  # 0D9A..0DB1 ; Sinhala
+    'Zzzz',  # 0DB2..0DB2 ; Unknown
+    'Sinh',  # 0DB3..0DBB ; Sinhala
+    'Zzzz',  # 0DBC..0DBC ; Unknown
+    'Sinh',  # 0DBD..0DBD ; Sinhala
+    'Zzzz',  # 0DBE..0DBF ; Unknown
+    'Sinh',  # 0DC0..0DC6 ; Sinhala
+    'Zzzz',  # 0DC7..0DC9 ; Unknown
+    'Sinh',  # 0DCA..0DCA ; Sinhala
+    'Zzzz',  # 0DCB..0DCE ; Unknown
+    'Sinh',  # 0DCF..0DD4 ; Sinhala
+    'Zzzz',  # 0DD5..0DD5 ; Unknown
+    'Sinh',  # 0DD6..0DD6 ; Sinhala
+    'Zzzz',  # 0DD7..0DD7 ; Unknown
+    'Sinh',  # 0DD8..0DDF ; Sinhala
+    'Zzzz',  # 0DE0..0DE5 ; Unknown
+    'Sinh',  # 0DE6..0DEF ; Sinhala
+    'Zzzz',  # 0DF0..0DF1 ; Unknown
+    'Sinh',  # 0DF2..0DF4 ; Sinhala
+    'Zzzz',  # 0DF5..0E00 ; Unknown
+    'Thai',  # 0E01..0E3A ; Thai
+    'Zzzz',  # 0E3B..0E3E ; Unknown
+    'Zyyy',  # 0E3F..0E3F ; Common
+    'Thai',  # 0E40..0E5B ; Thai
+    'Zzzz',  # 0E5C..0E80 ; Unknown
+    'Laoo',  # 0E81..0E82 ; Lao
+    'Zzzz',  # 0E83..0E83 ; Unknown
+    'Laoo',  # 0E84..0E84 ; Lao
+    'Zzzz',  # 0E85..0E86 ; Unknown
+    'Laoo',  # 0E87..0E88 ; Lao
+    'Zzzz',  # 0E89..0E89 ; Unknown
+    'Laoo',  # 0E8A..0E8A ; Lao
+    'Zzzz',  # 0E8B..0E8C ; Unknown
+    'Laoo',  # 0E8D..0E8D ; Lao
+    'Zzzz',  # 0E8E..0E93 ; Unknown
+    'Laoo',  # 0E94..0E97 ; Lao
+    'Zzzz',  # 0E98..0E98 ; Unknown
+    'Laoo',  # 0E99..0E9F ; Lao
+    'Zzzz',  # 0EA0..0EA0 ; Unknown
+    'Laoo',  # 0EA1..0EA3 ; Lao
+    'Zzzz',  # 0EA4..0EA4 ; Unknown
+    'Laoo',  # 0EA5..0EA5 ; Lao
+    'Zzzz',  # 0EA6..0EA6 ; Unknown
+    'Laoo',  # 0EA7..0EA7 ; Lao
+    'Zzzz',  # 0EA8..0EA9 ; Unknown
+    'Laoo',  # 0EAA..0EAB ; Lao
+    'Zzzz',  # 0EAC..0EAC ; Unknown
+    'Laoo',  # 0EAD..0EB9 ; Lao
+    'Zzzz',  # 0EBA..0EBA ; Unknown
+    'Laoo',  # 0EBB..0EBD ; Lao
+    'Zzzz',  # 0EBE..0EBF ; Unknown
+    'Laoo',  # 0EC0..0EC4 ; Lao
+    'Zzzz',  # 0EC5..0EC5 ; Unknown
+    'Laoo',  # 0EC6..0EC6 ; Lao
+    'Zzzz',  # 0EC7..0EC7 ; Unknown
+    'Laoo',  # 0EC8..0ECD ; Lao
+    'Zzzz',  # 0ECE..0ECF ; Unknown
+    'Laoo',  # 0ED0..0ED9 ; Lao
+    'Zzzz',  # 0EDA..0EDB ; Unknown
+    'Laoo',  # 0EDC..0EDF ; Lao
+    'Zzzz',  # 0EE0..0EFF ; Unknown
+    'Tibt',  # 0F00..0F47 ; Tibetan
+    'Zzzz',  # 0F48..0F48 ; Unknown
+    'Tibt',  # 0F49..0F6C ; Tibetan
+    'Zzzz',  # 0F6D..0F70 ; Unknown
+    'Tibt',  # 0F71..0F97 ; Tibetan
+    'Zzzz',  # 0F98..0F98 ; Unknown
+    'Tibt',  # 0F99..0FBC ; Tibetan
+    'Zzzz',  # 0FBD..0FBD ; Unknown
+    'Tibt',  # 0FBE..0FCC ; Tibetan
+    'Zzzz',  # 0FCD..0FCD ; Unknown
+    'Tibt',  # 0FCE..0FD4 ; Tibetan
+    'Zyyy',  # 0FD5..0FD8 ; Common
+    'Tibt',  # 0FD9..0FDA ; Tibetan
+    'Zzzz',  # 0FDB..0FFF ; Unknown
+    'Mymr',  # 1000..109F ; Myanmar
+    'Geor',  # 10A0..10C5 ; Georgian
+    'Zzzz',  # 10C6..10C6 ; Unknown
+    'Geor',  # 10C7..10C7 ; Georgian
+    'Zzzz',  # 10C8..10CC ; Unknown
+    'Geor',  # 10CD..10CD ; Georgian
+    'Zzzz',  # 10CE..10CF ; Unknown
+    'Geor',  # 10D0..10FA ; Georgian
+    'Zyyy',  # 10FB..10FB ; Common
+    'Geor',  # 10FC..10FF ; Georgian
+    'Hang',  # 1100..11FF ; Hangul
+    'Ethi',  # 1200..1248 ; Ethiopic
+    'Zzzz',  # 1249..1249 ; Unknown
+    'Ethi',  # 124A..124D ; Ethiopic
+    'Zzzz',  # 124E..124F ; Unknown
+    'Ethi',  # 1250..1256 ; Ethiopic
+    'Zzzz',  # 1257..1257 ; Unknown
+    'Ethi',  # 1258..1258 ; Ethiopic
+    'Zzzz',  # 1259..1259 ; Unknown
+    'Ethi',  # 125A..125D ; Ethiopic
+    'Zzzz',  # 125E..125F ; Unknown
+    'Ethi',  # 1260..1288 ; Ethiopic
+    'Zzzz',  # 1289..1289 ; Unknown
+    'Ethi',  # 128A..128D ; Ethiopic
+    'Zzzz',  # 128E..128F ; Unknown
+    'Ethi',  # 1290..12B0 ; Ethiopic
+    'Zzzz',  # 12B1..12B1 ; Unknown
+    'Ethi',  # 12B2..12B5 ; Ethiopic
+    'Zzzz',  # 12B6..12B7 ; Unknown
+    'Ethi',  # 12B8..12BE ; Ethiopic
+    'Zzzz',  # 12BF..12BF ; Unknown
+    'Ethi',  # 12C0..12C0 ; Ethiopic
+    'Zzzz',  # 12C1..12C1 ; Unknown
+    'Ethi',  # 12C2..12C5 ; Ethiopic
+    'Zzzz',  # 12C6..12C7 ; Unknown
+    'Ethi',  # 12C8..12D6 ; Ethiopic
+    'Zzzz',  # 12D7..12D7 ; Unknown
+    'Ethi',  # 12D8..1310 ; Ethiopic
+    'Zzzz',  # 1311..1311 ; Unknown
+    'Ethi',  # 1312..1315 ; Ethiopic
+    'Zzzz',  # 1316..1317 ; Unknown
+    'Ethi',  # 1318..135A ; Ethiopic
+    'Zzzz',  # 135B..135C ; Unknown
+    'Ethi',  # 135D..137C ; Ethiopic
+    'Zzzz',  # 137D..137F ; Unknown
+    'Ethi',  # 1380..1399 ; Ethiopic
+    'Zzzz',  # 139A..139F ; Unknown
+    'Cher',  # 13A0..13F5 ; Cherokee
+    'Zzzz',  # 13F6..13F7 ; Unknown
+    'Cher',  # 13F8..13FD ; Cherokee
+    'Zzzz',  # 13FE..13FF ; Unknown
+    'Cans',  # 1400..167F ; Canadian_Aboriginal
+    'Ogam',  # 1680..169C ; Ogham
+    'Zzzz',  # 169D..169F ; Unknown
+    'Runr',  # 16A0..16EA ; Runic
+    'Zyyy',  # 16EB..16ED ; Common
+    'Runr',  # 16EE..16F8 ; Runic
+    'Zzzz',  # 16F9..16FF ; Unknown
+    'Tglg',  # 1700..170C ; Tagalog
+    'Zzzz',  # 170D..170D ; Unknown
+    'Tglg',  # 170E..1714 ; Tagalog
+    'Zzzz',  # 1715..171F ; Unknown
+    'Hano',  # 1720..1734 ; Hanunoo
+    'Zyyy',  # 1735..1736 ; Common
+    'Zzzz',  # 1737..173F ; Unknown
+    'Buhd',  # 1740..1753 ; Buhid
+    'Zzzz',  # 1754..175F ; Unknown
+    'Tagb',  # 1760..176C ; Tagbanwa
+    'Zzzz',  # 176D..176D ; Unknown
+    'Tagb',  # 176E..1770 ; Tagbanwa
+    'Zzzz',  # 1771..1771 ; Unknown
+    'Tagb',  # 1772..1773 ; Tagbanwa
+    'Zzzz',  # 1774..177F ; Unknown
+    'Khmr',  # 1780..17DD ; Khmer
+    'Zzzz',  # 17DE..17DF ; Unknown
+    'Khmr',  # 17E0..17E9 ; Khmer
+    'Zzzz',  # 17EA..17EF ; Unknown
+    'Khmr',  # 17F0..17F9 ; Khmer
+    'Zzzz',  # 17FA..17FF ; Unknown
+    'Mong',  # 1800..1801 ; Mongolian
+    'Zyyy',  # 1802..1803 ; Common
+    'Mong',  # 1804..1804 ; Mongolian
+    'Zyyy',  # 1805..1805 ; Common
+    'Mong',  # 1806..180E ; Mongolian
+    'Zzzz',  # 180F..180F ; Unknown
+    'Mong',  # 1810..1819 ; Mongolian
+    'Zzzz',  # 181A..181F ; Unknown
+    'Mong',  # 1820..1877 ; Mongolian
+    'Zzzz',  # 1878..187F ; Unknown
+    'Mong',  # 1880..18AA ; Mongolian
+    'Zzzz',  # 18AB..18AF ; Unknown
+    'Cans',  # 18B0..18F5 ; Canadian_Aboriginal
+    'Zzzz',  # 18F6..18FF ; Unknown
+    'Limb',  # 1900..191E ; Limbu
+    'Zzzz',  # 191F..191F ; Unknown
+    'Limb',  # 1920..192B ; Limbu
+    'Zzzz',  # 192C..192F ; Unknown
+    'Limb',  # 1930..193B ; Limbu
+    'Zzzz',  # 193C..193F ; Unknown
+    'Limb',  # 1940..1940 ; Limbu
+    'Zzzz',  # 1941..1943 ; Unknown
+    'Limb',  # 1944..194F ; Limbu
+    'Tale',  # 1950..196D ; Tai_Le
+    'Zzzz',  # 196E..196F ; Unknown
+    'Tale',  # 1970..1974 ; Tai_Le
+    'Zzzz',  # 1975..197F ; Unknown
+    'Talu',  # 1980..19AB ; New_Tai_Lue
+    'Zzzz',  # 19AC..19AF ; Unknown
+    'Talu',  # 19B0..19C9 ; New_Tai_Lue
+    'Zzzz',  # 19CA..19CF ; Unknown
+    'Talu',  # 19D0..19DA ; New_Tai_Lue
+    'Zzzz',  # 19DB..19DD ; Unknown
+    'Talu',  # 19DE..19DF ; New_Tai_Lue
+    'Khmr',  # 19E0..19FF ; Khmer
+    'Bugi',  # 1A00..1A1B ; Buginese
+    'Zzzz',  # 1A1C..1A1D ; Unknown
+    'Bugi',  # 1A1E..1A1F ; Buginese
+    'Lana',  # 1A20..1A5E ; Tai_Tham
+    'Zzzz',  # 1A5F..1A5F ; Unknown
+    'Lana',  # 1A60..1A7C ; Tai_Tham
+    'Zzzz',  # 1A7D..1A7E ; Unknown
+    'Lana',  # 1A7F..1A89 ; Tai_Tham
+    'Zzzz',  # 1A8A..1A8F ; Unknown
+    'Lana',  # 1A90..1A99 ; Tai_Tham
+    'Zzzz',  # 1A9A..1A9F ; Unknown
+    'Lana',  # 1AA0..1AAD ; Tai_Tham
+    'Zzzz',  # 1AAE..1AAF ; Unknown
+    'Zinh',  # 1AB0..1ABE ; Inherited
+    'Zzzz',  # 1ABF..1AFF ; Unknown
+    'Bali',  # 1B00..1B4B ; Balinese
+    'Zzzz',  # 1B4C..1B4F ; Unknown
+    'Bali',  # 1B50..1B7C ; Balinese
+    'Zzzz',  # 1B7D..1B7F ; Unknown
+    'Sund',  # 1B80..1BBF ; Sundanese
+    'Batk',  # 1BC0..1BF3 ; Batak
+    'Zzzz',  # 1BF4..1BFB ; Unknown
+    'Batk',  # 1BFC..1BFF ; Batak
+    'Lepc',  # 1C00..1C37 ; Lepcha
+    'Zzzz',  # 1C38..1C3A ; Unknown
+    'Lepc',  # 1C3B..1C49 ; Lepcha
+    'Zzzz',  # 1C4A..1C4C ; Unknown
+    'Lepc',  # 1C4D..1C4F ; Lepcha
+    'Olck',  # 1C50..1C7F ; Ol_Chiki
+    'Cyrl',  # 1C80..1C88 ; Cyrillic
+    'Zzzz',  # 1C89..1CBF ; Unknown
+    'Sund',  # 1CC0..1CC7 ; Sundanese
+    'Zzzz',  # 1CC8..1CCF ; Unknown
+    'Zinh',  # 1CD0..1CD2 ; Inherited
+    'Zyyy',  # 1CD3..1CD3 ; Common
+    'Zinh',  # 1CD4..1CE0 ; Inherited
+    'Zyyy',  # 1CE1..1CE1 ; Common
+    'Zinh',  # 1CE2..1CE8 ; Inherited
+    'Zyyy',  # 1CE9..1CEC ; Common
+    'Zinh',  # 1CED..1CED ; Inherited
+    'Zyyy',  # 1CEE..1CF3 ; Common
+    'Zinh',  # 1CF4..1CF4 ; Inherited
+    'Zyyy',  # 1CF5..1CF7 ; Common
+    'Zinh',  # 1CF8..1CF9 ; Inherited
+    'Zzzz',  # 1CFA..1CFF ; Unknown
+    'Latn',  # 1D00..1D25 ; Latin
+    'Grek',  # 1D26..1D2A ; Greek
+    'Cyrl',  # 1D2B..1D2B ; Cyrillic
+    'Latn',  # 1D2C..1D5C ; Latin
+    'Grek',  # 1D5D..1D61 ; Greek
+    'Latn',  # 1D62..1D65 ; Latin
+    'Grek',  # 1D66..1D6A ; Greek
+    'Latn',  # 1D6B..1D77 ; Latin
+    'Cyrl',  # 1D78..1D78 ; Cyrillic
+    'Latn',  # 1D79..1DBE ; Latin
+    'Grek',  # 1DBF..1DBF ; Greek
+    'Zinh',  # 1DC0..1DF9 ; Inherited
+    'Zzzz',  # 1DFA..1DFA ; Unknown
+    'Zinh',  # 1DFB..1DFF ; Inherited
+    'Latn',  # 1E00..1EFF ; Latin
+    'Grek',  # 1F00..1F15 ; Greek
+    'Zzzz',  # 1F16..1F17 ; Unknown
+    'Grek',  # 1F18..1F1D ; Greek
+    'Zzzz',  # 1F1E..1F1F ; Unknown
+    'Grek',  # 1F20..1F45 ; Greek
+    'Zzzz',  # 1F46..1F47 ; Unknown
+    'Grek',  # 1F48..1F4D ; Greek
+    'Zzzz',  # 1F4E..1F4F ; Unknown
+    'Grek',  # 1F50..1F57 ; Greek
+    'Zzzz',  # 1F58..1F58 ; Unknown
+    'Grek',  # 1F59..1F59 ; Greek
+    'Zzzz',  # 1F5A..1F5A ; Unknown
+    'Grek',  # 1F5B..1F5B ; Greek
+    'Zzzz',  # 1F5C..1F5C ; Unknown
+    'Grek',  # 1F5D..1F5D ; Greek
+    'Zzzz',  # 1F5E..1F5E ; Unknown
+    'Grek',  # 1F5F..1F7D ; Greek
+    'Zzzz',  # 1F7E..1F7F ; Unknown
+    'Grek',  # 1F80..1FB4 ; Greek
+    'Zzzz',  # 1FB5..1FB5 ; Unknown
+    'Grek',  # 1FB6..1FC4 ; Greek
+    'Zzzz',  # 1FC5..1FC5 ; Unknown
+    'Grek',  # 1FC6..1FD3 ; Greek
+    'Zzzz',  # 1FD4..1FD5 ; Unknown
+    'Grek',  # 1FD6..1FDB ; Greek
+    'Zzzz',  # 1FDC..1FDC ; Unknown
+    'Grek',  # 1FDD..1FEF ; Greek
+    'Zzzz',  # 1FF0..1FF1 ; Unknown
+    'Grek',  # 1FF2..1FF4 ; Greek
+    'Zzzz',  # 1FF5..1FF5 ; Unknown
+    'Grek',  # 1FF6..1FFE ; Greek
+    'Zzzz',  # 1FFF..1FFF ; Unknown
+    'Zyyy',  # 2000..200B ; Common
+    'Zinh',  # 200C..200D ; Inherited
+    'Zyyy',  # 200E..2064 ; Common
+    'Zzzz',  # 2065..2065 ; Unknown
+    'Zyyy',  # 2066..2070 ; Common
+    'Latn',  # 2071..2071 ; Latin
+    'Zzzz',  # 2072..2073 ; Unknown
+    'Zyyy',  # 2074..207E ; Common
+    'Latn',  # 207F..207F ; Latin
+    'Zyyy',  # 2080..208E ; Common
+    'Zzzz',  # 208F..208F ; Unknown
+    'Latn',  # 2090..209C ; Latin
+    'Zzzz',  # 209D..209F ; Unknown
+    'Zyyy',  # 20A0..20BF ; Common
+    'Zzzz',  # 20C0..20CF ; Unknown
+    'Zinh',  # 20D0..20F0 ; Inherited
+    'Zzzz',  # 20F1..20FF ; Unknown
+    'Zyyy',  # 2100..2125 ; Common
+    'Grek',  # 2126..2126 ; Greek
+    'Zyyy',  # 2127..2129 ; Common
+    'Latn',  # 212A..212B ; Latin
+    'Zyyy',  # 212C..2131 ; Common
+    'Latn',  # 2132..2132 ; Latin
+    'Zyyy',  # 2133..214D ; Common
+    'Latn',  # 214E..214E ; Latin
+    'Zyyy',  # 214F..215F ; Common
+    'Latn',  # 2160..2188 ; Latin
+    'Zyyy',  # 2189..218B ; Common
+    'Zzzz',  # 218C..218F ; Unknown
+    'Zyyy',  # 2190..2426 ; Common
+    'Zzzz',  # 2427..243F ; Unknown
+    'Zyyy',  # 2440..244A ; Common
+    'Zzzz',  # 244B..245F ; Unknown
+    'Zyyy',  # 2460..27FF ; Common
+    'Brai',  # 2800..28FF ; Braille
+    'Zyyy',  # 2900..2B73 ; Common
+    'Zzzz',  # 2B74..2B75 ; Unknown
+    'Zyyy',  # 2B76..2B95 ; Common
+    'Zzzz',  # 2B96..2B97 ; Unknown
+    'Zyyy',  # 2B98..2BB9 ; Common
+    'Zzzz',  # 2BBA..2BBC ; Unknown
+    'Zyyy',  # 2BBD..2BC8 ; Common
+    'Zzzz',  # 2BC9..2BC9 ; Unknown
+    'Zyyy',  # 2BCA..2BD2 ; Common
+    'Zzzz',  # 2BD3..2BEB ; Unknown
+    'Zyyy',  # 2BEC..2BEF ; Common
+    'Zzzz',  # 2BF0..2BFF ; Unknown
+    'Glag',  # 2C00..2C2E ; Glagolitic
+    'Zzzz',  # 2C2F..2C2F ; Unknown
+    'Glag',  # 2C30..2C5E ; Glagolitic
+    'Zzzz',  # 2C5F..2C5F ; Unknown
+    'Latn',  # 2C60..2C7F ; Latin
+    'Copt',  # 2C80..2CF3 ; Coptic
+    'Zzzz',  # 2CF4..2CF8 ; Unknown
+    'Copt',  # 2CF9..2CFF ; Coptic
+    'Geor',  # 2D00..2D25 ; Georgian
+    'Zzzz',  # 2D26..2D26 ; Unknown
+    'Geor',  # 2D27..2D27 ; Georgian
+    'Zzzz',  # 2D28..2D2C ; Unknown
+    'Geor',  # 2D2D..2D2D ; Georgian
+    'Zzzz',  # 2D2E..2D2F ; Unknown
+    'Tfng',  # 2D30..2D67 ; Tifinagh
+    'Zzzz',  # 2D68..2D6E ; Unknown
+    'Tfng',  # 2D6F..2D70 ; Tifinagh
+    'Zzzz',  # 2D71..2D7E ; Unknown
+    'Tfng',  # 2D7F..2D7F ; Tifinagh
+    'Ethi',  # 2D80..2D96 ; Ethiopic
+    'Zzzz',  # 2D97..2D9F ; Unknown
+    'Ethi',  # 2DA0..2DA6 ; Ethiopic
+    'Zzzz',  # 2DA7..2DA7 ; Unknown
+    'Ethi',  # 2DA8..2DAE ; Ethiopic
+    'Zzzz',  # 2DAF..2DAF ; Unknown
+    'Ethi',  # 2DB0..2DB6 ; Ethiopic
+    'Zzzz',  # 2DB7..2DB7 ; Unknown
+    'Ethi',  # 2DB8..2DBE ; Ethiopic
+    'Zzzz',  # 2DBF..2DBF ; Unknown
+    'Ethi',  # 2DC0..2DC6 ; Ethiopic
+    'Zzzz',  # 2DC7..2DC7 ; Unknown
+    'Ethi',  # 2DC8..2DCE ; Ethiopic
+    'Zzzz',  # 2DCF..2DCF ; Unknown
+    'Ethi',  # 2DD0..2DD6 ; Ethiopic
+    'Zzzz',  # 2DD7..2DD7 ; Unknown
+    'Ethi',  # 2DD8..2DDE ; Ethiopic
+    'Zzzz',  # 2DDF..2DDF ; Unknown
+    'Cyrl',  # 2DE0..2DFF ; Cyrillic
+    'Zyyy',  # 2E00..2E49 ; Common
+    'Zzzz',  # 2E4A..2E7F ; Unknown
+    'Hani',  # 2E80..2E99 ; Han
+    'Zzzz',  # 2E9A..2E9A ; Unknown
+    'Hani',  # 2E9B..2EF3 ; Han
+    'Zzzz',  # 2EF4..2EFF ; Unknown
+    'Hani',  # 2F00..2FD5 ; Han
+    'Zzzz',  # 2FD6..2FEF ; Unknown
+    'Zyyy',  # 2FF0..2FFB ; Common
+    'Zzzz',  # 2FFC..2FFF ; Unknown
+    'Zyyy',  # 3000..3004 ; Common
+    'Hani',  # 3005..3005 ; Han
+    'Zyyy',  # 3006..3006 ; Common
+    'Hani',  # 3007..3007 ; Han
+    'Zyyy',  # 3008..3020 ; Common
+    'Hani',  # 3021..3029 ; Han
+    'Zinh',  # 302A..302D ; Inherited
+    'Hang',  # 302E..302F ; Hangul
+    'Zyyy',  # 3030..3037 ; Common
+    'Hani',  # 3038..303B ; Han
+    'Zyyy',  # 303C..303F ; Common
+    'Zzzz',  # 3040..3040 ; Unknown
+    'Hira',  # 3041..3096 ; Hiragana
+    'Zzzz',  # 3097..3098 ; Unknown
+    'Zinh',  # 3099..309A ; Inherited
+    'Zyyy',  # 309B..309C ; Common
+    'Hira',  # 309D..309F ; Hiragana
+    'Zyyy',  # 30A0..30A0 ; Common
+    'Kana',  # 30A1..30FA ; Katakana
+    'Zyyy',  # 30FB..30FC ; Common
+    'Kana',  # 30FD..30FF ; Katakana
+    'Zzzz',  # 3100..3104 ; Unknown
+    'Bopo',  # 3105..312E ; Bopomofo
+    'Zzzz',  # 312F..3130 ; Unknown
+    'Hang',  # 3131..318E ; Hangul
+    'Zzzz',  # 318F..318F ; Unknown
+    'Zyyy',  # 3190..319F ; Common
+    'Bopo',  # 31A0..31BA ; Bopomofo
+    'Zzzz',  # 31BB..31BF ; Unknown
+    'Zyyy',  # 31C0..31E3 ; Common
+    'Zzzz',  # 31E4..31EF ; Unknown
+    'Kana',  # 31F0..31FF ; Katakana
+    'Hang',  # 3200..321E ; Hangul
+    'Zzzz',  # 321F..321F ; Unknown
+    'Zyyy',  # 3220..325F ; Common
+    'Hang',  # 3260..327E ; Hangul
+    'Zyyy',  # 327F..32CF ; Common
+    'Kana',  # 32D0..32FE ; Katakana
+    'Zzzz',  # 32FF..32FF ; Unknown
+    'Kana',  # 3300..3357 ; Katakana
+    'Zyyy',  # 3358..33FF ; Common
+    'Hani',  # 3400..4DB5 ; Han
+    'Zzzz',  # 4DB6..4DBF ; Unknown
+    'Zyyy',  # 4DC0..4DFF ; Common
+    'Hani',  # 4E00..9FEA ; Han
+    'Zzzz',  # 9FEB..9FFF ; Unknown
+    'Yiii',  # A000..A48C ; Yi
+    'Zzzz',  # A48D..A48F ; Unknown
+    'Yiii',  # A490..A4C6 ; Yi
+    'Zzzz',  # A4C7..A4CF ; Unknown
+    'Lisu',  # A4D0..A4FF ; Lisu
+    'Vaii',  # A500..A62B ; Vai
+    'Zzzz',  # A62C..A63F ; Unknown
+    'Cyrl',  # A640..A69F ; Cyrillic
+    'Bamu',  # A6A0..A6F7 ; Bamum
+    'Zzzz',  # A6F8..A6FF ; Unknown
+    'Zyyy',  # A700..A721 ; Common
+    'Latn',  # A722..A787 ; Latin
+    'Zyyy',  # A788..A78A ; Common
+    'Latn',  # A78B..A7AE ; Latin
+    'Zzzz',  # A7AF..A7AF ; Unknown
+    'Latn',  # A7B0..A7B7 ; Latin
+    'Zzzz',  # A7B8..A7F6 ; Unknown
+    'Latn',  # A7F7..A7FF ; Latin
+    'Sylo',  # A800..A82B ; Syloti_Nagri
+    'Zzzz',  # A82C..A82F ; Unknown
+    'Zyyy',  # A830..A839 ; Common
+    'Zzzz',  # A83A..A83F ; Unknown
+    'Phag',  # A840..A877 ; Phags_Pa
+    'Zzzz',  # A878..A87F ; Unknown
+    'Saur',  # A880..A8C5 ; Saurashtra
+    'Zzzz',  # A8C6..A8CD ; Unknown
+    'Saur',  # A8CE..A8D9 ; Saurashtra
+    'Zzzz',  # A8DA..A8DF ; Unknown
+    'Deva',  # A8E0..A8FD ; Devanagari
+    'Zzzz',  # A8FE..A8FF ; Unknown
+    'Kali',  # A900..A92D ; Kayah_Li
+    'Zyyy',  # A92E..A92E ; Common
+    'Kali',  # A92F..A92F ; Kayah_Li
+    'Rjng',  # A930..A953 ; Rejang
+    'Zzzz',  # A954..A95E ; Unknown
+    'Rjng',  # A95F..A95F ; Rejang
+    'Hang',  # A960..A97C ; Hangul
+    'Zzzz',  # A97D..A97F ; Unknown
+    'Java',  # A980..A9CD ; Javanese
+    'Zzzz',  # A9CE..A9CE ; Unknown
+    'Zyyy',  # A9CF..A9CF ; Common
+    'Java',  # A9D0..A9D9 ; Javanese
+    'Zzzz',  # A9DA..A9DD ; Unknown
+    'Java',  # A9DE..A9DF ; Javanese
+    'Mymr',  # A9E0..A9FE ; Myanmar
+    'Zzzz',  # A9FF..A9FF ; Unknown
+    'Cham',  # AA00..AA36 ; Cham
+    'Zzzz',  # AA37..AA3F ; Unknown
+    'Cham',  # AA40..AA4D ; Cham
+    'Zzzz',  # AA4E..AA4F ; Unknown
+    'Cham',  # AA50..AA59 ; Cham
+    'Zzzz',  # AA5A..AA5B ; Unknown
+    'Cham',  # AA5C..AA5F ; Cham
+    'Mymr',  # AA60..AA7F ; Myanmar
+    'Tavt',  # AA80..AAC2 ; Tai_Viet
+    'Zzzz',  # AAC3..AADA ; Unknown
+    'Tavt',  # AADB..AADF ; Tai_Viet
+    'Mtei',  # AAE0..AAF6 ; Meetei_Mayek
+    'Zzzz',  # AAF7..AB00 ; Unknown
+    'Ethi',  # AB01..AB06 ; Ethiopic
+    'Zzzz',  # AB07..AB08 ; Unknown
+    'Ethi',  # AB09..AB0E ; Ethiopic
+    'Zzzz',  # AB0F..AB10 ; Unknown
+    'Ethi',  # AB11..AB16 ; Ethiopic
+    'Zzzz',  # AB17..AB1F ; Unknown
+    'Ethi',  # AB20..AB26 ; Ethiopic
+    'Zzzz',  # AB27..AB27 ; Unknown
+    'Ethi',  # AB28..AB2E ; Ethiopic
+    'Zzzz',  # AB2F..AB2F ; Unknown
+    'Latn',  # AB30..AB5A ; Latin
+    'Zyyy',  # AB5B..AB5B ; Common
+    'Latn',  # AB5C..AB64 ; Latin
+    'Grek',  # AB65..AB65 ; Greek
+    'Zzzz',  # AB66..AB6F ; Unknown
+    'Cher',  # AB70..ABBF ; Cherokee
+    'Mtei',  # ABC0..ABED ; Meetei_Mayek
+    'Zzzz',  # ABEE..ABEF ; Unknown
+    'Mtei',  # ABF0..ABF9 ; Meetei_Mayek
+    'Zzzz',  # ABFA..ABFF ; Unknown
+    'Hang',  # AC00..D7A3 ; Hangul
+    'Zzzz',  # D7A4..D7AF ; Unknown
+    'Hang',  # D7B0..D7C6 ; Hangul
+    'Zzzz',  # D7C7..D7CA ; Unknown
+    'Hang',  # D7CB..D7FB ; Hangul
+    'Zzzz',  # D7FC..F8FF ; Unknown
+    'Hani',  # F900..FA6D ; Han
+    'Zzzz',  # FA6E..FA6F ; Unknown
+    'Hani',  # FA70..FAD9 ; Han
+    'Zzzz',  # FADA..FAFF ; Unknown
+    'Latn',  # FB00..FB06 ; Latin
+    'Zzzz',  # FB07..FB12 ; Unknown
+    'Armn',  # FB13..FB17 ; Armenian
+    'Zzzz',  # FB18..FB1C ; Unknown
+    'Hebr',  # FB1D..FB36 ; Hebrew
+    'Zzzz',  # FB37..FB37 ; Unknown
+    'Hebr',  # FB38..FB3C ; Hebrew
+    'Zzzz',  # FB3D..FB3D ; Unknown
+    'Hebr',  # FB3E..FB3E ; Hebrew
+    'Zzzz',  # FB3F..FB3F ; Unknown
+    'Hebr',  # FB40..FB41 ; Hebrew
+    'Zzzz',  # FB42..FB42 ; Unknown
+    'Hebr',  # FB43..FB44 ; Hebrew
+    'Zzzz',  # FB45..FB45 ; Unknown
+    'Hebr',  # FB46..FB4F ; Hebrew
+    'Arab',  # FB50..FBC1 ; Arabic
+    'Zzzz',  # FBC2..FBD2 ; Unknown
+    'Arab',  # FBD3..FD3D ; Arabic
+    'Zyyy',  # FD3E..FD3F ; Common
+    'Zzzz',  # FD40..FD4F ; Unknown
+    'Arab',  # FD50..FD8F ; Arabic
+    'Zzzz',  # FD90..FD91 ; Unknown
+    'Arab',  # FD92..FDC7 ; Arabic
+    'Zzzz',  # FDC8..FDEF ; Unknown
+    'Arab',  # FDF0..FDFD ; Arabic
+    'Zzzz',  # FDFE..FDFF ; Unknown
+    'Zinh',  # FE00..FE0F ; Inherited
+    'Zyyy',  # FE10..FE19 ; Common
+    'Zzzz',  # FE1A..FE1F ; Unknown
+    'Zinh',  # FE20..FE2D ; Inherited
+    'Cyrl',  # FE2E..FE2F ; Cyrillic
+    'Zyyy',  # FE30..FE52 ; Common
+    'Zzzz',  # FE53..FE53 ; Unknown
+    'Zyyy',  # FE54..FE66 ; Common
+    'Zzzz',  # FE67..FE67 ; Unknown
+    'Zyyy',  # FE68..FE6B ; Common
+    'Zzzz',  # FE6C..FE6F ; Unknown
+    'Arab',  # FE70..FE74 ; Arabic
+    'Zzzz',  # FE75..FE75 ; Unknown
+    'Arab',  # FE76..FEFC ; Arabic
+    'Zzzz',  # FEFD..FEFE ; Unknown
+    'Zyyy',  # FEFF..FEFF ; Common
+    'Zzzz',  # FF00..FF00 ; Unknown
+    'Zyyy',  # FF01..FF20 ; Common
+    'Latn',  # FF21..FF3A ; Latin
+    'Zyyy',  # FF3B..FF40 ; Common
+    'Latn',  # FF41..FF5A ; Latin
+    'Zyyy',  # FF5B..FF65 ; Common
+    'Kana',  # FF66..FF6F ; Katakana
+    'Zyyy',  # FF70..FF70 ; Common
+    'Kana',  # FF71..FF9D ; Katakana
+    'Zyyy',  # FF9E..FF9F ; Common
+    'Hang',  # FFA0..FFBE ; Hangul
+    'Zzzz',  # FFBF..FFC1 ; Unknown
+    'Hang',  # FFC2..FFC7 ; Hangul
+    'Zzzz',  # FFC8..FFC9 ; Unknown
+    'Hang',  # FFCA..FFCF ; Hangul
+    'Zzzz',  # FFD0..FFD1 ; Unknown
+    'Hang',  # FFD2..FFD7 ; Hangul
+    'Zzzz',  # FFD8..FFD9 ; Unknown
+    'Hang',  # FFDA..FFDC ; Hangul
+    'Zzzz',  # FFDD..FFDF ; Unknown
+    'Zyyy',  # FFE0..FFE6 ; Common
+    'Zzzz',  # FFE7..FFE7 ; Unknown
+    'Zyyy',  # FFE8..FFEE ; Common
+    'Zzzz',  # FFEF..FFF8 ; Unknown
+    'Zyyy',  # FFF9..FFFD ; Common
+    'Zzzz',  # FFFE..FFFF ; Unknown
+    'Linb',  # 10000..1000B ; Linear_B
+    'Zzzz',  # 1000C..1000C ; Unknown
+    'Linb',  # 1000D..10026 ; Linear_B
+    'Zzzz',  # 10027..10027 ; Unknown
+    'Linb',  # 10028..1003A ; Linear_B
+    'Zzzz',  # 1003B..1003B ; Unknown
+    'Linb',  # 1003C..1003D ; Linear_B
+    'Zzzz',  # 1003E..1003E ; Unknown
+    'Linb',  # 1003F..1004D ; Linear_B
+    'Zzzz',  # 1004E..1004F ; Unknown
+    'Linb',  # 10050..1005D ; Linear_B
+    'Zzzz',  # 1005E..1007F ; Unknown
+    'Linb',  # 10080..100FA ; Linear_B
+    'Zzzz',  # 100FB..100FF ; Unknown
+    'Zyyy',  # 10100..10102 ; Common
+    'Zzzz',  # 10103..10106 ; Unknown
+    'Zyyy',  # 10107..10133 ; Common
+    'Zzzz',  # 10134..10136 ; Unknown
+    'Zyyy',  # 10137..1013F ; Common
+    'Grek',  # 10140..1018E ; Greek
+    'Zzzz',  # 1018F..1018F ; Unknown
+    'Zyyy',  # 10190..1019B ; Common
+    'Zzzz',  # 1019C..1019F ; Unknown
+    'Grek',  # 101A0..101A0 ; Greek
+    'Zzzz',  # 101A1..101CF ; Unknown
+    'Zyyy',  # 101D0..101FC ; Common
+    'Zinh',  # 101FD..101FD ; Inherited
+    'Zzzz',  # 101FE..1027F ; Unknown
+    'Lyci',  # 10280..1029C ; Lycian
+    'Zzzz',  # 1029D..1029F ; Unknown
+    'Cari',  # 102A0..102D0 ; Carian
+    'Zzzz',  # 102D1..102DF ; Unknown
+    'Zinh',  # 102E0..102E0 ; Inherited
+    'Zyyy',  # 102E1..102FB ; Common
+    'Zzzz',  # 102FC..102FF ; Unknown
+    'Ital',  # 10300..10323 ; Old_Italic
+    'Zzzz',  # 10324..1032C ; Unknown
+    'Ital',  # 1032D..1032F ; Old_Italic
+    'Goth',  # 10330..1034A ; Gothic
+    'Zzzz',  # 1034B..1034F ; Unknown
+    'Perm',  # 10350..1037A ; Old_Permic
+    'Zzzz',  # 1037B..1037F ; Unknown
+    'Ugar',  # 10380..1039D ; Ugaritic
+    'Zzzz',  # 1039E..1039E ; Unknown
+    'Ugar',  # 1039F..1039F ; Ugaritic
+    'Xpeo',  # 103A0..103C3 ; Old_Persian
+    'Zzzz',  # 103C4..103C7 ; Unknown
+    'Xpeo',  # 103C8..103D5 ; Old_Persian
+    'Zzzz',  # 103D6..103FF ; Unknown
+    'Dsrt',  # 10400..1044F ; Deseret
+    'Shaw',  # 10450..1047F ; Shavian
+    'Osma',  # 10480..1049D ; Osmanya
+    'Zzzz',  # 1049E..1049F ; Unknown
+    'Osma',  # 104A0..104A9 ; Osmanya
+    'Zzzz',  # 104AA..104AF ; Unknown
+    'Osge',  # 104B0..104D3 ; Osage
+    'Zzzz',  # 104D4..104D7 ; Unknown
+    'Osge',  # 104D8..104FB ; Osage
+    'Zzzz',  # 104FC..104FF ; Unknown
+    'Elba',  # 10500..10527 ; Elbasan
+    'Zzzz',  # 10528..1052F ; Unknown
+    'Aghb',  # 10530..10563 ; Caucasian_Albanian
+    'Zzzz',  # 10564..1056E ; Unknown
+    'Aghb',  # 1056F..1056F ; Caucasian_Albanian
+    'Zzzz',  # 10570..105FF ; Unknown
+    'Lina',  # 10600..10736 ; Linear_A
+    'Zzzz',  # 10737..1073F ; Unknown
+    'Lina',  # 10740..10755 ; Linear_A
+    'Zzzz',  # 10756..1075F ; Unknown
+    'Lina',  # 10760..10767 ; Linear_A
+    'Zzzz',  # 10768..107FF ; Unknown
+    'Cprt',  # 10800..10805 ; Cypriot
+    'Zzzz',  # 10806..10807 ; Unknown
+    'Cprt',  # 10808..10808 ; Cypriot
+    'Zzzz',  # 10809..10809 ; Unknown
+    'Cprt',  # 1080A..10835 ; Cypriot
+    'Zzzz',  # 10836..10836 ; Unknown
+    'Cprt',  # 10837..10838 ; Cypriot
+    'Zzzz',  # 10839..1083B ; Unknown
+    'Cprt',  # 1083C..1083C ; Cypriot
+    'Zzzz',  # 1083D..1083E ; Unknown
+    'Cprt',  # 1083F..1083F ; Cypriot
+    'Armi',  # 10840..10855 ; Imperial_Aramaic
+    'Zzzz',  # 10856..10856 ; Unknown
+    'Armi',  # 10857..1085F ; Imperial_Aramaic
+    'Palm',  # 10860..1087F ; Palmyrene
+    'Nbat',  # 10880..1089E ; Nabataean
+    'Zzzz',  # 1089F..108A6 ; Unknown
+    'Nbat',  # 108A7..108AF ; Nabataean
+    'Zzzz',  # 108B0..108DF ; Unknown
+    'Hatr',  # 108E0..108F2 ; Hatran
+    'Zzzz',  # 108F3..108F3 ; Unknown
+    'Hatr',  # 108F4..108F5 ; Hatran
+    'Zzzz',  # 108F6..108FA ; Unknown
+    'Hatr',  # 108FB..108FF ; Hatran
+    'Phnx',  # 10900..1091B ; Phoenician
+    'Zzzz',  # 1091C..1091E ; Unknown
+    'Phnx',  # 1091F..1091F ; Phoenician
+    'Lydi',  # 10920..10939 ; Lydian
+    'Zzzz',  # 1093A..1093E ; Unknown
+    'Lydi',  # 1093F..1093F ; Lydian
+    'Zzzz',  # 10940..1097F ; Unknown
+    'Mero',  # 10980..1099F ; Meroitic_Hieroglyphs
+    'Merc',  # 109A0..109B7 ; Meroitic_Cursive
+    'Zzzz',  # 109B8..109BB ; Unknown
+    'Merc',  # 109BC..109CF ; Meroitic_Cursive
+    'Zzzz',  # 109D0..109D1 ; Unknown
+    'Merc',  # 109D2..109FF ; Meroitic_Cursive
+    'Khar',  # 10A00..10A03 ; Kharoshthi
+    'Zzzz',  # 10A04..10A04 ; Unknown
+    'Khar',  # 10A05..10A06 ; Kharoshthi
+    'Zzzz',  # 10A07..10A0B ; Unknown
+    'Khar',  # 10A0C..10A13 ; Kharoshthi
+    'Zzzz',  # 10A14..10A14 ; Unknown
+    'Khar',  # 10A15..10A17 ; Kharoshthi
+    'Zzzz',  # 10A18..10A18 ; Unknown
+    'Khar',  # 10A19..10A33 ; Kharoshthi
+    'Zzzz',  # 10A34..10A37 ; Unknown
+    'Khar',  # 10A38..10A3A ; Kharoshthi
+    'Zzzz',  # 10A3B..10A3E ; Unknown
+    'Khar',  # 10A3F..10A47 ; Kharoshthi
+    'Zzzz',  # 10A48..10A4F ; Unknown
+    'Khar',  # 10A50..10A58 ; Kharoshthi
+    'Zzzz',  # 10A59..10A5F ; Unknown
+    'Sarb',  # 10A60..10A7F ; Old_South_Arabian
+    'Narb',  # 10A80..10A9F ; Old_North_Arabian
+    'Zzzz',  # 10AA0..10ABF ; Unknown
+    'Mani',  # 10AC0..10AE6 ; Manichaean
+    'Zzzz',  # 10AE7..10AEA ; Unknown
+    'Mani',  # 10AEB..10AF6 ; Manichaean
+    'Zzzz',  # 10AF7..10AFF ; Unknown
+    'Avst',  # 10B00..10B35 ; Avestan
+    'Zzzz',  # 10B36..10B38 ; Unknown
+    'Avst',  # 10B39..10B3F ; Avestan
+    'Prti',  # 10B40..10B55 ; Inscriptional_Parthian
+    'Zzzz',  # 10B56..10B57 ; Unknown
+    'Prti',  # 10B58..10B5F ; Inscriptional_Parthian
+    'Phli',  # 10B60..10B72 ; Inscriptional_Pahlavi
+    'Zzzz',  # 10B73..10B77 ; Unknown
+    'Phli',  # 10B78..10B7F ; Inscriptional_Pahlavi
+    'Phlp',  # 10B80..10B91 ; Psalter_Pahlavi
+    'Zzzz',  # 10B92..10B98 ; Unknown
+    'Phlp',  # 10B99..10B9C ; Psalter_Pahlavi
+    'Zzzz',  # 10B9D..10BA8 ; Unknown
+    'Phlp',  # 10BA9..10BAF ; Psalter_Pahlavi
+    'Zzzz',  # 10BB0..10BFF ; Unknown
+    'Orkh',  # 10C00..10C48 ; Old_Turkic
+    'Zzzz',  # 10C49..10C7F ; Unknown
+    'Hung',  # 10C80..10CB2 ; Old_Hungarian
+    'Zzzz',  # 10CB3..10CBF ; Unknown
+    'Hung',  # 10CC0..10CF2 ; Old_Hungarian
+    'Zzzz',  # 10CF3..10CF9 ; Unknown
+    'Hung',  # 10CFA..10CFF ; Old_Hungarian
+    'Zzzz',  # 10D00..10E5F ; Unknown
+    'Arab',  # 10E60..10E7E ; Arabic
+    'Zzzz',  # 10E7F..10FFF ; Unknown
+    'Brah',  # 11000..1104D ; Brahmi
+    'Zzzz',  # 1104E..11051 ; Unknown
+    'Brah',  # 11052..1106F ; Brahmi
+    'Zzzz',  # 11070..1107E ; Unknown
+    'Brah',  # 1107F..1107F ; Brahmi
+    'Kthi',  # 11080..110C1 ; Kaithi
+    'Zzzz',  # 110C2..110CF ; Unknown
+    'Sora',  # 110D0..110E8 ; Sora_Sompeng
+    'Zzzz',  # 110E9..110EF ; Unknown
+    'Sora',  # 110F0..110F9 ; Sora_Sompeng
+    'Zzzz',  # 110FA..110FF ; Unknown
+    'Cakm',  # 11100..11134 ; Chakma
+    'Zzzz',  # 11135..11135 ; Unknown
+    'Cakm',  # 11136..11143 ; Chakma
+    'Zzzz',  # 11144..1114F ; Unknown
+    'Mahj',  # 11150..11176 ; Mahajani
+    'Zzzz',  # 11177..1117F ; Unknown
+    'Shrd',  # 11180..111CD ; Sharada
+    'Zzzz',  # 111CE..111CF ; Unknown
+    'Shrd',  # 111D0..111DF ; Sharada
+    'Zzzz',  # 111E0..111E0 ; Unknown
+    'Sinh',  # 111E1..111F4 ; Sinhala
+    'Zzzz',  # 111F5..111FF ; Unknown
+    'Khoj',  # 11200..11211 ; Khojki
+    'Zzzz',  # 11212..11212 ; Unknown
+    'Khoj',  # 11213..1123E ; Khojki
+    'Zzzz',  # 1123F..1127F ; Unknown
+    'Mult',  # 11280..11286 ; Multani
+    'Zzzz',  # 11287..11287 ; Unknown
+    'Mult',  # 11288..11288 ; Multani
+    'Zzzz',  # 11289..11289 ; Unknown
+    'Mult',  # 1128A..1128D ; Multani
+    'Zzzz',  # 1128E..1128E ; Unknown
+    'Mult',  # 1128F..1129D ; Multani
+    'Zzzz',  # 1129E..1129E ; Unknown
+    'Mult',  # 1129F..112A9 ; Multani
+    'Zzzz',  # 112AA..112AF ; Unknown
+    'Sind',  # 112B0..112EA ; Khudawadi
+    'Zzzz',  # 112EB..112EF ; Unknown
+    'Sind',  # 112F0..112F9 ; Khudawadi
+    'Zzzz',  # 112FA..112FF ; Unknown
+    'Gran',  # 11300..11303 ; Grantha
+    'Zzzz',  # 11304..11304 ; Unknown
+    'Gran',  # 11305..1130C ; Grantha
+    'Zzzz',  # 1130D..1130E ; Unknown
+    'Gran',  # 1130F..11310 ; Grantha
+    'Zzzz',  # 11311..11312 ; Unknown
+    'Gran',  # 11313..11328 ; Grantha
+    'Zzzz',  # 11329..11329 ; Unknown
+    'Gran',  # 1132A..11330 ; Grantha
+    'Zzzz',  # 11331..11331 ; Unknown
+    'Gran',  # 11332..11333 ; Grantha
+    'Zzzz',  # 11334..11334 ; Unknown
+    'Gran',  # 11335..11339 ; Grantha
+    'Zzzz',  # 1133A..1133B ; Unknown
+    'Gran',  # 1133C..11344 ; Grantha
+    'Zzzz',  # 11345..11346 ; Unknown
+    'Gran',  # 11347..11348 ; Grantha
+    'Zzzz',  # 11349..1134A ; Unknown
+    'Gran',  # 1134B..1134D ; Grantha
+    'Zzzz',  # 1134E..1134F ; Unknown
+    'Gran',  # 11350..11350 ; Grantha
+    'Zzzz',  # 11351..11356 ; Unknown
+    'Gran',  # 11357..11357 ; Grantha
+    'Zzzz',  # 11358..1135C ; Unknown
+    'Gran',  # 1135D..11363 ; Grantha
+    'Zzzz',  # 11364..11365 ; Unknown
+    'Gran',  # 11366..1136C ; Grantha
+    'Zzzz',  # 1136D..1136F ; Unknown
+    'Gran',  # 11370..11374 ; Grantha
+    'Zzzz',  # 11375..113FF ; Unknown
+    'Newa',  # 11400..11459 ; Newa
+    'Zzzz',  # 1145A..1145A ; Unknown
+    'Newa',  # 1145B..1145B ; Newa
+    'Zzzz',  # 1145C..1145C ; Unknown
+    'Newa',  # 1145D..1145D ; Newa
+    'Zzzz',  # 1145E..1147F ; Unknown
+    'Tirh',  # 11480..114C7 ; Tirhuta
+    'Zzzz',  # 114C8..114CF ; Unknown
+    'Tirh',  # 114D0..114D9 ; Tirhuta
+    'Zzzz',  # 114DA..1157F ; Unknown
+    'Sidd',  # 11580..115B5 ; Siddham
+    'Zzzz',  # 115B6..115B7 ; Unknown
+    'Sidd',  # 115B8..115DD ; Siddham
+    'Zzzz',  # 115DE..115FF ; Unknown
+    'Modi',  # 11600..11644 ; Modi
+    'Zzzz',  # 11645..1164F ; Unknown
+    'Modi',  # 11650..11659 ; Modi
+    'Zzzz',  # 1165A..1165F ; Unknown
+    'Mong',  # 11660..1166C ; Mongolian
+    'Zzzz',  # 1166D..1167F ; Unknown
+    'Takr',  # 11680..116B7 ; Takri
+    'Zzzz',  # 116B8..116BF ; Unknown
+    'Takr',  # 116C0..116C9 ; Takri
+    'Zzzz',  # 116CA..116FF ; Unknown
+    'Ahom',  # 11700..11719 ; Ahom
+    'Zzzz',  # 1171A..1171C ; Unknown
+    'Ahom',  # 1171D..1172B ; Ahom
+    'Zzzz',  # 1172C..1172F ; Unknown
+    'Ahom',  # 11730..1173F ; Ahom
+    'Zzzz',  # 11740..1189F ; Unknown
+    'Wara',  # 118A0..118F2 ; Warang_Citi
+    'Zzzz',  # 118F3..118FE ; Unknown
+    'Wara',  # 118FF..118FF ; Warang_Citi
+    'Zzzz',  # 11900..119FF ; Unknown
+    'Zanb',  # 11A00..11A47 ; Zanabazar_Square
+    'Zzzz',  # 11A48..11A4F ; Unknown
+    'Soyo',  # 11A50..11A83 ; Soyombo
+    'Zzzz',  # 11A84..11A85 ; Unknown
+    'Soyo',  # 11A86..11A9C ; Soyombo
+    'Zzzz',  # 11A9D..11A9D ; Unknown
+    'Soyo',  # 11A9E..11AA2 ; Soyombo
+    'Zzzz',  # 11AA3..11ABF ; Unknown
+    'Pauc',  # 11AC0..11AF8 ; Pau_Cin_Hau
+    'Zzzz',  # 11AF9..11BFF ; Unknown
+    'Bhks',  # 11C00..11C08 ; Bhaiksuki
+    'Zzzz',  # 11C09..11C09 ; Unknown
+    'Bhks',  # 11C0A..11C36 ; Bhaiksuki
+    'Zzzz',  # 11C37..11C37 ; Unknown
+    'Bhks',  # 11C38..11C45 ; Bhaiksuki
+    'Zzzz',  # 11C46..11C4F ; Unknown
+    'Bhks',  # 11C50..11C6C ; Bhaiksuki
+    'Zzzz',  # 11C6D..11C6F ; Unknown
+    'Marc',  # 11C70..11C8F ; Marchen
+    'Zzzz',  # 11C90..11C91 ; Unknown
+    'Marc',  # 11C92..11CA7 ; Marchen
+    'Zzzz',  # 11CA8..11CA8 ; Unknown
+    'Marc',  # 11CA9..11CB6 ; Marchen
+    'Zzzz',  # 11CB7..11CFF ; Unknown
+    'Gonm',  # 11D00..11D06 ; Masaram_Gondi
+    'Zzzz',  # 11D07..11D07 ; Unknown
+    'Gonm',  # 11D08..11D09 ; Masaram_Gondi
+    'Zzzz',  # 11D0A..11D0A ; Unknown
+    'Gonm',  # 11D0B..11D36 ; Masaram_Gondi
+    'Zzzz',  # 11D37..11D39 ; Unknown
+    'Gonm',  # 11D3A..11D3A ; Masaram_Gondi
+    'Zzzz',  # 11D3B..11D3B ; Unknown
+    'Gonm',  # 11D3C..11D3D ; Masaram_Gondi
+    'Zzzz',  # 11D3E..11D3E ; Unknown
+    'Gonm',  # 11D3F..11D47 ; Masaram_Gondi
+    'Zzzz',  # 11D48..11D4F ; Unknown
+    'Gonm',  # 11D50..11D59 ; Masaram_Gondi
+    'Zzzz',  # 11D5A..11FFF ; Unknown
+    'Xsux',  # 12000..12399 ; Cuneiform
+    'Zzzz',  # 1239A..123FF ; Unknown
+    'Xsux',  # 12400..1246E ; Cuneiform
+    'Zzzz',  # 1246F..1246F ; Unknown
+    'Xsux',  # 12470..12474 ; Cuneiform
+    'Zzzz',  # 12475..1247F ; Unknown
+    'Xsux',  # 12480..12543 ; Cuneiform
+    'Zzzz',  # 12544..12FFF ; Unknown
+    'Egyp',  # 13000..1342E ; Egyptian_Hieroglyphs
+    'Zzzz',  # 1342F..143FF ; Unknown
+    'Hluw',  # 14400..14646 ; Anatolian_Hieroglyphs
+    'Zzzz',  # 14647..167FF ; Unknown
+    'Bamu',  # 16800..16A38 ; Bamum
+    'Zzzz',  # 16A39..16A3F ; Unknown
+    'Mroo',  # 16A40..16A5E ; Mro
+    'Zzzz',  # 16A5F..16A5F ; Unknown
+    'Mroo',  # 16A60..16A69 ; Mro
+    'Zzzz',  # 16A6A..16A6D ; Unknown
+    'Mroo',  # 16A6E..16A6F ; Mro
+    'Zzzz',  # 16A70..16ACF ; Unknown
+    'Bass',  # 16AD0..16AED ; Bassa_Vah
+    'Zzzz',  # 16AEE..16AEF ; Unknown
+    'Bass',  # 16AF0..16AF5 ; Bassa_Vah
+    'Zzzz',  # 16AF6..16AFF ; Unknown
+    'Hmng',  # 16B00..16B45 ; Pahawh_Hmong
+    'Zzzz',  # 16B46..16B4F ; Unknown
+    'Hmng',  # 16B50..16B59 ; Pahawh_Hmong
+    'Zzzz',  # 16B5A..16B5A ; Unknown
+    'Hmng',  # 16B5B..16B61 ; Pahawh_Hmong
+    'Zzzz',  # 16B62..16B62 ; Unknown
+    'Hmng',  # 16B63..16B77 ; Pahawh_Hmong
+    'Zzzz',  # 16B78..16B7C ; Unknown
+    'Hmng',  # 16B7D..16B8F ; Pahawh_Hmong
+    'Zzzz',  # 16B90..16EFF ; Unknown
+    'Plrd',  # 16F00..16F44 ; Miao
+    'Zzzz',  # 16F45..16F4F ; Unknown
+    'Plrd',  # 16F50..16F7E ; Miao
+    'Zzzz',  # 16F7F..16F8E ; Unknown
+    'Plrd',  # 16F8F..16F9F ; Miao
+    'Zzzz',  # 16FA0..16FDF ; Unknown
+    'Tang',  # 16FE0..16FE0 ; Tangut
+    'Nshu',  # 16FE1..16FE1 ; Nushu
+    'Zzzz',  # 16FE2..16FFF ; Unknown
+    'Tang',  # 17000..187EC ; Tangut
+    'Zzzz',  # 187ED..187FF ; Unknown
+    'Tang',  # 18800..18AF2 ; Tangut
+    'Zzzz',  # 18AF3..1AFFF ; Unknown
+    'Kana',  # 1B000..1B000 ; Katakana
+    'Hira',  # 1B001..1B11E ; Hiragana
+    'Zzzz',  # 1B11F..1B16F ; Unknown
+    'Nshu',  # 1B170..1B2FB ; Nushu
+    'Zzzz',  # 1B2FC..1BBFF ; Unknown
+    'Dupl',  # 1BC00..1BC6A ; Duployan
+    'Zzzz',  # 1BC6B..1BC6F ; Unknown
+    'Dupl',  # 1BC70..1BC7C ; Duployan
+    'Zzzz',  # 1BC7D..1BC7F ; Unknown
+    'Dupl',  # 1BC80..1BC88 ; Duployan
+    'Zzzz',  # 1BC89..1BC8F ; Unknown
+    'Dupl',  # 1BC90..1BC99 ; Duployan
+    'Zzzz',  # 1BC9A..1BC9B ; Unknown
+    'Dupl',  # 1BC9C..1BC9F ; Duployan
+    'Zyyy',  # 1BCA0..1BCA3 ; Common
+    'Zzzz',  # 1BCA4..1CFFF ; Unknown
+    'Zyyy',  # 1D000..1D0F5 ; Common
+    'Zzzz',  # 1D0F6..1D0FF ; Unknown
+    'Zyyy',  # 1D100..1D126 ; Common
+    'Zzzz',  # 1D127..1D128 ; Unknown
+    'Zyyy',  # 1D129..1D166 ; Common
+    'Zinh',  # 1D167..1D169 ; Inherited
+    'Zyyy',  # 1D16A..1D17A ; Common
+    'Zinh',  # 1D17B..1D182 ; Inherited
+    'Zyyy',  # 1D183..1D184 ; Common
+    'Zinh',  # 1D185..1D18B ; Inherited
+    'Zyyy',  # 1D18C..1D1A9 ; Common
+    'Zinh',  # 1D1AA..1D1AD ; Inherited
+    'Zyyy',  # 1D1AE..1D1E8 ; Common
+    'Zzzz',  # 1D1E9..1D1FF ; Unknown
+    'Grek',  # 1D200..1D245 ; Greek
+    'Zzzz',  # 1D246..1D2FF ; Unknown
+    'Zyyy',  # 1D300..1D356 ; Common
+    'Zzzz',  # 1D357..1D35F ; Unknown
+    'Zyyy',  # 1D360..1D371 ; Common
+    'Zzzz',  # 1D372..1D3FF ; Unknown
+    'Zyyy',  # 1D400..1D454 ; Common
+    'Zzzz',  # 1D455..1D455 ; Unknown
+    'Zyyy',  # 1D456..1D49C ; Common
+    'Zzzz',  # 1D49D..1D49D ; Unknown
+    'Zyyy',  # 1D49E..1D49F ; Common
+    'Zzzz',  # 1D4A0..1D4A1 ; Unknown
+    'Zyyy',  # 1D4A2..1D4A2 ; Common
+    'Zzzz',  # 1D4A3..1D4A4 ; Unknown
+    'Zyyy',  # 1D4A5..1D4A6 ; Common
+    'Zzzz',  # 1D4A7..1D4A8 ; Unknown
+    'Zyyy',  # 1D4A9..1D4AC ; Common
+    'Zzzz',  # 1D4AD..1D4AD ; Unknown
+    'Zyyy',  # 1D4AE..1D4B9 ; Common
+    'Zzzz',  # 1D4BA..1D4BA ; Unknown
+    'Zyyy',  # 1D4BB..1D4BB ; Common
+    'Zzzz',  # 1D4BC..1D4BC ; Unknown
+    'Zyyy',  # 1D4BD..1D4C3 ; Common
+    'Zzzz',  # 1D4C4..1D4C4 ; Unknown
+    'Zyyy',  # 1D4C5..1D505 ; Common
+    'Zzzz',  # 1D506..1D506 ; Unknown
+    'Zyyy',  # 1D507..1D50A ; Common
+    'Zzzz',  # 1D50B..1D50C ; Unknown
+    'Zyyy',  # 1D50D..1D514 ; Common
+    'Zzzz',  # 1D515..1D515 ; Unknown
+    'Zyyy',  # 1D516..1D51C ; Common
+    'Zzzz',  # 1D51D..1D51D ; Unknown
+    'Zyyy',  # 1D51E..1D539 ; Common
+    'Zzzz',  # 1D53A..1D53A ; Unknown
+    'Zyyy',  # 1D53B..1D53E ; Common
+    'Zzzz',  # 1D53F..1D53F ; Unknown
+    'Zyyy',  # 1D540..1D544 ; Common
+    'Zzzz',  # 1D545..1D545 ; Unknown
+    'Zyyy',  # 1D546..1D546 ; Common
+    'Zzzz',  # 1D547..1D549 ; Unknown
+    'Zyyy',  # 1D54A..1D550 ; Common
+    'Zzzz',  # 1D551..1D551 ; Unknown
+    'Zyyy',  # 1D552..1D6A5 ; Common
+    'Zzzz',  # 1D6A6..1D6A7 ; Unknown
+    'Zyyy',  # 1D6A8..1D7CB ; Common
+    'Zzzz',  # 1D7CC..1D7CD ; Unknown
+    'Zyyy',  # 1D7CE..1D7FF ; Common
+    'Sgnw',  # 1D800..1DA8B ; SignWriting
+    'Zzzz',  # 1DA8C..1DA9A ; Unknown
+    'Sgnw',  # 1DA9B..1DA9F ; SignWriting
+    'Zzzz',  # 1DAA0..1DAA0 ; Unknown
+    'Sgnw',  # 1DAA1..1DAAF ; SignWriting
+    'Zzzz',  # 1DAB0..1DFFF ; Unknown
+    'Glag',  # 1E000..1E006 ; Glagolitic
+    'Zzzz',  # 1E007..1E007 ; Unknown
+    'Glag',  # 1E008..1E018 ; Glagolitic
+    'Zzzz',  # 1E019..1E01A ; Unknown
+    'Glag',  # 1E01B..1E021 ; Glagolitic
+    'Zzzz',  # 1E022..1E022 ; Unknown
+    'Glag',  # 1E023..1E024 ; Glagolitic
+    'Zzzz',  # 1E025..1E025 ; Unknown
+    'Glag',  # 1E026..1E02A ; Glagolitic
+    'Zzzz',  # 1E02B..1E7FF ; Unknown
+    'Mend',  # 1E800..1E8C4 ; Mende_Kikakui
+    'Zzzz',  # 1E8C5..1E8C6 ; Unknown
+    'Mend',  # 1E8C7..1E8D6 ; Mende_Kikakui
+    'Zzzz',  # 1E8D7..1E8FF ; Unknown
+    'Adlm',  # 1E900..1E94A ; Adlam
+    'Zzzz',  # 1E94B..1E94F ; Unknown
+    'Adlm',  # 1E950..1E959 ; Adlam
+    'Zzzz',  # 1E95A..1E95D ; Unknown
+    'Adlm',  # 1E95E..1E95F ; Adlam
+    'Zzzz',  # 1E960..1EDFF ; Unknown
+    'Arab',  # 1EE00..1EE03 ; Arabic
+    'Zzzz',  # 1EE04..1EE04 ; Unknown
+    'Arab',  # 1EE05..1EE1F ; Arabic
+    'Zzzz',  # 1EE20..1EE20 ; Unknown
+    'Arab',  # 1EE21..1EE22 ; Arabic
+    'Zzzz',  # 1EE23..1EE23 ; Unknown
+    'Arab',  # 1EE24..1EE24 ; Arabic
+    'Zzzz',  # 1EE25..1EE26 ; Unknown
+    'Arab',  # 1EE27..1EE27 ; Arabic
+    'Zzzz',  # 1EE28..1EE28 ; Unknown
+    'Arab',  # 1EE29..1EE32 ; Arabic
+    'Zzzz',  # 1EE33..1EE33 ; Unknown
+    'Arab',  # 1EE34..1EE37 ; Arabic
+    'Zzzz',  # 1EE38..1EE38 ; Unknown
+    'Arab',  # 1EE39..1EE39 ; Arabic
+    'Zzzz',  # 1EE3A..1EE3A ; Unknown
+    'Arab',  # 1EE3B..1EE3B ; Arabic
+    'Zzzz',  # 1EE3C..1EE41 ; Unknown
+    'Arab',  # 1EE42..1EE42 ; Arabic
+    'Zzzz',  # 1EE43..1EE46 ; Unknown
+    'Arab',  # 1EE47..1EE47 ; Arabic
+    'Zzzz',  # 1EE48..1EE48 ; Unknown
+    'Arab',  # 1EE49..1EE49 ; Arabic
+    'Zzzz',  # 1EE4A..1EE4A ; Unknown
+    'Arab',  # 1EE4B..1EE4B ; Arabic
+    'Zzzz',  # 1EE4C..1EE4C ; Unknown
+    'Arab',  # 1EE4D..1EE4F ; Arabic
+    'Zzzz',  # 1EE50..1EE50 ; Unknown
+    'Arab',  # 1EE51..1EE52 ; Arabic
+    'Zzzz',  # 1EE53..1EE53 ; Unknown
+    'Arab',  # 1EE54..1EE54 ; Arabic
+    'Zzzz',  # 1EE55..1EE56 ; Unknown
+    'Arab',  # 1EE57..1EE57 ; Arabic
+    'Zzzz',  # 1EE58..1EE58 ; Unknown
+    'Arab',  # 1EE59..1EE59 ; Arabic
+    'Zzzz',  # 1EE5A..1EE5A ; Unknown
+    'Arab',  # 1EE5B..1EE5B ; Arabic
+    'Zzzz',  # 1EE5C..1EE5C ; Unknown
+    'Arab',  # 1EE5D..1EE5D ; Arabic
+    'Zzzz',  # 1EE5E..1EE5E ; Unknown
+    'Arab',  # 1EE5F..1EE5F ; Arabic
+    'Zzzz',  # 1EE60..1EE60 ; Unknown
+    'Arab',  # 1EE61..1EE62 ; Arabic
+    'Zzzz',  # 1EE63..1EE63 ; Unknown
+    'Arab',  # 1EE64..1EE64 ; Arabic
+    'Zzzz',  # 1EE65..1EE66 ; Unknown
+    'Arab',  # 1EE67..1EE6A ; Arabic
+    'Zzzz',  # 1EE6B..1EE6B ; Unknown
+    'Arab',  # 1EE6C..1EE72 ; Arabic
+    'Zzzz',  # 1EE73..1EE73 ; Unknown
+    'Arab',  # 1EE74..1EE77 ; Arabic
+    'Zzzz',  # 1EE78..1EE78 ; Unknown
+    'Arab',  # 1EE79..1EE7C ; Arabic
+    'Zzzz',  # 1EE7D..1EE7D ; Unknown
+    'Arab',  # 1EE7E..1EE7E ; Arabic
+    'Zzzz',  # 1EE7F..1EE7F ; Unknown
+    'Arab',  # 1EE80..1EE89 ; Arabic
+    'Zzzz',  # 1EE8A..1EE8A ; Unknown
+    'Arab',  # 1EE8B..1EE9B ; Arabic
+    'Zzzz',  # 1EE9C..1EEA0 ; Unknown
+    'Arab',  # 1EEA1..1EEA3 ; Arabic
+    'Zzzz',  # 1EEA4..1EEA4 ; Unknown
+    'Arab',  # 1EEA5..1EEA9 ; Arabic
+    'Zzzz',  # 1EEAA..1EEAA ; Unknown
+    'Arab',  # 1EEAB..1EEBB ; Arabic
+    'Zzzz',  # 1EEBC..1EEEF ; Unknown
+    'Arab',  # 1EEF0..1EEF1 ; Arabic
+    'Zzzz',  # 1EEF2..1EFFF ; Unknown
+    'Zyyy',  # 1F000..1F02B ; Common
+    'Zzzz',  # 1F02C..1F02F ; Unknown
+    'Zyyy',  # 1F030..1F093 ; Common
+    'Zzzz',  # 1F094..1F09F ; Unknown
+    'Zyyy',  # 1F0A0..1F0AE ; Common
+    'Zzzz',  # 1F0AF..1F0B0 ; Unknown
+    'Zyyy',  # 1F0B1..1F0BF ; Common
+    'Zzzz',  # 1F0C0..1F0C0 ; Unknown
+    'Zyyy',  # 1F0C1..1F0CF ; Common
+    'Zzzz',  # 1F0D0..1F0D0 ; Unknown
+    'Zyyy',  # 1F0D1..1F0F5 ; Common
+    'Zzzz',  # 1F0F6..1F0FF ; Unknown
+    'Zyyy',  # 1F100..1F10C ; Common
+    'Zzzz',  # 1F10D..1F10F ; Unknown
+    'Zyyy',  # 1F110..1F12E ; Common
+    'Zzzz',  # 1F12F..1F12F ; Unknown
+    'Zyyy',  # 1F130..1F16B ; Common
+    'Zzzz',  # 1F16C..1F16F ; Unknown
+    'Zyyy',  # 1F170..1F1AC ; Common
+    'Zzzz',  # 1F1AD..1F1E5 ; Unknown
+    'Zyyy',  # 1F1E6..1F1FF ; Common
+    'Hira',  # 1F200..1F200 ; Hiragana
+    'Zyyy',  # 1F201..1F202 ; Common
+    'Zzzz',  # 1F203..1F20F ; Unknown
+    'Zyyy',  # 1F210..1F23B ; Common
+    'Zzzz',  # 1F23C..1F23F ; Unknown
+    'Zyyy',  # 1F240..1F248 ; Common
+    'Zzzz',  # 1F249..1F24F ; Unknown
+    'Zyyy',  # 1F250..1F251 ; Common
+    'Zzzz',  # 1F252..1F25F ; Unknown
+    'Zyyy',  # 1F260..1F265 ; Common
+    'Zzzz',  # 1F266..1F2FF ; Unknown
+    'Zyyy',  # 1F300..1F6D4 ; Common
+    'Zzzz',  # 1F6D5..1F6DF ; Unknown
+    'Zyyy',  # 1F6E0..1F6EC ; Common
+    'Zzzz',  # 1F6ED..1F6EF ; Unknown
+    'Zyyy',  # 1F6F0..1F6F8 ; Common
+    'Zzzz',  # 1F6F9..1F6FF ; Unknown
+    'Zyyy',  # 1F700..1F773 ; Common
+    'Zzzz',  # 1F774..1F77F ; Unknown
+    'Zyyy',  # 1F780..1F7D4 ; Common
+    'Zzzz',  # 1F7D5..1F7FF ; Unknown
+    'Zyyy',  # 1F800..1F80B ; Common
+    'Zzzz',  # 1F80C..1F80F ; Unknown
+    'Zyyy',  # 1F810..1F847 ; Common
+    'Zzzz',  # 1F848..1F84F ; Unknown
+    'Zyyy',  # 1F850..1F859 ; Common
+    'Zzzz',  # 1F85A..1F85F ; Unknown
+    'Zyyy',  # 1F860..1F887 ; Common
+    'Zzzz',  # 1F888..1F88F ; Unknown
+    'Zyyy',  # 1F890..1F8AD ; Common
+    'Zzzz',  # 1F8AE..1F8FF ; Unknown
+    'Zyyy',  # 1F900..1F90B ; Common
+    'Zzzz',  # 1F90C..1F90F ; Unknown
+    'Zyyy',  # 1F910..1F93E ; Common
+    'Zzzz',  # 1F93F..1F93F ; Unknown
+    'Zyyy',  # 1F940..1F94C ; Common
+    'Zzzz',  # 1F94D..1F94F ; Unknown
+    'Zyyy',  # 1F950..1F96B ; Common
+    'Zzzz',  # 1F96C..1F97F ; Unknown
+    'Zyyy',  # 1F980..1F997 ; Common
+    'Zzzz',  # 1F998..1F9BF ; Unknown
+    'Zyyy',  # 1F9C0..1F9C0 ; Common
+    'Zzzz',  # 1F9C1..1F9CF ; Unknown
+    'Zyyy',  # 1F9D0..1F9E6 ; Common
+    'Zzzz',  # 1F9E7..1FFFF ; Unknown
+    'Hani',  # 20000..2A6D6 ; Han
+    'Zzzz',  # 2A6D7..2A6FF ; Unknown
+    'Hani',  # 2A700..2B734 ; Han
+    'Zzzz',  # 2B735..2B73F ; Unknown
+    'Hani',  # 2B740..2B81D ; Han
+    'Zzzz',  # 2B81E..2B81F ; Unknown
+    'Hani',  # 2B820..2CEA1 ; Han
+    'Zzzz',  # 2CEA2..2CEAF ; Unknown
+    'Hani',  # 2CEB0..2EBE0 ; Han
+    'Zzzz',  # 2EBE1..2F7FF ; Unknown
+    'Hani',  # 2F800..2FA1D ; Han
+    'Zzzz',  # 2FA1E..E0000 ; Unknown
+    'Zyyy',  # E0001..E0001 ; Common
+    'Zzzz',  # E0002..E001F ; Unknown
+    'Zyyy',  # E0020..E007F ; Common
+    'Zzzz',  # E0080..E00FF ; Unknown
+    'Zinh',  # E0100..E01EF ; Inherited
+    'Zzzz',  # E01F0..10FFFF ; Unknown
+]
+
+NAMES = {
+    'Adlm': 'Adlam',
+    'Aghb': 'Caucasian_Albanian',
+    'Ahom': 'Ahom',
+    'Arab': 'Arabic',
+    'Armi': 'Imperial_Aramaic',
+    'Armn': 'Armenian',
+    'Avst': 'Avestan',
+    'Bali': 'Balinese',
+    'Bamu': 'Bamum',
+    'Bass': 'Bassa_Vah',
+    'Batk': 'Batak',
+    'Beng': 'Bengali',
+    'Bhks': 'Bhaiksuki',
+    'Bopo': 'Bopomofo',
+    'Brah': 'Brahmi',
+    'Brai': 'Braille',
+    'Bugi': 'Buginese',
+    'Buhd': 'Buhid',
+    'Cakm': 'Chakma',
+    'Cans': 'Canadian_Aboriginal',
+    'Cari': 'Carian',
+    'Cham': 'Cham',
+    'Cher': 'Cherokee',
+    'Copt': 'Coptic',
+    'Cprt': 'Cypriot',
+    'Cyrl': 'Cyrillic',
+    'Deva': 'Devanagari',
+    'Dsrt': 'Deseret',
+    'Dupl': 'Duployan',
+    'Egyp': 'Egyptian_Hieroglyphs',
+    'Elba': 'Elbasan',
+    'Ethi': 'Ethiopic',
+    'Geor': 'Georgian',
+    'Glag': 'Glagolitic',
+    'Gonm': 'Masaram_Gondi',
+    'Goth': 'Gothic',
+    'Gran': 'Grantha',
+    'Grek': 'Greek',
+    'Gujr': 'Gujarati',
+    'Guru': 'Gurmukhi',
+    'Hang': 'Hangul',
+    'Hani': 'Han',
+    'Hano': 'Hanunoo',
+    'Hatr': 'Hatran',
+    'Hebr': 'Hebrew',
+    'Hira': 'Hiragana',
+    'Hluw': 'Anatolian_Hieroglyphs',
+    'Hmng': 'Pahawh_Hmong',
+    'Hrkt': 'Katakana_Or_Hiragana',
+    'Hung': 'Old_Hungarian',
+    'Ital': 'Old_Italic',
+    'Java': 'Javanese',
+    'Kali': 'Kayah_Li',
+    'Kana': 'Katakana',
+    'Khar': 'Kharoshthi',
+    'Khmr': 'Khmer',
+    'Khoj': 'Khojki',
+    'Knda': 'Kannada',
+    'Kthi': 'Kaithi',
+    'Lana': 'Tai_Tham',
+    'Laoo': 'Lao',
+    'Latn': 'Latin',
+    'Lepc': 'Lepcha',
+    'Limb': 'Limbu',
+    'Lina': 'Linear_A',
+    'Linb': 'Linear_B',
+    'Lisu': 'Lisu',
+    'Lyci': 'Lycian',
+    'Lydi': 'Lydian',
+    'Mahj': 'Mahajani',
+    'Mand': 'Mandaic',
+    'Mani': 'Manichaean',
+    'Marc': 'Marchen',
+    'Mend': 'Mende_Kikakui',
+    'Merc': 'Meroitic_Cursive',
+    'Mero': 'Meroitic_Hieroglyphs',
+    'Mlym': 'Malayalam',
+    'Modi': 'Modi',
+    'Mong': 'Mongolian',
+    'Mroo': 'Mro',
+    'Mtei': 'Meetei_Mayek',
+    'Mult': 'Multani',
+    'Mymr': 'Myanmar',
+    'Narb': 'Old_North_Arabian',
+    'Nbat': 'Nabataean',
+    'Newa': 'Newa',
+    'Nkoo': 'Nko',
+    'Nshu': 'Nushu',
+    'Ogam': 'Ogham',
+    'Olck': 'Ol_Chiki',
+    'Orkh': 'Old_Turkic',
+    'Orya': 'Oriya',
+    'Osge': 'Osage',
+    'Osma': 'Osmanya',
+    'Palm': 'Palmyrene',
+    'Pauc': 'Pau_Cin_Hau',
+    'Perm': 'Old_Permic',
+    'Phag': 'Phags_Pa',
+    'Phli': 'Inscriptional_Pahlavi',
+    'Phlp': 'Psalter_Pahlavi',
+    'Phnx': 'Phoenician',
+    'Plrd': 'Miao',
+    'Prti': 'Inscriptional_Parthian',
+    'Rjng': 'Rejang',
+    'Runr': 'Runic',
+    'Samr': 'Samaritan',
+    'Sarb': 'Old_South_Arabian',
+    'Saur': 'Saurashtra',
+    'Sgnw': 'SignWriting',
+    'Shaw': 'Shavian',
+    'Shrd': 'Sharada',
+    'Sidd': 'Siddham',
+    'Sind': 'Khudawadi',
+    'Sinh': 'Sinhala',
+    'Sora': 'Sora_Sompeng',
+    'Soyo': 'Soyombo',
+    'Sund': 'Sundanese',
+    'Sylo': 'Syloti_Nagri',
+    'Syrc': 'Syriac',
+    'Tagb': 'Tagbanwa',
+    'Takr': 'Takri',
+    'Tale': 'Tai_Le',
+    'Talu': 'New_Tai_Lue',
+    'Taml': 'Tamil',
+    'Tang': 'Tangut',
+    'Tavt': 'Tai_Viet',
+    'Telu': 'Telugu',
+    'Tfng': 'Tifinagh',
+    'Tglg': 'Tagalog',
+    'Thaa': 'Thaana',
+    'Thai': 'Thai',
+    'Tibt': 'Tibetan',
+    'Tirh': 'Tirhuta',
+    'Ugar': 'Ugaritic',
+    'Vaii': 'Vai',
+    'Wara': 'Warang_Citi',
+    'Xpeo': 'Old_Persian',
+    'Xsux': 'Cuneiform',
+    'Yiii': 'Yi',
+    'Zanb': 'Zanabazar_Square',
+    'Zinh': 'Inherited',
+    'Zyyy': 'Common',
+    'Zzzz': 'Unknown',
+}
diff --git a/Lib/fontTools/unicodedata/__init__.py b/Lib/fontTools/unicodedata/__init__.py
new file mode 100644
index 0000000..0a69dbd
--- /dev/null
+++ b/Lib/fontTools/unicodedata/__init__.py
@@ -0,0 +1,276 @@
+from __future__ import (
+    print_function, division, absolute_import, unicode_literals)
+from fontTools.misc.py23 import *
+
+import re
+from bisect import bisect_right
+
+try:
+    # use unicodedata backport compatible with python2:
+    # https://github.com/mikekap/unicodedata2
+    from unicodedata2 import *
+except ImportError:  # pragma: no cover
+    # fall back to built-in unicodedata (possibly outdated)
+    from unicodedata import *
+
+from . import Blocks, Scripts, ScriptExtensions, OTTags
+
+
+__all__ = [tostr(s) for s in (
+    # names from built-in unicodedata module
+    "lookup",
+    "name",
+    "decimal",
+    "digit",
+    "numeric",
+    "category",
+    "bidirectional",
+    "combining",
+    "east_asian_width",
+    "mirrored",
+    "decomposition",
+    "normalize",
+    "unidata_version",
+    "ucd_3_2_0",
+    # additonal functions
+    "block",
+    "script",
+    "script_extension",
+    "script_name",
+    "script_code",
+    "script_horizontal_direction",
+    "ot_tags_from_script",
+    "ot_tag_to_script",
+)]
+
+
+def script(char):
+    """ Return the four-letter script code assigned to the Unicode character
+    'char' as string.
+
+    >>> script("a")
+    'Latn'
+    >>> script(",")
+    'Zyyy'
+    >>> script(unichr(0x10FFFF))
+    'Zzzz'
+    """
+    code = byteord(char)
+    # 'bisect_right(a, x, lo=0, hi=len(a))' returns an insertion point which
+    # comes after (to the right of) any existing entries of x in a, and it
+    # partitions array a into two halves so that, for the left side
+    # all(val <= x for val in a[lo:i]), and for the right side
+    # all(val > x for val in a[i:hi]).
+    # Our 'SCRIPT_RANGES' is a sorted list of ranges (only their starting
+    # breakpoints); we want to use `bisect_right` to look up the range that
+    # contains the given codepoint: i.e. whose start is less than or equal
+    # to the codepoint. Thus, we subtract -1 from the index returned.
+    i = bisect_right(Scripts.RANGES, code)
+    return Scripts.VALUES[i-1]
+
+
+def script_extension(char):
+    """ Return the script extension property assigned to the Unicode character
+    'char' as a set of string.
+
+    >>> script_extension("a") == {'Latn'}
+    True
+    >>> script_extension(unichr(0x060C)) == {'Arab', 'Syrc', 'Thaa'}
+    True
+    >>> script_extension(unichr(0x10FFFF)) == {'Zzzz'}
+    True
+    """
+    code = byteord(char)
+    i = bisect_right(ScriptExtensions.RANGES, code)
+    value = ScriptExtensions.VALUES[i-1]
+    if value is None:
+        # code points not explicitly listed for Script Extensions
+        # have as their value the corresponding Script property value
+        return {script(char)}
+    return value
+
+
+def script_name(code, default=KeyError):
+    """ Return the long, human-readable script name given a four-letter
+    Unicode script code.
+
+    If no matching name is found, a KeyError is raised by default.
+
+    You can use the 'default' argument to return a fallback value (e.g.
+    'Unknown' or None) instead of throwing an error.
+    """
+    try:
+        return str(Scripts.NAMES[code].replace("_", " "))
+    except KeyError:
+        if isinstance(default, type) and issubclass(default, KeyError):
+            raise
+        return default
+
+
+_normalize_re = re.compile(r"[-_ ]+")
+
+
+def _normalize_property_name(string):
+    """Remove case, strip space, '-' and '_' for loose matching."""
+    return _normalize_re.sub("", string).lower()
+
+
+_SCRIPT_CODES = {_normalize_property_name(v): k
+                 for k, v in Scripts.NAMES.items()}
+
+
+def script_code(script_name, default=KeyError):
+    """Returns the four-letter Unicode script code from its long name
+
+    If no matching script code is found, a KeyError is raised by default.
+
+    You can use the 'default' argument to return a fallback string (e.g.
+    'Zzzz' or None) instead of throwing an error.
+    """
+    normalized_name = _normalize_property_name(script_name)
+    try:
+        return _SCRIPT_CODES[normalized_name]
+    except KeyError:
+        if isinstance(default, type) and issubclass(default, KeyError):
+            raise
+        return default
+
+
+# The data on script direction is taken from harfbuzz's "hb-common.cc":
+# https://goo.gl/X5FDXC
+# It matches the CLDR "scriptMetadata.txt as of January 2018:
+# http://unicode.org/repos/cldr/trunk/common/properties/scriptMetadata.txt
+RTL_SCRIPTS = {
+    # Unicode-1.1 additions
+    'Arab',  # Arabic
+    'Hebr',  # Hebrew
+
+    # Unicode-3.0 additions
+    'Syrc',  # Syriac
+    'Thaa',  # Thaana
+
+    # Unicode-4.0 additions
+    'Cprt',  # Cypriot
+
+    # Unicode-4.1 additions
+    'Khar',  # Kharoshthi
+
+    # Unicode-5.0 additions
+    'Phnx',  # Phoenician
+    'Nkoo',  # Nko
+
+    # Unicode-5.1 additions
+    'Lydi',  # Lydian
+
+    # Unicode-5.2 additions
+    'Avst',  # Avestan
+    'Armi',  # Imperial Aramaic
+    'Phli',  # Inscriptional Pahlavi
+    'Prti',  # Inscriptional Parthian
+    'Sarb',  # Old South Arabian
+    'Orkh',  # Old Turkic
+    'Samr',  # Samaritan
+
+    # Unicode-6.0 additions
+    'Mand',  # Mandaic
+
+    # Unicode-6.1 additions
+    'Merc',  # Meroitic Cursive
+    'Mero',  # Meroitic Hieroglyphs
+
+    # Unicode-7.0 additions
+    'Mani',  # Manichaean
+    'Mend',  # Mende Kikakui
+    'Nbat',  # Nabataean
+    'Narb',  # Old North Arabian
+    'Palm',  # Palmyrene
+    'Phlp',  # Psalter Pahlavi
+
+    # Unicode-8.0 additions
+    'Hatr',  # Hatran
+    'Hung',  # Old Hungarian
+
+    # Unicode-9.0 additions
+    'Adlm',  # Adlam
+}
+
+def script_horizontal_direction(script_code, default=KeyError):
+    """ Return "RTL" for scripts that contain right-to-left characters
+    according to the Bidi_Class property. Otherwise return "LTR".
+    """
+    if script_code not in Scripts.NAMES:
+        if isinstance(default, type) and issubclass(default, KeyError):
+            raise default(script_code)
+        return default
+    return str("RTL") if script_code in RTL_SCRIPTS else str("LTR")
+
+
+def block(char):
+    """ Return the block property assigned to the Unicode character 'char'
+    as a string.
+
+    >>> block("a")
+    'Basic Latin'
+    >>> block(unichr(0x060C))
+    'Arabic'
+    >>> block(unichr(0xEFFFF))
+    'No_Block'
+    """
+    code = byteord(char)
+    i = bisect_right(Blocks.RANGES, code)
+    return Blocks.VALUES[i-1]
+
+
+def ot_tags_from_script(script_code):
+    """ Return a list of OpenType script tags associated with a given
+    Unicode script code.
+    Return ['DFLT'] script tag for invalid/unknown script codes.
+    """
+    if script_code not in Scripts.NAMES:
+        return [OTTags.DEFAULT_SCRIPT]
+
+    script_tags = [
+        OTTags.SCRIPT_EXCEPTIONS.get(
+            script_code,
+            script_code[0].lower() + script_code[1:]
+        )
+    ]
+    if script_code in OTTags.NEW_SCRIPT_TAGS:
+        script_tags.extend(OTTags.NEW_SCRIPT_TAGS[script_code])
+        script_tags.reverse()  # last in, first out
+
+    return script_tags
+
+
+def ot_tag_to_script(tag):
+    """ Return the Unicode script code for the given OpenType script tag, or
+    None for "DFLT" tag or if there is no Unicode script associated with it.
+    Raises ValueError if the tag is invalid.
+    """
+    tag = tostr(tag).strip()
+    if not tag or " " in tag or len(tag) > 4:
+        raise ValueError("invalid OpenType tag: %r" % tag)
+
+    while len(tag) != 4:
+        tag += str(" ")  # pad with spaces
+
+    if tag == OTTags.DEFAULT_SCRIPT:
+        # it's unclear which Unicode script the "DFLT" OpenType tag maps to,
+        # so here we return None
+        return None
+
+    if tag in OTTags.NEW_SCRIPT_TAGS_REVERSED:
+        return OTTags.NEW_SCRIPT_TAGS_REVERSED[tag]
+
+    # This side of the conversion is fully algorithmic
+
+    # Any spaces at the end of the tag are replaced by repeating the last
+    # letter. Eg 'nko ' -> 'Nkoo'.
+    # Change first char to uppercase
+    script_code = tag[0].upper() + tag[1]
+    for i in range(2, 4):
+        script_code += (script_code[i-1] if tag[i] == " " else tag[i])
+
+    if script_code not in Scripts.NAMES:
+        return None
+    return script_code
diff --git a/Lib/fontTools/varLib/__init__.py b/Lib/fontTools/varLib/__init__.py
new file mode 100644
index 0000000..5f0f9bd
--- /dev/null
+++ b/Lib/fontTools/varLib/__init__.py
@@ -0,0 +1,869 @@
+"""
+Module for dealing with 'gvar'-style font variations, also known as run-time
+interpolation.
+
+The ideas here are very similar to MutatorMath.  There is even code to read
+MutatorMath .designspace files in the varLib.designspace module.
+
+For now, if you run this file on a designspace file, it tries to find
+ttf-interpolatable files for the masters and build a variable-font from
+them.  Such ttf-interpolatable and designspace files can be generated from
+a Glyphs source, eg., using noto-source as an example:
+
+	$ fontmake -o ttf-interpolatable -g NotoSansArabic-MM.glyphs
+
+Then you can make a variable-font this way:
+
+	$ fonttools varLib master_ufo/NotoSansArabic.designspace
+
+API *will* change in near future.
+"""
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.misc.fixedTools import otRound
+from fontTools.misc.arrayTools import Vector
+from fontTools.ttLib import TTFont, newTable
+from fontTools.ttLib.tables._n_a_m_e import NameRecord
+from fontTools.ttLib.tables._f_v_a_r import Axis, NamedInstance
+from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates
+from fontTools.ttLib.tables.ttProgram import Program
+from fontTools.ttLib.tables.TupleVariation import TupleVariation
+from fontTools.ttLib.tables import otTables as ot
+from fontTools.ttLib.tables.otBase import OTTableWriter
+from fontTools.varLib import builder, designspace, models, varStore
+from fontTools.varLib.merger import VariationMerger, _all_equal
+from fontTools.varLib.mvar import MVAR_ENTRIES
+from fontTools.varLib.iup import iup_delta_optimize
+from collections import OrderedDict
+import os.path
+import logging
+from pprint import pformat
+
+log = logging.getLogger("fontTools.varLib")
+
+
+class VarLibError(Exception):
+	pass
+
+#
+# Creation routines
+#
+
+def _add_fvar(font, axes, instances):
+	"""
+	Add 'fvar' table to font.
+
+	axes is an ordered dictionary of DesignspaceAxis objects.
+
+	instances is list of dictionary objects with 'location', 'stylename',
+	and possibly 'postscriptfontname' entries.
+	"""
+
+	assert axes
+	assert isinstance(axes, OrderedDict)
+
+	log.info("Generating fvar")
+
+	fvar = newTable('fvar')
+	nameTable = font['name']
+
+	for a in axes.values():
+		axis = Axis()
+		axis.axisTag = Tag(a.tag)
+		# TODO Skip axes that have no variation.
+		axis.minValue, axis.defaultValue, axis.maxValue = a.minimum, a.default, a.maximum
+		axis.axisNameID = nameTable.addName(tounicode(a.labelname['en']))
+		# TODO:
+		# Replace previous line with the following when the following issues are resolved:
+		# https://github.com/fonttools/fonttools/issues/930
+		# https://github.com/fonttools/fonttools/issues/931
+		# axis.axisNameID = nameTable.addMultilingualName(a.labelname, font)
+		fvar.axes.append(axis)
+
+	for instance in instances:
+		coordinates = instance['location']
+		name = tounicode(instance['stylename'])
+		psname = instance.get('postscriptfontname')
+
+		inst = NamedInstance()
+		inst.subfamilyNameID = nameTable.addName(name)
+		if psname is not None:
+			psname = tounicode(psname)
+			inst.postscriptNameID = nameTable.addName(psname)
+		inst.coordinates = {axes[k].tag:axes[k].map_backward(v) for k,v in coordinates.items()}
+		#inst.coordinates = {axes[k].tag:v for k,v in coordinates.items()}
+		fvar.instances.append(inst)
+
+	assert "fvar" not in font
+	font['fvar'] = fvar
+
+	return fvar
+
+def _add_avar(font, axes):
+	"""
+	Add 'avar' table to font.
+
+	axes is an ordered dictionary of DesignspaceAxis objects.
+	"""
+
+	assert axes
+	assert isinstance(axes, OrderedDict)
+
+	log.info("Generating avar")
+
+	avar = newTable('avar')
+
+	interesting = False
+	for axis in axes.values():
+		# Currently, some rasterizers require that the default value maps
+		# (-1 to -1, 0 to 0, and 1 to 1) be present for all the segment
+		# maps, even when the default normalization mapping for the axis
+		# was not modified.
+		# https://github.com/googlei18n/fontmake/issues/295
+		# https://github.com/fonttools/fonttools/issues/1011
+		# TODO(anthrotype) revert this (and 19c4b37) when issue is fixed
+		curve = avar.segments[axis.tag] = {-1.0: -1.0, 0.0: 0.0, 1.0: 1.0}
+		if not axis.map:
+			continue
+
+		items = sorted(axis.map.items())
+		keys = [item[0] for item in items]
+		vals = [item[1] for item in items]
+
+		# Current avar requirements.  We don't have to enforce
+		# these on the designer and can deduce some ourselves,
+		# but for now just enforce them.
+		assert axis.minimum == min(keys)
+		assert axis.maximum == max(keys)
+		assert axis.default in keys
+		# No duplicates
+		assert len(set(keys)) == len(keys)
+		assert len(set(vals)) == len(vals)
+		# Ascending values
+		assert sorted(vals) == vals
+
+		keys_triple = (axis.minimum, axis.default, axis.maximum)
+		vals_triple = tuple(axis.map_forward(v) for v in keys_triple)
+
+		keys = [models.normalizeValue(v, keys_triple) for v in keys]
+		vals = [models.normalizeValue(v, vals_triple) for v in vals]
+
+		if all(k == v for k, v in zip(keys, vals)):
+			continue
+		interesting = True
+
+		curve.update(zip(keys, vals))
+
+		assert 0.0 in curve and curve[0.0] == 0.0
+		assert -1.0 not in curve or curve[-1.0] == -1.0
+		assert +1.0 not in curve or curve[+1.0] == +1.0
+		# curve.update({-1.0: -1.0, 0.0: 0.0, 1.0: 1.0})
+
+	assert "avar" not in font
+	if not interesting:
+		log.info("No need for avar")
+		avar = None
+	else:
+		font['avar'] = avar
+
+	return avar
+
+def _add_stat(font, axes):
+	# for now we just get the axis tags and nameIDs from the fvar,
+	# so we can reuse the same nameIDs which were defined in there.
+	# TODO make use of 'axes' once it adds style attributes info:
+	# https://github.com/LettError/designSpaceDocument/issues/8
+
+	if "STAT" in font:
+		return
+
+	fvarTable = font['fvar']
+
+	STAT = font["STAT"] = newTable('STAT')
+	stat = STAT.table = ot.STAT()
+	stat.Version = 0x00010002
+
+	axisRecords = []
+	for i, a in enumerate(fvarTable.axes):
+		axis = ot.AxisRecord()
+		axis.AxisTag = Tag(a.axisTag)
+		axis.AxisNameID = a.axisNameID
+		axis.AxisOrdering = i
+		axisRecords.append(axis)
+
+	axisRecordArray = ot.AxisRecordArray()
+	axisRecordArray.Axis = axisRecords
+	# XXX these should not be hard-coded but computed automatically
+	stat.DesignAxisRecordSize = 8
+	stat.DesignAxisCount = len(axisRecords)
+	stat.DesignAxisRecord = axisRecordArray
+
+	# for the elided fallback name, we default to the base style name.
+	# TODO make this user-configurable via designspace document
+	stat.ElidedFallbackNameID = 2
+
+# TODO Move to glyf or gvar table proper
+def _GetCoordinates(font, glyphName):
+	"""font, glyphName --> glyph coordinates as expected by "gvar" table
+
+	The result includes four "phantom points" for the glyph metrics,
+	as mandated by the "gvar" spec.
+	"""
+	glyf = font["glyf"]
+	if glyphName not in glyf.glyphs: return None
+	glyph = glyf[glyphName]
+	if glyph.isComposite():
+		coord = GlyphCoordinates([(getattr(c, 'x', 0),getattr(c, 'y', 0)) for c in glyph.components])
+		control = (glyph.numberOfContours,[c.glyphName for c in glyph.components])
+	else:
+		allData = glyph.getCoordinates(glyf)
+		coord = allData[0]
+		control = (glyph.numberOfContours,)+allData[1:]
+
+	# Add phantom points for (left, right, top, bottom) positions.
+	horizontalAdvanceWidth, leftSideBearing = font["hmtx"].metrics[glyphName]
+	if not hasattr(glyph, 'xMin'):
+		glyph.recalcBounds(glyf)
+	leftSideX = glyph.xMin - leftSideBearing
+	rightSideX = leftSideX + horizontalAdvanceWidth
+	# XXX these are incorrect.  Load vmtx and fix.
+	topSideY = glyph.yMax
+	bottomSideY = -glyph.yMin
+	coord = coord.copy()
+	coord.extend([(leftSideX, 0),
+	              (rightSideX, 0),
+	              (0, topSideY),
+	              (0, bottomSideY)])
+
+	return coord, control
+
+# TODO Move to glyf or gvar table proper
+def _SetCoordinates(font, glyphName, coord):
+	glyf = font["glyf"]
+	assert glyphName in glyf.glyphs
+	glyph = glyf[glyphName]
+
+	# Handle phantom points for (left, right, top, bottom) positions.
+	assert len(coord) >= 4
+	if not hasattr(glyph, 'xMin'):
+		glyph.recalcBounds(glyf)
+	leftSideX = coord[-4][0]
+	rightSideX = coord[-3][0]
+	topSideY = coord[-2][1]
+	bottomSideY = coord[-1][1]
+
+	for _ in range(4):
+		del coord[-1]
+
+	if glyph.isComposite():
+		assert len(coord) == len(glyph.components)
+		for p,comp in zip(coord, glyph.components):
+			if hasattr(comp, 'x'):
+				comp.x,comp.y = p
+	elif glyph.numberOfContours is 0:
+		assert len(coord) == 0
+	else:
+		assert len(coord) == len(glyph.coordinates)
+		glyph.coordinates = coord
+
+	glyph.recalcBounds(glyf)
+
+	horizontalAdvanceWidth = otRound(rightSideX - leftSideX)
+	if horizontalAdvanceWidth < 0:
+		# unlikely, but it can happen, see:
+		# https://github.com/fonttools/fonttools/pull/1198
+		horizontalAdvanceWidth = 0
+	leftSideBearing = otRound(glyph.xMin - leftSideX)
+	# XXX Handle vertical
+	font["hmtx"].metrics[glyphName] = horizontalAdvanceWidth, leftSideBearing
+
+def _add_gvar(font, model, master_ttfs, tolerance=0.5, optimize=True):
+
+	assert tolerance >= 0
+
+	log.info("Generating gvar")
+	assert "gvar" not in font
+	gvar = font["gvar"] = newTable('gvar')
+	gvar.version = 1
+	gvar.reserved = 0
+	gvar.variations = {}
+
+	for glyph in font.getGlyphOrder():
+
+		allData = [_GetCoordinates(m, glyph) for m in master_ttfs]
+		allCoords = [d[0] for d in allData]
+		allControls = [d[1] for d in allData]
+		control = allControls[0]
+		if (any(c != control for c in allControls)):
+			log.warning("glyph %s has incompatible masters; skipping" % glyph)
+			continue
+		del allControls
+
+		# Update gvar
+		gvar.variations[glyph] = []
+		deltas = model.getDeltas(allCoords)
+		supports = model.supports
+		assert len(deltas) == len(supports)
+
+		# Prepare for IUP optimization
+		origCoords = deltas[0]
+		endPts = control[1] if control[0] >= 1 else list(range(len(control[1])))
+
+		for i,(delta,support) in enumerate(zip(deltas[1:], supports[1:])):
+			if all(abs(v) <= tolerance for v in delta.array):
+				continue
+			var = TupleVariation(support, delta)
+			if optimize:
+				delta_opt = iup_delta_optimize(delta, origCoords, endPts, tolerance=tolerance)
+
+				if None in delta_opt:
+					# Use "optimized" version only if smaller...
+					var_opt = TupleVariation(support, delta_opt)
+
+					axis_tags = sorted(support.keys()) # Shouldn't matter that this is different from fvar...?
+					tupleData, auxData, _ = var.compile(axis_tags, [], None)
+					unoptimized_len = len(tupleData) + len(auxData)
+					tupleData, auxData, _ = var_opt.compile(axis_tags, [], None)
+					optimized_len = len(tupleData) + len(auxData)
+
+					if optimized_len < unoptimized_len:
+						var = var_opt
+
+			gvar.variations[glyph].append(var)
+
+def _remove_TTHinting(font):
+	for tag in ("cvar", "cvt ", "fpgm", "prep"):
+		if tag in font:
+			del font[tag]
+	for attr in ("maxTwilightPoints", "maxStorage", "maxFunctionDefs", "maxInstructionDefs", "maxStackElements", "maxSizeOfInstructions"):
+		setattr(font["maxp"], attr, 0)
+	font["maxp"].maxZones = 1
+	font["glyf"].removeHinting()
+	# TODO: Modify gasp table to deactivate gridfitting for all ranges?
+
+def _merge_TTHinting(font, model, master_ttfs, tolerance=0.5):
+
+	log.info("Merging TT hinting")
+	assert "cvar" not in font
+
+	# Check that the existing hinting is compatible
+
+	# fpgm and prep table
+
+	for tag in ("fpgm", "prep"):
+		all_pgms = [m[tag].program for m in master_ttfs if tag in m]
+		if len(all_pgms) == 0:
+			continue
+		if tag in font:
+			font_pgm = font[tag].program
+		else:
+			font_pgm = Program()
+		if any(pgm != font_pgm for pgm in all_pgms):
+			log.warning("Masters have incompatible %s tables, hinting is discarded." % tag)
+			_remove_TTHinting(font)
+			return
+
+	# glyf table
+
+	for name, glyph in font["glyf"].glyphs.items():
+		all_pgms = [
+			m["glyf"][name].program
+			for m in master_ttfs
+			if hasattr(m["glyf"][name], "program")
+		]
+		if not any(all_pgms):
+			continue
+		glyph.expand(font["glyf"])
+		if hasattr(glyph, "program"):
+			font_pgm = glyph.program
+		else:
+			font_pgm = Program()
+		if any(pgm != font_pgm for pgm in all_pgms if pgm):
+			log.warning("Masters have incompatible glyph programs in glyph '%s', hinting is discarded." % name)
+			_remove_TTHinting(font)
+			return
+
+	# cvt table
+
+	all_cvs = [Vector(m["cvt "].values) for m in master_ttfs if "cvt " in m]
+	
+	if len(all_cvs) == 0:
+		# There is no cvt table to make a cvar table from, we're done here.
+		return
+
+	if len(all_cvs) != len(master_ttfs):
+		log.warning("Some masters have no cvt table, hinting is discarded.")
+		_remove_TTHinting(font)
+		return
+
+	num_cvt0 = len(all_cvs[0])
+	if (any(len(c) != num_cvt0 for c in all_cvs)):
+		log.warning("Masters have incompatible cvt tables, hinting is discarded.")
+		_remove_TTHinting(font)
+		return
+
+	# We can build the cvar table now.
+
+	cvar = font["cvar"] = newTable('cvar')
+	cvar.version = 1
+	cvar.variations = []
+
+	deltas = model.getDeltas(all_cvs)
+	supports = model.supports
+	for i,(delta,support) in enumerate(zip(deltas[1:], supports[1:])):
+		delta = [otRound(d) for d in delta]
+		if all(abs(v) <= tolerance for v in delta):
+			continue
+		var = TupleVariation(support, delta)
+		cvar.variations.append(var)
+
+def _add_HVAR(font, model, master_ttfs, axisTags):
+
+	log.info("Generating HVAR")
+
+	hAdvanceDeltas = {}
+	metricses = [m["hmtx"].metrics for m in master_ttfs]
+	for glyph in font.getGlyphOrder():
+		hAdvances = [metrics[glyph][0] for metrics in metricses]
+		# TODO move round somewhere else?
+		hAdvanceDeltas[glyph] = tuple(otRound(d) for d in model.getDeltas(hAdvances)[1:])
+
+	# Direct mapping
+	supports = model.supports[1:]
+	varTupleList = builder.buildVarRegionList(supports, axisTags)
+	varTupleIndexes = list(range(len(supports)))
+	n = len(supports)
+	items = []
+	for glyphName in font.getGlyphOrder():
+		items.append(hAdvanceDeltas[glyphName])
+
+	# Build indirect mapping to save on duplicates, compare both sizes
+	uniq = list(set(items))
+	mapper = {v:i for i,v in enumerate(uniq)}
+	mapping = [mapper[item] for item in items]
+	advanceMapping = builder.buildVarIdxMap(mapping, font.getGlyphOrder())
+
+	# Direct
+	varData = builder.buildVarData(varTupleIndexes, items)
+	directStore = builder.buildVarStore(varTupleList, [varData])
+
+	# Indirect
+	varData = builder.buildVarData(varTupleIndexes, uniq)
+	indirectStore = builder.buildVarStore(varTupleList, [varData])
+	mapping = indirectStore.optimize()
+	advanceMapping.mapping = {k:mapping[v] for k,v in advanceMapping.mapping.items()}
+
+	# Compile both, see which is more compact
+
+	writer = OTTableWriter()
+	directStore.compile(writer, font)
+	directSize = len(writer.getAllData())
+
+	writer = OTTableWriter()
+	indirectStore.compile(writer, font)
+	advanceMapping.compile(writer, font)
+	indirectSize = len(writer.getAllData())
+
+	use_direct = directSize < indirectSize
+
+	# Done; put it all together.
+	assert "HVAR" not in font
+	HVAR = font["HVAR"] = newTable('HVAR')
+	hvar = HVAR.table = ot.HVAR()
+	hvar.Version = 0x00010000
+	hvar.LsbMap = hvar.RsbMap = None
+	if use_direct:
+		hvar.VarStore = directStore
+		hvar.AdvWidthMap = None
+	else:
+		hvar.VarStore = indirectStore
+		hvar.AdvWidthMap = advanceMapping
+
+def _add_MVAR(font, model, master_ttfs, axisTags):
+
+	log.info("Generating MVAR")
+
+	store_builder = varStore.OnlineVarStoreBuilder(axisTags)
+	store_builder.setModel(model)
+
+	records = []
+	lastTableTag = None
+	fontTable = None
+	tables = None
+	for tag, (tableTag, itemName) in sorted(MVAR_ENTRIES.items(), key=lambda kv: kv[1]):
+		if tableTag != lastTableTag:
+			tables = fontTable = None
+			if tableTag in font:
+				# TODO Check all masters have same table set?
+				fontTable = font[tableTag]
+				tables = [master[tableTag] for master in master_ttfs]
+			lastTableTag = tableTag
+		if tables is None:
+			continue
+
+		# TODO support gasp entries
+
+		master_values = [getattr(table, itemName) for table in tables]
+		if _all_equal(master_values):
+			base, varIdx = master_values[0], None
+		else:
+			base, varIdx = store_builder.storeMasters(master_values)
+		setattr(fontTable, itemName, base)
+
+		if varIdx is None:
+			continue
+		log.info('	%s: %s.%s	%s', tag, tableTag, itemName, master_values)
+		rec = ot.MetricsValueRecord()
+		rec.ValueTag = tag
+		rec.VarIdx = varIdx
+		records.append(rec)
+
+	assert "MVAR" not in font
+	if records:
+		store = store_builder.finish()
+		# Optimize
+		mapping = store.optimize()
+		for rec in records:
+			rec.VarIdx = mapping[rec.VarIdx]
+
+		MVAR = font["MVAR"] = newTable('MVAR')
+		mvar = MVAR.table = ot.MVAR()
+		mvar.Version = 0x00010000
+		mvar.Reserved = 0
+		mvar.VarStore = store
+		# XXX these should not be hard-coded but computed automatically
+		mvar.ValueRecordSize = 8
+		mvar.ValueRecordCount = len(records)
+		mvar.ValueRecord = sorted(records, key=lambda r: r.ValueTag)
+
+
+def _merge_OTL(font, model, master_fonts, axisTags):
+
+	log.info("Merging OpenType Layout tables")
+	merger = VariationMerger(model, axisTags, font)
+
+	merger.mergeTables(font, master_fonts, ['GPOS'])
+	# TODO Merge GSUB
+	# TODO Merge GDEF itself!
+	store = merger.store_builder.finish()
+	if not store.VarData:
+		return
+	try:
+		GDEF = font['GDEF'].table
+		assert GDEF.Version <= 0x00010002
+	except KeyError:
+		font['GDEF']= newTable('GDEF')
+		GDEFTable = font["GDEF"] = newTable('GDEF')
+		GDEF = GDEFTable.table = ot.GDEF()
+	GDEF.Version = 0x00010003
+	GDEF.VarStore = store
+
+	# Optimize
+	varidx_map = store.optimize()
+	GDEF.remap_device_varidxes(varidx_map)
+	if 'GPOS' in font:
+		font['GPOS'].table.remap_device_varidxes(varidx_map)
+
+
+
+# Pretty much all of this file should be redesigned and moved inot submodules...
+# Such a mess right now, but kludging along...
+class _DesignspaceAxis(object):
+
+	def __repr__(self):
+		return repr(self.__dict__)
+
+	@staticmethod
+	def _map(v, map):
+		keys = map.keys()
+		if not keys:
+			return v
+		if v in keys:
+			return map[v]
+		k = min(keys)
+		if v < k:
+			return v + map[k] - k
+		k = max(keys)
+		if v > k:
+			return v + map[k] - k
+		# Interpolate
+		a = max(k for k in keys if k < v)
+		b = min(k for k in keys if k > v)
+		va = map[a]
+		vb = map[b]
+		return va + (vb - va) * (v - a) / (b - a)
+
+	def map_forward(self, v):
+		if self.map is None: return v
+		return self._map(v, self.map)
+
+	def map_backward(self, v):
+		if self.map is None: return v
+		map = {v:k for k,v in self.map.items()}
+		return self._map(v, map)
+
+
+def load_designspace(designspace_filename):
+
+	ds = designspace.load(designspace_filename)
+	axes = ds.get('axes')
+	masters = ds.get('sources')
+	if not masters:
+		raise VarLibError("no sources found in .designspace")
+	instances = ds.get('instances', [])
+
+	standard_axis_map = OrderedDict([
+		('weight',  ('wght', {'en':'Weight'})),
+		('width',   ('wdth', {'en':'Width'})),
+		('slant',   ('slnt', {'en':'Slant'})),
+		('optical', ('opsz', {'en':'Optical Size'})),
+		])
+
+
+	# Setup axes
+	axis_objects = OrderedDict()
+	if axes is not None:
+		for axis_dict in axes:
+			axis_name = axis_dict.get('name')
+			if not axis_name:
+				axis_name = axis_dict['name'] = axis_dict['tag']
+			if 'map' not in axis_dict:
+				axis_dict['map'] = None
+			else:
+				axis_dict['map'] = {m['input']:m['output'] for m in axis_dict['map']}
+
+			if axis_name in standard_axis_map:
+				if 'tag' not in axis_dict:
+					axis_dict['tag'] = standard_axis_map[axis_name][0]
+				if 'labelname' not in axis_dict:
+					axis_dict['labelname'] = standard_axis_map[axis_name][1].copy()
+
+			axis = _DesignspaceAxis()
+			for item in ['name', 'tag', 'minimum', 'default', 'maximum', 'map']:
+				assert item in axis_dict, 'Axis does not have "%s"' % item
+			if 'labelname' not in axis_dict:
+				axis_dict['labelname'] = {'en': axis_name}
+			axis.__dict__ = axis_dict
+			axis_objects[axis_name] = axis
+	else:
+		# No <axes> element. Guess things...
+		base_idx = None
+		for i,m in enumerate(masters):
+			if 'info' in m and m['info']['copy']:
+				assert base_idx is None
+				base_idx = i
+		assert base_idx is not None, "Cannot find 'base' master; Either add <axes> element to .designspace document, or add <info> element to one of the sources in the .designspace document."
+
+		master_locs = [o['location'] for o in masters]
+		base_loc = master_locs[base_idx]
+		axis_names = set(base_loc.keys())
+		assert all(name in standard_axis_map for name in axis_names), "Non-standard axis found and there exist no <axes> element."
+
+		for name,(tag,labelname) in standard_axis_map.items():
+			if name not in axis_names:
+				continue
+
+			axis = _DesignspaceAxis()
+			axis.name = name
+			axis.tag = tag
+			axis.labelname = labelname.copy()
+			axis.default = base_loc[name]
+			axis.minimum = min(m[name] for m in master_locs if name in m)
+			axis.maximum = max(m[name] for m in master_locs if name in m)
+			axis.map = None
+			# TODO Fill in weight / width mapping from OS/2 table? Need loading fonts...
+			axis_objects[name] = axis
+		del base_idx, base_loc, axis_names, master_locs
+	axes = axis_objects
+	del axis_objects
+	log.info("Axes:\n%s", pformat(axes))
+
+
+	# Check all master and instance locations are valid and fill in defaults
+	for obj in masters+instances:
+		obj_name = obj.get('name', obj.get('stylename', ''))
+		loc = obj['location']
+		for axis_name in loc.keys():
+			assert axis_name in axes, "Location axis '%s' unknown for '%s'." % (axis_name, obj_name)
+		for axis_name,axis in axes.items():
+			if axis_name not in loc:
+				loc[axis_name] = axis.default
+			else:
+				v = axis.map_backward(loc[axis_name])
+				assert axis.minimum <= v <= axis.maximum, "Location for axis '%s' (mapped to %s) out of range for '%s' [%s..%s]" % (axis_name, v, obj_name, axis.minimum, axis.maximum)
+
+
+	# Normalize master locations
+
+	internal_master_locs = [o['location'] for o in masters]
+	log.info("Internal master locations:\n%s", pformat(internal_master_locs))
+
+	# TODO This mapping should ideally be moved closer to logic in _add_fvar/avar
+	internal_axis_supports = {}
+	for axis in axes.values():
+		triple = (axis.minimum, axis.default, axis.maximum)
+		internal_axis_supports[axis.name] = [axis.map_forward(v) for v in triple]
+	log.info("Internal axis supports:\n%s", pformat(internal_axis_supports))
+
+	normalized_master_locs = [models.normalizeLocation(m, internal_axis_supports) for m in internal_master_locs]
+	log.info("Normalized master locations:\n%s", pformat(normalized_master_locs))
+
+
+	# Find base master
+	base_idx = None
+	for i,m in enumerate(normalized_master_locs):
+		if all(v == 0 for v in m.values()):
+			assert base_idx is None
+			base_idx = i
+	assert base_idx is not None, "Base master not found; no master at default location?"
+	log.info("Index of base master: %s", base_idx)
+
+	return axes, internal_axis_supports, base_idx, normalized_master_locs, masters, instances
+
+
+def build(designspace_filename, master_finder=lambda s:s, exclude=[], optimize=True):
+	"""
+	Build variation font from a designspace file.
+
+	If master_finder is set, it should be a callable that takes master
+	filename as found in designspace file and map it to master font
+	binary as to be opened (eg. .ttf or .otf).
+	"""
+
+	axes, internal_axis_supports, base_idx, normalized_master_locs, masters, instances = load_designspace(designspace_filename)
+
+	log.info("Building variable font")
+	log.info("Loading master fonts")
+	basedir = os.path.dirname(designspace_filename)
+	master_ttfs = [master_finder(os.path.join(basedir, m['filename'])) for m in masters]
+	master_fonts = [TTFont(ttf_path) for ttf_path in master_ttfs]
+	# Reload base font as target font
+	vf = TTFont(master_ttfs[base_idx])
+
+	# TODO append masters as named-instances as well; needs .designspace change.
+	fvar = _add_fvar(vf, axes, instances)
+	if 'STAT' not in exclude:
+		_add_stat(vf, axes)
+	if 'avar' not in exclude:
+		_add_avar(vf, axes)
+	del instances
+
+	# Map from axis names to axis tags...
+	normalized_master_locs = [{axes[k].tag:v for k,v in loc.items()} for loc in normalized_master_locs]
+	#del axes
+	# From here on, we use fvar axes only
+	axisTags = [axis.axisTag for axis in fvar.axes]
+
+	# Assume single-model for now.
+	model = models.VariationModel(normalized_master_locs, axisOrder=axisTags)
+	assert 0 == model.mapping[base_idx]
+
+	log.info("Building variations tables")
+	if 'MVAR' not in exclude:
+		_add_MVAR(vf, model, master_fonts, axisTags)
+	if 'HVAR' not in exclude:
+		_add_HVAR(vf, model, master_fonts, axisTags)
+	if 'GDEF' not in exclude or 'GPOS' not in exclude:
+		_merge_OTL(vf, model, master_fonts, axisTags)
+	if 'gvar' not in exclude and 'glyf' in vf:
+		_add_gvar(vf, model, master_fonts, optimize=optimize)
+	if 'cvar' not in exclude and 'glyf' in vf:
+		_merge_TTHinting(vf, model, master_fonts)
+
+	for tag in exclude:
+		if tag in vf:
+			del vf[tag]
+
+	return vf, model, master_ttfs
+
+
+class MasterFinder(object):
+
+	def __init__(self, template):
+		self.template = template
+
+	def __call__(self, src_path):
+		fullname = os.path.abspath(src_path)
+		dirname, basename = os.path.split(fullname)
+		stem, ext = os.path.splitext(basename)
+		path = self.template.format(
+			fullname=fullname,
+			dirname=dirname,
+			basename=basename,
+			stem=stem,
+			ext=ext,
+		)
+		return os.path.normpath(path)
+
+
+def main(args=None):
+	from argparse import ArgumentParser
+	from fontTools import configLogger
+
+	parser = ArgumentParser(prog='varLib')
+	parser.add_argument('designspace')
+	parser.add_argument(
+		'-o',
+		metavar='OUTPUTFILE',
+		dest='outfile',
+		default=None,
+		help='output file'
+	)
+	parser.add_argument(
+		'-x',
+		metavar='TAG',
+		dest='exclude',
+		action='append',
+		default=[],
+		help='exclude table'
+	)
+	parser.add_argument(
+		'--disable-iup',
+		dest='optimize',
+		action='store_false',
+		help='do not perform IUP optimization'
+	)
+	parser.add_argument(
+		'--master-finder',
+		default='master_ttf_interpolatable/{stem}.ttf',
+		help=(
+			'templated string used for finding binary font '
+			'files given the source file names defined in the '
+			'designspace document. The following special strings '
+			'are defined: {fullname} is the absolute source file '
+			'name; {basename} is the file name without its '
+			'directory; {stem} is the basename without the file '
+			'extension; {ext} is the source file extension; '
+			'{dirname} is the directory of the absolute file '
+			'name. The default value is "%(default)s".'
+		)
+	)
+	options = parser.parse_args(args)
+
+	# TODO: allow user to configure logging via command-line options
+	configLogger(level="INFO")
+
+	designspace_filename = options.designspace
+	finder = MasterFinder(options.master_finder)
+	outfile = options.outfile
+	if outfile is None:
+		outfile = os.path.splitext(designspace_filename)[0] + '-VF.ttf'
+
+	vf, model, master_ttfs = build(
+		designspace_filename,
+		finder,
+		exclude=options.exclude,
+		optimize=options.optimize
+	)
+
+	log.info("Saving variation font %s", outfile)
+	vf.save(outfile)
+
+
+if __name__ == "__main__":
+	import sys
+	if len(sys.argv) > 1:
+		sys.exit(main())
+	import doctest
+	sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/varLib/__main__.py b/Lib/fontTools/varLib/__main__.py
new file mode 100644
index 0000000..5cf05e5
--- /dev/null
+++ b/Lib/fontTools/varLib/__main__.py
@@ -0,0 +1,7 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+import sys
+from fontTools.varLib import main
+
+if __name__ == '__main__':
+	sys.exit(main())
diff --git a/Lib/fontTools/varLib/builder.py b/Lib/fontTools/varLib/builder.py
new file mode 100644
index 0000000..90e3337
--- /dev/null
+++ b/Lib/fontTools/varLib/builder.py
@@ -0,0 +1,101 @@
+from __future__ import print_function, division, absolute_import
+from fontTools import ttLib
+from fontTools.ttLib.tables import otTables as ot
+
+# VariationStore
+
+def buildVarRegionAxis(axisSupport):
+	self = ot.VarRegionAxis()
+	self.StartCoord, self.PeakCoord, self.EndCoord = [float(v) for v in axisSupport]
+	return self
+
+def buildVarRegion(support, axisTags):
+	assert all(tag in axisTags for tag in support.keys()), ("Unknown axis tag found.", support, axisTags)
+	self = ot.VarRegion()
+	self.VarRegionAxis = []
+	for tag in axisTags:
+		self.VarRegionAxis.append(buildVarRegionAxis(support.get(tag, (0,0,0))))
+	self.VarRegionAxisCount = len(self.VarRegionAxis)
+	return self
+
+def buildVarRegionList(supports, axisTags):
+	self = ot.VarRegionList()
+	self.RegionAxisCount = len(axisTags)
+	self.Region = []
+	for support in supports:
+		self.Region.append(buildVarRegion(support, axisTags))
+	self.RegionCount = len(self.Region)
+	return self
+
+
+def _reorderItem(lst, narrows, zeroes):
+	out = []
+	count = len(lst)
+	for i in range(count):
+		if i not in narrows:
+			out.append(lst[i])
+	for i in range(count):
+		if i in narrows  and i not in zeroes:
+			out.append(lst[i])
+	return out
+
+def VarData_CalculateNumShorts(self, optimize=True):
+	count = self.VarRegionCount
+	items = self.Item
+	narrows = set(range(count))
+	zeroes = set(range(count))
+	for item in items:
+		wides = [i for i in narrows if not (-128 <= item[i] <= 127)]
+		narrows.difference_update(wides)
+		nonzeroes = [i for i in zeroes if item[i]]
+		zeroes.difference_update(nonzeroes)
+		if not narrows and not zeroes:
+			break
+	if optimize:
+		# Reorder columns such that all SHORT columns come before UINT8
+		self.VarRegionIndex = _reorderItem(self.VarRegionIndex, narrows, zeroes)
+		self.VarRegionCount = len(self.VarRegionIndex)
+		for i in range(self.ItemCount):
+			items[i] = _reorderItem(items[i], narrows, zeroes)
+		self.NumShorts = count - len(narrows)
+	else:
+		wides = set(range(count)) - narrows
+		self.NumShorts = 1+max(wides) if wides else 0
+	return self
+
+def buildVarData(varRegionIndices, items, optimize=True):
+	self = ot.VarData()
+	self.VarRegionIndex = list(varRegionIndices)
+	regionCount = self.VarRegionCount = len(self.VarRegionIndex)
+	records = self.Item = []
+	if items:
+		for item in items:
+			assert len(item) == regionCount
+			records.append(list(item))
+	self.ItemCount = len(self.Item)
+	VarData_CalculateNumShorts(self, optimize=optimize)
+	return self
+
+
+def buildVarStore(varRegionList, varDataList):
+	self = ot.VarStore()
+	self.Format = 1
+	self.VarRegionList = varRegionList
+	self.VarData = list(varDataList)
+	self.VarDataCount = len(self.VarData)
+	return self
+
+
+# Variation helpers
+
+def buildVarIdxMap(varIdxes, glyphOrder):
+	self = ot.VarIdxMap()
+	self.mapping = {g:v for g,v in zip(glyphOrder, varIdxes)}
+	return self
+
+def buildVarDevTable(varIdx):
+	self = ot.Device()
+	self.DeltaFormat = 0x8000
+	self.StartSize = varIdx >> 16
+	self.EndSize = varIdx & 0xFFFF
+	return self
diff --git a/Lib/fontTools/varLib/designspace.py b/Lib/fontTools/varLib/designspace.py
new file mode 100644
index 0000000..7f235af
--- /dev/null
+++ b/Lib/fontTools/varLib/designspace.py
@@ -0,0 +1,113 @@
+"""Rudimentary support for loading MutatorMath .designspace files."""
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+try:
+	import xml.etree.cElementTree as ET
+except ImportError:
+	import xml.etree.ElementTree as ET
+
+__all__ = ['load', 'loads']
+
+namespaces = {'xml': '{http://www.w3.org/XML/1998/namespace}'}
+
+
+def _xml_parse_location(et):
+	loc = {}
+	for dim in et.find('location'):
+		assert dim.tag == 'dimension'
+		name = dim.attrib['name']
+		value = float(dim.attrib['xvalue'])
+		assert name not in loc
+		loc[name] = value
+	return loc
+
+
+def _load_item(et):
+	item = dict(et.attrib)
+	for element in et:
+		if element.tag == 'location':
+			value = _xml_parse_location(et)
+		else:
+			value = {}
+			if 'copy' in element.attrib:
+				value['copy'] = bool(int(element.attrib['copy']))
+			# TODO load more?!
+		item[element.tag] = value
+	return item
+
+
+def _xml_parse_axis_or_map(element):
+	dic = {}
+	for name in element.attrib:
+		if name in ['name', 'tag']:
+			dic[name] = element.attrib[name]
+		else:
+			dic[name] = float(element.attrib[name])
+	return dic
+
+
+def _load_axis(et):
+	item = _xml_parse_axis_or_map(et)
+	maps = []
+	labelnames = {}
+	for element in et:
+		assert element.tag in ['labelname', 'map']
+		if element.tag == 'labelname':
+			lang = element.attrib["{0}lang".format(namespaces['xml'])]
+			labelnames[lang] = element.text
+		elif element.tag == 'map':
+			maps.append(_xml_parse_axis_or_map(element))
+	if labelnames:
+		item['labelname'] = labelnames
+	if maps:
+		item['map'] = maps
+	return item
+
+
+def _load(et):
+	designspace = {}
+	ds = et.getroot()
+
+	axes_element = ds.find('axes')
+	if axes_element is not None:
+		axes = []
+		for et in axes_element:
+			axes.append(_load_axis(et))
+		designspace['axes'] = axes
+
+	sources_element = ds.find('sources')
+	if sources_element is not None:
+		sources = []
+		for et in sources_element:
+			sources.append(_load_item(et))
+		designspace['sources'] = sources
+
+	instances_element = ds.find('instances')
+	if instances_element is not None:
+		instances = []
+		for et in instances_element:
+			instances.append(_load_item(et))
+		designspace['instances'] = instances
+
+	return designspace
+
+
+def load(filename):
+	"""Load designspace from a file name or object.
+	   Returns a dictionary containing three (optional) items:
+	   - list of "axes"
+	   - list of "sources" (aka masters)
+	   - list of "instances"
+	"""
+	return _load(ET.parse(filename))
+
+
+def loads(string):
+	"""Load designspace from a string."""
+	return _load(ET.fromstring(string))
+
+if __name__ == '__main__':
+	import sys
+	from pprint import pprint
+	for f in sys.argv[1:]:
+		pprint(load(f))
diff --git a/Lib/fontTools/varLib/featureVars.py b/Lib/fontTools/varLib/featureVars.py
new file mode 100644
index 0000000..6033621
--- /dev/null
+++ b/Lib/fontTools/varLib/featureVars.py
@@ -0,0 +1,392 @@
+"""Module to build FeatureVariation tables:
+https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#featurevariations-table
+
+NOTE: The API is experimental and subject to change.
+"""
+from __future__ import print_function, absolute_import, division
+
+from fontTools.ttLib import newTable
+from fontTools.ttLib.tables import otTables as ot
+from fontTools.otlLib.builder import buildLookup, buildSingleSubstSubtable
+import itertools
+
+
+def addFeatureVariations(font, conditionalSubstitutions):
+    """Add conditional substitutions to a Variable Font.
+
+    The `conditionalSubstitutions` argument is a list of (Region, Substitutions)
+    tuples.
+
+    A Region is a list of Spaces. A Space is a dict mapping axisTags to
+    (minValue, maxValue) tuples. Irrelevant axes may be omitted.
+    A Space represents a 'rectangular' subset of an N-dimensional design space.
+    A Region represents a more complex subset of an N-dimensional design space,
+    ie. the union of all the Spaces in the Region.
+    For efficiency, Spaces within a Region should ideally not overlap, but
+    functionality is not compromised if they do.
+
+    The minimum and maximum values are expressed in normalized coordinates.
+
+    A Substitution is a dict mapping source glyph names to substitute glyph names.
+    """
+
+    # Example:
+    #
+    #     >>> f = TTFont(srcPath)
+    #     >>> condSubst = [
+    #     ...     # A list of (Region, Substitution) tuples.
+    #     ...     ([{"wght": (0.5, 1.0)}], {"dollar": "dollar.rvrn"}),
+    #     ...     ([{"wdth": (0.5, 1.0)}], {"cent": "cent.rvrn"}),
+    #     ... ]
+    #     >>> addFeatureVariations(f, condSubst)
+    #     >>> f.save(dstPath)
+
+    # Since the FeatureVariations table will only ever match one rule at a time,
+    # we will make new rules for all possible combinations of our input, so we
+    # can indirectly support overlapping rules.
+    explodedConditionalSubstitutions = []
+    for combination in iterAllCombinations(len(conditionalSubstitutions)):
+        regions = []
+        lookups = []
+        for index in combination:
+            regions.append(conditionalSubstitutions[index][0])
+            lookups.append(conditionalSubstitutions[index][1])
+        if not regions:
+            continue
+        intersection = regions[0]
+        for region in regions[1:]:
+            intersection = intersectRegions(intersection, region)
+        for space in intersection:
+            # Remove default values, so we don't generate redundant ConditionSets
+            space = cleanupSpace(space)
+            if space:
+                explodedConditionalSubstitutions.append((space, lookups))
+
+    addFeatureVariationsRaw(font, explodedConditionalSubstitutions)
+
+
+def iterAllCombinations(numRules):
+    """Given a number of rules, yield all the combinations of indices, sorted
+    by decreasing length, so we get the most specialized rules first.
+
+        >>> list(iterAllCombinations(0))
+        []
+        >>> list(iterAllCombinations(1))
+        [(0,)]
+        >>> list(iterAllCombinations(2))
+        [(0, 1), (0,), (1,)]
+        >>> list(iterAllCombinations(3))
+        [(0, 1, 2), (0, 1), (0, 2), (1, 2), (0,), (1,), (2,)]
+    """
+    indices = range(numRules)
+    for length in range(numRules, 0, -1):
+        for combinations in itertools.combinations(indices, length):
+            yield combinations
+
+
+#
+# Region and Space support
+#
+# Terminology:
+#
+# A 'Space' is a dict representing a "rectangular" bit of N-dimensional space.
+# The keys in the dict are axis tags, the values are (minValue, maxValue) tuples.
+# Missing dimensions (keys) are substituted by the default min and max values
+# from the corresponding axes.
+#
+# A 'Region' is a list of Space dicts, representing the union of the Spaces,
+# therefore representing a more complex subset of design space.
+#
+
+def intersectRegions(region1, region2):
+    """Return the region intersecting `region1` and `region2`.
+
+        >>> intersectRegions([], [])
+        []
+        >>> intersectRegions([{'wdth': (0.0, 1.0)}], [])
+        []
+        >>> expected = [{'wdth': (0.0, 1.0), 'wght': (-1.0, 0.0)}]
+        >>> expected == intersectRegions([{'wdth': (0.0, 1.0)}], [{'wght': (-1.0, 0.0)}])
+        True
+        >>> expected = [{'wdth': (0.0, 1.0), 'wght': (-0.5, 0.0)}]
+        >>> expected == intersectRegions([{'wdth': (0.0, 1.0), 'wght': (-0.5, 0.5)}], [{'wght': (-1.0, 0.0)}])
+        True
+        >>> intersectRegions(
+        ...     [{'wdth': (0.0, 1.0), 'wght': (-0.5, 0.5)}],
+        ...     [{'wdth': (-1.0, 0.0), 'wght': (-1.0, 0.0)}])
+        []
+
+    """
+    region = []
+    for space1 in region1:
+        for space2 in region2:
+            space = intersectSpaces(space1, space2)
+            if space is not None:
+                region.append(space)
+    return region
+
+
+def intersectSpaces(space1, space2):
+    """Return the space intersected by `space1` and `space2`, or None if there
+    is no intersection.
+
+        >>> intersectSpaces({}, {})
+        {}
+        >>> intersectSpaces({'wdth': (-0.5, 0.5)}, {})
+        {'wdth': (-0.5, 0.5)}
+        >>> intersectSpaces({'wdth': (-0.5, 0.5)}, {'wdth': (0.0, 1.0)})
+        {'wdth': (0.0, 0.5)}
+        >>> expected = {'wdth': (0.0, 0.5), 'wght': (0.25, 0.5)}
+        >>> expected == intersectSpaces({'wdth': (-0.5, 0.5), 'wght': (0.0, 0.5)}, {'wdth': (0.0, 1.0), 'wght': (0.25, 0.75)})
+        True
+        >>> expected = {'wdth': (-0.5, 0.5), 'wght': (0.0, 1.0)}
+        >>> expected == intersectSpaces({'wdth': (-0.5, 0.5)}, {'wght': (0.0, 1.0)})
+        True
+        >>> intersectSpaces({'wdth': (-0.5, 0)}, {'wdth': (0.1, 0.5)})
+
+    """
+    space = {}
+    space.update(space1)
+    space.update(space2)
+    for axisTag in set(space1) & set(space2):
+        min1, max1 = space1[axisTag]
+        min2, max2 = space2[axisTag]
+        minimum = max(min1, min2)
+        maximum = min(max1, max2)
+        if not minimum < maximum:
+            return None
+        space[axisTag] = minimum, maximum
+    return space
+
+
+def cleanupSpace(space):
+    """Return a sparse copy of `space`, without redundant (default) values.
+
+        >>> cleanupSpace({})
+        {}
+        >>> cleanupSpace({'wdth': (0.0, 1.0)})
+        {'wdth': (0.0, 1.0)}
+        >>> cleanupSpace({'wdth': (-1.0, 1.0)})
+        {}
+
+    """
+    return {tag: limit for tag, limit in space.items() if limit != (-1.0, 1.0)}
+
+
+#
+# Low level implementation
+#
+
+def addFeatureVariationsRaw(font, conditionalSubstitutions):
+    """Low level implementation of addFeatureVariations that directly
+    models the possibilities of the FeatureVariations table."""
+
+    #
+    # assert there is no 'rvrn' feature
+    # make dummy 'rvrn' feature with no lookups
+    # sort features, get 'rvrn' feature index
+    # add 'rvrn' feature to all scripts
+    # make lookups
+    # add feature variations
+    #
+
+    if "GSUB" not in font:
+        font["GSUB"] = buildGSUB()
+
+    gsub = font["GSUB"].table
+
+    if gsub.Version < 0x00010001:
+        gsub.Version = 0x00010001  # allow gsub.FeatureVariations
+
+    gsub.FeatureVariations = None  # delete any existing FeatureVariations
+
+    for feature in gsub.FeatureList.FeatureRecord:
+        assert feature.FeatureTag != 'rvrn'
+
+    rvrnFeature = buildFeatureRecord('rvrn', [])
+    gsub.FeatureList.FeatureRecord.append(rvrnFeature)
+
+    sortFeatureList(gsub)
+    rvrnFeatureIndex = gsub.FeatureList.FeatureRecord.index(rvrnFeature)
+
+    for scriptRecord in gsub.ScriptList.ScriptRecord:
+        for langSys in [scriptRecord.Script.DefaultLangSys] + scriptRecord.Script.LangSysRecord:
+            langSys.FeatureIndex.append(rvrnFeatureIndex)
+
+    # setup lookups
+
+    # turn substitution dicts into tuples of tuples, so they are hashable
+    conditionalSubstitutions, allSubstitutions = makeSubstitutionsHashable(conditionalSubstitutions)
+
+    lookupMap = buildSubstitutionLookups(gsub, allSubstitutions)
+
+    axisIndices = {axis.axisTag: axisIndex for axisIndex, axis in enumerate(font["fvar"].axes)}
+
+    featureVariationRecords = []
+    for conditionSet, substitutions in conditionalSubstitutions:
+        conditionTable = []
+        for axisTag, (minValue, maxValue) in sorted(conditionSet.items()):
+            assert minValue < maxValue
+            ct = buildConditionTable(axisIndices[axisTag], minValue, maxValue)
+            conditionTable.append(ct)
+
+        lookupIndices = [lookupMap[subst] for subst in substitutions]
+        record = buildFeatureTableSubstitutionRecord(rvrnFeatureIndex, lookupIndices)
+        featureVariationRecords.append(buildFeatureVariationRecord(conditionTable, [record]))
+
+    gsub.FeatureVariations = buildFeatureVariations(featureVariationRecords)
+
+
+#
+# Building GSUB/FeatureVariations internals
+#
+
+def buildGSUB():
+    """Build a GSUB table from scratch."""
+    fontTable = newTable("GSUB")
+    gsub = fontTable.table = ot.GSUB()
+    gsub.Version = 0x00010001  # allow gsub.FeatureVariations
+
+    gsub.ScriptList = ot.ScriptList()
+    gsub.ScriptList.ScriptRecord = []
+    gsub.FeatureList = ot.FeatureList()
+    gsub.FeatureList.FeatureRecord = []
+    gsub.LookupList = ot.LookupList()
+    gsub.LookupList.Lookup = []
+
+    srec = ot.ScriptRecord()
+    srec.ScriptTag = 'DFLT'
+    srec.Script = ot.Script()
+    srec.Script.DefaultLangSys = None
+    srec.Script.LangSysRecord = []
+
+    langrec = ot.LangSysRecord()
+    langrec.LangSys = ot.LangSys()
+    langrec.LangSys.ReqFeatureIndex = 0xFFFF
+    langrec.LangSys.FeatureIndex = [0]
+    srec.Script.DefaultLangSys = langrec.LangSys
+
+    gsub.ScriptList.ScriptRecord.append(srec)
+    gsub.FeatureVariations = None
+
+    return fontTable
+
+
+def makeSubstitutionsHashable(conditionalSubstitutions):
+    """Turn all the substitution dictionaries in sorted tuples of tuples so
+    they are hashable, to detect duplicates so we don't write out redundant
+    data."""
+    allSubstitutions = set()
+    condSubst = []
+    for conditionSet, substitutionMaps in conditionalSubstitutions:
+        substitutions = []
+        for substitutionMap in substitutionMaps:
+            subst = tuple(sorted(substitutionMap.items()))
+            substitutions.append(subst)
+            allSubstitutions.add(subst)
+        condSubst.append((conditionSet, substitutions))
+    return condSubst, sorted(allSubstitutions)
+
+
+def buildSubstitutionLookups(gsub, allSubstitutions):
+    """Build the lookups for the glyph substitutions, return a dict mapping
+    the substitution to lookup indices."""
+    firstIndex = len(gsub.LookupList.Lookup)
+    lookupMap = {}
+    for i, substitutionMap in enumerate(allSubstitutions):
+        lookupMap[substitutionMap] = i + firstIndex
+
+    for subst in allSubstitutions:
+        substMap = dict(subst)
+        lookup = buildLookup([buildSingleSubstSubtable(substMap)])
+        gsub.LookupList.Lookup.append(lookup)
+        assert gsub.LookupList.Lookup[lookupMap[subst]] is lookup
+    return lookupMap
+
+
+def buildFeatureVariations(featureVariationRecords):
+    """Build the FeatureVariations subtable."""
+    fv = ot.FeatureVariations()
+    fv.Version = 0x00010000
+    fv.FeatureVariationRecord = featureVariationRecords
+    return fv
+
+
+def buildFeatureRecord(featureTag, lookupListIndices):
+    """Build a FeatureRecord."""
+    fr = ot.FeatureRecord()
+    fr.FeatureTag = featureTag
+    fr.Feature = ot.Feature()
+    fr.Feature.LookupListIndex = lookupListIndices
+    return fr
+
+
+def buildFeatureVariationRecord(conditionTable, substitutionRecords):
+    """Build a FeatureVariationRecord."""
+    fvr = ot.FeatureVariationRecord()
+    fvr.ConditionSet = ot.ConditionSet()
+    fvr.ConditionSet.ConditionTable = conditionTable
+    fvr.FeatureTableSubstitution = ot.FeatureTableSubstitution()
+    fvr.FeatureTableSubstitution.Version = 0x00010001
+    fvr.FeatureTableSubstitution.SubstitutionRecord = substitutionRecords
+    return fvr
+
+
+def buildFeatureTableSubstitutionRecord(featureIndex, lookupListIndices):
+    """Build a FeatureTableSubstitutionRecord."""
+    ftsr = ot.FeatureTableSubstitutionRecord()
+    ftsr.FeatureIndex = featureIndex
+    ftsr.Feature = ot.Feature()
+    ftsr.Feature.LookupListIndex = lookupListIndices
+    return ftsr
+
+
+def buildConditionTable(axisIndex, filterRangeMinValue, filterRangeMaxValue):
+    """Build a ConditionTable."""
+    ct = ot.ConditionTable()
+    ct.Format = 1
+    ct.AxisIndex = axisIndex
+    ct.FilterRangeMinValue = filterRangeMinValue
+    ct.FilterRangeMaxValue = filterRangeMaxValue
+    return ct
+
+
+def sortFeatureList(table):
+    """Sort the feature list by feature tag, and remap the feature indices
+    elsewhere. This is needed after the feature list has been modified.
+    """
+    # decorate, sort, undecorate, because we need to make an index remapping table
+    tagIndexFea = [(fea.FeatureTag, index, fea) for index, fea in enumerate(table.FeatureList.FeatureRecord)]
+    tagIndexFea.sort()
+    table.FeatureList.FeatureRecord = [fea for tag, index, fea in tagIndexFea]
+    featureRemap = dict(zip([index for tag, index, fea in tagIndexFea], range(len(tagIndexFea))))
+
+    # Remap the feature indices
+    remapFeatures(table, featureRemap)
+
+
+def remapFeatures(table, featureRemap):
+    """Go through the scripts list, and remap feature indices."""
+    for scriptIndex, script in enumerate(table.ScriptList.ScriptRecord):
+        defaultLangSys = script.Script.DefaultLangSys
+        if defaultLangSys is not None:
+            _remapLangSys(defaultLangSys, featureRemap)
+        for langSysRecordIndex, langSysRec in enumerate(script.Script.LangSysRecord):
+            langSys = langSysRec.LangSys
+            _remapLangSys(langSys, featureRemap)
+
+    if hasattr(table, "FeatureVariations") and table.FeatureVariations is not None:
+        for fvr in table.FeatureVariations.FeatureVariationRecord:
+            for ftsr in fvr.FeatureTableSubstitution.SubstitutionRecord:
+                ftsr.FeatureIndex = featureRemap[ftsr.FeatureIndex]
+
+
+def _remapLangSys(langSys, featureRemap):
+    if langSys.ReqFeatureIndex != 0xffff:
+        langSys.ReqFeatureIndex = featureRemap[langSys.ReqFeatureIndex]
+    langSys.FeatureIndex = [featureRemap[index] for index in langSys.FeatureIndex]
+
+
+if __name__ == "__main__":
+    import doctest
+    doctest.testmod()
diff --git a/Lib/fontTools/varLib/interpolatable.py b/Lib/fontTools/varLib/interpolatable.py
new file mode 100644
index 0000000..d4feed2
--- /dev/null
+++ b/Lib/fontTools/varLib/interpolatable.py
@@ -0,0 +1,181 @@
+"""
+Tool to find wrong contour order between different masters, and
+other interpolatability (or lack thereof) issues.
+
+Call as:
+$ fonttools varLib.interpolatable font1 font2 ...
+"""
+
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+
+from fontTools.pens.basePen import AbstractPen, BasePen
+from fontTools.pens.recordingPen import RecordingPen
+from fontTools.pens.statisticsPen import StatisticsPen
+import itertools
+
+
+class PerContourPen(BasePen):
+	def __init__(self, Pen, glyphset=None):
+		BasePen.__init__(self, glyphset)
+		self._glyphset = glyphset
+		self._Pen = Pen
+		self._pen = None
+		self.value = []
+	def _moveTo(self, p0):
+		self._newItem()
+		self._pen.moveTo(p0)
+	def _lineTo(self, p1):
+		self._pen.lineTo(p1)
+	def _qCurveToOne(self, p1, p2):
+		self._pen.qCurveTo(p1, p2)
+	def _curveToOne(self, p1, p2, p3):
+		self._pen.curveTo(p1, p2, p3)
+	def _closePath(self):
+		self._pen.closePath()
+		self._pen = None
+	def _endPath(self):
+		self._pen.endPath()
+		self._pen = None
+
+	def _newItem(self):
+		self._pen = pen = self._Pen()
+		self.value.append(pen)
+
+class PerContourOrComponentPen(PerContourPen):
+
+	def addComponent(self, glyphName, transformation):
+		self._newItem()
+		self.value[-1].addComponent(glyphName, transformation)
+
+
+def _vdiff(v0, v1):
+	return tuple(b-a for a,b in zip(v0,v1))
+def _vlen(vec):
+	v = 0
+	for x in vec:
+		v += x*x
+	return v
+
+def _matching_cost(G, matching):
+	return sum(G[i][j] for i,j in enumerate(matching))
+
+def min_cost_perfect_bipartite_matching(G):
+	n = len(G)
+	try:
+		from scipy.optimize import linear_sum_assignment
+		rows, cols = linear_sum_assignment(G)
+		assert (rows == list(range(n))).all()
+		return list(cols), _matching_cost(G, cols)
+	except ImportError:
+		pass
+
+	try:
+		from munkres import Munkres
+		cols = [None] * n
+		for row,col in Munkres().compute(G):
+			cols[row] = col
+		return cols, _matching_cost(G, cols)
+	except ImportError:
+		pass
+
+	if n > 6:
+		raise Exception("Install Python module 'munkres' or 'scipy >= 0.17.0'")
+
+	# Otherwise just brute-force
+	permutations = itertools.permutations(range(n))
+	best = list(next(permutations))
+	best_cost = _matching_cost(G, best)
+	for p in permutations:
+		cost = _matching_cost(G, p)
+		if cost < best_cost:
+			best, best_cost = list(p), cost
+	return best, best_cost
+
+
+def test(glyphsets, glyphs=None, names=None):
+
+	if names is None:
+		names = glyphsets
+	if glyphs is None:
+		glyphs = glyphsets[0].keys()
+
+	hist = []
+	for glyph_name in glyphs:
+		#print()
+		#print(glyph_name)
+
+		try:
+			allVectors = []
+			for glyphset,name in zip(glyphsets, names):
+				#print('.', end='')
+				glyph = glyphset[glyph_name]
+
+				perContourPen = PerContourOrComponentPen(RecordingPen, glyphset=glyphset)
+				glyph.draw(perContourPen)
+				contourPens = perContourPen.value
+				del perContourPen
+
+				contourVectors = []
+				allVectors.append(contourVectors)
+				for contour in contourPens:
+					stats = StatisticsPen(glyphset=glyphset)
+					contour.replay(stats)
+					size = abs(stats.area) ** .5 * .5
+					vector = (
+						int(size),
+						int(stats.meanX),
+						int(stats.meanY),
+						int(stats.stddevX * 2),
+						int(stats.stddevY * 2),
+						int(stats.correlation * size),
+					)
+					contourVectors.append(vector)
+					#print(vector)
+
+			# Check each master against the next one in the list.
+			for i,(m0,m1) in enumerate(zip(allVectors[:-1],allVectors[1:])):
+				if len(m0) != len(m1):
+					print('%s: %s+%s: Glyphs not compatible!!!!!' % (glyph_name, names[i], names[i+1]))
+					continue
+				if not m0:
+					continue
+				costs = [[_vlen(_vdiff(v0,v1)) for v1 in m1] for v0 in m0]
+				matching, matching_cost = min_cost_perfect_bipartite_matching(costs)
+				if matching != list(range(len(m0))):
+					print('%s: %s+%s: Glyph has wrong contour/component order: %s' % (glyph_name, names[i], names[i+1], matching)) #, m0, m1)
+					break
+				upem = 2048
+				item_cost = round((matching_cost / len(m0) / len(m0[0])) ** .5 / upem * 100)
+				hist.append(item_cost)
+				threshold = 7
+				if item_cost >= threshold:
+					print('%s: %s+%s: Glyph has very high cost: %d%%' % (glyph_name, names[i], names[i+1], item_cost))
+
+
+		except ValueError as e:
+			print('%s: %s: math error %s; skipping glyph.' % (glyph_name, name, e))
+			print(contour.value)
+			#raise
+	#for x in hist:
+	#	print(x)
+
+def main(args):
+	filenames = args
+	glyphs = None
+	#glyphs = ['uni08DB', 'uniFD76']
+	#glyphs = ['uni08DE', 'uni0034']
+	#glyphs = ['uni08DE', 'uni0034', 'uni0751', 'uni0753', 'uni0754', 'uni08A4', 'uni08A4.fina', 'uni08A5.fina']
+
+	from os.path import basename
+	names = [basename(filename).rsplit('.', 1)[0] for filename in filenames]
+
+	from fontTools.ttLib import TTFont
+	fonts = [TTFont(filename) for filename in filenames]
+
+	glyphsets = [font.getGlyphSet() for font in fonts]
+	test(glyphsets, glyphs=glyphs, names=names)
+
+if __name__ == '__main__':
+	import sys
+	main(sys.argv[1:])
diff --git a/Lib/fontTools/varLib/interpolate_layout.py b/Lib/fontTools/varLib/interpolate_layout.py
new file mode 100644
index 0000000..ca9ccfe
--- /dev/null
+++ b/Lib/fontTools/varLib/interpolate_layout.py
@@ -0,0 +1,91 @@
+"""
+Interpolate OpenType Layout tables (GDEF / GPOS / GSUB).
+"""
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.ttLib import TTFont
+from fontTools.varLib import models, VarLibError, load_designspace
+from fontTools.varLib.merger import InstancerMerger
+import os.path
+import logging
+from pprint import pformat
+
+log = logging.getLogger("fontTools.varLib.interpolate_layout")
+
+
+def interpolate_layout(designspace_filename, loc, master_finder=lambda s:s, mapped=False):
+	"""
+	Interpolate GPOS from a designspace file and location.
+
+	If master_finder is set, it should be a callable that takes master
+	filename as found in designspace file and map it to master font
+	binary as to be opened (eg. .ttf or .otf).
+
+	If mapped is False (default), then location is mapped using the
+	map element of the axes in designspace file.  If mapped is True,
+	it is assumed that location is in designspace's internal space and
+	no mapping is performed.
+	"""
+
+	axes, internal_axis_supports, base_idx, normalized_master_locs, masters, instances = load_designspace(designspace_filename)
+
+
+	log.info("Building interpolated font")
+	log.info("Loading master fonts")
+	basedir = os.path.dirname(designspace_filename)
+	master_ttfs = [master_finder(os.path.join(basedir, m['filename'])) for m in masters]
+	master_fonts = [TTFont(ttf_path) for ttf_path in master_ttfs]
+
+	#font = master_fonts[base_idx]
+	font = TTFont(master_ttfs[base_idx])
+
+	log.info("Location: %s", pformat(loc))
+	if not mapped:
+		loc = {name:axes[name].map_forward(v) for name,v in loc.items()}
+	log.info("Internal location: %s", pformat(loc))
+	loc = models.normalizeLocation(loc, internal_axis_supports)
+	log.info("Normalized location: %s", pformat(loc))
+
+	# Assume single-model for now.
+	model = models.VariationModel(normalized_master_locs)
+	assert 0 == model.mapping[base_idx]
+
+	merger = InstancerMerger(font, model, loc)
+
+	log.info("Building interpolated tables")
+	merger.mergeTables(font, master_fonts, ['GPOS'])
+	return font
+
+
+def main(args=None):
+	from fontTools import configLogger
+
+	import sys
+	if args is None:
+		args = sys.argv[1:]
+
+	designspace_filename = args[0]
+	locargs = args[1:]
+	outfile = os.path.splitext(designspace_filename)[0] + '-instance.ttf'
+
+	# TODO: allow user to configure logging via command-line options
+	configLogger(level="INFO")
+
+	finder = lambda s: s.replace('master_ufo', 'master_ttf_interpolatable').replace('.ufo', '.ttf')
+
+	loc = {}
+	for arg in locargs:
+		tag,val = arg.split('=')
+		loc[tag] = float(val)
+
+	font = interpolate_layout(designspace_filename, loc, finder)
+	log.info("Saving font %s", outfile)
+	font.save(outfile)
+
+
+if __name__ == "__main__":
+	import sys
+	if len(sys.argv) > 1:
+		sys.exit(main())
+	import doctest
+	sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/varLib/iup.py b/Lib/fontTools/varLib/iup.py
new file mode 100644
index 0000000..912ff0a
--- /dev/null
+++ b/Lib/fontTools/varLib/iup.py
@@ -0,0 +1,305 @@
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+from fontTools.misc.py23 import *
+
+
+def iup_segment(coords, rc1, rd1, rc2, rd2):
+	# rc1 = reference coord 1
+	# rd1 = reference delta 1
+	out_arrays = [None, None]
+	for j in 0,1:
+		out_arrays[j] = out = []
+		x1, x2, d1, d2 = rc1[j], rc2[j], rd1[j], rd2[j]
+
+
+		if x1 == x2:
+			n = len(coords)
+			if d1 == d2:
+				out.extend([d1]*n)
+			else:
+				out.extend([0]*n)
+			continue
+
+		if x1 > x2:
+			x1, x2 = x2, x1
+			d1, d2 = d2, d1
+
+		# x1 < x2
+		scale = (d2 - d1) / (x2 - x1)
+		for pair in coords:
+			x = pair[j]
+
+			if x <= x1:
+				d = d1
+			elif x >= x2:
+				d = d2
+			else:
+				# Interpolate
+				d = d1 + (x - x1) * scale
+
+			out.append(d)
+
+	return zip(*out_arrays)
+
+def iup_contour(delta, coords):
+	assert len(delta) == len(coords)
+	if None not in delta:
+		return delta
+
+	n = len(delta)
+	# indices of points with explicit deltas
+	indices = [i for i,v in enumerate(delta) if v is not None]
+	if not indices:
+		# All deltas are None.  Return 0,0 for all.
+		return [(0,0)]*n
+
+	out = []
+	it = iter(indices)
+	start = next(it)
+	if start != 0:
+		# Initial segment that wraps around
+		i1, i2, ri1, ri2 = 0, start, start, indices[-1]
+		out.extend(iup_segment(coords[i1:i2], coords[ri1], delta[ri1], coords[ri2], delta[ri2]))
+	out.append(delta[start])
+	for end in it:
+		if end - start > 1:
+			i1, i2, ri1, ri2 = start+1, end, start, end
+			out.extend(iup_segment(coords[i1:i2], coords[ri1], delta[ri1], coords[ri2], delta[ri2]))
+		out.append(delta[end])
+		start = end
+	if start != n-1:
+		# Final segment that wraps around
+		i1, i2, ri1, ri2 = start+1, n, start, indices[0]
+		out.extend(iup_segment(coords[i1:i2], coords[ri1], delta[ri1], coords[ri2], delta[ri2]))
+
+	assert len(delta) == len(out), (len(delta), len(out))
+	return out
+
+def iup_delta(delta, coords, ends):
+	assert sorted(ends) == ends and len(coords) == (ends[-1]+1 if ends else 0) + 4
+	n = len(coords)
+	ends = ends + [n-4, n-3, n-2, n-1]
+	out = []
+	start = 0
+	for end in ends:
+		end += 1
+		contour = iup_contour(delta[start:end], coords[start:end])
+		out.extend(contour)
+		start = end
+
+	return out
+
+# Optimizer
+
+def can_iup_in_between(deltas, coords, i, j, tolerance):
+	assert j - i >= 2
+	interp = list(iup_segment(coords[i+1:j], coords[i], deltas[i], coords[j], deltas[j]))
+	deltas = deltas[i+1:j]
+
+	assert len(deltas) == len(interp)
+
+	return all(abs(complex(x-p, y-q)) <= tolerance for (x,y),(p,q) in zip(deltas, interp))
+
+def _iup_contour_bound_forced_set(delta, coords, tolerance=0):
+	"""The forced set is a conservative set of points on the contour that must be encoded
+	explicitly (ie. cannot be interpolated).  Calculating this set allows for significantly
+	speeding up the dynamic-programming, as well as resolve circularity in DP.
+
+	The set is precise; that is, if an index is in the returned set, then there is no way
+	that IUP can generate delta for that point, given coords and delta.
+	"""
+	assert len(delta) == len(coords)
+
+	forced = set()
+	# Track "last" and "next" points on the contour as we sweep.
+	nd, nc = delta[0], coords[0]
+	ld, lc = delta[-1], coords[-1]
+	for i in range(len(delta)-1, -1, -1):
+		d, c = ld, lc
+		ld, lc = delta[i-1], coords[i-1]
+
+		for j in (0,1): # For X and for Y
+			cj = c[j]
+			dj = d[j]
+			lcj = lc[j]
+			ldj = ld[j]
+			ncj = nc[j]
+			ndj = nd[j]
+
+			if lcj <= ncj:
+				c1, c2 = lcj, ncj
+				d1, d2 = ldj, ndj
+			else:
+				c1, c2 = ncj, lcj
+				d1, d2 = ndj, ldj
+
+			# If coordinate for current point is between coordinate of adjacent
+			# points on the two sides, but the delta for current point is NOT
+			# between delta for those adjacent points (considering tolerance
+			# allowance), then there is no way that current point can be IUP-ed.
+			# Mark it forced.
+			force = False
+			if c1 <= cj <= c2:
+				if not (min(d1,d2)-tolerance <= dj <= max(d1,d2)+tolerance):
+					force = True
+			else: # cj < c1 or c2 < cj
+				if c1 == c2:
+					if d1 == d2:
+						if abs(dj - d1) > tolerance:
+							force = True
+					else:
+						if abs(dj) > tolerance:
+							# Disabled the following because the "d1 == d2" does
+							# check does not take tolerance into consideration...
+							pass # force = True
+				elif d1 != d2:
+					if cj < c1:
+						if dj != d1 and ((dj-tolerance < d1) != (d1 < d2)):
+							force = True
+					else: # c2 < cj
+						if d2 != dj and ((d2 < dj+tolerance) != (d1 < d2)):
+							force = True
+
+			if force:
+				forced.add(i)
+				break
+
+		nd, nc = d, c
+
+	return forced
+
+def _iup_contour_optimize_dp(delta, coords, forced={}, tolerance=0, lookback=None):
+	"""Straightforward Dynamic-Programming.  For each index i, find least-costly encoding of
+	points i to n-1 where i is explicitly encoded.  We find this by considering all next
+	explicit points j and check whether interpolation can fill points between i and j.
+
+	Note that solution always encodes last point explicitly.  Higher-level is responsible
+	for removing that restriction.
+
+	As major speedup, we stop looking further whenever we see a "forced" point."""
+
+	n = len(delta)
+	if lookback is None:
+		lookback = n
+	costs = {-1:0}
+	chain = {-1:None}
+	for i in range(0, n):
+		best_cost = costs[i-1] + 1
+
+		costs[i] = best_cost
+		chain[i] = i - 1
+
+		if i - 1 in forced:
+			continue
+
+		for j in range(i-2, max(i-lookback, -2), -1):
+
+			cost = costs[j] + 1
+
+			if cost < best_cost and can_iup_in_between(delta, coords, j, i, tolerance):
+				costs[i] = best_cost = cost
+				chain[i] = j
+
+			if j in forced:
+				break
+
+	return chain, costs
+
+def _rot_list(l, k):
+	"""Rotate list by k items forward.  Ie. item at position 0 will be
+	at position k in returned list.  Negative k is allowed."""
+	n = len(l)
+	k %= n
+	if not k: return l
+	return l[n-k:] + l[:n-k]
+
+def _rot_set(s, k, n):
+	k %= n
+	if not k: return s
+	return {(v + k) % n for v in s}
+
+def iup_contour_optimize(delta, coords, tolerance=0.):
+	n = len(delta)
+
+	# Get the easy cases out of the way:
+
+	# If all are within tolerance distance of 0, encode nothing:
+	if all(abs(complex(*p)) <= tolerance for p in delta):
+		return [None] * n
+
+	# If there's exactly one point, return it:
+	if n == 1:
+		return delta
+
+	# If all deltas are exactly the same, return just one (the first one):
+	d0 = delta[0]
+	if all(d0 == d for d in delta):
+		return [d0] + [None] * (n-1)
+
+	# Else, solve the general problem using Dynamic Programming.
+
+	forced = _iup_contour_bound_forced_set(delta, coords, tolerance)
+	# The _iup_contour_optimize_dp() routine returns the optimal encoding
+	# solution given the constraint that the last point is always encoded.
+	# To remove this constraint, we use two different methods, depending on
+	# whether forced set is non-empty or not:
+
+	if forced:
+		# Forced set is non-empty: rotate the contour start point
+		# such that the last point in the list is a forced point.
+		k = (n-1) - max(forced)
+		assert k >= 0
+
+		delta  = _rot_list(delta, k)
+		coords = _rot_list(coords, k)
+		forced = _rot_set(forced, k, n)
+
+		chain, costs = _iup_contour_optimize_dp(delta, coords, forced, tolerance)
+
+		# Assemble solution.
+		solution = set()
+		i = n - 1
+		while i is not None:
+			solution.add(i)
+			i = chain[i]
+		assert forced <= solution, (forced, solution)
+		delta = [delta[i] if i in solution else None for i in range(n)]
+
+		delta = _rot_list(delta, -k)
+	else:
+		# Repeat the contour an extra time, solve the 2*n case, then look for solutions of the
+		# circular n-length problem in the solution for 2*n linear case.  I cannot prove that
+		# this always produces the optimal solution...
+		chain, costs = _iup_contour_optimize_dp(delta+delta, coords+coords, forced, tolerance, n)
+		best_sol, best_cost = None, n+1
+
+		for start in range(n-1, 2*n-1):
+			# Assemble solution.
+			solution = set()
+			i = start
+			while i > start - n:
+				solution.add(i % n)
+				i = chain[i]
+			if i == start - n:
+				cost = costs[start] - costs[start - n]
+				if cost <= best_cost:
+					best_sol, best_cost = solution, cost
+
+		delta = [delta[i] if i in best_sol else None for i in range(n)]
+
+
+	return delta
+
+def iup_delta_optimize(delta, coords, ends, tolerance=0.):
+	assert sorted(ends) == ends and len(coords) == (ends[-1]+1 if ends else 0) + 4
+	n = len(coords)
+	ends = ends + [n-4, n-3, n-2, n-1]
+	out = []
+	start = 0
+	for end in ends:
+		contour = iup_contour_optimize(delta[start:end+1], coords[start:end+1], tolerance)
+		assert len(contour) == end - start + 1
+		out.extend(contour)
+		start = end+1
+
+	return out
diff --git a/Lib/fontTools/varLib/merger.py b/Lib/fontTools/varLib/merger.py
new file mode 100644
index 0000000..0e4095d
--- /dev/null
+++ b/Lib/fontTools/varLib/merger.py
@@ -0,0 +1,830 @@
+"""
+Merge OpenType Layout tables (GDEF / GPOS / GSUB).
+"""
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.fixedTools import otRound
+from fontTools.misc import classifyTools
+from fontTools.ttLib.tables import otTables as ot
+from fontTools.ttLib.tables import otBase as otBase
+from fontTools.ttLib.tables.DefaultTable import DefaultTable
+from fontTools.varLib import builder, varStore
+from fontTools.varLib.varStore import VarStoreInstancer
+from functools import reduce
+
+
+class Merger(object):
+
+	def __init__(self, font=None):
+		self.font = font
+
+	@classmethod
+	def merger(celf, clazzes, attrs=(None,)):
+		assert celf != Merger, 'Subclass Merger instead.'
+		if 'mergers' not in celf.__dict__:
+			celf.mergers = {}
+		if type(clazzes) == type:
+			clazzes = (clazzes,)
+		if type(attrs) == str:
+			attrs = (attrs,)
+		def wrapper(method):
+			assert method.__name__ == 'merge'
+			done = []
+			for clazz in clazzes:
+				if clazz in done: continue # Support multiple names of a clazz
+				done.append(clazz)
+				mergers = celf.mergers.setdefault(clazz, {})
+				for attr in attrs:
+					assert attr not in mergers, \
+						"Oops, class '%s' has merge function for '%s' defined already." % (clazz.__name__, attr)
+					mergers[attr] = method
+			return None
+		return wrapper
+
+	@classmethod
+	def mergersFor(celf, thing, _default={}):
+		typ = type(thing)
+
+		for celf in celf.mro():
+
+			mergers = getattr(celf, 'mergers', None)
+			if mergers is None:
+				break;
+
+			m = celf.mergers.get(typ, None)
+			if m is not None:
+				return m
+
+		return _default
+
+	def mergeObjects(self, out, lst, exclude=()):
+		keys = sorted(vars(out).keys())
+		assert all(keys == sorted(vars(v).keys()) for v in lst), \
+			(keys, [sorted(vars(v).keys()) for v in lst])
+		mergers = self.mergersFor(out)
+		defaultMerger = mergers.get('*', self.__class__.mergeThings)
+		try:
+			for key in keys:
+				if key in exclude: continue
+				value = getattr(out, key)
+				values = [getattr(table, key) for table in lst]
+				mergerFunc = mergers.get(key, defaultMerger)
+				mergerFunc(self, value, values)
+		except Exception as e:
+			e.args = e.args + ('.'+key,)
+			raise
+
+	def mergeLists(self, out, lst):
+		count = len(out)
+		assert all(count == len(v) for v in lst), (count, [len(v) for v in lst])
+		for i,(value,values) in enumerate(zip(out, zip(*lst))):
+			try:
+				self.mergeThings(value, values)
+			except Exception as e:
+				e.args = e.args + ('[%d]' % i,)
+				raise
+
+	def mergeThings(self, out, lst):
+		clazz = type(out)
+		try:
+			assert all(type(item) == clazz for item in lst), (out, lst)
+			mergerFunc = self.mergersFor(out).get(None, None)
+			if mergerFunc is not None:
+				mergerFunc(self, out, lst)
+			elif hasattr(out, '__dict__'):
+				self.mergeObjects(out, lst)
+			elif isinstance(out, list):
+				self.mergeLists(out, lst)
+			else:
+				assert all(out == v for v in lst), (out, lst)
+		except Exception as e:
+			e.args = e.args + (clazz.__name__,)
+			raise
+
+	def mergeTables(self, font, master_ttfs, tables):
+
+		for tag in tables:
+			if tag not in font: continue
+			self.mergeThings(font[tag], [m[tag] for m in master_ttfs])
+
+#
+# Aligning merger
+#
+class AligningMerger(Merger):
+	pass
+
+def _SinglePosUpgradeToFormat2(self):
+	if self.Format == 2: return self
+
+	ret = ot.SinglePos()
+	ret.Format = 2
+	ret.Coverage = self.Coverage
+	ret.ValueFormat = self.ValueFormat
+	ret.Value = [self.Value for g in ret.Coverage.glyphs]
+	ret.ValueCount = len(ret.Value)
+
+	return ret
+
+def _merge_GlyphOrders(font, lst, values_lst=None, default=None):
+	"""Takes font and list of glyph lists (must be sorted by glyph id), and returns
+	two things:
+	- Combined glyph list,
+	- If values_lst is None, return input glyph lists, but padded with None when a glyph
+	  was missing in a list.  Otherwise, return values_lst list-of-list, padded with None
+	  to match combined glyph lists.
+	"""
+	if values_lst is None:
+		dict_sets = [set(l) for l in lst]
+	else:
+		dict_sets = [{g:v for g,v in zip(l,vs)} for l,vs in zip(lst,values_lst)]
+	combined = set()
+	combined.update(*dict_sets)
+
+	sortKey = font.getReverseGlyphMap().__getitem__
+	order = sorted(combined, key=sortKey)
+	# Make sure all input glyphsets were in proper order
+	assert all(sorted(vs, key=sortKey) == vs for vs in lst)
+	del combined
+
+	paddedValues = None
+	if values_lst is None:
+		padded = [[glyph if glyph in dict_set else default
+			   for glyph in order]
+			  for dict_set in dict_sets]
+	else:
+		assert len(lst) == len(values_lst)
+		padded = [[dict_set[glyph] if glyph in dict_set else default
+			   for glyph in order]
+			  for dict_set in dict_sets]
+	return order, padded
+
+def _Lookup_SinglePos_get_effective_value(subtables, glyph):
+	for self in subtables:
+		if self is None or \
+		   type(self) != ot.SinglePos or \
+		   self.Coverage is None or \
+		   glyph not in self.Coverage.glyphs:
+			continue
+		if self.Format == 1:
+			return self.Value
+		elif self.Format == 2:
+			return self.Value[self.Coverage.glyphs.index(glyph)]
+		else:
+			assert 0
+	return None
+
+def _Lookup_PairPos_get_effective_value_pair(subtables, firstGlyph, secondGlyph):
+	for self in subtables:
+		if self is None or \
+		   type(self) != ot.PairPos or \
+		   self.Coverage is None or \
+		   firstGlyph not in self.Coverage.glyphs:
+			continue
+		if self.Format == 1:
+			ps = self.PairSet[self.Coverage.glyphs.index(firstGlyph)]
+			pvr = ps.PairValueRecord
+			for rec in pvr: # TODO Speed up
+				if rec.SecondGlyph == secondGlyph:
+					return rec
+			continue
+		elif self.Format == 2:
+			klass1 = self.ClassDef1.classDefs.get(firstGlyph, 0)
+			klass2 = self.ClassDef2.classDefs.get(secondGlyph, 0)
+			return self.Class1Record[klass1].Class2Record[klass2]
+		else:
+			assert 0
+	return None
+
+@AligningMerger.merger(ot.SinglePos)
+def merge(merger, self, lst):
+	self.ValueFormat = valueFormat = reduce(int.__or__, [l.ValueFormat for l in lst], 0)
+	assert len(lst) == 1 or (valueFormat & ~0xF == 0), valueFormat
+
+	# If all have same coverage table and all are format 1,
+	if all(v.Format == 1 for v in lst) and all(self.Coverage.glyphs == v.Coverage.glyphs for v in lst):
+		self.Value = otBase.ValueRecord(valueFormat)
+		merger.mergeThings(self.Value, [v.Value for v in lst])
+		self.ValueFormat = self.Value.getFormat()
+		return
+
+	# Upgrade everything to Format=2
+	self.Format = 2
+	lst = [_SinglePosUpgradeToFormat2(v) for v in lst]
+
+	# Align them
+	glyphs, padded = _merge_GlyphOrders(merger.font,
+					    [v.Coverage.glyphs for v in lst],
+					    [v.Value for v in lst])
+
+	self.Coverage.glyphs = glyphs
+	self.Value = [otBase.ValueRecord(valueFormat) for g in glyphs]
+	self.ValueCount = len(self.Value)
+
+	for i,values in enumerate(padded):
+		for j,glyph in enumerate(glyphs):
+			if values[j] is not None: continue
+			# Fill in value from other subtables
+			# Note!!! This *might* result in behavior change if ValueFormat2-zeroedness
+			# is different between used subtable and current subtable!
+			# TODO(behdad) Check and warn if that happens?
+			v = _Lookup_SinglePos_get_effective_value(merger.lookup_subtables[i], glyph)
+			if v is None:
+				v = otBase.ValueRecord(valueFormat)
+			values[j] = v
+
+	merger.mergeLists(self.Value, padded)
+
+	# Merge everything else; though, there shouldn't be anything else. :)
+	merger.mergeObjects(self, lst,
+			    exclude=('Format', 'Coverage', 'Value', 'ValueCount'))
+	self.ValueFormat = reduce(int.__or__, [v.getFormat() for v in self.Value], 0)
+
+@AligningMerger.merger(ot.PairSet)
+def merge(merger, self, lst):
+	# Align them
+	glyphs, padded = _merge_GlyphOrders(merger.font,
+				[[v.SecondGlyph for v in vs.PairValueRecord] for vs in lst],
+				[vs.PairValueRecord for vs in lst])
+
+	self.PairValueRecord = pvrs = []
+	for glyph in glyphs:
+		pvr = ot.PairValueRecord()
+		pvr.SecondGlyph = glyph
+		pvr.Value1 = otBase.ValueRecord(merger.valueFormat1) if merger.valueFormat1 else None
+		pvr.Value2 = otBase.ValueRecord(merger.valueFormat2) if merger.valueFormat2 else None
+		pvrs.append(pvr)
+	self.PairValueCount = len(self.PairValueRecord)
+
+	for i,values in enumerate(padded):
+		for j,glyph in enumerate(glyphs):
+			# Fill in value from other subtables
+			v = ot.PairValueRecord()
+			v.SecondGlyph = glyph
+			if values[j] is not None:
+				vpair = values[j]
+			else:
+				vpair = _Lookup_PairPos_get_effective_value_pair(merger.lookup_subtables[i], self._firstGlyph, glyph)
+			if vpair is None:
+				v1, v2 = None, None
+			else:
+				v1, v2 = vpair.Value1, vpair.Value2
+			v.Value1 = otBase.ValueRecord(merger.valueFormat1, src=v1) if merger.valueFormat1 else None
+			v.Value2 = otBase.ValueRecord(merger.valueFormat2, src=v2) if merger.valueFormat2 else None
+			values[j] = v
+	del self._firstGlyph
+
+	merger.mergeLists(self.PairValueRecord, padded)
+
+def _PairPosFormat1_merge(self, lst, merger):
+	assert _all_equal([l.ValueFormat2 == 0 for l in lst if l.PairSet]), "Report bug against fonttools."
+
+	# Merge everything else; makes sure Format is the same.
+	merger.mergeObjects(self, lst,
+			    exclude=('Coverage',
+				     'PairSet', 'PairSetCount',
+				     'ValueFormat1', 'ValueFormat2'))
+
+	empty = ot.PairSet()
+	empty.PairValueRecord = []
+	empty.PairValueCount = 0
+
+	# Align them
+	glyphs, padded = _merge_GlyphOrders(merger.font,
+					    [v.Coverage.glyphs for v in lst],
+					    [v.PairSet for v in lst],
+					    default=empty)
+
+	self.Coverage.glyphs = glyphs
+	self.PairSet = [ot.PairSet() for g in glyphs]
+	self.PairSetCount = len(self.PairSet)
+	for glyph, ps in zip(glyphs, self.PairSet):
+		ps._firstGlyph = glyph
+
+	merger.mergeLists(self.PairSet, padded)
+
+def _ClassDef_invert(self, allGlyphs=None):
+
+	if isinstance(self, dict):
+		classDefs = self
+	else:
+		classDefs = self.classDefs if self and self.classDefs else {}
+	m = max(classDefs.values()) if classDefs else 0
+
+	ret = []
+	for _ in range(m + 1):
+		ret.append(set())
+
+	for k,v in classDefs.items():
+		ret[v].add(k)
+
+	# Class-0 is special.  It's "everything else".
+	if allGlyphs is None:
+		ret[0] = None
+	else:
+		# Limit all classes to glyphs in allGlyphs.
+		# Collect anything without a non-zero class into class=zero.
+		ret[0] = class0 = set(allGlyphs)
+		for s in ret[1:]:
+			s.intersection_update(class0)
+			class0.difference_update(s)
+
+	return ret
+
+def _ClassDef_merge_classify(lst, allGlyphs=None):
+	self = ot.ClassDef()
+	self.classDefs = classDefs = {}
+
+	classifier = classifyTools.Classifier()
+	for l in lst:
+		sets = _ClassDef_invert(l, allGlyphs=allGlyphs)
+		if allGlyphs is None:
+			sets = sets[1:]
+		classifier.update(sets)
+	classes = classifier.getClasses()
+
+	if allGlyphs is None:
+		classes.insert(0, set())
+
+	for i,classSet in enumerate(classes):
+		if i == 0:
+			continue
+		for g in classSet:
+			classDefs[g] = i
+
+	return self, classes
+
+def _ClassDef_calculate_Format(self, font):
+	fmt = 2
+	ranges = self._getClassRanges(font)
+	if ranges:
+		startGlyph = ranges[0][1]
+		endGlyph = ranges[-1][3]
+		glyphCount = endGlyph - startGlyph + 1
+		if len(ranges) * 3 >= glyphCount + 1:
+			# Format 1 is more compact
+			fmt = 1
+	self.Format = fmt
+
+def _PairPosFormat2_align_matrices(self, lst, font, transparent=False):
+
+	matrices = [l.Class1Record for l in lst]
+
+	# Align first classes
+	self.ClassDef1, classes = _ClassDef_merge_classify([l.ClassDef1 for l in lst], allGlyphs=set(self.Coverage.glyphs))
+	_ClassDef_calculate_Format(self.ClassDef1, font)
+	self.Class1Count = len(classes)
+	new_matrices = []
+	for l,matrix in zip(lst, matrices):
+		nullRow = None
+		coverage = set(l.Coverage.glyphs)
+		classDef1 = l.ClassDef1.classDefs
+		class1Records = []
+		for classSet in classes:
+			exemplarGlyph = next(iter(classSet))
+			if exemplarGlyph not in coverage:
+				if nullRow is None:
+					nullRow = ot.Class1Record()
+					class2records = nullRow.Class2Record = []
+					# TODO: When merger becomes selfless, revert e6125b353e1f54a0280ded5434b8e40d042de69f
+					for _ in range(l.Class2Count):
+						if transparent:
+							rec2 = None
+						else:
+							rec2 = ot.Class2Record()
+							rec2.Value1 = otBase.ValueRecord(self.ValueFormat1) if self.ValueFormat1 else None
+							rec2.Value2 = otBase.ValueRecord(self.ValueFormat2) if self.ValueFormat2 else None
+						class2records.append(rec2)
+				rec1 = nullRow
+			else:
+				klass = classDef1.get(exemplarGlyph, 0)
+				rec1 = matrix[klass] # TODO handle out-of-range?
+			class1Records.append(rec1)
+		new_matrices.append(class1Records)
+	matrices = new_matrices
+	del new_matrices
+
+	# Align second classes
+	self.ClassDef2, classes = _ClassDef_merge_classify([l.ClassDef2 for l in lst])
+	_ClassDef_calculate_Format(self.ClassDef2, font)
+	self.Class2Count = len(classes)
+	new_matrices = []
+	for l,matrix in zip(lst, matrices):
+		classDef2 = l.ClassDef2.classDefs
+		class1Records = []
+		for rec1old in matrix:
+			oldClass2Records = rec1old.Class2Record
+			rec1new = ot.Class1Record()
+			class2Records = rec1new.Class2Record = []
+			for classSet in classes:
+				if not classSet: # class=0
+					rec2 = oldClass2Records[0]
+				else:
+					exemplarGlyph = next(iter(classSet))
+					klass = classDef2.get(exemplarGlyph, 0)
+					rec2 = oldClass2Records[klass]
+				class2Records.append(rec2)
+			class1Records.append(rec1new)
+		new_matrices.append(class1Records)
+	matrices = new_matrices
+	del new_matrices
+
+	return matrices
+
+def _PairPosFormat2_merge(self, lst, merger):
+	assert _all_equal([l.ValueFormat2 == 0 for l in lst if l.Class1Record]), "Report bug against fonttools."
+
+	merger.mergeObjects(self, lst,
+			    exclude=('Coverage',
+				     'ClassDef1', 'Class1Count',
+				     'ClassDef2', 'Class2Count',
+				     'Class1Record',
+				     'ValueFormat1', 'ValueFormat2'))
+
+	# Align coverages
+	glyphs, _ = _merge_GlyphOrders(merger.font,
+				       [v.Coverage.glyphs for v in lst])
+	self.Coverage.glyphs = glyphs
+
+	# Currently, if the coverage of PairPosFormat2 subtables are different,
+	# we do NOT bother walking down the subtable list when filling in new
+	# rows for alignment.  As such, this is only correct if current subtable
+	# is the last subtable in the lookup.  Ensure that.
+	#
+	# Note that our canonicalization process merges trailing PairPosFormat2's,
+	# so in reality this is rare.
+	for l,subtables in zip(lst,merger.lookup_subtables):
+		if l.Coverage.glyphs != glyphs:
+			assert l == subtables[-1]
+
+	matrices = _PairPosFormat2_align_matrices(self, lst, merger.font)
+
+	self.Class1Record = list(matrices[0]) # TODO move merger to be selfless
+	merger.mergeLists(self.Class1Record, matrices)
+
+@AligningMerger.merger(ot.PairPos)
+def merge(merger, self, lst):
+	merger.valueFormat1 = self.ValueFormat1 = reduce(int.__or__, [l.ValueFormat1 for l in lst], 0)
+	merger.valueFormat2 = self.ValueFormat2 = reduce(int.__or__, [l.ValueFormat2 for l in lst], 0)
+
+	if self.Format == 1:
+		_PairPosFormat1_merge(self, lst, merger)
+	elif self.Format == 2:
+		_PairPosFormat2_merge(self, lst, merger)
+	else:
+		assert False
+
+	del merger.valueFormat1, merger.valueFormat2
+
+	# Now examine the list of value records, and update to the union of format values,
+	# as merge might have created new values.
+	vf1 = 0
+	vf2 = 0
+	if self.Format == 1:
+		for pairSet in self.PairSet:
+			for pairValueRecord in pairSet.PairValueRecord:
+				pv1 = pairValueRecord.Value1
+				if pv1 is not None:
+					vf1 |= pv1.getFormat()
+				pv2 = pairValueRecord.Value2
+				if pv2 is not None:
+					vf2 |= pv2.getFormat()
+	elif self.Format == 2:
+		for class1Record in self.Class1Record:
+			for class2Record in class1Record.Class2Record:
+				pv1 = class2Record.Value1
+				if pv1 is not None:
+					vf1 |= pv1.getFormat()
+				pv2 = class2Record.Value2
+				if pv2 is not None:
+					vf2 |= pv2.getFormat()
+	self.ValueFormat1 = vf1
+	self.ValueFormat2 = vf2
+
+
+def _PairSet_flatten(lst, font):
+	self = ot.PairSet()
+	self.Coverage = ot.Coverage()
+	self.Coverage.Format = 1
+
+	# Align them
+	glyphs, padded = _merge_GlyphOrders(font,
+				[[v.SecondGlyph for v in vs.PairValueRecord] for vs in lst],
+				[vs.PairValueRecord for vs in lst])
+
+	self.Coverage.glyphs = glyphs
+	self.PairValueRecord = pvrs = []
+	for values in zip(*padded):
+		for v in values:
+			if v is not None:
+				pvrs.append(v)
+				break
+		else:
+			assert False
+	self.PairValueCount = len(self.PairValueRecord)
+
+	return self
+
+def _Lookup_PairPosFormat1_subtables_flatten(lst, font):
+	assert _all_equal([l.ValueFormat2 == 0 for l in lst if l.PairSet]), "Report bug against fonttools."
+
+	self = ot.PairPos()
+	self.Format = 1
+	self.Coverage = ot.Coverage()
+	self.Coverage.Format = 1
+	self.ValueFormat1 = reduce(int.__or__, [l.ValueFormat1 for l in lst], 0)
+	self.ValueFormat2 = reduce(int.__or__, [l.ValueFormat2 for l in lst], 0)
+
+	# Align them
+	glyphs, padded = _merge_GlyphOrders(font,
+					    [v.Coverage.glyphs for v in lst],
+					    [v.PairSet for v in lst])
+
+	self.Coverage.glyphs = glyphs
+	self.PairSet = [_PairSet_flatten([v for v in values if v is not None], font)
+		        for values in zip(*padded)]
+	self.PairSetCount = len(self.PairSet)
+	return self
+
+def _Lookup_PairPosFormat2_subtables_flatten(lst, font):
+	assert _all_equal([l.ValueFormat2 == 0 for l in lst if l.Class1Record]), "Report bug against fonttools."
+
+	self = ot.PairPos()
+	self.Format = 2
+	self.Coverage = ot.Coverage()
+	self.Coverage.Format = 1
+	self.ValueFormat1 = reduce(int.__or__, [l.ValueFormat1 for l in lst], 0)
+	self.ValueFormat2 = reduce(int.__or__, [l.ValueFormat2 for l in lst], 0)
+
+	# Align them
+	glyphs, _ = _merge_GlyphOrders(font,
+				       [v.Coverage.glyphs for v in lst])
+	self.Coverage.glyphs = glyphs
+
+	matrices = _PairPosFormat2_align_matrices(self, lst, font, transparent=True)
+
+	matrix = self.Class1Record = []
+	for rows in zip(*matrices):
+		row = ot.Class1Record()
+		matrix.append(row)
+		row.Class2Record = []
+		row = row.Class2Record
+		for cols in zip(*list(r.Class2Record for r in rows)):
+			col = next(iter(c for c in cols if c is not None))
+			row.append(col)
+
+	return self
+
+def _Lookup_PairPos_subtables_canonicalize(lst, font):
+	"""Merge multiple Format1 subtables at the beginning of lst,
+	and merge multiple consecutive Format2 subtables that have the same
+	Class2 (ie. were split because of offset overflows).  Returns new list."""
+	lst = list(lst)
+
+	l = len(lst)
+	i = 0
+	while i < l and lst[i].Format == 1:
+		i += 1
+	lst[:i] = [_Lookup_PairPosFormat1_subtables_flatten(lst[:i], font)]
+
+	l = len(lst)
+	i = l
+	while i > 0 and lst[i - 1].Format == 2:
+		i -= 1
+	lst[i:] = [_Lookup_PairPosFormat2_subtables_flatten(lst[i:], font)]
+
+	return lst
+
+@AligningMerger.merger(ot.Lookup)
+def merge(merger, self, lst):
+	subtables = merger.lookup_subtables = [l.SubTable for l in lst]
+
+	# Remove Extension subtables
+	for l,sts in list(zip(lst,subtables))+[(self,self.SubTable)]:
+		if not sts:
+			continue
+		if sts[0].__class__.__name__.startswith('Extension'):
+			assert _all_equal([st.__class__ for st in sts])
+			assert _all_equal([st.ExtensionLookupType for st in sts])
+			l.LookupType = sts[0].ExtensionLookupType
+			new_sts = [st.ExtSubTable for st in sts]
+			del sts[:]
+			sts.extend(new_sts)
+
+	isPairPos = self.SubTable and isinstance(self.SubTable[0], ot.PairPos)
+
+	if isPairPos:
+
+		# AFDKO and feaLib sometimes generate two Format1 subtables instead of one.
+		# Merge those before continuing.
+		# https://github.com/fonttools/fonttools/issues/719
+		self.SubTable = _Lookup_PairPos_subtables_canonicalize(self.SubTable, merger.font)
+		subtables = merger.lookup_subtables = [_Lookup_PairPos_subtables_canonicalize(st, merger.font) for st in subtables]
+
+	merger.mergeLists(self.SubTable, subtables)
+	self.SubTableCount = len(self.SubTable)
+
+	if isPairPos:
+		# If format-1 subtable created during canonicalization is empty, remove it.
+		assert len(self.SubTable) >= 1 and self.SubTable[0].Format == 1
+		if not self.SubTable[0].Coverage.glyphs:
+			self.SubTable.pop(0)
+			self.SubTableCount -= 1
+
+		# If format-2 subtable created during canonicalization is empty, remove it.
+		assert len(self.SubTable) >= 1 and self.SubTable[-1].Format == 2
+		if not self.SubTable[-1].Coverage.glyphs:
+			self.SubTable.pop(-1)
+			self.SubTableCount -= 1
+
+	merger.mergeObjects(self, lst, exclude=['SubTable', 'SubTableCount'])
+
+	del merger.lookup_subtables
+
+
+#
+# InstancerMerger
+#
+
+class InstancerMerger(AligningMerger):
+	"""A merger that takes multiple master fonts, and instantiates
+	an instance."""
+
+	def __init__(self, font, model, location):
+		Merger.__init__(self, font)
+		self.model = model
+		self.location = location
+		self.scalars = model.getScalars(location)
+
+@InstancerMerger.merger(ot.Anchor)
+def merge(merger, self, lst):
+	XCoords = [a.XCoordinate for a in lst]
+	YCoords = [a.YCoordinate for a in lst]
+	model = merger.model
+	scalars = merger.scalars
+	self.XCoordinate = otRound(model.interpolateFromMastersAndScalars(XCoords, scalars))
+	self.YCoordinate = otRound(model.interpolateFromMastersAndScalars(YCoords, scalars))
+
+@InstancerMerger.merger(otBase.ValueRecord)
+def merge(merger, self, lst):
+	model = merger.model
+	scalars = merger.scalars
+	# TODO Handle differing valueformats
+	for name, tableName in [('XAdvance','XAdvDevice'),
+				('YAdvance','YAdvDevice'),
+				('XPlacement','XPlaDevice'),
+				('YPlacement','YPlaDevice')]:
+
+		assert not hasattr(self, tableName)
+
+		if hasattr(self, name):
+			values = [getattr(a, name, 0) for a in lst]
+			value = otRound(model.interpolateFromMastersAndScalars(values, scalars))
+			setattr(self, name, value)
+
+
+#
+# MutatorMerger
+#
+
+class MutatorMerger(AligningMerger):
+	"""A merger that takes a variable font, and instantiates
+	an instance."""
+
+	def __init__(self, font, location):
+		Merger.__init__(self, font)
+		self.location = location
+
+		store = None
+		if 'GDEF' in font:
+			gdef = font['GDEF'].table
+			if gdef.Version >= 0x00010003:
+				store = gdef.VarStore
+
+		self.instancer = VarStoreInstancer(store, font['fvar'].axes, location)
+
+	def instantiate(self):
+		font = self.font
+
+		self.mergeTables(font, [font], ['GPOS'])
+
+		if 'GDEF' in font:
+			gdef = font['GDEF'].table
+			if gdef.Version >= 0x00010003:
+				del gdef.VarStore
+				gdef.Version = 0x00010002
+				if gdef.MarkGlyphSetsDef is None:
+					del gdef.MarkGlyphSetsDef
+					gdef.Version = 0x00010000
+			if not (gdef.LigCaretList or
+				gdef.MarkAttachClassDef or
+				gdef.GlyphClassDef or
+				gdef.AttachList or
+				(gdef.Version >= 0x00010002 and gdef.MarkGlyphSetsDef)):
+				del font['GDEF']
+
+@MutatorMerger.merger(ot.Anchor)
+def merge(merger, self, lst):
+	if self.Format != 3:
+		return
+
+	instancer = merger.instancer
+	for v in "XY":
+		tableName = v+'DeviceTable'
+		if not hasattr(self, tableName):
+			continue
+		dev = getattr(self, tableName)
+		delattr(self, tableName)
+		if dev is None:
+			continue
+
+		assert dev.DeltaFormat == 0x8000
+		varidx = (dev.StartSize << 16) + dev.EndSize
+		delta = otRound(instancer[varidx])
+
+		attr = v+'Coordinate'
+		setattr(self, attr, getattr(self, attr) + delta)
+
+	self.Format = 1
+
+@MutatorMerger.merger(otBase.ValueRecord)
+def merge(merger, self, lst):
+
+	# All other structs are merged with self pointing to a copy of base font,
+	# except for ValueRecords which are sometimes created later and initialized
+	# to have 0/None members.  Hence the copy.
+	self.__dict__ = lst[0].__dict__.copy()
+
+	instancer = merger.instancer
+	# TODO Handle differing valueformats
+	for name, tableName in [('XAdvance','XAdvDevice'),
+				('YAdvance','YAdvDevice'),
+				('XPlacement','XPlaDevice'),
+				('YPlacement','YPlaDevice')]:
+
+		if not hasattr(self, tableName):
+			continue
+		dev = getattr(self, tableName)
+		delattr(self, tableName)
+		if dev is None:
+			continue
+
+		assert dev.DeltaFormat == 0x8000
+		varidx = (dev.StartSize << 16) + dev.EndSize
+		delta = otRound(instancer[varidx])
+
+		setattr(self, name, getattr(self, name) + delta)
+
+
+#
+# VariationMerger
+#
+
+class VariationMerger(AligningMerger):
+	"""A merger that takes multiple master fonts, and builds a
+	variable font."""
+
+	def __init__(self, model, axisTags, font):
+		Merger.__init__(self, font)
+		self.model = model
+		self.store_builder = varStore.OnlineVarStoreBuilder(axisTags)
+		self.store_builder.setModel(model)
+
+def _all_equal(lst):
+	if not lst:
+		return True
+	it = iter(lst)
+	v0 = next(it)
+	for v in it:
+		if v0 != v:
+			return False
+	return True
+
+def buildVarDevTable(store_builder, master_values):
+	if _all_equal(master_values):
+		return master_values[0], None
+	base, varIdx = store_builder.storeMasters(master_values)
+	return base, builder.buildVarDevTable(varIdx)
+
+@VariationMerger.merger(ot.Anchor)
+def merge(merger, self, lst):
+	assert self.Format == 1
+	self.XCoordinate, XDeviceTable = buildVarDevTable(merger.store_builder, [a.XCoordinate for a in lst])
+	self.YCoordinate, YDeviceTable = buildVarDevTable(merger.store_builder, [a.YCoordinate for a in lst])
+	if XDeviceTable or YDeviceTable:
+		self.Format = 3
+		self.XDeviceTable = XDeviceTable
+		self.YDeviceTable = YDeviceTable
+
+@VariationMerger.merger(otBase.ValueRecord)
+def merge(merger, self, lst):
+	for name, tableName in [('XAdvance','XAdvDevice'),
+				('YAdvance','YAdvDevice'),
+				('XPlacement','XPlaDevice'),
+				('YPlacement','YPlaDevice')]:
+
+		if hasattr(self, name):
+			value, deviceTable = buildVarDevTable(merger.store_builder,
+							      [getattr(a, name, 0) for a in lst])
+			setattr(self, name, value)
+			if deviceTable:
+				setattr(self, tableName, deviceTable)
diff --git a/Lib/fontTools/varLib/models.py b/Lib/fontTools/varLib/models.py
new file mode 100644
index 0000000..8ea1432
--- /dev/null
+++ b/Lib/fontTools/varLib/models.py
@@ -0,0 +1,384 @@
+"""Variation fonts interpolation models."""
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+
+__all__ = ['normalizeValue', 'normalizeLocation', 'supportScalar', 'VariationModel']
+
+
+def normalizeValue(v, triple):
+	"""Normalizes value based on a min/default/max triple.
+	>>> normalizeValue(400, (100, 400, 900))
+	0.0
+	>>> normalizeValue(100, (100, 400, 900))
+	-1.0
+	>>> normalizeValue(650, (100, 400, 900))
+	0.5
+	"""
+	lower, default, upper = triple
+	assert lower <= default <= upper, "invalid axis values: %3.3f, %3.3f %3.3f"%(lower, default, upper)
+	v = max(min(v, upper), lower)
+	if v == default:
+		v = 0.
+	elif v < default:
+		v = (v - default) / (default - lower)
+	else:
+		v = (v - default) / (upper - default)
+	return v
+
+def normalizeLocation(location, axes):
+	"""Normalizes location based on axis min/default/max values from axes.
+	>>> axes = {"wght": (100, 400, 900)}
+	>>> normalizeLocation({"wght": 400}, axes)
+	{'wght': 0.0}
+	>>> normalizeLocation({"wght": 100}, axes)
+	{'wght': -1.0}
+	>>> normalizeLocation({"wght": 900}, axes)
+	{'wght': 1.0}
+	>>> normalizeLocation({"wght": 650}, axes)
+	{'wght': 0.5}
+	>>> normalizeLocation({"wght": 1000}, axes)
+	{'wght': 1.0}
+	>>> normalizeLocation({"wght": 0}, axes)
+	{'wght': -1.0}
+	>>> axes = {"wght": (0, 0, 1000)}
+	>>> normalizeLocation({"wght": 0}, axes)
+	{'wght': 0.0}
+	>>> normalizeLocation({"wght": -1}, axes)
+	{'wght': 0.0}
+	>>> normalizeLocation({"wght": 1000}, axes)
+	{'wght': 1.0}
+	>>> normalizeLocation({"wght": 500}, axes)
+	{'wght': 0.5}
+	>>> normalizeLocation({"wght": 1001}, axes)
+	{'wght': 1.0}
+	>>> axes = {"wght": (0, 1000, 1000)}
+	>>> normalizeLocation({"wght": 0}, axes)
+	{'wght': -1.0}
+	>>> normalizeLocation({"wght": -1}, axes)
+	{'wght': -1.0}
+	>>> normalizeLocation({"wght": 500}, axes)
+	{'wght': -0.5}
+	>>> normalizeLocation({"wght": 1000}, axes)
+	{'wght': 0.0}
+	>>> normalizeLocation({"wght": 1001}, axes)
+	{'wght': 0.0}
+	"""
+	out = {}
+	for tag,triple in axes.items():
+		v = location.get(tag, triple[1])
+		out[tag] = normalizeValue(v, triple)
+	return out
+
+def supportScalar(location, support, ot=True):
+	"""Returns the scalar multiplier at location, for a master
+	with support.  If ot is True, then a peak value of zero
+	for support of an axis means "axis does not participate".  That
+	is how OpenType Variation Font technology works.
+	>>> supportScalar({}, {})
+	1.0
+	>>> supportScalar({'wght':.2}, {})
+	1.0
+	>>> supportScalar({'wght':.2}, {'wght':(0,2,3)})
+	0.1
+	>>> supportScalar({'wght':2.5}, {'wght':(0,2,4)})
+	0.75
+	>>> supportScalar({'wght':2.5, 'wdth':0}, {'wght':(0,2,4), 'wdth':(-1,0,+1)})
+	0.75
+	>>> supportScalar({'wght':2.5, 'wdth':.5}, {'wght':(0,2,4), 'wdth':(-1,0,+1)}, ot=False)
+	0.375
+	>>> supportScalar({'wght':2.5, 'wdth':0}, {'wght':(0,2,4), 'wdth':(-1,0,+1)})
+	0.75
+	>>> supportScalar({'wght':2.5, 'wdth':.5}, {'wght':(0,2,4), 'wdth':(-1,0,+1)})
+	0.75
+	"""
+	scalar = 1.
+	for axis,(lower,peak,upper) in support.items():
+		if ot:
+			# OpenType-specific case handling
+			if peak == 0.:
+				continue
+			if lower > peak or peak > upper:
+				continue
+			if lower < 0. and upper > 0.:
+				continue
+			v = location.get(axis, 0.)
+		else:
+			assert axis in location
+			v = location[axis]
+		if v == peak:
+			continue
+		if v <= lower or upper <= v:
+			scalar = 0.
+			break;
+		if v < peak:
+			scalar *= (v - lower) / (peak - lower)
+		else: # v > peak
+			scalar *= (v - upper) / (peak - upper)
+	return scalar
+
+
+class VariationModel(object):
+
+	"""
+	Locations must be in normalized space.  Ie. base master
+	is at origin (0).
+	>>> from pprint import pprint
+	>>> locations = [ \
+	{'wght':100}, \
+	{'wght':-100}, \
+	{'wght':-180}, \
+	{'wdth':+.3}, \
+	{'wght':+120,'wdth':.3}, \
+	{'wght':+120,'wdth':.2}, \
+	{}, \
+	{'wght':+180,'wdth':.3}, \
+	{'wght':+180}, \
+	]
+	>>> model = VariationModel(locations, axisOrder=['wght'])
+	>>> pprint(model.locations)
+	[{},
+	 {'wght': -100},
+	 {'wght': -180},
+	 {'wght': 100},
+	 {'wght': 180},
+	 {'wdth': 0.3},
+	 {'wdth': 0.3, 'wght': 180},
+	 {'wdth': 0.3, 'wght': 120},
+	 {'wdth': 0.2, 'wght': 120}]
+	>>> pprint(model.deltaWeights)
+	[{},
+	 {0: 1.0},
+	 {0: 1.0},
+	 {0: 1.0},
+	 {0: 1.0},
+	 {0: 1.0},
+	 {0: 1.0, 4: 1.0, 5: 1.0},
+	 {0: 1.0, 3: 0.75, 4: 0.25, 5: 1.0, 6: 0.6666666666666666},
+	 {0: 1.0,
+	  3: 0.75,
+	  4: 0.25,
+	  5: 0.6666666666666667,
+	  6: 0.4444444444444445,
+	  7: 0.6666666666666667}]
+	"""
+
+	def __init__(self, locations, axisOrder=[]):
+		locations = [{k:v for k,v in loc.items() if v != 0.} for loc in locations]
+		keyFunc = self.getMasterLocationsSortKeyFunc(locations, axisOrder=axisOrder)
+		axisPoints = keyFunc.axisPoints
+		self.locations = sorted(locations, key=keyFunc)
+		# TODO Assert that locations are unique.
+		self.mapping = [self.locations.index(l) for l in locations] # Mapping from user's master order to our master order
+		self.reverseMapping = [locations.index(l) for l in self.locations] # Reverse of above
+
+		self._computeMasterSupports(axisPoints, axisOrder)
+
+	@staticmethod
+	def getMasterLocationsSortKeyFunc(locations, axisOrder=[]):
+		assert {} in locations, "Base master not found."
+		axisPoints = {}
+		for loc in locations:
+			if len(loc) != 1:
+				continue
+			axis = next(iter(loc))
+			value = loc[axis]
+			if axis not in axisPoints:
+				axisPoints[axis] = {0.}
+			assert value not in axisPoints[axis], (
+				'Value "%s" in axisPoints["%s"] -->  %s' % (value, axis, axisPoints)
+			)
+			axisPoints[axis].add(value)
+
+		def getKey(axisPoints, axisOrder):
+			def sign(v):
+				return -1 if v < 0 else +1 if v > 0 else 0
+			def key(loc):
+				rank = len(loc)
+				onPointAxes = [axis for axis,value in loc.items() if value in axisPoints[axis]]
+				orderedAxes = [axis for axis in axisOrder if axis in loc]
+				orderedAxes.extend([axis for axis in sorted(loc.keys()) if axis not in axisOrder])
+				return (
+					rank, # First, order by increasing rank
+					-len(onPointAxes), # Next, by decreasing number of onPoint axes
+					tuple(axisOrder.index(axis) if axis in axisOrder else 0x10000 for axis in orderedAxes), # Next, by known axes
+					tuple(orderedAxes), # Next, by all axes
+					tuple(sign(loc[axis]) for axis in orderedAxes), # Next, by signs of axis values
+					tuple(abs(loc[axis]) for axis in orderedAxes), # Next, by absolute value of axis values
+				)
+			return key
+
+		ret = getKey(axisPoints, axisOrder)
+		ret.axisPoints = axisPoints
+		return ret
+
+	@staticmethod
+	def lowerBound(value, lst):
+		if any(v < value for v in lst):
+			return max(v for v in lst if v < value)
+		else:
+			return value
+	@staticmethod
+	def upperBound(value, lst):
+		if any(v > value for v in lst):
+			return min(v for v in lst if v > value)
+		else:
+			return value
+
+	def _computeMasterSupports(self, axisPoints, axisOrder):
+		supports = []
+		deltaWeights = []
+		locations = self.locations
+		for i,loc in enumerate(locations):
+			box = {}
+
+			# Account for axisPoints first
+			# TODO Use axis min/max instead? Isn't that always -1/+1?
+			for axis,values in axisPoints.items():
+				if not axis in loc:
+					continue
+				locV = loc[axis]
+				if locV > 0:
+					box[axis] = (0, locV, max({locV}|values))
+				else:
+					box[axis] = (min({locV}|values), locV, 0)
+
+			locAxes = set(loc.keys())
+			# Walk over previous masters now
+			for j,m in enumerate(locations[:i]):
+				# Master with extra axes do not participte
+				if not set(m.keys()).issubset(locAxes):
+					continue
+				# If it's NOT in the current box, it does not participate
+				relevant = True
+				for axis, (lower,peak,upper) in box.items():
+					if axis not in m or not (m[axis] == peak or lower < m[axis] < upper):
+						relevant = False
+						break
+				if not relevant:
+					continue
+
+				# Split the box for new master; split in whatever direction
+				# that has largest range ratio.  See commit for details.
+				orderedAxes = [axis for axis in axisOrder if axis in m.keys()]
+				orderedAxes.extend([axis for axis in sorted(m.keys()) if axis not in axisOrder])
+				bestAxis = None
+				bestRatio = -1
+				for axis in orderedAxes:
+					val = m[axis]
+					assert axis in box
+					lower,locV,upper = box[axis]
+					newLower, newUpper = lower, upper
+					if val < locV:
+						newLower = val
+						ratio = (val - locV) / (lower - locV)
+					elif locV < val:
+						newUpper = val
+						ratio = (val - locV) / (upper - locV)
+					else: # val == locV
+						# Can't split box in this direction.
+						continue
+					if ratio > bestRatio:
+						bestRatio = ratio
+						bestAxis = axis
+						bestLower = newLower
+						bestUpper = newUpper
+						bestLocV = locV
+
+				if bestAxis:
+					box[bestAxis] = (bestLower,bestLocV,bestUpper)
+			supports.append(box)
+
+			deltaWeight = {}
+			# Walk over previous masters now, populate deltaWeight
+			for j,m in enumerate(locations[:i]):
+				scalar = supportScalar(loc, supports[j])
+				if scalar:
+					deltaWeight[j] = scalar
+			deltaWeights.append(deltaWeight)
+
+		self.supports = supports
+		self.deltaWeights = deltaWeights
+
+	def getDeltas(self, masterValues):
+		assert len(masterValues) == len(self.deltaWeights)
+		mapping = self.reverseMapping
+		out = []
+		for i,weights in enumerate(self.deltaWeights):
+			delta = masterValues[mapping[i]]
+			for j,weight in weights.items():
+				delta -= out[j] * weight
+			out.append(delta)
+		return out
+
+	def getScalars(self, loc):
+		return [supportScalar(loc, support) for support in self.supports]
+
+	@staticmethod
+	def interpolateFromDeltasAndScalars(deltas, scalars):
+		v = None
+		assert len(deltas) == len(scalars)
+		for i,(delta,scalar) in enumerate(zip(deltas, scalars)):
+			if not scalar: continue
+			contribution = delta * scalar
+			if v is None:
+				v = contribution
+			else:
+				v += contribution
+		return v
+
+	def interpolateFromDeltas(self, loc, deltas):
+		scalars = self.getScalars(loc)
+		return self.interpolateFromDeltasAndScalars(deltas, scalars)
+
+	def interpolateFromMasters(self, loc, masterValues):
+		deltas = self.getDeltas(masterValues)
+		return self.interpolateFromDeltas(loc, deltas)
+
+	def interpolateFromMastersAndScalars(self, masterValues, scalars):
+		deltas = self.getDeltas(masterValues)
+		return self.interpolateFromDeltasAndScalars(deltas, scalars)
+
+
+def main(args):
+	from fontTools import configLogger
+
+	args = args[1:]
+
+	# TODO: allow user to configure logging via command-line options
+	configLogger(level="INFO")
+
+	if len(args) < 1:
+		print("usage: fonttools varLib.models source.designspace", file=sys.stderr)
+		print("  or")
+		print("usage: fonttools varLib.models location1 location2 ...", file=sys.stderr)
+		sys.exit(1)
+
+	from pprint import pprint
+
+	if len(args) == 1 and args[0].endswith('.designspace'):
+		from fontTools.designspaceLib import DesignSpaceDocument
+		doc = DesignSpaceDocument()
+		doc.read(args[0])
+		locs = [s.location for s in doc.sources]
+		print("Original locations:")
+		pprint(locs)
+		doc.normalize()
+		print("Normalized locations:")
+		pprint(locs)
+	else:
+		axes = [chr(c) for c in range(ord('A'), ord('Z')+1)]
+		locs = [dict(zip(axes, (float(v) for v in s.split(',')))) for s in args]
+
+	model = VariationModel(locs)
+	print("Sorted locations:")
+	pprint(model.locations)
+	print("Supports:")
+	pprint(model.supports)
+
+if __name__ == "__main__":
+	import doctest, sys
+
+	if len(sys.argv) > 1:
+		sys.exit(main(sys.argv))
+
+	sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/varLib/mutator.py b/Lib/fontTools/varLib/mutator.py
new file mode 100644
index 0000000..ac771e8
--- /dev/null
+++ b/Lib/fontTools/varLib/mutator.py
@@ -0,0 +1,212 @@
+"""
+Instantiate a variation font.  Run, eg:
+
+$ fonttools varLib.mutator ./NotoSansArabic-VF.ttf wght=140 wdth=85
+"""
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.fixedTools import floatToFixedToFloat, otRound
+from fontTools.ttLib import TTFont
+from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates
+from fontTools.varLib import _GetCoordinates, _SetCoordinates, _DesignspaceAxis
+from fontTools.varLib.models import supportScalar, normalizeLocation
+from fontTools.varLib.merger import MutatorMerger
+from fontTools.varLib.varStore import VarStoreInstancer
+from fontTools.varLib.mvar import MVAR_ENTRIES
+from fontTools.varLib.iup import iup_delta
+import os.path
+import logging
+
+
+log = logging.getLogger("fontTools.varlib.mutator")
+
+# map 'wdth' axis (1..200) to OS/2.usWidthClass (1..9), rounding to closest
+OS2_WIDTH_CLASS_VALUES = {}
+percents = [50.0, 62.5, 75.0, 87.5, 100.0, 112.5, 125.0, 150.0, 200.0]
+for i, (prev, curr) in enumerate(zip(percents[:-1], percents[1:]), start=1):
+	half = (prev + curr) / 2
+	OS2_WIDTH_CLASS_VALUES[half] = i
+
+
+def instantiateVariableFont(varfont, location, inplace=False):
+	""" Generate a static instance from a variable TTFont and a dictionary
+	defining the desired location along the variable font's axes.
+	The location values must be specified as user-space coordinates, e.g.:
+
+		{'wght': 400, 'wdth': 100}
+
+	By default, a new TTFont object is returned. If ``inplace`` is True, the
+	input varfont is modified and reduced to a static font.
+	"""
+	if not inplace:
+		# make a copy to leave input varfont unmodified
+		stream = BytesIO()
+		varfont.save(stream)
+		stream.seek(0)
+		varfont = TTFont(stream)
+
+	fvar = varfont['fvar']
+	axes = {a.axisTag:(a.minValue,a.defaultValue,a.maxValue) for a in fvar.axes}
+	loc = normalizeLocation(location, axes)
+	if 'avar' in varfont:
+		maps = varfont['avar'].segments
+		loc = {k:_DesignspaceAxis._map(v, maps[k]) for k,v in loc.items()}
+	# Quantize to F2Dot14, to avoid surprise interpolations.
+	loc = {k:floatToFixedToFloat(v, 14) for k,v in loc.items()}
+	# Location is normalized now
+	log.info("Normalized location: %s", loc)
+
+	log.info("Mutating glyf/gvar tables")
+	gvar = varfont['gvar']
+	glyf = varfont['glyf']
+	# get list of glyph names in gvar sorted by component depth
+	glyphnames = sorted(
+		gvar.variations.keys(),
+		key=lambda name: (
+			glyf[name].getCompositeMaxpValues(glyf).maxComponentDepth
+			if glyf[name].isComposite() else 0,
+			name))
+	for glyphname in glyphnames:
+		variations = gvar.variations[glyphname]
+		coordinates,_ = _GetCoordinates(varfont, glyphname)
+		origCoords, endPts = None, None
+		for var in variations:
+			scalar = supportScalar(loc, var.axes)
+			if not scalar: continue
+			delta = var.coordinates
+			if None in delta:
+				if origCoords is None:
+					origCoords,control = _GetCoordinates(varfont, glyphname)
+					endPts = control[1] if control[0] >= 1 else list(range(len(control[1])))
+				delta = iup_delta(delta, origCoords, endPts)
+			coordinates += GlyphCoordinates(delta) * scalar
+		_SetCoordinates(varfont, glyphname, coordinates)
+
+	if 'cvar' in varfont:
+		log.info("Mutating cvt/cvar tables")
+		cvar = varfont['cvar']
+		cvt = varfont['cvt ']
+		deltas = {}
+		for var in cvar.variations:
+			scalar = supportScalar(loc, var.axes)
+			if not scalar: continue
+			for i, c in enumerate(var.coordinates):
+				if c is not None:
+					deltas[i] = deltas.get(i, 0) + scalar * c
+		for i, delta in deltas.items():
+			cvt[i] += otRound(delta)
+
+	if 'MVAR' in varfont:
+		log.info("Mutating MVAR table")
+		mvar = varfont['MVAR'].table
+		varStoreInstancer = VarStoreInstancer(mvar.VarStore, fvar.axes, loc)
+		records = mvar.ValueRecord
+		for rec in records:
+			mvarTag = rec.ValueTag
+			if mvarTag not in MVAR_ENTRIES:
+				continue
+			tableTag, itemName = MVAR_ENTRIES[mvarTag]
+			delta = otRound(varStoreInstancer[rec.VarIdx])
+			if not delta:
+				continue
+			setattr(varfont[tableTag], itemName,
+				getattr(varfont[tableTag], itemName) + delta)
+
+	if 'GDEF' in varfont:
+		log.info("Mutating GDEF/GPOS/GSUB tables")
+		merger = MutatorMerger(varfont, loc)
+
+		log.info("Building interpolated tables")
+		merger.instantiate()
+
+	if 'name' in varfont:
+		log.info("Pruning name table")
+		exclude = {a.axisNameID for a in fvar.axes}
+		for i in fvar.instances:
+			exclude.add(i.subfamilyNameID)
+			exclude.add(i.postscriptNameID)
+		varfont['name'].names[:] = [
+			n for n in varfont['name'].names
+			if n.nameID not in exclude
+		]
+
+	if "wght" in location and "OS/2" in varfont:
+		varfont["OS/2"].usWeightClass = otRound(
+			max(1, min(location["wght"], 1000))
+		)
+	if "wdth" in location:
+		wdth = location["wdth"]
+		for percent, widthClass in sorted(OS2_WIDTH_CLASS_VALUES.items()):
+			if wdth < percent:
+				varfont["OS/2"].usWidthClass = widthClass
+				break
+		else:
+			varfont["OS/2"].usWidthClass = 9
+	if "slnt" in location and "post" in varfont:
+		varfont["post"].italicAngle = max(-90, min(location["slnt"], 90))
+
+	log.info("Removing variable tables")
+	for tag in ('avar','cvar','fvar','gvar','HVAR','MVAR','VVAR','STAT'):
+		if tag in varfont:
+			del varfont[tag]
+
+	return varfont
+
+
+def main(args=None):
+	from fontTools import configLogger
+	import argparse
+
+	parser = argparse.ArgumentParser(
+		"fonttools varLib.mutator", description="Instantiate a variable font")
+	parser.add_argument(
+		"input", metavar="INPUT.ttf", help="Input variable TTF file.")
+	parser.add_argument(
+		"locargs", metavar="AXIS=LOC", nargs="*",
+		help="List of space separated locations. A location consist in "
+		"the name of a variation axis, followed by '=' and a number. E.g.: "
+		" wght=700 wdth=80. The default is the location of the base master.")
+	parser.add_argument(
+		"-o", "--output", metavar="OUTPUT.ttf", default=None,
+		help="Output instance TTF file (default: INPUT-instance.ttf).")
+	logging_group = parser.add_mutually_exclusive_group(required=False)
+	logging_group.add_argument(
+		"-v", "--verbose", action="store_true", help="Run more verbosely.")
+	logging_group.add_argument(
+		"-q", "--quiet", action="store_true", help="Turn verbosity off.")
+	options = parser.parse_args(args)
+
+	varfilename = options.input
+	outfile = (
+		os.path.splitext(varfilename)[0] + '-instance.ttf'
+		if not options.output else options.output)
+	configLogger(level=(
+		"DEBUG" if options.verbose else
+		"ERROR" if options.quiet else
+		"INFO"))
+
+	loc = {}
+	for arg in options.locargs:
+		try:
+			tag, val = arg.split('=')
+			assert len(tag) <= 4
+			loc[tag.ljust(4)] = float(val)
+		except (ValueError, AssertionError):
+			parser.error("invalid location argument format: %r" % arg)
+	log.info("Location: %s", loc)
+
+	log.info("Loading variable font")
+	varfont = TTFont(varfilename)
+
+	instantiateVariableFont(varfont, loc, inplace=True)
+
+	log.info("Saving instance font %s", outfile)
+	varfont.save(outfile)
+
+
+if __name__ == "__main__":
+	import sys
+	if len(sys.argv) > 1:
+		sys.exit(main())
+	import doctest
+	sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/varLib/mvar.py b/Lib/fontTools/varLib/mvar.py
new file mode 100644
index 0000000..92083dd
--- /dev/null
+++ b/Lib/fontTools/varLib/mvar.py
@@ -0,0 +1,44 @@
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+from fontTools.misc.py23 import *
+
+MVAR_ENTRIES = {
+	'hasc': ('OS/2', 'sTypoAscender'),		 # horizontal ascender
+	'hdsc': ('OS/2', 'sTypoDescender'),		 # horizontal descender
+	'hlgp': ('OS/2', 'sTypoLineGap'),		 # horizontal line gap
+	'hcla': ('OS/2', 'usWinAscent'),		 # horizontal clipping ascent
+	'hcld': ('OS/2', 'usWinDescent'),		 # horizontal clipping descent
+	'vasc': ('vhea', 'ascent'),			 # vertical ascender
+	'vdsc': ('vhea', 'descent'),			 # vertical descender
+	'vlgp': ('vhea', 'lineGap'),			 # vertical line gap
+	'hcrs': ('hhea', 'caretSlopeRise'),		 # horizontal caret rise
+	'hcrn': ('hhea', 'caretSlopeRun'),		 # horizontal caret run
+	'hcof': ('hhea', 'caretOffset'),		 # horizontal caret offset
+	'vcrs': ('vhea', 'caretSlopeRise'),		 # vertical caret rise
+	'vcrn': ('vhea', 'caretSlopeRun'),		 # vertical caret run
+	'vcof': ('vhea', 'caretOffset'),		 # vertical caret offset
+	'xhgt': ('OS/2', 'sxHeight'),			 # x height
+	'cpht': ('OS/2', 'sCapHeight'),			 # cap height
+	'sbxs': ('OS/2', 'ySubscriptXSize'),		 # subscript em x size
+	'sbys': ('OS/2', 'ySubscriptYSize'),		 # subscript em y size
+	'sbxo': ('OS/2', 'ySubscriptXOffset'),		 # subscript em x offset
+	'sbyo': ('OS/2', 'ySubscriptYOffset'),		 # subscript em y offset
+	'spxs': ('OS/2', 'ySuperscriptXSize'),		 # superscript em x size
+	'spys': ('OS/2', 'ySuperscriptYSize'),		 # superscript em y size
+	'spxo': ('OS/2', 'ySuperscriptXOffset'),	 # superscript em x offset
+	'spyo': ('OS/2', 'ySuperscriptYOffset'),	 # superscript em y offset
+	'strs': ('OS/2', 'yStrikeoutSize'),		 # strikeout size
+	'stro': ('OS/2', 'yStrikeoutPosition'),		 # strikeout offset
+	'unds': ('post', 'underlineThickness'),		 # underline size
+	'undo': ('post', 'underlinePosition'),		 # underline offset
+	#'gsp0': ('gasp', 'gaspRange[0].rangeMaxPPEM'),	 # gaspRange[0]
+	#'gsp1': ('gasp', 'gaspRange[1].rangeMaxPPEM'),	 # gaspRange[1]
+	#'gsp2': ('gasp', 'gaspRange[2].rangeMaxPPEM'),	 # gaspRange[2]
+	#'gsp3': ('gasp', 'gaspRange[3].rangeMaxPPEM'),	 # gaspRange[3]
+	#'gsp4': ('gasp', 'gaspRange[4].rangeMaxPPEM'),	 # gaspRange[4]
+	#'gsp5': ('gasp', 'gaspRange[5].rangeMaxPPEM'),	 # gaspRange[5]
+	#'gsp6': ('gasp', 'gaspRange[6].rangeMaxPPEM'),	 # gaspRange[6]
+	#'gsp7': ('gasp', 'gaspRange[7].rangeMaxPPEM'),	 # gaspRange[7]
+	#'gsp8': ('gasp', 'gaspRange[8].rangeMaxPPEM'),	 # gaspRange[8]
+	#'gsp9': ('gasp', 'gaspRange[9].rangeMaxPPEM'),	 # gaspRange[9]
+}
diff --git a/Lib/fontTools/varLib/plot.py b/Lib/fontTools/varLib/plot.py
new file mode 100644
index 0000000..b03744e
--- /dev/null
+++ b/Lib/fontTools/varLib/plot.py
@@ -0,0 +1,118 @@
+"""Visualize DesignSpaceDocument and resulting VariationModel."""
+
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.varLib.models import VariationModel, supportScalar
+from fontTools.designspaceLib import DesignSpaceDocument
+from mpl_toolkits.mplot3d import axes3d
+from matplotlib import pyplot
+from itertools import cycle
+import math
+import logging
+import sys
+
+log = logging.getLogger(__name__)
+
+
+def stops(support, count=10):
+	a,b,c = support
+
+	return [a + (b - a) * i / count for i in range(count)] + \
+	       [b + (c - b) * i / count for i in range(count)] + \
+	       [c]
+
+def plotLocations(locations, axes, axis3D, **kwargs):
+	for loc,color in zip(locations, cycle(pyplot.cm.Set1.colors)):
+		axis3D.plot([loc.get(axes[0], 0)],
+			    [loc.get(axes[1], 0)],
+			    [1.],
+			    'o',
+			    color=color,
+			    **kwargs)
+
+def plotLocationsSurfaces(locations, fig, names=None, **kwargs):
+
+	assert len(locations[0].keys()) == 2
+
+	if names is None:
+		names = ['']
+
+	n = len(locations)
+	cols = math.ceil(n**.5)
+	rows = math.ceil(n / cols)
+
+	model = VariationModel(locations)
+	names = [names[model.reverseMapping[i]] for i in range(len(names))]
+
+	ax1, ax2 = sorted(locations[0].keys())
+	for i, (support,color, name) in enumerate(zip(model.supports, cycle(pyplot.cm.Set1.colors), cycle(names))):
+
+		axis3D = fig.add_subplot(rows, cols, i + 1, projection='3d')
+		axis3D.set_title(name)
+		axis3D.set_xlabel(ax1)
+		axis3D.set_ylabel(ax2)
+		pyplot.xlim(-1.,+1.)
+		pyplot.ylim(-1.,+1.)
+
+		Xs = support.get(ax1, (-1.,0.,+1.))
+		Ys = support.get(ax2, (-1.,0.,+1.))
+		for x in stops(Xs):
+			X, Y, Z = [], [], []
+			for y in Ys:
+				z = supportScalar({ax1:x, ax2:y}, support)
+				X.append(x)
+				Y.append(y)
+				Z.append(z)
+			axis3D.plot(X, Y, Z, color=color, **kwargs)
+		for y in stops(Ys):
+			X, Y, Z = [], [], []
+			for x in Xs:
+				z = supportScalar({ax1:x, ax2:y}, support)
+				X.append(x)
+				Y.append(y)
+				Z.append(z)
+			axis3D.plot(X, Y, Z, color=color, **kwargs)
+
+		plotLocations(model.locations, [ax1, ax2], axis3D)
+
+
+def plotDocument(doc, fig, **kwargs):
+	doc.normalize()
+	locations = [s.location for s in doc.sources]
+	names = [s.name for s in doc.sources]
+	plotLocationsSurfaces(locations, fig, names, **kwargs)
+
+
+def main(args=None):
+	from fontTools import configLogger
+
+	if args is None:
+		args = sys.argv[1:]
+
+	# configure the library logger (for >= WARNING)
+	configLogger()
+	# comment this out to enable debug messages from logger
+	# log.setLevel(logging.DEBUG)
+
+	if len(args) < 1:
+		print("usage: fonttools varLib.plot source.designspace", file=sys.stderr)
+		print("  or")
+		print("usage: fonttools varLib.plot location1 location2 ...", file=sys.stderr)
+		sys.exit(1)
+
+	fig = pyplot.figure()
+
+	if len(args) == 1 and args[0].endswith('.designspace'):
+		doc = DesignSpaceDocument()
+		doc.read(args[0])
+		plotDocument(doc, fig)
+	else:
+		axes = [chr(c) for c in range(ord('A'), ord('Z')+1)]
+		locs = [dict(zip(axes, (float(v) for v in s.split(',')))) for s in args]
+		plotLocationsSurfaces(locs, fig)
+
+	pyplot.show()
+
+if __name__ == '__main__':
+	import sys
+	sys.exit(main())
diff --git a/Lib/fontTools/varLib/varStore.py b/Lib/fontTools/varLib/varStore.py
new file mode 100644
index 0000000..a1ca7df
--- /dev/null
+++ b/Lib/fontTools/varLib/varStore.py
@@ -0,0 +1,520 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.fixedTools import otRound
+from fontTools.ttLib.tables import otTables as ot
+from fontTools.varLib.models import supportScalar
+from fontTools.varLib.builder import (buildVarRegionList, buildVarStore,
+				      buildVarRegion, buildVarData,
+				      VarData_CalculateNumShorts)
+from functools import partial
+from collections import defaultdict
+from array import array
+
+
+def _getLocationKey(loc):
+	return tuple(sorted(loc.items(), key=lambda kv: kv[0]))
+
+
+class OnlineVarStoreBuilder(object):
+
+	def __init__(self, axisTags):
+		self._axisTags = axisTags
+		self._regionMap = {}
+		self._regionList = buildVarRegionList([], axisTags)
+		self._store = buildVarStore(self._regionList, [])
+		self._data = None
+		self._model = None
+		self._cache = {}
+
+	def setModel(self, model):
+		self._model = model
+		self._cache = {} # Empty cached items
+
+	def finish(self, optimize=True):
+		self._regionList.RegionCount = len(self._regionList.Region)
+		self._store.VarDataCount = len(self._store.VarData)
+		for data in self._store.VarData:
+			data.ItemCount = len(data.Item)
+			VarData_CalculateNumShorts(data, optimize)
+		return self._store
+
+	def _add_VarData(self):
+		regionMap = self._regionMap
+		regionList = self._regionList
+
+		regions = self._model.supports[1:]
+		regionIndices = []
+		for region in regions:
+			key = _getLocationKey(region)
+			idx = regionMap.get(key)
+			if idx is None:
+				varRegion = buildVarRegion(region, self._axisTags)
+				idx = regionMap[key] = len(regionList.Region)
+				regionList.Region.append(varRegion)
+			regionIndices.append(idx)
+
+		data = self._data = buildVarData(regionIndices, [], optimize=False)
+		self._outer = len(self._store.VarData)
+		self._store.VarData.append(data)
+
+	def storeMasters(self, master_values):
+		deltas = [otRound(d) for d in self._model.getDeltas(master_values)]
+		base = deltas.pop(0)
+		deltas = tuple(deltas)
+		varIdx = self._cache.get(deltas)
+		if varIdx is not None:
+			return base, varIdx
+
+		if not self._data:
+			self._add_VarData()
+		inner = len(self._data.Item)
+		if inner == 0xFFFF:
+			# Full array. Start new one.
+			self._add_VarData()
+			return self.storeMasters(master_values)
+		self._data.Item.append(deltas)
+
+		varIdx = (self._outer << 16) + inner
+		self._cache[deltas] = varIdx
+		return base, varIdx
+
+
+def VarRegion_get_support(self, fvar_axes):
+	return {fvar_axes[i].axisTag: (reg.StartCoord,reg.PeakCoord,reg.EndCoord)
+		for i,reg in enumerate(self.VarRegionAxis)}
+
+class VarStoreInstancer(object):
+
+	def __init__(self, varstore, fvar_axes, location={}):
+		self.fvar_axes = fvar_axes
+		assert varstore is None or varstore.Format == 1
+		self._varData = varstore.VarData if varstore else []
+		self._regions = varstore.VarRegionList.Region if varstore else []
+		self.setLocation(location)
+
+	def setLocation(self, location):
+		self.location = dict(location)
+		self._clearCaches()
+
+	def _clearCaches(self):
+		self._scalars = {}
+
+	def _getScalar(self, regionIdx):
+		scalar = self._scalars.get(regionIdx)
+		if scalar is None:
+			support = VarRegion_get_support(self._regions[regionIdx], self.fvar_axes)
+			scalar = supportScalar(self.location, support)
+			self._scalars[regionIdx] = scalar
+		return scalar
+
+	def __getitem__(self, varidx):
+
+		major, minor = varidx >> 16, varidx & 0xFFFF
+
+		varData = self._varData
+		scalars = [self._getScalar(ri) for ri in varData[major].VarRegionIndex]
+
+		deltas = varData[major].Item[minor]
+		delta = 0.
+		for d,s in zip(deltas, scalars):
+			delta += d * s
+		return delta
+
+
+#
+# Optimizations
+#
+
+def VarStore_subset_varidxes(self, varIdxes, optimize=True):
+
+	# Sort out used varIdxes by major/minor.
+	used = {}
+	for varIdx in varIdxes:
+		major = varIdx >> 16
+		minor = varIdx & 0xFFFF
+		d = used.get(major)
+		if d is None:
+			d = used[major] = set()
+		d.add(minor)
+	del varIdxes
+
+	#
+	# Subset VarData
+	#
+
+	varData = self.VarData
+	newVarData = []
+	varDataMap = {}
+	for major,data in enumerate(varData):
+		usedMinors = used.get(major)
+		if usedMinors is None:
+			continue
+		newMajor = varDataMap[major] = len(newVarData)
+		newVarData.append(data)
+
+		items = data.Item
+		newItems = []
+		for minor in sorted(usedMinors):
+			newMinor = len(newItems)
+			newItems.append(items[minor])
+			varDataMap[(major<<16)+minor] = (newMajor<<16)+newMinor
+
+		data.Item = newItems
+		data.ItemCount = len(data.Item)
+
+		if optimize:
+			VarData_CalculateNumShorts(data)
+
+	self.VarData = newVarData
+	self.VarDataCount = len(self.VarData)
+
+	self.prune_regions()
+
+	return varDataMap
+
+ot.VarStore.subset_varidxes = VarStore_subset_varidxes
+
+def VarStore_prune_regions(self):
+	"""Remove unused VarRegions."""
+	#
+	# Subset VarRegionList
+	#
+
+	# Collect.
+	usedRegions = set()
+	for data in self.VarData:
+		usedRegions.update(data.VarRegionIndex)
+	# Subset.
+	regionList = self.VarRegionList
+	regions = regionList.Region
+	newRegions = []
+	regionMap = {}
+	for i in sorted(usedRegions):
+		regionMap[i] = len(newRegions)
+		newRegions.append(regions[i])
+	regionList.Region = newRegions
+	regionList.RegionCount = len(regionList.Region)
+	# Map.
+	for data in self.VarData:
+		data.VarRegionIndex = [regionMap[i] for i in data.VarRegionIndex]
+
+ot.VarStore.prune_regions = VarStore_prune_regions
+
+
+def _visit(self, objType, func):
+	"""Recurse down from self, if type of an object is objType,
+	call func() on it.  Only works for otData-style classes."""
+
+	if type(self) == objType:
+		func(self)
+		return # We don't recurse down; don't need to.
+
+	if isinstance(self, list):
+		for that in self:
+			_visit(that, objType, func)
+
+	if hasattr(self, 'getConverters'):
+		for conv in self.getConverters():
+			that = getattr(self, conv.name, None)
+			if that is not None:
+				_visit(that, objType, func)
+
+	if isinstance(self, ot.ValueRecord):
+		for that in self.__dict__.values():
+			_visit(that, objType, func)
+
+def _Device_recordVarIdx(self, s):
+	"""Add VarIdx in this Device table (if any) to the set s."""
+	if self.DeltaFormat == 0x8000:
+		s.add((self.StartSize<<16)+self.EndSize)
+
+def Object_collect_device_varidxes(self, varidxes):
+	adder = partial(_Device_recordVarIdx, s=varidxes)
+	_visit(self, ot.Device, adder)
+
+ot.GDEF.collect_device_varidxes = Object_collect_device_varidxes
+ot.GPOS.collect_device_varidxes = Object_collect_device_varidxes
+
+def _Device_mapVarIdx(self, mapping, done):
+	"""Add VarIdx in this Device table (if any) to the set s."""
+	if id(self) in done:
+		return
+	done.add(id(self))
+	if self.DeltaFormat == 0x8000:
+		varIdx = mapping[(self.StartSize<<16)+self.EndSize]
+		self.StartSize = varIdx >> 16
+		self.EndSize = varIdx & 0xFFFF
+
+def Object_remap_device_varidxes(self, varidxes_map):
+	mapper = partial(_Device_mapVarIdx, mapping=varidxes_map, done=set())
+	_visit(self, ot.Device, mapper)
+
+ot.GDEF.remap_device_varidxes = Object_remap_device_varidxes
+ot.GPOS.remap_device_varidxes = Object_remap_device_varidxes
+
+
+class _Encoding(object):
+
+	def __init__(self, chars):
+		self.chars = chars
+		self.width = self._popcount(chars)
+		self.overhead = self._characteristic_overhead(chars)
+		self.items = set()
+
+	def append(self, row):
+		self.items.add(row)
+
+	def extend(self, lst):
+		self.items.update(lst)
+
+	def get_room(self):
+		"""Maximum number of bytes that can be added to characteristic
+		while still being beneficial to merge it into another one."""
+		count = len(self.items)
+		return max(0, (self.overhead - 1) // count - self.width)
+	room = property(get_room)
+
+	@property
+	def gain(self):
+		"""Maximum possible byte gain from merging this into another
+		characteristic."""
+		count = len(self.items)
+		return max(0, self.overhead - count * (self.width + 1))
+
+	def sort_key(self):
+		return self.width, self.chars
+
+	def __len__(self):
+		return len(self.items)
+
+	def can_encode(self, chars):
+		return not (chars & ~self.chars)
+
+	def __sub__(self, other):
+		return self._popcount(self.chars & ~other.chars)
+
+	@staticmethod
+	def _popcount(n):
+		# Apparently this is the fastest native way to do it...
+		# https://stackoverflow.com/a/9831671
+		return bin(n).count('1')
+
+	@staticmethod
+	def _characteristic_overhead(chars):
+		"""Returns overhead in bytes of encoding this characteristic
+		as a VarData."""
+		c = 6
+		while chars:
+			if chars & 3:
+				c += 2
+			chars >>= 2
+		return c
+
+
+	def _find_yourself_best_new_encoding(self, done_by_width):
+		self.best_new_encoding = None
+		for new_width in range(self.width+1, self.width+self.room+1):
+			for new_encoding in done_by_width[new_width]:
+				if new_encoding.can_encode(self.chars):
+					break
+			else:
+				new_encoding = None
+			self.best_new_encoding = new_encoding
+
+
+class _EncodingDict(dict):
+
+	def __missing__(self, chars):
+		r = self[chars] = _Encoding(chars)
+		return r
+
+	def add_row(self, row):
+		chars = self._row_characteristics(row)
+		self[chars].append(row)
+
+	@staticmethod
+	def _row_characteristics(row):
+		"""Returns encoding characteristics for a row."""
+		chars = 0
+		i = 1
+		for v in row:
+			if v:
+				chars += i
+			if not (-128 <= v <= 127):
+				chars += i * 2
+			i <<= 2
+		return chars
+
+
+def VarStore_optimize(self):
+	"""Optimize storage. Returns mapping from old VarIdxes to new ones."""
+
+	# TODO
+	# Check that no two VarRegions are the same; if they are, fold them.
+
+	n = len(self.VarRegionList.Region) # Number of columns
+	zeroes = array('h', [0]*n)
+
+	front_mapping = {} # Map from old VarIdxes to full row tuples
+
+	encodings = _EncodingDict()
+
+	# Collect all items into a set of full rows (with lots of zeroes.)
+	for major,data in enumerate(self.VarData):
+		regionIndices = data.VarRegionIndex
+
+		for minor,item in enumerate(data.Item):
+
+			row = array('h', zeroes)
+			for regionIdx,v in zip(regionIndices, item):
+				row[regionIdx] += v
+			row = tuple(row)
+
+			encodings.add_row(row)
+			front_mapping[(major<<16)+minor] = row
+
+	# Separate encodings that have no gain (are decided) and those having
+	# possible gain (possibly to be merged into others.)
+	encodings = sorted(encodings.values(), key=_Encoding.__len__, reverse=True)
+	done_by_width = defaultdict(list)
+	todo = []
+	for encoding in encodings:
+		if not encoding.gain:
+			done_by_width[encoding.width].append(encoding)
+		else:
+			todo.append(encoding)
+
+	# For each encoding that is possibly to be merged, find the best match
+	# in the decided encodings, and record that.
+	todo.sort(key=_Encoding.get_room)
+	for encoding in todo:
+		encoding._find_yourself_best_new_encoding(done_by_width)
+
+	# Walk through todo encodings, for each, see if merging it with
+	# another todo encoding gains more than each of them merging with
+	# their best decided encoding. If yes, merge them and add resulting
+	# encoding back to todo queue.  If not, move the enconding to decided
+	# list.  Repeat till done.
+	while todo:
+		encoding = todo.pop()
+		best_idx = None
+		best_gain = 0
+		for i,other_encoding in enumerate(todo):
+			combined_chars = other_encoding.chars | encoding.chars
+			combined_width = _Encoding._popcount(combined_chars)
+			combined_overhead = _Encoding._characteristic_overhead(combined_chars)
+			combined_gain = (
+					+ encoding.overhead
+					+ other_encoding.overhead
+					- combined_overhead
+					- (combined_width - encoding.width) * len(encoding)
+					- (combined_width - other_encoding.width) * len(other_encoding)
+					)
+			this_gain = 0 if encoding.best_new_encoding is None else (
+						+ encoding.overhead
+						- (encoding.best_new_encoding.width - encoding.width) * len(encoding)
+					)
+			other_gain = 0 if other_encoding.best_new_encoding is None else (
+						+ other_encoding.overhead
+						- (other_encoding.best_new_encoding.width - other_encoding.width) * len(other_encoding)
+					)
+			separate_gain = this_gain + other_gain
+
+			if combined_gain > separate_gain:
+				best_idx = i
+				best_gain = combined_gain - separate_gain
+
+		if best_idx is None:
+			# Encoding is decided as is
+			done_by_width[encoding.width].append(encoding)
+		else:
+			other_encoding = todo[best_idx]
+			combined_chars = other_encoding.chars | encoding.chars
+			combined_encoding = _Encoding(combined_chars)
+			combined_encoding.extend(encoding.items)
+			combined_encoding.extend(other_encoding.items)
+			combined_encoding._find_yourself_best_new_encoding(done_by_width)
+			del todo[best_idx]
+			todo.append(combined_encoding)
+
+	# Assemble final store.
+	back_mapping = {} # Mapping from full rows to new VarIdxes
+	encodings = sum(done_by_width.values(), [])
+	encodings.sort(key=_Encoding.sort_key)
+	self.VarData = []
+	for major,encoding in enumerate(encodings):
+		data = ot.VarData()
+		self.VarData.append(data)
+		data.VarRegionIndex = range(n)
+		data.VarRegionCount = len(data.VarRegionIndex)
+		data.Item = sorted(encoding.items)
+		for minor,item in enumerate(data.Item):
+			back_mapping[item] = (major<<16)+minor
+
+	# Compile final mapping.
+	varidx_map = {}
+	for k,v in front_mapping.items():
+		varidx_map[k] = back_mapping[v]
+
+	# Remove unused regions.
+	self.prune_regions()
+
+	# Recalculate things and go home.
+	self.VarRegionList.RegionCount = len(self.VarRegionList.Region)
+	self.VarDataCount = len(self.VarData)
+	for data in self.VarData:
+		data.ItemCount = len(data.Item)
+		VarData_CalculateNumShorts(data)
+
+	return varidx_map
+
+ot.VarStore.optimize = VarStore_optimize
+
+
+def main(args=None):
+	from argparse import ArgumentParser
+	from fontTools import configLogger
+	from fontTools.ttLib import TTFont
+	from fontTools.ttLib.tables.otBase import OTTableWriter
+
+	parser = ArgumentParser(prog='varLib.varStore')
+	parser.add_argument('fontfile')
+	parser.add_argument('outfile', nargs='?')
+	options = parser.parse_args(args)
+
+	# TODO: allow user to configure logging via command-line options
+	configLogger(level="INFO")
+
+	fontfile = options.fontfile
+	outfile = options.outfile
+
+	font = TTFont(fontfile)
+	gdef = font['GDEF']
+	store = gdef.table.VarStore
+
+	writer = OTTableWriter()
+	store.compile(writer, font)
+	size = len(writer.getAllData())
+	print("Before: %7d bytes" % size)
+
+	varidx_map = store.optimize()
+
+	gdef.table.remap_device_varidxes(varidx_map)
+	if 'GPOS' in font:
+		font['GPOS'].table.remap_device_varidxes(varidx_map)
+
+	writer = OTTableWriter()
+	store.compile(writer, font)
+	size = len(writer.getAllData())
+	print("After:  %7d bytes" % size)
+
+	if outfile is not None:
+		font.save(outfile)
+
+
+if __name__ == "__main__":
+	import sys
+	if len(sys.argv) > 1:
+		sys.exit(main())
+	import doctest
+	sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/voltLib/__init__.py b/Lib/fontTools/voltLib/__init__.py
new file mode 100644
index 0000000..886aa3a
--- /dev/null
+++ b/Lib/fontTools/voltLib/__init__.py
@@ -0,0 +1,5 @@
+"""fontTools.voltLib -- a package for dealing with Visual OpenType Layout Tool
+(VOLT) files."""
+
+# See
+# http://www.microsoft.com/typography/VOLT.mspx
diff --git a/Lib/fontTools/voltLib/ast.py b/Lib/fontTools/voltLib/ast.py
new file mode 100644
index 0000000..4e78600
--- /dev/null
+++ b/Lib/fontTools/voltLib/ast.py
@@ -0,0 +1,253 @@
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+from fontTools.voltLib.error import VoltLibError
+
+
+class Statement(object):
+    def __init__(self, location=None):
+        self.location = location
+
+    def build(self, builder):
+        pass
+
+
+class Expression(object):
+    def __init__(self, location=None):
+        self.location = location
+
+    def build(self, builder):
+        pass
+
+
+class Block(Statement):
+    def __init__(self, location=None):
+        Statement.__init__(self, location)
+        self.statements = []
+
+    def build(self, builder):
+        for s in self.statements:
+            s.build(builder)
+
+
+class VoltFile(Block):
+    def __init__(self):
+        Block.__init__(self, location=None)
+
+
+class LookupBlock(Block):
+    def __init__(self, name, location=None):
+        Block.__init__(self, location)
+        self.name = name
+
+    def build(self, builder):
+        builder.start_lookup_block(self.location, self.name)
+        Block.build(self, builder)
+        builder.end_lookup_block()
+
+
+class GlyphDefinition(Statement):
+    def __init__(self, name, gid, gunicode, gtype, components, location=None):
+        Statement.__init__(self, location)
+        self.name = name
+        self.id = gid
+        self.unicode = gunicode
+        self.type = gtype
+        self.components = components
+
+
+class GroupDefinition(Statement):
+    def __init__(self, name, enum, location=None):
+        Statement.__init__(self, location)
+        self.name = name
+        self.enum = enum
+        self.glyphs_ = None
+
+    def glyphSet(self, groups=None):
+        if groups is not None and self.name in groups:
+            raise VoltLibError(
+                'Group "%s" contains itself.' % (self.name),
+                self.location)
+        if self.glyphs_ is None:
+            if groups is None:
+                groups = set({self.name})
+            else:
+                groups.add(self.name)
+            self.glyphs_ = self.enum.glyphSet(groups)
+        return self.glyphs_
+
+
+class GlyphName(Expression):
+    """A single glyph name, such as cedilla."""
+    def __init__(self, glyph, location=None):
+        Expression.__init__(self, location)
+        self.glyph = glyph
+
+    def glyphSet(self):
+        return frozenset((self.glyph,))
+
+
+class Enum(Expression):
+    """An enum"""
+    def __init__(self, enum, location=None):
+        Expression.__init__(self, location)
+        self.enum = enum
+
+    def __iter__(self):
+        for e in self.glyphSet():
+            yield e
+
+    def glyphSet(self, groups=None):
+        glyphs = set()
+        for element in self.enum:
+            if isinstance(element, (GroupName, Enum)):
+                glyphs = glyphs.union(element.glyphSet(groups))
+            else:
+                glyphs = glyphs.union(element.glyphSet())
+        return frozenset(glyphs)
+
+
+class GroupName(Expression):
+    """A glyph group"""
+    def __init__(self, group, parser, location=None):
+        Expression.__init__(self, location)
+        self.group = group
+        self.parser_ = parser
+
+    def glyphSet(self, groups=None):
+        group = self.parser_.resolve_group(self.group)
+        if group is not None:
+            self.glyphs_ = group.glyphSet(groups)
+            return self.glyphs_
+        else:
+            raise VoltLibError(
+                'Group "%s" is used but undefined.' % (self.group),
+                self.location)
+
+
+class Range(Expression):
+    """A glyph range"""
+    def __init__(self, start, end, parser, location=None):
+        Expression.__init__(self, location)
+        self.start = start
+        self.end = end
+        self.parser = parser
+
+    def glyphSet(self):
+        glyphs = self.parser.glyph_range(self.start, self.end)
+        return frozenset(glyphs)
+
+
+class ScriptDefinition(Statement):
+    def __init__(self, name, tag, langs, location=None):
+        Statement.__init__(self, location)
+        self.name = name
+        self.tag = tag
+        self.langs = langs
+
+
+class LangSysDefinition(Statement):
+    def __init__(self, name, tag, features, location=None):
+        Statement.__init__(self, location)
+        self.name = name
+        self.tag = tag
+        self.features = features
+
+
+class FeatureDefinition(Statement):
+    def __init__(self, name, tag, lookups, location=None):
+        Statement.__init__(self, location)
+        self.name = name
+        self.tag = tag
+        self.lookups = lookups
+
+
+class LookupDefinition(Statement):
+    def __init__(self, name, process_base, process_marks, direction,
+                 reversal, comments, context, sub, pos, location=None):
+        Statement.__init__(self, location)
+        self.name = name
+        self.process_base = process_base
+        self.process_marks = process_marks
+        self.direction = direction
+        self.reversal = reversal
+        self.comments = comments
+        self.context = context
+        self.sub = sub
+        self.pos = pos
+
+
+class SubstitutionDefinition(Statement):
+    def __init__(self, mapping, location=None):
+        Statement.__init__(self, location)
+        self.mapping = mapping
+
+
+class SubstitutionSingleDefinition(SubstitutionDefinition):
+    pass
+
+
+class SubstitutionMultipleDefinition(SubstitutionDefinition):
+    pass
+
+
+class SubstitutionLigatureDefinition(SubstitutionDefinition):
+    pass
+
+
+class SubstitutionReverseChainingSingleDefinition(SubstitutionDefinition):
+    pass
+
+
+class PositionAttachDefinition(Statement):
+    def __init__(self, coverage, coverage_to, location=None):
+        Statement.__init__(self, location)
+        self.coverage = coverage
+        self.coverage_to = coverage_to
+
+
+class PositionAttachCursiveDefinition(Statement):
+    def __init__(self, coverages_exit, coverages_enter, location=None):
+        Statement.__init__(self, location)
+        self.coverages_exit = coverages_exit
+        self.coverages_enter = coverages_enter
+
+
+class PositionAdjustPairDefinition(Statement):
+    def __init__(self, coverages_1, coverages_2, adjust_pair, location=None):
+        Statement.__init__(self, location)
+        self.coverages_1 = coverages_1
+        self.coverages_2 = coverages_2
+        self.adjust_pair = adjust_pair
+
+
+class PositionAdjustSingleDefinition(Statement):
+    def __init__(self, adjust_single, location=None):
+        Statement.__init__(self, location)
+        self.adjust_single = adjust_single
+
+
+class ContextDefinition(Statement):
+    def __init__(self, ex_or_in, left=None, right=None, location=None):
+        Statement.__init__(self, location)
+        self.ex_or_in = ex_or_in
+        self.left = left if left is not None else []
+        self.right = right if right is not None else []
+
+
+class AnchorDefinition(Statement):
+    def __init__(self, name, gid, glyph_name, component, locked,
+                 pos, location=None):
+        Statement.__init__(self, location)
+        self.name = name
+        self.gid = gid
+        self.glyph_name = glyph_name
+        self.component = component
+        self.locked = locked
+        self.pos = pos
+
+
+class SettingDefinition(Statement):
+    def __init__(self, name, value, location=None):
+        Statement.__init__(self, location)
+        self.name = name
+        self.value = value
diff --git a/Lib/fontTools/voltLib/error.py b/Lib/fontTools/voltLib/error.py
new file mode 100644
index 0000000..f9900ad
--- /dev/null
+++ b/Lib/fontTools/voltLib/error.py
@@ -0,0 +1,16 @@
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+
+
+class VoltLibError(Exception):
+    def __init__(self, message, location):
+        Exception.__init__(self, message)
+        self.location = location
+
+    def __str__(self):
+        message = Exception.__str__(self)
+        if self.location:
+            path, line, column = self.location
+            return "%s:%d:%d: %s" % (path, line, column, message)
+        else:
+            return message
diff --git a/Lib/fontTools/voltLib/lexer.py b/Lib/fontTools/voltLib/lexer.py
new file mode 100644
index 0000000..271fe3b
--- /dev/null
+++ b/Lib/fontTools/voltLib/lexer.py
@@ -0,0 +1,98 @@
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+from fontTools.voltLib.error import VoltLibError
+
+class Lexer(object):
+    NUMBER = "NUMBER"
+    STRING = "STRING"
+    NAME = "NAME"
+    NEWLINE = "NEWLINE"
+
+    CHAR_WHITESPACE_ = " \t"
+    CHAR_NEWLINE_ = "\r\n"
+    CHAR_DIGIT_ = "0123456789"
+    CHAR_UC_LETTER_ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+    CHAR_LC_LETTER_ = "abcdefghijklmnopqrstuvwxyz"
+    CHAR_UNDERSCORE_ = "_"
+    CHAR_PERIOD_ = "."
+    CHAR_NAME_START_ = CHAR_UC_LETTER_ + CHAR_LC_LETTER_ + CHAR_PERIOD_ + \
+        CHAR_UNDERSCORE_
+    CHAR_NAME_CONTINUATION_ = CHAR_NAME_START_ + CHAR_DIGIT_
+
+    def __init__(self, text, filename):
+        self.filename_ = filename
+        self.line_ = 1
+        self.pos_ = 0
+        self.line_start_ = 0
+        self.text_ = text
+        self.text_length_ = len(text)
+
+    def __iter__(self):
+        return self
+
+    def next(self):  # Python 2
+        return self.__next__()
+
+    def __next__(self):  # Python 3
+        while True:
+            token_type, token, location = self.next_()
+            if token_type not in {Lexer.NEWLINE}:
+                return (token_type, token, location)
+
+    def next_(self):
+        self.scan_over_(Lexer.CHAR_WHITESPACE_)
+        column = self.pos_ - self.line_start_ + 1
+        location = (self.filename_, self.line_, column)
+        start = self.pos_
+        text = self.text_
+        limit = len(text)
+        if start >= limit:
+            raise StopIteration()
+        cur_char = text[start]
+        next_char = text[start + 1] if start + 1 < limit else None
+
+        if cur_char == "\n":
+            self.pos_ += 1
+            self.line_ += 1
+            self.line_start_ = self.pos_
+            return (Lexer.NEWLINE, None, location)
+        if cur_char == "\r":
+            self.pos_ += (2 if next_char == "\n" else 1)
+            self.line_ += 1
+            self.line_start_ = self.pos_
+            return (Lexer.NEWLINE, None, location)
+        if cur_char == '"':
+            self.pos_ += 1
+            self.scan_until_('"\r\n')
+            if self.pos_ < self.text_length_ and self.text_[self.pos_] == '"':
+                self.pos_ += 1
+                return (Lexer.STRING, text[start + 1:self.pos_ - 1], location)
+            else:
+                raise VoltLibError("Expected '\"' to terminate string",
+                                   location)
+        if cur_char in Lexer.CHAR_NAME_START_:
+            self.pos_ += 1
+            self.scan_over_(Lexer.CHAR_NAME_CONTINUATION_)
+            token = text[start:self.pos_]
+            return (Lexer.NAME, token, location)
+        if cur_char in Lexer.CHAR_DIGIT_:
+            self.scan_over_(Lexer.CHAR_DIGIT_)
+            return (Lexer.NUMBER, int(text[start:self.pos_], 10), location)
+        if cur_char == "-" and next_char in Lexer.CHAR_DIGIT_:
+            self.pos_ += 1
+            self.scan_over_(Lexer.CHAR_DIGIT_)
+            return (Lexer.NUMBER, int(text[start:self.pos_], 10), location)
+        raise VoltLibError("Unexpected character: '%s'" % cur_char,
+                           location)
+
+    def scan_over_(self, valid):
+        p = self.pos_
+        while p < self.text_length_ and self.text_[p] in valid:
+            p += 1
+        self.pos_ = p
+
+    def scan_until_(self, stop_at):
+        p = self.pos_
+        while p < self.text_length_ and self.text_[p] not in stop_at:
+            p += 1
+        self.pos_ = p
diff --git a/Lib/fontTools/voltLib/parser.py b/Lib/fontTools/voltLib/parser.py
new file mode 100644
index 0000000..db3ccf3
--- /dev/null
+++ b/Lib/fontTools/voltLib/parser.py
@@ -0,0 +1,651 @@
+from __future__ import (
+    print_function, division, absolute_import, unicode_literals)
+from collections import OrderedDict
+import fontTools.voltLib.ast as ast
+from fontTools.voltLib.lexer import Lexer
+from fontTools.voltLib.error import VoltLibError
+from io import open
+
+PARSE_FUNCS = {
+    "DEF_GLYPH": "parse_def_glyph_",
+    "DEF_GROUP": "parse_def_group_",
+    "DEF_SCRIPT": "parse_def_script_",
+    "DEF_LOOKUP": "parse_def_lookup_",
+    "DEF_ANCHOR": "parse_def_anchor_",
+    "GRID_PPEM": "parse_ppem_",
+    "PRESENTATION_PPEM": "parse_ppem_",
+    "PPOSITIONING_PPEM": "parse_ppem_",
+    "COMPILER_USEEXTENSIONLOOKUPS": "parse_compiler_flag_",
+    "COMPILER_USEPAIRPOSFORMAT2": "parse_compiler_flag_",
+    "CMAP_FORMAT": "parse_cmap_format",
+}
+
+
+class Parser(object):
+    def __init__(self, path):
+        self.doc_ = ast.VoltFile()
+        self.glyphs_ = OrderedSymbolTable()
+        self.groups_ = SymbolTable()
+        self.anchors_ = {}  # dictionary of SymbolTable() keyed by glyph
+        self.scripts_ = SymbolTable()
+        self.langs_ = SymbolTable()
+        self.lookups_ = SymbolTable()
+        self.next_token_type_, self.next_token_ = (None, None)
+        self.next_token_location_ = None
+        with open(path, "r") as f:
+            self.lexer_ = Lexer(f.read(), path)
+        self.advance_lexer_()
+
+    def parse(self):
+        statements = self.doc_.statements
+        while self.next_token_type_ is not None:
+            self.advance_lexer_()
+            if self.cur_token_ in PARSE_FUNCS.keys():
+                func = getattr(self, PARSE_FUNCS[self.cur_token_])
+                statements.append(func())
+            elif self.is_cur_keyword_("END"):
+                if self.next_token_type_ is not None:
+                    raise VoltLibError("Expected the end of the file",
+                                       self.cur_token_location_)
+                return self.doc_
+            else:
+                raise VoltLibError(
+                    "Expected " + ", ".join(sorted(PARSE_FUNCS.keys())),
+                    self.cur_token_location_)
+        return self.doc_
+
+    def parse_def_glyph_(self):
+        assert self.is_cur_keyword_("DEF_GLYPH")
+        location = self.cur_token_location_
+        name = self.expect_string_()
+        self.expect_keyword_("ID")
+        gid = self.expect_number_()
+        if gid < 0:
+            raise VoltLibError("Invalid glyph ID", self.cur_token_location_)
+        gunicode = None
+        if self.next_token_ == "UNICODE":
+            self.expect_keyword_("UNICODE")
+            gunicode = [self.expect_number_()]
+            if gunicode[0] < 0:
+                raise VoltLibError("Invalid glyph UNICODE",
+                                   self.cur_token_location_)
+        elif self.next_token_ == "UNICODEVALUES":
+            self.expect_keyword_("UNICODEVALUES")
+            gunicode = self.parse_unicode_values_()
+        gtype = None
+        if self.next_token_ == "TYPE":
+            self.expect_keyword_("TYPE")
+            gtype = self.expect_name_()
+            assert gtype in ("BASE", "LIGATURE", "MARK")
+        components = None
+        if self.next_token_ == "COMPONENTS":
+            self.expect_keyword_("COMPONENTS")
+            components = self.expect_number_()
+        self.expect_keyword_("END_GLYPH")
+        if self.glyphs_.resolve(name) is not None:
+            raise VoltLibError(
+                'Glyph "%s" (gid %i) already defined' % (name, gid),
+                location
+            )
+        def_glyph = ast.GlyphDefinition(name, gid,
+                                        gunicode, gtype, components,
+                                        location=location)
+        self.glyphs_.define(name, def_glyph)
+        return def_glyph
+
+    def parse_def_group_(self):
+        assert self.is_cur_keyword_("DEF_GROUP")
+        location = self.cur_token_location_
+        name = self.expect_string_()
+        enum = None
+        if self.next_token_ == "ENUM":
+            self.expect_keyword_("ENUM")
+            enum = self.parse_enum_()
+        self.expect_keyword_("END_GROUP")
+        if self.groups_.resolve(name) is not None:
+            raise VoltLibError(
+                'Glyph group "%s" already defined, '
+                'group names are case insensitive' % name,
+                location
+            )
+        def_group = ast.GroupDefinition(name, enum,
+                                        location=location)
+        self.groups_.define(name, def_group)
+        return def_group
+
+    def parse_def_script_(self):
+        assert self.is_cur_keyword_("DEF_SCRIPT")
+        location = self.cur_token_location_
+        name = None
+        if self.next_token_ == "NAME":
+            self.expect_keyword_("NAME")
+            name = self.expect_string_()
+        self.expect_keyword_("TAG")
+        tag = self.expect_string_()
+        if self.scripts_.resolve(tag) is not None:
+            raise VoltLibError(
+                'Script "%s" already defined, '
+                'script tags are case insensitive' % tag,
+                location
+            )
+        self.langs_.enter_scope()
+        langs = []
+        while self.next_token_ != "END_SCRIPT":
+            self.advance_lexer_()
+            lang = self.parse_langsys_()
+            self.expect_keyword_("END_LANGSYS")
+            if self.langs_.resolve(lang.tag) is not None:
+                raise VoltLibError(
+                    'Language "%s" already defined in script "%s", '
+                    'language tags are case insensitive' % (lang.tag, tag),
+                    location
+                )
+            self.langs_.define(lang.tag, lang)
+            langs.append(lang)
+        self.expect_keyword_("END_SCRIPT")
+        self.langs_.exit_scope()
+        def_script = ast.ScriptDefinition(name, tag, langs, location=location)
+        self.scripts_.define(tag, def_script)
+        return def_script
+
+    def parse_langsys_(self):
+        assert self.is_cur_keyword_("DEF_LANGSYS")
+        location = self.cur_token_location_
+        name = None
+        if self.next_token_ == "NAME":
+            self.expect_keyword_("NAME")
+            name = self.expect_string_()
+        self.expect_keyword_("TAG")
+        tag = self.expect_string_()
+        features = []
+        while self.next_token_ != "END_LANGSYS":
+            self.advance_lexer_()
+            feature = self.parse_feature_()
+            self.expect_keyword_("END_FEATURE")
+            features.append(feature)
+        def_langsys = ast.LangSysDefinition(name, tag, features,
+                                            location=location)
+        return def_langsys
+
+    def parse_feature_(self):
+        assert self.is_cur_keyword_("DEF_FEATURE")
+        location = self.cur_token_location_
+        self.expect_keyword_("NAME")
+        name = self.expect_string_()
+        self.expect_keyword_("TAG")
+        tag = self.expect_string_()
+        lookups = []
+        while self.next_token_ != "END_FEATURE":
+            # self.advance_lexer_()
+            self.expect_keyword_("LOOKUP")
+            lookup = self.expect_string_()
+            lookups.append(lookup)
+        feature = ast.FeatureDefinition(name, tag, lookups,
+                                        location=location)
+        return feature
+
+    def parse_def_lookup_(self):
+        assert self.is_cur_keyword_("DEF_LOOKUP")
+        location = self.cur_token_location_
+        name = self.expect_string_()
+        if not name[0].isalpha():
+            raise VoltLibError(
+                'Lookup name "%s" must start with a letter' % name,
+                location
+            )
+        if self.lookups_.resolve(name) is not None:
+            raise VoltLibError(
+                'Lookup "%s" already defined, '
+                'lookup names are case insensitive' % name,
+                location
+            )
+        process_base = True
+        if self.next_token_ == "PROCESS_BASE":
+            self.advance_lexer_()
+        elif self.next_token_ == "SKIP_BASE":
+            self.advance_lexer_()
+            process_base = False
+        process_marks = True
+        if self.next_token_ == "PROCESS_MARKS":
+            self.advance_lexer_()
+            if self.next_token_ == "MARK_GLYPH_SET":
+                self.advance_lexer_()
+                process_marks = self.expect_string_()
+            elif self.next_token_type_ == Lexer.STRING:
+                process_marks = self.expect_string_()
+            elif self.next_token_ == "ALL":
+                self.advance_lexer_()
+            else:
+                raise VoltLibError(
+                    "Expected ALL, MARK_GLYPH_SET or an ID. "
+                    "Got %s" % (self.next_token_type_),
+                    location)
+        elif self.next_token_ == "SKIP_MARKS":
+            self.advance_lexer_()
+            process_marks = False
+        direction = None
+        if self.next_token_ == "DIRECTION":
+            self.expect_keyword_("DIRECTION")
+            direction = self.expect_name_()
+            assert direction in ("LTR", "RTL")
+        reversal = None
+        if self.next_token_ == "REVERSAL":
+            self.expect_keyword_("REVERSAL")
+            reversal = True
+        comments = None
+        if self.next_token_ == "COMMENTS":
+            self.expect_keyword_("COMMENTS")
+            comments = self.expect_string_()
+        context = []
+        while self.next_token_ in ("EXCEPT_CONTEXT", "IN_CONTEXT"):
+            context = self.parse_context_()
+        as_pos_or_sub = self.expect_name_()
+        sub = None
+        pos = None
+        if as_pos_or_sub == "AS_SUBSTITUTION":
+            sub = self.parse_substitution_(reversal)
+        elif as_pos_or_sub == "AS_POSITION":
+            pos = self.parse_position_()
+        else:
+            raise VoltLibError(
+                "Expected AS_SUBSTITUTION or AS_POSITION. "
+                "Got %s" % (as_pos_or_sub),
+                location)
+        def_lookup = ast.LookupDefinition(
+            name, process_base, process_marks, direction, reversal,
+            comments, context, sub, pos, location=location)
+        self.lookups_.define(name, def_lookup)
+        return def_lookup
+
+    def parse_context_(self):
+        location = self.cur_token_location_
+        contexts = []
+        while self.next_token_ in ("EXCEPT_CONTEXT", "IN_CONTEXT"):
+            side = None
+            coverage = None
+            ex_or_in = self.expect_name_()
+            # side_contexts = [] # XXX
+            if self.next_token_ != "END_CONTEXT":
+                left = []
+                right = []
+                while self.next_token_ in ("LEFT", "RIGHT"):
+                    side = self.expect_name_()
+                    coverage = self.parse_coverage_()
+                    if side == "LEFT":
+                        left.append(coverage)
+                    else:
+                        right.append(coverage)
+                self.expect_keyword_("END_CONTEXT")
+                context = ast.ContextDefinition(ex_or_in, left,
+                                                right, location=location)
+                contexts.append(context)
+            else:
+                self.expect_keyword_("END_CONTEXT")
+        return contexts
+
+    def parse_substitution_(self, reversal):
+        assert self.is_cur_keyword_("AS_SUBSTITUTION")
+        location = self.cur_token_location_
+        src = []
+        dest = []
+        if self.next_token_ != "SUB":
+            raise VoltLibError("Expected SUB", location)
+        while self.next_token_ == "SUB":
+            self.expect_keyword_("SUB")
+            src.append(self.parse_coverage_())
+            self.expect_keyword_("WITH")
+            dest.append(self.parse_coverage_())
+            self.expect_keyword_("END_SUB")
+        self.expect_keyword_("END_SUBSTITUTION")
+        max_src = max([len(cov) for cov in src])
+        max_dest = max([len(cov) for cov in dest])
+        # many to many or mixed is invalid
+        if ((max_src > 1 and max_dest > 1) or
+                (reversal and (max_src > 1 or max_dest > 1))):
+            raise VoltLibError(
+                "Invalid substitution type",
+                location)
+        mapping = OrderedDict(zip(tuple(src), tuple(dest)))
+        if max_src == 1 and max_dest == 1:
+            if reversal:
+                sub = ast.SubstitutionReverseChainingSingleDefinition(
+                    mapping, location=location)
+            else:
+                sub = ast.SubstitutionSingleDefinition(mapping,
+                                                       location=location)
+        elif max_src == 1 and max_dest > 1:
+            sub = ast.SubstitutionMultipleDefinition(mapping,
+                                                     location=location)
+        elif max_src > 1 and max_dest == 1:
+            sub = ast.SubstitutionLigatureDefinition(mapping,
+                                                     location=location)
+        return sub
+
+    def parse_position_(self):
+        assert self.is_cur_keyword_("AS_POSITION")
+        location = self.cur_token_location_
+        pos_type = self.expect_name_()
+        if pos_type not in (
+                "ATTACH", "ATTACH_CURSIVE", "ADJUST_PAIR", "ADJUST_SINGLE"):
+            raise VoltLibError(
+                "Expected ATTACH, ATTACH_CURSIVE, ADJUST_PAIR, ADJUST_SINGLE",
+                location)
+        if pos_type == "ATTACH":
+            position = self.parse_attach_()
+        elif pos_type == "ATTACH_CURSIVE":
+            position = self.parse_attach_cursive_()
+        elif pos_type == "ADJUST_PAIR":
+            position = self.parse_adjust_pair_()
+        elif pos_type == "ADJUST_SINGLE":
+            position = self.parse_adjust_single_()
+        self.expect_keyword_("END_POSITION")
+        return position
+
+    def parse_attach_(self):
+        assert self.is_cur_keyword_("ATTACH")
+        location = self.cur_token_location_
+        coverage = self.parse_coverage_()
+        coverage_to = []
+        self.expect_keyword_("TO")
+        while self.next_token_ != "END_ATTACH":
+            cov = self.parse_coverage_()
+            self.expect_keyword_("AT")
+            self.expect_keyword_("ANCHOR")
+            anchor_name = self.expect_string_()
+            coverage_to.append((cov, anchor_name))
+        self.expect_keyword_("END_ATTACH")
+        position = ast.PositionAttachDefinition(
+            coverage, coverage_to, location=location)
+        return position
+
+    def parse_attach_cursive_(self):
+        assert self.is_cur_keyword_("ATTACH_CURSIVE")
+        location = self.cur_token_location_
+        coverages_exit = []
+        coverages_enter = []
+        while self.next_token_ != "ENTER":
+            self.expect_keyword_("EXIT")
+            coverages_exit.append(self.parse_coverage_())
+        while self.next_token_ != "END_ATTACH":
+            self.expect_keyword_("ENTER")
+            coverages_enter.append(self.parse_coverage_())
+        self.expect_keyword_("END_ATTACH")
+        position = ast.PositionAttachCursiveDefinition(
+            coverages_exit, coverages_enter, location=location)
+        return position
+
+    def parse_adjust_pair_(self):
+        assert self.is_cur_keyword_("ADJUST_PAIR")
+        location = self.cur_token_location_
+        coverages_1 = []
+        coverages_2 = []
+        adjust_pair = {}
+        while self.next_token_ == "FIRST":
+            self.advance_lexer_()
+            coverage_1 = self.parse_coverage_()
+            coverages_1.append(coverage_1)
+        while self.next_token_ == "SECOND":
+            self.advance_lexer_()
+            coverage_2 = self.parse_coverage_()
+            coverages_2.append(coverage_2)
+        while self.next_token_ != "END_ADJUST":
+            id_1 = self.expect_number_()
+            id_2 = self.expect_number_()
+            self.expect_keyword_("BY")
+            pos_1 = self.parse_pos_()
+            pos_2 = self.parse_pos_()
+            adjust_pair[(id_1, id_2)] = (pos_1, pos_2)
+        self.expect_keyword_("END_ADJUST")
+        position = ast.PositionAdjustPairDefinition(
+            coverages_1, coverages_2, adjust_pair, location=location)
+        return position
+
+    def parse_adjust_single_(self):
+        assert self.is_cur_keyword_("ADJUST_SINGLE")
+        location = self.cur_token_location_
+        adjust_single = []
+        while self.next_token_ != "END_ADJUST":
+            coverages = self.parse_coverage_()
+            self.expect_keyword_("BY")
+            pos = self.parse_pos_()
+            adjust_single.append((coverages, pos))
+        self.expect_keyword_("END_ADJUST")
+        position = ast.PositionAdjustSingleDefinition(
+            adjust_single, location=location)
+        return position
+
+    def parse_def_anchor_(self):
+        assert self.is_cur_keyword_("DEF_ANCHOR")
+        location = self.cur_token_location_
+        name = self.expect_string_()
+        self.expect_keyword_("ON")
+        gid = self.expect_number_()
+        self.expect_keyword_("GLYPH")
+        glyph_name = self.expect_name_()
+        # check for duplicate anchor names on this glyph
+        if (glyph_name in self.anchors_
+                and self.anchors_[glyph_name].resolve(name) is not None):
+            raise VoltLibError(
+                'Anchor "%s" already defined, '
+                'anchor names are case insensitive' % name,
+                location
+            )
+        self.expect_keyword_("COMPONENT")
+        component = self.expect_number_()
+        if self.next_token_ == "LOCKED":
+            locked = True
+            self.advance_lexer_()
+        else:
+            locked = False
+        self.expect_keyword_("AT")
+        pos = self.parse_pos_()
+        self.expect_keyword_("END_ANCHOR")
+        anchor = ast.AnchorDefinition(name, gid, glyph_name,
+                                      component, locked, pos,
+                                      location=location)
+        if glyph_name not in self.anchors_:
+            self.anchors_[glyph_name] = SymbolTable()
+        self.anchors_[glyph_name].define(name, anchor)
+        return anchor
+
+    def parse_adjust_by_(self):
+        self.advance_lexer_()
+        assert self.is_cur_keyword_("ADJUST_BY")
+        adjustment = self.expect_number_()
+        self.expect_keyword_("AT")
+        size = self.expect_number_()
+        return adjustment, size
+
+    def parse_pos_(self):
+        # VOLT syntax doesn't seem to take device Y advance
+        self.advance_lexer_()
+        location = self.cur_token_location_
+        assert self.is_cur_keyword_("POS"), location
+        adv = None
+        dx = None
+        dy = None
+        adv_adjust_by = {}
+        dx_adjust_by = {}
+        dy_adjust_by = {}
+        if self.next_token_ == "ADV":
+            self.advance_lexer_()
+            adv = self.expect_number_()
+            while self.next_token_ == "ADJUST_BY":
+                adjustment, size = self.parse_adjust_by_()
+                adv_adjust_by[size] = adjustment
+        if self.next_token_ == "DX":
+            self.advance_lexer_()
+            dx = self.expect_number_()
+            while self.next_token_ == "ADJUST_BY":
+                adjustment, size = self.parse_adjust_by_()
+                dx_adjust_by[size] = adjustment
+        if self.next_token_ == "DY":
+            self.advance_lexer_()
+            dy = self.expect_number_()
+            while self.next_token_ == "ADJUST_BY":
+                adjustment, size = self.parse_adjust_by_()
+                dy_adjust_by[size] = adjustment
+        self.expect_keyword_("END_POS")
+        return (adv, dx, dy, adv_adjust_by, dx_adjust_by, dy_adjust_by)
+
+    def parse_unicode_values_(self):
+        location = self.cur_token_location_
+        try:
+            unicode_values = self.expect_string_().split(",")
+            unicode_values = [
+                int(uni[2:], 16)
+                for uni in unicode_values if uni != ""]
+        except ValueError as err:
+            raise VoltLibError(str(err), location)
+        return unicode_values if unicode_values != [] else None
+
+    def parse_enum_(self):
+        assert self.is_cur_keyword_("ENUM")
+        enum = self.parse_coverage_()
+        self.expect_keyword_("END_ENUM")
+        return enum
+
+    def parse_coverage_(self):
+        coverage = []
+        location = self.cur_token_location_
+        while self.next_token_ in ("GLYPH", "GROUP", "RANGE", "ENUM"):
+            if self.next_token_ == "ENUM":
+                self.advance_lexer_()
+                enum = self.parse_enum_()
+                coverage.append(enum)
+            elif self.next_token_ == "GLYPH":
+                self.expect_keyword_("GLYPH")
+                name = self.expect_string_()
+                coverage.append(name)
+            elif self.next_token_ == "GROUP":
+                self.expect_keyword_("GROUP")
+                name = self.expect_string_()
+                # resolved_group = self.groups_.resolve(name)
+                group = (name,)
+                coverage.append(group)
+                # if resolved_group is not None:
+                #     coverage.extend(resolved_group.enum)
+                # # TODO: check that group exists after all groups are defined
+                # else:
+                #     group = (name,)
+                #     coverage.append(group)
+                #     # raise VoltLibError(
+                #     #     'Glyph group "%s" is not defined' % name,
+                #     #     location)
+            elif self.next_token_ == "RANGE":
+                self.expect_keyword_("RANGE")
+                start = self.expect_string_()
+                self.expect_keyword_("TO")
+                end = self.expect_string_()
+                coverage.append((start, end))
+        return tuple(coverage)
+
+    def resolve_group(self, group_name):
+        return self.groups_.resolve(group_name)
+
+    def glyph_range(self, start, end):
+        rng = self.glyphs_.range(start, end)
+        return frozenset(rng)
+
+    def parse_ppem_(self):
+        location = self.cur_token_location_
+        ppem_name = self.cur_token_
+        value = self.expect_number_()
+        setting = ast.SettingDefinition(ppem_name, value, location=location)
+        return setting
+
+    def parse_compiler_flag_(self):
+        location = self.cur_token_location_
+        flag_name = self.cur_token_
+        value = True
+        setting = ast.SettingDefinition(flag_name, value, location=location)
+        return setting
+
+    def parse_cmap_format(self):
+        location = self.cur_token_location_
+        name = self.cur_token_
+        value = (self.expect_number_(), self.expect_number_(),
+                 self.expect_number_())
+        setting = ast.SettingDefinition(name, value, location=location)
+        return setting
+
+    def is_cur_keyword_(self, k):
+        return (self.cur_token_type_ is Lexer.NAME) and (self.cur_token_ == k)
+
+    def expect_string_(self):
+        self.advance_lexer_()
+        if self.cur_token_type_ is not Lexer.STRING:
+            raise VoltLibError("Expected a string", self.cur_token_location_)
+        return self.cur_token_
+
+    def expect_keyword_(self, keyword):
+        self.advance_lexer_()
+        if self.cur_token_type_ is Lexer.NAME and self.cur_token_ == keyword:
+            return self.cur_token_
+        raise VoltLibError("Expected \"%s\"" % keyword,
+                           self.cur_token_location_)
+
+    def expect_name_(self):
+        self.advance_lexer_()
+        if self.cur_token_type_ is Lexer.NAME:
+            return self.cur_token_
+        raise VoltLibError("Expected a name", self.cur_token_location_)
+
+    def expect_number_(self):
+        self.advance_lexer_()
+        if self.cur_token_type_ is not Lexer.NUMBER:
+            raise VoltLibError("Expected a number", self.cur_token_location_)
+        return self.cur_token_
+
+    def advance_lexer_(self):
+        self.cur_token_type_, self.cur_token_, self.cur_token_location_ = (
+            self.next_token_type_, self.next_token_, self.next_token_location_)
+        try:
+            (self.next_token_type_, self.next_token_,
+             self.next_token_location_) = self.lexer_.next()
+        except StopIteration:
+            self.next_token_type_, self.next_token_ = (None, None)
+
+
+class SymbolTable(object):
+    def __init__(self):
+        self.scopes_ = [{}]
+
+    def enter_scope(self):
+        self.scopes_.append({})
+
+    def exit_scope(self):
+        self.scopes_.pop()
+
+    def define(self, name, item):
+        self.scopes_[-1][name] = item
+
+    def resolve(self, name, case_insensitive=True):
+        for scope in reversed(self.scopes_):
+            item = scope.get(name)
+            if item:
+                return item
+        if case_insensitive:
+            for key in scope:
+                if key.lower() == name.lower():
+                    return scope[key]
+        return None
+
+
+class OrderedSymbolTable(SymbolTable):
+    def __init__(self):
+        self.scopes_ = [OrderedDict()]
+
+    def enter_scope(self):
+        self.scopes_.append(OrderedDict())
+
+    def resolve(self, name, case_insensitive=False):
+        SymbolTable.resolve(self, name, case_insensitive=case_insensitive)
+
+    def range(self, start, end):
+        for scope in reversed(self.scopes_):
+            if start in scope and end in scope:
+                start_idx = list(scope.keys()).index(start)
+                end_idx = list(scope.keys()).index(end)
+                return list(scope.keys())[start_idx:end_idx + 1]
+        return None
diff --git a/Lib/fonttools.egg-info/PKG-INFO b/Lib/fonttools.egg-info/PKG-INFO
new file mode 100644
index 0000000..c92fa09
--- /dev/null
+++ b/Lib/fonttools.egg-info/PKG-INFO
@@ -0,0 +1,1375 @@
+Metadata-Version: 1.2
+Name: fonttools
+Version: 3.28.0
+Summary: Tools to manipulate font files
+Home-page: http://github.com/fonttools/fonttools
+Author: Just van Rossum
+Author-email: just@letterror.com
+Maintainer: Behdad Esfahbod
+Maintainer-email: behdad@behdad.org
+License: MIT
+Description: |Travis Build Status| |Appveyor Build status| |Health| |Coverage Status|
+        |PyPI| |Gitter Chat|
+        
+        What is this?
+        ~~~~~~~~~~~~~
+        
+        | fontTools is a library for manipulating fonts, written in Python. The
+          project includes the TTX tool, that can convert TrueType and OpenType
+          fonts to and from an XML text format, which is also called TTX. It
+          supports TrueType, OpenType, AFM and to an extent Type 1 and some
+          Mac-specific formats. The project has a `MIT open-source
+          licence <LICENSE>`__.
+        | Among other things this means you can use it free of charge.
+        
+        Installation
+        ~~~~~~~~~~~~
+        
+        FontTools requires `Python <http://www.python.org/download/>`__ 2.7, 3.4
+        or later.
+        
+        The package is listed in the Python Package Index (PyPI), so you can
+        install it with `pip <https://pip.pypa.io>`__:
+        
+        .. code:: sh
+        
+            pip install fonttools
+        
+        If you would like to contribute to its development, you can clone the
+        repository from Github, install the package in 'editable' mode and
+        modify the source code in place. We recommend creating a virtual
+        environment, using `virtualenv <https://virtualenv.pypa.io>`__ or
+        Python 3 `venv <https://docs.python.org/3/library/venv.html>`__ module.
+        
+        .. code:: sh
+        
+            # download the source code to 'fonttools' folder
+            git clone https://github.com/fonttools/fonttools.git
+            cd fonttools
+        
+            # create new virtual environment called e.g. 'fonttools-venv', or anything you like
+            python -m virtualenv fonttools-venv
+        
+            # source the `activate` shell script to enter the environment (Un\*x); to exit, just type `deactivate`
+            . fonttools-venv/bin/activate
+        
+            # to activate the virtual environment in Windows `cmd.exe`, do
+            fonttools-venv\Scripts\activate.bat
+        
+            # install in 'editable' mode
+            pip install -e .
+        
+        TTX – From OpenType and TrueType to XML and Back
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+        
+        Once installed you can use the ``ttx`` command to convert binary font
+        files (``.otf``, ``.ttf``, etc) to the TTX xml format, edit them, and
+        convert them back to binary format. TTX files have a .ttx file
+        extension.
+        
+        .. code:: sh
+        
+            ttx /path/to/font.otf
+            ttx /path/to/font.ttx
+        
+        The TTX application works can be used in two ways, depending on what
+        platform you run it on:
+        
+        -  As a command line tool (Windows/DOS, Unix, MacOSX)
+        -  By dropping files onto the application (Windows, MacOS)
+        
+        TTX detects what kind of files it is fed: it will output a ``.ttx`` file
+        when it sees a ``.ttf`` or ``.otf``, and it will compile a ``.ttf`` or
+        ``.otf`` when the input file is a ``.ttx`` file. By default, the output
+        file is created in the same folder as the input file, and will have the
+        same name as the input file but with a different extension. TTX will
+        *never* overwrite existing files, but if necessary will append a unique
+        number to the output filename (before the extension) such as
+        ``Arial#1.ttf``
+        
+        When using TTX from the command line there are a bunch of extra options,
+        these are explained in the help text, as displayed when typing
+        ``ttx -h`` at the command prompt. These additional options include:
+        
+        -  specifying the folder where the output files are created
+        -  specifying which tables to dump or which tables to exclude
+        -  merging partial ``.ttx`` files with existing ``.ttf`` or ``.otf``
+           files
+        -  listing brief table info instead of dumping to ``.ttx``
+        -  splitting tables to separate ``.ttx`` files
+        -  disabling TrueType instruction disassembly
+        
+        The TTX file format
+        -------------------
+        
+        The following tables are currently supported:
+        
+        .. begin table list
+        .. code::
+        
+            BASE, CBDT, CBLC, CFF, CFF2, COLR, CPAL, DSIG, EBDT, EBLC, FFTM,
+            Feat, GDEF, GMAP, GPKG, GPOS, GSUB, Glat, Gloc, HVAR, JSTF, LTSH,
+            MATH, META, MVAR, OS/2, SING, STAT, SVG, Silf, Sill, TSI0, TSI1,
+            TSI2, TSI3, TSI5, TSIB, TSID, TSIJ, TSIP, TSIS, TSIV, TTFA, VDMX,
+            VORG, VVAR, ankr, avar, bsln, cidg, cmap, cvar, cvt, feat, fpgm,
+            fvar, gasp, gcid, glyf, gvar, hdmx, head, hhea, hmtx, kern, lcar,
+            loca, ltag, maxp, meta, mort, morx, name, opbd, post, prep, prop,
+            sbix, trak, vhea and vmtx
+        .. end table list
+        
+        Other tables are dumped as hexadecimal data.
+        
+        TrueType fonts use glyph indices (GlyphIDs) to refer to glyphs in most
+        places. While this is fine in binary form, it is really hard to work
+        with for humans. Therefore we use names instead.
+        
+        The glyph names are either extracted from the ``CFF`` table or the
+        ``post`` table, or are derived from a Unicode ``cmap`` table. In the
+        latter case the Adobe Glyph List is used to calculate names based on
+        Unicode values. If all of these methods fail, names are invented based
+        on GlyphID (eg ``glyph00142``)
+        
+        It is possible that different glyphs use the same name. If this happens,
+        we force the names to be unique by appending ``#n`` to the name (``n``
+        being an integer number.) The original names are being kept, so this has
+        no influence on a "round tripped" font.
+        
+        Because the order in which glyphs are stored inside the binary font is
+        important, we maintain an ordered list of glyph names in the font.
+        
+        Other Tools
+        ~~~~~~~~~~~
+        
+        Commands for inspecting, merging and subsetting fonts are also
+        available:
+        
+        .. code:: sh
+        
+            pyftinspect
+            pyftmerge
+            pyftsubset
+        
+        fontTools Python Module
+        ~~~~~~~~~~~~~~~~~~~~~~~
+        
+        The fontTools python module provides a convenient way to
+        programmatically edit font files.
+        
+        .. code:: py
+        
+            >>> from fontTools.ttLib import TTFont
+            >>> font = TTFont('/path/to/font.ttf')
+            >>> font
+            <fontTools.ttLib.TTFont object at 0x10c34ed50>
+            >>>
+        
+        A selection of sample python programs is in the
+        `Snippets <https://github.com/fonttools/fonttools/blob/master/Snippets/>`__
+        directory.
+        
+        Optional Requirements
+        ---------------------
+        
+        The ``fontTools`` package currently has no (required) external dependencies
+        besides the modules included in the Python Standard Library.
+        However, a few extra dependencies are required by some of its modules, which
+        are needed to unlock optional features.
+        
+        -  ``Lib/fontTools/ttLib/woff2.py``
+        
+           Module to compress/decompress WOFF 2.0 web fonts; it requires:
+        
+           -  `brotli <https://pypi.python.org/pypi/Brotli>`__: Python bindings of
+              the Brotli compression library.
+        
+        -  ``Lib/fontTools/ttLib/sfnt.py``
+        
+           To better compress WOFF 1.0 web fonts, the following module can be used
+           instead of the built-in ``zlib`` library:
+        
+           -  `zopfli <https://pypi.python.org/pypi/zopfli>`__: Python bindings of
+              the Zopfli compression library.
+        
+        -  ``Lib/fontTools/unicode.py``
+        
+           To display the Unicode character names when dumping the ``cmap`` table
+           with ``ttx`` we use the ``unicodedata`` module in the Standard Library.
+           The version included in there varies between different Python versions.
+           To use the latest available data, you can install:
+        
+           -  `unicodedata2 <https://pypi.python.org/pypi/unicodedata2>`__:
+              ``unicodedata`` backport for Python 2.7 and 3.5 updated to the latest
+              Unicode version 9.0. Note this is not necessary if you use Python 3.6
+              as the latter already comes with an up-to-date ``unicodedata``.
+        
+        -  ``Lib/fontTools/varLib/interpolatable.py``
+        
+           Module for finding wrong contour/component order between different masters.
+           It requires one of the following packages in order to solve the so-called
+           "minimum weight perfect matching problem in bipartite graphs", or
+           the Assignment problem:
+        
+           *  `scipy <https://pypi.python.org/pypi/scipy>`__: the Scientific Library
+              for Python, which internally uses `NumPy <https://pypi.python.org/pypi/numpy>`__
+              arrays and hence is very fast;
+           *  `munkres <https://pypi.python.org/pypi/munkres>`__: a pure-Python
+              module that implements the Hungarian or Kuhn-Munkres algorithm.
+        
+        -  ``Lib/fontTools/misc/symfont.py``
+        
+           Advanced module for symbolic font statistics analysis; it requires:
+        
+           *  `sympy <https://pypi.python.org/pypi/sympy>`__: the Python library for
+              symbolic mathematics.
+        
+        -  ``Lib/fontTools/t1Lib.py``
+        
+           To get the file creator and type of Macintosh PostScript Type 1 fonts
+           on Python 3 you need to install the following module, as the old ``MacOS``
+           module is no longer included in Mac Python:
+        
+           *  `xattr <https://pypi.python.org/pypi/xattr>`__: Python wrapper for
+              extended filesystem attributes (macOS platform only).
+        
+        -  ``Lib/fontTools/pens/cocoaPen.py``
+        
+           Pen for drawing glyphs with Cocoa ``NSBezierPath``, requires:
+        
+           *  `PyObjC <https://pypi.python.org/pypi/pyobjc>`__: the bridge between
+              Python and the Objective-C runtime (macOS platform only).
+        
+        -  ``Lib/fontTools/pens/qtPen.py``
+        
+           Pen for drawing glyphs with Qt's ``QPainterPath``, requires:
+        
+           *  `PyQt5 <https://pypi.python.org/pypi/PyQt5>`__: Python bindings for
+              the Qt cross platform UI and application toolkit.
+        
+        -  ``Lib/fontTools/pens/reportLabPen.py``
+        
+           Pen to drawing glyphs as PNG images, requires:
+        
+           *  `reportlab <https://pypi.python.org/pypi/reportlab>`__: Python toolkit
+              for generating PDFs and graphics.
+        
+        -  ``Lib/fontTools/inspect.py``
+        
+           A GUI font inspector, requires one of the following packages:
+        
+           *  `PyGTK <https://pypi.python.org/pypi/PyGTK>`__: Python bindings for
+              GTK  2.x (only works with Python 2).
+           *  `PyGObject <https://wiki.gnome.org/action/show/Projects/PyGObject>`__ :
+              Python bindings for GTK 3.x and gobject-introspection libraries (also
+              compatible with Python 3).
+        
+        Testing
+        ~~~~~~~
+        
+        To run the test suite, you can do:
+        
+        .. code:: sh
+        
+            python setup.py test
+        
+        If you have `pytest <http://docs.pytest.org/en/latest/>`__, you can run
+        the ``pytest`` command directly. The tests will run against the
+        installed ``fontTools`` package, or the first one found in the
+        ``PYTHONPATH``.
+        
+        You can also use `tox <https://testrun.org/tox/latest/>`__ to
+        automatically run tests on different Python versions in isolated virtual
+        environments.
+        
+        .. code:: sh
+        
+            pip install tox
+            tox
+        
+        Note that when you run ``tox`` without arguments, the tests are executed
+        for all the environments listed in tox.ini's ``envlist``. In our case,
+        this includes Python 2.7 and 3.6, so for this to work the ``python2.7``
+        and ``python3.6`` executables must be available in your ``PATH``.
+        
+        You can specify an alternative environment list via the ``-e`` option,
+        or the ``TOXENV`` environment variable:
+        
+        .. code:: sh
+        
+            tox -e py27-nocov
+            TOXENV="py36-cov,htmlcov" tox
+        
+        Development Community
+        ~~~~~~~~~~~~~~~~~~~~~
+        
+        TTX/FontTools development is ongoing in an active community of
+        developers, that includes professional developers employed at major
+        software corporations and type foundries as well as hobbyists.
+        
+        Feature requests and bug reports are always welcome at
+        https://github.com/fonttools/fonttools/issues/
+        
+        The best place for discussions about TTX from an end-user perspective as
+        well as TTX/FontTools development is the
+        https://groups.google.com/d/forum/fonttools mailing list. There is also
+        a development https://groups.google.com/d/forum/fonttools-dev mailing
+        list for continuous integration notifications. You can also email Behdad
+        privately at behdad@behdad.org
+        
+        History
+        ~~~~~~~
+        
+        The fontTools project was started by Just van Rossum in 1999, and was
+        maintained as an open source project at
+        http://sourceforge.net/projects/fonttools/. In 2008, Paul Wise (pabs3)
+        began helping Just with stability maintenance. In 2013 Behdad Esfahbod
+        began a friendly fork, thoroughly reviewing the codebase and making
+        changes at https://github.com/behdad/fonttools to add new features and
+        support for new font formats.
+        
+        Acknowledgements
+        ~~~~~~~~~~~~~~~~
+        
+        In alphabetical order:
+        
+        Olivier Berten, Samyak Bhuta, Erik van Blokland, Petr van Blokland,
+        Jelle Bosma, Sascha Brawer, Tom Byrer, Frédéric Coiffier, Vincent
+        Connare, Dave Crossland, Simon Daniels, Behdad Esfahbod, Behnam
+        Esfahbod, Hannes Famira, Sam Fishman, Matt Fontaine, Yannis Haralambous,
+        Greg Hitchcock, Jeremie Hornus, Khaled Hosny, John Hudson, Denis Moyogo
+        Jacquerye, Jack Jansen, Tom Kacvinsky, Jens Kutilek, Antoine Leca,
+        Werner Lemberg, Tal Leming, Peter Lofting, Cosimo Lupo, Masaya Nakamura,
+        Dave Opstad, Laurence Penney, Roozbeh Pournader, Garret Rieger, Read
+        Roberts, Guido van Rossum, Just van Rossum, Andreas Seidel, Georg
+        Seifert, Miguel Sousa, Adam Twardoch, Adrien Tétar, Vitaly Volkov, Paul
+        Wise.
+        
+        Copyrights
+        ~~~~~~~~~~
+        
+        | Copyright (c) 1999-2004 Just van Rossum, LettError
+          (just@letterror.com)
+        | See `LICENSE <LICENSE>`__ for the full license.
+        
+        Copyright (c) 2000 BeOpen.com. All Rights Reserved.
+        
+        Copyright (c) 1995-2001 Corporation for National Research Initiatives.
+        All Rights Reserved.
+        
+        Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam. All
+        Rights Reserved.
+        
+        Have fun!
+        
+        .. |Travis Build Status| image:: https://travis-ci.org/fonttools/fonttools.svg
+           :target: https://travis-ci.org/fonttools/fonttools
+        .. |Appveyor Build status| image:: https://ci.appveyor.com/api/projects/status/0f7fmee9as744sl7/branch/master?svg=true
+           :target: https://ci.appveyor.com/project/fonttools/fonttools/branch/master
+        .. |Health| image:: https://landscape.io/github/behdad/fonttools/master/landscape.svg?style=flat
+           :target: https://landscape.io/github/behdad/fonttools/master
+        .. |Coverage Status| image:: https://codecov.io/gh/fonttools/fonttools/branch/master/graph/badge.svg
+           :target: https://codecov.io/gh/fonttools/fonttools
+        .. |PyPI| image:: https://img.shields.io/pypi/v/fonttools.svg
+           :target: https://pypi.org/project/FontTools
+        .. |Gitter Chat| image:: https://badges.gitter.im/fonttools-dev/Lobby.svg
+           :alt: Join the chat at https://gitter.im/fonttools-dev/Lobby
+           :target: https://gitter.im/fonttools-dev/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
+        
+        Changelog
+        ~~~~~~~~~
+        
+        3.28.0 (released 2018-06-19)
+        ----------------------------
+        
+        - [featureVars] Added experimental module to build ``FeatureVariations``
+          tables. Still needs to be hooked up to ``varLib.build`` (#1240).
+        - [fixedTools] Added ``otRound`` to round floats to nearest integer towards
+          positive Infinity. This is now used where we deal with visual data like X/Y
+          coordinates, advance widths/heights, variation deltas, and similar (#1274,
+          #1248).
+        - [subset] Improved GSUB closure memoize algorithm.
+        - [varLib.models] Fixed regression in model resolution (180124, #1269).
+        - [feaLib.ast] Fixed error when converting ``SubtableStatement`` to string
+          (#1275).
+        - [varLib.mutator] Set ``OS/2.usWeightClass`` and ``usWidthClass``, and
+          ``post.italicAngle`` based on the 'wght', 'wdth' and 'slnt' axis values
+          (#1276, #1264).
+        - [py23/loggingTools] Don't automatically set ``logging.lastResort`` handler
+          on py27. Moved ``LastResortLogger`` to the ``loggingTools`` module (#1277).
+        
+        3.27.1 (released 2018-06-11)
+        ----------------------------
+        
+        - [ttGlyphPen] Issue a warning and skip building non-existing components
+          (https://github.com/googlei18n/fontmake/issues/411).
+        - [tests] Fixed issue running ttx_test.py from a tagged commit.
+        
+        3.27.0 (released 2018-06-11)
+        ----------------------------
+        
+        - [designspaceLib] Added new ``conditionSet`` element to ``rule`` element in
+          designspace document. Bumped ``format`` attribute to ``4.0`` (previously,
+          it was formatted as an integer). Removed ``checkDefault``, ``checkAxes``
+          methods, and any kind of guessing about the axes when the ``<axes>`` element
+          is missing. The default master is expected at the intersection of all default
+          values for each axis (#1254, #1255, #1267).
+        - [cffLib] Fixed issues when compiling CFF2 or converting from CFF when the
+          font has an FDArray (#1211, #1271).
+        - [varLib] Avoid attempting to build ``cvar`` table when ``glyf`` table is not
+          present, as is the case for CFF2 fonts.
+        - [subset] Handle None coverages in MarkGlyphSets; revert commit 02616ab that
+          sets empty Coverage tables in MarkGlyphSets to None, to make OTS happy.
+        - [ttFont] Allow to build glyph order from ``maxp.numGlyphs`` when ``post`` or
+          ``cmap`` are missing.
+        - [ttFont] Added ``__len__`` method to ``_TTGlyphSet``.
+        - [glyf] Ensure ``GlyphCoordinates`` never overflow signed shorts (#1230).
+        - [py23] Added alias for ``itertools.izip`` shadowing the built-in ``zip``.
+        - [loggingTools] Memoize ``log`` property of ``LogMixin`` class (fbab12).
+        - [ttx] Impoved test coverage (#1261).
+        - [Snippets] Addded script to append a suffix to all family names in a font.
+        - [varLib.plot] Make it work with matplotlib >= 2.1 (b38e2b).
+        
+        3.26.0 (released 2018-05-03)
+        ----------------------------
+        
+        - [designspace] Added a new optional ``layer`` attribute to the source element,
+          and a corresponding ``layerName`` attribute to the ``SourceDescriptor``
+          object (#1253).
+          Added ``conditionset`` element to the ``rule`` element to the spec, but not
+          implemented in designspace reader/writer yet (#1254).
+        - [varLib.models] Refine modeling one last time (0ecf5c5).
+        - [otBase] Fixed sharing of tables referred to by different offset sizes
+          (795f2f9).
+        - [subset] Don't drop a GDEF that only has VarStore (fc819d6). Set to None
+          empty Coverage tables in MarkGlyphSets (02616ab).
+        - [varLib]: Added ``--master-finder`` command-line option (#1249).
+        - [varLib.mutator] Prune fvar nameIDs from instance's name table (#1245).
+        - [otTables] Allow decompiling bad ClassDef tables with invalid format, with
+          warning (#1236).
+        - [varLib] Make STAT v1.2 and reuse nameIDs from fvar table (#1242).
+        - [varLib.plot] Show master locations. Set axis limits to -1, +1.
+        - [subset] Handle HVAR direct mapping. Passthrough 'cvar'.
+          Added ``--font-number`` command-line option for collections.
+        - [t1Lib] Allow a text encoding to be specified when parsing a Type 1 font
+          (#1234). Added ``kind`` argument to T1Font constructor (c5c161c).
+        - [ttLib] Added context manager API to ``TTFont`` class, so it can be used in
+          ``with`` statements to auto-close the file when exiting the context (#1232).
+        
+        3.25.0 (released 2018-04-03)
+        ----------------------------
+        
+        - [varLib] Improved support-resolution algorithm. Previously, the on-axis
+          masters would always cut the space. They don't anymore. That's more
+          consistent, and fixes the main issue Erik showed at TYPO Labs 2017.
+          Any varfont built that had an unusual master configuration will change
+          when rebuilt (42bef17, a523a697,
+          https://github.com/googlei18n/fontmake/issues/264).
+        - [varLib.models] Added a ``main()`` entry point, that takes positions and
+          prints model results.
+        - [varLib.plot] Added new module to plot a designspace's
+          VariationModel. Requires ``matplotlib``.
+        - [varLib.mutator] Added -o option to specify output file path (2ef60fa).
+        - [otTables] Fixed IndexError while pruning of HVAR pre-write (6b6c34a).
+        - [varLib.models] Convert delta array to floats if values overflows signed
+          short integer (0055f94).
+        
+        3.24.2 (released 2018-03-26)
+        ----------------------------
+        
+        - [otBase] Don't fail during ``ValueRecord`` copy if src has more items.
+          We drop hinting in the subsetter by simply changing ValueFormat, without
+          cleaning up the actual ValueRecords. This was causing assertion error if
+          a variable font was subsetted without hinting and then passed directly to
+          the mutator for instantiation without first it saving to disk.
+        
+        3.24.1 (released 2018-03-06)
+        ----------------------------
+        
+        - [varLib] Don't remap the same ``DeviceTable`` twice in VarStore optimizer
+          (#1206).
+        - [varLib] Add ``--disable-iup`` option to ``fonttools varLib`` script,
+          and a ``optimize=True`` keyword argument to ``varLib.build`` function,
+          to optionally disable IUP optimization while building varfonts.
+        - [ttCollection] Fixed issue while decompiling ttc with python3 (#1207).
+        
+        3.24.0 (released 2018-03-01)
+        ----------------------------
+        
+        - [ttGlyphPen] Decompose composite glyphs if any components' transform is too
+          large to fit a ``F2Dot14`` value, or clamp transform values that are
+          (almost) equal to +2.0 to make them fit and avoid decomposing (#1200,
+          #1204, #1205).
+        - [ttx] Added new ``-g`` option to dump glyphs from the ``glyf`` table
+          splitted as individual ttx files (#153, #1035, #1132, #1202).
+        - Copied ``ufoLib.filenames`` module to ``fontTools.misc.filenames``, used
+          for the ttx split-glyphs option (#1202).
+        - [feaLib] Added support for ``cvParameters`` blocks in Character Variant
+          feautures ``cv01-cv99`` (#860, #1169).
+        - [Snippets] Added ``checksum.py`` script to generate/check SHA1 hash of
+          ttx files (#1197).
+        - [varLib.mutator] Fixed issue while instantiating some variable fonts
+          whereby the horizontal advance width computed from ``gvar`` phantom points
+          could turn up to be negative (#1198).
+        - [varLib/subset] Fixed issue with subsetting GPOS variation data not
+          picking up ``ValueRecord`` ``Device`` objects (54fd71f).
+        - [feaLib/voltLib] In all AST elements, the ``location`` is no longer a
+          required positional argument, but an optional kewyord argument (defaults
+          to ``None``). This will make it easier to construct feature AST from
+          code (#1201).
+        
+        
+        3.23.0 (released 2018-02-26)
+        ----------------------------
+        
+        - [designspaceLib] Added an optional ``lib`` element to the designspace as a
+          whole, as well as to the instance elements, to store arbitrary data in a
+          property list dictionary, similar to the UFO's ``lib``. Added an optional
+          ``font`` attribute to the ``SourceDescriptor``, to allow operating on
+          in-memory font objects (#1175).
+        - [cffLib] Fixed issue with lazy-loading of attributes when attempting to
+          set the CFF TopDict.Encoding (#1177, #1187).
+        - [ttx] Fixed regression introduced in 3.22.0 that affected the split tables
+          ``-s`` option (#1188).
+        - [feaLib] Added ``IncludedFeaNotFound`` custom exception subclass, raised
+          when an included feature file cannot be found (#1186).
+        - [otTables] Changed ``VarIdxMap`` to use glyph names internally instead of
+          glyph indexes. The old ttx dumps of HVAR/VVAR tables that contain indexes
+          can still be imported (21cbab8, 38a0ffb).
+        - [varLib] Implemented VarStore optimizer (#1184).
+        - [subset] Implemented pruning of GDEF VarStore, HVAR and MVAR (#1179).
+        - [sfnt] Restore backward compatiblity with ``numFonts`` attribute of
+          ``SFNTReader`` object (#1181).
+        - [merge] Initial support for merging ``LangSysRecords`` (#1180).
+        - [ttCollection] don't seek(0) when writing to possibly unseekable strems.
+        - [subset] Keep all ``--name-IDs`` from 0 to 6 by default (#1170, #605, #114).
+        - [cffLib] Added ``width`` module to calculate optimal CFF default and
+          nominal glyph widths.
+        - [varLib] Don’t fail if STAT already in the master fonts (#1166).
+        
+        3.22.0 (released 2018-02-04)
+        ----------------------------
+        
+        - [subset] Support subsetting ``endchar`` acting as ``seac``-like components
+          in ``CFF`` (fixes #1162).
+        - [feaLib] Allow to build from pre-parsed ``ast.FeatureFile`` object.
+          Added ``tables`` argument to only build some tables instead of all (#1159,
+          #1163).
+        - [textTools] Replaced ``safeEval`` with ``ast.literal_eval`` (#1139).
+        - [feaLib] Added option to the parser to not resolve ``include`` statements
+          (#1154).
+        - [ttLib] Added new ``ttCollection`` module to read/write TrueType and
+          OpenType Collections. Exports a ``TTCollection`` class with a ``fonts``
+          attribute containing a list of ``TTFont`` instances, the methods ``save``
+          and ``saveXML``, plus some list-like methods. The ``importXML`` method is
+          not implemented yet (#17).
+        - [unicodeadata] Added ``ot_tag_to_script`` function that converts from
+          OpenType script tag to Unicode script code.
+        - Added new ``designspaceLib`` subpackage, originally from Erik Van Blokland's
+          ``designSpaceDocument``: https://github.com/LettError/designSpaceDocument
+          NOTE: this is not yet used internally by varLib, and the API may be subject
+          to changes (#911, #1110, LettError/designSpaceDocument#28).
+        - Added new FontTools icon images (8ee7c32).
+        - [unicodedata] Added ``script_horizontal_direction`` function that returns
+          either "LTR" or "RTL" given a unicode script code.
+        - [otConverters] Don't write descriptive name string as XML comment if the
+          NameID value is 0 (== NULL) (#1151, #1152).
+        - [unicodedata] Add ``ot_tags_from_script`` function to get the list of
+          OpenType script tags associated with unicode script code (#1150).
+        - [feaLib] Don't error when "enumerated" kern pairs conflict with preceding
+          single pairs; emit warning and chose the first value (#1147, #1148).
+        - [loggingTools] In ``CapturingLogHandler.assertRegex`` method, match the
+          fully formatted log message.
+        - [sbix] Fixed TypeError when concatenating str and bytes (#1154).
+        - [bezierTools] Implemented cusp support and removed ``approximate_fallback``
+          arg in ``calcQuadraticArcLength``. Added ``calcCubicArcLength`` (#1142).
+        
+        3.21.2 (released 2018-01-08)
+        ----------------------------
+        
+        - [varLib] Fixed merging PairPos Format1/2 with missing subtables (#1125).
+        
+        3.21.1 (released 2018-01-03)
+        ----------------------------
+        
+        - [feaLib] Allow mixed single/multiple substitutions (#612)
+        - Added missing ``*.afm`` test assets to MAINFEST.in (#1137).
+        - Fixed dumping ``SVG`` tables containing color palettes (#1124).
+        
+        3.21.0 (released 2017-12-18)
+        ----------------------------
+        
+        - [cmap] when compiling format6 subtable, don't assume gid0 is always called
+          '.notdef' (1e42224).
+        - [ot] Allow decompiling fonts with bad Coverage format number (1aafae8).
+        - Change FontTools licence to MIT (#1127).
+        - [post] Prune extra names already in standard Mac set (df1e8c7).
+        - [subset] Delete empty SubrsIndex after subsetting (#994, #1118).
+        - [varLib] Don't share points in cvar by default, as it currently fails on
+          some browsers (#1113).
+        - [afmLib] Make poor old afmLib work on python3.
+        
+        3.20.1 (released 2017-11-22)
+        ----------------------------
+        
+        - [unicodedata] Fixed issue with ``script`` and ``script_extension`` functions
+          returning inconsistent short vs long names. They both return the short four-
+          letter script codes now. Added ``script_name`` and ``script_code`` functions
+          to look up the long human-readable script name from the script code, and
+          viceversa (#1109, #1111).
+        
+        3.20.0 (released 2017-11-21)
+        ----------------------------
+        
+        - [unicodedata] Addded new module ``fontTools.unicodedata`` which exports the
+          same interface as the built-in ``unicodedata`` module, with the addition of
+          a few functions that are missing from the latter, such as ``script``,
+          ``script_extension`` and ``block``. Added a ``MetaTools/buildUCD.py`` script
+          to download and parse data files from the Unicode Character Database and
+          generate python modules containing lists of ranges and property values.
+        - [feaLib] Added ``__str__`` method to all ``ast`` elements (delegates to the
+          ``asFea`` method).
+        - [feaLib] ``Parser`` constructor now accepts a ``glyphNames`` iterable
+          instead of ``glyphMap`` dict. The latter still works but with a pending
+          deprecation warning (#1104).
+        - [bezierTools] Added arc length calculation functions originally from
+          ``pens.perimeterPen`` module (#1101).
+        - [varLib] Started generating STAT table (8af4309). Right now it just reflects
+          the axes, and even that with certain limitations:
+          * AxisOrdering is set to the order axes are defined,
+          * Name-table entries are not shared with fvar.
+        - [py23] Added backports for ``redirect_stdout`` and ``redirect_stderr``
+          context managers (#1097).
+        - [Graphite] Fixed some round-trip bugs (#1093).
+        
+        3.19.0 (released 2017-11-06)
+        ----------------------------
+        
+        - [varLib] Try set of used points instead of all points when testing whether to
+          share points between tuples (#1090).
+        - [CFF2] Fixed issue with reading/writing PrivateDict BlueValues to TTX file.
+          Read the commit message 8b02b5a and issue #1030 for more details.
+          NOTE: this change invalidates all the TTX files containing CFF2 tables
+          that where dumped with previous verisons of fonttools.
+          CFF2 Subr items can have values on the stack after the last operator, thus
+          a ``CFF2Subr`` class was added to accommodate this (#1091).
+        - [_k_e_r_n] Fixed compilation of AAT kern version=1.0 tables (#1089, #1094)
+        - [ttLib] Added getBestCmap() convenience method to TTFont class and cmap table
+          class that returns a preferred Unicode cmap subtable given a list of options
+          (#1092).
+        - [morx] Emit more meaningful subtable flags. Implement InsertionMorphAction
+        
+        3.18.0 (released 2017-10-30)
+        ----------------------------
+        
+        - [feaLib] Fixed writing back nested glyph classes (#1086).
+        - [TupleVariation] Reactivated shared points logic, bugfixes (#1009).
+        - [AAT] Implemented ``morx`` ligature subtables (#1082).
+        - [reverseContourPen] Keep duplicate lineTo following a moveTo (#1080,
+          https://github.com/googlei18n/cu2qu/issues/51).
+        - [varLib.mutator] Suport instantiation of GPOS, GDEF and MVAR (#1079).
+        - [sstruct] Fixed issue with ``unicode_literals`` and ``struct`` module in
+          old versions of python 2.7 (#993).
+        
+        3.17.0 (released 2017-10-16)
+        ----------------------------
+        
+        - [svgPathPen] Added an ``SVGPathPen`` that translates segment pen commands
+          into SVG path descriptions. Copied from Tal Leming's ``ufo2svg.svgPathPen``
+          https://github.com/typesupply/ufo2svg/blob/d69f992/Lib/ufo2svg/svgPathPen.py
+        - [reverseContourPen] Added ``ReverseContourPen``, a filter pen that draws
+          contours with the winding direction reversed, while keeping the starting
+          point (#1071).
+        - [filterPen] Added ``ContourFilterPen`` to manipulate contours as a whole
+          rather than segment by segment.
+        - [arrayTools] Added ``Vector`` class to apply math operations on an array
+          of numbers, and ``pairwise`` function to loop over pairs of items in an
+          iterable.
+        - [varLib] Added support for building and interpolation of ``cvar`` table
+          (f874cf6, a25a401).
+        
+        3.16.0 (released 2017-10-03)
+        ----------------------------
+        
+        - [head] Try using ``SOURCE_DATE_EPOCH`` environment variable when setting
+          the ``head`` modified timestamp to ensure reproducible builds (#1063).
+          See https://reproducible-builds.org/specs/source-date-epoch/
+        - [VTT] Decode VTT's ``TSI*`` tables text as UTF-8 (#1060).
+        - Added support for Graphite font tables: Feat, Glat, Gloc, Silf and Sill.
+          Thanks @mhosken! (#1054).
+        - [varLib] Default to using axis "name" attribute if "labelname" element
+          is missing (588f524).
+        - [merge] Added support for merging Script records. Remove unused features
+          and lookups after merge (d802580, 556508b).
+        - Added ``fontTools.svgLib`` package. Includes a parser for SVG Paths that
+          supports the Pen protocol (#1051). Also, added a snippet to convert SVG
+          outlines to UFO GLIF (#1053).
+        - [AAT] Added support for ``ankr``, ``bsln``, ``mort``, ``morx``, ``gcid``,
+          and ``cidg``.
+        - [subset] Implemented subsetting of ``prop``, ``opbd``, ``bsln``, ``lcar``.
+        
+        3.15.1 (released 2017-08-18)
+        ----------------------------
+        
+        - [otConverters] Implemented ``__add__`` and ``__radd__`` methods on
+          ``otConverters._LazyList`` that decompile a lazy list before adding
+          it to another list or ``_LazyList`` instance. Fixes an ``AttributeError``
+          in the ``subset`` module when attempting to sum ``_LazyList`` objects
+          (6ef48bd2, 1aef1683).
+        - [AAT] Support the `opbd` table with optical bounds (a47f6588).
+        - [AAT] Support `prop` table with glyph properties (d05617b4).
+        
+        
+        3.15.0 (released 2017-08-17)
+        ----------------------------
+        
+        - [AAT] Added support for AAT lookups. The ``lcar`` table can be decompiled
+          and recompiled; futher work needed to handle ``morx`` table (#1025).
+        - [subset] Keep (empty) DefaultLangSys for Script 'DFLT' (6eb807b5).
+        - [subset] Support GSUB/GPOS.FeatureVariations (fe01d87b).
+        - [varLib] In ``models.supportScalars``, ignore an axis when its peak value
+          is 0 (fixes #1020).
+        - [varLib] Add default mappings to all axes in avar to fix rendering issue
+          in some rasterizers (19c4b377, 04eacf13).
+        - [varLib] Flatten multiple tail PairPosFormat2 subtables before merging
+          (c55ef525).
+        - [ttLib] Added support for recalculating font bounding box in ``CFF`` and
+          ``head`` tables, and min/max values in ``hhea`` and ``vhea`` tables (#970).
+        
+        3.14.0 (released 2017-07-31)
+        ----------------------------
+        
+        - [varLib.merger] Remove Extensions subtables before merging (f7c20cf8).
+        - [varLib] Initialize the avar segment map with required default entries
+          (#1014).
+        - [varLib] Implemented optimal IUP optmiziation (#1019).
+        - [otData] Add ``AxisValueFormat4`` for STAT table v1.2 from OT v1.8.2
+          (#1015).
+        - [name] Fixed BCP46 language tag for Mac langID=9: 'si' -> 'sl'.
+        - [subset] Return value from ``_DehintingT2Decompiler.op_hintmask``
+          (c0d672ba).
+        - [cffLib] Allow to get TopDict by index as well as by name (dca96c9c).
+        - [cffLib] Removed global ``isCFF2`` state; use one set of classes for
+          both CFF and CFF2, maintaining backward compatibility existing code (#1007).
+        - [cffLib] Deprecated maxstack operator, per OpenType spec update 1.8.1.
+        - [cffLib] Added missing default (-100) for UnderlinePosition (#983).
+        - [feaLib] Enable setting nameIDs greater than 255 (#1003).
+        - [varLib] Recalculate ValueFormat when merging SinglePos (#996).
+        - [varLib] Do not emit MVAR if there are no entries in the variation store
+          (#987).
+        - [ttx] For ``-x`` option, pad with space if table tag length is < 4.
+        
+        3.13.1 (released 2017-05-30)
+        ----------------------------
+        
+        - [feaLib.builder] Removed duplicate lookups optimization. The original
+          lookup order and semantics of the feature file are preserved (#976).
+        
+        3.13.0 (released 2017-05-24)
+        ----------------------------
+        
+        - [varLib.mutator] Implement IUP optimization (#969).
+        - [_g_l_y_f.GlyphCoordinates] Changed ``__bool__()`` semantics to match those
+          of other iterables (e46f949). Removed ``__abs__()`` (3db5be2).
+        - [varLib.interpolate_layout] Added ``mapped`` keyword argument to
+          ``interpolate_layout`` to allow disabling avar mapping: if False (default),
+          the location is mapped using the map element of the axes in designspace file;
+          if True, it is assumed that location is in designspace's internal space and
+          no mapping is performed (#950, #975).
+        - [varLib.interpolate_layout] Import designspace-loading logic from varLib.
+        - [varLib] Fixed bug with recombining PairPosClass2 subtables (81498e5, #914).
+        - [cffLib.specializer] When copying iterables, cast to list (462b7f86).
+        
+        3.12.1 (released 2017-05-18)
+        ----------------------------
+        
+        - [pens.t2CharStringPen] Fixed AttributeError when calling addComponent in
+          T2CharStringPen (#965).
+        
+        3.12.0 (released 2017-05-17)
+        ----------------------------
+        
+        - [cffLib.specializer] Added new ``specializer`` module to optimize CFF
+          charstrings, used by the T2CharStringPen (#948).
+        - [varLib.mutator] Sort glyphs by component depth before calculating composite
+          glyphs' bounding boxes to ensure deltas are correctly caclulated (#945).
+        - [_g_l_y_f] Fixed loss of precision in GlyphCoordinates by using 'd' (double)
+          instead of 'f' (float) as ``array.array`` typecode (#963, #964).
+        
+        3.11.0 (released 2017-05-03)
+        ----------------------------
+        
+        - [t2CharStringPen] Initial support for specialized Type2 path operators:
+          vmoveto, hmoveto, vlineto, hlineto, vvcurveto, hhcurveto, vhcurveto and
+          hvcurveto. This should produce more compact charstrings (#940, #403).
+        - [Doc] Added Sphinx sources for the documentation. Thanks @gferreira (#935).
+        - [fvar] Expose flags in XML (#932)
+        - [name] Add helper function for building multi-lingual names (#921)
+        - [varLib] Fixed kern merging when a PairPosFormat2 has ClassDef1 with glyphs
+          that are NOT present in the Coverage (1b5e1c4, #939).
+        - [varLib] Fixed non-deterministic ClassDef order with PY3 (f056c12, #927).
+        - [feLib] Throw an error when the same glyph is defined in multiple mark
+          classes within the same lookup (3e3ff00, #453).
+        
+        3.10.0 (released 2017-04-14)
+        ----------------------------
+        
+        - [varLib] Added support for building ``avar`` table, using the designspace
+          ``<map>`` elements.
+        - [varLib] Removed unused ``build(..., axisMap)`` argument. Axis map should
+          be specified in designspace file now. We do not accept nonstandard axes
+          if ``<axes>`` element is not present.
+        - [varLib] Removed "custom" axis from the ``standard_axis_map``. This was
+          added before when glyphsLib was always exporting the (unused) custom axis.
+        - [varLib] Added partial support for building ``MVAR`` table; does not
+          implement ``gasp`` table variations yet.
+        - [pens] Added FilterPen base class, for pens that control another pen;
+          factored out ``addComponent`` method from BasePen into a separate abstract
+          DecomposingPen class; added DecomposingRecordingPen, which records
+          components decomposed as regular contours.
+        - [TSI1] Fixed computation of the textLength of VTT private tables (#913).
+        - [loggingTools] Added ``LogMixin`` class providing a ``log`` property to
+          subclasses, which returns a ``logging.Logger`` named after the latter.
+        - [loggingTools] Added ``assertRegex`` method to ``CapturingLogHandler``.
+        - [py23] Added backport for python 3's ``types.SimpleNamespace`` class.
+        - [EBLC] Fixed issue with python 3 ``zip`` iterator.
+        
+        3.9.2 (released 2017-04-08)
+        ---------------------------
+        
+        - [pens] Added pen to draw glyphs using WxPython ``GraphicsPath`` class:
+          https://wxpython.org/docs/api/wx.GraphicsPath-class.html
+        - [varLib.merger] Fixed issue with recombining multiple PairPosFormat2
+          subtables (#888)
+        - [varLib] Do not encode gvar deltas that are all zeroes, or if all values
+          are smaller than tolerance.
+        - [ttLib] _TTGlyphSet glyphs now also have ``height`` and ``tsb`` (top
+          side bearing) attributes from the ``vmtx`` table, if present.
+        - [glyf] In ``GlyphCoordintes`` class, added ``__bool__`` / ``__nonzero__``
+          methods, and ``array`` property to get raw array.
+        - [ttx] Support reading TTX files with BOM (#896)
+        - [CFF2] Fixed the reporting of the number of regions in the font.
+        
+        3.9.1 (released 2017-03-20)
+        ---------------------------
+        
+        - [varLib.merger] Fixed issue while recombining multiple PairPosFormat2
+          subtables if they were split because of offset overflows (9798c30).
+        - [varLib.merger] Only merge multiple PairPosFormat1 subtables if there is
+          at least one of the fonts with a non-empty Format1 subtable (0f5a46b).
+        - [varLib.merger] Fixed IndexError with empty ClassDef1 in PairPosFormat2
+          (aad0d46).
+        - [varLib.merger] Avoid reusing Class2Record (mutable) objects (e6125b3).
+        - [varLib.merger] Calculate ClassDef1 and ClassDef2's Format when merging
+          PairPosFormat2 (23511fd).
+        - [macUtils] Added missing ttLib import (b05f203).
+        
+        3.9.0 (released 2017-03-13)
+        ---------------------------
+        
+        - [feaLib] Added (partial) support for parsing feature file comments ``# ...``
+          appearing in between statements (#879).
+        - [feaLib] Cleaned up syntax tree for FeatureNames.
+        - [ttLib] Added support for reading/writing ``CFF2`` table (thanks to
+          @readroberts at Adobe), and ``TTFA`` (ttfautohint) table.
+        - [varLib] Fixed regression introduced with 3.8.0 in the calculation of
+          ``NumShorts``, i.e. the number of deltas in ItemVariationData's delta sets
+          that use a 16-bit representation (b2825ff).
+        
+        3.8.0 (released 2017-03-05)
+        ---------------------------
+        
+        - New pens: MomentsPen, StatisticsPen, RecordingPen, and TeePen.
+        - [misc] Added new ``fontTools.misc.symfont`` module, for symbolic font
+          statistical analysis; requires ``sympy`` (http://www.sympy.org/en/index.html)
+        - [varLib] Added experimental ``fontTools.varLib.interpolatable`` module for
+          finding wrong contour order between different masters
+        - [varLib] designspace.load() now returns a dictionary, instead of a tuple,
+          and supports <axes> element (#864); the 'masters' item was renamed 'sources',
+          like the <sources> element in the designspace document
+        - [ttLib] Fixed issue with recalculating ``head`` modified timestamp when
+          saving CFF fonts
+        - [ttLib] In TupleVariation, round deltas before compiling (#861, fixed #592)
+        - [feaLib] Ignore duplicate glyphs in classes used as MarkFilteringSet and
+          MarkAttachmentType (#863)
+        - [merge] Changed the ``gasp`` table merge logic so that only the one from
+          the first font is retained, similar to other hinting tables (#862)
+        - [Tests] Added tests for the ``varLib`` package, as well as test fonts
+          from the "Annotated OpenType Specification" (AOTS) to exercise ``ttLib``'s
+          table readers/writers (<https://github.com/adobe-type-tools/aots>)
+        
+        3.7.2 (released 2017-02-17)
+        ---------------------------
+        
+        - [subset] Keep advance widths when stripping ".notdef" glyph outline in
+          CID-keyed CFF fonts (#845)
+        - [feaLib] Zero values now produce the same results as makeotf (#633, #848)
+        - [feaLib] More compact encoding for “Contextual positioning with in-line
+          single positioning rules” (#514)
+        
+        3.7.1 (released 2017-02-15)
+        ---------------------------
+        
+        - [subset] Fixed issue with ``--no-hinting`` option whereby advance widths in
+          Type 2 charstrings were also being stripped (#709, #343)
+        - [feaLib] include statements now resolve relative paths like makeotf (#838)
+        - [feaLib] table ``name`` now handles Unicode codepoints beyond the Basic
+          Multilingual Plane, also supports old-style MacOS platform encodings (#842)
+        - [feaLib] correctly escape string literals when emitting feature syntax (#780)
+        
+        3.7.0 (released 2017-02-11)
+        ---------------------------
+        
+        - [ttx, mtiLib] Preserve ordering of glyph alternates in GSUB type 3 (#833).
+        - [feaLib] Glyph names can have dashes, as per new AFDKO syntax v1.20 (#559).
+        - [feaLib] feaLib.Parser now needs the font's glyph map for parsing.
+        - [varLib] Fix regression where GPOS values were stored as 0.
+        - [varLib] Allow merging of class-based kerning when ClassDefs are different
+        
+        3.6.3 (released 2017-02-06)
+        ---------------------------
+        
+        - [varLib] Fix building variation of PairPosFormat2 (b5c34ce).
+        - Populate defaults even for otTables that have postRead (e45297b).
+        - Fix compiling of MultipleSubstFormat1 with zero 'out' glyphs (b887860).
+        
+        3.6.2 (released 2017-01-30)
+        ---------------------------
+        
+        - [varLib.merger] Fixed "TypeError: reduce() of empty sequence with no
+          initial value" (3717dc6).
+        
+        3.6.1 (released 2017-01-28)
+        ---------------------------
+        
+        -  [py23] Fixed unhandled exception occurring at interpreter shutdown in
+           the "last resort" logging handler (972b3e6).
+        -  [agl] Ensure all glyph names are of native 'str' type; avoid mixing
+           'str' and 'unicode' in TTFont.glyphOrder (d8c4058).
+        -  Fixed inconsistent title levels in README.rst that caused PyPI to
+           incorrectly render the reStructuredText page.
+        
+        3.6.0 (released 2017-01-26)
+        ---------------------------
+        
+        -  [varLib] Refactored and improved the variation-font-building process.
+        -  Assembly code in the fpgm, prep, and glyf tables is now indented in
+           XML output for improved readability. The ``instruction`` element is
+           written as a simple tag if empty (#819).
+        -  [ttx] Fixed 'I/O operation on closed file' error when dumping
+           multiple TTXs to standard output with the '-o -' option.
+        -  The unit test modules (``*_test.py``) have been moved outside of the
+           fontTools package to the Tests folder, thus they are no longer
+           installed (#811).
+        
+        3.5.0 (released 2017-01-14)
+        ---------------------------
+        
+        -  Font tables read from XML can now be written back to XML with no
+           loss.
+        -  GSUB/GPOS LookupType is written out in XML as an element, not
+           comment. (#792)
+        -  When parsing cmap table, do not store items mapped to glyph id 0.
+           (#790)
+        -  [otlLib] Make ClassDef sorting deterministic. Fixes #766 (7d1ddb2)
+        -  [mtiLib] Added unit tests (#787)
+        -  [cvar] Implemented cvar table
+        -  [gvar] Renamed GlyphVariation to TupleVariation to match OpenType
+           terminology.
+        -  [otTables] Handle gracefully empty VarData.Item array when compiling
+           XML. (#797)
+        -  [varLib] Re-enabled generation of ``HVAR`` table for fonts with
+           TrueType outlines; removed ``--build-HVAR`` command-line option.
+        -  [feaLib] The parser can now be extended to support non-standard
+           statements in FEA code by using a customized Abstract Syntax Tree.
+           See, for example, ``feaLib.builder_test.test_extensions`` and
+           baseClass.feax (#794, fixes #773).
+        -  [feaLib] Added ``feaLib`` command to the 'fonttools' command-line
+           tool; applies a feature file to a font. ``fonttools feaLib -h`` for
+           help.
+        -  [pens] The ``T2CharStringPen`` now takes an optional
+           ``roundTolerance`` argument to control the rounding of coordinates
+           (#804, fixes #769).
+        -  [ci] Measure test coverage on all supported python versions and OSes,
+           combine coverage data and upload to
+           https://codecov.io/gh/fonttools/fonttools (#786)
+        -  [ci] Configured Travis and Appveyor for running tests on Python 3.6
+           (#785, 55c03bc)
+        -  The manual pages installation directory can be customized through
+           ``FONTTOOLS_MANPATH`` environment variable (#799, fixes #84).
+        -  [Snippets] Added otf2ttf.py, for converting fonts from CFF to
+           TrueType using the googlei18n/cu2qu module (#802)
+        
+        3.4.0 (released 2016-12-21)
+        ---------------------------
+        
+        -  [feaLib] Added support for generating FEA text from abstract syntax
+           tree (AST) objects (#776). Thanks @mhosken
+        -  Added ``agl.toUnicode`` function to convert AGL-compliant glyph names
+           to Unicode strings (#774)
+        -  Implemented MVAR table (b4d5381)
+        
+        3.3.1 (released 2016-12-15)
+        ---------------------------
+        
+        -  [setup] We no longer use versioneer.py to compute fonttools version
+           from git metadata, as this has caused issues for some users (#767).
+           Now we bump the version strings manually with a custom ``release``
+           command of setup.py script.
+        
+        3.3.0 (released 2016-12-06)
+        ---------------------------
+        
+        -  [ttLib] Implemented STAT table from OpenType 1.8 (#758)
+        -  [cffLib] Fixed decompilation of CFF fonts containing non-standard
+           key/value pairs in FontDict (issue #740; PR #744)
+        -  [py23] minor: in ``round3`` function, allow the second argument to be
+           ``None`` (#757)
+        -  The standalone ``sstruct`` and ``xmlWriter`` modules, deprecated
+           since vesion 3.2.0, have been removed. They can be imported from the
+           ``fontTools.misc`` package.
+        
+        3.2.3 (released 2016-12-02)
+        ---------------------------
+        
+        -  [py23] optimized performance of round3 function; added backport for
+           py35 math.isclose() (9d8dacb)
+        -  [subset] fixed issue with 'narrow' (UCS-2) Python 2 builds and
+           ``--text``/``--text-file`` options containing non-BMP chararcters
+           (16d0e5e)
+        -  [varLib] fixed issuewhen normalizing location values (8fa2ee1, #749)
+        -  [inspect] Made it compatible with both python2 and python3 (167ee60,
+           #748). Thanks @pnemade
+        
+        3.2.2 (released 2016-11-24)
+        ---------------------------
+        
+        -  [varLib] Do not emit null axes in fvar (1bebcec). Thanks @robmck-ms
+        -  [varLib] Handle fonts without GPOS (7915a45)
+        -  [merge] Ignore LangSys if None (a11bc56)
+        -  [subset] Fix subsetting MathVariants (78d3cbe)
+        -  [OS/2] Fix "Private Use (plane 15)" range (08a0d55). Thanks @mashabow
+        
+        3.2.1 (released 2016-11-03)
+        ---------------------------
+        
+        -  [OS/2] fix checking ``fsSelection`` bits matching ``head.macStyle``
+           bits
+        -  [varLib] added ``--build-HVAR`` option to generate ``HVAR`` table for
+           fonts with TrueType outlines. For ``CFF2``, it is enabled by default.
+        
+        3.2.0 (released 2016-11-02)
+        ---------------------------
+        
+        -  [varLib] Improve support for OpenType 1.8 Variable Fonts:
+        -  Implement GDEF's VariationStore
+        -  Implement HVAR/VVAR tables
+        -  Partial support for loading MutatorMath .designspace files with
+           varLib.designspace module
+        -  Add varLib.models with Variation fonts interpolation models
+        -  Implement GSUB/GPOS FeatureVariations
+        -  Initial support for interpolating and merging OpenType Layout tables
+           (see ``varLib.interpolate_layout`` and ``varLib.merger`` modules)
+        -  [API change] Change version to be an integer instead of a float in
+           XML output for GSUB, GPOS, GDEF, MATH, BASE, JSTF, HVAR, VVAR, feat,
+           hhea and vhea tables. Scripts that set the Version for those to 1.0
+           or other float values also need fixing. A warning is emitted when
+           code or XML needs fix.
+        -  several bug fixes to the cffLib module, contributed by Adobe's
+           @readroberts
+        -  The XML output for CFF table now has a 'major' and 'minor' elements
+           for specifying whether it's version 1.0 or 2.0 (support for CFF2 is
+           coming soon)
+        -  [setup.py] remove undocumented/deprecated ``extra_path`` Distutils
+           argument. This means that we no longer create a "FontTools" subfolder
+           in site-packages containing the actual fontTools package, as well as
+           the standalone xmlWriter and sstruct modules. The latter modules are
+           also deprecated, and scheduled for removal in upcoming releases.
+           Please change your import statements to point to from fontTools.misc
+           import xmlWriter and from fontTools.misc import sstruct.
+        -  [scripts] Add a 'fonttools' command-line tool that simply runs
+           ``fontTools.*`` sub-modules: e.g. ``fonttools ttx``,
+           ``fonttools subset``, etc.
+        -  [hmtx/vmts] Read advance width/heights as unsigned short (uint16);
+           automatically round float values to integers.
+        -  [ttLib/xmlWriter] add 'newlinestr=None' keyword argument to
+           ``TTFont.saveXML`` for overriding os-specific line endings (passed on
+           to ``XMLWriter`` instances).
+        -  [versioning] Use versioneer instead of ``setuptools_scm`` to
+           dynamically load version info from a git checkout at import time.
+        -  [feaLib] Support backslash-prefixed glyph names.
+        
+        3.1.2 (released 2016-09-27)
+        ---------------------------
+        
+        -  restore Makefile as an alternative way to build/check/install
+        -  README.md: update instructions for installing package from source,
+           and for running test suite
+        -  NEWS: Change log was out of sync with tagged release
+        
+        3.1.1 (released 2016-09-27)
+        ---------------------------
+        
+        -  Fix ``ttLibVersion`` attribute in TTX files still showing '3.0'
+           instead of '3.1'.
+        -  Use ``setuptools_scm`` to manage package versions.
+        
+        3.1.0 (released 2016-09-26)
+        ---------------------------
+        
+        -  [feaLib] New library to parse and compile Adobe FDK OpenType Feature
+           files.
+        -  [mtiLib] New library to parse and compile Monotype 'FontDame'
+           OpenType Layout Tables files.
+        -  [voltLib] New library to parse Microsoft VOLT project files.
+        -  [otlLib] New library to work with OpenType Layout tables.
+        -  [varLib] New library to work with OpenType Font Variations.
+        -  [pens] Add ttGlyphPen to draw to TrueType glyphs, and t2CharStringPen
+           to draw to Type 2 Charstrings (CFF); add areaPen and perimeterPen.
+        -  [ttLib.tables] Implement 'meta' and 'trak' tables.
+        -  [ttx] Add --flavor option for compiling to 'woff' or 'woff2'; add
+           ``--with-zopfli`` option to use Zopfli to compress WOFF 1.0 fonts.
+        -  [subset] Support subsetting 'COLR'/'CPAL' and 'CBDT'/'CBLC' color
+           fonts tables, and 'gvar' table for variation fonts.
+        -  [Snippets] Add ``symfont.py``, for symbolic font statistics analysis;
+           interpolatable.py, a preliminary script for detecting interpolation
+           errors; ``{merge,dump}_woff_metadata.py``.
+        -  [classifyTools] Helpers to classify things into classes.
+        -  [CI] Run tests on Windows, Linux and macOS using Appveyor and Travis
+           CI; check unit test coverage with Coverage.py/Coveralls; automatic
+           deployment to PyPI on tags.
+        -  [loggingTools] Use Python built-in logging module to print messages.
+        -  [py23] Make round() behave like Python 3 built-in round(); define
+           round2() and round3().
+        
+        3.0 (released 2015-09-01)
+        -------------------------
+        
+        -  Add Snippet scripts for cmap subtable format conversion, printing
+           GSUB/GPOS features, building a GX font from two masters
+        -  TTX WOFF2 support and a ``-f`` option to overwrite output file(s)
+        -  Support GX tables: ``avar``, ``gvar``, ``fvar``, ``meta``
+        -  Support ``feat`` and gzip-compressed SVG tables
+        -  Upgrade Mac East Asian encodings to native implementation if
+           available
+        -  Add Roman Croatian and Romanian encodings, codecs for mac-extended
+           East Asian encodings
+        -  Implement optimal GLYF glyph outline packing; disabled by default
+        
+        2.5 (released 2014-09-24)
+        -------------------------
+        
+        -  Add a Qt pen
+        -  Add VDMX table converter
+        -  Load all OpenType sub-structures lazily
+        -  Add support for cmap format 13.
+        -  Add pyftmerge tool
+        -  Update to Unicode 6.3.0d3
+        -  Add pyftinspect tool
+        -  Add support for Google CBLC/CBDT color bitmaps, standard EBLC/EBDT
+           embedded bitmaps, and ``SVG`` table (thanks to Read Roberts at Adobe)
+        -  Add support for loading, saving and ttx'ing WOFF file format
+        -  Add support for Microsoft COLR/CPAL layered color glyphs
+        -  Support PyPy
+        -  Support Jython, by replacing numpy with array/lists modules and
+           removed it, pure-Python StringIO, not cStringIO
+        -  Add pyftsubset and Subsetter object, supporting CFF and TTF
+        -  Add to ttx args for -q for quiet mode, -z to choose a bitmap dump
+           format
+        
+        2.4 (released 2013-06-22)
+        -------------------------
+        
+        -  Option to write to arbitrary files
+        -  Better dump format for DSIG
+        -  Better detection of OTF XML
+        -  Fix issue with Apple's kern table format
+        -  Fix mangling of TT glyph programs
+        -  Fix issues related to mona.ttf
+        -  Fix Windows Installer instructions
+        -  Fix some modern MacOS issues
+        -  Fix minor issues and typos
+        
+        2.3 (released 2009-11-08)
+        -------------------------
+        
+        -  TrueType Collection (TTC) support
+        -  Python 2.6 support
+        -  Update Unicode data to 5.2.0
+        -  Couple of bug fixes
+        
+        2.2 (released 2008-05-18)
+        -------------------------
+        
+        -  ClearType support
+        -  cmap format 1 support
+        -  PFA font support
+        -  Switched from Numeric to numpy
+        -  Update Unicode data to 5.1.0
+        -  Update AGLFN data to 1.6
+        -  Many bug fixes
+        
+        2.1 (released 2008-01-28)
+        -------------------------
+        
+        -  Many years worth of fixes and features
+        
+        2.0b2 (released 2002-??-??)
+        ---------------------------
+        
+        -  Be "forgiving" when interpreting the maxp table version field:
+           interpret any value as 1.0 if it's not 0.5. Fixes dumping of these
+           GPL fonts: http://www.freebsd.org/cgi/pds.cgi?ports/chinese/wangttf
+        -  Fixed ttx -l: it turned out this part of the code didn't work with
+           Python 2.2.1 and earlier. My bad to do most of my testing with a
+           different version than I shipped TTX with :-(
+        -  Fixed bug in ClassDef format 1 subtable (Andreas Seidel bumped into
+           this one).
+        
+        2.0b1 (released 2002-09-10)
+        ---------------------------
+        
+        -  Fixed embarrassing bug: the master checksum in the head table is now
+           calculated correctly even on little-endian platforms (such as Intel).
+        -  Made the cmap format 4 compiler smarter: the binary data it creates
+           is now more or less as compact as possible. TTX now makes more
+           compact data than in any shipping font I've tested it with.
+        -  Dump glyph names as a separate "GlyphOrder" pseudo table as opposed
+           to as part of the glyf table (obviously needed for CFF-OTF's).
+        -  Added proper support for the CFF table.
+        -  Don't barf on empty tables (questionable, but "there are font out
+           there...")
+        -  When writing TT glyf data, align glyphs on 4-byte boundaries. This
+           seems to be the current recommendation by MS. Also: don't barf on
+           fonts which are already 4-byte aligned.
+        -  Windows installer contributed bu Adam Twardoch! Yay!
+        -  Changed the command line interface again, now by creating one new
+           tool replacing the old ones: ttx It dumps and compiles, depending on
+           input file types. The options have changed somewhat.
+        -  The -d option is back (output dir)
+        -  ttcompile's -i options is now called -m (as in "merge"), to avoid
+           clash with dump's -i.
+        -  The -s option ("split tables") no longer creates a directory, but
+           instead outputs a small .ttx file containing references to the
+           individual table files. This is not a true link, it's a simple file
+           name, and the referenced file should be in the same directory so
+           ttcompile can find them.
+        -  compile no longer accepts a directory as input argument. Instead it
+           can parse the new "mini-ttx" format as output by "ttx -s".
+        -  all arguments are input files
+        -  Renamed the command line programs and moved them to the Tools
+           subdirectory. They are now installed by the setup.py install script.
+        -  Added OpenType support. BASE, GDEF, GPOS, GSUB and JSTF are (almost)
+           fully supported. The XML output is not yet final, as I'm still
+           considering to output certain subtables in a more human-friendly
+           manner.
+        -  Fixed 'kern' table to correctly accept subtables it doesn't know
+           about, as well as interpreting Apple's definition of the 'kern' table
+           headers correctly.
+        -  Fixed bug where glyphnames were not calculated from 'cmap' if it was
+           (one of the) first tables to be decompiled. More specifically: it
+           cmap was the first to ask for a glyphID -> glyphName mapping.
+        -  Switched XML parsers: use expat instead of xmlproc. Should be faster.
+        -  Removed my UnicodeString object: I now require Python 2.0 or up,
+           which has unicode support built in.
+        -  Removed assert in glyf table: redundant data at the end of the table
+           is now ignored instead of raising an error. Should become a warning.
+        -  Fixed bug in hmtx/vmtx code that only occured if all advances were
+           equal.
+        -  Fixed subtle bug in TT instruction disassembler.
+        -  Couple of fixes to the 'post' table.
+        -  Updated OS/2 table to latest spec.
+        
+        1.0b1 (released 2001-08-10)
+        ---------------------------
+        
+        -  Reorganized the command line interface for ttDump.py and
+           ttCompile.py, they now behave more like "normal" command line tool,
+           in that they accept multiple input files for batch processing.
+        -  ttDump.py and ttCompile.py don't silently override files anymore, but
+           ask before doing so. Can be overridden by -f.
+        -  Added -d option to both ttDump.py and ttCompile.py.
+        -  Installation is now done with distutils. (Needs work for environments
+           without compilers.)
+        -  Updated installation instructions.
+        -  Added some workarounds so as to handle certain buggy fonts more
+           gracefully.
+        -  Updated Unicode table to Unicode 3.0 (Thanks Antoine!)
+        -  Included a Python script by Adam Twardoch that adds some useful stuff
+           to the Windows registry.
+        -  Moved the project to SourceForge.
+        
+        1.0a6 (released 2000-03-15)
+        ---------------------------
+        
+        -  Big reorganization: made ttLib a subpackage of the new fontTools
+           package, changed several module names. Called the entire suite
+           "FontTools"
+        -  Added several submodules to fontTools, some new, some older.
+        -  Added experimental CFF/GPOS/GSUB support to ttLib, read-only (but XML
+           dumping of GPOS/GSUB is for now disabled)
+        -  Fixed hdmx endian bug
+        -  Added -b option to ttCompile.py, it disables recalculation of
+           bounding boxes, as requested by Werner Lemberg.
+        -  Renamed tt2xml.pt to ttDump.py and xml2tt.py to ttCompile.py
+        -  Use ".ttx" as file extension instead of ".xml".
+        -  TTX is now the name of the XML-based *format* for TT fonts, and not
+           just an application.
+        
+        1.0a5
+        -----
+        
+        Never released
+        
+        -  More tables supported: hdmx, vhea, vmtx
+        
+        1.0a3 & 1.0a4
+        -------------
+        
+        Never released
+        
+        -  fixed most portability issues
+        -  retracted the "Euro_or_currency" change from 1.0a2: it was
+           nonsense!
+        
+        1.0a2 (released 1999-05-02)
+        ---------------------------
+        
+        -  binary release for MacOS
+        -  genenates full FOND resources: including width table, PS font name
+           info and kern table if applicable.
+        -  added cmap format 4 support. Extra: dumps Unicode char names as XML
+           comments!
+        -  added cmap format 6 support
+        -  now accepts true type files starting with "true" (instead of just
+           0x00010000 and "OTTO")
+        -  'glyf' table support is now complete: I added support for composite
+           scale, xy-scale and two-by-two for the 'glyf' table. For now,
+           component offset scale behaviour defaults to Apple-style. This only
+           affects the (re)calculation of the glyph bounding box.
+        -  changed "Euro" to "Euro_or_currency" in the Standard Apple Glyph
+           order list, since we cannot tell from the 'post' table which is
+           meant. I should probably doublecheck with a Unicode encoding if
+           available. (This does not affect the output!)
+        
+        Fixed bugs: - 'hhea' table is now recalculated correctly - fixed wrong
+        assumption about sfnt resource names
+        
+        1.0a1 (released 1999-04-27)
+        ---------------------------
+        
+        -  initial binary release for MacOS
+        
+Platform: Any
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Console
+Classifier: Environment :: Other Environment
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: End Users/Desktop
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Natural Language :: English
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
+Classifier: Topic :: Text Processing :: Fonts
+Classifier: Topic :: Multimedia :: Graphics
+Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion
diff --git a/Lib/fonttools.egg-info/SOURCES.txt b/Lib/fonttools.egg-info/SOURCES.txt
new file mode 100644
index 0000000..fa92e3e
--- /dev/null
+++ b/Lib/fonttools.egg-info/SOURCES.txt
@@ -0,0 +1,1576 @@
+.appveyor.yml
+.codecov.yml
+.coveragerc
+.travis.yml
+LICENSE
+LICENSE.external
+MANIFEST.in
+Makefile
+NEWS.rst
+README.rst
+dev-requirements.txt
+fonttools
+requirements.txt
+run-tests.sh
+setup.cfg
+setup.py
+tox.ini
+.travis/after_success.sh
+.travis/before_deploy.sh
+.travis/before_install.sh
+.travis/install.sh
+.travis/run.sh
+Doc/Makefile
+Doc/make.bat
+Doc/man/man1/ttx.1
+Doc/source/afmLib.rst
+Doc/source/agl.rst
+Doc/source/cffLib.rst
+Doc/source/conf.py
+Doc/source/encodings.rst
+Doc/source/feaLib.rst
+Doc/source/index.rst
+Doc/source/inspect.rst
+Doc/source/merge.rst
+Doc/source/subset.rst
+Doc/source/t1Lib.rst
+Doc/source/ttx.rst
+Doc/source/voltLib.rst
+Doc/source/designspaceLib/index.rst
+Doc/source/designspaceLib/readme.rst
+Doc/source/designspaceLib/scripting.rst
+Doc/source/misc/arrayTools.rst
+Doc/source/misc/bezierTools.rst
+Doc/source/misc/classifyTools.rst
+Doc/source/misc/eexec.rst
+Doc/source/misc/encodingTools.rst
+Doc/source/misc/fixedTools.rst
+Doc/source/misc/index.rst
+Doc/source/misc/loggingTools.rst
+Doc/source/misc/psCharStrings.rst
+Doc/source/misc/sstruct.rst
+Doc/source/misc/testTools.rst
+Doc/source/misc/textTools.rst
+Doc/source/misc/timeTools.rst
+Doc/source/misc/transform.rst
+Doc/source/misc/xmlReader.rst
+Doc/source/misc/xmlWriter.rst
+Doc/source/pens/areaPen.rst
+Doc/source/pens/basePen.rst
+Doc/source/pens/boundsPen.rst
+Doc/source/pens/filterPen.rst
+Doc/source/pens/index.rst
+Doc/source/pens/perimeterPen.rst
+Doc/source/pens/pointInsidePen.rst
+Doc/source/pens/recordingPen.rst
+Doc/source/pens/statisticsPen.rst
+Doc/source/pens/t2CharStringPen.rst
+Doc/source/pens/teePen.rst
+Doc/source/pens/transformPen.rst
+Doc/source/ttLib/index.rst
+Doc/source/ttLib/macUtils.rst
+Doc/source/ttLib/sfnt.rst
+Doc/source/ttLib/tables.rst
+Doc/source/ttLib/woff2.rst
+Doc/source/varLib/designspace.rst
+Doc/source/varLib/index.rst
+Doc/source/varLib/interpolatable.rst
+Doc/source/varLib/interpolate_layout.rst
+Doc/source/varLib/merger.rst
+Doc/source/varLib/models.rst
+Doc/source/varLib/mutator.rst
+Lib/fontTools/__init__.py
+Lib/fontTools/__main__.py
+Lib/fontTools/afmLib.py
+Lib/fontTools/agl.py
+Lib/fontTools/inspect.py
+Lib/fontTools/merge.py
+Lib/fontTools/ttx.py
+Lib/fontTools/unicode.py
+Lib/fontTools/cffLib/__init__.py
+Lib/fontTools/cffLib/specializer.py
+Lib/fontTools/cffLib/width.py
+Lib/fontTools/designspaceLib/__init__.py
+Lib/fontTools/encodings/MacRoman.py
+Lib/fontTools/encodings/StandardEncoding.py
+Lib/fontTools/encodings/__init__.py
+Lib/fontTools/encodings/codecs.py
+Lib/fontTools/feaLib/__init__.py
+Lib/fontTools/feaLib/__main__.py
+Lib/fontTools/feaLib/ast.py
+Lib/fontTools/feaLib/builder.py
+Lib/fontTools/feaLib/error.py
+Lib/fontTools/feaLib/lexer.py
+Lib/fontTools/feaLib/parser.py
+Lib/fontTools/misc/__init__.py
+Lib/fontTools/misc/arrayTools.py
+Lib/fontTools/misc/bezierTools.py
+Lib/fontTools/misc/classifyTools.py
+Lib/fontTools/misc/cliTools.py
+Lib/fontTools/misc/eexec.py
+Lib/fontTools/misc/encodingTools.py
+Lib/fontTools/misc/filenames.py
+Lib/fontTools/misc/fixedTools.py
+Lib/fontTools/misc/loggingTools.py
+Lib/fontTools/misc/macCreatorType.py
+Lib/fontTools/misc/macRes.py
+Lib/fontTools/misc/psCharStrings.py
+Lib/fontTools/misc/psLib.py
+Lib/fontTools/misc/psOperators.py
+Lib/fontTools/misc/py23.py
+Lib/fontTools/misc/sstruct.py
+Lib/fontTools/misc/symfont.py
+Lib/fontTools/misc/testTools.py
+Lib/fontTools/misc/textTools.py
+Lib/fontTools/misc/timeTools.py
+Lib/fontTools/misc/transform.py
+Lib/fontTools/misc/xmlReader.py
+Lib/fontTools/misc/xmlWriter.py
+Lib/fontTools/mtiLib/__init__.py
+Lib/fontTools/mtiLib/__main__.py
+Lib/fontTools/otlLib/__init__.py
+Lib/fontTools/otlLib/builder.py
+Lib/fontTools/pens/__init__.py
+Lib/fontTools/pens/areaPen.py
+Lib/fontTools/pens/basePen.py
+Lib/fontTools/pens/boundsPen.py
+Lib/fontTools/pens/cocoaPen.py
+Lib/fontTools/pens/filterPen.py
+Lib/fontTools/pens/momentsPen.py
+Lib/fontTools/pens/perimeterPen.py
+Lib/fontTools/pens/pointInsidePen.py
+Lib/fontTools/pens/qtPen.py
+Lib/fontTools/pens/recordingPen.py
+Lib/fontTools/pens/reportLabPen.py
+Lib/fontTools/pens/reverseContourPen.py
+Lib/fontTools/pens/statisticsPen.py
+Lib/fontTools/pens/svgPathPen.py
+Lib/fontTools/pens/t2CharStringPen.py
+Lib/fontTools/pens/teePen.py
+Lib/fontTools/pens/transformPen.py
+Lib/fontTools/pens/ttGlyphPen.py
+Lib/fontTools/pens/wxPen.py
+Lib/fontTools/subset/__init__.py
+Lib/fontTools/subset/__main__.py
+Lib/fontTools/svgLib/__init__.py
+Lib/fontTools/svgLib/path/__init__.py
+Lib/fontTools/svgLib/path/parser.py
+Lib/fontTools/t1Lib/__init__.py
+Lib/fontTools/ttLib/__init__.py
+Lib/fontTools/ttLib/macUtils.py
+Lib/fontTools/ttLib/sfnt.py
+Lib/fontTools/ttLib/standardGlyphOrder.py
+Lib/fontTools/ttLib/ttCollection.py
+Lib/fontTools/ttLib/ttFont.py
+Lib/fontTools/ttLib/woff2.py
+Lib/fontTools/ttLib/tables/B_A_S_E_.py
+Lib/fontTools/ttLib/tables/BitmapGlyphMetrics.py
+Lib/fontTools/ttLib/tables/C_B_D_T_.py
+Lib/fontTools/ttLib/tables/C_B_L_C_.py
+Lib/fontTools/ttLib/tables/C_F_F_.py
+Lib/fontTools/ttLib/tables/C_F_F__2.py
+Lib/fontTools/ttLib/tables/C_O_L_R_.py
+Lib/fontTools/ttLib/tables/C_P_A_L_.py
+Lib/fontTools/ttLib/tables/D_S_I_G_.py
+Lib/fontTools/ttLib/tables/DefaultTable.py
+Lib/fontTools/ttLib/tables/E_B_D_T_.py
+Lib/fontTools/ttLib/tables/E_B_L_C_.py
+Lib/fontTools/ttLib/tables/F_F_T_M_.py
+Lib/fontTools/ttLib/tables/F__e_a_t.py
+Lib/fontTools/ttLib/tables/G_D_E_F_.py
+Lib/fontTools/ttLib/tables/G_M_A_P_.py
+Lib/fontTools/ttLib/tables/G_P_K_G_.py
+Lib/fontTools/ttLib/tables/G_P_O_S_.py
+Lib/fontTools/ttLib/tables/G_S_U_B_.py
+Lib/fontTools/ttLib/tables/G__l_a_t.py
+Lib/fontTools/ttLib/tables/G__l_o_c.py
+Lib/fontTools/ttLib/tables/H_V_A_R_.py
+Lib/fontTools/ttLib/tables/J_S_T_F_.py
+Lib/fontTools/ttLib/tables/L_T_S_H_.py
+Lib/fontTools/ttLib/tables/M_A_T_H_.py
+Lib/fontTools/ttLib/tables/M_E_T_A_.py
+Lib/fontTools/ttLib/tables/M_V_A_R_.py
+Lib/fontTools/ttLib/tables/O_S_2f_2.py
+Lib/fontTools/ttLib/tables/S_I_N_G_.py
+Lib/fontTools/ttLib/tables/S_T_A_T_.py
+Lib/fontTools/ttLib/tables/S_V_G_.py
+Lib/fontTools/ttLib/tables/S__i_l_f.py
+Lib/fontTools/ttLib/tables/S__i_l_l.py
+Lib/fontTools/ttLib/tables/T_S_I_B_.py
+Lib/fontTools/ttLib/tables/T_S_I_D_.py
+Lib/fontTools/ttLib/tables/T_S_I_J_.py
+Lib/fontTools/ttLib/tables/T_S_I_P_.py
+Lib/fontTools/ttLib/tables/T_S_I_S_.py
+Lib/fontTools/ttLib/tables/T_S_I_V_.py
+Lib/fontTools/ttLib/tables/T_S_I__0.py
+Lib/fontTools/ttLib/tables/T_S_I__1.py
+Lib/fontTools/ttLib/tables/T_S_I__2.py
+Lib/fontTools/ttLib/tables/T_S_I__3.py
+Lib/fontTools/ttLib/tables/T_S_I__5.py
+Lib/fontTools/ttLib/tables/T_T_F_A_.py
+Lib/fontTools/ttLib/tables/TupleVariation.py
+Lib/fontTools/ttLib/tables/V_D_M_X_.py
+Lib/fontTools/ttLib/tables/V_O_R_G_.py
+Lib/fontTools/ttLib/tables/V_V_A_R_.py
+Lib/fontTools/ttLib/tables/__init__.py
+Lib/fontTools/ttLib/tables/_a_n_k_r.py
+Lib/fontTools/ttLib/tables/_a_v_a_r.py
+Lib/fontTools/ttLib/tables/_b_s_l_n.py
+Lib/fontTools/ttLib/tables/_c_i_d_g.py
+Lib/fontTools/ttLib/tables/_c_m_a_p.py
+Lib/fontTools/ttLib/tables/_c_v_a_r.py
+Lib/fontTools/ttLib/tables/_c_v_t.py
+Lib/fontTools/ttLib/tables/_f_e_a_t.py
+Lib/fontTools/ttLib/tables/_f_p_g_m.py
+Lib/fontTools/ttLib/tables/_f_v_a_r.py
+Lib/fontTools/ttLib/tables/_g_a_s_p.py
+Lib/fontTools/ttLib/tables/_g_c_i_d.py
+Lib/fontTools/ttLib/tables/_g_l_y_f.py
+Lib/fontTools/ttLib/tables/_g_v_a_r.py
+Lib/fontTools/ttLib/tables/_h_d_m_x.py
+Lib/fontTools/ttLib/tables/_h_e_a_d.py
+Lib/fontTools/ttLib/tables/_h_h_e_a.py
+Lib/fontTools/ttLib/tables/_h_m_t_x.py
+Lib/fontTools/ttLib/tables/_k_e_r_n.py
+Lib/fontTools/ttLib/tables/_l_c_a_r.py
+Lib/fontTools/ttLib/tables/_l_o_c_a.py
+Lib/fontTools/ttLib/tables/_l_t_a_g.py
+Lib/fontTools/ttLib/tables/_m_a_x_p.py
+Lib/fontTools/ttLib/tables/_m_e_t_a.py
+Lib/fontTools/ttLib/tables/_m_o_r_t.py
+Lib/fontTools/ttLib/tables/_m_o_r_x.py
+Lib/fontTools/ttLib/tables/_n_a_m_e.py
+Lib/fontTools/ttLib/tables/_o_p_b_d.py
+Lib/fontTools/ttLib/tables/_p_o_s_t.py
+Lib/fontTools/ttLib/tables/_p_r_e_p.py
+Lib/fontTools/ttLib/tables/_p_r_o_p.py
+Lib/fontTools/ttLib/tables/_s_b_i_x.py
+Lib/fontTools/ttLib/tables/_t_r_a_k.py
+Lib/fontTools/ttLib/tables/_v_h_e_a.py
+Lib/fontTools/ttLib/tables/_v_m_t_x.py
+Lib/fontTools/ttLib/tables/asciiTable.py
+Lib/fontTools/ttLib/tables/grUtils.py
+Lib/fontTools/ttLib/tables/otBase.py
+Lib/fontTools/ttLib/tables/otConverters.py
+Lib/fontTools/ttLib/tables/otData.py
+Lib/fontTools/ttLib/tables/otTables.py
+Lib/fontTools/ttLib/tables/sbixGlyph.py
+Lib/fontTools/ttLib/tables/sbixStrike.py
+Lib/fontTools/ttLib/tables/table_API_readme.txt
+Lib/fontTools/ttLib/tables/ttProgram.py
+Lib/fontTools/unicodedata/Blocks.py
+Lib/fontTools/unicodedata/OTTags.py
+Lib/fontTools/unicodedata/ScriptExtensions.py
+Lib/fontTools/unicodedata/Scripts.py
+Lib/fontTools/unicodedata/__init__.py
+Lib/fontTools/varLib/__init__.py
+Lib/fontTools/varLib/__main__.py
+Lib/fontTools/varLib/builder.py
+Lib/fontTools/varLib/designspace.py
+Lib/fontTools/varLib/featureVars.py
+Lib/fontTools/varLib/interpolatable.py
+Lib/fontTools/varLib/interpolate_layout.py
+Lib/fontTools/varLib/iup.py
+Lib/fontTools/varLib/merger.py
+Lib/fontTools/varLib/models.py
+Lib/fontTools/varLib/mutator.py
+Lib/fontTools/varLib/mvar.py
+Lib/fontTools/varLib/plot.py
+Lib/fontTools/varLib/varStore.py
+Lib/fontTools/voltLib/__init__.py
+Lib/fontTools/voltLib/ast.py
+Lib/fontTools/voltLib/error.py
+Lib/fontTools/voltLib/lexer.py
+Lib/fontTools/voltLib/parser.py
+Lib/fonttools.egg-info/PKG-INFO
+Lib/fonttools.egg-info/SOURCES.txt
+Lib/fonttools.egg-info/dependency_links.txt
+Lib/fonttools.egg-info/entry_points.txt
+Lib/fonttools.egg-info/top_level.txt
+MetaTools/buildTableList.py
+MetaTools/buildUCD.py
+MetaTools/roundTrip.py
+Snippets/README.md
+Snippets/checksum.py
+Snippets/cmap-format.py
+Snippets/dump_woff_metadata.py
+Snippets/edit_raw_table_data.py
+Snippets/fix-dflt-langsys.py
+Snippets/interpolate.py
+Snippets/layout-features.py
+Snippets/merge_woff_metadata.py
+Snippets/otf2ttf.py
+Snippets/rename-fonts.py
+Snippets/subset-fpgm.py
+Snippets/svg2glif.py
+Snippets/woff2_compress.py
+Snippets/woff2_decompress.py
+Tests/agl_test.py
+Tests/merge_test.py
+Tests/unicodedata_test.py
+Tests/afmLib/afmLib_test.py
+Tests/afmLib/data/TestAFM.afm
+Tests/cffLib/cffLib_test.py
+Tests/cffLib/specializer_test.py
+Tests/cffLib/data/TestOTF.otf
+Tests/designspaceLib/designspace_test.py
+Tests/designspaceLib/data/test.designspace
+Tests/encodings/codecs_test.py
+Tests/feaLib/__init__.py
+Tests/feaLib/builder_test.py
+Tests/feaLib/error_test.py
+Tests/feaLib/lexer_test.py
+Tests/feaLib/parser_test.py
+Tests/feaLib/data/Attach.fea
+Tests/feaLib/data/Attach.ttx
+Tests/feaLib/data/GPOS_1.fea
+Tests/feaLib/data/GPOS_1.ttx
+Tests/feaLib/data/GPOS_1_zero.fea
+Tests/feaLib/data/GPOS_1_zero.ttx
+Tests/feaLib/data/GPOS_2.fea
+Tests/feaLib/data/GPOS_2.ttx
+Tests/feaLib/data/GPOS_2b.fea
+Tests/feaLib/data/GPOS_2b.ttx
+Tests/feaLib/data/GPOS_3.fea
+Tests/feaLib/data/GPOS_3.ttx
+Tests/feaLib/data/GPOS_4.fea
+Tests/feaLib/data/GPOS_4.ttx
+Tests/feaLib/data/GPOS_5.fea
+Tests/feaLib/data/GPOS_5.ttx
+Tests/feaLib/data/GPOS_6.fea
+Tests/feaLib/data/GPOS_6.ttx
+Tests/feaLib/data/GPOS_8.fea
+Tests/feaLib/data/GPOS_8.ttx
+Tests/feaLib/data/GSUB_2.fea
+Tests/feaLib/data/GSUB_2.ttx
+Tests/feaLib/data/GSUB_3.fea
+Tests/feaLib/data/GSUB_3.ttx
+Tests/feaLib/data/GSUB_6.fea
+Tests/feaLib/data/GSUB_6.ttx
+Tests/feaLib/data/GSUB_8.fea
+Tests/feaLib/data/GSUB_8.ttx
+Tests/feaLib/data/GlyphClassDef.fea
+Tests/feaLib/data/GlyphClassDef.ttx
+Tests/feaLib/data/LigatureCaretByIndex.fea
+Tests/feaLib/data/LigatureCaretByIndex.ttx
+Tests/feaLib/data/LigatureCaretByPos.fea
+Tests/feaLib/data/LigatureCaretByPos.ttx
+Tests/feaLib/data/PairPosSubtable.fea
+Tests/feaLib/data/PairPosSubtable.ttx
+Tests/feaLib/data/ZeroValue_ChainSinglePos_horizontal.fea
+Tests/feaLib/data/ZeroValue_ChainSinglePos_horizontal.ttx
+Tests/feaLib/data/ZeroValue_ChainSinglePos_vertical.fea
+Tests/feaLib/data/ZeroValue_ChainSinglePos_vertical.ttx
+Tests/feaLib/data/ZeroValue_PairPos_horizontal.fea
+Tests/feaLib/data/ZeroValue_PairPos_horizontal.ttx
+Tests/feaLib/data/ZeroValue_PairPos_vertical.fea
+Tests/feaLib/data/ZeroValue_PairPos_vertical.ttx
+Tests/feaLib/data/ZeroValue_SinglePos_horizontal.fea
+Tests/feaLib/data/ZeroValue_SinglePos_horizontal.ttx
+Tests/feaLib/data/ZeroValue_SinglePos_vertical.fea
+Tests/feaLib/data/ZeroValue_SinglePos_vertical.ttx
+Tests/feaLib/data/baseClass.fea
+Tests/feaLib/data/baseClass.feax
+Tests/feaLib/data/bug453.fea
+Tests/feaLib/data/bug453.ttx
+Tests/feaLib/data/bug457.fea
+Tests/feaLib/data/bug457.ttx
+Tests/feaLib/data/bug463.fea
+Tests/feaLib/data/bug463.ttx
+Tests/feaLib/data/bug501.fea
+Tests/feaLib/data/bug501.ttx
+Tests/feaLib/data/bug502.fea
+Tests/feaLib/data/bug502.ttx
+Tests/feaLib/data/bug504.fea
+Tests/feaLib/data/bug504.ttx
+Tests/feaLib/data/bug505.fea
+Tests/feaLib/data/bug505.ttx
+Tests/feaLib/data/bug506.fea
+Tests/feaLib/data/bug506.ttx
+Tests/feaLib/data/bug509.fea
+Tests/feaLib/data/bug509.ttx
+Tests/feaLib/data/bug512.fea
+Tests/feaLib/data/bug512.ttx
+Tests/feaLib/data/bug514.fea
+Tests/feaLib/data/bug514.ttx
+Tests/feaLib/data/bug568.fea
+Tests/feaLib/data/bug568.ttx
+Tests/feaLib/data/bug633.fea
+Tests/feaLib/data/bug633.ttx
+Tests/feaLib/data/enum.fea
+Tests/feaLib/data/enum.ttx
+Tests/feaLib/data/feature_aalt.fea
+Tests/feaLib/data/feature_aalt.ttx
+Tests/feaLib/data/ignore_pos.fea
+Tests/feaLib/data/ignore_pos.ttx
+Tests/feaLib/data/include0.fea
+Tests/feaLib/data/language_required.fea
+Tests/feaLib/data/language_required.ttx
+Tests/feaLib/data/lookup.fea
+Tests/feaLib/data/lookup.ttx
+Tests/feaLib/data/lookupflag.fea
+Tests/feaLib/data/lookupflag.ttx
+Tests/feaLib/data/markClass.fea
+Tests/feaLib/data/markClass.ttx
+Tests/feaLib/data/mini.fea
+Tests/feaLib/data/multiple_feature_blocks.fea
+Tests/feaLib/data/multiple_feature_blocks.ttx
+Tests/feaLib/data/name.fea
+Tests/feaLib/data/name.ttx
+Tests/feaLib/data/omitted_GlyphClassDef.fea
+Tests/feaLib/data/omitted_GlyphClassDef.ttx
+Tests/feaLib/data/size.fea
+Tests/feaLib/data/size.ttx
+Tests/feaLib/data/size2.fea
+Tests/feaLib/data/size2.ttx
+Tests/feaLib/data/spec10.fea
+Tests/feaLib/data/spec10.ttx
+Tests/feaLib/data/spec4h1.fea
+Tests/feaLib/data/spec4h1.ttx
+Tests/feaLib/data/spec4h2.fea
+Tests/feaLib/data/spec4h2.ttx
+Tests/feaLib/data/spec5d1.fea
+Tests/feaLib/data/spec5d1.ttx
+Tests/feaLib/data/spec5d2.fea
+Tests/feaLib/data/spec5d2.ttx
+Tests/feaLib/data/spec5f_ii_1.fea
+Tests/feaLib/data/spec5f_ii_1.ttx
+Tests/feaLib/data/spec5f_ii_2.fea
+Tests/feaLib/data/spec5f_ii_2.ttx
+Tests/feaLib/data/spec5f_ii_3.fea
+Tests/feaLib/data/spec5f_ii_3.ttx
+Tests/feaLib/data/spec5f_ii_4.fea
+Tests/feaLib/data/spec5f_ii_4.ttx
+Tests/feaLib/data/spec5fi1.fea
+Tests/feaLib/data/spec5fi1.ttx
+Tests/feaLib/data/spec5fi2.fea
+Tests/feaLib/data/spec5fi2.ttx
+Tests/feaLib/data/spec5fi3.fea
+Tests/feaLib/data/spec5fi3.ttx
+Tests/feaLib/data/spec5fi4.fea
+Tests/feaLib/data/spec5fi4.ttx
+Tests/feaLib/data/spec5h1.fea
+Tests/feaLib/data/spec5h1.ttx
+Tests/feaLib/data/spec6b_ii.fea
+Tests/feaLib/data/spec6b_ii.ttx
+Tests/feaLib/data/spec6d2.fea
+Tests/feaLib/data/spec6d2.ttx
+Tests/feaLib/data/spec6e.fea
+Tests/feaLib/data/spec6e.ttx
+Tests/feaLib/data/spec6f.fea
+Tests/feaLib/data/spec6f.ttx
+Tests/feaLib/data/spec6h_ii.fea
+Tests/feaLib/data/spec6h_ii.ttx
+Tests/feaLib/data/spec6h_iii_1.fea
+Tests/feaLib/data/spec6h_iii_1.ttx
+Tests/feaLib/data/spec6h_iii_3d.fea
+Tests/feaLib/data/spec6h_iii_3d.ttx
+Tests/feaLib/data/spec8a.fea
+Tests/feaLib/data/spec8a.ttx
+Tests/feaLib/data/spec8b.fea
+Tests/feaLib/data/spec8b.ttx
+Tests/feaLib/data/spec8c.fea
+Tests/feaLib/data/spec8c.ttx
+Tests/feaLib/data/spec8d.fea
+Tests/feaLib/data/spec8d.ttx
+Tests/feaLib/data/spec9a.fea
+Tests/feaLib/data/spec9a.ttx
+Tests/feaLib/data/spec9b.fea
+Tests/feaLib/data/spec9b.ttx
+Tests/feaLib/data/spec9c1.fea
+Tests/feaLib/data/spec9c1.ttx
+Tests/feaLib/data/spec9c2.fea
+Tests/feaLib/data/spec9c2.ttx
+Tests/feaLib/data/spec9c3.fea
+Tests/feaLib/data/spec9c3.ttx
+Tests/feaLib/data/spec9d.fea
+Tests/feaLib/data/spec9d.ttx
+Tests/feaLib/data/spec9e.fea
+Tests/feaLib/data/spec9e.ttx
+Tests/feaLib/data/spec9f.fea
+Tests/feaLib/data/spec9f.ttx
+Tests/feaLib/data/spec9g.fea
+Tests/feaLib/data/spec9g.ttx
+Tests/feaLib/data/include/include1.fea
+Tests/feaLib/data/include/include3.fea
+Tests/feaLib/data/include/include4.fea
+Tests/feaLib/data/include/include5.fea
+Tests/feaLib/data/include/include6.fea
+Tests/feaLib/data/include/includemissingfile.fea
+Tests/feaLib/data/include/includeself.fea
+Tests/feaLib/data/include/subdir/include2.fea
+Tests/misc/arrayTools_test.py
+Tests/misc/bezierTools_test.py
+Tests/misc/classifyTools_test.py
+Tests/misc/eexec_test.py
+Tests/misc/encodingTools_test.py
+Tests/misc/filenames_test.py
+Tests/misc/fixedTools_test.py
+Tests/misc/loggingTools_test.py
+Tests/misc/macRes_test.py
+Tests/misc/psCharStrings_test.py
+Tests/misc/py23_test.py
+Tests/misc/testTools_test.py
+Tests/misc/textTools_test.py
+Tests/misc/timeTools_test.py
+Tests/misc/transform_test.py
+Tests/misc/xmlReader_test.py
+Tests/misc/xmlWriter_test.py
+Tests/mtiLib/mti_test.py
+Tests/mtiLib/data/featurename-backward.ttx.GSUB
+Tests/mtiLib/data/featurename-backward.txt
+Tests/mtiLib/data/featurename-forward.ttx.GSUB
+Tests/mtiLib/data/featurename-forward.txt
+Tests/mtiLib/data/lookupnames-backward.ttx.GSUB
+Tests/mtiLib/data/lookupnames-backward.txt
+Tests/mtiLib/data/lookupnames-forward.ttx.GSUB
+Tests/mtiLib/data/lookupnames-forward.txt
+Tests/mtiLib/data/mixed-toplevels.ttx.GSUB
+Tests/mtiLib/data/mixed-toplevels.txt
+Tests/mtiLib/data/mti/README
+Tests/mtiLib/data/mti/chained-glyph.ttx.GPOS
+Tests/mtiLib/data/mti/chained-glyph.ttx.GSUB
+Tests/mtiLib/data/mti/chained-glyph.txt
+Tests/mtiLib/data/mti/chainedclass.ttx.GSUB
+Tests/mtiLib/data/mti/chainedclass.txt
+Tests/mtiLib/data/mti/chainedcoverage.ttx.GSUB
+Tests/mtiLib/data/mti/chainedcoverage.txt
+Tests/mtiLib/data/mti/cmap.ttx
+Tests/mtiLib/data/mti/cmap.ttx.cmap
+Tests/mtiLib/data/mti/cmap.txt
+Tests/mtiLib/data/mti/context-glyph.txt
+Tests/mtiLib/data/mti/contextclass.txt
+Tests/mtiLib/data/mti/contextcoverage.txt
+Tests/mtiLib/data/mti/featuretable.txt
+Tests/mtiLib/data/mti/gdefattach.ttx.GDEF
+Tests/mtiLib/data/mti/gdefattach.txt
+Tests/mtiLib/data/mti/gdefclasses.ttx.GDEF
+Tests/mtiLib/data/mti/gdefclasses.txt
+Tests/mtiLib/data/mti/gdefligcaret.ttx.GDEF
+Tests/mtiLib/data/mti/gdefligcaret.txt
+Tests/mtiLib/data/mti/gdefmarkattach.ttx.GDEF
+Tests/mtiLib/data/mti/gdefmarkattach.txt
+Tests/mtiLib/data/mti/gdefmarkfilter.ttx.GDEF
+Tests/mtiLib/data/mti/gdefmarkfilter.txt
+Tests/mtiLib/data/mti/gposcursive.ttx.GPOS
+Tests/mtiLib/data/mti/gposcursive.txt
+Tests/mtiLib/data/mti/gposkernset.ttx.GPOS
+Tests/mtiLib/data/mti/gposkernset.txt
+Tests/mtiLib/data/mti/gposmarktobase.ttx.GPOS
+Tests/mtiLib/data/mti/gposmarktobase.txt
+Tests/mtiLib/data/mti/gpospairclass.ttx.GPOS
+Tests/mtiLib/data/mti/gpospairclass.txt
+Tests/mtiLib/data/mti/gpospairglyph.ttx.GPOS
+Tests/mtiLib/data/mti/gpospairglyph.txt
+Tests/mtiLib/data/mti/gpossingle.ttx.GPOS
+Tests/mtiLib/data/mti/gpossingle.txt
+Tests/mtiLib/data/mti/gsubalternate.ttx.GSUB
+Tests/mtiLib/data/mti/gsubalternate.txt
+Tests/mtiLib/data/mti/gsubligature.ttx.GSUB
+Tests/mtiLib/data/mti/gsubligature.txt
+Tests/mtiLib/data/mti/gsubmultiple.ttx.GSUB
+Tests/mtiLib/data/mti/gsubmultiple.txt
+Tests/mtiLib/data/mti/gsubreversechanined.ttx.GSUB
+Tests/mtiLib/data/mti/gsubreversechanined.txt
+Tests/mtiLib/data/mti/gsubsingle.ttx.GSUB
+Tests/mtiLib/data/mti/gsubsingle.txt
+Tests/mtiLib/data/mti/mark-to-ligature.ttx.GPOS
+Tests/mtiLib/data/mti/mark-to-ligature.txt
+Tests/mtiLib/data/mti/scripttable.ttx.GPOS
+Tests/mtiLib/data/mti/scripttable.ttx.GSUB
+Tests/mtiLib/data/mti/scripttable.txt
+Tests/otlLib/builder_test.py
+Tests/pens/areaPen_test.py
+Tests/pens/basePen_test.py
+Tests/pens/boundsPen_test.py
+Tests/pens/perimeterPen_test.py
+Tests/pens/pointInsidePen_test.py
+Tests/pens/recordingPen_test.py
+Tests/pens/reverseContourPen_test.py
+Tests/pens/t2CharStringPen_test.py
+Tests/pens/ttGlyphPen_test.py
+Tests/subset/subset_test.py
+Tests/subset/data/Lobster.subset.ttx
+Tests/subset/data/NotdefWidthCID-Regular.ttx
+Tests/subset/data/TestANKR.ttx
+Tests/subset/data/TestBSLN-0.ttx
+Tests/subset/data/TestBSLN-1.ttx
+Tests/subset/data/TestBSLN-2.ttx
+Tests/subset/data/TestBSLN-3.ttx
+Tests/subset/data/TestCID-Regular.ttx
+Tests/subset/data/TestCLR-Regular.ttx
+Tests/subset/data/TestGVAR.ttx
+Tests/subset/data/TestLCAR-0.ttx
+Tests/subset/data/TestLCAR-1.ttx
+Tests/subset/data/TestMATH-Regular.ttx
+Tests/subset/data/TestOPBD-0.ttx
+Tests/subset/data/TestOPBD-1.ttx
+Tests/subset/data/TestOTF-Regular.ttx
+Tests/subset/data/TestPROP.ttx
+Tests/subset/data/TestTTF-Regular.ttx
+Tests/subset/data/TestTTF-Regular_non_BMP_char.ttx
+Tests/subset/data/expect_ankr.ttx
+Tests/subset/data/expect_bsln_0.ttx
+Tests/subset/data/expect_bsln_1.ttx
+Tests/subset/data/expect_bsln_2.ttx
+Tests/subset/data/expect_bsln_3.ttx
+Tests/subset/data/expect_desubroutinize_CFF.ttx
+Tests/subset/data/expect_keep_colr.ttx
+Tests/subset/data/expect_keep_gvar.ttx
+Tests/subset/data/expect_keep_gvar_notdef_outline.ttx
+Tests/subset/data/expect_keep_math.ttx
+Tests/subset/data/expect_lcar_0.ttx
+Tests/subset/data/expect_lcar_1.ttx
+Tests/subset/data/expect_no_hinting_CFF.ttx
+Tests/subset/data/expect_no_hinting_TTF.ttx
+Tests/subset/data/expect_no_hinting_desubroutinize_CFF.ttx
+Tests/subset/data/expect_no_notdef_outline_cid.ttx
+Tests/subset/data/expect_no_notdef_outline_otf.ttx
+Tests/subset/data/expect_no_notdef_outline_ttf.ttx
+Tests/subset/data/expect_notdef_width_cid.ttx
+Tests/subset/data/expect_opbd_0.ttx
+Tests/subset/data/expect_opbd_1.ttx
+Tests/subset/data/expect_prop_0.ttx
+Tests/subset/data/expect_prop_1.ttx
+Tests/subset/data/google_color.ttx
+Tests/svgLib/path/__init__.py
+Tests/svgLib/path/parser_test.py
+Tests/svgLib/path/path_test.py
+Tests/t1Lib/t1Lib_test.py
+Tests/t1Lib/data/TestT1-Regular.lwfn
+Tests/t1Lib/data/TestT1-Regular.pfa
+Tests/t1Lib/data/TestT1-Regular.pfb
+Tests/ttLib/sfnt_test.py
+Tests/ttLib/woff2_test.py
+Tests/ttLib/data/TestOTF-Regular.otx
+Tests/ttLib/data/TestTTF-Regular.ttx
+Tests/ttLib/data/TestTTFComplex-Regular.ttx
+Tests/ttLib/data/test_woff2_metadata.xml
+Tests/ttLib/tables/C_F_F__2_test.py
+Tests/ttLib/tables/C_F_F_test.py
+Tests/ttLib/tables/C_P_A_L_test.py
+Tests/ttLib/tables/M_V_A_R_test.py
+Tests/ttLib/tables/O_S_2f_2_test.py
+Tests/ttLib/tables/S_T_A_T_test.py
+Tests/ttLib/tables/T_S_I__0_test.py
+Tests/ttLib/tables/T_S_I__1_test.py
+Tests/ttLib/tables/TupleVariation_test.py
+Tests/ttLib/tables/_a_n_k_r_test.py
+Tests/ttLib/tables/_a_v_a_r_test.py
+Tests/ttLib/tables/_b_s_l_n_test.py
+Tests/ttLib/tables/_c_i_d_g_test.py
+Tests/ttLib/tables/_c_m_a_p_test.py
+Tests/ttLib/tables/_c_v_a_r_test.py
+Tests/ttLib/tables/_f_p_g_m_test.py
+Tests/ttLib/tables/_f_v_a_r_test.py
+Tests/ttLib/tables/_g_c_i_d_test.py
+Tests/ttLib/tables/_g_l_y_f_test.py
+Tests/ttLib/tables/_g_v_a_r_test.py
+Tests/ttLib/tables/_h_h_e_a_test.py
+Tests/ttLib/tables/_h_m_t_x_test.py
+Tests/ttLib/tables/_k_e_r_n_test.py
+Tests/ttLib/tables/_l_c_a_r_test.py
+Tests/ttLib/tables/_l_t_a_g_test.py
+Tests/ttLib/tables/_m_e_t_a_test.py
+Tests/ttLib/tables/_m_o_r_t_test.py
+Tests/ttLib/tables/_m_o_r_x_test.py
+Tests/ttLib/tables/_n_a_m_e_test.py
+Tests/ttLib/tables/_o_p_b_d_test.py
+Tests/ttLib/tables/_p_r_o_p_test.py
+Tests/ttLib/tables/_t_r_a_k_test.py
+Tests/ttLib/tables/_v_h_e_a_test.py
+Tests/ttLib/tables/_v_m_t_x_test.py
+Tests/ttLib/tables/otBase_test.py
+Tests/ttLib/tables/otConverters_test.py
+Tests/ttLib/tables/otTables_test.py
+Tests/ttLib/tables/tables_test.py
+Tests/ttLib/tables/ttProgram_test.py
+Tests/ttLib/tables/data/C_F_F_.bin
+Tests/ttLib/tables/data/C_F_F_.ttx
+Tests/ttLib/tables/data/C_F_F__2.bin
+Tests/ttLib/tables/data/C_F_F__2.ttx
+Tests/ttLib/tables/data/_h_h_e_a_recalc_OTF.ttx
+Tests/ttLib/tables/data/_h_h_e_a_recalc_TTF.ttx
+Tests/ttLib/tables/data/_h_h_e_a_recalc_empty.ttx
+Tests/ttLib/tables/data/_v_h_e_a_recalc_OTF.ttx
+Tests/ttLib/tables/data/_v_h_e_a_recalc_TTF.ttx
+Tests/ttLib/tables/data/_v_h_e_a_recalc_empty.ttx
+Tests/ttLib/tables/data/ttProgram.ttx
+Tests/ttLib/tables/data/aots/README
+Tests/ttLib/tables/data/aots/base.otf
+Tests/ttLib/tables/data/aots/base.ttx.CFF
+Tests/ttLib/tables/data/aots/base.ttx.OS_2
+Tests/ttLib/tables/data/aots/base.ttx.cmap
+Tests/ttLib/tables/data/aots/base.ttx.head
+Tests/ttLib/tables/data/aots/base.ttx.hhea
+Tests/ttLib/tables/data/aots/base.ttx.hmtx
+Tests/ttLib/tables/data/aots/base.ttx.maxp
+Tests/ttLib/tables/data/aots/base.ttx.name
+Tests/ttLib/tables/data/aots/base.ttx.post
+Tests/ttLib/tables/data/aots/classdef1_font1.otf
+Tests/ttLib/tables/data/aots/classdef1_font1.ttx.GSUB
+Tests/ttLib/tables/data/aots/classdef1_font2.otf
+Tests/ttLib/tables/data/aots/classdef1_font2.ttx.GSUB
+Tests/ttLib/tables/data/aots/classdef1_font3.otf
+Tests/ttLib/tables/data/aots/classdef1_font3.ttx.GSUB
+Tests/ttLib/tables/data/aots/classdef1_font4.otf
+Tests/ttLib/tables/data/aots/classdef1_font4.ttx.GSUB
+Tests/ttLib/tables/data/aots/classdef2_font1.otf
+Tests/ttLib/tables/data/aots/classdef2_font1.ttx.GSUB
+Tests/ttLib/tables/data/aots/classdef2_font2.otf
+Tests/ttLib/tables/data/aots/classdef2_font2.ttx.GSUB
+Tests/ttLib/tables/data/aots/classdef2_font3.otf
+Tests/ttLib/tables/data/aots/classdef2_font3.ttx.GSUB
+Tests/ttLib/tables/data/aots/classdef2_font4.otf
+Tests/ttLib/tables/data/aots/classdef2_font4.ttx.GSUB
+Tests/ttLib/tables/data/aots/cmap0_font1.otf
+Tests/ttLib/tables/data/aots/cmap0_font1.ttx.cmap
+Tests/ttLib/tables/data/aots/cmap10_font1.otf
+Tests/ttLib/tables/data/aots/cmap10_font1.ttx.cmap
+Tests/ttLib/tables/data/aots/cmap10_font2.otf
+Tests/ttLib/tables/data/aots/cmap10_font2.ttx.cmap
+Tests/ttLib/tables/data/aots/cmap12_font1.otf
+Tests/ttLib/tables/data/aots/cmap12_font1.ttx.cmap
+Tests/ttLib/tables/data/aots/cmap14_font1.otf
+Tests/ttLib/tables/data/aots/cmap14_font1.ttx.cmap
+Tests/ttLib/tables/data/aots/cmap2_font1.otf
+Tests/ttLib/tables/data/aots/cmap2_font1.ttx.cmap
+Tests/ttLib/tables/data/aots/cmap4_font1.otf
+Tests/ttLib/tables/data/aots/cmap4_font1.ttx.cmap
+Tests/ttLib/tables/data/aots/cmap4_font2.otf
+Tests/ttLib/tables/data/aots/cmap4_font2.ttx.cmap
+Tests/ttLib/tables/data/aots/cmap4_font3.otf
+Tests/ttLib/tables/data/aots/cmap4_font3.ttx.cmap
+Tests/ttLib/tables/data/aots/cmap4_font4.otf
+Tests/ttLib/tables/data/aots/cmap4_font4.ttx.cmap
+Tests/ttLib/tables/data/aots/cmap6_font1.otf
+Tests/ttLib/tables/data/aots/cmap6_font1.ttx.cmap
+Tests/ttLib/tables/data/aots/cmap6_font2.otf
+Tests/ttLib/tables/data/aots/cmap6_font2.ttx.cmap
+Tests/ttLib/tables/data/aots/cmap8_font1.otf
+Tests/ttLib/tables/data/aots/cmap8_font1.ttx.cmap
+Tests/ttLib/tables/data/aots/cmap_composition_font1.otf
+Tests/ttLib/tables/data/aots/cmap_composition_font1.ttx.cmap
+Tests/ttLib/tables/data/aots/cmap_subtableselection_font1.otf
+Tests/ttLib/tables/data/aots/cmap_subtableselection_font1.ttx.cmap
+Tests/ttLib/tables/data/aots/cmap_subtableselection_font2.otf
+Tests/ttLib/tables/data/aots/cmap_subtableselection_font2.ttx.cmap
+Tests/ttLib/tables/data/aots/cmap_subtableselection_font3.otf
+Tests/ttLib/tables/data/aots/cmap_subtableselection_font3.ttx.cmap
+Tests/ttLib/tables/data/aots/cmap_subtableselection_font4.otf
+Tests/ttLib/tables/data/aots/cmap_subtableselection_font4.ttx.cmap
+Tests/ttLib/tables/data/aots/cmap_subtableselection_font5.otf
+Tests/ttLib/tables/data/aots/cmap_subtableselection_font5.ttx.cmap
+Tests/ttLib/tables/data/aots/gpos1_1_lookupflag_f1.otf
+Tests/ttLib/tables/data/aots/gpos1_1_lookupflag_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos1_1_lookupflag_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos1_1_simple_f1.otf
+Tests/ttLib/tables/data/aots/gpos1_1_simple_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos1_1_simple_f2.otf
+Tests/ttLib/tables/data/aots/gpos1_1_simple_f2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos1_1_simple_f3.otf
+Tests/ttLib/tables/data/aots/gpos1_1_simple_f3.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos1_1_simple_f4.otf
+Tests/ttLib/tables/data/aots/gpos1_1_simple_f4.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos1_2_font1.otf
+Tests/ttLib/tables/data/aots/gpos1_2_font1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos1_2_font2.otf
+Tests/ttLib/tables/data/aots/gpos1_2_font2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos1_2_font2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos2_1_font6.otf
+Tests/ttLib/tables/data/aots/gpos2_1_font6.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos2_1_font7.otf
+Tests/ttLib/tables/data/aots/gpos2_1_font7.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f1.otf
+Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f2.otf
+Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos2_1_next_glyph_f1.otf
+Tests/ttLib/tables/data/aots/gpos2_1_next_glyph_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos2_1_next_glyph_f2.otf
+Tests/ttLib/tables/data/aots/gpos2_1_next_glyph_f2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos2_1_simple_f1.otf
+Tests/ttLib/tables/data/aots/gpos2_1_simple_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos2_2_font1.otf
+Tests/ttLib/tables/data/aots/gpos2_2_font1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos2_2_font2.otf
+Tests/ttLib/tables/data/aots/gpos2_2_font2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos2_2_font2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos2_2_font3.otf
+Tests/ttLib/tables/data/aots/gpos2_2_font3.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos2_2_font3.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos2_2_font4.otf
+Tests/ttLib/tables/data/aots/gpos2_2_font4.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos2_2_font5.otf
+Tests/ttLib/tables/data/aots/gpos2_2_font5.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos3_font1.otf
+Tests/ttLib/tables/data/aots/gpos3_font1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos3_font2.otf
+Tests/ttLib/tables/data/aots/gpos3_font2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos3_font2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos3_font3.otf
+Tests/ttLib/tables/data/aots/gpos3_font3.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos3_font3.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos4_lookupflag_f1.otf
+Tests/ttLib/tables/data/aots/gpos4_lookupflag_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos4_lookupflag_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos4_lookupflag_f2.otf
+Tests/ttLib/tables/data/aots/gpos4_lookupflag_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos4_lookupflag_f2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos4_multiple_anchors_1.otf
+Tests/ttLib/tables/data/aots/gpos4_multiple_anchors_1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos4_multiple_anchors_1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos4_simple_1.otf
+Tests/ttLib/tables/data/aots/gpos4_simple_1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos4_simple_1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos5_font1.otf
+Tests/ttLib/tables/data/aots/gpos5_font1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos5_font1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos5_font1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gpos6_font1.otf
+Tests/ttLib/tables/data/aots/gpos6_font1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos6_font1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos7_1_font1.otf
+Tests/ttLib/tables/data/aots/gpos7_1_font1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos9_font1.otf
+Tests/ttLib/tables/data/aots/gpos9_font1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos9_font2.otf
+Tests/ttLib/tables/data/aots/gpos9_font2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f1.otf
+Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f2.otf
+Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f3.otf
+Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f3.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f3.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f4.otf
+Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f4.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f4.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining1_lookupflag_f1.otf
+Tests/ttLib/tables/data/aots/gpos_chaining1_lookupflag_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining1_lookupflag_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining1_multiple_subrules_f1.otf
+Tests/ttLib/tables/data/aots/gpos_chaining1_multiple_subrules_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining1_multiple_subrules_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining1_multiple_subrules_f2.otf
+Tests/ttLib/tables/data/aots/gpos_chaining1_multiple_subrules_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining1_multiple_subrules_f2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining1_next_glyph_f1.otf
+Tests/ttLib/tables/data/aots/gpos_chaining1_next_glyph_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining1_next_glyph_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f1.otf
+Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f2.otf
+Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining1_successive_f1.otf
+Tests/ttLib/tables/data/aots/gpos_chaining1_successive_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining1_successive_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f1.otf
+Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f2.otf
+Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f3.otf
+Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f3.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f3.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f4.otf
+Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f4.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f4.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining2_lookupflag_f1.otf
+Tests/ttLib/tables/data/aots/gpos_chaining2_lookupflag_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining2_lookupflag_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining2_multiple_subrules_f1.otf
+Tests/ttLib/tables/data/aots/gpos_chaining2_multiple_subrules_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining2_multiple_subrules_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining2_multiple_subrules_f2.otf
+Tests/ttLib/tables/data/aots/gpos_chaining2_multiple_subrules_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining2_multiple_subrules_f2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining2_next_glyph_f1.otf
+Tests/ttLib/tables/data/aots/gpos_chaining2_next_glyph_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining2_next_glyph_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f1.otf
+Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f2.otf
+Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining2_successive_f1.otf
+Tests/ttLib/tables/data/aots/gpos_chaining2_successive_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining2_successive_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f1.otf
+Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f2.otf
+Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f3.otf
+Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f3.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f3.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f4.otf
+Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f4.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f4.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining3_lookupflag_f1.otf
+Tests/ttLib/tables/data/aots/gpos_chaining3_lookupflag_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining3_lookupflag_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining3_next_glyph_f1.otf
+Tests/ttLib/tables/data/aots/gpos_chaining3_next_glyph_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining3_next_glyph_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f1.otf
+Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f2.otf
+Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_chaining3_successive_f1.otf
+Tests/ttLib/tables/data/aots/gpos_chaining3_successive_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_chaining3_successive_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context1_boundary_f1.otf
+Tests/ttLib/tables/data/aots/gpos_context1_boundary_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context1_boundary_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context1_boundary_f2.otf
+Tests/ttLib/tables/data/aots/gpos_context1_boundary_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context1_boundary_f2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context1_expansion_f1.otf
+Tests/ttLib/tables/data/aots/gpos_context1_expansion_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context1_expansion_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f1.otf
+Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f2.otf
+Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context1_multiple_subrules_f1.otf
+Tests/ttLib/tables/data/aots/gpos_context1_multiple_subrules_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context1_multiple_subrules_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context1_multiple_subrules_f2.otf
+Tests/ttLib/tables/data/aots/gpos_context1_multiple_subrules_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context1_multiple_subrules_f2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context1_next_glyph_f1.otf
+Tests/ttLib/tables/data/aots/gpos_context1_next_glyph_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context1_next_glyph_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context1_simple_f1.otf
+Tests/ttLib/tables/data/aots/gpos_context1_simple_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context1_simple_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context1_simple_f2.otf
+Tests/ttLib/tables/data/aots/gpos_context1_simple_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context1_simple_f2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context1_successive_f1.otf
+Tests/ttLib/tables/data/aots/gpos_context1_successive_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context1_successive_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context2_boundary_f1.otf
+Tests/ttLib/tables/data/aots/gpos_context2_boundary_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context2_boundary_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context2_boundary_f2.otf
+Tests/ttLib/tables/data/aots/gpos_context2_boundary_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context2_boundary_f2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context2_classes_f1.otf
+Tests/ttLib/tables/data/aots/gpos_context2_classes_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context2_classes_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context2_classes_f2.otf
+Tests/ttLib/tables/data/aots/gpos_context2_classes_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context2_classes_f2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context2_expansion_f1.otf
+Tests/ttLib/tables/data/aots/gpos_context2_expansion_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context2_expansion_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f1.otf
+Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f2.otf
+Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context2_multiple_subrules_f1.otf
+Tests/ttLib/tables/data/aots/gpos_context2_multiple_subrules_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context2_multiple_subrules_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context2_multiple_subrules_f2.otf
+Tests/ttLib/tables/data/aots/gpos_context2_multiple_subrules_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context2_multiple_subrules_f2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context2_next_glyph_f1.otf
+Tests/ttLib/tables/data/aots/gpos_context2_next_glyph_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context2_next_glyph_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context2_simple_f1.otf
+Tests/ttLib/tables/data/aots/gpos_context2_simple_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context2_simple_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context2_simple_f2.otf
+Tests/ttLib/tables/data/aots/gpos_context2_simple_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context2_simple_f2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context2_successive_f1.otf
+Tests/ttLib/tables/data/aots/gpos_context2_successive_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context2_successive_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context3_boundary_f1.otf
+Tests/ttLib/tables/data/aots/gpos_context3_boundary_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context3_boundary_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context3_boundary_f2.otf
+Tests/ttLib/tables/data/aots/gpos_context3_boundary_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context3_boundary_f2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f1.otf
+Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f2.otf
+Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f2.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context3_next_glyph_f1.otf
+Tests/ttLib/tables/data/aots/gpos_context3_next_glyph_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context3_next_glyph_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context3_simple_f1.otf
+Tests/ttLib/tables/data/aots/gpos_context3_simple_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context3_simple_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gpos_context3_successive_f1.otf
+Tests/ttLib/tables/data/aots/gpos_context3_successive_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gpos_context3_successive_f1.ttx.GPOS
+Tests/ttLib/tables/data/aots/gsub1_1_lookupflag_f1.otf
+Tests/ttLib/tables/data/aots/gsub1_1_lookupflag_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub1_1_lookupflag_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub1_1_modulo_f1.otf
+Tests/ttLib/tables/data/aots/gsub1_1_modulo_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub1_1_simple_f1.otf
+Tests/ttLib/tables/data/aots/gsub1_1_simple_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub1_2_lookupflag_f1.otf
+Tests/ttLib/tables/data/aots/gsub1_2_lookupflag_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub1_2_lookupflag_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub1_2_simple_f1.otf
+Tests/ttLib/tables/data/aots/gsub1_2_simple_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub2_1_lookupflag_f1.otf
+Tests/ttLib/tables/data/aots/gsub2_1_lookupflag_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub2_1_lookupflag_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub2_1_multiple_sequences_f1.otf
+Tests/ttLib/tables/data/aots/gsub2_1_multiple_sequences_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub2_1_simple_f1.otf
+Tests/ttLib/tables/data/aots/gsub2_1_simple_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub3_1_lookupflag_f1.otf
+Tests/ttLib/tables/data/aots/gsub3_1_lookupflag_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub3_1_lookupflag_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub3_1_multiple_f1.otf
+Tests/ttLib/tables/data/aots/gsub3_1_multiple_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub3_1_simple_f1.otf
+Tests/ttLib/tables/data/aots/gsub3_1_simple_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub4_1_lookupflag_f1.otf
+Tests/ttLib/tables/data/aots/gsub4_1_lookupflag_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub4_1_lookupflag_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub4_1_multiple_ligatures_f1.otf
+Tests/ttLib/tables/data/aots/gsub4_1_multiple_ligatures_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub4_1_multiple_ligatures_f2.otf
+Tests/ttLib/tables/data/aots/gsub4_1_multiple_ligatures_f2.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub4_1_multiple_ligsets_f1.otf
+Tests/ttLib/tables/data/aots/gsub4_1_multiple_ligsets_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub4_1_simple_f1.otf
+Tests/ttLib/tables/data/aots/gsub4_1_simple_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub7_font1.otf
+Tests/ttLib/tables/data/aots/gsub7_font1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub7_font2.otf
+Tests/ttLib/tables/data/aots/gsub7_font2.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f1.otf
+Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f2.otf
+Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f2.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f3.otf
+Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f3.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f3.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f4.otf
+Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f4.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f4.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining1_lookupflag_f1.otf
+Tests/ttLib/tables/data/aots/gsub_chaining1_lookupflag_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining1_lookupflag_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining1_multiple_subrules_f1.otf
+Tests/ttLib/tables/data/aots/gsub_chaining1_multiple_subrules_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining1_multiple_subrules_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining1_multiple_subrules_f2.otf
+Tests/ttLib/tables/data/aots/gsub_chaining1_multiple_subrules_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining1_multiple_subrules_f2.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining1_next_glyph_f1.otf
+Tests/ttLib/tables/data/aots/gsub_chaining1_next_glyph_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining1_next_glyph_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f1.otf
+Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f2.otf
+Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f2.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining1_successive_f1.otf
+Tests/ttLib/tables/data/aots/gsub_chaining1_successive_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining1_successive_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f1.otf
+Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f2.otf
+Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f2.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f3.otf
+Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f3.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f3.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f4.otf
+Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f4.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f4.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining2_lookupflag_f1.otf
+Tests/ttLib/tables/data/aots/gsub_chaining2_lookupflag_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining2_lookupflag_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining2_multiple_subrules_f1.otf
+Tests/ttLib/tables/data/aots/gsub_chaining2_multiple_subrules_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining2_multiple_subrules_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining2_multiple_subrules_f2.otf
+Tests/ttLib/tables/data/aots/gsub_chaining2_multiple_subrules_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining2_multiple_subrules_f2.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining2_next_glyph_f1.otf
+Tests/ttLib/tables/data/aots/gsub_chaining2_next_glyph_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining2_next_glyph_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f1.otf
+Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f2.otf
+Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f2.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining2_successive_f1.otf
+Tests/ttLib/tables/data/aots/gsub_chaining2_successive_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining2_successive_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f1.otf
+Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f2.otf
+Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f2.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f3.otf
+Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f3.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f3.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f4.otf
+Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f4.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f4.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining3_lookupflag_f1.otf
+Tests/ttLib/tables/data/aots/gsub_chaining3_lookupflag_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining3_lookupflag_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining3_next_glyph_f1.otf
+Tests/ttLib/tables/data/aots/gsub_chaining3_next_glyph_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining3_next_glyph_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f1.otf
+Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f2.otf
+Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f2.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_chaining3_successive_f1.otf
+Tests/ttLib/tables/data/aots/gsub_chaining3_successive_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_chaining3_successive_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context1_boundary_f1.otf
+Tests/ttLib/tables/data/aots/gsub_context1_boundary_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context1_boundary_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context1_boundary_f2.otf
+Tests/ttLib/tables/data/aots/gsub_context1_boundary_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context1_boundary_f2.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context1_expansion_f1.otf
+Tests/ttLib/tables/data/aots/gsub_context1_expansion_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context1_expansion_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f1.otf
+Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f2.otf
+Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f2.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context1_multiple_subrules_f1.otf
+Tests/ttLib/tables/data/aots/gsub_context1_multiple_subrules_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context1_multiple_subrules_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context1_multiple_subrules_f2.otf
+Tests/ttLib/tables/data/aots/gsub_context1_multiple_subrules_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context1_multiple_subrules_f2.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context1_next_glyph_f1.otf
+Tests/ttLib/tables/data/aots/gsub_context1_next_glyph_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context1_next_glyph_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context1_simple_f1.otf
+Tests/ttLib/tables/data/aots/gsub_context1_simple_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context1_simple_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context1_simple_f2.otf
+Tests/ttLib/tables/data/aots/gsub_context1_simple_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context1_simple_f2.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context1_successive_f1.otf
+Tests/ttLib/tables/data/aots/gsub_context1_successive_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context1_successive_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context2_boundary_f1.otf
+Tests/ttLib/tables/data/aots/gsub_context2_boundary_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context2_boundary_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context2_boundary_f2.otf
+Tests/ttLib/tables/data/aots/gsub_context2_boundary_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context2_boundary_f2.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context2_classes_f1.otf
+Tests/ttLib/tables/data/aots/gsub_context2_classes_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context2_classes_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context2_classes_f2.otf
+Tests/ttLib/tables/data/aots/gsub_context2_classes_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context2_classes_f2.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context2_expansion_f1.otf
+Tests/ttLib/tables/data/aots/gsub_context2_expansion_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context2_expansion_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f1.otf
+Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f2.otf
+Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f2.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context2_multiple_subrules_f1.otf
+Tests/ttLib/tables/data/aots/gsub_context2_multiple_subrules_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context2_multiple_subrules_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context2_multiple_subrules_f2.otf
+Tests/ttLib/tables/data/aots/gsub_context2_multiple_subrules_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context2_multiple_subrules_f2.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context2_next_glyph_f1.otf
+Tests/ttLib/tables/data/aots/gsub_context2_next_glyph_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context2_next_glyph_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context2_simple_f1.otf
+Tests/ttLib/tables/data/aots/gsub_context2_simple_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context2_simple_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context2_simple_f2.otf
+Tests/ttLib/tables/data/aots/gsub_context2_simple_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context2_simple_f2.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context2_successive_f1.otf
+Tests/ttLib/tables/data/aots/gsub_context2_successive_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context2_successive_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context3_boundary_f1.otf
+Tests/ttLib/tables/data/aots/gsub_context3_boundary_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context3_boundary_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context3_boundary_f2.otf
+Tests/ttLib/tables/data/aots/gsub_context3_boundary_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context3_boundary_f2.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f1.otf
+Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f2.otf
+Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f2.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f2.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context3_next_glyph_f1.otf
+Tests/ttLib/tables/data/aots/gsub_context3_next_glyph_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context3_next_glyph_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context3_simple_f1.otf
+Tests/ttLib/tables/data/aots/gsub_context3_simple_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context3_simple_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/gsub_context3_successive_f1.otf
+Tests/ttLib/tables/data/aots/gsub_context3_successive_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/gsub_context3_successive_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/lookupflag_ignore_attach_f1.otf
+Tests/ttLib/tables/data/aots/lookupflag_ignore_attach_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/lookupflag_ignore_attach_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/lookupflag_ignore_base_f1.otf
+Tests/ttLib/tables/data/aots/lookupflag_ignore_base_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/lookupflag_ignore_base_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/lookupflag_ignore_combination_f1.otf
+Tests/ttLib/tables/data/aots/lookupflag_ignore_combination_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/lookupflag_ignore_combination_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/lookupflag_ignore_ligatures_f1.otf
+Tests/ttLib/tables/data/aots/lookupflag_ignore_ligatures_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/lookupflag_ignore_ligatures_f1.ttx.GSUB
+Tests/ttLib/tables/data/aots/lookupflag_ignore_marks_f1.otf
+Tests/ttLib/tables/data/aots/lookupflag_ignore_marks_f1.ttx.GDEF
+Tests/ttLib/tables/data/aots/lookupflag_ignore_marks_f1.ttx.GSUB
+Tests/ttLib/tables/data/graphite/graphite_tests.ttf
+Tests/ttLib/tables/data/graphite/graphite_tests.ttx.Feat
+Tests/ttLib/tables/data/graphite/graphite_tests.ttx.Glat
+Tests/ttLib/tables/data/graphite/graphite_tests.ttx.Glat.setup
+Tests/ttLib/tables/data/graphite/graphite_tests.ttx.Silf
+Tests/ttLib/tables/data/graphite/graphite_tests.ttx.Silf.setup
+Tests/ttLib/tables/data/graphite/graphite_tests.ttx.Sill
+Tests/ttx/ttx_test.py
+Tests/ttx/data/TestBOM.ttx
+Tests/ttx/data/TestDFONT.dfont
+Tests/ttx/data/TestNoSFNT.ttx
+Tests/ttx/data/TestNoXML.ttx
+Tests/ttx/data/TestOTF.otf
+Tests/ttx/data/TestOTF.ttx
+Tests/ttx/data/TestTTC.ttc
+Tests/ttx/data/TestTTF.ttf
+Tests/ttx/data/TestTTF.ttx
+Tests/ttx/data/TestWOFF.woff
+Tests/ttx/data/TestWOFF2.woff2
+Tests/varLib/__init__.py
+Tests/varLib/builder_test.py
+Tests/varLib/designspace_test.py
+Tests/varLib/interpolatable_test.py
+Tests/varLib/interpolate_layout_test.py
+Tests/varLib/models_test.py
+Tests/varLib/mutator_test.py
+Tests/varLib/varLib_test.py
+Tests/varLib/data/Build.designspace
+Tests/varLib/data/BuildAvarEmptyAxis.designspace
+Tests/varLib/data/BuildAvarIdentityMaps.designspace
+Tests/varLib/data/BuildAvarSingleAxis.designspace
+Tests/varLib/data/Designspace.designspace
+Tests/varLib/data/Designspace2.designspace
+Tests/varLib/data/InterpolateLayout.designspace
+Tests/varLib/data/InterpolateLayout2.designspace
+Tests/varLib/data/InterpolateLayout3.designspace
+Tests/varLib/data/master_ttx_interpolatable_otf/TestFamily2-Master0.ttx
+Tests/varLib/data/master_ttx_interpolatable_otf/TestFamily2-Master1.ttx
+Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master0.ttx
+Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master1.ttx
+Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master2.ttx
+Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master3.ttx
+Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master4.ttx
+Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily2-Master0.ttx
+Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily2-Master1.ttx
+Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Bold.ttx
+Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Condensed.ttx
+Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-CondensedBold.ttx
+Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-CondensedLight.ttx
+Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-CondensedSemiBold.ttx
+Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Light.ttx
+Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Regular.ttx
+Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-SemiBold.ttx
+Tests/varLib/data/master_ttx_varfont_ttf/Mutator_IUP.ttx
+Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/features.fea
+Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/fontinfo.plist
+Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/lib.plist
+Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/metainfo.plist
+Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/A_.glif
+Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/_notdef.glif
+Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/a.glif
+Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/contents.plist
+Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/dollar.glif
+Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/dollar.nostroke.glif
+Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/space.glif
+Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/features.fea
+Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/fontinfo.plist
+Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/lib.plist
+Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/metainfo.plist
+Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/A_.glif
+Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/_notdef.glif
+Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/a.glif
+Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/contents.plist
+Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/dollar.glif
+Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/dollar.nostroke.glif
+Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/space.glif
+Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/features.fea
+Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/fontinfo.plist
+Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/lib.plist
+Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/metainfo.plist
+Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/A_.glif
+Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/_notdef.glif
+Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/a.glif
+Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/contents.plist
+Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/dollar.glif
+Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/dollar.nostroke.glif
+Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/space.glif
+Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/features.fea
+Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/fontinfo.plist
+Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/lib.plist
+Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/metainfo.plist
+Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/A_.glif
+Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/_notdef.glif
+Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/a.glif
+Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/contents.plist
+Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/dollar.glif
+Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/dollar.nostroke.glif
+Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/space.glif
+Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/features.fea
+Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/fontinfo.plist
+Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/lib.plist
+Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/metainfo.plist
+Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/A_.glif
+Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/_notdef.glif
+Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/a.glif
+Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/contents.plist
+Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/dollar.glif
+Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/dollar.nostroke.glif
+Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/space.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/features.fea
+Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/fontinfo.plist
+Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/lib.plist
+Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/metainfo.plist
+Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/A_.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/A_.sc.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/_notdef.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/a.alt.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/a.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/ampersand.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/atilde.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/circledotted.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/contents.plist
+Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/d.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/dieresisbelowcmb.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/dieresiscmb.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/f.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/f_t.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/n.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/space.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/t.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/tildebelowcmb.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/tildecmb.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/features.fea
+Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/fontinfo.plist
+Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/lib.plist
+Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/metainfo.plist
+Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/A_.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/A_.sc.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/_notdef.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/a.alt.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/a.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/ampersand.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/atilde.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/circledotted.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/contents.plist
+Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/d.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/dieresisbelowcmb.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/dieresiscmb.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/f.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/f_t.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/n.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/space.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/t.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/tildebelowcmb.glif
+Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/tildecmb.glif
+Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/fontinfo.plist
+Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/groups.plist
+Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/kerning.plist
+Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/layercontents.plist
+Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/lib.plist
+Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/metainfo.plist
+Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/F_.glif
+Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/T_.glif
+Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/contents.plist
+Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/l.glif
+Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/layerinfo.plist
+Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/n.glif
+Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/o.glif
+Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/s.glif
+Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/t.glif
+Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/fontinfo.plist
+Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/groups.plist
+Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/kerning.plist
+Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/layercontents.plist
+Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/lib.plist
+Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/metainfo.plist
+Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/F_.glif
+Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/T_.glif
+Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/contents.plist
+Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/l.glif
+Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/layerinfo.plist
+Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/n.glif
+Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/o.glif
+Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/s.glif
+Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/t.glif
+Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/fontinfo.plist
+Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/groups.plist
+Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/kerning.plist
+Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/layercontents.plist
+Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/lib.plist
+Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/metainfo.plist
+Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/F_.glif
+Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/T_.glif
+Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/contents.plist
+Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/l.glif
+Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/layerinfo.plist
+Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/n.glif
+Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/o.glif
+Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/s.glif
+Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/t.glif
+Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/fontinfo.plist
+Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/groups.plist
+Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/kerning.plist
+Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/layercontents.plist
+Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/lib.plist
+Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/metainfo.plist
+Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/F_.glif
+Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/T_.glif
+Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/contents.plist
+Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/l.glif
+Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/layerinfo.plist
+Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/n.glif
+Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/o.glif
+Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/s.glif
+Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/t.glif
+Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/fontinfo.plist
+Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/groups.plist
+Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/kerning.plist
+Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/layercontents.plist
+Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/lib.plist
+Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/metainfo.plist
+Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/F_.glif
+Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/T_.glif
+Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/contents.plist
+Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/l.glif
+Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/layerinfo.plist
+Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/n.glif
+Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/o.glif
+Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/s.glif
+Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/t.glif
+Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/fontinfo.plist
+Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/groups.plist
+Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/kerning.plist
+Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/layercontents.plist
+Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/lib.plist
+Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/metainfo.plist
+Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/F_.glif
+Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/T_.glif
+Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/contents.plist
+Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/l.glif
+Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/layerinfo.plist
+Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/n.glif
+Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/o.glif
+Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/s.glif
+Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/t.glif
+Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/fontinfo.plist
+Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/groups.plist
+Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/kerning.plist
+Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/layercontents.plist
+Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/lib.plist
+Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/metainfo.plist
+Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/F_.glif
+Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/T_.glif
+Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/contents.plist
+Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/l.glif
+Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/layerinfo.plist
+Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/n.glif
+Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/o.glif
+Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/s.glif
+Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/t.glif
+Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/fontinfo.plist
+Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/groups.plist
+Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/kerning.plist
+Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/layercontents.plist
+Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/lib.plist
+Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/metainfo.plist
+Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/F_.glif
+Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/T_.glif
+Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/contents.plist
+Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/l.glif
+Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/layerinfo.plist
+Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/n.glif
+Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/o.glif
+Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/s.glif
+Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/t.glif
+Tests/varLib/data/test_results/Build.ttx
+Tests/varLib/data/test_results/Build3.ttx
+Tests/varLib/data/test_results/BuildAvarEmptyAxis.ttx
+Tests/varLib/data/test_results/BuildAvarIdentityMaps.ttx
+Tests/varLib/data/test_results/BuildAvarSingleAxis.ttx
+Tests/varLib/data/test_results/BuildMain.ttx
+Tests/varLib/data/test_results/InterpolateLayout.ttx
+Tests/varLib/data/test_results/InterpolateLayout2.ttx
+Tests/varLib/data/test_results/InterpolateLayoutGPOS_1_diff.ttx
+Tests/varLib/data/test_results/InterpolateLayoutGPOS_1_diff2.ttx
+Tests/varLib/data/test_results/InterpolateLayoutGPOS_1_same.ttx
+Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_class_diff.ttx
+Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_class_diff2.ttx
+Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_class_same.ttx
+Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_spec_diff.ttx
+Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_spec_diff2.ttx
+Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_spec_same.ttx
+Tests/varLib/data/test_results/InterpolateLayoutGPOS_3_diff.ttx
+Tests/varLib/data/test_results/InterpolateLayoutGPOS_3_same.ttx
+Tests/varLib/data/test_results/InterpolateLayoutGPOS_4_diff.ttx
+Tests/varLib/data/test_results/InterpolateLayoutGPOS_4_same.ttx
+Tests/varLib/data/test_results/InterpolateLayoutGPOS_5_diff.ttx
+Tests/varLib/data/test_results/InterpolateLayoutGPOS_5_same.ttx
+Tests/varLib/data/test_results/InterpolateLayoutGPOS_6_diff.ttx
+Tests/varLib/data/test_results/InterpolateLayoutGPOS_6_same.ttx
+Tests/varLib/data/test_results/InterpolateLayoutGPOS_8_diff.ttx
+Tests/varLib/data/test_results/InterpolateLayoutGPOS_8_same.ttx
+Tests/varLib/data/test_results/InterpolateLayoutGPOS_size_feat_same.ttx
+Tests/varLib/data/test_results/InterpolateLayoutMain.ttx
+Tests/varLib/data/test_results/Mutator.ttx
+Tests/varLib/data/test_results/Mutator_IUP-instance.ttx
+Tests/voltLib/lexer_test.py
+Tests/voltLib/parser_test.py
\ No newline at end of file
diff --git a/Lib/fonttools.egg-info/dependency_links.txt b/Lib/fonttools.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/Lib/fonttools.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/Lib/fonttools.egg-info/entry_points.txt b/Lib/fonttools.egg-info/entry_points.txt
new file mode 100644
index 0000000..6d70774
--- /dev/null
+++ b/Lib/fonttools.egg-info/entry_points.txt
@@ -0,0 +1,7 @@
+[console_scripts]
+fonttools = fontTools.__main__:main
+pyftinspect = fontTools.inspect:main
+pyftmerge = fontTools.merge:main
+pyftsubset = fontTools.subset:main
+ttx = fontTools.ttx:main
+
diff --git a/Lib/fonttools.egg-info/top_level.txt b/Lib/fonttools.egg-info/top_level.txt
new file mode 100644
index 0000000..9af65ba
--- /dev/null
+++ b/Lib/fonttools.egg-info/top_level.txt
@@ -0,0 +1 @@
+fontTools
diff --git a/MANIFEST.in b/MANIFEST.in
index db16aa2..1529112 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,13 +1,34 @@
-include LICENSE.txt
-include MANIFEST.in
-include Doc/ttx.1
-include Doc/ChangeLog
-include Doc/*.txt
-include Doc/*.html
+include README.rst
+include LICENSE
+include LICENSE.external
+include NEWS.rst
+include Makefile
+include fonttools
+include Snippets/*.py
+include Snippets/README.md
 include MetaTools/*.py
-include Windows/mcmillan.bat
-include Windows/ttx.ico
-include Windows/README.TXT
-include Windows/fonttools-win-setup.iss
-include Windows/fonttools-win-setup.txt
 include Lib/fontTools/ttLib/tables/table_API_readme.txt
+
+include *requirements.txt
+include tox.ini
+include run-tests.sh
+
+include .appveyor.yml
+include .codecov.yml
+include .coveragerc
+include .travis.yml
+recursive-include .travis *.sh
+
+include Doc/Makefile
+include Doc/make.bat
+recursive-include Doc/man/man1 *.1
+recursive-include Doc/source *.py *.rst
+
+recursive-include Tests *.py *.ttx *.otx *.fea *.feax
+recursive-include Tests *.ttc *.ttf *.dfont *.woff *.woff2
+recursive-include Tests *.otf *.ttx.*
+recursive-include Tests *.glif *.plist
+recursive-include Tests *.txt README
+recursive-include Tests *.lwfn *.pfa *.pfb
+recursive-include Tests *.xml *.designspace *.bin
+recursive-include Tests *.afm
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..f5503e3
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,18 @@
+name: "fonttools"
+description: "fontTools is a library for manipulating fonts, written in Python."
+third_party {
+  url {
+    type: HOMEPAGE
+    value: "https://github.com/fonttools/fonttools"
+  }
+  url {
+    type: ARCHIVE
+    value: "https://github.com/fonttools/fonttools/releases/download/3.28.0/fonttools-3.28.0.zip"
+  }
+  version: "3.28.0"
+  last_upgrade_date {
+    year: 2018
+    month: 7
+    day: 3
+  }
+}
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..bd91d7f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,22 @@
+all:
+	./setup.py build
+
+dist:
+	./setup.py sdist bdist_wheel
+
+install:
+	pip install --ignore-installed .
+
+install-user:
+	pip install --ignore-installed --user .
+
+uninstall:
+	pip uninstall --yes fonttools
+
+check: all
+	./run-tests.sh
+
+clean:
+	./setup.py clean --all
+
+.PHONY: all dist install install-user uninstall check clean
diff --git a/MetaTools/buildChangeLog.py b/MetaTools/buildChangeLog.py
deleted file mode 100755
index d29e379..0000000
--- a/MetaTools/buildChangeLog.py
+++ /dev/null
@@ -1,10 +0,0 @@
-#! /usr/bin/env python
-
-import os, sys
-
-fontToolsDir = os.path.dirname(os.path.dirname(os.path.normpath(
-		os.path.join(os.getcwd(), sys.argv[0]))))
-
-os.chdir(fontToolsDir)
-os.system("git2cl > Doc/ChangeLog")
-print("done.")
diff --git a/MetaTools/buildTableList.py b/MetaTools/buildTableList.py
index 1e77492..de8a039 100755
--- a/MetaTools/buildTableList.py
+++ b/MetaTools/buildTableList.py
@@ -4,13 +4,14 @@
 import os
 import glob
 from fontTools.ttLib import identifierToTag
+import textwrap
 
 
 fontToolsDir = os.path.dirname(os.path.dirname(os.path.join(os.getcwd(), sys.argv[0])))
 fontToolsDir= os.path.normpath(fontToolsDir)
 tablesDir = os.path.join(fontToolsDir,
 		"Lib", "fontTools", "ttLib", "tables")
-docFile = os.path.join(fontToolsDir, "Doc", "documentation.html")
+docFile = os.path.join(fontToolsDir, "README.rst")
 
 names = glob.glob1(tablesDir, "*.py")
 
@@ -31,25 +32,42 @@
 
 file = open(os.path.join(tablesDir, "__init__.py"), "w")
 
-file.write("# DON'T EDIT! This file is generated by MetaTools/buildTableList.py.\n")
-file.write("def _moduleFinderHint():\n")
-file.write('\t"""Dummy function to let modulefinder know what tables may be\n')
-file.write('\tdynamically imported. Generated by MetaTools/buildTableList.py.\n')
-file.write('\t"""\n')
+file.write('''
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+
+# DON'T EDIT! This file is generated by MetaTools/buildTableList.py.
+def _moduleFinderHint():
+	"""Dummy function to let modulefinder know what tables may be
+	dynamically imported. Generated by MetaTools/buildTableList.py.
+
+		>>> _moduleFinderHint()
+	"""
+''')
+
 for module in modules:
 	file.write("\tfrom . import %s\n" % module)
 
+file.write('''
+if __name__ == "__main__":
+	import doctest, sys
+	sys.exit(doctest.testmod().failed)
+''')
+
 file.close()
 
 
-begin = "<!-- begin table list -->"
-end = "<!-- end table list -->"
+begin = ".. begin table list\n.. code::\n"
+end = ".. end table list"
 doc = open(docFile).read()
 beginPos = doc.find(begin)
 assert beginPos > 0
 beginPos = beginPos + len(begin) + 1
 endPos = doc.find(end)
 
-doc = doc[:beginPos] + ", ".join(tables[:-1]) + " and " + tables[-1] + "\n" + doc[endPos:]
+lines = textwrap.wrap(", ".join(tables[:-1]) + " and " + tables[-1], 66)
+blockquote = "\n".join(" "*4 + line for line in lines) + "\n"
+
+doc = doc[:beginPos] + blockquote + doc[endPos:]
 
 open(docFile, "w").write(doc)
diff --git a/MetaTools/buildUCD.py b/MetaTools/buildUCD.py
new file mode 100755
index 0000000..12bd58f
--- /dev/null
+++ b/MetaTools/buildUCD.py
@@ -0,0 +1,293 @@
+#!/usr/bin/env python
+"""
+Tools to parse data files from the Unicode Character Database.
+"""
+
+from __future__ import print_function, absolute_import, division
+from __future__ import unicode_literals
+
+try:
+    from urllib.request import urlopen
+except ImportError:
+    from urllib2 import urlopen
+from contextlib import closing, contextmanager
+import re
+from codecs import iterdecode
+import logging
+import os
+from io import open
+from os.path import abspath, dirname, join as pjoin, pardir, sep
+
+
+try:  # pragma: no cover
+    unicode
+except NameError:
+    unicode = str
+
+
+UNIDATA_URL = "https://unicode.org/Public/UNIDATA/"
+UNIDATA_LICENSE_URL = "http://unicode.org/copyright.html#License"
+
+# by default save output files to ../Lib/fontTools/unicodedata/
+UNIDATA_PATH = pjoin(abspath(dirname(__file__)), pardir,
+                     "Lib", "fontTools", "unicodedata") + sep
+
+SRC_ENCODING = "# -*- coding: utf-8 -*-\n"
+
+NOTICE = "# NOTE: This file was auto-generated with MetaTools/buildUCD.py.\n"
+
+MAX_UNICODE = 0x10FFFF
+
+log = logging.getLogger()
+
+
+@contextmanager
+def open_unidata_file(filename):
+    """Open a text file from https://unicode.org/Public/UNIDATA/"""
+    url = UNIDATA_URL + filename
+    with closing(urlopen(url)) as response:
+        yield iterdecode(response, encoding="utf-8")
+
+
+def parse_unidata_header(infile):
+    """Read the top header of data files, until the first line
+    that does not start with '#'.
+    """
+    header = []
+    line = next(infile)
+    while line.startswith("#"):
+        header.append(line)
+        line = next(infile)
+    return "".join(header)
+
+
+def parse_range_properties(infile, default=None, is_set=False):
+    """Parse a Unicode data file containing a column with one character or
+    a range of characters, and another column containing a property value
+    separated by a semicolon. Comments after '#' are ignored.
+
+    If the ranges defined in the data file are not continuous, assign the
+    'default' property to the unassigned codepoints.
+
+    Return a list of (start, end, property_name) tuples.
+    """
+    ranges = []
+    line_regex = re.compile(
+        r"^"
+        r"([0-9A-F]{4,6})"  # first character code
+        r"(?:\.\.([0-9A-F]{4,6}))?"  # optional second character code
+        r"\s*;\s*"
+        r"([^#]+)")  # everything up to the potential comment
+    for line in infile:
+        match = line_regex.match(line)
+        if not match:
+            continue
+
+        first, last, data = match.groups()
+        if last is None:
+            last = first
+
+        first = int(first, 16)
+        last = int(last, 16)
+        data = str(data.rstrip())
+
+        ranges.append((first, last, data))
+
+    ranges.sort()
+
+    if isinstance(default, unicode):
+        default = str(default)
+
+    # fill the gaps between explicitly defined ranges
+    last_start, last_end = -1, -1
+    full_ranges = []
+    for start, end, value in ranges:
+        assert last_end < start
+        assert start <= end
+        if start - last_end > 1:
+            full_ranges.append((last_end+1, start-1, default))
+        if is_set:
+            value = set(value.split())
+        full_ranges.append((start, end, value))
+        last_start, last_end = start, end
+    if last_end != MAX_UNICODE:
+        full_ranges.append((last_end+1, MAX_UNICODE, default))
+
+    # reduce total number of ranges by combining continuous ones
+    last_start, last_end, last_value = full_ranges.pop(0)
+    merged_ranges = []
+    for start, end, value in full_ranges:
+        if value == last_value:
+            continue
+        else:
+            merged_ranges.append((last_start, start-1, last_value))
+            last_start, line_end, last_value = start, end, value
+    merged_ranges.append((last_start, MAX_UNICODE, last_value))
+
+    # make sure that the ranges cover the full unicode repertoire
+    assert merged_ranges[0][0] == 0
+    for (cs, ce, cv), (ns, ne, nv) in zip(merged_ranges, merged_ranges[1:]):
+        assert ce+1 == ns
+    assert merged_ranges[-1][1] == MAX_UNICODE
+
+    return merged_ranges
+
+
+def parse_semicolon_separated_data(infile):
+    """Parse a Unicode data file where each line contains a lists of values
+    separated by a semicolon (e.g. "PropertyValueAliases.txt").
+    The number of the values on different lines may be different.
+
+    Returns a list of lists each containing the values as strings.
+    """
+    data = []
+    for line in infile:
+        line = line.split('#', 1)[0].strip()  # remove the comment
+        if not line:
+            continue
+        fields = [str(field.strip()) for field in line.split(';')]
+        data.append(fields)
+    return data
+
+
+def _set_repr(value):
+    return 'None' if value is None else "{{{}}}".format(
+        ", ".join(repr(v) for v in sorted(value)))
+
+
+def build_ranges(filename, local_ucd=None, output_path=None,
+                 default=None, is_set=False, aliases=None):
+    """Fetch 'filename' UCD data file from Unicode official website, parse
+    the property ranges and values and write them as two Python lists
+    to 'fontTools.unicodedata.<filename>.py'.
+
+    'aliases' is an optional mapping of property codes (short names) to long
+    name aliases (list of strings, with the first item being the preferred
+    alias). When this is provided, the property values are written using the
+    short notation, and an additional 'NAMES' dict with the aliases is
+    written to the output module.
+
+    To load the data file from a local directory, you can use the
+    'local_ucd' argument.
+    """
+    modname = os.path.splitext(filename)[0] + ".py"
+    if not output_path:
+        output_path = UNIDATA_PATH + modname
+
+    if local_ucd:
+        log.info("loading '%s' from local directory '%s'", filename, local_ucd)
+        cm = open(pjoin(local_ucd, filename), "r", encoding="utf-8")
+    else:
+        log.info("downloading '%s' from '%s'", filename, UNIDATA_URL)
+        cm = open_unidata_file(filename)
+
+    with cm as f:
+        header = parse_unidata_header(f)
+        ranges = parse_range_properties(f, default=default, is_set=is_set)
+
+    if aliases:
+        reversed_aliases = {normalize(v[0]): k for k, v in aliases.items()}
+        max_value_length = 6  # 4-letter tags plus two quotes for repr
+    else:
+        max_value_length = min(56, max(len(repr(v)) for _, _, v in ranges))
+
+    with open(output_path, "w", encoding="utf-8") as f:
+        f.write(SRC_ENCODING)
+        f.write("#\n")
+        f.write(NOTICE)
+        f.write("# Source: {}{}\n".format(UNIDATA_URL, filename))
+        f.write("# License: {}\n".format(UNIDATA_LICENSE_URL))
+        f.write("#\n")
+        f.write(header+"\n\n")
+
+        f.write("RANGES = [\n")
+        for first, last, value in ranges:
+            f.write("    0x{:0>4X},  # .. 0x{:0>4X} ; {}\n".format(
+                first, last, _set_repr(value) if is_set else value))
+        f.write("]\n")
+
+        f.write("\n")
+        f.write("VALUES = [\n")
+        for first, last, value in ranges:
+            comment = "# {:0>4X}..{:0>4X}".format(first, last)
+            if is_set:
+                value_repr = "{},".format(_set_repr(value))
+            else:
+                if aliases:
+                    # append long name to comment and use the short code
+                    comment += " ; {}".format(value)
+                    value = reversed_aliases[normalize(value)]
+                value_repr = "{!r},".format(value)
+            f.write("    {}  {}\n".format(
+                value_repr.ljust(max_value_length+1), comment))
+        f.write("]\n")
+
+        if aliases:
+            f.write("\n")
+            f.write("NAMES = {\n")
+            for value, names in sorted(aliases.items()):
+                # we only write the first preferred alias
+                f.write("    {!r}: {!r},\n".format(value, names[0]))
+            f.write("}\n")
+
+    log.info("saved new file: '%s'", os.path.normpath(output_path))
+
+
+_normalize_re = re.compile(r"[-_ ]+")
+
+def normalize(string):
+    """Remove case, strip space, '-' and '_' for loose matching."""
+    return _normalize_re.sub("", string).lower()
+
+
+def parse_property_value_aliases(property_tag, local_ucd=None):
+    """Fetch the current 'PropertyValueAliases.txt' from the Unicode website,
+    parse the values for the specified 'property_tag' and return a dictionary
+    of name aliases (list of strings) keyed by short value codes (strings).
+
+    To load the data file from a local directory, you can use the
+    'local_ucd' argument.
+    """
+    filename = "PropertyValueAliases.txt"
+    if local_ucd:
+        log.info("loading '%s' from local directory '%s'", filename, local_ucd)
+        cm = open(pjoin(local_ucd, filename), "r", encoding="utf-8")
+    else:
+        log.info("downloading '%s' from '%s'", filename, UNIDATA_URL)
+        cm = open_unidata_file(filename)
+
+    with cm as f:
+        header = parse_unidata_header(f)
+        data = parse_semicolon_separated_data(f)
+
+    aliases = {item[1]: item[2:] for item in data
+               if item[0] == property_tag}
+
+    return aliases
+
+
+def main():
+    import argparse
+
+    parser = argparse.ArgumentParser(
+        description="Generate fontTools.unicodedata from UCD data files")
+    parser.add_argument(
+        '--ucd-path', help="Path to local folder containing UCD data files")
+    parser.add_argument('-q', '--quiet', action="store_true")
+    options = parser.parse_args()
+
+    level = "WARNING" if options.quiet else "INFO"
+    logging.basicConfig(level=level, format="%(message)s")
+
+    build_ranges("Blocks.txt", local_ucd=options.ucd_path, default="No_Block")
+
+    script_aliases = parse_property_value_aliases("sc", options.ucd_path)
+    build_ranges("Scripts.txt", local_ucd=options.ucd_path, default="Unknown",
+                 aliases=script_aliases)
+    build_ranges("ScriptExtensions.txt", local_ucd=options.ucd_path,
+                 is_set=True)
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(main())
diff --git a/NEWS.rst b/NEWS.rst
new file mode 100644
index 0000000..5341ca5
--- /dev/null
+++ b/NEWS.rst
@@ -0,0 +1,980 @@
+3.28.0 (released 2018-06-19)
+----------------------------
+
+- [featureVars] Added experimental module to build ``FeatureVariations``
+  tables. Still needs to be hooked up to ``varLib.build`` (#1240).
+- [fixedTools] Added ``otRound`` to round floats to nearest integer towards
+  positive Infinity. This is now used where we deal with visual data like X/Y
+  coordinates, advance widths/heights, variation deltas, and similar (#1274,
+  #1248).
+- [subset] Improved GSUB closure memoize algorithm.
+- [varLib.models] Fixed regression in model resolution (180124, #1269).
+- [feaLib.ast] Fixed error when converting ``SubtableStatement`` to string
+  (#1275).
+- [varLib.mutator] Set ``OS/2.usWeightClass`` and ``usWidthClass``, and
+  ``post.italicAngle`` based on the 'wght', 'wdth' and 'slnt' axis values
+  (#1276, #1264).
+- [py23/loggingTools] Don't automatically set ``logging.lastResort`` handler
+  on py27. Moved ``LastResortLogger`` to the ``loggingTools`` module (#1277).
+
+3.27.1 (released 2018-06-11)
+----------------------------
+
+- [ttGlyphPen] Issue a warning and skip building non-existing components
+  (https://github.com/googlei18n/fontmake/issues/411).
+- [tests] Fixed issue running ttx_test.py from a tagged commit.
+
+3.27.0 (released 2018-06-11)
+----------------------------
+
+- [designspaceLib] Added new ``conditionSet`` element to ``rule`` element in
+  designspace document. Bumped ``format`` attribute to ``4.0`` (previously,
+  it was formatted as an integer). Removed ``checkDefault``, ``checkAxes``
+  methods, and any kind of guessing about the axes when the ``<axes>`` element
+  is missing. The default master is expected at the intersection of all default
+  values for each axis (#1254, #1255, #1267).
+- [cffLib] Fixed issues when compiling CFF2 or converting from CFF when the
+  font has an FDArray (#1211, #1271).
+- [varLib] Avoid attempting to build ``cvar`` table when ``glyf`` table is not
+  present, as is the case for CFF2 fonts.
+- [subset] Handle None coverages in MarkGlyphSets; revert commit 02616ab that
+  sets empty Coverage tables in MarkGlyphSets to None, to make OTS happy.
+- [ttFont] Allow to build glyph order from ``maxp.numGlyphs`` when ``post`` or
+  ``cmap`` are missing.
+- [ttFont] Added ``__len__`` method to ``_TTGlyphSet``.
+- [glyf] Ensure ``GlyphCoordinates`` never overflow signed shorts (#1230).
+- [py23] Added alias for ``itertools.izip`` shadowing the built-in ``zip``.
+- [loggingTools] Memoize ``log`` property of ``LogMixin`` class (fbab12).
+- [ttx] Impoved test coverage (#1261).
+- [Snippets] Addded script to append a suffix to all family names in a font.
+- [varLib.plot] Make it work with matplotlib >= 2.1 (b38e2b).
+
+3.26.0 (released 2018-05-03)
+----------------------------
+
+- [designspace] Added a new optional ``layer`` attribute to the source element,
+  and a corresponding ``layerName`` attribute to the ``SourceDescriptor``
+  object (#1253).
+  Added ``conditionset`` element to the ``rule`` element to the spec, but not
+  implemented in designspace reader/writer yet (#1254).
+- [varLib.models] Refine modeling one last time (0ecf5c5).
+- [otBase] Fixed sharing of tables referred to by different offset sizes
+  (795f2f9).
+- [subset] Don't drop a GDEF that only has VarStore (fc819d6). Set to None
+  empty Coverage tables in MarkGlyphSets (02616ab).
+- [varLib]: Added ``--master-finder`` command-line option (#1249).
+- [varLib.mutator] Prune fvar nameIDs from instance's name table (#1245).
+- [otTables] Allow decompiling bad ClassDef tables with invalid format, with
+  warning (#1236).
+- [varLib] Make STAT v1.2 and reuse nameIDs from fvar table (#1242).
+- [varLib.plot] Show master locations. Set axis limits to -1, +1.
+- [subset] Handle HVAR direct mapping. Passthrough 'cvar'.
+  Added ``--font-number`` command-line option for collections.
+- [t1Lib] Allow a text encoding to be specified when parsing a Type 1 font
+  (#1234). Added ``kind`` argument to T1Font constructor (c5c161c).
+- [ttLib] Added context manager API to ``TTFont`` class, so it can be used in
+  ``with`` statements to auto-close the file when exiting the context (#1232).
+
+3.25.0 (released 2018-04-03)
+----------------------------
+
+- [varLib] Improved support-resolution algorithm. Previously, the on-axis
+  masters would always cut the space. They don't anymore. That's more
+  consistent, and fixes the main issue Erik showed at TYPO Labs 2017.
+  Any varfont built that had an unusual master configuration will change
+  when rebuilt (42bef17, a523a697,
+  https://github.com/googlei18n/fontmake/issues/264).
+- [varLib.models] Added a ``main()`` entry point, that takes positions and
+  prints model results.
+- [varLib.plot] Added new module to plot a designspace's
+  VariationModel. Requires ``matplotlib``.
+- [varLib.mutator] Added -o option to specify output file path (2ef60fa).
+- [otTables] Fixed IndexError while pruning of HVAR pre-write (6b6c34a).
+- [varLib.models] Convert delta array to floats if values overflows signed
+  short integer (0055f94).
+
+3.24.2 (released 2018-03-26)
+----------------------------
+
+- [otBase] Don't fail during ``ValueRecord`` copy if src has more items.
+  We drop hinting in the subsetter by simply changing ValueFormat, without
+  cleaning up the actual ValueRecords. This was causing assertion error if
+  a variable font was subsetted without hinting and then passed directly to
+  the mutator for instantiation without first it saving to disk.
+
+3.24.1 (released 2018-03-06)
+----------------------------
+
+- [varLib] Don't remap the same ``DeviceTable`` twice in VarStore optimizer
+  (#1206).
+- [varLib] Add ``--disable-iup`` option to ``fonttools varLib`` script,
+  and a ``optimize=True`` keyword argument to ``varLib.build`` function,
+  to optionally disable IUP optimization while building varfonts.
+- [ttCollection] Fixed issue while decompiling ttc with python3 (#1207).
+
+3.24.0 (released 2018-03-01)
+----------------------------
+
+- [ttGlyphPen] Decompose composite glyphs if any components' transform is too
+  large to fit a ``F2Dot14`` value, or clamp transform values that are
+  (almost) equal to +2.0 to make them fit and avoid decomposing (#1200,
+  #1204, #1205).
+- [ttx] Added new ``-g`` option to dump glyphs from the ``glyf`` table
+  splitted as individual ttx files (#153, #1035, #1132, #1202).
+- Copied ``ufoLib.filenames`` module to ``fontTools.misc.filenames``, used
+  for the ttx split-glyphs option (#1202).
+- [feaLib] Added support for ``cvParameters`` blocks in Character Variant
+  feautures ``cv01-cv99`` (#860, #1169).
+- [Snippets] Added ``checksum.py`` script to generate/check SHA1 hash of
+  ttx files (#1197).
+- [varLib.mutator] Fixed issue while instantiating some variable fonts
+  whereby the horizontal advance width computed from ``gvar`` phantom points
+  could turn up to be negative (#1198).
+- [varLib/subset] Fixed issue with subsetting GPOS variation data not
+  picking up ``ValueRecord`` ``Device`` objects (54fd71f).
+- [feaLib/voltLib] In all AST elements, the ``location`` is no longer a
+  required positional argument, but an optional kewyord argument (defaults
+  to ``None``). This will make it easier to construct feature AST from
+  code (#1201).
+
+
+3.23.0 (released 2018-02-26)
+----------------------------
+
+- [designspaceLib] Added an optional ``lib`` element to the designspace as a
+  whole, as well as to the instance elements, to store arbitrary data in a
+  property list dictionary, similar to the UFO's ``lib``. Added an optional
+  ``font`` attribute to the ``SourceDescriptor``, to allow operating on
+  in-memory font objects (#1175).
+- [cffLib] Fixed issue with lazy-loading of attributes when attempting to
+  set the CFF TopDict.Encoding (#1177, #1187).
+- [ttx] Fixed regression introduced in 3.22.0 that affected the split tables
+  ``-s`` option (#1188).
+- [feaLib] Added ``IncludedFeaNotFound`` custom exception subclass, raised
+  when an included feature file cannot be found (#1186).
+- [otTables] Changed ``VarIdxMap`` to use glyph names internally instead of
+  glyph indexes. The old ttx dumps of HVAR/VVAR tables that contain indexes
+  can still be imported (21cbab8, 38a0ffb).
+- [varLib] Implemented VarStore optimizer (#1184).
+- [subset] Implemented pruning of GDEF VarStore, HVAR and MVAR (#1179).
+- [sfnt] Restore backward compatiblity with ``numFonts`` attribute of
+  ``SFNTReader`` object (#1181).
+- [merge] Initial support for merging ``LangSysRecords`` (#1180).
+- [ttCollection] don't seek(0) when writing to possibly unseekable strems.
+- [subset] Keep all ``--name-IDs`` from 0 to 6 by default (#1170, #605, #114).
+- [cffLib] Added ``width`` module to calculate optimal CFF default and
+  nominal glyph widths.
+- [varLib] Don’t fail if STAT already in the master fonts (#1166).
+
+3.22.0 (released 2018-02-04)
+----------------------------
+
+- [subset] Support subsetting ``endchar`` acting as ``seac``-like components
+  in ``CFF`` (fixes #1162).
+- [feaLib] Allow to build from pre-parsed ``ast.FeatureFile`` object.
+  Added ``tables`` argument to only build some tables instead of all (#1159,
+  #1163).
+- [textTools] Replaced ``safeEval`` with ``ast.literal_eval`` (#1139).
+- [feaLib] Added option to the parser to not resolve ``include`` statements
+  (#1154).
+- [ttLib] Added new ``ttCollection`` module to read/write TrueType and
+  OpenType Collections. Exports a ``TTCollection`` class with a ``fonts``
+  attribute containing a list of ``TTFont`` instances, the methods ``save``
+  and ``saveXML``, plus some list-like methods. The ``importXML`` method is
+  not implemented yet (#17).
+- [unicodeadata] Added ``ot_tag_to_script`` function that converts from
+  OpenType script tag to Unicode script code.
+- Added new ``designspaceLib`` subpackage, originally from Erik Van Blokland's
+  ``designSpaceDocument``: https://github.com/LettError/designSpaceDocument
+  NOTE: this is not yet used internally by varLib, and the API may be subject
+  to changes (#911, #1110, LettError/designSpaceDocument#28).
+- Added new FontTools icon images (8ee7c32).
+- [unicodedata] Added ``script_horizontal_direction`` function that returns
+  either "LTR" or "RTL" given a unicode script code.
+- [otConverters] Don't write descriptive name string as XML comment if the
+  NameID value is 0 (== NULL) (#1151, #1152).
+- [unicodedata] Add ``ot_tags_from_script`` function to get the list of
+  OpenType script tags associated with unicode script code (#1150).
+- [feaLib] Don't error when "enumerated" kern pairs conflict with preceding
+  single pairs; emit warning and chose the first value (#1147, #1148).
+- [loggingTools] In ``CapturingLogHandler.assertRegex`` method, match the
+  fully formatted log message.
+- [sbix] Fixed TypeError when concatenating str and bytes (#1154).
+- [bezierTools] Implemented cusp support and removed ``approximate_fallback``
+  arg in ``calcQuadraticArcLength``. Added ``calcCubicArcLength`` (#1142).
+
+3.21.2 (released 2018-01-08)
+----------------------------
+
+- [varLib] Fixed merging PairPos Format1/2 with missing subtables (#1125).
+
+3.21.1 (released 2018-01-03)
+----------------------------
+
+- [feaLib] Allow mixed single/multiple substitutions (#612)
+- Added missing ``*.afm`` test assets to MAINFEST.in (#1137).
+- Fixed dumping ``SVG`` tables containing color palettes (#1124).
+
+3.21.0 (released 2017-12-18)
+----------------------------
+
+- [cmap] when compiling format6 subtable, don't assume gid0 is always called
+  '.notdef' (1e42224).
+- [ot] Allow decompiling fonts with bad Coverage format number (1aafae8).
+- Change FontTools licence to MIT (#1127).
+- [post] Prune extra names already in standard Mac set (df1e8c7).
+- [subset] Delete empty SubrsIndex after subsetting (#994, #1118).
+- [varLib] Don't share points in cvar by default, as it currently fails on
+  some browsers (#1113).
+- [afmLib] Make poor old afmLib work on python3.
+
+3.20.1 (released 2017-11-22)
+----------------------------
+
+- [unicodedata] Fixed issue with ``script`` and ``script_extension`` functions
+  returning inconsistent short vs long names. They both return the short four-
+  letter script codes now. Added ``script_name`` and ``script_code`` functions
+  to look up the long human-readable script name from the script code, and
+  viceversa (#1109, #1111).
+
+3.20.0 (released 2017-11-21)
+----------------------------
+
+- [unicodedata] Addded new module ``fontTools.unicodedata`` which exports the
+  same interface as the built-in ``unicodedata`` module, with the addition of
+  a few functions that are missing from the latter, such as ``script``,
+  ``script_extension`` and ``block``. Added a ``MetaTools/buildUCD.py`` script
+  to download and parse data files from the Unicode Character Database and
+  generate python modules containing lists of ranges and property values.
+- [feaLib] Added ``__str__`` method to all ``ast`` elements (delegates to the
+  ``asFea`` method).
+- [feaLib] ``Parser`` constructor now accepts a ``glyphNames`` iterable
+  instead of ``glyphMap`` dict. The latter still works but with a pending
+  deprecation warning (#1104).
+- [bezierTools] Added arc length calculation functions originally from
+  ``pens.perimeterPen`` module (#1101).
+- [varLib] Started generating STAT table (8af4309). Right now it just reflects
+  the axes, and even that with certain limitations:
+  * AxisOrdering is set to the order axes are defined,
+  * Name-table entries are not shared with fvar.
+- [py23] Added backports for ``redirect_stdout`` and ``redirect_stderr``
+  context managers (#1097).
+- [Graphite] Fixed some round-trip bugs (#1093).
+
+3.19.0 (released 2017-11-06)
+----------------------------
+
+- [varLib] Try set of used points instead of all points when testing whether to
+  share points between tuples (#1090).
+- [CFF2] Fixed issue with reading/writing PrivateDict BlueValues to TTX file.
+  Read the commit message 8b02b5a and issue #1030 for more details.
+  NOTE: this change invalidates all the TTX files containing CFF2 tables
+  that where dumped with previous verisons of fonttools.
+  CFF2 Subr items can have values on the stack after the last operator, thus
+  a ``CFF2Subr`` class was added to accommodate this (#1091).
+- [_k_e_r_n] Fixed compilation of AAT kern version=1.0 tables (#1089, #1094)
+- [ttLib] Added getBestCmap() convenience method to TTFont class and cmap table
+  class that returns a preferred Unicode cmap subtable given a list of options
+  (#1092).
+- [morx] Emit more meaningful subtable flags. Implement InsertionMorphAction
+
+3.18.0 (released 2017-10-30)
+----------------------------
+
+- [feaLib] Fixed writing back nested glyph classes (#1086).
+- [TupleVariation] Reactivated shared points logic, bugfixes (#1009).
+- [AAT] Implemented ``morx`` ligature subtables (#1082).
+- [reverseContourPen] Keep duplicate lineTo following a moveTo (#1080,
+  https://github.com/googlei18n/cu2qu/issues/51).
+- [varLib.mutator] Suport instantiation of GPOS, GDEF and MVAR (#1079).
+- [sstruct] Fixed issue with ``unicode_literals`` and ``struct`` module in
+  old versions of python 2.7 (#993).
+
+3.17.0 (released 2017-10-16)
+----------------------------
+
+- [svgPathPen] Added an ``SVGPathPen`` that translates segment pen commands
+  into SVG path descriptions. Copied from Tal Leming's ``ufo2svg.svgPathPen``
+  https://github.com/typesupply/ufo2svg/blob/d69f992/Lib/ufo2svg/svgPathPen.py
+- [reverseContourPen] Added ``ReverseContourPen``, a filter pen that draws
+  contours with the winding direction reversed, while keeping the starting
+  point (#1071).
+- [filterPen] Added ``ContourFilterPen`` to manipulate contours as a whole
+  rather than segment by segment.
+- [arrayTools] Added ``Vector`` class to apply math operations on an array
+  of numbers, and ``pairwise`` function to loop over pairs of items in an
+  iterable.
+- [varLib] Added support for building and interpolation of ``cvar`` table
+  (f874cf6, a25a401).
+
+3.16.0 (released 2017-10-03)
+----------------------------
+
+- [head] Try using ``SOURCE_DATE_EPOCH`` environment variable when setting
+  the ``head`` modified timestamp to ensure reproducible builds (#1063).
+  See https://reproducible-builds.org/specs/source-date-epoch/
+- [VTT] Decode VTT's ``TSI*`` tables text as UTF-8 (#1060).
+- Added support for Graphite font tables: Feat, Glat, Gloc, Silf and Sill.
+  Thanks @mhosken! (#1054).
+- [varLib] Default to using axis "name" attribute if "labelname" element
+  is missing (588f524).
+- [merge] Added support for merging Script records. Remove unused features
+  and lookups after merge (d802580, 556508b).
+- Added ``fontTools.svgLib`` package. Includes a parser for SVG Paths that
+  supports the Pen protocol (#1051). Also, added a snippet to convert SVG
+  outlines to UFO GLIF (#1053).
+- [AAT] Added support for ``ankr``, ``bsln``, ``mort``, ``morx``, ``gcid``,
+  and ``cidg``.
+- [subset] Implemented subsetting of ``prop``, ``opbd``, ``bsln``, ``lcar``.
+
+3.15.1 (released 2017-08-18)
+----------------------------
+
+- [otConverters] Implemented ``__add__`` and ``__radd__`` methods on
+  ``otConverters._LazyList`` that decompile a lazy list before adding
+  it to another list or ``_LazyList`` instance. Fixes an ``AttributeError``
+  in the ``subset`` module when attempting to sum ``_LazyList`` objects
+  (6ef48bd2, 1aef1683).
+- [AAT] Support the `opbd` table with optical bounds (a47f6588).
+- [AAT] Support `prop` table with glyph properties (d05617b4).
+
+
+3.15.0 (released 2017-08-17)
+----------------------------
+
+- [AAT] Added support for AAT lookups. The ``lcar`` table can be decompiled
+  and recompiled; futher work needed to handle ``morx`` table (#1025).
+- [subset] Keep (empty) DefaultLangSys for Script 'DFLT' (6eb807b5).
+- [subset] Support GSUB/GPOS.FeatureVariations (fe01d87b).
+- [varLib] In ``models.supportScalars``, ignore an axis when its peak value
+  is 0 (fixes #1020).
+- [varLib] Add default mappings to all axes in avar to fix rendering issue
+  in some rasterizers (19c4b377, 04eacf13).
+- [varLib] Flatten multiple tail PairPosFormat2 subtables before merging
+  (c55ef525).
+- [ttLib] Added support for recalculating font bounding box in ``CFF`` and
+  ``head`` tables, and min/max values in ``hhea`` and ``vhea`` tables (#970).
+
+3.14.0 (released 2017-07-31)
+----------------------------
+
+- [varLib.merger] Remove Extensions subtables before merging (f7c20cf8).
+- [varLib] Initialize the avar segment map with required default entries
+  (#1014).
+- [varLib] Implemented optimal IUP optmiziation (#1019).
+- [otData] Add ``AxisValueFormat4`` for STAT table v1.2 from OT v1.8.2
+  (#1015).
+- [name] Fixed BCP46 language tag for Mac langID=9: 'si' -> 'sl'.
+- [subset] Return value from ``_DehintingT2Decompiler.op_hintmask``
+  (c0d672ba).
+- [cffLib] Allow to get TopDict by index as well as by name (dca96c9c).
+- [cffLib] Removed global ``isCFF2`` state; use one set of classes for
+  both CFF and CFF2, maintaining backward compatibility existing code (#1007).
+- [cffLib] Deprecated maxstack operator, per OpenType spec update 1.8.1.
+- [cffLib] Added missing default (-100) for UnderlinePosition (#983).
+- [feaLib] Enable setting nameIDs greater than 255 (#1003).
+- [varLib] Recalculate ValueFormat when merging SinglePos (#996).
+- [varLib] Do not emit MVAR if there are no entries in the variation store
+  (#987).
+- [ttx] For ``-x`` option, pad with space if table tag length is < 4.
+
+3.13.1 (released 2017-05-30)
+----------------------------
+
+- [feaLib.builder] Removed duplicate lookups optimization. The original
+  lookup order and semantics of the feature file are preserved (#976).
+
+3.13.0 (released 2017-05-24)
+----------------------------
+
+- [varLib.mutator] Implement IUP optimization (#969).
+- [_g_l_y_f.GlyphCoordinates] Changed ``__bool__()`` semantics to match those
+  of other iterables (e46f949). Removed ``__abs__()`` (3db5be2).
+- [varLib.interpolate_layout] Added ``mapped`` keyword argument to
+  ``interpolate_layout`` to allow disabling avar mapping: if False (default),
+  the location is mapped using the map element of the axes in designspace file;
+  if True, it is assumed that location is in designspace's internal space and
+  no mapping is performed (#950, #975).
+- [varLib.interpolate_layout] Import designspace-loading logic from varLib.
+- [varLib] Fixed bug with recombining PairPosClass2 subtables (81498e5, #914).
+- [cffLib.specializer] When copying iterables, cast to list (462b7f86).
+
+3.12.1 (released 2017-05-18)
+----------------------------
+
+- [pens.t2CharStringPen] Fixed AttributeError when calling addComponent in
+  T2CharStringPen (#965).
+
+3.12.0 (released 2017-05-17)
+----------------------------
+
+- [cffLib.specializer] Added new ``specializer`` module to optimize CFF
+  charstrings, used by the T2CharStringPen (#948).
+- [varLib.mutator] Sort glyphs by component depth before calculating composite
+  glyphs' bounding boxes to ensure deltas are correctly caclulated (#945).
+- [_g_l_y_f] Fixed loss of precision in GlyphCoordinates by using 'd' (double)
+  instead of 'f' (float) as ``array.array`` typecode (#963, #964).
+
+3.11.0 (released 2017-05-03)
+----------------------------
+
+- [t2CharStringPen] Initial support for specialized Type2 path operators:
+  vmoveto, hmoveto, vlineto, hlineto, vvcurveto, hhcurveto, vhcurveto and
+  hvcurveto. This should produce more compact charstrings (#940, #403).
+- [Doc] Added Sphinx sources for the documentation. Thanks @gferreira (#935).
+- [fvar] Expose flags in XML (#932)
+- [name] Add helper function for building multi-lingual names (#921)
+- [varLib] Fixed kern merging when a PairPosFormat2 has ClassDef1 with glyphs
+  that are NOT present in the Coverage (1b5e1c4, #939).
+- [varLib] Fixed non-deterministic ClassDef order with PY3 (f056c12, #927).
+- [feLib] Throw an error when the same glyph is defined in multiple mark
+  classes within the same lookup (3e3ff00, #453).
+
+3.10.0 (released 2017-04-14)
+----------------------------
+
+- [varLib] Added support for building ``avar`` table, using the designspace
+  ``<map>`` elements.
+- [varLib] Removed unused ``build(..., axisMap)`` argument. Axis map should
+  be specified in designspace file now. We do not accept nonstandard axes
+  if ``<axes>`` element is not present.
+- [varLib] Removed "custom" axis from the ``standard_axis_map``. This was
+  added before when glyphsLib was always exporting the (unused) custom axis.
+- [varLib] Added partial support for building ``MVAR`` table; does not
+  implement ``gasp`` table variations yet.
+- [pens] Added FilterPen base class, for pens that control another pen;
+  factored out ``addComponent`` method from BasePen into a separate abstract
+  DecomposingPen class; added DecomposingRecordingPen, which records
+  components decomposed as regular contours.
+- [TSI1] Fixed computation of the textLength of VTT private tables (#913).
+- [loggingTools] Added ``LogMixin`` class providing a ``log`` property to
+  subclasses, which returns a ``logging.Logger`` named after the latter.
+- [loggingTools] Added ``assertRegex`` method to ``CapturingLogHandler``.
+- [py23] Added backport for python 3's ``types.SimpleNamespace`` class.
+- [EBLC] Fixed issue with python 3 ``zip`` iterator.
+
+3.9.2 (released 2017-04-08)
+---------------------------
+
+- [pens] Added pen to draw glyphs using WxPython ``GraphicsPath`` class:
+  https://wxpython.org/docs/api/wx.GraphicsPath-class.html
+- [varLib.merger] Fixed issue with recombining multiple PairPosFormat2
+  subtables (#888)
+- [varLib] Do not encode gvar deltas that are all zeroes, or if all values
+  are smaller than tolerance.
+- [ttLib] _TTGlyphSet glyphs now also have ``height`` and ``tsb`` (top
+  side bearing) attributes from the ``vmtx`` table, if present.
+- [glyf] In ``GlyphCoordintes`` class, added ``__bool__`` / ``__nonzero__``
+  methods, and ``array`` property to get raw array.
+- [ttx] Support reading TTX files with BOM (#896)
+- [CFF2] Fixed the reporting of the number of regions in the font.
+
+3.9.1 (released 2017-03-20)
+---------------------------
+
+- [varLib.merger] Fixed issue while recombining multiple PairPosFormat2
+  subtables if they were split because of offset overflows (9798c30).
+- [varLib.merger] Only merge multiple PairPosFormat1 subtables if there is
+  at least one of the fonts with a non-empty Format1 subtable (0f5a46b).
+- [varLib.merger] Fixed IndexError with empty ClassDef1 in PairPosFormat2
+  (aad0d46).
+- [varLib.merger] Avoid reusing Class2Record (mutable) objects (e6125b3).
+- [varLib.merger] Calculate ClassDef1 and ClassDef2's Format when merging
+  PairPosFormat2 (23511fd).
+- [macUtils] Added missing ttLib import (b05f203).
+
+3.9.0 (released 2017-03-13)
+---------------------------
+
+- [feaLib] Added (partial) support for parsing feature file comments ``# ...``
+  appearing in between statements (#879).
+- [feaLib] Cleaned up syntax tree for FeatureNames.
+- [ttLib] Added support for reading/writing ``CFF2`` table (thanks to
+  @readroberts at Adobe), and ``TTFA`` (ttfautohint) table.
+- [varLib] Fixed regression introduced with 3.8.0 in the calculation of
+  ``NumShorts``, i.e. the number of deltas in ItemVariationData's delta sets
+  that use a 16-bit representation (b2825ff).
+
+3.8.0 (released 2017-03-05)
+---------------------------
+
+- New pens: MomentsPen, StatisticsPen, RecordingPen, and TeePen.
+- [misc] Added new ``fontTools.misc.symfont`` module, for symbolic font
+  statistical analysis; requires ``sympy`` (http://www.sympy.org/en/index.html)
+- [varLib] Added experimental ``fontTools.varLib.interpolatable`` module for
+  finding wrong contour order between different masters
+- [varLib] designspace.load() now returns a dictionary, instead of a tuple,
+  and supports <axes> element (#864); the 'masters' item was renamed 'sources',
+  like the <sources> element in the designspace document
+- [ttLib] Fixed issue with recalculating ``head`` modified timestamp when
+  saving CFF fonts
+- [ttLib] In TupleVariation, round deltas before compiling (#861, fixed #592)
+- [feaLib] Ignore duplicate glyphs in classes used as MarkFilteringSet and
+  MarkAttachmentType (#863)
+- [merge] Changed the ``gasp`` table merge logic so that only the one from
+  the first font is retained, similar to other hinting tables (#862)
+- [Tests] Added tests for the ``varLib`` package, as well as test fonts
+  from the "Annotated OpenType Specification" (AOTS) to exercise ``ttLib``'s
+  table readers/writers (<https://github.com/adobe-type-tools/aots>)
+
+3.7.2 (released 2017-02-17)
+---------------------------
+
+- [subset] Keep advance widths when stripping ".notdef" glyph outline in
+  CID-keyed CFF fonts (#845)
+- [feaLib] Zero values now produce the same results as makeotf (#633, #848)
+- [feaLib] More compact encoding for “Contextual positioning with in-line
+  single positioning rules” (#514)
+
+3.7.1 (released 2017-02-15)
+---------------------------
+
+- [subset] Fixed issue with ``--no-hinting`` option whereby advance widths in
+  Type 2 charstrings were also being stripped (#709, #343)
+- [feaLib] include statements now resolve relative paths like makeotf (#838)
+- [feaLib] table ``name`` now handles Unicode codepoints beyond the Basic
+  Multilingual Plane, also supports old-style MacOS platform encodings (#842)
+- [feaLib] correctly escape string literals when emitting feature syntax (#780)
+
+3.7.0 (released 2017-02-11)
+---------------------------
+
+- [ttx, mtiLib] Preserve ordering of glyph alternates in GSUB type 3 (#833).
+- [feaLib] Glyph names can have dashes, as per new AFDKO syntax v1.20 (#559).
+- [feaLib] feaLib.Parser now needs the font's glyph map for parsing.
+- [varLib] Fix regression where GPOS values were stored as 0.
+- [varLib] Allow merging of class-based kerning when ClassDefs are different
+
+3.6.3 (released 2017-02-06)
+---------------------------
+
+- [varLib] Fix building variation of PairPosFormat2 (b5c34ce).
+- Populate defaults even for otTables that have postRead (e45297b).
+- Fix compiling of MultipleSubstFormat1 with zero 'out' glyphs (b887860).
+
+3.6.2 (released 2017-01-30)
+---------------------------
+
+- [varLib.merger] Fixed "TypeError: reduce() of empty sequence with no
+  initial value" (3717dc6).
+
+3.6.1 (released 2017-01-28)
+---------------------------
+
+-  [py23] Fixed unhandled exception occurring at interpreter shutdown in
+   the "last resort" logging handler (972b3e6).
+-  [agl] Ensure all glyph names are of native 'str' type; avoid mixing
+   'str' and 'unicode' in TTFont.glyphOrder (d8c4058).
+-  Fixed inconsistent title levels in README.rst that caused PyPI to
+   incorrectly render the reStructuredText page.
+
+3.6.0 (released 2017-01-26)
+---------------------------
+
+-  [varLib] Refactored and improved the variation-font-building process.
+-  Assembly code in the fpgm, prep, and glyf tables is now indented in
+   XML output for improved readability. The ``instruction`` element is
+   written as a simple tag if empty (#819).
+-  [ttx] Fixed 'I/O operation on closed file' error when dumping
+   multiple TTXs to standard output with the '-o -' option.
+-  The unit test modules (``*_test.py``) have been moved outside of the
+   fontTools package to the Tests folder, thus they are no longer
+   installed (#811).
+
+3.5.0 (released 2017-01-14)
+---------------------------
+
+-  Font tables read from XML can now be written back to XML with no
+   loss.
+-  GSUB/GPOS LookupType is written out in XML as an element, not
+   comment. (#792)
+-  When parsing cmap table, do not store items mapped to glyph id 0.
+   (#790)
+-  [otlLib] Make ClassDef sorting deterministic. Fixes #766 (7d1ddb2)
+-  [mtiLib] Added unit tests (#787)
+-  [cvar] Implemented cvar table
+-  [gvar] Renamed GlyphVariation to TupleVariation to match OpenType
+   terminology.
+-  [otTables] Handle gracefully empty VarData.Item array when compiling
+   XML. (#797)
+-  [varLib] Re-enabled generation of ``HVAR`` table for fonts with
+   TrueType outlines; removed ``--build-HVAR`` command-line option.
+-  [feaLib] The parser can now be extended to support non-standard
+   statements in FEA code by using a customized Abstract Syntax Tree.
+   See, for example, ``feaLib.builder_test.test_extensions`` and
+   baseClass.feax (#794, fixes #773).
+-  [feaLib] Added ``feaLib`` command to the 'fonttools' command-line
+   tool; applies a feature file to a font. ``fonttools feaLib -h`` for
+   help.
+-  [pens] The ``T2CharStringPen`` now takes an optional
+   ``roundTolerance`` argument to control the rounding of coordinates
+   (#804, fixes #769).
+-  [ci] Measure test coverage on all supported python versions and OSes,
+   combine coverage data and upload to
+   https://codecov.io/gh/fonttools/fonttools (#786)
+-  [ci] Configured Travis and Appveyor for running tests on Python 3.6
+   (#785, 55c03bc)
+-  The manual pages installation directory can be customized through
+   ``FONTTOOLS_MANPATH`` environment variable (#799, fixes #84).
+-  [Snippets] Added otf2ttf.py, for converting fonts from CFF to
+   TrueType using the googlei18n/cu2qu module (#802)
+
+3.4.0 (released 2016-12-21)
+---------------------------
+
+-  [feaLib] Added support for generating FEA text from abstract syntax
+   tree (AST) objects (#776). Thanks @mhosken
+-  Added ``agl.toUnicode`` function to convert AGL-compliant glyph names
+   to Unicode strings (#774)
+-  Implemented MVAR table (b4d5381)
+
+3.3.1 (released 2016-12-15)
+---------------------------
+
+-  [setup] We no longer use versioneer.py to compute fonttools version
+   from git metadata, as this has caused issues for some users (#767).
+   Now we bump the version strings manually with a custom ``release``
+   command of setup.py script.
+
+3.3.0 (released 2016-12-06)
+---------------------------
+
+-  [ttLib] Implemented STAT table from OpenType 1.8 (#758)
+-  [cffLib] Fixed decompilation of CFF fonts containing non-standard
+   key/value pairs in FontDict (issue #740; PR #744)
+-  [py23] minor: in ``round3`` function, allow the second argument to be
+   ``None`` (#757)
+-  The standalone ``sstruct`` and ``xmlWriter`` modules, deprecated
+   since vesion 3.2.0, have been removed. They can be imported from the
+   ``fontTools.misc`` package.
+
+3.2.3 (released 2016-12-02)
+---------------------------
+
+-  [py23] optimized performance of round3 function; added backport for
+   py35 math.isclose() (9d8dacb)
+-  [subset] fixed issue with 'narrow' (UCS-2) Python 2 builds and
+   ``--text``/``--text-file`` options containing non-BMP chararcters
+   (16d0e5e)
+-  [varLib] fixed issuewhen normalizing location values (8fa2ee1, #749)
+-  [inspect] Made it compatible with both python2 and python3 (167ee60,
+   #748). Thanks @pnemade
+
+3.2.2 (released 2016-11-24)
+---------------------------
+
+-  [varLib] Do not emit null axes in fvar (1bebcec). Thanks @robmck-ms
+-  [varLib] Handle fonts without GPOS (7915a45)
+-  [merge] Ignore LangSys if None (a11bc56)
+-  [subset] Fix subsetting MathVariants (78d3cbe)
+-  [OS/2] Fix "Private Use (plane 15)" range (08a0d55). Thanks @mashabow
+
+3.2.1 (released 2016-11-03)
+---------------------------
+
+-  [OS/2] fix checking ``fsSelection`` bits matching ``head.macStyle``
+   bits
+-  [varLib] added ``--build-HVAR`` option to generate ``HVAR`` table for
+   fonts with TrueType outlines. For ``CFF2``, it is enabled by default.
+
+3.2.0 (released 2016-11-02)
+---------------------------
+
+-  [varLib] Improve support for OpenType 1.8 Variable Fonts:
+-  Implement GDEF's VariationStore
+-  Implement HVAR/VVAR tables
+-  Partial support for loading MutatorMath .designspace files with
+   varLib.designspace module
+-  Add varLib.models with Variation fonts interpolation models
+-  Implement GSUB/GPOS FeatureVariations
+-  Initial support for interpolating and merging OpenType Layout tables
+   (see ``varLib.interpolate_layout`` and ``varLib.merger`` modules)
+-  [API change] Change version to be an integer instead of a float in
+   XML output for GSUB, GPOS, GDEF, MATH, BASE, JSTF, HVAR, VVAR, feat,
+   hhea and vhea tables. Scripts that set the Version for those to 1.0
+   or other float values also need fixing. A warning is emitted when
+   code or XML needs fix.
+-  several bug fixes to the cffLib module, contributed by Adobe's
+   @readroberts
+-  The XML output for CFF table now has a 'major' and 'minor' elements
+   for specifying whether it's version 1.0 or 2.0 (support for CFF2 is
+   coming soon)
+-  [setup.py] remove undocumented/deprecated ``extra_path`` Distutils
+   argument. This means that we no longer create a "FontTools" subfolder
+   in site-packages containing the actual fontTools package, as well as
+   the standalone xmlWriter and sstruct modules. The latter modules are
+   also deprecated, and scheduled for removal in upcoming releases.
+   Please change your import statements to point to from fontTools.misc
+   import xmlWriter and from fontTools.misc import sstruct.
+-  [scripts] Add a 'fonttools' command-line tool that simply runs
+   ``fontTools.*`` sub-modules: e.g. ``fonttools ttx``,
+   ``fonttools subset``, etc.
+-  [hmtx/vmts] Read advance width/heights as unsigned short (uint16);
+   automatically round float values to integers.
+-  [ttLib/xmlWriter] add 'newlinestr=None' keyword argument to
+   ``TTFont.saveXML`` for overriding os-specific line endings (passed on
+   to ``XMLWriter`` instances).
+-  [versioning] Use versioneer instead of ``setuptools_scm`` to
+   dynamically load version info from a git checkout at import time.
+-  [feaLib] Support backslash-prefixed glyph names.
+
+3.1.2 (released 2016-09-27)
+---------------------------
+
+-  restore Makefile as an alternative way to build/check/install
+-  README.md: update instructions for installing package from source,
+   and for running test suite
+-  NEWS: Change log was out of sync with tagged release
+
+3.1.1 (released 2016-09-27)
+---------------------------
+
+-  Fix ``ttLibVersion`` attribute in TTX files still showing '3.0'
+   instead of '3.1'.
+-  Use ``setuptools_scm`` to manage package versions.
+
+3.1.0 (released 2016-09-26)
+---------------------------
+
+-  [feaLib] New library to parse and compile Adobe FDK OpenType Feature
+   files.
+-  [mtiLib] New library to parse and compile Monotype 'FontDame'
+   OpenType Layout Tables files.
+-  [voltLib] New library to parse Microsoft VOLT project files.
+-  [otlLib] New library to work with OpenType Layout tables.
+-  [varLib] New library to work with OpenType Font Variations.
+-  [pens] Add ttGlyphPen to draw to TrueType glyphs, and t2CharStringPen
+   to draw to Type 2 Charstrings (CFF); add areaPen and perimeterPen.
+-  [ttLib.tables] Implement 'meta' and 'trak' tables.
+-  [ttx] Add --flavor option for compiling to 'woff' or 'woff2'; add
+   ``--with-zopfli`` option to use Zopfli to compress WOFF 1.0 fonts.
+-  [subset] Support subsetting 'COLR'/'CPAL' and 'CBDT'/'CBLC' color
+   fonts tables, and 'gvar' table for variation fonts.
+-  [Snippets] Add ``symfont.py``, for symbolic font statistics analysis;
+   interpolatable.py, a preliminary script for detecting interpolation
+   errors; ``{merge,dump}_woff_metadata.py``.
+-  [classifyTools] Helpers to classify things into classes.
+-  [CI] Run tests on Windows, Linux and macOS using Appveyor and Travis
+   CI; check unit test coverage with Coverage.py/Coveralls; automatic
+   deployment to PyPI on tags.
+-  [loggingTools] Use Python built-in logging module to print messages.
+-  [py23] Make round() behave like Python 3 built-in round(); define
+   round2() and round3().
+
+3.0 (released 2015-09-01)
+-------------------------
+
+-  Add Snippet scripts for cmap subtable format conversion, printing
+   GSUB/GPOS features, building a GX font from two masters
+-  TTX WOFF2 support and a ``-f`` option to overwrite output file(s)
+-  Support GX tables: ``avar``, ``gvar``, ``fvar``, ``meta``
+-  Support ``feat`` and gzip-compressed SVG tables
+-  Upgrade Mac East Asian encodings to native implementation if
+   available
+-  Add Roman Croatian and Romanian encodings, codecs for mac-extended
+   East Asian encodings
+-  Implement optimal GLYF glyph outline packing; disabled by default
+
+2.5 (released 2014-09-24)
+-------------------------
+
+-  Add a Qt pen
+-  Add VDMX table converter
+-  Load all OpenType sub-structures lazily
+-  Add support for cmap format 13.
+-  Add pyftmerge tool
+-  Update to Unicode 6.3.0d3
+-  Add pyftinspect tool
+-  Add support for Google CBLC/CBDT color bitmaps, standard EBLC/EBDT
+   embedded bitmaps, and ``SVG`` table (thanks to Read Roberts at Adobe)
+-  Add support for loading, saving and ttx'ing WOFF file format
+-  Add support for Microsoft COLR/CPAL layered color glyphs
+-  Support PyPy
+-  Support Jython, by replacing numpy with array/lists modules and
+   removed it, pure-Python StringIO, not cStringIO
+-  Add pyftsubset and Subsetter object, supporting CFF and TTF
+-  Add to ttx args for -q for quiet mode, -z to choose a bitmap dump
+   format
+
+2.4 (released 2013-06-22)
+-------------------------
+
+-  Option to write to arbitrary files
+-  Better dump format for DSIG
+-  Better detection of OTF XML
+-  Fix issue with Apple's kern table format
+-  Fix mangling of TT glyph programs
+-  Fix issues related to mona.ttf
+-  Fix Windows Installer instructions
+-  Fix some modern MacOS issues
+-  Fix minor issues and typos
+
+2.3 (released 2009-11-08)
+-------------------------
+
+-  TrueType Collection (TTC) support
+-  Python 2.6 support
+-  Update Unicode data to 5.2.0
+-  Couple of bug fixes
+
+2.2 (released 2008-05-18)
+-------------------------
+
+-  ClearType support
+-  cmap format 1 support
+-  PFA font support
+-  Switched from Numeric to numpy
+-  Update Unicode data to 5.1.0
+-  Update AGLFN data to 1.6
+-  Many bug fixes
+
+2.1 (released 2008-01-28)
+-------------------------
+
+-  Many years worth of fixes and features
+
+2.0b2 (released 2002-??-??)
+---------------------------
+
+-  Be "forgiving" when interpreting the maxp table version field:
+   interpret any value as 1.0 if it's not 0.5. Fixes dumping of these
+   GPL fonts: http://www.freebsd.org/cgi/pds.cgi?ports/chinese/wangttf
+-  Fixed ttx -l: it turned out this part of the code didn't work with
+   Python 2.2.1 and earlier. My bad to do most of my testing with a
+   different version than I shipped TTX with :-(
+-  Fixed bug in ClassDef format 1 subtable (Andreas Seidel bumped into
+   this one).
+
+2.0b1 (released 2002-09-10)
+---------------------------
+
+-  Fixed embarrassing bug: the master checksum in the head table is now
+   calculated correctly even on little-endian platforms (such as Intel).
+-  Made the cmap format 4 compiler smarter: the binary data it creates
+   is now more or less as compact as possible. TTX now makes more
+   compact data than in any shipping font I've tested it with.
+-  Dump glyph names as a separate "GlyphOrder" pseudo table as opposed
+   to as part of the glyf table (obviously needed for CFF-OTF's).
+-  Added proper support for the CFF table.
+-  Don't barf on empty tables (questionable, but "there are font out
+   there...")
+-  When writing TT glyf data, align glyphs on 4-byte boundaries. This
+   seems to be the current recommendation by MS. Also: don't barf on
+   fonts which are already 4-byte aligned.
+-  Windows installer contributed bu Adam Twardoch! Yay!
+-  Changed the command line interface again, now by creating one new
+   tool replacing the old ones: ttx It dumps and compiles, depending on
+   input file types. The options have changed somewhat.
+-  The -d option is back (output dir)
+-  ttcompile's -i options is now called -m (as in "merge"), to avoid
+   clash with dump's -i.
+-  The -s option ("split tables") no longer creates a directory, but
+   instead outputs a small .ttx file containing references to the
+   individual table files. This is not a true link, it's a simple file
+   name, and the referenced file should be in the same directory so
+   ttcompile can find them.
+-  compile no longer accepts a directory as input argument. Instead it
+   can parse the new "mini-ttx" format as output by "ttx -s".
+-  all arguments are input files
+-  Renamed the command line programs and moved them to the Tools
+   subdirectory. They are now installed by the setup.py install script.
+-  Added OpenType support. BASE, GDEF, GPOS, GSUB and JSTF are (almost)
+   fully supported. The XML output is not yet final, as I'm still
+   considering to output certain subtables in a more human-friendly
+   manner.
+-  Fixed 'kern' table to correctly accept subtables it doesn't know
+   about, as well as interpreting Apple's definition of the 'kern' table
+   headers correctly.
+-  Fixed bug where glyphnames were not calculated from 'cmap' if it was
+   (one of the) first tables to be decompiled. More specifically: it
+   cmap was the first to ask for a glyphID -> glyphName mapping.
+-  Switched XML parsers: use expat instead of xmlproc. Should be faster.
+-  Removed my UnicodeString object: I now require Python 2.0 or up,
+   which has unicode support built in.
+-  Removed assert in glyf table: redundant data at the end of the table
+   is now ignored instead of raising an error. Should become a warning.
+-  Fixed bug in hmtx/vmtx code that only occured if all advances were
+   equal.
+-  Fixed subtle bug in TT instruction disassembler.
+-  Couple of fixes to the 'post' table.
+-  Updated OS/2 table to latest spec.
+
+1.0b1 (released 2001-08-10)
+---------------------------
+
+-  Reorganized the command line interface for ttDump.py and
+   ttCompile.py, they now behave more like "normal" command line tool,
+   in that they accept multiple input files for batch processing.
+-  ttDump.py and ttCompile.py don't silently override files anymore, but
+   ask before doing so. Can be overridden by -f.
+-  Added -d option to both ttDump.py and ttCompile.py.
+-  Installation is now done with distutils. (Needs work for environments
+   without compilers.)
+-  Updated installation instructions.
+-  Added some workarounds so as to handle certain buggy fonts more
+   gracefully.
+-  Updated Unicode table to Unicode 3.0 (Thanks Antoine!)
+-  Included a Python script by Adam Twardoch that adds some useful stuff
+   to the Windows registry.
+-  Moved the project to SourceForge.
+
+1.0a6 (released 2000-03-15)
+---------------------------
+
+-  Big reorganization: made ttLib a subpackage of the new fontTools
+   package, changed several module names. Called the entire suite
+   "FontTools"
+-  Added several submodules to fontTools, some new, some older.
+-  Added experimental CFF/GPOS/GSUB support to ttLib, read-only (but XML
+   dumping of GPOS/GSUB is for now disabled)
+-  Fixed hdmx endian bug
+-  Added -b option to ttCompile.py, it disables recalculation of
+   bounding boxes, as requested by Werner Lemberg.
+-  Renamed tt2xml.pt to ttDump.py and xml2tt.py to ttCompile.py
+-  Use ".ttx" as file extension instead of ".xml".
+-  TTX is now the name of the XML-based *format* for TT fonts, and not
+   just an application.
+
+1.0a5
+-----
+
+Never released
+
+-  More tables supported: hdmx, vhea, vmtx
+
+1.0a3 & 1.0a4
+-------------
+
+Never released
+
+-  fixed most portability issues
+-  retracted the "Euro_or_currency" change from 1.0a2: it was
+   nonsense!
+
+1.0a2 (released 1999-05-02)
+---------------------------
+
+-  binary release for MacOS
+-  genenates full FOND resources: including width table, PS font name
+   info and kern table if applicable.
+-  added cmap format 4 support. Extra: dumps Unicode char names as XML
+   comments!
+-  added cmap format 6 support
+-  now accepts true type files starting with "true" (instead of just
+   0x00010000 and "OTTO")
+-  'glyf' table support is now complete: I added support for composite
+   scale, xy-scale and two-by-two for the 'glyf' table. For now,
+   component offset scale behaviour defaults to Apple-style. This only
+   affects the (re)calculation of the glyph bounding box.
+-  changed "Euro" to "Euro_or_currency" in the Standard Apple Glyph
+   order list, since we cannot tell from the 'post' table which is
+   meant. I should probably doublecheck with a Unicode encoding if
+   available. (This does not affect the output!)
+
+Fixed bugs: - 'hhea' table is now recalculated correctly - fixed wrong
+assumption about sfnt resource names
+
+1.0a1 (released 1999-04-27)
+---------------------------
+
+-  initial binary release for MacOS
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..c92fa09
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,1375 @@
+Metadata-Version: 1.2
+Name: fonttools
+Version: 3.28.0
+Summary: Tools to manipulate font files
+Home-page: http://github.com/fonttools/fonttools
+Author: Just van Rossum
+Author-email: just@letterror.com
+Maintainer: Behdad Esfahbod
+Maintainer-email: behdad@behdad.org
+License: MIT
+Description: |Travis Build Status| |Appveyor Build status| |Health| |Coverage Status|
+        |PyPI| |Gitter Chat|
+        
+        What is this?
+        ~~~~~~~~~~~~~
+        
+        | fontTools is a library for manipulating fonts, written in Python. The
+          project includes the TTX tool, that can convert TrueType and OpenType
+          fonts to and from an XML text format, which is also called TTX. It
+          supports TrueType, OpenType, AFM and to an extent Type 1 and some
+          Mac-specific formats. The project has a `MIT open-source
+          licence <LICENSE>`__.
+        | Among other things this means you can use it free of charge.
+        
+        Installation
+        ~~~~~~~~~~~~
+        
+        FontTools requires `Python <http://www.python.org/download/>`__ 2.7, 3.4
+        or later.
+        
+        The package is listed in the Python Package Index (PyPI), so you can
+        install it with `pip <https://pip.pypa.io>`__:
+        
+        .. code:: sh
+        
+            pip install fonttools
+        
+        If you would like to contribute to its development, you can clone the
+        repository from Github, install the package in 'editable' mode and
+        modify the source code in place. We recommend creating a virtual
+        environment, using `virtualenv <https://virtualenv.pypa.io>`__ or
+        Python 3 `venv <https://docs.python.org/3/library/venv.html>`__ module.
+        
+        .. code:: sh
+        
+            # download the source code to 'fonttools' folder
+            git clone https://github.com/fonttools/fonttools.git
+            cd fonttools
+        
+            # create new virtual environment called e.g. 'fonttools-venv', or anything you like
+            python -m virtualenv fonttools-venv
+        
+            # source the `activate` shell script to enter the environment (Un\*x); to exit, just type `deactivate`
+            . fonttools-venv/bin/activate
+        
+            # to activate the virtual environment in Windows `cmd.exe`, do
+            fonttools-venv\Scripts\activate.bat
+        
+            # install in 'editable' mode
+            pip install -e .
+        
+        TTX – From OpenType and TrueType to XML and Back
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+        
+        Once installed you can use the ``ttx`` command to convert binary font
+        files (``.otf``, ``.ttf``, etc) to the TTX xml format, edit them, and
+        convert them back to binary format. TTX files have a .ttx file
+        extension.
+        
+        .. code:: sh
+        
+            ttx /path/to/font.otf
+            ttx /path/to/font.ttx
+        
+        The TTX application works can be used in two ways, depending on what
+        platform you run it on:
+        
+        -  As a command line tool (Windows/DOS, Unix, MacOSX)
+        -  By dropping files onto the application (Windows, MacOS)
+        
+        TTX detects what kind of files it is fed: it will output a ``.ttx`` file
+        when it sees a ``.ttf`` or ``.otf``, and it will compile a ``.ttf`` or
+        ``.otf`` when the input file is a ``.ttx`` file. By default, the output
+        file is created in the same folder as the input file, and will have the
+        same name as the input file but with a different extension. TTX will
+        *never* overwrite existing files, but if necessary will append a unique
+        number to the output filename (before the extension) such as
+        ``Arial#1.ttf``
+        
+        When using TTX from the command line there are a bunch of extra options,
+        these are explained in the help text, as displayed when typing
+        ``ttx -h`` at the command prompt. These additional options include:
+        
+        -  specifying the folder where the output files are created
+        -  specifying which tables to dump or which tables to exclude
+        -  merging partial ``.ttx`` files with existing ``.ttf`` or ``.otf``
+           files
+        -  listing brief table info instead of dumping to ``.ttx``
+        -  splitting tables to separate ``.ttx`` files
+        -  disabling TrueType instruction disassembly
+        
+        The TTX file format
+        -------------------
+        
+        The following tables are currently supported:
+        
+        .. begin table list
+        .. code::
+        
+            BASE, CBDT, CBLC, CFF, CFF2, COLR, CPAL, DSIG, EBDT, EBLC, FFTM,
+            Feat, GDEF, GMAP, GPKG, GPOS, GSUB, Glat, Gloc, HVAR, JSTF, LTSH,
+            MATH, META, MVAR, OS/2, SING, STAT, SVG, Silf, Sill, TSI0, TSI1,
+            TSI2, TSI3, TSI5, TSIB, TSID, TSIJ, TSIP, TSIS, TSIV, TTFA, VDMX,
+            VORG, VVAR, ankr, avar, bsln, cidg, cmap, cvar, cvt, feat, fpgm,
+            fvar, gasp, gcid, glyf, gvar, hdmx, head, hhea, hmtx, kern, lcar,
+            loca, ltag, maxp, meta, mort, morx, name, opbd, post, prep, prop,
+            sbix, trak, vhea and vmtx
+        .. end table list
+        
+        Other tables are dumped as hexadecimal data.
+        
+        TrueType fonts use glyph indices (GlyphIDs) to refer to glyphs in most
+        places. While this is fine in binary form, it is really hard to work
+        with for humans. Therefore we use names instead.
+        
+        The glyph names are either extracted from the ``CFF`` table or the
+        ``post`` table, or are derived from a Unicode ``cmap`` table. In the
+        latter case the Adobe Glyph List is used to calculate names based on
+        Unicode values. If all of these methods fail, names are invented based
+        on GlyphID (eg ``glyph00142``)
+        
+        It is possible that different glyphs use the same name. If this happens,
+        we force the names to be unique by appending ``#n`` to the name (``n``
+        being an integer number.) The original names are being kept, so this has
+        no influence on a "round tripped" font.
+        
+        Because the order in which glyphs are stored inside the binary font is
+        important, we maintain an ordered list of glyph names in the font.
+        
+        Other Tools
+        ~~~~~~~~~~~
+        
+        Commands for inspecting, merging and subsetting fonts are also
+        available:
+        
+        .. code:: sh
+        
+            pyftinspect
+            pyftmerge
+            pyftsubset
+        
+        fontTools Python Module
+        ~~~~~~~~~~~~~~~~~~~~~~~
+        
+        The fontTools python module provides a convenient way to
+        programmatically edit font files.
+        
+        .. code:: py
+        
+            >>> from fontTools.ttLib import TTFont
+            >>> font = TTFont('/path/to/font.ttf')
+            >>> font
+            <fontTools.ttLib.TTFont object at 0x10c34ed50>
+            >>>
+        
+        A selection of sample python programs is in the
+        `Snippets <https://github.com/fonttools/fonttools/blob/master/Snippets/>`__
+        directory.
+        
+        Optional Requirements
+        ---------------------
+        
+        The ``fontTools`` package currently has no (required) external dependencies
+        besides the modules included in the Python Standard Library.
+        However, a few extra dependencies are required by some of its modules, which
+        are needed to unlock optional features.
+        
+        -  ``Lib/fontTools/ttLib/woff2.py``
+        
+           Module to compress/decompress WOFF 2.0 web fonts; it requires:
+        
+           -  `brotli <https://pypi.python.org/pypi/Brotli>`__: Python bindings of
+              the Brotli compression library.
+        
+        -  ``Lib/fontTools/ttLib/sfnt.py``
+        
+           To better compress WOFF 1.0 web fonts, the following module can be used
+           instead of the built-in ``zlib`` library:
+        
+           -  `zopfli <https://pypi.python.org/pypi/zopfli>`__: Python bindings of
+              the Zopfli compression library.
+        
+        -  ``Lib/fontTools/unicode.py``
+        
+           To display the Unicode character names when dumping the ``cmap`` table
+           with ``ttx`` we use the ``unicodedata`` module in the Standard Library.
+           The version included in there varies between different Python versions.
+           To use the latest available data, you can install:
+        
+           -  `unicodedata2 <https://pypi.python.org/pypi/unicodedata2>`__:
+              ``unicodedata`` backport for Python 2.7 and 3.5 updated to the latest
+              Unicode version 9.0. Note this is not necessary if you use Python 3.6
+              as the latter already comes with an up-to-date ``unicodedata``.
+        
+        -  ``Lib/fontTools/varLib/interpolatable.py``
+        
+           Module for finding wrong contour/component order between different masters.
+           It requires one of the following packages in order to solve the so-called
+           "minimum weight perfect matching problem in bipartite graphs", or
+           the Assignment problem:
+        
+           *  `scipy <https://pypi.python.org/pypi/scipy>`__: the Scientific Library
+              for Python, which internally uses `NumPy <https://pypi.python.org/pypi/numpy>`__
+              arrays and hence is very fast;
+           *  `munkres <https://pypi.python.org/pypi/munkres>`__: a pure-Python
+              module that implements the Hungarian or Kuhn-Munkres algorithm.
+        
+        -  ``Lib/fontTools/misc/symfont.py``
+        
+           Advanced module for symbolic font statistics analysis; it requires:
+        
+           *  `sympy <https://pypi.python.org/pypi/sympy>`__: the Python library for
+              symbolic mathematics.
+        
+        -  ``Lib/fontTools/t1Lib.py``
+        
+           To get the file creator and type of Macintosh PostScript Type 1 fonts
+           on Python 3 you need to install the following module, as the old ``MacOS``
+           module is no longer included in Mac Python:
+        
+           *  `xattr <https://pypi.python.org/pypi/xattr>`__: Python wrapper for
+              extended filesystem attributes (macOS platform only).
+        
+        -  ``Lib/fontTools/pens/cocoaPen.py``
+        
+           Pen for drawing glyphs with Cocoa ``NSBezierPath``, requires:
+        
+           *  `PyObjC <https://pypi.python.org/pypi/pyobjc>`__: the bridge between
+              Python and the Objective-C runtime (macOS platform only).
+        
+        -  ``Lib/fontTools/pens/qtPen.py``
+        
+           Pen for drawing glyphs with Qt's ``QPainterPath``, requires:
+        
+           *  `PyQt5 <https://pypi.python.org/pypi/PyQt5>`__: Python bindings for
+              the Qt cross platform UI and application toolkit.
+        
+        -  ``Lib/fontTools/pens/reportLabPen.py``
+        
+           Pen to drawing glyphs as PNG images, requires:
+        
+           *  `reportlab <https://pypi.python.org/pypi/reportlab>`__: Python toolkit
+              for generating PDFs and graphics.
+        
+        -  ``Lib/fontTools/inspect.py``
+        
+           A GUI font inspector, requires one of the following packages:
+        
+           *  `PyGTK <https://pypi.python.org/pypi/PyGTK>`__: Python bindings for
+              GTK  2.x (only works with Python 2).
+           *  `PyGObject <https://wiki.gnome.org/action/show/Projects/PyGObject>`__ :
+              Python bindings for GTK 3.x and gobject-introspection libraries (also
+              compatible with Python 3).
+        
+        Testing
+        ~~~~~~~
+        
+        To run the test suite, you can do:
+        
+        .. code:: sh
+        
+            python setup.py test
+        
+        If you have `pytest <http://docs.pytest.org/en/latest/>`__, you can run
+        the ``pytest`` command directly. The tests will run against the
+        installed ``fontTools`` package, or the first one found in the
+        ``PYTHONPATH``.
+        
+        You can also use `tox <https://testrun.org/tox/latest/>`__ to
+        automatically run tests on different Python versions in isolated virtual
+        environments.
+        
+        .. code:: sh
+        
+            pip install tox
+            tox
+        
+        Note that when you run ``tox`` without arguments, the tests are executed
+        for all the environments listed in tox.ini's ``envlist``. In our case,
+        this includes Python 2.7 and 3.6, so for this to work the ``python2.7``
+        and ``python3.6`` executables must be available in your ``PATH``.
+        
+        You can specify an alternative environment list via the ``-e`` option,
+        or the ``TOXENV`` environment variable:
+        
+        .. code:: sh
+        
+            tox -e py27-nocov
+            TOXENV="py36-cov,htmlcov" tox
+        
+        Development Community
+        ~~~~~~~~~~~~~~~~~~~~~
+        
+        TTX/FontTools development is ongoing in an active community of
+        developers, that includes professional developers employed at major
+        software corporations and type foundries as well as hobbyists.
+        
+        Feature requests and bug reports are always welcome at
+        https://github.com/fonttools/fonttools/issues/
+        
+        The best place for discussions about TTX from an end-user perspective as
+        well as TTX/FontTools development is the
+        https://groups.google.com/d/forum/fonttools mailing list. There is also
+        a development https://groups.google.com/d/forum/fonttools-dev mailing
+        list for continuous integration notifications. You can also email Behdad
+        privately at behdad@behdad.org
+        
+        History
+        ~~~~~~~
+        
+        The fontTools project was started by Just van Rossum in 1999, and was
+        maintained as an open source project at
+        http://sourceforge.net/projects/fonttools/. In 2008, Paul Wise (pabs3)
+        began helping Just with stability maintenance. In 2013 Behdad Esfahbod
+        began a friendly fork, thoroughly reviewing the codebase and making
+        changes at https://github.com/behdad/fonttools to add new features and
+        support for new font formats.
+        
+        Acknowledgements
+        ~~~~~~~~~~~~~~~~
+        
+        In alphabetical order:
+        
+        Olivier Berten, Samyak Bhuta, Erik van Blokland, Petr van Blokland,
+        Jelle Bosma, Sascha Brawer, Tom Byrer, Frédéric Coiffier, Vincent
+        Connare, Dave Crossland, Simon Daniels, Behdad Esfahbod, Behnam
+        Esfahbod, Hannes Famira, Sam Fishman, Matt Fontaine, Yannis Haralambous,
+        Greg Hitchcock, Jeremie Hornus, Khaled Hosny, John Hudson, Denis Moyogo
+        Jacquerye, Jack Jansen, Tom Kacvinsky, Jens Kutilek, Antoine Leca,
+        Werner Lemberg, Tal Leming, Peter Lofting, Cosimo Lupo, Masaya Nakamura,
+        Dave Opstad, Laurence Penney, Roozbeh Pournader, Garret Rieger, Read
+        Roberts, Guido van Rossum, Just van Rossum, Andreas Seidel, Georg
+        Seifert, Miguel Sousa, Adam Twardoch, Adrien Tétar, Vitaly Volkov, Paul
+        Wise.
+        
+        Copyrights
+        ~~~~~~~~~~
+        
+        | Copyright (c) 1999-2004 Just van Rossum, LettError
+          (just@letterror.com)
+        | See `LICENSE <LICENSE>`__ for the full license.
+        
+        Copyright (c) 2000 BeOpen.com. All Rights Reserved.
+        
+        Copyright (c) 1995-2001 Corporation for National Research Initiatives.
+        All Rights Reserved.
+        
+        Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam. All
+        Rights Reserved.
+        
+        Have fun!
+        
+        .. |Travis Build Status| image:: https://travis-ci.org/fonttools/fonttools.svg
+           :target: https://travis-ci.org/fonttools/fonttools
+        .. |Appveyor Build status| image:: https://ci.appveyor.com/api/projects/status/0f7fmee9as744sl7/branch/master?svg=true
+           :target: https://ci.appveyor.com/project/fonttools/fonttools/branch/master
+        .. |Health| image:: https://landscape.io/github/behdad/fonttools/master/landscape.svg?style=flat
+           :target: https://landscape.io/github/behdad/fonttools/master
+        .. |Coverage Status| image:: https://codecov.io/gh/fonttools/fonttools/branch/master/graph/badge.svg
+           :target: https://codecov.io/gh/fonttools/fonttools
+        .. |PyPI| image:: https://img.shields.io/pypi/v/fonttools.svg
+           :target: https://pypi.org/project/FontTools
+        .. |Gitter Chat| image:: https://badges.gitter.im/fonttools-dev/Lobby.svg
+           :alt: Join the chat at https://gitter.im/fonttools-dev/Lobby
+           :target: https://gitter.im/fonttools-dev/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
+        
+        Changelog
+        ~~~~~~~~~
+        
+        3.28.0 (released 2018-06-19)
+        ----------------------------
+        
+        - [featureVars] Added experimental module to build ``FeatureVariations``
+          tables. Still needs to be hooked up to ``varLib.build`` (#1240).
+        - [fixedTools] Added ``otRound`` to round floats to nearest integer towards
+          positive Infinity. This is now used where we deal with visual data like X/Y
+          coordinates, advance widths/heights, variation deltas, and similar (#1274,
+          #1248).
+        - [subset] Improved GSUB closure memoize algorithm.
+        - [varLib.models] Fixed regression in model resolution (180124, #1269).
+        - [feaLib.ast] Fixed error when converting ``SubtableStatement`` to string
+          (#1275).
+        - [varLib.mutator] Set ``OS/2.usWeightClass`` and ``usWidthClass``, and
+          ``post.italicAngle`` based on the 'wght', 'wdth' and 'slnt' axis values
+          (#1276, #1264).
+        - [py23/loggingTools] Don't automatically set ``logging.lastResort`` handler
+          on py27. Moved ``LastResortLogger`` to the ``loggingTools`` module (#1277).
+        
+        3.27.1 (released 2018-06-11)
+        ----------------------------
+        
+        - [ttGlyphPen] Issue a warning and skip building non-existing components
+          (https://github.com/googlei18n/fontmake/issues/411).
+        - [tests] Fixed issue running ttx_test.py from a tagged commit.
+        
+        3.27.0 (released 2018-06-11)
+        ----------------------------
+        
+        - [designspaceLib] Added new ``conditionSet`` element to ``rule`` element in
+          designspace document. Bumped ``format`` attribute to ``4.0`` (previously,
+          it was formatted as an integer). Removed ``checkDefault``, ``checkAxes``
+          methods, and any kind of guessing about the axes when the ``<axes>`` element
+          is missing. The default master is expected at the intersection of all default
+          values for each axis (#1254, #1255, #1267).
+        - [cffLib] Fixed issues when compiling CFF2 or converting from CFF when the
+          font has an FDArray (#1211, #1271).
+        - [varLib] Avoid attempting to build ``cvar`` table when ``glyf`` table is not
+          present, as is the case for CFF2 fonts.
+        - [subset] Handle None coverages in MarkGlyphSets; revert commit 02616ab that
+          sets empty Coverage tables in MarkGlyphSets to None, to make OTS happy.
+        - [ttFont] Allow to build glyph order from ``maxp.numGlyphs`` when ``post`` or
+          ``cmap`` are missing.
+        - [ttFont] Added ``__len__`` method to ``_TTGlyphSet``.
+        - [glyf] Ensure ``GlyphCoordinates`` never overflow signed shorts (#1230).
+        - [py23] Added alias for ``itertools.izip`` shadowing the built-in ``zip``.
+        - [loggingTools] Memoize ``log`` property of ``LogMixin`` class (fbab12).
+        - [ttx] Impoved test coverage (#1261).
+        - [Snippets] Addded script to append a suffix to all family names in a font.
+        - [varLib.plot] Make it work with matplotlib >= 2.1 (b38e2b).
+        
+        3.26.0 (released 2018-05-03)
+        ----------------------------
+        
+        - [designspace] Added a new optional ``layer`` attribute to the source element,
+          and a corresponding ``layerName`` attribute to the ``SourceDescriptor``
+          object (#1253).
+          Added ``conditionset`` element to the ``rule`` element to the spec, but not
+          implemented in designspace reader/writer yet (#1254).
+        - [varLib.models] Refine modeling one last time (0ecf5c5).
+        - [otBase] Fixed sharing of tables referred to by different offset sizes
+          (795f2f9).
+        - [subset] Don't drop a GDEF that only has VarStore (fc819d6). Set to None
+          empty Coverage tables in MarkGlyphSets (02616ab).
+        - [varLib]: Added ``--master-finder`` command-line option (#1249).
+        - [varLib.mutator] Prune fvar nameIDs from instance's name table (#1245).
+        - [otTables] Allow decompiling bad ClassDef tables with invalid format, with
+          warning (#1236).
+        - [varLib] Make STAT v1.2 and reuse nameIDs from fvar table (#1242).
+        - [varLib.plot] Show master locations. Set axis limits to -1, +1.
+        - [subset] Handle HVAR direct mapping. Passthrough 'cvar'.
+          Added ``--font-number`` command-line option for collections.
+        - [t1Lib] Allow a text encoding to be specified when parsing a Type 1 font
+          (#1234). Added ``kind`` argument to T1Font constructor (c5c161c).
+        - [ttLib] Added context manager API to ``TTFont`` class, so it can be used in
+          ``with`` statements to auto-close the file when exiting the context (#1232).
+        
+        3.25.0 (released 2018-04-03)
+        ----------------------------
+        
+        - [varLib] Improved support-resolution algorithm. Previously, the on-axis
+          masters would always cut the space. They don't anymore. That's more
+          consistent, and fixes the main issue Erik showed at TYPO Labs 2017.
+          Any varfont built that had an unusual master configuration will change
+          when rebuilt (42bef17, a523a697,
+          https://github.com/googlei18n/fontmake/issues/264).
+        - [varLib.models] Added a ``main()`` entry point, that takes positions and
+          prints model results.
+        - [varLib.plot] Added new module to plot a designspace's
+          VariationModel. Requires ``matplotlib``.
+        - [varLib.mutator] Added -o option to specify output file path (2ef60fa).
+        - [otTables] Fixed IndexError while pruning of HVAR pre-write (6b6c34a).
+        - [varLib.models] Convert delta array to floats if values overflows signed
+          short integer (0055f94).
+        
+        3.24.2 (released 2018-03-26)
+        ----------------------------
+        
+        - [otBase] Don't fail during ``ValueRecord`` copy if src has more items.
+          We drop hinting in the subsetter by simply changing ValueFormat, without
+          cleaning up the actual ValueRecords. This was causing assertion error if
+          a variable font was subsetted without hinting and then passed directly to
+          the mutator for instantiation without first it saving to disk.
+        
+        3.24.1 (released 2018-03-06)
+        ----------------------------
+        
+        - [varLib] Don't remap the same ``DeviceTable`` twice in VarStore optimizer
+          (#1206).
+        - [varLib] Add ``--disable-iup`` option to ``fonttools varLib`` script,
+          and a ``optimize=True`` keyword argument to ``varLib.build`` function,
+          to optionally disable IUP optimization while building varfonts.
+        - [ttCollection] Fixed issue while decompiling ttc with python3 (#1207).
+        
+        3.24.0 (released 2018-03-01)
+        ----------------------------
+        
+        - [ttGlyphPen] Decompose composite glyphs if any components' transform is too
+          large to fit a ``F2Dot14`` value, or clamp transform values that are
+          (almost) equal to +2.0 to make them fit and avoid decomposing (#1200,
+          #1204, #1205).
+        - [ttx] Added new ``-g`` option to dump glyphs from the ``glyf`` table
+          splitted as individual ttx files (#153, #1035, #1132, #1202).
+        - Copied ``ufoLib.filenames`` module to ``fontTools.misc.filenames``, used
+          for the ttx split-glyphs option (#1202).
+        - [feaLib] Added support for ``cvParameters`` blocks in Character Variant
+          feautures ``cv01-cv99`` (#860, #1169).
+        - [Snippets] Added ``checksum.py`` script to generate/check SHA1 hash of
+          ttx files (#1197).
+        - [varLib.mutator] Fixed issue while instantiating some variable fonts
+          whereby the horizontal advance width computed from ``gvar`` phantom points
+          could turn up to be negative (#1198).
+        - [varLib/subset] Fixed issue with subsetting GPOS variation data not
+          picking up ``ValueRecord`` ``Device`` objects (54fd71f).
+        - [feaLib/voltLib] In all AST elements, the ``location`` is no longer a
+          required positional argument, but an optional kewyord argument (defaults
+          to ``None``). This will make it easier to construct feature AST from
+          code (#1201).
+        
+        
+        3.23.0 (released 2018-02-26)
+        ----------------------------
+        
+        - [designspaceLib] Added an optional ``lib`` element to the designspace as a
+          whole, as well as to the instance elements, to store arbitrary data in a
+          property list dictionary, similar to the UFO's ``lib``. Added an optional
+          ``font`` attribute to the ``SourceDescriptor``, to allow operating on
+          in-memory font objects (#1175).
+        - [cffLib] Fixed issue with lazy-loading of attributes when attempting to
+          set the CFF TopDict.Encoding (#1177, #1187).
+        - [ttx] Fixed regression introduced in 3.22.0 that affected the split tables
+          ``-s`` option (#1188).
+        - [feaLib] Added ``IncludedFeaNotFound`` custom exception subclass, raised
+          when an included feature file cannot be found (#1186).
+        - [otTables] Changed ``VarIdxMap`` to use glyph names internally instead of
+          glyph indexes. The old ttx dumps of HVAR/VVAR tables that contain indexes
+          can still be imported (21cbab8, 38a0ffb).
+        - [varLib] Implemented VarStore optimizer (#1184).
+        - [subset] Implemented pruning of GDEF VarStore, HVAR and MVAR (#1179).
+        - [sfnt] Restore backward compatiblity with ``numFonts`` attribute of
+          ``SFNTReader`` object (#1181).
+        - [merge] Initial support for merging ``LangSysRecords`` (#1180).
+        - [ttCollection] don't seek(0) when writing to possibly unseekable strems.
+        - [subset] Keep all ``--name-IDs`` from 0 to 6 by default (#1170, #605, #114).
+        - [cffLib] Added ``width`` module to calculate optimal CFF default and
+          nominal glyph widths.
+        - [varLib] Don’t fail if STAT already in the master fonts (#1166).
+        
+        3.22.0 (released 2018-02-04)
+        ----------------------------
+        
+        - [subset] Support subsetting ``endchar`` acting as ``seac``-like components
+          in ``CFF`` (fixes #1162).
+        - [feaLib] Allow to build from pre-parsed ``ast.FeatureFile`` object.
+          Added ``tables`` argument to only build some tables instead of all (#1159,
+          #1163).
+        - [textTools] Replaced ``safeEval`` with ``ast.literal_eval`` (#1139).
+        - [feaLib] Added option to the parser to not resolve ``include`` statements
+          (#1154).
+        - [ttLib] Added new ``ttCollection`` module to read/write TrueType and
+          OpenType Collections. Exports a ``TTCollection`` class with a ``fonts``
+          attribute containing a list of ``TTFont`` instances, the methods ``save``
+          and ``saveXML``, plus some list-like methods. The ``importXML`` method is
+          not implemented yet (#17).
+        - [unicodeadata] Added ``ot_tag_to_script`` function that converts from
+          OpenType script tag to Unicode script code.
+        - Added new ``designspaceLib`` subpackage, originally from Erik Van Blokland's
+          ``designSpaceDocument``: https://github.com/LettError/designSpaceDocument
+          NOTE: this is not yet used internally by varLib, and the API may be subject
+          to changes (#911, #1110, LettError/designSpaceDocument#28).
+        - Added new FontTools icon images (8ee7c32).
+        - [unicodedata] Added ``script_horizontal_direction`` function that returns
+          either "LTR" or "RTL" given a unicode script code.
+        - [otConverters] Don't write descriptive name string as XML comment if the
+          NameID value is 0 (== NULL) (#1151, #1152).
+        - [unicodedata] Add ``ot_tags_from_script`` function to get the list of
+          OpenType script tags associated with unicode script code (#1150).
+        - [feaLib] Don't error when "enumerated" kern pairs conflict with preceding
+          single pairs; emit warning and chose the first value (#1147, #1148).
+        - [loggingTools] In ``CapturingLogHandler.assertRegex`` method, match the
+          fully formatted log message.
+        - [sbix] Fixed TypeError when concatenating str and bytes (#1154).
+        - [bezierTools] Implemented cusp support and removed ``approximate_fallback``
+          arg in ``calcQuadraticArcLength``. Added ``calcCubicArcLength`` (#1142).
+        
+        3.21.2 (released 2018-01-08)
+        ----------------------------
+        
+        - [varLib] Fixed merging PairPos Format1/2 with missing subtables (#1125).
+        
+        3.21.1 (released 2018-01-03)
+        ----------------------------
+        
+        - [feaLib] Allow mixed single/multiple substitutions (#612)
+        - Added missing ``*.afm`` test assets to MAINFEST.in (#1137).
+        - Fixed dumping ``SVG`` tables containing color palettes (#1124).
+        
+        3.21.0 (released 2017-12-18)
+        ----------------------------
+        
+        - [cmap] when compiling format6 subtable, don't assume gid0 is always called
+          '.notdef' (1e42224).
+        - [ot] Allow decompiling fonts with bad Coverage format number (1aafae8).
+        - Change FontTools licence to MIT (#1127).
+        - [post] Prune extra names already in standard Mac set (df1e8c7).
+        - [subset] Delete empty SubrsIndex after subsetting (#994, #1118).
+        - [varLib] Don't share points in cvar by default, as it currently fails on
+          some browsers (#1113).
+        - [afmLib] Make poor old afmLib work on python3.
+        
+        3.20.1 (released 2017-11-22)
+        ----------------------------
+        
+        - [unicodedata] Fixed issue with ``script`` and ``script_extension`` functions
+          returning inconsistent short vs long names. They both return the short four-
+          letter script codes now. Added ``script_name`` and ``script_code`` functions
+          to look up the long human-readable script name from the script code, and
+          viceversa (#1109, #1111).
+        
+        3.20.0 (released 2017-11-21)
+        ----------------------------
+        
+        - [unicodedata] Addded new module ``fontTools.unicodedata`` which exports the
+          same interface as the built-in ``unicodedata`` module, with the addition of
+          a few functions that are missing from the latter, such as ``script``,
+          ``script_extension`` and ``block``. Added a ``MetaTools/buildUCD.py`` script
+          to download and parse data files from the Unicode Character Database and
+          generate python modules containing lists of ranges and property values.
+        - [feaLib] Added ``__str__`` method to all ``ast`` elements (delegates to the
+          ``asFea`` method).
+        - [feaLib] ``Parser`` constructor now accepts a ``glyphNames`` iterable
+          instead of ``glyphMap`` dict. The latter still works but with a pending
+          deprecation warning (#1104).
+        - [bezierTools] Added arc length calculation functions originally from
+          ``pens.perimeterPen`` module (#1101).
+        - [varLib] Started generating STAT table (8af4309). Right now it just reflects
+          the axes, and even that with certain limitations:
+          * AxisOrdering is set to the order axes are defined,
+          * Name-table entries are not shared with fvar.
+        - [py23] Added backports for ``redirect_stdout`` and ``redirect_stderr``
+          context managers (#1097).
+        - [Graphite] Fixed some round-trip bugs (#1093).
+        
+        3.19.0 (released 2017-11-06)
+        ----------------------------
+        
+        - [varLib] Try set of used points instead of all points when testing whether to
+          share points between tuples (#1090).
+        - [CFF2] Fixed issue with reading/writing PrivateDict BlueValues to TTX file.
+          Read the commit message 8b02b5a and issue #1030 for more details.
+          NOTE: this change invalidates all the TTX files containing CFF2 tables
+          that where dumped with previous verisons of fonttools.
+          CFF2 Subr items can have values on the stack after the last operator, thus
+          a ``CFF2Subr`` class was added to accommodate this (#1091).
+        - [_k_e_r_n] Fixed compilation of AAT kern version=1.0 tables (#1089, #1094)
+        - [ttLib] Added getBestCmap() convenience method to TTFont class and cmap table
+          class that returns a preferred Unicode cmap subtable given a list of options
+          (#1092).
+        - [morx] Emit more meaningful subtable flags. Implement InsertionMorphAction
+        
+        3.18.0 (released 2017-10-30)
+        ----------------------------
+        
+        - [feaLib] Fixed writing back nested glyph classes (#1086).
+        - [TupleVariation] Reactivated shared points logic, bugfixes (#1009).
+        - [AAT] Implemented ``morx`` ligature subtables (#1082).
+        - [reverseContourPen] Keep duplicate lineTo following a moveTo (#1080,
+          https://github.com/googlei18n/cu2qu/issues/51).
+        - [varLib.mutator] Suport instantiation of GPOS, GDEF and MVAR (#1079).
+        - [sstruct] Fixed issue with ``unicode_literals`` and ``struct`` module in
+          old versions of python 2.7 (#993).
+        
+        3.17.0 (released 2017-10-16)
+        ----------------------------
+        
+        - [svgPathPen] Added an ``SVGPathPen`` that translates segment pen commands
+          into SVG path descriptions. Copied from Tal Leming's ``ufo2svg.svgPathPen``
+          https://github.com/typesupply/ufo2svg/blob/d69f992/Lib/ufo2svg/svgPathPen.py
+        - [reverseContourPen] Added ``ReverseContourPen``, a filter pen that draws
+          contours with the winding direction reversed, while keeping the starting
+          point (#1071).
+        - [filterPen] Added ``ContourFilterPen`` to manipulate contours as a whole
+          rather than segment by segment.
+        - [arrayTools] Added ``Vector`` class to apply math operations on an array
+          of numbers, and ``pairwise`` function to loop over pairs of items in an
+          iterable.
+        - [varLib] Added support for building and interpolation of ``cvar`` table
+          (f874cf6, a25a401).
+        
+        3.16.0 (released 2017-10-03)
+        ----------------------------
+        
+        - [head] Try using ``SOURCE_DATE_EPOCH`` environment variable when setting
+          the ``head`` modified timestamp to ensure reproducible builds (#1063).
+          See https://reproducible-builds.org/specs/source-date-epoch/
+        - [VTT] Decode VTT's ``TSI*`` tables text as UTF-8 (#1060).
+        - Added support for Graphite font tables: Feat, Glat, Gloc, Silf and Sill.
+          Thanks @mhosken! (#1054).
+        - [varLib] Default to using axis "name" attribute if "labelname" element
+          is missing (588f524).
+        - [merge] Added support for merging Script records. Remove unused features
+          and lookups after merge (d802580, 556508b).
+        - Added ``fontTools.svgLib`` package. Includes a parser for SVG Paths that
+          supports the Pen protocol (#1051). Also, added a snippet to convert SVG
+          outlines to UFO GLIF (#1053).
+        - [AAT] Added support for ``ankr``, ``bsln``, ``mort``, ``morx``, ``gcid``,
+          and ``cidg``.
+        - [subset] Implemented subsetting of ``prop``, ``opbd``, ``bsln``, ``lcar``.
+        
+        3.15.1 (released 2017-08-18)
+        ----------------------------
+        
+        - [otConverters] Implemented ``__add__`` and ``__radd__`` methods on
+          ``otConverters._LazyList`` that decompile a lazy list before adding
+          it to another list or ``_LazyList`` instance. Fixes an ``AttributeError``
+          in the ``subset`` module when attempting to sum ``_LazyList`` objects
+          (6ef48bd2, 1aef1683).
+        - [AAT] Support the `opbd` table with optical bounds (a47f6588).
+        - [AAT] Support `prop` table with glyph properties (d05617b4).
+        
+        
+        3.15.0 (released 2017-08-17)
+        ----------------------------
+        
+        - [AAT] Added support for AAT lookups. The ``lcar`` table can be decompiled
+          and recompiled; futher work needed to handle ``morx`` table (#1025).
+        - [subset] Keep (empty) DefaultLangSys for Script 'DFLT' (6eb807b5).
+        - [subset] Support GSUB/GPOS.FeatureVariations (fe01d87b).
+        - [varLib] In ``models.supportScalars``, ignore an axis when its peak value
+          is 0 (fixes #1020).
+        - [varLib] Add default mappings to all axes in avar to fix rendering issue
+          in some rasterizers (19c4b377, 04eacf13).
+        - [varLib] Flatten multiple tail PairPosFormat2 subtables before merging
+          (c55ef525).
+        - [ttLib] Added support for recalculating font bounding box in ``CFF`` and
+          ``head`` tables, and min/max values in ``hhea`` and ``vhea`` tables (#970).
+        
+        3.14.0 (released 2017-07-31)
+        ----------------------------
+        
+        - [varLib.merger] Remove Extensions subtables before merging (f7c20cf8).
+        - [varLib] Initialize the avar segment map with required default entries
+          (#1014).
+        - [varLib] Implemented optimal IUP optmiziation (#1019).
+        - [otData] Add ``AxisValueFormat4`` for STAT table v1.2 from OT v1.8.2
+          (#1015).
+        - [name] Fixed BCP46 language tag for Mac langID=9: 'si' -> 'sl'.
+        - [subset] Return value from ``_DehintingT2Decompiler.op_hintmask``
+          (c0d672ba).
+        - [cffLib] Allow to get TopDict by index as well as by name (dca96c9c).
+        - [cffLib] Removed global ``isCFF2`` state; use one set of classes for
+          both CFF and CFF2, maintaining backward compatibility existing code (#1007).
+        - [cffLib] Deprecated maxstack operator, per OpenType spec update 1.8.1.
+        - [cffLib] Added missing default (-100) for UnderlinePosition (#983).
+        - [feaLib] Enable setting nameIDs greater than 255 (#1003).
+        - [varLib] Recalculate ValueFormat when merging SinglePos (#996).
+        - [varLib] Do not emit MVAR if there are no entries in the variation store
+          (#987).
+        - [ttx] For ``-x`` option, pad with space if table tag length is < 4.
+        
+        3.13.1 (released 2017-05-30)
+        ----------------------------
+        
+        - [feaLib.builder] Removed duplicate lookups optimization. The original
+          lookup order and semantics of the feature file are preserved (#976).
+        
+        3.13.0 (released 2017-05-24)
+        ----------------------------
+        
+        - [varLib.mutator] Implement IUP optimization (#969).
+        - [_g_l_y_f.GlyphCoordinates] Changed ``__bool__()`` semantics to match those
+          of other iterables (e46f949). Removed ``__abs__()`` (3db5be2).
+        - [varLib.interpolate_layout] Added ``mapped`` keyword argument to
+          ``interpolate_layout`` to allow disabling avar mapping: if False (default),
+          the location is mapped using the map element of the axes in designspace file;
+          if True, it is assumed that location is in designspace's internal space and
+          no mapping is performed (#950, #975).
+        - [varLib.interpolate_layout] Import designspace-loading logic from varLib.
+        - [varLib] Fixed bug with recombining PairPosClass2 subtables (81498e5, #914).
+        - [cffLib.specializer] When copying iterables, cast to list (462b7f86).
+        
+        3.12.1 (released 2017-05-18)
+        ----------------------------
+        
+        - [pens.t2CharStringPen] Fixed AttributeError when calling addComponent in
+          T2CharStringPen (#965).
+        
+        3.12.0 (released 2017-05-17)
+        ----------------------------
+        
+        - [cffLib.specializer] Added new ``specializer`` module to optimize CFF
+          charstrings, used by the T2CharStringPen (#948).
+        - [varLib.mutator] Sort glyphs by component depth before calculating composite
+          glyphs' bounding boxes to ensure deltas are correctly caclulated (#945).
+        - [_g_l_y_f] Fixed loss of precision in GlyphCoordinates by using 'd' (double)
+          instead of 'f' (float) as ``array.array`` typecode (#963, #964).
+        
+        3.11.0 (released 2017-05-03)
+        ----------------------------
+        
+        - [t2CharStringPen] Initial support for specialized Type2 path operators:
+          vmoveto, hmoveto, vlineto, hlineto, vvcurveto, hhcurveto, vhcurveto and
+          hvcurveto. This should produce more compact charstrings (#940, #403).
+        - [Doc] Added Sphinx sources for the documentation. Thanks @gferreira (#935).
+        - [fvar] Expose flags in XML (#932)
+        - [name] Add helper function for building multi-lingual names (#921)
+        - [varLib] Fixed kern merging when a PairPosFormat2 has ClassDef1 with glyphs
+          that are NOT present in the Coverage (1b5e1c4, #939).
+        - [varLib] Fixed non-deterministic ClassDef order with PY3 (f056c12, #927).
+        - [feLib] Throw an error when the same glyph is defined in multiple mark
+          classes within the same lookup (3e3ff00, #453).
+        
+        3.10.0 (released 2017-04-14)
+        ----------------------------
+        
+        - [varLib] Added support for building ``avar`` table, using the designspace
+          ``<map>`` elements.
+        - [varLib] Removed unused ``build(..., axisMap)`` argument. Axis map should
+          be specified in designspace file now. We do not accept nonstandard axes
+          if ``<axes>`` element is not present.
+        - [varLib] Removed "custom" axis from the ``standard_axis_map``. This was
+          added before when glyphsLib was always exporting the (unused) custom axis.
+        - [varLib] Added partial support for building ``MVAR`` table; does not
+          implement ``gasp`` table variations yet.
+        - [pens] Added FilterPen base class, for pens that control another pen;
+          factored out ``addComponent`` method from BasePen into a separate abstract
+          DecomposingPen class; added DecomposingRecordingPen, which records
+          components decomposed as regular contours.
+        - [TSI1] Fixed computation of the textLength of VTT private tables (#913).
+        - [loggingTools] Added ``LogMixin`` class providing a ``log`` property to
+          subclasses, which returns a ``logging.Logger`` named after the latter.
+        - [loggingTools] Added ``assertRegex`` method to ``CapturingLogHandler``.
+        - [py23] Added backport for python 3's ``types.SimpleNamespace`` class.
+        - [EBLC] Fixed issue with python 3 ``zip`` iterator.
+        
+        3.9.2 (released 2017-04-08)
+        ---------------------------
+        
+        - [pens] Added pen to draw glyphs using WxPython ``GraphicsPath`` class:
+          https://wxpython.org/docs/api/wx.GraphicsPath-class.html
+        - [varLib.merger] Fixed issue with recombining multiple PairPosFormat2
+          subtables (#888)
+        - [varLib] Do not encode gvar deltas that are all zeroes, or if all values
+          are smaller than tolerance.
+        - [ttLib] _TTGlyphSet glyphs now also have ``height`` and ``tsb`` (top
+          side bearing) attributes from the ``vmtx`` table, if present.
+        - [glyf] In ``GlyphCoordintes`` class, added ``__bool__`` / ``__nonzero__``
+          methods, and ``array`` property to get raw array.
+        - [ttx] Support reading TTX files with BOM (#896)
+        - [CFF2] Fixed the reporting of the number of regions in the font.
+        
+        3.9.1 (released 2017-03-20)
+        ---------------------------
+        
+        - [varLib.merger] Fixed issue while recombining multiple PairPosFormat2
+          subtables if they were split because of offset overflows (9798c30).
+        - [varLib.merger] Only merge multiple PairPosFormat1 subtables if there is
+          at least one of the fonts with a non-empty Format1 subtable (0f5a46b).
+        - [varLib.merger] Fixed IndexError with empty ClassDef1 in PairPosFormat2
+          (aad0d46).
+        - [varLib.merger] Avoid reusing Class2Record (mutable) objects (e6125b3).
+        - [varLib.merger] Calculate ClassDef1 and ClassDef2's Format when merging
+          PairPosFormat2 (23511fd).
+        - [macUtils] Added missing ttLib import (b05f203).
+        
+        3.9.0 (released 2017-03-13)
+        ---------------------------
+        
+        - [feaLib] Added (partial) support for parsing feature file comments ``# ...``
+          appearing in between statements (#879).
+        - [feaLib] Cleaned up syntax tree for FeatureNames.
+        - [ttLib] Added support for reading/writing ``CFF2`` table (thanks to
+          @readroberts at Adobe), and ``TTFA`` (ttfautohint) table.
+        - [varLib] Fixed regression introduced with 3.8.0 in the calculation of
+          ``NumShorts``, i.e. the number of deltas in ItemVariationData's delta sets
+          that use a 16-bit representation (b2825ff).
+        
+        3.8.0 (released 2017-03-05)
+        ---------------------------
+        
+        - New pens: MomentsPen, StatisticsPen, RecordingPen, and TeePen.
+        - [misc] Added new ``fontTools.misc.symfont`` module, for symbolic font
+          statistical analysis; requires ``sympy`` (http://www.sympy.org/en/index.html)
+        - [varLib] Added experimental ``fontTools.varLib.interpolatable`` module for
+          finding wrong contour order between different masters
+        - [varLib] designspace.load() now returns a dictionary, instead of a tuple,
+          and supports <axes> element (#864); the 'masters' item was renamed 'sources',
+          like the <sources> element in the designspace document
+        - [ttLib] Fixed issue with recalculating ``head`` modified timestamp when
+          saving CFF fonts
+        - [ttLib] In TupleVariation, round deltas before compiling (#861, fixed #592)
+        - [feaLib] Ignore duplicate glyphs in classes used as MarkFilteringSet and
+          MarkAttachmentType (#863)
+        - [merge] Changed the ``gasp`` table merge logic so that only the one from
+          the first font is retained, similar to other hinting tables (#862)
+        - [Tests] Added tests for the ``varLib`` package, as well as test fonts
+          from the "Annotated OpenType Specification" (AOTS) to exercise ``ttLib``'s
+          table readers/writers (<https://github.com/adobe-type-tools/aots>)
+        
+        3.7.2 (released 2017-02-17)
+        ---------------------------
+        
+        - [subset] Keep advance widths when stripping ".notdef" glyph outline in
+          CID-keyed CFF fonts (#845)
+        - [feaLib] Zero values now produce the same results as makeotf (#633, #848)
+        - [feaLib] More compact encoding for “Contextual positioning with in-line
+          single positioning rules” (#514)
+        
+        3.7.1 (released 2017-02-15)
+        ---------------------------
+        
+        - [subset] Fixed issue with ``--no-hinting`` option whereby advance widths in
+          Type 2 charstrings were also being stripped (#709, #343)
+        - [feaLib] include statements now resolve relative paths like makeotf (#838)
+        - [feaLib] table ``name`` now handles Unicode codepoints beyond the Basic
+          Multilingual Plane, also supports old-style MacOS platform encodings (#842)
+        - [feaLib] correctly escape string literals when emitting feature syntax (#780)
+        
+        3.7.0 (released 2017-02-11)
+        ---------------------------
+        
+        - [ttx, mtiLib] Preserve ordering of glyph alternates in GSUB type 3 (#833).
+        - [feaLib] Glyph names can have dashes, as per new AFDKO syntax v1.20 (#559).
+        - [feaLib] feaLib.Parser now needs the font's glyph map for parsing.
+        - [varLib] Fix regression where GPOS values were stored as 0.
+        - [varLib] Allow merging of class-based kerning when ClassDefs are different
+        
+        3.6.3 (released 2017-02-06)
+        ---------------------------
+        
+        - [varLib] Fix building variation of PairPosFormat2 (b5c34ce).
+        - Populate defaults even for otTables that have postRead (e45297b).
+        - Fix compiling of MultipleSubstFormat1 with zero 'out' glyphs (b887860).
+        
+        3.6.2 (released 2017-01-30)
+        ---------------------------
+        
+        - [varLib.merger] Fixed "TypeError: reduce() of empty sequence with no
+          initial value" (3717dc6).
+        
+        3.6.1 (released 2017-01-28)
+        ---------------------------
+        
+        -  [py23] Fixed unhandled exception occurring at interpreter shutdown in
+           the "last resort" logging handler (972b3e6).
+        -  [agl] Ensure all glyph names are of native 'str' type; avoid mixing
+           'str' and 'unicode' in TTFont.glyphOrder (d8c4058).
+        -  Fixed inconsistent title levels in README.rst that caused PyPI to
+           incorrectly render the reStructuredText page.
+        
+        3.6.0 (released 2017-01-26)
+        ---------------------------
+        
+        -  [varLib] Refactored and improved the variation-font-building process.
+        -  Assembly code in the fpgm, prep, and glyf tables is now indented in
+           XML output for improved readability. The ``instruction`` element is
+           written as a simple tag if empty (#819).
+        -  [ttx] Fixed 'I/O operation on closed file' error when dumping
+           multiple TTXs to standard output with the '-o -' option.
+        -  The unit test modules (``*_test.py``) have been moved outside of the
+           fontTools package to the Tests folder, thus they are no longer
+           installed (#811).
+        
+        3.5.0 (released 2017-01-14)
+        ---------------------------
+        
+        -  Font tables read from XML can now be written back to XML with no
+           loss.
+        -  GSUB/GPOS LookupType is written out in XML as an element, not
+           comment. (#792)
+        -  When parsing cmap table, do not store items mapped to glyph id 0.
+           (#790)
+        -  [otlLib] Make ClassDef sorting deterministic. Fixes #766 (7d1ddb2)
+        -  [mtiLib] Added unit tests (#787)
+        -  [cvar] Implemented cvar table
+        -  [gvar] Renamed GlyphVariation to TupleVariation to match OpenType
+           terminology.
+        -  [otTables] Handle gracefully empty VarData.Item array when compiling
+           XML. (#797)
+        -  [varLib] Re-enabled generation of ``HVAR`` table for fonts with
+           TrueType outlines; removed ``--build-HVAR`` command-line option.
+        -  [feaLib] The parser can now be extended to support non-standard
+           statements in FEA code by using a customized Abstract Syntax Tree.
+           See, for example, ``feaLib.builder_test.test_extensions`` and
+           baseClass.feax (#794, fixes #773).
+        -  [feaLib] Added ``feaLib`` command to the 'fonttools' command-line
+           tool; applies a feature file to a font. ``fonttools feaLib -h`` for
+           help.
+        -  [pens] The ``T2CharStringPen`` now takes an optional
+           ``roundTolerance`` argument to control the rounding of coordinates
+           (#804, fixes #769).
+        -  [ci] Measure test coverage on all supported python versions and OSes,
+           combine coverage data and upload to
+           https://codecov.io/gh/fonttools/fonttools (#786)
+        -  [ci] Configured Travis and Appveyor for running tests on Python 3.6
+           (#785, 55c03bc)
+        -  The manual pages installation directory can be customized through
+           ``FONTTOOLS_MANPATH`` environment variable (#799, fixes #84).
+        -  [Snippets] Added otf2ttf.py, for converting fonts from CFF to
+           TrueType using the googlei18n/cu2qu module (#802)
+        
+        3.4.0 (released 2016-12-21)
+        ---------------------------
+        
+        -  [feaLib] Added support for generating FEA text from abstract syntax
+           tree (AST) objects (#776). Thanks @mhosken
+        -  Added ``agl.toUnicode`` function to convert AGL-compliant glyph names
+           to Unicode strings (#774)
+        -  Implemented MVAR table (b4d5381)
+        
+        3.3.1 (released 2016-12-15)
+        ---------------------------
+        
+        -  [setup] We no longer use versioneer.py to compute fonttools version
+           from git metadata, as this has caused issues for some users (#767).
+           Now we bump the version strings manually with a custom ``release``
+           command of setup.py script.
+        
+        3.3.0 (released 2016-12-06)
+        ---------------------------
+        
+        -  [ttLib] Implemented STAT table from OpenType 1.8 (#758)
+        -  [cffLib] Fixed decompilation of CFF fonts containing non-standard
+           key/value pairs in FontDict (issue #740; PR #744)
+        -  [py23] minor: in ``round3`` function, allow the second argument to be
+           ``None`` (#757)
+        -  The standalone ``sstruct`` and ``xmlWriter`` modules, deprecated
+           since vesion 3.2.0, have been removed. They can be imported from the
+           ``fontTools.misc`` package.
+        
+        3.2.3 (released 2016-12-02)
+        ---------------------------
+        
+        -  [py23] optimized performance of round3 function; added backport for
+           py35 math.isclose() (9d8dacb)
+        -  [subset] fixed issue with 'narrow' (UCS-2) Python 2 builds and
+           ``--text``/``--text-file`` options containing non-BMP chararcters
+           (16d0e5e)
+        -  [varLib] fixed issuewhen normalizing location values (8fa2ee1, #749)
+        -  [inspect] Made it compatible with both python2 and python3 (167ee60,
+           #748). Thanks @pnemade
+        
+        3.2.2 (released 2016-11-24)
+        ---------------------------
+        
+        -  [varLib] Do not emit null axes in fvar (1bebcec). Thanks @robmck-ms
+        -  [varLib] Handle fonts without GPOS (7915a45)
+        -  [merge] Ignore LangSys if None (a11bc56)
+        -  [subset] Fix subsetting MathVariants (78d3cbe)
+        -  [OS/2] Fix "Private Use (plane 15)" range (08a0d55). Thanks @mashabow
+        
+        3.2.1 (released 2016-11-03)
+        ---------------------------
+        
+        -  [OS/2] fix checking ``fsSelection`` bits matching ``head.macStyle``
+           bits
+        -  [varLib] added ``--build-HVAR`` option to generate ``HVAR`` table for
+           fonts with TrueType outlines. For ``CFF2``, it is enabled by default.
+        
+        3.2.0 (released 2016-11-02)
+        ---------------------------
+        
+        -  [varLib] Improve support for OpenType 1.8 Variable Fonts:
+        -  Implement GDEF's VariationStore
+        -  Implement HVAR/VVAR tables
+        -  Partial support for loading MutatorMath .designspace files with
+           varLib.designspace module
+        -  Add varLib.models with Variation fonts interpolation models
+        -  Implement GSUB/GPOS FeatureVariations
+        -  Initial support for interpolating and merging OpenType Layout tables
+           (see ``varLib.interpolate_layout`` and ``varLib.merger`` modules)
+        -  [API change] Change version to be an integer instead of a float in
+           XML output for GSUB, GPOS, GDEF, MATH, BASE, JSTF, HVAR, VVAR, feat,
+           hhea and vhea tables. Scripts that set the Version for those to 1.0
+           or other float values also need fixing. A warning is emitted when
+           code or XML needs fix.
+        -  several bug fixes to the cffLib module, contributed by Adobe's
+           @readroberts
+        -  The XML output for CFF table now has a 'major' and 'minor' elements
+           for specifying whether it's version 1.0 or 2.0 (support for CFF2 is
+           coming soon)
+        -  [setup.py] remove undocumented/deprecated ``extra_path`` Distutils
+           argument. This means that we no longer create a "FontTools" subfolder
+           in site-packages containing the actual fontTools package, as well as
+           the standalone xmlWriter and sstruct modules. The latter modules are
+           also deprecated, and scheduled for removal in upcoming releases.
+           Please change your import statements to point to from fontTools.misc
+           import xmlWriter and from fontTools.misc import sstruct.
+        -  [scripts] Add a 'fonttools' command-line tool that simply runs
+           ``fontTools.*`` sub-modules: e.g. ``fonttools ttx``,
+           ``fonttools subset``, etc.
+        -  [hmtx/vmts] Read advance width/heights as unsigned short (uint16);
+           automatically round float values to integers.
+        -  [ttLib/xmlWriter] add 'newlinestr=None' keyword argument to
+           ``TTFont.saveXML`` for overriding os-specific line endings (passed on
+           to ``XMLWriter`` instances).
+        -  [versioning] Use versioneer instead of ``setuptools_scm`` to
+           dynamically load version info from a git checkout at import time.
+        -  [feaLib] Support backslash-prefixed glyph names.
+        
+        3.1.2 (released 2016-09-27)
+        ---------------------------
+        
+        -  restore Makefile as an alternative way to build/check/install
+        -  README.md: update instructions for installing package from source,
+           and for running test suite
+        -  NEWS: Change log was out of sync with tagged release
+        
+        3.1.1 (released 2016-09-27)
+        ---------------------------
+        
+        -  Fix ``ttLibVersion`` attribute in TTX files still showing '3.0'
+           instead of '3.1'.
+        -  Use ``setuptools_scm`` to manage package versions.
+        
+        3.1.0 (released 2016-09-26)
+        ---------------------------
+        
+        -  [feaLib] New library to parse and compile Adobe FDK OpenType Feature
+           files.
+        -  [mtiLib] New library to parse and compile Monotype 'FontDame'
+           OpenType Layout Tables files.
+        -  [voltLib] New library to parse Microsoft VOLT project files.
+        -  [otlLib] New library to work with OpenType Layout tables.
+        -  [varLib] New library to work with OpenType Font Variations.
+        -  [pens] Add ttGlyphPen to draw to TrueType glyphs, and t2CharStringPen
+           to draw to Type 2 Charstrings (CFF); add areaPen and perimeterPen.
+        -  [ttLib.tables] Implement 'meta' and 'trak' tables.
+        -  [ttx] Add --flavor option for compiling to 'woff' or 'woff2'; add
+           ``--with-zopfli`` option to use Zopfli to compress WOFF 1.0 fonts.
+        -  [subset] Support subsetting 'COLR'/'CPAL' and 'CBDT'/'CBLC' color
+           fonts tables, and 'gvar' table for variation fonts.
+        -  [Snippets] Add ``symfont.py``, for symbolic font statistics analysis;
+           interpolatable.py, a preliminary script for detecting interpolation
+           errors; ``{merge,dump}_woff_metadata.py``.
+        -  [classifyTools] Helpers to classify things into classes.
+        -  [CI] Run tests on Windows, Linux and macOS using Appveyor and Travis
+           CI; check unit test coverage with Coverage.py/Coveralls; automatic
+           deployment to PyPI on tags.
+        -  [loggingTools] Use Python built-in logging module to print messages.
+        -  [py23] Make round() behave like Python 3 built-in round(); define
+           round2() and round3().
+        
+        3.0 (released 2015-09-01)
+        -------------------------
+        
+        -  Add Snippet scripts for cmap subtable format conversion, printing
+           GSUB/GPOS features, building a GX font from two masters
+        -  TTX WOFF2 support and a ``-f`` option to overwrite output file(s)
+        -  Support GX tables: ``avar``, ``gvar``, ``fvar``, ``meta``
+        -  Support ``feat`` and gzip-compressed SVG tables
+        -  Upgrade Mac East Asian encodings to native implementation if
+           available
+        -  Add Roman Croatian and Romanian encodings, codecs for mac-extended
+           East Asian encodings
+        -  Implement optimal GLYF glyph outline packing; disabled by default
+        
+        2.5 (released 2014-09-24)
+        -------------------------
+        
+        -  Add a Qt pen
+        -  Add VDMX table converter
+        -  Load all OpenType sub-structures lazily
+        -  Add support for cmap format 13.
+        -  Add pyftmerge tool
+        -  Update to Unicode 6.3.0d3
+        -  Add pyftinspect tool
+        -  Add support for Google CBLC/CBDT color bitmaps, standard EBLC/EBDT
+           embedded bitmaps, and ``SVG`` table (thanks to Read Roberts at Adobe)
+        -  Add support for loading, saving and ttx'ing WOFF file format
+        -  Add support for Microsoft COLR/CPAL layered color glyphs
+        -  Support PyPy
+        -  Support Jython, by replacing numpy with array/lists modules and
+           removed it, pure-Python StringIO, not cStringIO
+        -  Add pyftsubset and Subsetter object, supporting CFF and TTF
+        -  Add to ttx args for -q for quiet mode, -z to choose a bitmap dump
+           format
+        
+        2.4 (released 2013-06-22)
+        -------------------------
+        
+        -  Option to write to arbitrary files
+        -  Better dump format for DSIG
+        -  Better detection of OTF XML
+        -  Fix issue with Apple's kern table format
+        -  Fix mangling of TT glyph programs
+        -  Fix issues related to mona.ttf
+        -  Fix Windows Installer instructions
+        -  Fix some modern MacOS issues
+        -  Fix minor issues and typos
+        
+        2.3 (released 2009-11-08)
+        -------------------------
+        
+        -  TrueType Collection (TTC) support
+        -  Python 2.6 support
+        -  Update Unicode data to 5.2.0
+        -  Couple of bug fixes
+        
+        2.2 (released 2008-05-18)
+        -------------------------
+        
+        -  ClearType support
+        -  cmap format 1 support
+        -  PFA font support
+        -  Switched from Numeric to numpy
+        -  Update Unicode data to 5.1.0
+        -  Update AGLFN data to 1.6
+        -  Many bug fixes
+        
+        2.1 (released 2008-01-28)
+        -------------------------
+        
+        -  Many years worth of fixes and features
+        
+        2.0b2 (released 2002-??-??)
+        ---------------------------
+        
+        -  Be "forgiving" when interpreting the maxp table version field:
+           interpret any value as 1.0 if it's not 0.5. Fixes dumping of these
+           GPL fonts: http://www.freebsd.org/cgi/pds.cgi?ports/chinese/wangttf
+        -  Fixed ttx -l: it turned out this part of the code didn't work with
+           Python 2.2.1 and earlier. My bad to do most of my testing with a
+           different version than I shipped TTX with :-(
+        -  Fixed bug in ClassDef format 1 subtable (Andreas Seidel bumped into
+           this one).
+        
+        2.0b1 (released 2002-09-10)
+        ---------------------------
+        
+        -  Fixed embarrassing bug: the master checksum in the head table is now
+           calculated correctly even on little-endian platforms (such as Intel).
+        -  Made the cmap format 4 compiler smarter: the binary data it creates
+           is now more or less as compact as possible. TTX now makes more
+           compact data than in any shipping font I've tested it with.
+        -  Dump glyph names as a separate "GlyphOrder" pseudo table as opposed
+           to as part of the glyf table (obviously needed for CFF-OTF's).
+        -  Added proper support for the CFF table.
+        -  Don't barf on empty tables (questionable, but "there are font out
+           there...")
+        -  When writing TT glyf data, align glyphs on 4-byte boundaries. This
+           seems to be the current recommendation by MS. Also: don't barf on
+           fonts which are already 4-byte aligned.
+        -  Windows installer contributed bu Adam Twardoch! Yay!
+        -  Changed the command line interface again, now by creating one new
+           tool replacing the old ones: ttx It dumps and compiles, depending on
+           input file types. The options have changed somewhat.
+        -  The -d option is back (output dir)
+        -  ttcompile's -i options is now called -m (as in "merge"), to avoid
+           clash with dump's -i.
+        -  The -s option ("split tables") no longer creates a directory, but
+           instead outputs a small .ttx file containing references to the
+           individual table files. This is not a true link, it's a simple file
+           name, and the referenced file should be in the same directory so
+           ttcompile can find them.
+        -  compile no longer accepts a directory as input argument. Instead it
+           can parse the new "mini-ttx" format as output by "ttx -s".
+        -  all arguments are input files
+        -  Renamed the command line programs and moved them to the Tools
+           subdirectory. They are now installed by the setup.py install script.
+        -  Added OpenType support. BASE, GDEF, GPOS, GSUB and JSTF are (almost)
+           fully supported. The XML output is not yet final, as I'm still
+           considering to output certain subtables in a more human-friendly
+           manner.
+        -  Fixed 'kern' table to correctly accept subtables it doesn't know
+           about, as well as interpreting Apple's definition of the 'kern' table
+           headers correctly.
+        -  Fixed bug where glyphnames were not calculated from 'cmap' if it was
+           (one of the) first tables to be decompiled. More specifically: it
+           cmap was the first to ask for a glyphID -> glyphName mapping.
+        -  Switched XML parsers: use expat instead of xmlproc. Should be faster.
+        -  Removed my UnicodeString object: I now require Python 2.0 or up,
+           which has unicode support built in.
+        -  Removed assert in glyf table: redundant data at the end of the table
+           is now ignored instead of raising an error. Should become a warning.
+        -  Fixed bug in hmtx/vmtx code that only occured if all advances were
+           equal.
+        -  Fixed subtle bug in TT instruction disassembler.
+        -  Couple of fixes to the 'post' table.
+        -  Updated OS/2 table to latest spec.
+        
+        1.0b1 (released 2001-08-10)
+        ---------------------------
+        
+        -  Reorganized the command line interface for ttDump.py and
+           ttCompile.py, they now behave more like "normal" command line tool,
+           in that they accept multiple input files for batch processing.
+        -  ttDump.py and ttCompile.py don't silently override files anymore, but
+           ask before doing so. Can be overridden by -f.
+        -  Added -d option to both ttDump.py and ttCompile.py.
+        -  Installation is now done with distutils. (Needs work for environments
+           without compilers.)
+        -  Updated installation instructions.
+        -  Added some workarounds so as to handle certain buggy fonts more
+           gracefully.
+        -  Updated Unicode table to Unicode 3.0 (Thanks Antoine!)
+        -  Included a Python script by Adam Twardoch that adds some useful stuff
+           to the Windows registry.
+        -  Moved the project to SourceForge.
+        
+        1.0a6 (released 2000-03-15)
+        ---------------------------
+        
+        -  Big reorganization: made ttLib a subpackage of the new fontTools
+           package, changed several module names. Called the entire suite
+           "FontTools"
+        -  Added several submodules to fontTools, some new, some older.
+        -  Added experimental CFF/GPOS/GSUB support to ttLib, read-only (but XML
+           dumping of GPOS/GSUB is for now disabled)
+        -  Fixed hdmx endian bug
+        -  Added -b option to ttCompile.py, it disables recalculation of
+           bounding boxes, as requested by Werner Lemberg.
+        -  Renamed tt2xml.pt to ttDump.py and xml2tt.py to ttCompile.py
+        -  Use ".ttx" as file extension instead of ".xml".
+        -  TTX is now the name of the XML-based *format* for TT fonts, and not
+           just an application.
+        
+        1.0a5
+        -----
+        
+        Never released
+        
+        -  More tables supported: hdmx, vhea, vmtx
+        
+        1.0a3 & 1.0a4
+        -------------
+        
+        Never released
+        
+        -  fixed most portability issues
+        -  retracted the "Euro_or_currency" change from 1.0a2: it was
+           nonsense!
+        
+        1.0a2 (released 1999-05-02)
+        ---------------------------
+        
+        -  binary release for MacOS
+        -  genenates full FOND resources: including width table, PS font name
+           info and kern table if applicable.
+        -  added cmap format 4 support. Extra: dumps Unicode char names as XML
+           comments!
+        -  added cmap format 6 support
+        -  now accepts true type files starting with "true" (instead of just
+           0x00010000 and "OTTO")
+        -  'glyf' table support is now complete: I added support for composite
+           scale, xy-scale and two-by-two for the 'glyf' table. For now,
+           component offset scale behaviour defaults to Apple-style. This only
+           affects the (re)calculation of the glyph bounding box.
+        -  changed "Euro" to "Euro_or_currency" in the Standard Apple Glyph
+           order list, since we cannot tell from the 'post' table which is
+           meant. I should probably doublecheck with a Unicode encoding if
+           available. (This does not affect the output!)
+        
+        Fixed bugs: - 'hhea' table is now recalculated correctly - fixed wrong
+        assumption about sfnt resource names
+        
+        1.0a1 (released 1999-04-27)
+        ---------------------------
+        
+        -  initial binary release for MacOS
+        
+Platform: Any
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Console
+Classifier: Environment :: Other Environment
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: End Users/Desktop
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Natural Language :: English
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
+Classifier: Topic :: Text Processing :: Fonts
+Classifier: Topic :: Multimedia :: Graphics
+Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion
diff --git a/README.md b/README.md
deleted file mode 100644
index c2f7d0c..0000000
--- a/README.md
+++ /dev/null
@@ -1,34 +0,0 @@
-### What it is ?
-
-Quoting from [TTX/FontTools Sourceforge Project](http://sourceforge.net/projects/fonttools/) 
-> a tool to convert OpenType and TrueType fonts to and from XML. FontTools is a library for manipulating fonts, written in Python. It supports TrueType, OpenType, AFM and to an extent Type 1 and some Mac-specific formats.   
-
-### Quick start
-
-```python setup.py install```
-
-From your command line type the above command to get fontools installed on your system.
-
-### Installation
-
-See [install.txt](https://github.com/behdad/fonttools/blob/master/Doc/install.txt) in the 'Doc' subdirectory for instructions on how to build and install TTX/FontTools from the sources.
-
-
-### Documentation
-
-#### What is TTX ?
-
-See [documentation.html](https://github.com/behdad/fonttools/blob/master/Doc/documentation.html) in the "Doc" subdirectory for TTX usage instructions and information about the TTX file format.
-
-### Community
-* https://groups.google.com/d/forum/fonttools
-
-### License
-
-See "LICENSE.txt" for licensing information.
-
-
-
-Have fun!
-
-Just van Rossum <just@letterror.com>
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..664ad8f
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,365 @@
+|Travis Build Status| |Appveyor Build status| |Health| |Coverage Status|
+|PyPI| |Gitter Chat|
+
+What is this?
+~~~~~~~~~~~~~
+
+| fontTools is a library for manipulating fonts, written in Python. The
+  project includes the TTX tool, that can convert TrueType and OpenType
+  fonts to and from an XML text format, which is also called TTX. It
+  supports TrueType, OpenType, AFM and to an extent Type 1 and some
+  Mac-specific formats. The project has a `MIT open-source
+  licence <LICENSE>`__.
+| Among other things this means you can use it free of charge.
+
+Installation
+~~~~~~~~~~~~
+
+FontTools requires `Python <http://www.python.org/download/>`__ 2.7, 3.4
+or later.
+
+The package is listed in the Python Package Index (PyPI), so you can
+install it with `pip <https://pip.pypa.io>`__:
+
+.. code:: sh
+
+    pip install fonttools
+
+If you would like to contribute to its development, you can clone the
+repository from Github, install the package in 'editable' mode and
+modify the source code in place. We recommend creating a virtual
+environment, using `virtualenv <https://virtualenv.pypa.io>`__ or
+Python 3 `venv <https://docs.python.org/3/library/venv.html>`__ module.
+
+.. code:: sh
+
+    # download the source code to 'fonttools' folder
+    git clone https://github.com/fonttools/fonttools.git
+    cd fonttools
+
+    # create new virtual environment called e.g. 'fonttools-venv', or anything you like
+    python -m virtualenv fonttools-venv
+
+    # source the `activate` shell script to enter the environment (Un\*x); to exit, just type `deactivate`
+    . fonttools-venv/bin/activate
+
+    # to activate the virtual environment in Windows `cmd.exe`, do
+    fonttools-venv\Scripts\activate.bat
+
+    # install in 'editable' mode
+    pip install -e .
+
+TTX – From OpenType and TrueType to XML and Back
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Once installed you can use the ``ttx`` command to convert binary font
+files (``.otf``, ``.ttf``, etc) to the TTX xml format, edit them, and
+convert them back to binary format. TTX files have a .ttx file
+extension.
+
+.. code:: sh
+
+    ttx /path/to/font.otf
+    ttx /path/to/font.ttx
+
+The TTX application works can be used in two ways, depending on what
+platform you run it on:
+
+-  As a command line tool (Windows/DOS, Unix, MacOSX)
+-  By dropping files onto the application (Windows, MacOS)
+
+TTX detects what kind of files it is fed: it will output a ``.ttx`` file
+when it sees a ``.ttf`` or ``.otf``, and it will compile a ``.ttf`` or
+``.otf`` when the input file is a ``.ttx`` file. By default, the output
+file is created in the same folder as the input file, and will have the
+same name as the input file but with a different extension. TTX will
+*never* overwrite existing files, but if necessary will append a unique
+number to the output filename (before the extension) such as
+``Arial#1.ttf``
+
+When using TTX from the command line there are a bunch of extra options,
+these are explained in the help text, as displayed when typing
+``ttx -h`` at the command prompt. These additional options include:
+
+-  specifying the folder where the output files are created
+-  specifying which tables to dump or which tables to exclude
+-  merging partial ``.ttx`` files with existing ``.ttf`` or ``.otf``
+   files
+-  listing brief table info instead of dumping to ``.ttx``
+-  splitting tables to separate ``.ttx`` files
+-  disabling TrueType instruction disassembly
+
+The TTX file format
+-------------------
+
+The following tables are currently supported:
+
+.. begin table list
+.. code::
+
+    BASE, CBDT, CBLC, CFF, CFF2, COLR, CPAL, DSIG, EBDT, EBLC, FFTM,
+    Feat, GDEF, GMAP, GPKG, GPOS, GSUB, Glat, Gloc, HVAR, JSTF, LTSH,
+    MATH, META, MVAR, OS/2, SING, STAT, SVG, Silf, Sill, TSI0, TSI1,
+    TSI2, TSI3, TSI5, TSIB, TSID, TSIJ, TSIP, TSIS, TSIV, TTFA, VDMX,
+    VORG, VVAR, ankr, avar, bsln, cidg, cmap, cvar, cvt, feat, fpgm,
+    fvar, gasp, gcid, glyf, gvar, hdmx, head, hhea, hmtx, kern, lcar,
+    loca, ltag, maxp, meta, mort, morx, name, opbd, post, prep, prop,
+    sbix, trak, vhea and vmtx
+.. end table list
+
+Other tables are dumped as hexadecimal data.
+
+TrueType fonts use glyph indices (GlyphIDs) to refer to glyphs in most
+places. While this is fine in binary form, it is really hard to work
+with for humans. Therefore we use names instead.
+
+The glyph names are either extracted from the ``CFF`` table or the
+``post`` table, or are derived from a Unicode ``cmap`` table. In the
+latter case the Adobe Glyph List is used to calculate names based on
+Unicode values. If all of these methods fail, names are invented based
+on GlyphID (eg ``glyph00142``)
+
+It is possible that different glyphs use the same name. If this happens,
+we force the names to be unique by appending ``#n`` to the name (``n``
+being an integer number.) The original names are being kept, so this has
+no influence on a "round tripped" font.
+
+Because the order in which glyphs are stored inside the binary font is
+important, we maintain an ordered list of glyph names in the font.
+
+Other Tools
+~~~~~~~~~~~
+
+Commands for inspecting, merging and subsetting fonts are also
+available:
+
+.. code:: sh
+
+    pyftinspect
+    pyftmerge
+    pyftsubset
+
+fontTools Python Module
+~~~~~~~~~~~~~~~~~~~~~~~
+
+The fontTools python module provides a convenient way to
+programmatically edit font files.
+
+.. code:: py
+
+    >>> from fontTools.ttLib import TTFont
+    >>> font = TTFont('/path/to/font.ttf')
+    >>> font
+    <fontTools.ttLib.TTFont object at 0x10c34ed50>
+    >>>
+
+A selection of sample python programs is in the
+`Snippets <https://github.com/fonttools/fonttools/blob/master/Snippets/>`__
+directory.
+
+Optional Requirements
+---------------------
+
+The ``fontTools`` package currently has no (required) external dependencies
+besides the modules included in the Python Standard Library.
+However, a few extra dependencies are required by some of its modules, which
+are needed to unlock optional features.
+
+-  ``Lib/fontTools/ttLib/woff2.py``
+
+   Module to compress/decompress WOFF 2.0 web fonts; it requires:
+
+   -  `brotli <https://pypi.python.org/pypi/Brotli>`__: Python bindings of
+      the Brotli compression library.
+
+-  ``Lib/fontTools/ttLib/sfnt.py``
+
+   To better compress WOFF 1.0 web fonts, the following module can be used
+   instead of the built-in ``zlib`` library:
+
+   -  `zopfli <https://pypi.python.org/pypi/zopfli>`__: Python bindings of
+      the Zopfli compression library.
+
+-  ``Lib/fontTools/unicode.py``
+
+   To display the Unicode character names when dumping the ``cmap`` table
+   with ``ttx`` we use the ``unicodedata`` module in the Standard Library.
+   The version included in there varies between different Python versions.
+   To use the latest available data, you can install:
+
+   -  `unicodedata2 <https://pypi.python.org/pypi/unicodedata2>`__:
+      ``unicodedata`` backport for Python 2.7 and 3.5 updated to the latest
+      Unicode version 9.0. Note this is not necessary if you use Python 3.6
+      as the latter already comes with an up-to-date ``unicodedata``.
+
+-  ``Lib/fontTools/varLib/interpolatable.py``
+
+   Module for finding wrong contour/component order between different masters.
+   It requires one of the following packages in order to solve the so-called
+   "minimum weight perfect matching problem in bipartite graphs", or
+   the Assignment problem:
+
+   *  `scipy <https://pypi.python.org/pypi/scipy>`__: the Scientific Library
+      for Python, which internally uses `NumPy <https://pypi.python.org/pypi/numpy>`__
+      arrays and hence is very fast;
+   *  `munkres <https://pypi.python.org/pypi/munkres>`__: a pure-Python
+      module that implements the Hungarian or Kuhn-Munkres algorithm.
+
+-  ``Lib/fontTools/misc/symfont.py``
+
+   Advanced module for symbolic font statistics analysis; it requires:
+
+   *  `sympy <https://pypi.python.org/pypi/sympy>`__: the Python library for
+      symbolic mathematics.
+
+-  ``Lib/fontTools/t1Lib.py``
+
+   To get the file creator and type of Macintosh PostScript Type 1 fonts
+   on Python 3 you need to install the following module, as the old ``MacOS``
+   module is no longer included in Mac Python:
+
+   *  `xattr <https://pypi.python.org/pypi/xattr>`__: Python wrapper for
+      extended filesystem attributes (macOS platform only).
+
+-  ``Lib/fontTools/pens/cocoaPen.py``
+
+   Pen for drawing glyphs with Cocoa ``NSBezierPath``, requires:
+
+   *  `PyObjC <https://pypi.python.org/pypi/pyobjc>`__: the bridge between
+      Python and the Objective-C runtime (macOS platform only).
+
+-  ``Lib/fontTools/pens/qtPen.py``
+
+   Pen for drawing glyphs with Qt's ``QPainterPath``, requires:
+
+   *  `PyQt5 <https://pypi.python.org/pypi/PyQt5>`__: Python bindings for
+      the Qt cross platform UI and application toolkit.
+
+-  ``Lib/fontTools/pens/reportLabPen.py``
+
+   Pen to drawing glyphs as PNG images, requires:
+
+   *  `reportlab <https://pypi.python.org/pypi/reportlab>`__: Python toolkit
+      for generating PDFs and graphics.
+
+-  ``Lib/fontTools/inspect.py``
+
+   A GUI font inspector, requires one of the following packages:
+
+   *  `PyGTK <https://pypi.python.org/pypi/PyGTK>`__: Python bindings for
+      GTK  2.x (only works with Python 2).
+   *  `PyGObject <https://wiki.gnome.org/action/show/Projects/PyGObject>`__ :
+      Python bindings for GTK 3.x and gobject-introspection libraries (also
+      compatible with Python 3).
+
+Testing
+~~~~~~~
+
+To run the test suite, you can do:
+
+.. code:: sh
+
+    python setup.py test
+
+If you have `pytest <http://docs.pytest.org/en/latest/>`__, you can run
+the ``pytest`` command directly. The tests will run against the
+installed ``fontTools`` package, or the first one found in the
+``PYTHONPATH``.
+
+You can also use `tox <https://testrun.org/tox/latest/>`__ to
+automatically run tests on different Python versions in isolated virtual
+environments.
+
+.. code:: sh
+
+    pip install tox
+    tox
+
+Note that when you run ``tox`` without arguments, the tests are executed
+for all the environments listed in tox.ini's ``envlist``. In our case,
+this includes Python 2.7 and 3.6, so for this to work the ``python2.7``
+and ``python3.6`` executables must be available in your ``PATH``.
+
+You can specify an alternative environment list via the ``-e`` option,
+or the ``TOXENV`` environment variable:
+
+.. code:: sh
+
+    tox -e py27-nocov
+    TOXENV="py36-cov,htmlcov" tox
+
+Development Community
+~~~~~~~~~~~~~~~~~~~~~
+
+TTX/FontTools development is ongoing in an active community of
+developers, that includes professional developers employed at major
+software corporations and type foundries as well as hobbyists.
+
+Feature requests and bug reports are always welcome at
+https://github.com/fonttools/fonttools/issues/
+
+The best place for discussions about TTX from an end-user perspective as
+well as TTX/FontTools development is the
+https://groups.google.com/d/forum/fonttools mailing list. There is also
+a development https://groups.google.com/d/forum/fonttools-dev mailing
+list for continuous integration notifications. You can also email Behdad
+privately at behdad@behdad.org
+
+History
+~~~~~~~
+
+The fontTools project was started by Just van Rossum in 1999, and was
+maintained as an open source project at
+http://sourceforge.net/projects/fonttools/. In 2008, Paul Wise (pabs3)
+began helping Just with stability maintenance. In 2013 Behdad Esfahbod
+began a friendly fork, thoroughly reviewing the codebase and making
+changes at https://github.com/behdad/fonttools to add new features and
+support for new font formats.
+
+Acknowledgements
+~~~~~~~~~~~~~~~~
+
+In alphabetical order:
+
+Olivier Berten, Samyak Bhuta, Erik van Blokland, Petr van Blokland,
+Jelle Bosma, Sascha Brawer, Tom Byrer, Frédéric Coiffier, Vincent
+Connare, Dave Crossland, Simon Daniels, Behdad Esfahbod, Behnam
+Esfahbod, Hannes Famira, Sam Fishman, Matt Fontaine, Yannis Haralambous,
+Greg Hitchcock, Jeremie Hornus, Khaled Hosny, John Hudson, Denis Moyogo
+Jacquerye, Jack Jansen, Tom Kacvinsky, Jens Kutilek, Antoine Leca,
+Werner Lemberg, Tal Leming, Peter Lofting, Cosimo Lupo, Masaya Nakamura,
+Dave Opstad, Laurence Penney, Roozbeh Pournader, Garret Rieger, Read
+Roberts, Guido van Rossum, Just van Rossum, Andreas Seidel, Georg
+Seifert, Miguel Sousa, Adam Twardoch, Adrien Tétar, Vitaly Volkov, Paul
+Wise.
+
+Copyrights
+~~~~~~~~~~
+
+| Copyright (c) 1999-2004 Just van Rossum, LettError
+  (just@letterror.com)
+| See `LICENSE <LICENSE>`__ for the full license.
+
+Copyright (c) 2000 BeOpen.com. All Rights Reserved.
+
+Copyright (c) 1995-2001 Corporation for National Research Initiatives.
+All Rights Reserved.
+
+Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam. All
+Rights Reserved.
+
+Have fun!
+
+.. |Travis Build Status| image:: https://travis-ci.org/fonttools/fonttools.svg
+   :target: https://travis-ci.org/fonttools/fonttools
+.. |Appveyor Build status| image:: https://ci.appveyor.com/api/projects/status/0f7fmee9as744sl7/branch/master?svg=true
+   :target: https://ci.appveyor.com/project/fonttools/fonttools/branch/master
+.. |Health| image:: https://landscape.io/github/behdad/fonttools/master/landscape.svg?style=flat
+   :target: https://landscape.io/github/behdad/fonttools/master
+.. |Coverage Status| image:: https://codecov.io/gh/fonttools/fonttools/branch/master/graph/badge.svg
+   :target: https://codecov.io/gh/fonttools/fonttools
+.. |PyPI| image:: https://img.shields.io/pypi/v/fonttools.svg
+   :target: https://pypi.org/project/FontTools
+.. |Gitter Chat| image:: https://badges.gitter.im/fonttools-dev/Lobby.svg
+   :alt: Join the chat at https://gitter.im/fonttools-dev/Lobby
+   :target: https://gitter.im/fonttools-dev/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
diff --git a/README.version b/README.version
deleted file mode 100644
index 850e5ea..0000000
--- a/README.version
+++ /dev/null
@@ -1,3 +0,0 @@
-URL: https://pypi.python.org/packages/source/F/FontTools/FontTools-2.4.tar.gz#md5=304b20e6109787c0ff4467d2b9f7f2c5
-Version: 2.4
-BugComponent: 75970
diff --git a/Snippets/README.md b/Snippets/README.md
new file mode 100644
index 0000000..915b1c3
--- /dev/null
+++ b/Snippets/README.md
@@ -0,0 +1,11 @@
+This directory includes snippets that people might useful to get ideas from.
+The contents will come and go, don't rely on them being there or having a certain API.
+If you need it, copy it and modify it.
+
+If you do and think your work is useful for others, please add a link to it here:
+
+* https://github.com/twardoch/fonttools-utils
+* https://github.com/twardoch/ttfdiet
+* https://github.com/googlei18n/nototools
+* https://github.com/googlefonts/fontbakery
+* https://github.com/Typefounding/setUseTypoMetricsFalse
diff --git a/Snippets/checksum.py b/Snippets/checksum.py
new file mode 100644
index 0000000..53a5318
--- /dev/null
+++ b/Snippets/checksum.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+import argparse
+import hashlib
+import os
+import sys
+
+from os.path import basename
+
+from fontTools.ttLib import TTFont
+
+
+def write_checksum(filepaths, stdout_write=False, use_ttx=False, include_tables=None, exclude_tables=None, do_not_cleanup=False):
+    checksum_dict = {}
+    for path in filepaths:
+        if not os.path.exists(path):
+            sys.stderr.write("[checksum.py] ERROR: " + path + " is not a valid file path" + os.linesep)
+            sys.exit(1)
+
+        if use_ttx:
+            # append a .ttx extension to existing extension to maintain data about the binary that
+            # was used to generate the .ttx XML dump.  This creates unique checksum path values for
+            # paths that would otherwise not be unique with a file extension replacement with .ttx
+            # An example is woff and woff2 web font files that share the same base file name:
+            #
+            #  coolfont-regular.woff  ==> coolfont-regular.ttx
+            #  coolfont-regular.woff2 ==> coolfont-regular.ttx  (KAPOW! checksum data lost as this would overwrite dict value)
+            temp_ttx_path = path + ".ttx"
+
+            tt = TTFont(path)
+            # important to keep the newlinestr value defined here as hash values will change across platforms
+            # if platform specific newline values are assumed
+            tt.saveXML(temp_ttx_path, newlinestr="\n", skipTables=exclude_tables, tables=include_tables)
+            checksum_path = temp_ttx_path
+        else:
+            if include_tables is not None:
+                sys.stderr.write("[checksum.py] -i and --include are not supported for font binary filepaths. \
+                    Use these flags for checksums with the --ttx flag.")
+                sys.exit(1)
+            if exclude_tables is not None:
+                sys.stderr.write("[checksum.py] -e and --exclude are not supported for font binary filepaths. \
+                    Use these flags for checksums with the --ttx flag.")
+                sys.exit(1)
+            checksum_path = path
+
+        file_contents = _read_binary(checksum_path)
+
+        # store SHA1 hash data and associated file path basename in the checksum_dict dictionary
+        checksum_dict[basename(checksum_path)] = hashlib.sha1(file_contents).hexdigest()
+
+        # remove temp ttx files when present
+        if use_ttx and do_not_cleanup is False:
+            os.remove(temp_ttx_path)
+
+    # generate the checksum list string for writes
+    checksum_out_data = ""
+    for key in checksum_dict.keys():
+        checksum_out_data += checksum_dict[key] + "  " + key + "\n"
+
+    # write to stdout stream or file based upon user request (default = file write)
+    if stdout_write:
+        sys.stdout.write(checksum_out_data)
+    else:
+        checksum_report_filepath = "checksum.txt"
+        with open(checksum_report_filepath, "w") as file:
+            file.write(checksum_out_data)
+
+
+def check_checksum(filepaths):
+    check_failed = False
+    for path in filepaths:
+        if not os.path.exists(path):
+            sys.stderr.write("[checksum.py] ERROR: " + path + " is not a valid filepath" + os.linesep)
+            sys.exit(1)
+
+        with open(path, mode='r') as file:
+            for line in file.readlines():
+                cleaned_line = line.rstrip()
+                line_list = cleaned_line.split(" ")
+                # eliminate empty strings parsed from > 1 space characters
+                line_list = list(filter(None, line_list))
+                if len(line_list) == 2:
+                    expected_sha1 = line_list[0]
+                    test_path = line_list[1]
+                else:
+                    sys.stderr.write("[checksum.py] ERROR: failed to parse checksum file values" + os.linesep)
+                    sys.exit(1)
+
+                if not os.path.exists(test_path):
+                    print(test_path + ": Filepath is not valid, ignored")
+                else:
+                    file_contents = _read_binary(test_path)
+                    observed_sha1 = hashlib.sha1(file_contents).hexdigest()
+                    if observed_sha1 == expected_sha1:
+                        print(test_path + ": OK")
+                    else:
+                        print("-" * 80)
+                        print(test_path + ": === FAIL ===")
+                        print("Expected vs. Observed:")
+                        print(expected_sha1)
+                        print(observed_sha1)
+                        print("-" * 80)
+                        check_failed = True
+
+    # exit with status code 1 if any fails detected across all tests in the check
+    if check_failed is True:
+        sys.exit(1)
+
+
+def _read_binary(filepath):
+    with open(filepath, mode='rb') as file:
+        return file.read()
+
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser(prog="checksum.py", description="A SHA1 hash checksum list generator and checksum testing script")
+    parser.add_argument("-t", "--ttx", help="Calculate from ttx file", action="store_true")
+    parser.add_argument("-s", "--stdout", help="Write output to stdout stream", action="store_true")
+    parser.add_argument("-n", "--noclean", help="Do not discard *.ttx files used to calculate SHA1 hashes", action="store_true")
+    parser.add_argument("-c", "--check", help="Verify checksum values vs. files", action="store_true")
+    parser.add_argument("filepaths", nargs="+", help="One or more file paths.  Use checksum file path for -c/--check.  Use paths\
+        to font files for all other commands.")
+
+    parser.add_argument("-i", "--include", action="append", help="Included OpenType tables for ttx data dump")
+    parser.add_argument("-e", "--exclude", action="append", help="Excluded OpenType tables for ttx data dump")
+
+    args = parser.parse_args(sys.argv[1:])
+
+    if args.check is True:
+        check_checksum(args.filepaths)
+    else:
+        write_checksum(args.filepaths, stdout_write=args.stdout, use_ttx=args.ttx, do_not_cleanup=args.noclean, include_tables=args.include, exclude_tables=args.exclude)
diff --git a/Snippets/cmap-format.py b/Snippets/cmap-format.py
new file mode 100755
index 0000000..0cee39c
--- /dev/null
+++ b/Snippets/cmap-format.py
@@ -0,0 +1,40 @@
+#! /usr/bin/env python
+
+# Sample script to convert legacy cmap subtables to format-4
+# subtables.  Note that this is rarely what one needs.  You
+# probably need to just drop the legacy subtables if the font
+# already has a format-4 subtable.
+#
+# Other times, you would need to convert a non-Unicode cmap
+# legacy subtable to a Unicode one.  In those cases, use the
+# getEncoding() of subtable and use that encoding to map the
+# characters to Unicode...  TODO: Extend this script to do that.
+
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.ttLib import TTFont
+from fontTools.ttLib.tables._c_m_a_p import CmapSubtable
+import sys
+
+if len(sys.argv) != 3:
+	print("usage: cmap-format.py fontfile.ttf outfile.ttf")
+	sys.exit(1)
+fontfile = sys.argv[1]
+outfile = sys.argv[2]
+font = TTFont(fontfile)
+
+cmap = font['cmap']
+outtables = []
+for table in cmap.tables:
+	if table.format in [4, 12, 13, 14]:
+		outtables.append(table)
+	# Convert ot format4
+	newtable = CmapSubtable.newSubtable(4)
+	newtable.platformID = table.platformID
+	newtable.platEncID = table.platEncID
+	newtable.language = table.language
+	newtable.cmap = table.cmap
+	outtables.append(newtable)
+cmap.tables = outtables
+
+font.save(outfile)
diff --git a/Snippets/dump_woff_metadata.py b/Snippets/dump_woff_metadata.py
new file mode 100644
index 0000000..0023a10
--- /dev/null
+++ b/Snippets/dump_woff_metadata.py
@@ -0,0 +1,33 @@
+from __future__ import print_function
+import sys
+from fontTools.ttx import makeOutputFileName
+from fontTools.ttLib import TTFont
+
+
+def main(args=None):
+    if args is None:
+        args = sys.argv[1:]
+
+    if len(args) < 1:
+        print("usage: dump_woff_metadata.py "
+              "INPUT.woff [OUTPUT.xml]", file=sys.stderr)
+        return 1
+
+    infile = args[0]
+    if len(args) > 1:
+        outfile = args[1]
+    else:
+        outfile = makeOutputFileName(infile, None, ".xml")
+
+    font = TTFont(infile)
+
+    if not font.flavorData or not font.flavorData.metaData:
+        print("No WOFF metadata")
+        return 1
+
+    with open(outfile, "wb") as f:
+        f.write(font.flavorData.metaData)
+
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/Snippets/edit_raw_table_data.py b/Snippets/edit_raw_table_data.py
new file mode 100644
index 0000000..89326fe
--- /dev/null
+++ b/Snippets/edit_raw_table_data.py
@@ -0,0 +1,31 @@
+from fontTools.ttLib import TTFont
+from fontTools.ttLib.tables.DefaultTable import DefaultTable
+
+font_path = "myfont.ttf"
+output_path = "myfont_patched.ttf"
+
+table_tag = "DSIG"
+
+
+# Get raw table data from the source font
+
+font = TTFont(font_path)
+raw_data = font.getTableData(table_tag)
+
+
+# Do something with the raw table data
+# This example just sets an empty DSIG table.
+
+raw_data = b"\0\0\0\1\0\0\0\0"
+
+
+# Write the data back to the font
+
+# We could re-use the existing table when the source and target font are
+# identical, but let's make a new empty table to be more universal.
+table = DefaultTable(table_tag)
+table.data = raw_data
+
+# Add the new table back into the source font and save under a new name.
+font[table_tag] = table
+font.save(output_path)
diff --git a/Snippets/fix-dflt-langsys.py b/Snippets/fix-dflt-langsys.py
new file mode 100644
index 0000000..d8eccb4
--- /dev/null
+++ b/Snippets/fix-dflt-langsys.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+
+import argparse
+import logging
+import os
+import sys
+
+from fontTools.ttLib import TTFont
+
+
+def ProcessTable(table):
+    found = set()
+
+    for rec in table.ScriptList.ScriptRecord:
+        if rec.ScriptTag == "DFLT" and rec.Script.LangSysCount != 0:
+            tags = [r.LangSysTag for r in rec.Script.LangSysRecord]
+            logging.info("Removing %d extraneous LangSys records: %s",
+                         rec.Script.LangSysCount, " ".join(tags))
+            rec.Script.LangSysRecord = []
+            rec.Script.LangSysCount = 0
+            found.update(tags)
+
+    if not found:
+        logging.info("All fine")
+        return False
+    else:
+        for rec in table.ScriptList.ScriptRecord:
+            tags = set([r.LangSysTag for r in rec.Script.LangSysRecord])
+            found -= tags
+
+        if found:
+            logging.warning("Records are missing from non-DFLT scripts: %s",
+                            " ".join(found))
+        return True
+
+
+def ProcessFont(font):
+    found = False
+    for tag in ("GSUB", "GPOS"):
+        if tag in font:
+            logging.info("Processing %s table", tag)
+            if ProcessTable(font[tag].table):
+                found = True
+            else:
+                # Unmark the table as loaded so that it is read from disk when
+                # writing the font, to avoid any unnecessary changes caused by
+                # decompiling then recompiling again.
+                del font.tables[tag]
+
+    return found
+
+
+def ProcessFiles(filenames):
+    for filename in filenames:
+        logging.info("Processing %s", filename)
+        font = TTFont(filename)
+        name, ext = os.path.splitext(filename)
+        fixedname = name + ".fixed" + ext
+        if ProcessFont(font):
+            logging.info("Saving fixed font to %s\n", fixedname)
+            font.save(fixedname)
+        else:
+            logging.info("Font file is fine, nothing to fix\n")
+
+
+def main():
+    parser = argparse.ArgumentParser(
+            description="Fix LangSys records for DFLT script")
+    parser.add_argument("files", metavar="FILE", type=str, nargs="+",
+                        help="input font to process")
+    parser.add_argument("-s", "--silent", action='store_true',
+                        help="suppress normal messages")
+
+    args = parser.parse_args()
+
+    logformat = "%(levelname)s: %(message)s"
+    if args.silent:
+        logging.basicConfig(format=logformat, level=logging.DEBUG)
+    else:
+        logging.basicConfig(format=logformat, level=logging.INFO)
+
+    ProcessFiles(args.files)
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/Snippets/interpolate.py b/Snippets/interpolate.py
new file mode 100755
index 0000000..7ed822d
--- /dev/null
+++ b/Snippets/interpolate.py
@@ -0,0 +1,144 @@
+#! /usr/bin/env python
+
+# Illustrates how a fonttools script can construct variable fonts.
+#
+# This script reads Roboto-Thin.ttf, Roboto-Regular.ttf, and
+# Roboto-Black.ttf from /tmp/Roboto, and writes a Multiple Master GX
+# font named "Roboto.ttf" into the current working directory.
+# This output font supports interpolation along the Weight axis,
+# and it contains named instances for "Thin", "Light", "Regular",
+# "Bold", and "Black".
+#
+# All input fonts must contain the same set of glyphs, and these glyphs
+# need to have the same control points in the same order. Note that this
+# is *not* the case for the normal Roboto fonts that can be downloaded
+# from Google. This demo script prints a warning for any problematic
+# glyphs; in the resulting font, these glyphs will not be interpolated
+# and get rendered in the "Regular" weight.
+#
+# Usage:
+# $ mkdir /tmp/Roboto && cp Roboto-*.ttf /tmp/Roboto
+# $ ./interpolate.py && open Roboto.ttf
+
+
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.ttLib import TTFont
+from fontTools.ttLib.tables._n_a_m_e import NameRecord
+from fontTools.ttLib.tables._f_v_a_r import table__f_v_a_r, Axis, NamedInstance
+from fontTools.ttLib.tables._g_v_a_r import table__g_v_a_r, TupleVariation
+import logging
+
+
+def AddFontVariations(font):
+    assert "fvar" not in font
+    fvar = font["fvar"] = table__f_v_a_r()
+
+    weight = Axis()
+    weight.axisTag = "wght"
+    weight.nameID = AddName(font, "Weight").nameID
+    weight.minValue, weight.defaultValue, weight.maxValue = (100, 400, 900)
+    fvar.axes.append(weight)
+
+    # https://www.microsoft.com/typography/otspec/os2.htm#wtc
+    for name, wght in (
+            ("Thin", 100),
+            ("Light", 300),
+            ("Regular", 400),
+            ("Bold", 700),
+            ("Black", 900)):
+        inst = NamedInstance()
+        inst.nameID = AddName(font, name).nameID
+        inst.coordinates = {"wght": wght}
+        fvar.instances.append(inst)
+
+
+def AddName(font, name):
+    """(font, "Bold") --> NameRecord"""
+    nameTable = font.get("name")
+    namerec = NameRecord()
+    namerec.nameID = 1 + max([n.nameID for n in nameTable.names] + [256])
+    namerec.string = name.encode("mac_roman")
+    namerec.platformID, namerec.platEncID, namerec.langID = (1, 0, 0)
+    nameTable.names.append(namerec)
+    return namerec
+
+
+def AddGlyphVariations(font, thin, regular, black):
+    assert "gvar" not in font
+    gvar = font["gvar"] = table__g_v_a_r()
+    gvar.version = 1
+    gvar.reserved = 0
+    gvar.variations = {}
+    for glyphName in regular.getGlyphOrder():
+        regularCoord = GetCoordinates(regular, glyphName)
+        thinCoord = GetCoordinates(thin, glyphName)
+        blackCoord = GetCoordinates(black, glyphName)
+        if not regularCoord or not blackCoord or not thinCoord:            
+            logging.warning("glyph %s not present in all input fonts",
+                            glyphName)
+            continue
+        if (len(regularCoord) != len(blackCoord) or
+            len(regularCoord) != len(thinCoord)):
+            logging.warning("glyph %s has not the same number of "
+                            "control points in all input fonts", glyphName)
+            continue
+        thinDelta = []
+        blackDelta = []
+        for ((regX, regY), (blackX, blackY), (thinX, thinY)) in \
+                zip(regularCoord, blackCoord, thinCoord):
+            thinDelta.append(((thinX - regX, thinY - regY)))
+            blackDelta.append((blackX - regX, blackY - regY))
+        thinVar = TupleVariation({"wght": (-1.0, -1.0, 0.0)}, thinDelta)
+        blackVar = TupleVariation({"wght": (0.0, 1.0, 1.0)}, blackDelta)
+        gvar.variations[glyphName] = [thinVar, blackVar]
+
+
+def GetCoordinates(font, glyphName):
+    """font, glyphName --> glyph coordinates as expected by "gvar" table
+
+    The result includes four "phantom points" for the glyph metrics,
+    as mandated by the "gvar" spec.
+    """
+    glyphTable = font["glyf"]
+    glyph = glyphTable.glyphs.get(glyphName)
+    if glyph is None:
+        return None
+    glyph.expand(glyphTable)
+    glyph.recalcBounds(glyphTable)
+    if glyph.isComposite():
+        coord = [c.getComponentInfo()[1][-2:] for c in glyph.components]
+    else:
+        coord = [c for c in glyph.getCoordinates(glyphTable)[0]]
+    # Add phantom points for (left, right, top, bottom) positions.
+    horizontalAdvanceWidth, leftSideBearing = font["hmtx"].metrics[glyphName]
+
+
+    leftSideX = glyph.xMin - leftSideBearing
+    rightSideX = leftSideX + horizontalAdvanceWidth
+
+    # XXX these are incorrect.  Load vmtx and fix.
+    topSideY = glyph.yMax
+    bottomSideY = -glyph.yMin
+
+    coord.extend([(leftSideX, 0),
+                  (rightSideX, 0),
+                  (0, topSideY),
+                  (0, bottomSideY)])
+    return coord
+
+
+def main():
+    logging.basicConfig(format="%(levelname)s: %(message)s")
+    thin = TTFont("/tmp/Roboto/Roboto-Thin.ttf")
+    regular = TTFont("/tmp/Roboto/Roboto-Regular.ttf")
+    black = TTFont("/tmp/Roboto/Roboto-Black.ttf")
+    out = regular
+    AddFontVariations(out)
+    AddGlyphVariations(out, thin, regular, black)
+    out.save("./Roboto.ttf")
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(main())
diff --git a/Snippets/layout-features.py b/Snippets/layout-features.py
new file mode 100755
index 0000000..25522cd
--- /dev/null
+++ b/Snippets/layout-features.py
@@ -0,0 +1,51 @@
+#! /usr/bin/env python
+
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.ttLib import TTFont
+from fontTools.ttLib.tables import otTables
+import sys
+
+if len(sys.argv) != 2:
+	print("usage: layout-features.py fontfile.ttf")
+	sys.exit(1)
+fontfile = sys.argv[1]
+if fontfile.rsplit(".", 1)[-1] == "ttx":
+	font = TTFont()
+	font.importXML(fontfile)
+else:
+	font = TTFont(fontfile)
+
+for tag in ('GSUB', 'GPOS'):
+	if not tag in font: continue
+	print("Table:", tag)
+	table = font[tag].table
+	if not table.ScriptList or not table.FeatureList: continue
+	featureRecords = table.FeatureList.FeatureRecord
+	for script in table.ScriptList.ScriptRecord:
+		print("  Script:", script.ScriptTag)
+		if not script.Script:
+			print ("    Null script.")
+			continue
+		languages = list(script.Script.LangSysRecord)
+		if script.Script.DefaultLangSys:
+			defaultlangsys = otTables.LangSysRecord()
+			defaultlangsys.LangSysTag = "default"
+			defaultlangsys.LangSys = script.Script.DefaultLangSys
+			languages.insert(0, defaultlangsys)
+		for langsys in languages:
+			print("    Language:", langsys.LangSysTag)
+			if not langsys.LangSys:
+				print ("    Null language.")
+				continue
+			features = [featureRecords[index] for index in langsys.LangSys.FeatureIndex]
+			if langsys.LangSys.ReqFeatureIndex != 0xFFFF:
+				record = featureRecords[langsys.LangSys.ReqFeatureIndex]
+				requiredfeature = otTables.FeatureRecord()
+				requiredfeature.FeatureTag = 'required(%s)' % record.FeatureTag
+				requiredfeature.Feature = record.Feature
+				features.insert(0, requiredfeature)
+			for feature in features:
+				print("      Feature:", feature.FeatureTag)
+				lookups = feature.Feature.LookupListIndex
+				print("        Lookups:", ','.join(str(l) for l in lookups))
diff --git a/Snippets/merge_woff_metadata.py b/Snippets/merge_woff_metadata.py
new file mode 100644
index 0000000..669de46
--- /dev/null
+++ b/Snippets/merge_woff_metadata.py
@@ -0,0 +1,43 @@
+from __future__ import print_function
+import sys
+import os
+from fontTools.ttx import makeOutputFileName
+from fontTools.ttLib import TTFont
+
+
+def main(args=None):
+    if args is None:
+        args = sys.argv[1:]
+
+    if len(args) < 2:
+        print("usage: merge_woff_metadata.py METADATA.xml "
+              "INPUT.woff [OUTPUT.woff]", file=sys.stderr)
+        return 1
+
+    metadata_file = args[0]
+    with open(metadata_file, 'rb') as f:
+        metadata = f.read()
+
+    infile = args[1]
+    if len(args) > 2:
+        outfile = args[2]
+    else:
+        filename, ext = os.path.splitext(infile)
+        outfile = makeOutputFileName(filename, None, ext)
+
+    font = TTFont(infile)
+
+    if font.flavor not in ("woff", "woff2"):
+        print("Input file is not a WOFF or WOFF2 font", file=sys.stderr)
+        return 1
+
+    data = font.flavorData
+
+    # this sets the new WOFF metadata
+    data.metaData = metadata
+
+    font.save(outfile)
+
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/Snippets/otf2ttf.py b/Snippets/otf2ttf.py
new file mode 100755
index 0000000..60acd54
--- /dev/null
+++ b/Snippets/otf2ttf.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python
+from __future__ import print_function, division, absolute_import
+import sys
+from fontTools.ttLib import TTFont, newTable
+from cu2qu.pens import Cu2QuPen
+from fontTools.pens.ttGlyphPen import TTGlyphPen
+from fontTools.ttx import makeOutputFileName
+import argparse
+
+
+# default approximation error, measured in UPEM
+MAX_ERR = 1.0
+
+# default 'post' table format
+POST_FORMAT = 2.0
+
+# assuming the input contours' direction is correctly set (counter-clockwise),
+# we just flip it to clockwise
+REVERSE_DIRECTION = True
+
+
+def glyphs_to_quadratic(
+        glyphs, max_err=MAX_ERR, reverse_direction=REVERSE_DIRECTION):
+    quadGlyphs = {}
+    for gname in glyphs.keys():
+        glyph = glyphs[gname]
+        ttPen = TTGlyphPen(glyphs)
+        cu2quPen = Cu2QuPen(ttPen, max_err,
+                            reverse_direction=reverse_direction)
+        glyph.draw(cu2quPen)
+        quadGlyphs[gname] = ttPen.glyph()
+    return quadGlyphs
+
+
+def otf_to_ttf(ttFont, post_format=POST_FORMAT, **kwargs):
+    assert ttFont.sfntVersion == "OTTO"
+    assert "CFF " in ttFont
+
+    glyphOrder = ttFont.getGlyphOrder()
+
+    ttFont["loca"] = newTable("loca")
+    ttFont["glyf"] = glyf = newTable("glyf")
+    glyf.glyphOrder = glyphOrder
+    glyf.glyphs = glyphs_to_quadratic(ttFont.getGlyphSet(), **kwargs)
+    del ttFont["CFF "]
+
+    ttFont["maxp"] = maxp = newTable("maxp")
+    maxp.tableVersion = 0x00010000
+    maxp.maxZones = 1
+    maxp.maxTwilightPoints = 0
+    maxp.maxStorage = 0
+    maxp.maxFunctionDefs = 0
+    maxp.maxInstructionDefs = 0
+    maxp.maxStackElements = 0
+    maxp.maxSizeOfInstructions = 0
+    maxp.maxComponentElements = max(
+        len(g.components if hasattr(g, 'components') else [])
+        for g in glyf.glyphs.values())
+
+    post = ttFont["post"]
+    post.formatType = post_format
+    post.extraNames = []
+    post.mapping = {}
+    post.glyphOrder = glyphOrder
+
+    ttFont.sfntVersion = "\000\001\000\000"
+
+
+def main(args=None):
+    parser = argparse.ArgumentParser()
+    parser.add_argument("input", metavar="INPUT")
+    parser.add_argument("-o", "--output")
+    parser.add_argument("-e", "--max-error", type=float, default=MAX_ERR)
+    parser.add_argument("--post-format", type=float, default=POST_FORMAT)
+    parser.add_argument(
+        "--keep-direction", dest='reverse_direction', action='store_false')
+    options = parser.parse_args(args)
+
+    output = options.output or makeOutputFileName(options.input,
+                                                  outputDir=None,
+                                                  extension='.ttf')
+    font = TTFont(options.input)
+    otf_to_ttf(font,
+               post_format=options.post_format,
+               max_err=options.max_error,
+               reverse_direction=options.reverse_direction)
+    font.save(output)
+
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/Snippets/rename-fonts.py b/Snippets/rename-fonts.py
new file mode 100755
index 0000000..ddfce10
--- /dev/null
+++ b/Snippets/rename-fonts.py
@@ -0,0 +1,170 @@
+#!/usr/bin/env python
+"""Script to add a suffix to all family names in the input font's `name` table,
+and to optionally rename the output files with the given suffix.
+
+The current family name substring is searched in the nameIDs 1, 3, 4, 6, 16,
+and 21, and if found the suffix is inserted after it; or else the suffix is
+appended at the end.
+"""
+from __future__ import print_function, absolute_import, unicode_literals
+import os
+import argparse
+import logging
+from fontTools.ttLib import TTFont
+from fontTools.misc.cliTools import makeOutputFileName
+
+
+logger = logging.getLogger()
+
+WINDOWS_ENGLISH_IDS = 3, 1, 0x409
+MAC_ROMAN_IDS = 1, 0, 0
+
+FAMILY_RELATED_IDS = dict(
+    LEGACY_FAMILY=1,
+    TRUETYPE_UNIQUE_ID=3,
+    FULL_NAME=4,
+    POSTSCRIPT_NAME=6,
+    PREFERRED_FAMILY=16,
+    WWS_FAMILY=21,
+)
+
+
+def get_current_family_name(table):
+    family_name_rec = None
+    for plat_id, enc_id, lang_id in (WINDOWS_ENGLISH_IDS, MAC_ROMAN_IDS):
+        for name_id in (
+            FAMILY_RELATED_IDS["PREFERRED_FAMILY"],
+            FAMILY_RELATED_IDS["LEGACY_FAMILY"],
+        ):
+            family_name_rec = table.getName(
+                nameID=name_id,
+                platformID=plat_id,
+                platEncID=enc_id,
+                langID=lang_id,
+            )
+            if family_name_rec is not None:
+                break
+        if family_name_rec is not None:
+            break
+    if not family_name_rec:
+        raise ValueError("family name not found; can't add suffix")
+    return family_name_rec.toUnicode()
+
+
+def insert_suffix(string, family_name, suffix):
+    # check whether family_name is a substring
+    start = string.find(family_name)
+    if start != -1:
+        # insert suffix after the family_name substring
+        end = start + len(family_name)
+        new_string = string[:end] + suffix + string[end:]
+    else:
+        # it's not, we just append the suffix at the end
+        new_string = string + suffix
+    return new_string
+
+
+def rename_record(name_record, family_name, suffix):
+    string = name_record.toUnicode()
+    new_string = insert_suffix(string, family_name, suffix)
+    name_record.string = new_string
+    return string, new_string
+
+
+def rename_file(filename, family_name, suffix):
+    filename, ext = os.path.splitext(filename)
+    ps_name = family_name.replace(" ", "")
+    if ps_name in filename:
+        ps_suffix = suffix.replace(" ", "")
+        return insert_suffix(filename, ps_name, ps_suffix) + ext
+    else:
+        return insert_suffix(filename, family_name, suffix) + ext
+
+
+def add_family_suffix(font, suffix):
+    table = font["name"]
+
+    family_name = get_current_family_name(table)
+    logger.info("  Current family name: '%s'", family_name)
+
+    # postcript name can't contain spaces
+    ps_family_name = family_name.replace(" ", "")
+    ps_suffix = suffix.replace(" ", "")
+    for rec in table.names:
+        name_id = rec.nameID
+        if name_id not in FAMILY_RELATED_IDS.values():
+            continue
+        if name_id == FAMILY_RELATED_IDS["POSTSCRIPT_NAME"]:
+            old, new = rename_record(rec, ps_family_name, ps_suffix)
+        elif name_id == FAMILY_RELATED_IDS["TRUETYPE_UNIQUE_ID"]:
+            # The Truetype Unique ID rec may contain either the PostScript
+            # Name or the Full Name string, so we try both
+            if ps_family_name in rec.toUnicode():
+                old, new = rename_record(rec, ps_family_name, ps_suffix)
+            else:
+                old, new = rename_record(rec, family_name, suffix)
+        else:
+            old, new = rename_record(rec, family_name, suffix)
+        logger.info("    %r: '%s' -> '%s'", rec, old, new)
+
+    return family_name
+
+
+def main(args=None):
+    parser = argparse.ArgumentParser(
+        description=__doc__,
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+    )
+    parser.add_argument("-s", "--suffix", required=True)
+    parser.add_argument("input_fonts", metavar="FONTFILE", nargs="+")
+    output_group = parser.add_mutually_exclusive_group()
+    output_group.add_argument("-i", "--inplace", action="store_true")
+    output_group.add_argument("-d", "--output-dir")
+    output_group.add_argument("-o", "--output-file")
+    parser.add_argument("-R", "--rename-files", action="store_true")
+    parser.add_argument("-v", "--verbose", action="count", default=0)
+    options = parser.parse_args(args)
+
+    if not options.verbose:
+        level = "WARNING"
+    elif options.verbose == 1:
+        level = "INFO"
+    else:
+        level = "DEBUG"
+    logging.basicConfig(level=level, format="%(message)s")
+
+    if options.output_file and len(options.input_fonts) > 1:
+        parser.error(
+            "argument -o/--output-file can't be used with multiple inputs"
+        )
+    if options.rename_files and (options.inplace or options.output_file):
+        parser.error("argument -R not allowed with arguments -i or -o")
+
+    for input_name in options.input_fonts:
+        logger.info("Renaming font: '%s'", input_name)
+
+        font = TTFont(input_name)
+        family_name = add_family_suffix(font, options.suffix)
+
+        if options.inplace:
+            output_name = input_name
+        elif options.output_file:
+            output_name = options.output_file
+        else:
+            if options.rename_files:
+                input_name = rename_file(
+                    input_name, family_name, options.suffix
+                )
+            output_name = makeOutputFileName(input_name, options.output_dir)
+
+        font.save(output_name)
+        logger.info("Saved font: '%s'", output_name)
+
+        font.close()
+        del font
+
+    logger.info("Done!")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/Snippets/subset-fpgm.py b/Snippets/subset-fpgm.py
new file mode 100755
index 0000000..c20c05f
--- /dev/null
+++ b/Snippets/subset-fpgm.py
@@ -0,0 +1,60 @@
+#! /usr/bin/env python
+
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.ttLib import TTFont
+import sys
+
+if len(sys.argv) < 2:
+	print("usage: subset-fpgm.py fontfile.ttf func-number...")
+	sys.exit(1)
+fontfile = sys.argv[1]
+func_nums = [int(x) for x in sys.argv[2:]]
+
+font = TTFont(fontfile)
+fpgm = font['fpgm']
+
+# Parse fpgm
+asm = fpgm.program.getAssembly()
+funcs = {}
+stack = []
+tokens = iter(asm)
+for token in tokens:
+	if token.startswith("PUSH") or token.startswith("NPUSH"):
+		for token in tokens:
+			try:
+				num = int(token)
+				stack.append(num)
+			except ValueError:
+				break
+	if token.startswith("FDEF"):
+		num = stack.pop()
+		body = []
+		for token in tokens:
+			if token.startswith("ENDF"):
+				break
+			body.append(token)
+		funcs[num] = body
+		continue
+	assert 0, "Unexpected token in fpgm: %s" % token
+
+# Subset!
+funcs = {i:funcs[i] for i in func_nums}
+
+# Put it back together:
+asm = []
+if funcs:
+	asm.append("PUSH[ ]")
+nums = sorted(funcs.keys())
+asm.extend(str(i) for i in nums)
+for i in nums:
+	asm.append("FDEF[ ]")
+	asm.extend(funcs[i])
+	asm.append("ENDF[ ]")
+
+import pprint
+pprint.pprint(asm)
+
+fpgm.program.fromAssembly(asm)
+# Make sure it compiles
+fpgm.program.getBytecode()
diff --git a/Snippets/svg2glif.py b/Snippets/svg2glif.py
new file mode 100755
index 0000000..2dd6402
--- /dev/null
+++ b/Snippets/svg2glif.py
@@ -0,0 +1,126 @@
+#!/usr/bin/env python
+""" Convert SVG paths to UFO glyphs. """
+
+from __future__ import print_function, absolute_import
+
+__requires__ = ["FontTools", "ufoLib"]
+
+from fontTools.misc.py23 import SimpleNamespace
+from fontTools.svgLib import SVGPath
+
+from ufoLib.pointPen import SegmentToPointPen
+from ufoLib.glifLib import writeGlyphToString
+
+
+__all__ = ["svg2glif"]
+
+
+def svg2glif(svg, name, width=0, height=0, unicodes=None, transform=None,
+             version=2):
+    """ Convert an SVG outline to a UFO glyph with given 'name', advance
+    'width' and 'height' (int), and 'unicodes' (list of int).
+    Return the resulting string in GLIF format (default: version 2).
+    If 'transform' is provided, apply a transformation matrix before the
+    conversion (must be tuple of 6 floats, or a FontTools Transform object).
+    """
+    glyph = SimpleNamespace(width=width, height=height, unicodes=unicodes)
+    outline = SVGPath.fromstring(svg, transform=transform)
+
+    # writeGlyphToString takes a callable (usually a glyph's drawPoints
+    # method) that accepts a PointPen, however SVGPath currently only has
+    # a draw method that accepts a segment pen. We need to wrap the call
+    # with a converter pen.
+    def drawPoints(pointPen):
+        pen = SegmentToPointPen(pointPen)
+        outline.draw(pen)
+
+    return writeGlyphToString(name,
+                              glyphObject=glyph,
+                              drawPointsFunc=drawPoints,
+                              formatVersion=version)
+
+
+def parse_args(args):
+    import argparse
+
+    def split(arg):
+        return arg.replace(",", " ").split()
+
+    def unicode_hex_list(arg):
+        try:
+            return [int(unihex, 16) for unihex in split(arg)]
+        except ValueError:
+            msg = "Invalid unicode hexadecimal value: %r" % arg
+            raise argparse.ArgumentTypeError(msg)
+
+    def transform_list(arg):
+        try:
+            return [float(n) for n in split(arg)]
+        except ValueError:
+            msg = "Invalid transformation matrix: %r" % arg
+            raise argparse.ArgumentTypeError(msg)
+
+    parser = argparse.ArgumentParser(
+        description="Convert SVG outlines to UFO glyphs (.glif)")
+    parser.add_argument(
+        "infile", metavar="INPUT.svg", help="Input SVG file containing "
+        '<path> elements with "d" attributes.')
+    parser.add_argument(
+        "outfile", metavar="OUTPUT.glif", help="Output GLIF file (default: "
+        "print to stdout)", nargs='?')
+    parser.add_argument(
+        "-n", "--name", help="The glyph name (default: input SVG file "
+        "basename, without the .svg extension)")
+    parser.add_argument(
+        "-w", "--width", help="The glyph advance width (default: 0)",
+        type=int, default=0)
+    parser.add_argument(
+        "-H", "--height", help="The glyph vertical advance (optional if "
+        '"width" is defined)', type=int, default=0)
+    parser.add_argument(
+        "-u", "--unicodes", help="List of Unicode code points as hexadecimal "
+        'numbers (e.g. -u "0041 0042")',
+        type=unicode_hex_list)
+    parser.add_argument(
+        "-t", "--transform", help="Transformation matrix as a list of six "
+        'float values (e.g. -t "0.1 0 0 -0.1 -50 200")', type=transform_list)
+    parser.add_argument(
+        "-f", "--format", help="UFO GLIF format version (default: 2)",
+        type=int, choices=(1, 2), default=2)
+
+    return parser.parse_args(args)
+
+
+def main(args=None):
+    from io import open
+
+    options = parse_args(args)
+
+    svg_file = options.infile
+
+    if options.name:
+        name = options.name
+    else:
+        import os
+        name = os.path.splitext(os.path.basename(svg_file))[0]
+
+    with open(svg_file, "r", encoding="utf-8") as f:
+        svg = f.read()
+
+    glif = svg2glif(svg, name,
+                    width=options.width,
+                    height=options.height,
+                    unicodes=options.unicodes,
+                    transform=options.transform,
+                    version=options.format)
+
+    if options.outfile is None:
+        print(glif)
+    else:
+        with open(options.outfile, 'w', encoding='utf-8') as f:
+            f.write(glif)
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(main())
diff --git a/Snippets/woff2_compress.py b/Snippets/woff2_compress.py
new file mode 100755
index 0000000..689ebdc
--- /dev/null
+++ b/Snippets/woff2_compress.py
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.ttLib import TTFont
+from fontTools.ttx import makeOutputFileName
+import sys
+import os
+
+
+def main(args=None):
+    if args is None:
+        args = sys.argv[1:]
+    if len(args) < 1:
+        print("One argument, the input filename, must be provided.", file=sys.stderr)
+        return 1
+
+    filename = args[0]
+    outfilename = makeOutputFileName(filename, outputDir=None, extension='.woff2')
+
+    print("Processing %s => %s" % (filename, outfilename))
+
+    font = TTFont(filename, recalcBBoxes=False, recalcTimestamp=False)
+    font.flavor = "woff2"
+    font.save(outfilename, reorderTables=False)
+
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/Snippets/woff2_decompress.py b/Snippets/woff2_decompress.py
new file mode 100755
index 0000000..e7c1bea
--- /dev/null
+++ b/Snippets/woff2_decompress.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.ttLib import TTFont
+from fontTools.ttx import makeOutputFileName
+import sys
+import os
+
+
+def make_output_name(filename):
+    with open(filename, "rb") as f:
+        f.seek(4)
+        sfntVersion = f.read(4)
+    assert len(sfntVersion) == 4, "not enough data"
+    ext = '.ttf' if sfntVersion == b"\x00\x01\x00\x00" else ".otf"
+    outfilename = makeOutputFileName(filename, outputDir=None, extension=ext)
+    return outfilename
+
+
+def main(args=None):
+    if args is None:
+        args = sys.argv[1:]
+    if len(args) < 1:
+        print("One argument, the input filename, must be provided.", file=sys.stderr)
+        return 1
+
+    filename = args[0]
+    outfilename = make_output_name(filename)
+
+    print("Processing %s => %s" % (filename, outfilename))
+
+    font = TTFont(filename, recalcBBoxes=False, recalcTimestamp=False)
+    font.flavor = None
+    font.save(outfilename, reorderTables=True)
+
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/Tests/afmLib/afmLib_test.py b/Tests/afmLib/afmLib_test.py
new file mode 100644
index 0000000..97fcf8d
--- /dev/null
+++ b/Tests/afmLib/afmLib_test.py
@@ -0,0 +1,55 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+import unittest
+import os
+from fontTools import afmLib
+
+
+CWD = os.path.abspath(os.path.dirname(__file__))
+DATADIR = os.path.join(CWD, 'data')
+AFM = os.path.join(DATADIR, 'TestAFM.afm')
+
+
+class AFMTest(unittest.TestCase):
+
+	def test_read_afm(self):
+		afm = afmLib.AFM(AFM)
+		self.assertEqual(sorted(afm.kernpairs()), 
+			sorted([('V', 'A'), ('T', 'comma'), ('V', 'd'), ('T', 'c'), ('T', 'period')]))
+		self.assertEqual(afm['V', 'A'], -60)
+		self.assertEqual(afm['V', 'd'], 30)
+		self.assertEqual(afm['A'], (65, 668, (8, -25, 660, 666)))
+
+	def test_write_afm(self):
+		afm = afmLib.AFM(AFM)
+		newAfm, afmData = self.write(afm)
+		self.assertEqual(afm.kernpairs(), newAfm.kernpairs())
+		self.assertEqual(afm.chars(), newAfm.chars())
+		self.assertEqual(afm.comments(), newAfm.comments()[1:])  # skip the "generated by afmLib" comment
+		for pair in afm.kernpairs():
+			self.assertEqual(afm[pair], newAfm[pair])
+		for char in afm.chars():
+			self.assertEqual(afm[char], newAfm[char])
+		with open(AFM, 'r') as f:
+			originalLines = f.read().splitlines()
+		newLines = afmData.splitlines()
+		del newLines[1]  # remove the "generated by afmLib" comment
+		self.assertEqual(originalLines, newLines)
+
+	@staticmethod
+	def write(afm, sep='\r'):
+		temp = os.path.join(DATADIR, 'temp.afm')
+		try:
+			afm.write(temp, sep)
+			with open(temp, 'r') as f:
+				afmData = f.read()
+			afm = afmLib.AFM(temp)
+		finally:
+			if os.path.exists(temp):
+				os.remove(temp)
+		return afm, afmData
+
+
+if __name__ == '__main__':
+	import sys
+	sys.exit(unittest.main())
diff --git a/Tests/afmLib/data/TestAFM.afm b/Tests/afmLib/data/TestAFM.afm
new file mode 100644
index 0000000..634c66a
--- /dev/null
+++ b/Tests/afmLib/data/TestAFM.afm
@@ -0,0 +1,37 @@
+StartFontMetrics 2.0
+Comment UniqueID 2123703
+Comment Panose 2 0 6 3 3 0 0 2 0 4
+FontName TestFont-Regular
+FullName TestFont-Regular
+FamilyName TestFont
+Weight Regular
+ItalicAngle 0.00
+IsFixedPitch false
+FontBBox -94 -317 1316 1009
+UnderlinePosition -296
+UnderlineThickness 111
+Version 001.000
+Notice [c] Copyright 2017. All Rights Reserved.
+EncodingScheme FontSpecific
+CapHeight 700
+XHeight 500
+Ascender 750
+Descender -250
+StdHW 181
+StdVW 194
+StartCharMetrics 4
+C 32 ; WX 200 ; N space ; B 0 0 0 0 ;
+C 65 ; WX 668 ; N A ; B 8 -25 660 666 ;
+C 66 ; WX 543 ; N B ; B 36 0 522 666 ;
+C 67 ; WX 582 ; N C ; B 24 -21 564 687 ;
+EndCharMetrics
+StartKernData
+StartKernPairs 5
+KPX T c 30
+KPX T comma -100
+KPX T period -100
+KPX V A -60
+KPX V d 30
+EndKernPairs
+EndKernData
+EndFontMetrics
diff --git a/Tests/agl_test.py b/Tests/agl_test.py
new file mode 100644
index 0000000..8754e6a
--- /dev/null
+++ b/Tests/agl_test.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+from __future__ import (print_function, division, absolute_import,
+                        unicode_literals)
+from fontTools.misc.py23 import *
+from fontTools import agl
+import unittest
+
+
+class AglToUnicodeTest(unittest.TestCase):
+    def test_spec_examples(self):
+        # https://github.com/adobe-type-tools/agl-specification#3-examples
+        #
+        # TODO: Currently, we only handle AGLFN instead of legacy AGL names.
+        # Therefore, the test cases below use Iogonek instead of Lcommaaccent.
+        # Change Iogonek to Lcommaaccent as soon as the implementation has
+        # been fixed to also support legacy AGL names.
+        # https://github.com/fonttools/fonttools/issues/775
+        self.assertEqual(agl.toUnicode("Iogonek"), "Į")
+        self.assertEqual(agl.toUnicode("uni20AC0308"), "\u20AC\u0308")
+        self.assertEqual(agl.toUnicode("u1040C"), "\U0001040C")
+        self.assertEqual(agl.toUnicode("uniD801DC0C"), "")
+        self.assertEqual(agl.toUnicode("uni20ac"), "")
+        self.assertEqual(
+            agl.toUnicode("Iogonek_uni20AC0308_u1040C.alternate"),
+            "\u012E\u20AC\u0308\U0001040C")
+        self.assertEqual(agl.toUnicode("Iogonek_uni012E_u012E"), "ĮĮĮ")
+        self.assertEqual(agl.toUnicode("foo"), "")
+        self.assertEqual(agl.toUnicode(".notdef"), "")
+
+    def test_aglfn(self):
+        self.assertEqual(agl.toUnicode("longs_t"), "ſt")
+        self.assertEqual(agl.toUnicode("f_f_i.alt123"), "ffi")
+
+    def test_uniABCD(self):
+        self.assertEqual(agl.toUnicode("uni0041"), "A")
+        self.assertEqual(agl.toUnicode("uni0041_uni0042_uni0043"), "ABC")
+        self.assertEqual(agl.toUnicode("uni004100420043"), "ABC")
+        self.assertEqual(agl.toUnicode("uni"), "")
+        self.assertEqual(agl.toUnicode("uni41"), "")
+        self.assertEqual(agl.toUnicode("uni004101"), "")
+        self.assertEqual(agl.toUnicode("uniDC00"), "")
+
+    def test_uABCD(self):
+        self.assertEqual(agl.toUnicode("u0041"), "A")
+        self.assertEqual(agl.toUnicode("u00041"), "A")
+        self.assertEqual(agl.toUnicode("u000041"), "A")
+        self.assertEqual(agl.toUnicode("u0000041"), "")
+        self.assertEqual(agl.toUnicode("u0041_uni0041_A.alt"), "AAA")
+
+    def test_union(self):
+        # Interesting test case because "uni" is a prefix of "union".
+        self.assertEqual(agl.toUnicode("union"), "∪")
+        # U+222A U+FE00 is a Standardized Variant for UNION WITH SERIFS.
+        self.assertEqual(agl.toUnicode("union_uniFE00"), "\u222A\uFE00")
+
+    def test_dingbats(self):
+        self.assertEqual(agl.toUnicode("a20", isZapfDingbats=True), "✔")
+        self.assertEqual(agl.toUnicode("a20.alt", isZapfDingbats=True), "✔")
+        self.assertEqual(agl.toUnicode("a206", isZapfDingbats=True), "❰")
+        self.assertEqual(agl.toUnicode("a20", isZapfDingbats=False), "")
+        self.assertEqual(agl.toUnicode("a0", isZapfDingbats=True), "")
+        self.assertEqual(agl.toUnicode("a207", isZapfDingbats=True), "")
+        self.assertEqual(agl.toUnicode("abcdef", isZapfDingbats=True), "")
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/cffLib/cffLib_test.py b/Tests/cffLib/cffLib_test.py
new file mode 100644
index 0000000..1d169c9
--- /dev/null
+++ b/Tests/cffLib/cffLib_test.py
@@ -0,0 +1,64 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.cffLib import TopDict, PrivateDict, CharStrings
+from fontTools.misc.testTools import parseXML, DataFilesHandler
+from fontTools.ttLib import TTFont
+import sys
+import unittest
+
+
+class CffLibTest(DataFilesHandler):
+
+    def test_topDict_recalcFontBBox(self):
+        topDict = TopDict()
+        topDict.CharStrings = CharStrings(None, None, None, PrivateDict(), None, None)
+        topDict.CharStrings.fromXML(None, None, parseXML("""
+            <CharString name=".notdef">
+              endchar
+            </CharString>
+            <CharString name="foo"><!-- [100, -100, 300, 100] -->
+              100 -100 rmoveto 200 hlineto 200 vlineto -200 hlineto endchar
+            </CharString>
+            <CharString name="bar"><!-- [0, 0, 200, 200] -->
+              0 0 rmoveto 200 hlineto 200 vlineto -200 hlineto endchar
+            </CharString>
+            <CharString name="baz"><!-- [-55.1, -55.1, 55.1, 55.1] -->
+              -55.1 -55.1 rmoveto 110.2 hlineto 110.2 vlineto -110.2 hlineto endchar
+            </CharString>
+        """))
+
+        topDict.recalcFontBBox()
+        self.assertEqual(topDict.FontBBox, [-56, -100, 300, 200])
+
+    def test_topDict_recalcFontBBox_empty(self):
+        topDict = TopDict()
+        topDict.CharStrings = CharStrings(None, None, None, PrivateDict(), None, None)
+        topDict.CharStrings.fromXML(None, None, parseXML("""
+            <CharString name=".notdef">
+              endchar
+            </CharString>
+            <CharString name="space">
+              123 endchar
+            </CharString>
+        """))
+
+        topDict.recalcFontBBox()
+        self.assertEqual(topDict.FontBBox, [0, 0, 0, 0])
+
+    def test_topDict_set_Encoding(self):
+        file_name = 'TestOTF.otf'
+        font_path = self.getpath(file_name)
+        temp_path = self.temp_font(font_path, file_name)
+        save_path = temp_path[:-4] + '2.otf'
+        font = TTFont(temp_path)
+        topDict = font["CFF "].cff.topDictIndex[0]
+        encoding = [".notdef"] * 256
+        encoding[0x20] = "space"
+        topDict.Encoding = encoding
+        font.save(save_path)
+        font2 = TTFont(save_path)
+        topDict2 = font2["CFF "].cff.topDictIndex[0]
+        self.assertEqual(topDict2.Encoding[32], "space")
+
+
+if __name__ == "__main__":
+    sys.exit(unittest.main())
diff --git a/Tests/cffLib/data/TestOTF.otf b/Tests/cffLib/data/TestOTF.otf
new file mode 100644
index 0000000..cd0bdf6
--- /dev/null
+++ b/Tests/cffLib/data/TestOTF.otf
Binary files differ
diff --git a/Tests/cffLib/specializer_test.py b/Tests/cffLib/specializer_test.py
new file mode 100644
index 0000000..aa50611
--- /dev/null
+++ b/Tests/cffLib/specializer_test.py
@@ -0,0 +1,918 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.cffLib.specializer import (programToString, stringToProgram,
+                                          generalizeProgram, specializeProgram)
+import unittest
+
+# TODO
+# https://github.com/fonttools/fonttools/pull/959#commitcomment-22059841
+# Maybe we should make these data driven. Each entry will have an input string,
+# and a generalized and specialized. For the latter two, if they are None, they
+# are considered equal to the input. Then we can do roundtripping tests as well...
+# There are a few other places (aosp tests for example) where we generate tests
+# from data.
+
+
+def get_generalized_charstr(charstr, **kwargs):
+    return programToString(generalizeProgram(stringToProgram(charstr), **kwargs))
+
+
+def get_specialized_charstr(charstr, **kwargs):
+    return programToString(specializeProgram(stringToProgram(charstr), **kwargs))
+
+
+class CFFGeneralizeProgramTest(unittest.TestCase):
+
+    def __init__(self, methodName):
+        unittest.TestCase.__init__(self, methodName)
+        # Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
+        # and fires deprecation warnings if a program uses the old name.
+        if not hasattr(self, "assertRaisesRegex"):
+            self.assertRaisesRegex = self.assertRaisesRegexp
+
+# no arguments/operands
+    def test_rmoveto_none(self):
+        test_charstr = 'rmoveto'
+        with self.assertRaisesRegex(ValueError, r'\[\]'):
+            get_generalized_charstr(test_charstr)
+
+    def test_hmoveto_none(self):
+        test_charstr = 'hmoveto'
+        with self.assertRaisesRegex(ValueError, r'\[\]'):
+            get_generalized_charstr(test_charstr)
+
+    def test_vmoveto_none(self):
+        test_charstr = 'vmoveto'
+        with self.assertRaisesRegex(ValueError, r'\[\]'):
+            get_generalized_charstr(test_charstr)
+
+    def test_rlineto_none(self):
+        test_charstr = 'rlineto'
+        with self.assertRaisesRegex(ValueError, r'\[\]'):
+            get_generalized_charstr(test_charstr)
+
+    def test_hlineto_none(self):
+        test_charstr = 'hlineto'
+        with self.assertRaisesRegex(ValueError, r'\[\]'):
+            get_generalized_charstr(test_charstr)
+
+    def test_vlineto_none(self):
+        test_charstr = 'vlineto'
+        with self.assertRaisesRegex(ValueError, r'\[\]'):
+            get_generalized_charstr(test_charstr)
+
+    def test_rrcurveto_none(self):
+        test_charstr = 'rrcurveto'
+        with self.assertRaisesRegex(ValueError, r'\[\]'):
+            get_generalized_charstr(test_charstr)
+
+    def test_hhcurveto_none(self):
+        test_charstr = 'hhcurveto'
+        with self.assertRaisesRegex(ValueError, r'\[\]'):
+            get_generalized_charstr(test_charstr)
+
+    def test_vvcurveto_none(self):
+        test_charstr = 'vvcurveto'
+        with self.assertRaisesRegex(ValueError, r'\[\]'):
+            get_generalized_charstr(test_charstr)
+
+    def test_hvcurveto_none(self):
+        test_charstr = 'hvcurveto'
+        with self.assertRaisesRegex(ValueError, r'\[\]'):
+            get_generalized_charstr(test_charstr)
+
+    def test_vhcurveto_none(self):
+        test_charstr = 'vhcurveto'
+        with self.assertRaisesRegex(ValueError, r'\[\]'):
+            get_generalized_charstr(test_charstr)
+
+    def test_rcurveline_none(self):
+        test_charstr = 'rcurveline'
+        with self.assertRaisesRegex(ValueError, r'\[\]'):
+            get_generalized_charstr(test_charstr)
+
+    def test_rlinecurve_none(self):
+        test_charstr = 'rlinecurve'
+        with self.assertRaisesRegex(ValueError, r'\[\]'):
+            get_generalized_charstr(test_charstr)
+
+# rmoveto
+    def test_rmoveto_zero(self):
+        test_charstr = '0 0 rmoveto'
+        xpct_charstr = test_charstr
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_rmoveto_zero_width(self):
+        test_charstr = '100 0 0 rmoveto'
+        xpct_charstr = test_charstr
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_rmoveto(self):
+        test_charstr = '.55 -.8 rmoveto'
+        xpct_charstr = '0.55 -0.8 rmoveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_rmoveto_width(self):
+        test_charstr = '100.5 50 -5.8 rmoveto'
+        xpct_charstr = test_charstr
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+# hmoveto
+    def test_hmoveto_zero(self):
+        test_charstr = '0 hmoveto'
+        xpct_charstr = '0 0 rmoveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_hmoveto_zero_width(self):
+        test_charstr = '100 0 hmoveto'
+        xpct_charstr = '100 0 0 rmoveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_hmoveto(self):
+        test_charstr = '.67 hmoveto'
+        xpct_charstr = '0.67 0 rmoveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_hmoveto_width(self):
+        test_charstr = '100 -70 hmoveto'
+        xpct_charstr = '100 -70 0 rmoveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+# vmoveto
+    def test_vmoveto_zero(self):
+        test_charstr = '0 vmoveto'
+        xpct_charstr = '0 0 rmoveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_vmoveto_zero_width(self):
+        test_charstr = '100 0 vmoveto'
+        xpct_charstr = '100 0 0 rmoveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_vmoveto(self):
+        test_charstr = '-.24 vmoveto'
+        xpct_charstr = '0 -0.24 rmoveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_vmoveto_width(self):
+        test_charstr = '100 44 vmoveto'
+        xpct_charstr = '100 0 44 rmoveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+# rlineto
+    def test_rlineto_zero(self):
+        test_charstr = '0 0 rlineto'
+        xpct_charstr = test_charstr
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_rlineto_zero_mult(self):
+        test_charstr = '0 0 0 0 0 0 rlineto'
+        xpct_charstr = ('0 0 rlineto '*3).rstrip()
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_rlineto(self):
+        test_charstr = '.55 -.8 rlineto'
+        xpct_charstr = '0.55 -0.8 rlineto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_rlineto_mult(self):
+        test_charstr = '.55 -.8 .55 -.8 .55 -.8 rlineto'
+        xpct_charstr = ('0.55 -0.8 rlineto '*3).rstrip()
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+# hlineto
+    def test_hlineto_zero(self):
+        test_charstr = '0 hlineto'
+        xpct_charstr = '0 0 rlineto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_hlineto_zero_mult(self):
+        test_charstr = '0 0 0 0 hlineto'
+        xpct_charstr = ('0 0 rlineto '*4).rstrip()
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_hlineto(self):
+        test_charstr = '.67 hlineto'
+        xpct_charstr = '0.67 0 rlineto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_hlineto_mult(self):
+        test_charstr = '.67 -6.0 .67 hlineto'
+        xpct_charstr = '0.67 0 rlineto 0 -6.0 rlineto 0.67 0 rlineto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+# vlineto
+    def test_vlineto_zero(self):
+        test_charstr = '0 vlineto'
+        xpct_charstr = '0 0 rlineto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_vlineto_zero_mult(self):
+        test_charstr = '0 0 0 vlineto'
+        xpct_charstr = ('0 0 rlineto '*3).rstrip()
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_vlineto(self):
+        test_charstr = '-.24 vlineto'
+        xpct_charstr = '0 -0.24 rlineto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_vlineto_mult(self):
+        test_charstr = '-.24 +50 30 -4 vlineto'
+        xpct_charstr = '0 -0.24 rlineto 50 0 rlineto 0 30 rlineto -4 0 rlineto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+# rrcurveto
+    def test_rrcurveto(self):
+        test_charstr = '-1 56 -2 57 -1 57 rrcurveto'
+        xpct_charstr = test_charstr
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_rrcurveto_mult(self):
+        test_charstr = '-30 8 -36 15 -37 22 44 54 31 61 22 68 rrcurveto'
+        xpct_charstr = '-30 8 -36 15 -37 22 rrcurveto 44 54 31 61 22 68 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_rrcurveto_d3947b8(self):
+        test_charstr = '1 2 3 4 5 0 rrcurveto'
+        xpct_charstr = test_charstr
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_rrcurveto_v0_0h_h0(self):
+        test_charstr = '0 10 1 2 0 0 0 0 1 2 0 1 0 1 3 4 0 0 rrcurveto'
+        xpct_charstr = '0 10 1 2 0 0 rrcurveto 0 0 1 2 0 1 rrcurveto 0 1 3 4 0 0 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_rrcurveto_h0_0h_h0(self):
+        test_charstr = '10 0 1 2 0 0 0 0 1 2 0 1 0 1 3 4 0 0 rrcurveto'
+        xpct_charstr = '10 0 1 2 0 0 rrcurveto 0 0 1 2 0 1 rrcurveto 0 1 3 4 0 0 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_rrcurveto_00_0h_h0(self):
+        test_charstr = '0 0 1 2 0 0 0 0 1 2 0 1 0 1 3 4 0 0 rrcurveto'
+        xpct_charstr = '0 0 1 2 0 0 rrcurveto 0 0 1 2 0 1 rrcurveto 0 1 3 4 0 0 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_rrcurveto_r0_0h_h0(self):
+        test_charstr = '10 10 1 2 0 0 0 0 1 2 0 1 0 1 3 4 0 0 rrcurveto'
+        xpct_charstr = '10 10 1 2 0 0 rrcurveto 0 0 1 2 0 1 rrcurveto 0 1 3 4 0 0 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_rrcurveto_v0_0v_v0(self):
+        test_charstr = '0 10 1 2 0 0 0 0 1 2 1 0 1 0 3 4 0 0 rrcurveto'
+        xpct_charstr = '0 10 1 2 0 0 rrcurveto 0 0 1 2 1 0 rrcurveto 1 0 3 4 0 0 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_rrcurveto_h0_0v_v0(self):
+        test_charstr = '10 0 1 2 0 0 0 0 1 2 1 0 1 0 3 4 0 0 rrcurveto'
+        xpct_charstr = '10 0 1 2 0 0 rrcurveto 0 0 1 2 1 0 rrcurveto 1 0 3 4 0 0 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_rrcurveto_00_0v_v0(self):
+        test_charstr = '0 0 1 2 0 0 0 0 1 2 1 0 1 0 3 4 0 0 rrcurveto'
+        xpct_charstr = '0 0 1 2 0 0 rrcurveto 0 0 1 2 1 0 rrcurveto 1 0 3 4 0 0 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_rrcurveto_r0_0v_v0(self):
+        test_charstr = '10 10 1 2 0 0 0 0 1 2 1 0 1 0 3 4 0 0 rrcurveto'
+        xpct_charstr = '10 10 1 2 0 0 rrcurveto 0 0 1 2 1 0 rrcurveto 1 0 3 4 0 0 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+# hhcurveto
+    def test_hhcurveto_4(self):
+        test_charstr = '10 30 0 10 hhcurveto'
+        xpct_charstr = '10 0 30 0 10 0 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_hhcurveto_5(self):
+        test_charstr = '40 -38 -60 41 -91 hhcurveto'
+        xpct_charstr = '-38 40 -60 41 -91 0 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_hhcurveto_mult_4_4(self):
+        test_charstr = '43 23 25 18 29 56 42 -84 hhcurveto'
+        xpct_charstr = '43 0 23 25 18 0 rrcurveto 29 0 56 42 -84 0 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_hhcurveto_mult_5_4(self):
+        test_charstr = '43 23 25 18 29 56 42 -84 79 hhcurveto'
+        xpct_charstr = '23 43 25 18 29 0 rrcurveto 56 0 42 -84 79 0 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_hhcurveto_mult_4_4_4(self):
+        test_charstr = '1 2 3 4 5 6 7 8 9 10 11 12 hhcurveto'
+        xpct_charstr = '1 0 2 3 4 0 rrcurveto 5 0 6 7 8 0 rrcurveto 9 0 10 11 12 0 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_hhcurveto_mult_5_4_4(self):
+        test_charstr = '1 2 3 4 5 6 7 8 9 10 11 12 13 hhcurveto'
+        xpct_charstr = '2 1 3 4 5 0 rrcurveto 6 0 7 8 9 0 rrcurveto 10 0 11 12 13 0 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+# vvcurveto
+    def test_vvcurveto_4(self):
+        test_charstr = '61 6 52 68 vvcurveto'
+        xpct_charstr = '0 61 6 52 0 68 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_vvcurveto_5(self):
+        test_charstr = '61 38 35 56 72 vvcurveto'
+        xpct_charstr = '61 38 35 56 0 72 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_vvcurveto_mult_4_4(self):
+        test_charstr = '-84 -88 -30 -90 -13 19 23 -11 vvcurveto'
+        xpct_charstr = '0 -84 -88 -30 0 -90 rrcurveto 0 -13 19 23 0 -11 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_vvcurveto_mult_5_4(self):
+        test_charstr = '43 12 17 32 65 68 -6 52 61 vvcurveto'
+        xpct_charstr = '43 12 17 32 0 65 rrcurveto 0 68 -6 52 0 61 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_vvcurveto_mult_4_4_4(self):
+        test_charstr = '1 2 3 4 5 6 7 8 9 10 11 12 vvcurveto'
+        xpct_charstr = '0 1 2 3 0 4 rrcurveto 0 5 6 7 0 8 rrcurveto 0 9 10 11 0 12 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_vvcurveto_mult_5_4_4(self):
+        test_charstr = '1 2 3 4 5 6 7 8 9 10 11 12 13 vvcurveto'
+        xpct_charstr = '1 2 3 4 0 5 rrcurveto 0 6 7 8 0 9 rrcurveto 0 10 11 12 0 13 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+# hvcurveto
+    def test_hvcurveto_4(self):
+        test_charstr = '1 2 3 4 hvcurveto'
+        xpct_charstr = '1 0 2 3 0 4 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_hvcurveto_5(self):
+        test_charstr = '57 44 22 40 34 hvcurveto'
+        xpct_charstr = '57 0 44 22 34 40 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_hvcurveto_4_4(self):
+        test_charstr = '65 33 -19 -45 -45 -29 -25 -71 hvcurveto'
+        xpct_charstr = '65 0 33 -19 0 -45 rrcurveto 0 -45 -29 -25 -71 0 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_hvcurveto_4_5(self):
+        test_charstr = '97 69 41 86 58 -36 34 -64 11 hvcurveto'
+        xpct_charstr = '97 0 69 41 0 86 rrcurveto 0 58 -36 34 -64 11 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_hvcurveto_4_4_4(self):
+        test_charstr = '1 2 3 4 5 6 7 8 9 10 11 12 hvcurveto'
+        xpct_charstr = '1 0 2 3 0 4 rrcurveto 0 5 6 7 8 0 rrcurveto 9 0 10 11 0 12 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_hvcurveto_4_4_5(self):
+        test_charstr = '-124 -79 104 165 163 82 102 124 56 43 -25 -37 35 hvcurveto'
+        xpct_charstr = '-124 0 -79 104 0 165 rrcurveto 0 163 82 102 124 0 rrcurveto 56 0 43 -25 35 -37 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_hvcurveto_4_4_4_4(self):
+        test_charstr = '32 25 22 32 31 -25 22 -32 -32 -25 -22 -31 -32 25 -22 32 hvcurveto'
+        xpct_charstr = '32 0 25 22 0 32 rrcurveto 0 31 -25 22 -32 0 rrcurveto -32 0 -25 -22 0 -31 rrcurveto 0 -32 25 -22 32 0 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_hvcurveto_4_4_4_4_5(self):
+        test_charstr = '-170 -128 111 195 234 172 151 178 182 95 -118 -161 -130 -71 -77 -63 -55 -19 38 79 20 hvcurveto'
+        xpct_charstr = '-170 0 -128 111 0 195 rrcurveto 0 234 172 151 178 0 rrcurveto 182 0 95 -118 0 -161 rrcurveto 0 -130 -71 -77 -63 0 rrcurveto -55 0 -19 38 20 79 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+# vhcurveto
+    def test_vhcurveto_4(self):
+        test_charstr = '-57 43 -30 53 vhcurveto'
+        xpct_charstr = '0 -57 43 -30 53 0 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_vhcurveto_5(self):
+        test_charstr = '41 -27 19 -46 11 vhcurveto'
+        xpct_charstr = '0 41 -27 19 -46 11 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_vhcurveto_4_4(self):
+        test_charstr = '1 2 3 4 5 6 7 8 vhcurveto'
+        xpct_charstr = '0 1 2 3 4 0 rrcurveto 5 0 6 7 0 8 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_vhcurveto_4_5(self):
+        test_charstr = '-64 -23 -25 -45 -30 -24 14 33 -19 vhcurveto'
+        xpct_charstr = '0 -64 -23 -25 -45 0 rrcurveto -30 0 -24 14 -19 33 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_vhcurveto_4_4_4(self):
+        test_charstr = '1 2 3 4 5 6 7 8 9 10 11 12 vhcurveto'
+        xpct_charstr = '0 1 2 3 4 0 rrcurveto 5 0 6 7 0 8 rrcurveto 0 9 10 11 12 0 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_vhcurveto_4_4_5(self):
+        test_charstr = '108 59 81 98 99 59 -81 -108 -100 -46 -66 -63 -47 vhcurveto'
+        xpct_charstr = '0 108 59 81 98 0 rrcurveto 99 0 59 -81 0 -108 rrcurveto 0 -100 -46 -66 -63 -47 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_vhcurveto_4_4_4_5(self):
+        test_charstr = '60 -26 37 -43 -33 -28 -22 -36 -37 27 -20 32 3 4 0 1 3 vhcurveto'
+        xpct_charstr = '0 60 -26 37 -43 0 rrcurveto -33 0 -28 -22 0 -36 rrcurveto 0 -37 27 -20 32 0 rrcurveto 3 0 4 0 3 1 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+# rcurveline
+    def test_rcurveline_6_2(self):
+        test_charstr = '21 -76 21 -72 24 -73 31 -100 rcurveline'
+        xpct_charstr = '21 -76 21 -72 24 -73 rrcurveto 31 -100 rlineto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_rcurveline_6_6_2(self):
+        test_charstr = '-73 80 -80 121 -49 96 60 65 55 41 54 17 -8 78 rcurveline'
+        xpct_charstr = '-73 80 -80 121 -49 96 rrcurveto 60 65 55 41 54 17 rrcurveto -8 78 rlineto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_rcurveline_6_6_6_2(self):
+        test_charstr = '1 64 10 51 29 39 15 21 15 20 15 18 47 -89 63 -98 52 -59 91 8 rcurveline'
+        xpct_charstr = '1 64 10 51 29 39 rrcurveto 15 21 15 20 15 18 rrcurveto 47 -89 63 -98 52 -59 rrcurveto 91 8 rlineto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_rcurveline_6_6_6_6_2(self):
+        test_charstr = '1 64 10 51 29 39 15 21 15 20 15 18 46 -88 63 -97 52 -59 -38 -57 -49 -62 -52 -54 96 -8 rcurveline'
+        xpct_charstr = '1 64 10 51 29 39 rrcurveto 15 21 15 20 15 18 rrcurveto 46 -88 63 -97 52 -59 rrcurveto -38 -57 -49 -62 -52 -54 rrcurveto 96 -8 rlineto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+# rlinecurve
+    def test_rlinecurve_2_6(self):
+        test_charstr = '21 -76 21 -72 24 -73 31 -100 rlinecurve'
+        xpct_charstr = '21 -76 rlineto 21 -72 24 -73 31 -100 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_rlinecurve_2_2_6(self):
+        test_charstr = '-73 80 -80 121 -49 96 60 65 55 41 rlinecurve'
+        xpct_charstr = '-73 80 rlineto -80 121 rlineto -49 96 60 65 55 41 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_rlinecurve_2_2_2_6(self):
+        test_charstr = '1 64 10 51 29 39 15 21 15 20 15 18 rlinecurve'
+        xpct_charstr = '1 64 rlineto 10 51 rlineto 29 39 rlineto 15 21 15 20 15 18 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+    def test_rlinecurve_2_2_2_2_6(self):
+        test_charstr = '1 64 10 51 29 39 15 21 15 20 15 18 46 -88 rlinecurve'
+        xpct_charstr = '1 64 rlineto 10 51 rlineto 29 39 rlineto 15 21 rlineto 15 20 15 18 46 -88 rrcurveto'
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+# hstem/vstem
+    def test_hstem_vstem(self):
+        test_charstr = '95 0 58 542 60 hstem 89 65 344 67 vstem 89 45 rmoveto'
+        xpct_charstr = test_charstr
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+# hstemhm/vstemhm
+    def test_hstemhm_vstemhm(self):
+        test_charstr = '-16 577 60 24 60 hstemhm 98 55 236 55 vstemhm 343 577 rmoveto'
+        xpct_charstr = test_charstr
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+# hintmask/cntrmask
+    def test_hintmask_cntrmask(self):
+        test_charstr = '52 80 153 61 4 83 -71.5 71.5 hintmask 11011100 94 119 216 119 216 119 cntrmask 1110000 154 -12 rmoveto'
+        xpct_charstr = test_charstr
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+# endchar
+    def test_endchar(self):
+        test_charstr = '-255 319 rmoveto 266 57 rlineto endchar'
+        xpct_charstr = test_charstr
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+# xtra
+    def test_xtra(self):
+        test_charstr = '-255 319 rmoveto 266 57 rlineto xtra 90 34'
+        xpct_charstr = test_charstr
+        self.assertEqual(get_generalized_charstr(test_charstr), xpct_charstr)
+
+
+class CFFSpecializeProgramTest(unittest.TestCase):
+
+    def __init__(self, methodName):
+        unittest.TestCase.__init__(self, methodName)
+        # Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
+        # and fires deprecation warnings if a program uses the old name.
+        if not hasattr(self, "assertRaisesRegex"):
+            self.assertRaisesRegex = self.assertRaisesRegexp
+
+# no arguments/operands
+    def test_rmoveto_none(self):
+        test_charstr = 'rmoveto'
+        with self.assertRaisesRegex(ValueError, r'\[\]'):
+            get_specialized_charstr(test_charstr)
+
+    def test_hmoveto_none(self):
+        test_charstr = 'hmoveto'
+        with self.assertRaisesRegex(ValueError, r'\[\]'):
+            get_specialized_charstr(test_charstr)
+
+    def test_vmoveto_none(self):
+        test_charstr = 'vmoveto'
+        with self.assertRaisesRegex(ValueError, r'\[\]'):
+            get_specialized_charstr(test_charstr)
+
+    def test_rlineto_none(self):
+        test_charstr = 'rlineto'
+        with self.assertRaisesRegex(ValueError, r'\[\]'):
+            get_specialized_charstr(test_charstr)
+
+    def test_hlineto_none(self):
+        test_charstr = 'hlineto'
+        with self.assertRaisesRegex(ValueError, r'\[\]'):
+            get_specialized_charstr(test_charstr)
+
+    def test_vlineto_none(self):
+        test_charstr = 'vlineto'
+        with self.assertRaisesRegex(ValueError, r'\[\]'):
+            get_specialized_charstr(test_charstr)
+
+    def test_rrcurveto_none(self):
+        test_charstr = 'rrcurveto'
+        with self.assertRaisesRegex(ValueError, r'\[\]'):
+            get_specialized_charstr(test_charstr)
+
+    def test_hhcurveto_none(self):
+        test_charstr = 'hhcurveto'
+        with self.assertRaisesRegex(ValueError, r'\[\]'):
+            get_specialized_charstr(test_charstr)
+
+    def test_vvcurveto_none(self):
+        test_charstr = 'vvcurveto'
+        with self.assertRaisesRegex(ValueError, r'\[\]'):
+            get_specialized_charstr(test_charstr)
+
+    def test_hvcurveto_none(self):
+        test_charstr = 'hvcurveto'
+        with self.assertRaisesRegex(ValueError, r'\[\]'):
+            get_specialized_charstr(test_charstr)
+
+    def test_vhcurveto_none(self):
+        test_charstr = 'vhcurveto'
+        with self.assertRaisesRegex(ValueError, r'\[\]'):
+            get_specialized_charstr(test_charstr)
+
+    def test_rcurveline_none(self):
+        test_charstr = 'rcurveline'
+        with self.assertRaisesRegex(ValueError, r'\[\]'):
+            get_specialized_charstr(test_charstr)
+
+    def test_rlinecurve_none(self):
+        test_charstr = 'rlinecurve'
+        with self.assertRaisesRegex(ValueError, r'\[\]'):
+            get_specialized_charstr(test_charstr)
+
+# rmoveto
+    def test_rmoveto_zero(self):
+        test_charstr = '0 0 rmoveto'
+        xpct_charstr = '0 hmoveto'
+        self.assertEqual(get_specialized_charstr(test_charstr,
+                                        generalizeFirst=False), xpct_charstr)
+
+    def test_rmoveto_zero_mult(self):
+        test_charstr = '0 0 rmoveto '*3
+        xpct_charstr = '0 hmoveto'
+        self.assertEqual(get_specialized_charstr(test_charstr,
+                                        generalizeFirst=False), xpct_charstr)
+
+    def test_rmoveto_zero_width(self):
+        test_charstr = '100 0 0 rmoveto'
+        xpct_charstr = '100 0 hmoveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_rmoveto(self):
+        test_charstr = '.55 -.8 rmoveto'
+        xpct_charstr = '0.55 -0.8 rmoveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_rmoveto_mult(self):
+        test_charstr = '55 -8 rmoveto '*3
+        xpct_charstr = '165 -24 rmoveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_rmoveto_width(self):
+        test_charstr = '100.5 50 -5.8 rmoveto'
+        xpct_charstr = test_charstr
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+# rlineto
+    def test_rlineto_zero(self):
+        test_charstr = '0 0 rlineto'
+        xpct_charstr = ''
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_rlineto_zero_mult(self):
+        test_charstr = '0 0 rlineto '*3
+        xpct_charstr = ''
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_rlineto(self):
+        test_charstr = '.55 -.8 rlineto'
+        xpct_charstr = '0.55 -0.8 rlineto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_rlineto_mult(self):
+        test_charstr = '.55 -.8 rlineto '*3
+        xpct_charstr = '0.55 -0.8 0.55 -0.8 0.55 -0.8 rlineto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_hlineto(self):
+        test_charstr = '.67 0 rlineto'
+        xpct_charstr = '0.67 hlineto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_hlineto_zero_mult(self):
+        test_charstr = '62 0 rlineto '*3
+        xpct_charstr = '186 hlineto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_hlineto_mult(self):
+        test_charstr = '.67 0 rlineto 0 -6.0 rlineto .67 0 rlineto'
+        xpct_charstr = '0.67 -6.0 0.67 hlineto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_vlineto(self):
+        test_charstr = '0 -.24 rlineto'
+        xpct_charstr = '-0.24 vlineto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_vlineto_zero_mult(self):
+        test_charstr = '0 -24 rlineto '*3
+        xpct_charstr = '-72 vlineto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_vlineto_mult(self):
+        test_charstr = '0 -.24 rlineto +50 0 rlineto 0 30 rlineto -4 0 rlineto'
+        xpct_charstr = '-0.24 50 30 -4 vlineto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_0lineto_peephole(self):
+        test_charstr = '1 2 0 0 3 4 rlineto'
+        xpct_charstr = '1 2 3 4 rlineto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_hlineto_peephole(self):
+        test_charstr = '1 2 5 0 3 4 rlineto'
+        xpct_charstr = test_charstr
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_vlineto_peephole(self):
+        test_charstr = '1 2 0 5 3 4 rlineto'
+        xpct_charstr = test_charstr
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+# rrcurveto
+    def test_rrcurveto(self):
+        test_charstr = '-1 56 -2 57 -1 57 rrcurveto'
+        xpct_charstr = test_charstr
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_rrcurveto_mult(self):
+        test_charstr = '-30 8 -36 15 -37 22 rrcurveto 44 54 31 61 22 68 rrcurveto'
+        xpct_charstr = '-30 8 -36 15 -37 22 44 54 31 61 22 68 rrcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_rrcurveto_d3947b8(self):
+        test_charstr = '1 2 3 4 5 0 rrcurveto'
+        xpct_charstr = '2 1 3 4 5 hhcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_hhcurveto_4(self):
+        test_charstr = '10 0 30 0 10 0 rrcurveto'
+        xpct_charstr = '10 30 0 10 hhcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_hhcurveto_5(self):
+        test_charstr = '-38 40 -60 41 -91 0 rrcurveto'
+        xpct_charstr = '40 -38 -60 41 -91 hhcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_hhcurveto_mult_4_4(self):
+        test_charstr = '43 0 23 25 18 0 rrcurveto 29 0 56 42 -84 0 rrcurveto'
+        xpct_charstr = '43 23 25 18 29 56 42 -84 hhcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_hhcurveto_mult_5_4(self):
+        test_charstr = '23 43 25 18 29 0 rrcurveto 56 0 42 -84 79 0 rrcurveto'
+        xpct_charstr = '43 23 25 18 29 56 42 -84 79 hhcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_hhcurveto_mult_4_4_4(self):
+        test_charstr = '1 0 2 3 4 0 rrcurveto 5 0 6 7 8 0 rrcurveto 9 0 10 11 12 0 rrcurveto'
+        xpct_charstr = '1 2 3 4 5 6 7 8 9 10 11 12 hhcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_hhcurveto_mult_5_4_4(self):
+        test_charstr = '2 1 3 4 5 0 rrcurveto 6 0 7 8 9 0 rrcurveto 10 0 11 12 13 0 rrcurveto'
+        xpct_charstr = '1 2 3 4 5 6 7 8 9 10 11 12 13 hhcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_vvcurveto_4(self):
+        test_charstr = '0 61 6 52 0 68 rrcurveto'
+        xpct_charstr = '61 6 52 68 vvcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_vvcurveto_5(self):
+        test_charstr = '61 38 35 56 0 72 rrcurveto'
+        xpct_charstr = '61 38 35 56 72 vvcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_vvcurveto_mult_4_4(self):
+        test_charstr = '0 -84 -88 -30 0 -90 rrcurveto 0 -13 19 23 0 -11 rrcurveto'
+        xpct_charstr = '-84 -88 -30 -90 -13 19 23 -11 vvcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_vvcurveto_mult_5_4(self):
+        test_charstr = '43 12 17 32 0 65 rrcurveto 0 68 -6 52 0 61 rrcurveto'
+        xpct_charstr = '43 12 17 32 65 68 -6 52 61 vvcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_vvcurveto_mult_4_4_4(self):
+        test_charstr = '0 1 2 3 0 4 rrcurveto 0 5 6 7 0 8 rrcurveto 0 9 10 11 0 12 rrcurveto'
+        xpct_charstr = '1 2 3 4 5 6 7 8 9 10 11 12 vvcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_vvcurveto_mult_5_4_4(self):
+        test_charstr = '1 2 3 4 0 5 rrcurveto 0 6 7 8 0 9 rrcurveto 0 10 11 12 0 13 rrcurveto'
+        xpct_charstr = '1 2 3 4 5 6 7 8 9 10 11 12 13 vvcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_hvcurveto_4(self):
+        test_charstr = '1 0 2 3 0 4 rrcurveto'
+        xpct_charstr = '1 2 3 4 hvcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_hvcurveto_5(self):
+        test_charstr = '57 0 44 22 34 40 rrcurveto'
+        xpct_charstr = '57 44 22 40 34 hvcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_hvcurveto_4_4(self):
+        test_charstr = '65 0 33 -19 0 -45 rrcurveto 0 -45 -29 -25 -71 0 rrcurveto'
+        xpct_charstr = '65 33 -19 -45 -45 -29 -25 -71 hvcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_hvcurveto_4_5(self):
+        test_charstr = '97 0 69 41 0 86 rrcurveto 0 58 -36 34 -64 11 rrcurveto'
+        xpct_charstr = '97 69 41 86 58 -36 34 -64 11 hvcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_hvcurveto_4_4_4(self):
+        test_charstr = '1 0 2 3 0 4 rrcurveto 0 5 6 7 8 0 rrcurveto 9 0 10 11 0 12 rrcurveto'
+        xpct_charstr = '1 2 3 4 5 6 7 8 9 10 11 12 hvcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_hvcurveto_4_4_5(self):
+        test_charstr = '-124 0 -79 104 0 165 rrcurveto 0 163 82 102 124 0 rrcurveto 56 0 43 -25 35 -37 rrcurveto'
+        xpct_charstr = '-124 -79 104 165 163 82 102 124 56 43 -25 -37 35 hvcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_hvcurveto_4_4_4_4(self):
+        test_charstr = '32 0 25 22 0 32 rrcurveto 0 31 -25 22 -32 0 rrcurveto -32 0 -25 -22 0 -31 rrcurveto 0 -32 25 -22 32 0 rrcurveto'
+        xpct_charstr = '32 25 22 32 31 -25 22 -32 -32 -25 -22 -31 -32 25 -22 32 hvcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_hvcurveto_4_4_4_4_5(self):
+        test_charstr = '-170 0 -128 111 0 195 rrcurveto 0 234 172 151 178 0 rrcurveto 182 0 95 -118 0 -161 rrcurveto 0 -130 -71 -77 -63 0 rrcurveto -55 0 -19 38 20 79 rrcurveto'
+        xpct_charstr = '-170 -128 111 195 234 172 151 178 182 95 -118 -161 -130 -71 -77 -63 -55 -19 38 79 20 hvcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_vhcurveto_4(self):
+        test_charstr = '0 -57 43 -30 53 0 rrcurveto'
+        xpct_charstr = '-57 43 -30 53 vhcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_vhcurveto_5(self):
+        test_charstr = '0 41 -27 19 -46 11 rrcurveto'
+        xpct_charstr = '41 -27 19 -46 11 vhcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_vhcurveto_4_4(self):
+        test_charstr = '0 1 2 3 4 0 rrcurveto 5 0 6 7 0 8 rrcurveto'
+        xpct_charstr = '1 2 3 4 5 6 7 8 vhcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_vhcurveto_4_5(self):
+        test_charstr = '0 -64 -23 -25 -45 0 rrcurveto -30 0 -24 14 -19 33 rrcurveto'
+        xpct_charstr = '-64 -23 -25 -45 -30 -24 14 33 -19 vhcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_vhcurveto_4_4_4(self):
+        test_charstr = '0 1 2 3 4 0 rrcurveto 5 0 6 7 0 8 rrcurveto 0 9 10 11 12 0 rrcurveto'
+        xpct_charstr = '1 2 3 4 5 6 7 8 9 10 11 12 vhcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_vhcurveto_4_4_5(self):
+        test_charstr = '0 108 59 81 98 0 rrcurveto 99 0 59 -81 0 -108 rrcurveto 0 -100 -46 -66 -63 -47 rrcurveto'
+        xpct_charstr = '108 59 81 98 99 59 -81 -108 -100 -46 -66 -63 -47 vhcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_vhcurveto_4_4_4_5(self):
+        test_charstr = '0 60 -26 37 -43 0 rrcurveto -33 0 -28 -22 0 -36 rrcurveto 0 -37 27 -20 32 0 rrcurveto 3 0 4 0 3 1 rrcurveto'
+        xpct_charstr = '60 -26 37 -43 -33 -28 -22 -36 -37 27 -20 32 3 4 0 1 3 vhcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_rrcurveto_v0_0h_h0(self):
+        test_charstr = '0 10 1 2 0 0 0 0 1 2 0 1 0 1 3 4 0 0 rrcurveto'
+        xpct_charstr = '10 1 2 0 0 1 2 1 1 3 4 0 vhcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_rrcurveto_h0_0h_h0(self):
+        test_charstr = '10 0 1 2 0 0 0 0 1 2 0 1 0 1 3 4 0 0 rrcurveto'
+        xpct_charstr = '10 1 2 0 hhcurveto 0 1 2 1 1 3 4 0 hvcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_rrcurveto_00_0h_h0(self):
+        test_charstr = '0 0 1 2 0 0 0 0 1 2 0 1 0 1 3 4 0 0 rrcurveto'
+        xpct_charstr = '1 2 rlineto 0 1 2 1 1 3 4 0 hvcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_rrcurveto_r0_0h_h0(self):
+        test_charstr = '10 10 1 2 0 0 0 0 1 2 0 1 0 1 3 4 0 0 rrcurveto'
+        xpct_charstr = '10 10 1 2 0 0 1 2 1 1 3 4 0 vvcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_rrcurveto_v0_0v_v0(self):
+        test_charstr = '0 10 1 2 0 0 0 0 1 2 1 0 1 0 3 4 0 0 rrcurveto'
+        xpct_charstr = '10 1 2 0 vhcurveto 0 1 2 1 1 3 4 0 hhcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_rrcurveto_h0_0v_v0(self):
+        test_charstr = '10 0 1 2 0 0 0 0 1 2 1 0 1 0 3 4 0 0 rrcurveto'
+        xpct_charstr = '10 1 2 0 0 1 2 1 1 3 4 0 hhcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_rrcurveto_00_0v_v0(self):
+        test_charstr = '0 0 1 2 0 0 0 0 1 2 1 0 1 0 3 4 0 0 rrcurveto'
+        xpct_charstr = '1 2 rlineto 0 1 2 1 1 3 4 0 hhcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_rrcurveto_r0_0v_v0(self):
+        test_charstr = '10 10 1 2 0 0 0 0 1 2 1 0 1 0 3 4 0 0 rrcurveto'
+        xpct_charstr = '10 10 1 2 0 0 1 2 1 1 3 4 0 hhcurveto'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_hhcurveto_peephole(self):
+        test_charstr = '1 2 3 4 5 6 1 2 3 4 5 0 1 2 3 4 5 6 rrcurveto'
+        xpct_charstr = test_charstr
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_vvcurveto_peephole(self):
+        test_charstr = '1 2 3 4 5 6 1 2 3 4 0 6 1 2 3 4 5 6 rrcurveto'
+        xpct_charstr = test_charstr
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_hvcurveto_peephole(self):
+        test_charstr = '1 2 3 4 5 6 1 0 3 4 5 6 1 2 3 4 5 6 rrcurveto'
+        xpct_charstr = test_charstr
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_vhcurveto_peephole(self):
+        test_charstr = '1 2 3 4 5 6 0 2 3 4 5 6 1 2 3 4 5 6 rrcurveto'
+        xpct_charstr = test_charstr
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_rcurveline_6_2(self):
+        test_charstr = '21 -76 21 -72 24 -73 rrcurveto 31 -100 rlineto'
+        xpct_charstr = '21 -76 21 -72 24 -73 31 -100 rcurveline'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_rcurveline_6_6_2(self):
+        test_charstr = '-73 80 -80 121 -49 96 rrcurveto 60 65 55 41 54 17 rrcurveto -8 78 rlineto'
+        xpct_charstr = '-73 80 -80 121 -49 96 60 65 55 41 54 17 -8 78 rcurveline'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_rcurveline_6_6_6_2(self):
+        test_charstr = '1 64 10 51 29 39 rrcurveto 15 21 15 20 15 18 rrcurveto 47 -89 63 -98 52 -59 rrcurveto 91 8 rlineto'
+        xpct_charstr = '1 64 10 51 29 39 15 21 15 20 15 18 47 -89 63 -98 52 -59 91 8 rcurveline'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_rlinecurve_2_6(self):
+        test_charstr = '21 -76 rlineto 21 -72 24 -73 31 -100 rrcurveto'
+        xpct_charstr = '21 -76 21 -72 24 -73 31 -100 rlinecurve'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_rlinecurve_2_2_6(self):
+        test_charstr = '-73 80 rlineto -80 121 rlineto -49 96 60 65 55 41 rrcurveto'
+        xpct_charstr = '-73 80 -80 121 -49 96 60 65 55 41 rlinecurve'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+    def test_rlinecurve_2_2_2_6(self):
+        test_charstr = '1 64 rlineto 10 51 rlineto 29 39 rlineto 15 21 15 20 15 18 rrcurveto'
+        xpct_charstr = '1 64 10 51 29 39 15 21 15 20 15 18 rlinecurve'
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+# maxstack CFF=48
+    def test_maxstack(self):
+        operands = '1 2 3 4 5 6 '
+        operator = 'rrcurveto '
+        test_charstr = (operands + operator)*9
+        xpct_charstr = (operands + operator + operands*8 + operator).rstrip()
+        self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/designspaceLib/data/test.designspace b/Tests/designspaceLib/data/test.designspace
new file mode 100644
index 0000000..cf7056b
--- /dev/null
+++ b/Tests/designspaceLib/data/test.designspace
@@ -0,0 +1,107 @@
+<?xml version='1.0' encoding='utf-8'?>
+<designspace format="4.0">
+    <axes>
+        <axis default="0" maximum="1000" minimum="0" name="weight" tag="wght">
+            <labelname xml:lang="en">Wéíght</labelname>
+            <labelname xml:lang="fa-IR">قطر</labelname>
+        </axis>
+        <axis default="20" hidden="1" maximum="1000" minimum="0" name="width" tag="wdth">
+            <labelname xml:lang="fr">Chasse</labelname>
+            <map input="0" output="10" />
+            <map input="401" output="66" />
+            <map input="1000" output="990" />
+        </axis>
+    </axes>
+    <rules>
+        <rule name="named.rule.1">
+            <conditionset>
+                <condition maximum="1" minimum="0" name="axisName_a" />
+                <condition maximum="3" minimum="2" name="axisName_b" />
+            </conditionset>
+            <sub name="a" with="a.alt" />
+        </rule>
+    </rules>
+    <sources>
+        <source familyname="MasterFamilyName" filename="masters/masterTest1.ufo" name="master.ufo1" stylename="MasterStyleNameOne">
+            <lib copy="1" />
+            <features copy="1" />
+            <info copy="1" />
+            <glyph mute="1" name="A" />
+            <glyph mute="1" name="Z" />
+            <location>
+                <dimension name="weight" xvalue="0" />
+                <dimension name="width" xvalue="20" />
+            </location>
+        </source>
+        <source familyname="MasterFamilyName" filename="masters/masterTest2.ufo" name="master.ufo2" stylename="MasterStyleNameTwo">
+            <kerning mute="1" />
+            <location>
+                <dimension name="weight" xvalue="1000" />
+                <dimension name="width" xvalue="20" />
+            </location>
+        </source>
+        <source familyname="MasterFamilyName" filename="masters/masterTest2.ufo" layer="supports" name="master.ufo2" stylename="Supports">
+            <location>
+                <dimension name="weight" xvalue="1000" />
+                <dimension name="width" xvalue="20" />
+            </location>
+        </source>
+    </sources>
+    <instances>
+        <instance familyname="InstanceFamilyName" filename="instances/instanceTest1.ufo" name="instance.ufo1" postscriptfontname="InstancePostscriptName" stylemapfamilyname="InstanceStyleMapFamilyName" stylemapstylename="InstanceStyleMapStyleName" stylename="InstanceStyleName">
+            <location>
+                <dimension name="weight" xvalue="500" />
+                <dimension name="width" xvalue="20" />
+            </location>
+            <glyphs>
+                <glyph mute="1" name="arrow" unicode="0x123 0x124 0x125" />
+            </glyphs>
+            <kerning />
+            <info />
+            <lib>
+                <dict>
+                    <key>com.coolDesignspaceApp.specimenText</key>
+                    <string>Hamburgerwhatever</string>
+                </dict>
+            </lib>
+        </instance>
+        <instance familyname="InstanceFamilyName" filename="instances/instanceTest2.ufo" name="instance.ufo2" postscriptfontname="InstancePostscriptName" stylemapfamilyname="InstanceStyleMapFamilyName" stylemapstylename="InstanceStyleMapStyleName" stylename="InstanceStyleName">
+            <location>
+                <dimension name="weight" xvalue="500" />
+                <dimension name="width" xvalue="400" yvalue="300" />
+            </location>
+            <glyphs>
+                <glyph name="arrow" unicode="0x65 0xc9 0x12d">
+                    <location>
+                        <dimension name="weight" xvalue="120" />
+                        <dimension name="width" xvalue="100" />
+                    </location>
+                    <note>A note about this glyph</note>
+                    <masters>
+                        <master glyphname="BB" source="master.ufo1">
+                            <location>
+                                <dimension name="weight" xvalue="20" />
+                                <dimension name="width" xvalue="20" />
+                            </location>
+                        </master>
+                        <master glyphname="CC" source="master.ufo2">
+                            <location>
+                                <dimension name="weight" xvalue="900" />
+                                <dimension name="width" xvalue="900" />
+                            </location>
+                        </master>
+                    </masters>
+                </glyph>
+                <glyph name="arrow2" />
+            </glyphs>
+            <kerning />
+            <info />
+        </instance>
+    </instances>
+    <lib>
+        <dict>
+            <key>com.coolDesignspaceApp.previewSize</key>
+            <integer>30</integer>
+        </dict>
+    </lib>
+</designspace>
diff --git a/Tests/designspaceLib/designspace_test.py b/Tests/designspaceLib/designspace_test.py
new file mode 100644
index 0000000..1d9b841
--- /dev/null
+++ b/Tests/designspaceLib/designspace_test.py
@@ -0,0 +1,791 @@
+# coding=utf-8
+
+from __future__ import (print_function, division, absolute_import,
+                        unicode_literals)
+
+import os
+import pytest
+import warnings
+
+from fontTools.misc.py23 import open
+from fontTools.designspaceLib import (
+    DesignSpaceDocument, SourceDescriptor, AxisDescriptor, RuleDescriptor,
+    InstanceDescriptor, evaluateRule, processRules, posix, DesignSpaceDocumentError)
+
+def _axesAsDict(axes):
+    """
+        Make the axis data we have available in
+    """
+    axesDict = {}
+    for axisDescriptor in axes:
+        d = {
+            'name': axisDescriptor.name,
+            'tag': axisDescriptor.tag,
+            'minimum': axisDescriptor.minimum,
+            'maximum': axisDescriptor.maximum,
+            'default': axisDescriptor.default,
+            'map': axisDescriptor.map,
+        }
+        axesDict[axisDescriptor.name] = d
+    return axesDict
+
+
+def assert_equals_test_file(path, test_filename):
+    with open(path) as fp:
+        actual = fp.read()
+
+    test_path = os.path.join(os.path.dirname(__file__), test_filename)
+    with open(test_path) as fp:
+        expected = fp.read()
+
+    assert actual == expected
+
+
+def test_fill_document(tmpdir):
+    tmpdir = str(tmpdir)
+    testDocPath = os.path.join(tmpdir, "test.designspace")
+    masterPath1 = os.path.join(tmpdir, "masters", "masterTest1.ufo")
+    masterPath2 = os.path.join(tmpdir, "masters", "masterTest2.ufo")
+    instancePath1 = os.path.join(tmpdir, "instances", "instanceTest1.ufo")
+    instancePath2 = os.path.join(tmpdir, "instances", "instanceTest2.ufo")
+    doc = DesignSpaceDocument()
+
+    # write some axes
+    a1 = AxisDescriptor()
+    a1.minimum = 0
+    a1.maximum = 1000
+    a1.default = 0
+    a1.name = "weight"
+    a1.tag = "wght"
+    # note: just to test the element language, not an actual label name recommendations.
+    a1.labelNames[u'fa-IR'] = u"قطر"
+    a1.labelNames[u'en'] = u"Wéíght"
+    doc.addAxis(a1)
+    a2 = AxisDescriptor()
+    a2.minimum = 0
+    a2.maximum = 1000
+    a2.default = 20
+    a2.name = "width"
+    a2.tag = "wdth"
+    a2.map = [(0.0, 10.0), (401.0, 66.0), (1000.0, 990.0)]
+    a2.hidden = True
+    a2.labelNames[u'fr'] = u"Chasse"
+    doc.addAxis(a2)
+
+    # add master 1
+    s1 = SourceDescriptor()
+    s1.filename = os.path.relpath(masterPath1, os.path.dirname(testDocPath))
+    assert s1.font is None
+    s1.name = "master.ufo1"
+    s1.copyLib = True
+    s1.copyInfo = True
+    s1.copyFeatures = True
+    s1.location = dict(weight=0)
+    s1.familyName = "MasterFamilyName"
+    s1.styleName = "MasterStyleNameOne"
+    s1.mutedGlyphNames.append("A")
+    s1.mutedGlyphNames.append("Z")
+    doc.addSource(s1)
+    # add master 2
+    s2 = SourceDescriptor()
+    s2.filename = os.path.relpath(masterPath2, os.path.dirname(testDocPath))
+    s2.name = "master.ufo2"
+    s2.copyLib = False
+    s2.copyInfo = False
+    s2.copyFeatures = False
+    s2.muteKerning = True
+    s2.location = dict(weight=1000)
+    s2.familyName = "MasterFamilyName"
+    s2.styleName = "MasterStyleNameTwo"
+    doc.addSource(s2)
+    # add master 3 from a different layer
+    s3 = SourceDescriptor()
+    s3.filename = os.path.relpath(masterPath2, os.path.dirname(testDocPath))
+    s3.name = "master.ufo2"
+    s3.copyLib = False
+    s3.copyInfo = False
+    s3.copyFeatures = False
+    s3.muteKerning = False
+    s3.layerName = "supports"
+    s3.location = dict(weight=1000)
+    s3.familyName = "MasterFamilyName"
+    s3.styleName = "Supports"
+    doc.addSource(s3)
+    # add instance 1
+    i1 = InstanceDescriptor()
+    i1.filename = os.path.relpath(instancePath1, os.path.dirname(testDocPath))
+    i1.familyName = "InstanceFamilyName"
+    i1.styleName = "InstanceStyleName"
+    i1.name = "instance.ufo1"
+    i1.location = dict(weight=500, spooky=666)  # this adds a dimension that is not defined.
+    i1.postScriptFontName = "InstancePostscriptName"
+    i1.styleMapFamilyName = "InstanceStyleMapFamilyName"
+    i1.styleMapStyleName = "InstanceStyleMapStyleName"
+    glyphData = dict(name="arrow", mute=True, unicodes=[0x123, 0x124, 0x125])
+    i1.glyphs['arrow'] = glyphData
+    i1.lib['com.coolDesignspaceApp.specimenText'] = "Hamburgerwhatever"
+    doc.addInstance(i1)
+    # add instance 2
+    i2 = InstanceDescriptor()
+    i2.filename = os.path.relpath(instancePath2, os.path.dirname(testDocPath))
+    i2.familyName = "InstanceFamilyName"
+    i2.styleName = "InstanceStyleName"
+    i2.name = "instance.ufo2"
+    # anisotropic location
+    i2.location = dict(weight=500, width=(400,300))
+    i2.postScriptFontName = "InstancePostscriptName"
+    i2.styleMapFamilyName = "InstanceStyleMapFamilyName"
+    i2.styleMapStyleName = "InstanceStyleMapStyleName"
+    glyphMasters = [dict(font="master.ufo1", glyphName="BB", location=dict(width=20,weight=20)), dict(font="master.ufo2", glyphName="CC", location=dict(width=900,weight=900))]
+    glyphData = dict(name="arrow", unicodes=[101, 201, 301])
+    glyphData['masters'] = glyphMasters
+    glyphData['note'] = "A note about this glyph"
+    glyphData['instanceLocation'] = dict(width=100, weight=120)
+    i2.glyphs['arrow'] = glyphData
+    i2.glyphs['arrow2'] = dict(mute=False)
+    doc.addInstance(i2)
+
+    doc.filename = "suggestedFileName.designspace"
+    doc.lib['com.coolDesignspaceApp.previewSize'] = 30
+
+    # write some rules
+    r1 = RuleDescriptor()
+    r1.name = "named.rule.1"
+    r1.conditionSets.append([
+        dict(name='axisName_a', minimum=0, maximum=1),
+        dict(name='axisName_b', minimum=2, maximum=3)
+    ])
+    r1.subs.append(("a", "a.alt"))
+    doc.addRule(r1)
+    # write the document
+    doc.write(testDocPath)
+    assert os.path.exists(testDocPath)
+    assert_equals_test_file(testDocPath, 'data/test.designspace')
+    # import it again
+    new = DesignSpaceDocument()
+    new.read(testDocPath)
+
+    assert new.default.location == {'width': 20.0, 'weight': 0.0}
+    assert new.filename == 'test.designspace'
+    assert new.lib == doc.lib
+    assert new.instances[0].lib == doc.instances[0].lib
+
+    # test roundtrip for the axis attributes and data
+    axes = {}
+    for axis in doc.axes:
+        if axis.tag not in axes:
+            axes[axis.tag] = []
+        axes[axis.tag].append(axis.serialize())
+    for axis in new.axes:
+        if axis.tag[0] == "_":
+            continue
+        if axis.tag not in axes:
+            axes[axis.tag] = []
+        axes[axis.tag].append(axis.serialize())
+    for v in axes.values():
+        a, b = v
+        assert a == b
+
+
+def test_unicodes(tmpdir):
+    tmpdir = str(tmpdir)
+    testDocPath = os.path.join(tmpdir, "testUnicodes.designspace")
+    testDocPath2 = os.path.join(tmpdir, "testUnicodes_roundtrip.designspace")
+    masterPath1 = os.path.join(tmpdir, "masters", "masterTest1.ufo")
+    masterPath2 = os.path.join(tmpdir, "masters", "masterTest2.ufo")
+    instancePath1 = os.path.join(tmpdir, "instances", "instanceTest1.ufo")
+    instancePath2 = os.path.join(tmpdir, "instances", "instanceTest2.ufo")
+    doc = DesignSpaceDocument()
+    # add master 1
+    s1 = SourceDescriptor()
+    s1.filename = os.path.relpath(masterPath1, os.path.dirname(testDocPath))
+    s1.name = "master.ufo1"
+    s1.copyInfo = True
+    s1.location = dict(weight=0)
+    doc.addSource(s1)
+    # add master 2
+    s2 = SourceDescriptor()
+    s2.filename = os.path.relpath(masterPath2, os.path.dirname(testDocPath))
+    s2.name = "master.ufo2"
+    s2.location = dict(weight=1000)
+    doc.addSource(s2)
+    # add instance 1
+    i1 = InstanceDescriptor()
+    i1.filename = os.path.relpath(instancePath1, os.path.dirname(testDocPath))
+    i1.name = "instance.ufo1"
+    i1.location = dict(weight=500)
+    glyphData = dict(name="arrow", mute=True, unicodes=[100, 200, 300])
+    i1.glyphs['arrow'] = glyphData
+    doc.addInstance(i1)
+    # now we have sources and instances, but no axes yet.
+    doc.axes = []   # clear the axes
+    # write some axes
+    a1 = AxisDescriptor()
+    a1.minimum = 0
+    a1.maximum = 1000
+    a1.default = 0
+    a1.name = "weight"
+    a1.tag = "wght"
+    doc.addAxis(a1)
+    # write the document
+    doc.write(testDocPath)
+    assert os.path.exists(testDocPath)
+    # import it again
+    new = DesignSpaceDocument()
+    new.read(testDocPath)
+    new.write(testDocPath2)
+    # compare the file contents
+    f1 = open(testDocPath, 'r', encoding='utf-8')
+    t1 = f1.read()
+    f1.close()
+    f2 = open(testDocPath2, 'r', encoding='utf-8')
+    t2 = f2.read()
+    f2.close()
+    assert t1 == t2
+    # check the unicode values read from the document
+    assert new.instances[0].glyphs['arrow']['unicodes'] == [100,200,300]
+
+
+def test_localisedNames(tmpdir):
+    tmpdir = str(tmpdir)
+    testDocPath = os.path.join(tmpdir, "testLocalisedNames.designspace")
+    testDocPath2 = os.path.join(tmpdir, "testLocalisedNames_roundtrip.designspace")
+    masterPath1 = os.path.join(tmpdir, "masters", "masterTest1.ufo")
+    masterPath2 = os.path.join(tmpdir, "masters", "masterTest2.ufo")
+    instancePath1 = os.path.join(tmpdir, "instances", "instanceTest1.ufo")
+    instancePath2 = os.path.join(tmpdir, "instances", "instanceTest2.ufo")
+    doc = DesignSpaceDocument()
+    # add master 1
+    s1 = SourceDescriptor()
+    s1.filename = os.path.relpath(masterPath1, os.path.dirname(testDocPath))
+    s1.name = "master.ufo1"
+    s1.copyInfo = True
+    s1.location = dict(weight=0)
+    doc.addSource(s1)
+    # add master 2
+    s2 = SourceDescriptor()
+    s2.filename = os.path.relpath(masterPath2, os.path.dirname(testDocPath))
+    s2.name = "master.ufo2"
+    s2.location = dict(weight=1000)
+    doc.addSource(s2)
+    # add instance 1
+    i1 = InstanceDescriptor()
+    i1.filename = os.path.relpath(instancePath1, os.path.dirname(testDocPath))
+    i1.familyName = "Montserrat"
+    i1.styleName = "SemiBold"
+    i1.styleMapFamilyName = "Montserrat SemiBold"
+    i1.styleMapStyleName = "Regular"
+    i1.setFamilyName("Montserrat", "fr")
+    i1.setFamilyName(u"モンセラート", "ja")
+    i1.setStyleName("Demigras", "fr")
+    i1.setStyleName(u"半ば", "ja")
+    i1.setStyleMapStyleName(u"Standard", "de")
+    i1.setStyleMapFamilyName("Montserrat Halbfett", "de")
+    i1.setStyleMapFamilyName(u"モンセラート SemiBold", "ja")
+    i1.name = "instance.ufo1"
+    i1.location = dict(weight=500, spooky=666)  # this adds a dimension that is not defined.
+    i1.postScriptFontName = "InstancePostscriptName"
+    glyphData = dict(name="arrow", mute=True, unicodes=[0x123])
+    i1.glyphs['arrow'] = glyphData
+    doc.addInstance(i1)
+    # now we have sources and instances, but no axes yet.
+    doc.axes = []   # clear the axes
+    # write some axes
+    a1 = AxisDescriptor()
+    a1.minimum = 0
+    a1.maximum = 1000
+    a1.default = 0
+    a1.name = "weight"
+    a1.tag = "wght"
+    # note: just to test the element language, not an actual label name recommendations.
+    a1.labelNames[u'fa-IR'] = u"قطر"
+    a1.labelNames[u'en'] = u"Wéíght"
+    doc.addAxis(a1)
+    a2 = AxisDescriptor()
+    a2.minimum = 0
+    a2.maximum = 1000
+    a2.default = 0
+    a2.name = "width"
+    a2.tag = "wdth"
+    a2.map = [(0.0, 10.0), (401.0, 66.0), (1000.0, 990.0)]
+    a2.labelNames[u'fr'] = u"Poids"
+    doc.addAxis(a2)
+    # add an axis that is not part of any location to see if that works
+    a3 = AxisDescriptor()
+    a3.minimum = 333
+    a3.maximum = 666
+    a3.default = 444
+    a3.name = "spooky"
+    a3.tag = "spok"
+    a3.map = [(0.0, 10.0), (401.0, 66.0), (1000.0, 990.0)]
+    #doc.addAxis(a3)    # uncomment this line to test the effects of default axes values
+    # write some rules
+    r1 = RuleDescriptor()
+    r1.name = "named.rule.1"
+    r1.conditionSets.append([
+        dict(name='weight', minimum=200, maximum=500),
+        dict(name='width', minimum=0, maximum=150)
+    ])
+    r1.subs.append(("a", "a.alt"))
+    doc.addRule(r1)
+    # write the document
+    doc.write(testDocPath)
+    assert os.path.exists(testDocPath)
+    # import it again
+    new = DesignSpaceDocument()
+    new.read(testDocPath)
+    new.write(testDocPath2)
+    f1 = open(testDocPath, 'r', encoding='utf-8')
+    t1 = f1.read()
+    f1.close()
+    f2 = open(testDocPath2, 'r', encoding='utf-8')
+    t2 = f2.read()
+    f2.close()
+    assert t1 == t2
+
+
+def test_handleNoAxes(tmpdir):
+    tmpdir = str(tmpdir)
+    # test what happens if the designspacedocument has no axes element.
+    testDocPath = os.path.join(tmpdir, "testNoAxes_source.designspace")
+    testDocPath2 = os.path.join(tmpdir, "testNoAxes_recontructed.designspace")
+    masterPath1 = os.path.join(tmpdir, "masters", "masterTest1.ufo")
+    masterPath2 = os.path.join(tmpdir, "masters", "masterTest2.ufo")
+    instancePath1 = os.path.join(tmpdir, "instances", "instanceTest1.ufo")
+    instancePath2 = os.path.join(tmpdir, "instances", "instanceTest2.ufo")
+
+    # Case 1: No axes element in the document, but there are sources and instances
+    doc = DesignSpaceDocument()
+
+    for name, value in [('One', 1),('Two', 2),('Three', 3)]:
+        a = AxisDescriptor()
+        a.minimum = 0
+        a.maximum = 1000
+        a.default = 0
+        a.name = "axisName%s" % (name)
+        a.tag = "ax_%d" % (value)
+        doc.addAxis(a)
+
+    # add master 1
+    s1 = SourceDescriptor()
+    s1.filename = os.path.relpath(masterPath1, os.path.dirname(testDocPath))
+    s1.name = "master.ufo1"
+    s1.copyLib = True
+    s1.copyInfo = True
+    s1.copyFeatures = True
+    s1.location = dict(axisNameOne=-1000, axisNameTwo=0, axisNameThree=1000)
+    s1.familyName = "MasterFamilyName"
+    s1.styleName = "MasterStyleNameOne"
+    doc.addSource(s1)
+
+    # add master 2
+    s2 = SourceDescriptor()
+    s2.filename = os.path.relpath(masterPath2, os.path.dirname(testDocPath))
+    s2.name = "master.ufo1"
+    s2.copyLib = False
+    s2.copyInfo = False
+    s2.copyFeatures = False
+    s2.location = dict(axisNameOne=1000, axisNameTwo=1000, axisNameThree=0)
+    s2.familyName = "MasterFamilyName"
+    s2.styleName = "MasterStyleNameTwo"
+    doc.addSource(s2)
+
+    # add instance 1
+    i1 = InstanceDescriptor()
+    i1.filename = os.path.relpath(instancePath1, os.path.dirname(testDocPath))
+    i1.familyName = "InstanceFamilyName"
+    i1.styleName = "InstanceStyleName"
+    i1.name = "instance.ufo1"
+    i1.location = dict(axisNameOne=(-1000,500), axisNameTwo=100)
+    i1.postScriptFontName = "InstancePostscriptName"
+    i1.styleMapFamilyName = "InstanceStyleMapFamilyName"
+    i1.styleMapStyleName = "InstanceStyleMapStyleName"
+    doc.addInstance(i1)
+
+    doc.write(testDocPath)
+    verify = DesignSpaceDocument()
+    verify.read(testDocPath)
+    verify.write(testDocPath2)
+
+def test_pathNameResolve(tmpdir):
+    tmpdir = str(tmpdir)
+    # test how descriptor.path and descriptor.filename are resolved
+    testDocPath1 = os.path.join(tmpdir, "testPathName_case1.designspace")
+    testDocPath2 = os.path.join(tmpdir, "testPathName_case2.designspace")
+    testDocPath3 = os.path.join(tmpdir, "testPathName_case3.designspace")
+    testDocPath4 = os.path.join(tmpdir, "testPathName_case4.designspace")
+    testDocPath5 = os.path.join(tmpdir, "testPathName_case5.designspace")
+    testDocPath6 = os.path.join(tmpdir, "testPathName_case6.designspace")
+    masterPath1 = os.path.join(tmpdir, "masters", "masterTest1.ufo")
+    masterPath2 = os.path.join(tmpdir, "masters", "masterTest2.ufo")
+    instancePath1 = os.path.join(tmpdir, "instances", "instanceTest1.ufo")
+    instancePath2 = os.path.join(tmpdir, "instances", "instanceTest2.ufo")
+
+    a1 = AxisDescriptor()
+    a1.tag = "TAGA"
+    a1.name = "axisName_a"
+    a1.minimum = 0
+    a1.maximum = 1000
+    a1.default = 0
+
+    # Case 1: filename and path are both empty. Nothing to calculate, nothing to put in the file.
+    doc = DesignSpaceDocument()
+    doc.addAxis(a1)
+    s = SourceDescriptor()
+    s.filename = None
+    s.path = None
+    s.copyInfo = True
+    s.location = dict(weight=0)
+    s.familyName = "MasterFamilyName"
+    s.styleName = "MasterStyleNameOne"
+    doc.addSource(s)
+    doc.write(testDocPath1)
+    verify = DesignSpaceDocument()
+    verify.read(testDocPath1)
+    assert verify.sources[0].filename == None
+    assert verify.sources[0].path == None
+
+    # Case 2: filename is empty, path points somewhere: calculate a new filename.
+    doc = DesignSpaceDocument()
+    doc.addAxis(a1)
+    s = SourceDescriptor()
+    s.filename = None
+    s.path = masterPath1
+    s.copyInfo = True
+    s.location = dict(weight=0)
+    s.familyName = "MasterFamilyName"
+    s.styleName = "MasterStyleNameOne"
+    doc.addSource(s)
+    doc.write(testDocPath2)
+    verify = DesignSpaceDocument()
+    verify.read(testDocPath2)
+    assert verify.sources[0].filename == "masters/masterTest1.ufo"
+    assert verify.sources[0].path == posix(masterPath1)
+
+    # Case 3: the filename is set, the path is None.
+    doc = DesignSpaceDocument()
+    doc.addAxis(a1)
+    s = SourceDescriptor()
+    s.filename = "../somewhere/over/the/rainbow.ufo"
+    s.path = None
+    s.copyInfo = True
+    s.location = dict(weight=0)
+    s.familyName = "MasterFamilyName"
+    s.styleName = "MasterStyleNameOne"
+    doc.addSource(s)
+    doc.write(testDocPath3)
+    verify = DesignSpaceDocument()
+    verify.read(testDocPath3)
+    assert verify.sources[0].filename == "../somewhere/over/the/rainbow.ufo"
+    # make the absolute path for filename so we can see if it matches the path
+    p = os.path.abspath(os.path.join(os.path.dirname(testDocPath3), verify.sources[0].filename))
+    assert verify.sources[0].path == posix(p)
+
+    # Case 4: the filename points to one file, the path points to another. The path takes precedence.
+    doc = DesignSpaceDocument()
+    doc.addAxis(a1)
+    s = SourceDescriptor()
+    s.filename = "../somewhere/over/the/rainbow.ufo"
+    s.path = masterPath1
+    s.copyInfo = True
+    s.location = dict(weight=0)
+    s.familyName = "MasterFamilyName"
+    s.styleName = "MasterStyleNameOne"
+    doc.addSource(s)
+    doc.write(testDocPath4)
+    verify = DesignSpaceDocument()
+    verify.read(testDocPath4)
+    assert verify.sources[0].filename == "masters/masterTest1.ufo"
+
+    # Case 5: the filename is None, path has a value, update the filename
+    doc = DesignSpaceDocument()
+    doc.addAxis(a1)
+    s = SourceDescriptor()
+    s.filename = None
+    s.path = masterPath1
+    s.copyInfo = True
+    s.location = dict(weight=0)
+    s.familyName = "MasterFamilyName"
+    s.styleName = "MasterStyleNameOne"
+    doc.addSource(s)
+    doc.write(testDocPath5) # so that the document has a path
+    doc.updateFilenameFromPath()
+    assert doc.sources[0].filename == "masters/masterTest1.ufo"
+
+    # Case 6: the filename has a value, path has a value, update the filenames with force
+    doc = DesignSpaceDocument()
+    doc.addAxis(a1)
+    s = SourceDescriptor()
+    s.filename = "../somewhere/over/the/rainbow.ufo"
+    s.path = masterPath1
+    s.copyInfo = True
+    s.location = dict(weight=0)
+    s.familyName = "MasterFamilyName"
+    s.styleName = "MasterStyleNameOne"
+    doc.write(testDocPath5) # so that the document has a path
+    doc.addSource(s)
+    assert doc.sources[0].filename == "../somewhere/over/the/rainbow.ufo"
+    doc.updateFilenameFromPath(force=True)
+    assert doc.sources[0].filename == "masters/masterTest1.ufo"
+
+
+def test_normalise1():
+    # normalisation of anisotropic locations, clipping
+    doc = DesignSpaceDocument()
+    # write some axes
+    a1 = AxisDescriptor()
+    a1.minimum = -1000
+    a1.maximum = 1000
+    a1.default = 0
+    a1.name = "axisName_a"
+    a1.tag = "TAGA"
+    doc.addAxis(a1)
+    assert doc.normalizeLocation(dict(axisName_a=0)) == {'axisName_a': 0.0}
+    assert doc.normalizeLocation(dict(axisName_a=1000)) == {'axisName_a': 1.0}
+    # clipping beyond max values:
+    assert doc.normalizeLocation(dict(axisName_a=1001)) == {'axisName_a': 1.0}
+    assert doc.normalizeLocation(dict(axisName_a=500)) == {'axisName_a': 0.5}
+    assert doc.normalizeLocation(dict(axisName_a=-1000)) == {'axisName_a': -1.0}
+    assert doc.normalizeLocation(dict(axisName_a=-1001)) == {'axisName_a': -1.0}
+    # anisotropic coordinates normalise to isotropic
+    assert doc.normalizeLocation(dict(axisName_a=(1000, -1000))) == {'axisName_a': 1.0}
+    doc.normalize()
+    r = []
+    for axis in doc.axes:
+        r.append((axis.name, axis.minimum, axis.default, axis.maximum))
+    r.sort()
+    assert r == [('axisName_a', -1.0, 0.0, 1.0)]
+
+def test_normalise2():
+    # normalisation with minimum > 0
+    doc = DesignSpaceDocument()
+    # write some axes
+    a2 = AxisDescriptor()
+    a2.minimum = 100
+    a2.maximum = 1000
+    a2.default = 100
+    a2.name = "axisName_b"
+    doc.addAxis(a2)
+    assert doc.normalizeLocation(dict(axisName_b=0)) == {'axisName_b': 0.0}
+    assert doc.normalizeLocation(dict(axisName_b=1000)) == {'axisName_b': 1.0}
+    # clipping beyond max values:
+    assert doc.normalizeLocation(dict(axisName_b=1001)) == {'axisName_b': 1.0}
+    assert doc.normalizeLocation(dict(axisName_b=500)) == {'axisName_b': 0.4444444444444444}
+    assert doc.normalizeLocation(dict(axisName_b=-1000)) == {'axisName_b': 0.0}
+    assert doc.normalizeLocation(dict(axisName_b=-1001)) == {'axisName_b': 0.0}
+    # anisotropic coordinates normalise to isotropic
+    assert doc.normalizeLocation(dict(axisName_b=(1000,-1000))) == {'axisName_b': 1.0}
+    assert doc.normalizeLocation(dict(axisName_b=1001)) == {'axisName_b': 1.0}
+    doc.normalize()
+    r = []
+    for axis in doc.axes:
+        r.append((axis.name, axis.minimum, axis.default, axis.maximum))
+    r.sort()
+    assert r == [('axisName_b', 0.0, 0.0, 1.0)]
+
+def test_normalise3():
+    # normalisation of negative values, with default == maximum
+    doc = DesignSpaceDocument()
+    # write some axes
+    a3 = AxisDescriptor()
+    a3.minimum = -1000
+    a3.maximum = 0
+    a3.default = 0
+    a3.name = "ccc"
+    doc.addAxis(a3)
+    assert doc.normalizeLocation(dict(ccc=0)) == {'ccc': 0.0}
+    assert doc.normalizeLocation(dict(ccc=1)) == {'ccc': 0.0}
+    assert doc.normalizeLocation(dict(ccc=-1000)) == {'ccc': -1.0}
+    assert doc.normalizeLocation(dict(ccc=-1001)) == {'ccc': -1.0}
+    doc.normalize()
+    r = []
+    for axis in doc.axes:
+        r.append((axis.name, axis.minimum, axis.default, axis.maximum))
+    r.sort()
+    assert r == [('ccc', -1.0, 0.0, 0.0)]
+
+def test_normalise4():
+    # normalisation with a map
+    doc = DesignSpaceDocument()
+    # write some axes
+    a4 = AxisDescriptor()
+    a4.minimum = 0
+    a4.maximum = 1000
+    a4.default = 0
+    a4.name = "ddd"
+    a4.map = [(0,100), (300, 500), (600, 500), (1000,900)]
+    doc.addAxis(a4)
+    doc.normalize()
+    r = []
+    for axis in doc.axes:
+        r.append((axis.name, axis.map))
+    r.sort()
+    assert r == [('ddd', [(0, 0.1), (300, 0.5), (600, 0.5), (1000, 0.9)])]
+
+def test_axisMapping():
+    # note: because designspance lib does not do any actual
+    # processing of the mapping data, we can only check if there data is there.
+    doc = DesignSpaceDocument()
+    # write some axes
+    a4 = AxisDescriptor()
+    a4.minimum = 0
+    a4.maximum = 1000
+    a4.default = 0
+    a4.name = "ddd"
+    a4.map = [(0,100), (300, 500), (600, 500), (1000,900)]
+    doc.addAxis(a4)
+    doc.normalize()
+    r = []
+    for axis in doc.axes:
+        r.append((axis.name, axis.map))
+    r.sort()
+    assert r == [('ddd', [(0, 0.1), (300, 0.5), (600, 0.5), (1000, 0.9)])]
+
+def test_rulesConditions(tmpdir):
+    # tests of rules, conditionsets and conditions
+    r1 = RuleDescriptor()
+    r1.name = "named.rule.1"
+    r1.conditionSets.append([
+        dict(name='axisName_a', minimum=0, maximum=1000),
+        dict(name='axisName_b', minimum=0, maximum=3000)
+    ])
+    r1.subs.append(("a", "a.alt"))
+
+    assert evaluateRule(r1, dict(axisName_a = 500, axisName_b = 0)) == True
+    assert evaluateRule(r1, dict(axisName_a = 0, axisName_b = 0)) == True
+    assert evaluateRule(r1, dict(axisName_a = 1000, axisName_b = 0)) == True
+    assert evaluateRule(r1, dict(axisName_a = 1000, axisName_b = -100)) == False
+    assert evaluateRule(r1, dict(axisName_a = 1000.0001, axisName_b = 0)) == False
+    assert evaluateRule(r1, dict(axisName_a = -0.0001, axisName_b = 0)) == False
+    assert evaluateRule(r1, dict(axisName_a = -100, axisName_b = 0)) == False
+    assert processRules([r1], dict(axisName_a = 500, axisName_b = 0), ["a", "b", "c"]) == ['a.alt', 'b', 'c']
+    assert processRules([r1], dict(axisName_a = 500, axisName_b = 0), ["a.alt", "b", "c"]) == ['a.alt', 'b', 'c']
+    assert processRules([r1], dict(axisName_a = 2000, axisName_b = 0), ["a", "b", "c"]) == ['a', 'b', 'c']
+
+    # rule with only a maximum
+    r2 = RuleDescriptor()
+    r2.name = "named.rule.2"
+    r2.conditionSets.append([dict(name='axisName_a', maximum=500)])
+    r2.subs.append(("b", "b.alt"))
+
+    assert evaluateRule(r2, dict(axisName_a = 0)) == True
+    assert evaluateRule(r2, dict(axisName_a = -500)) == True
+    assert evaluateRule(r2, dict(axisName_a = 1000)) == False
+
+    # rule with only a minimum
+    r3 = RuleDescriptor()
+    r3.name = "named.rule.3"
+    r3.conditionSets.append([dict(name='axisName_a', minimum=500)])
+    r3.subs.append(("c", "c.alt"))
+
+    assert evaluateRule(r3, dict(axisName_a = 0)) == False
+    assert evaluateRule(r3, dict(axisName_a = 1000)) == True
+    assert evaluateRule(r3, dict(axisName_a = 1000)) == True
+
+    # rule with only a minimum, maximum in separate conditions
+    r4 = RuleDescriptor()
+    r4.name = "named.rule.4"
+    r4.conditionSets.append([
+        dict(name='axisName_a', minimum=500),
+        dict(name='axisName_b', maximum=500)
+    ])
+    r4.subs.append(("c", "c.alt"))
+
+    assert evaluateRule(r4, dict(axisName_a = 1000, axisName_b = 0)) == True
+    assert evaluateRule(r4, dict(axisName_a = 0, axisName_b = 0)) == False
+    assert evaluateRule(r4, dict(axisName_a = 1000, axisName_b = 1000)) == False
+
+def test_rulesDocument(tmpdir):
+    # tests of rules in a document, roundtripping.
+    tmpdir = str(tmpdir)
+    testDocPath = os.path.join(tmpdir, "testRules.designspace")
+    testDocPath2 = os.path.join(tmpdir, "testRules_roundtrip.designspace")
+    doc = DesignSpaceDocument()
+    a1 = AxisDescriptor()
+    a1.minimum = 0
+    a1.maximum = 1000
+    a1.default = 0
+    a1.name = "axisName_a"
+    a1.tag = "TAGA"
+    b1 = AxisDescriptor()
+    b1.minimum = 2000
+    b1.maximum = 3000
+    b1.default = 2000
+    b1.name = "axisName_b"
+    b1.tag = "TAGB"
+    doc.addAxis(a1)
+    doc.addAxis(b1)
+    r1 = RuleDescriptor()
+    r1.name = "named.rule.1"
+    r1.conditionSets.append([
+        dict(name='axisName_a', minimum=0, maximum=1000),
+        dict(name='axisName_b', minimum=0, maximum=3000)
+    ])
+    r1.subs.append(("a", "a.alt"))
+    # rule with minium and maximum
+    doc.addRule(r1)
+    assert len(doc.rules) == 1
+    assert len(doc.rules[0].conditionSets) == 1
+    assert len(doc.rules[0].conditionSets[0]) == 2
+    assert _axesAsDict(doc.axes) == {'axisName_a': {'map': [], 'name': 'axisName_a', 'default': 0, 'minimum': 0, 'maximum': 1000, 'tag': 'TAGA'}, 'axisName_b': {'map': [], 'name': 'axisName_b', 'default': 2000, 'minimum': 2000, 'maximum': 3000, 'tag': 'TAGB'}}
+    assert doc.rules[0].conditionSets == [[
+        {'minimum': 0, 'maximum': 1000, 'name': 'axisName_a'},
+        {'minimum': 0, 'maximum': 3000, 'name': 'axisName_b'}]]
+    assert doc.rules[0].subs == [('a', 'a.alt')]
+    doc.normalize()
+    assert doc.rules[0].name == 'named.rule.1'
+    assert doc.rules[0].conditionSets == [[
+        {'minimum': 0.0, 'maximum': 1.0, 'name': 'axisName_a'},
+        {'minimum': 0.0, 'maximum': 1.0, 'name': 'axisName_b'}]]
+    # still one conditionset
+    assert len(doc.rules[0].conditionSets) == 1
+    doc.write(testDocPath)
+    # add a stray conditionset
+    _addUnwrappedCondition(testDocPath)
+    doc2 = DesignSpaceDocument()
+    doc2.read(testDocPath)
+    assert len(doc2.axes) == 2
+    assert len(doc2.rules) == 1
+    assert len(doc2.rules[0].conditionSets) == 2
+    doc2.write(testDocPath2)
+    # verify these results
+    # make sure the stray condition is now neatly wrapped in a conditionset.
+    doc3 = DesignSpaceDocument()
+    doc3.read(testDocPath2)
+    assert len(doc3.rules) == 1
+    assert len(doc3.rules[0].conditionSets) == 2
+
+def _addUnwrappedCondition(path):
+    # only for testing, so we can make an invalid designspace file
+    # older designspace files may have conditions that are not wrapped in a conditionset
+    # These can be read into a new conditionset.
+    f = open(path, 'r', encoding='utf-8')
+    d = f.read()
+    print(d)
+    f.close()
+    d = d.replace('<rule name="named.rule.1">', '<rule name="named.rule.1">\n\t<condition maximum="22" minimum="33" name="axisName_a" />')
+    f = open(path, 'w', encoding='utf-8')
+    f.write(d)
+    f.close()
+
+def test_documentLib(tmpdir):
+    # roundtrip test of the document lib with some nested data
+    tmpdir = str(tmpdir)
+    testDocPath1 = os.path.join(tmpdir, "testDocumentLibTest.designspace")
+    doc = DesignSpaceDocument()
+    a1 = AxisDescriptor()
+    a1.tag = "TAGA"
+    a1.name = "axisName_a"
+    a1.minimum = 0
+    a1.maximum = 1000
+    a1.default = 0
+    doc.addAxis(a1)
+    dummyData = dict(a=123, b=u"äbc", c=[1,2,3], d={'a':123})
+    dummyKey = "org.fontTools.designspaceLib"
+    doc.lib = {dummyKey: dummyData}
+    doc.write(testDocPath1)
+    new = DesignSpaceDocument()
+    new.read(testDocPath1)
+    assert dummyKey in new.lib
+    assert new.lib[dummyKey] == dummyData
+
diff --git a/Tests/encodings/codecs_test.py b/Tests/encodings/codecs_test.py
new file mode 100644
index 0000000..29e3a0d
--- /dev/null
+++ b/Tests/encodings/codecs_test.py
@@ -0,0 +1,26 @@
+from __future__ import print_function, division, absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+import unittest
+import fontTools.encodings.codecs # Not to be confused with "import codecs"
+
+class ExtendedCodecsTest(unittest.TestCase):
+
+	def test_decode_mac_japanese(self):
+		self.assertEqual(b'x\xfe\xfdy'.decode("x_mac_japanese_ttx"),
+				 unichr(0x78)+unichr(0x2122)+unichr(0x00A9)+unichr(0x79))
+
+	def test_encode_mac_japanese(self):
+		self.assertEqual(b'x\xfe\xfdy',
+				 (unichr(0x78)+unichr(0x2122)+unichr(0x00A9)+unichr(0x79)).encode("x_mac_japanese_ttx"))
+
+	def test_decode_mac_trad_chinese(self):
+		self.assertEqual(b'\x80'.decode("x_mac_trad_chinese_ttx"),
+				 unichr(0x5C))
+
+	def test_decode_mac_romanian(self):
+		self.assertEqual(b'x\xfb'.decode("mac_romanian"),
+				 unichr(0x78)+unichr(0x02DA))
+
+if __name__ == '__main__':
+	import sys
+	sys.exit(unittest.main())
diff --git a/Tests/feaLib/__init__.py b/Tests/feaLib/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/feaLib/__init__.py
diff --git a/Tests/feaLib/builder_test.py b/Tests/feaLib/builder_test.py
new file mode 100644
index 0000000..e831f97
--- /dev/null
+++ b/Tests/feaLib/builder_test.py
@@ -0,0 +1,528 @@
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.misc.loggingTools import CapturingLogHandler
+from fontTools.feaLib.builder import Builder, addOpenTypeFeatures, \
+        addOpenTypeFeaturesFromString
+from fontTools.feaLib.error import FeatureLibError
+from fontTools.ttLib import TTFont
+from fontTools.feaLib.parser import Parser
+from fontTools.feaLib import ast
+from fontTools.feaLib.lexer import Lexer
+import difflib
+import os
+import shutil
+import sys
+import tempfile
+import logging
+import unittest
+
+
+def makeTTFont():
+    glyphs = """
+        .notdef space slash fraction semicolon period comma ampersand
+        quotedblleft quotedblright quoteleft quoteright
+        zero one two three four five six seven eight nine
+        zero.oldstyle one.oldstyle two.oldstyle three.oldstyle
+        four.oldstyle five.oldstyle six.oldstyle seven.oldstyle
+        eight.oldstyle nine.oldstyle onequarter onehalf threequarters
+        onesuperior twosuperior threesuperior ordfeminine ordmasculine
+        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
+        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
+        A.sc B.sc C.sc D.sc E.sc F.sc G.sc H.sc I.sc J.sc K.sc L.sc M.sc
+        N.sc O.sc P.sc Q.sc R.sc S.sc T.sc U.sc V.sc W.sc X.sc Y.sc Z.sc
+        A.alt1 A.alt2 A.alt3 B.alt1 B.alt2 B.alt3 C.alt1 C.alt2 C.alt3
+        a.alt1 a.alt2 a.alt3 a.end b.alt c.mid d.alt d.mid
+        e.begin e.mid e.end m.begin n.end s.end z.end
+        Eng Eng.alt1 Eng.alt2 Eng.alt3
+        A.swash B.swash C.swash D.swash E.swash F.swash G.swash H.swash
+        I.swash J.swash K.swash L.swash M.swash N.swash O.swash P.swash
+        Q.swash R.swash S.swash T.swash U.swash V.swash W.swash X.swash
+        Y.swash Z.swash
+        f_l c_h c_k c_s c_t f_f f_f_i f_f_l f_i o_f_f_i s_t f_i.begin
+        a_n_d T_h T_h.swash germandbls ydieresis yacute breve
+        grave acute dieresis macron circumflex cedilla umlaut ogonek caron
+        damma hamza sukun kasratan lam_meem_jeem noon.final noon.initial
+        by feature lookup sub table
+    """.split()
+    font = TTFont()
+    font.setGlyphOrder(glyphs)
+    return font
+
+
+class BuilderTest(unittest.TestCase):
+    # Feature files in data/*.fea; output gets compared to data/*.ttx.
+    TEST_FEATURE_FILES = """
+        Attach enum markClass language_required
+        GlyphClassDef LigatureCaretByIndex LigatureCaretByPos
+        lookup lookupflag feature_aalt ignore_pos
+        GPOS_1 GPOS_1_zero GPOS_2 GPOS_2b GPOS_3 GPOS_4 GPOS_5 GPOS_6 GPOS_8
+        GSUB_2 GSUB_3 GSUB_6 GSUB_8
+        spec4h1 spec4h2 spec5d1 spec5d2 spec5fi1 spec5fi2 spec5fi3 spec5fi4
+        spec5f_ii_1 spec5f_ii_2 spec5f_ii_3 spec5f_ii_4
+        spec5h1 spec6b_ii spec6d2 spec6e spec6f
+        spec6h_ii spec6h_iii_1 spec6h_iii_3d spec8a spec8b spec8c spec8d
+        spec9a spec9b spec9c1 spec9c2 spec9c3 spec9d spec9e spec9f spec9g
+        spec10
+        bug453 bug457 bug463 bug501 bug502 bug504 bug505 bug506 bug509
+        bug512 bug514 bug568 bug633
+        name size size2 multiple_feature_blocks omitted_GlyphClassDef
+        ZeroValue_SinglePos_horizontal ZeroValue_SinglePos_vertical
+        ZeroValue_PairPos_horizontal ZeroValue_PairPos_vertical
+        ZeroValue_ChainSinglePos_horizontal ZeroValue_ChainSinglePos_vertical
+        PairPosSubtable
+    """.split()
+
+    def __init__(self, methodName):
+        unittest.TestCase.__init__(self, methodName)
+        # Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
+        # and fires deprecation warnings if a program uses the old name.
+        if not hasattr(self, "assertRaisesRegex"):
+            self.assertRaisesRegex = self.assertRaisesRegexp
+
+    def setUp(self):
+        self.tempdir = None
+        self.num_tempfiles = 0
+
+    def tearDown(self):
+        if self.tempdir:
+            shutil.rmtree(self.tempdir)
+
+    @staticmethod
+    def getpath(testfile):
+        path, _ = os.path.split(__file__)
+        return os.path.join(path, "data", testfile)
+
+    def temp_path(self, suffix):
+        if not self.tempdir:
+            self.tempdir = tempfile.mkdtemp()
+        self.num_tempfiles += 1
+        return os.path.join(self.tempdir,
+                            "tmp%d%s" % (self.num_tempfiles, suffix))
+
+    def read_ttx(self, path):
+        lines = []
+        with open(path, "r", encoding="utf-8") as ttx:
+            for line in ttx.readlines():
+                # Elide ttFont attributes because ttLibVersion may change,
+                # and use os-native line separators so we can run difflib.
+                if line.startswith("<ttFont "):
+                    lines.append("<ttFont>" + os.linesep)
+                else:
+                    lines.append(line.rstrip() + os.linesep)
+        return lines
+
+    def expect_ttx(self, font, expected_ttx):
+        path = self.temp_path(suffix=".ttx")
+        font.saveXML(path, tables=['head', 'name', 'BASE', 'GDEF', 'GSUB',
+                                   'GPOS', 'OS/2', 'hhea', 'vhea'])
+        actual = self.read_ttx(path)
+        expected = self.read_ttx(expected_ttx)
+        if actual != expected:
+            for line in difflib.unified_diff(
+                    expected, actual, fromfile=expected_ttx, tofile=path):
+                sys.stderr.write(line)
+            self.fail("TTX output is different from expected")
+
+    def build(self, featureFile, tables=None):
+        font = makeTTFont()
+        addOpenTypeFeaturesFromString(font, featureFile, tables=tables)
+        return font
+
+    def check_feature_file(self, name):
+        font = makeTTFont()
+        addOpenTypeFeatures(font, self.getpath("%s.fea" % name))
+        self.expect_ttx(font, self.getpath("%s.ttx" % name))
+        # Make sure we can produce binary OpenType tables, not just XML.
+        for tag in ('GDEF', 'GSUB', 'GPOS'):
+            if tag in font:
+                font[tag].compile(font)
+
+    def check_fea2fea_file(self, name, base=None, parser=Parser):
+        font = makeTTFont()
+        fname = (name + ".fea") if '.' not in name else name
+        p = parser(self.getpath(fname), glyphNames=font.getGlyphOrder())
+        doc = p.parse()
+        actual = self.normal_fea(doc.asFea().split("\n"))
+        with open(self.getpath(base or fname), "r", encoding="utf-8") as ofile:
+            expected = self.normal_fea(ofile.readlines())
+
+        if expected != actual:
+            fname = name.rsplit(".", 1)[0] + ".fea"
+            for line in difflib.unified_diff(
+                    expected, actual,
+                    fromfile=fname + " (expected)",
+                    tofile=fname + " (actual)"):
+                sys.stderr.write(line+"\n")
+            self.fail("Fea2Fea output is different from expected. "
+                      "Generated:\n{}\n".format("\n".join(actual)))
+
+    def normal_fea(self, lines):
+        output = []
+        skip = 0
+        for l in lines:
+            l = l.strip()
+            if l.startswith("#test-fea2fea:"):
+                if len(l) > 15:
+                    output.append(l[15:].strip())
+                skip = 1
+            x = l.find("#")
+            if x >= 0:
+                l = l[:x].strip()
+            if not len(l):
+                continue
+            if skip > 0:
+                skip = skip - 1
+                continue
+            output.append(l)
+        return output
+
+    def test_alternateSubst_multipleSubstitutionsForSameGlyph(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Already defined alternates for glyph \"A\"",
+            self.build,
+            "feature test {"
+            "    sub A from [A.alt1 A.alt2];"
+            "    sub B from [B.alt1 B.alt2 B.alt3];"
+            "    sub A from [A.alt1 A.alt2];"
+            "} test;")
+
+    def test_multipleSubst_multipleSubstitutionsForSameGlyph(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Already defined substitution for glyph \"f_f_i\"",
+            self.build,
+            "feature test {"
+            "    sub f_f_i by f f i;"
+            "    sub c_t by c t;"
+            "    sub f_f_i by f f i;"
+            "} test;")
+
+    def test_pairPos_redefinition_warning(self):
+        # https://github.com/fonttools/fonttools/issues/1147
+        logger = logging.getLogger("fontTools.feaLib.builder")
+        with CapturingLogHandler(logger, "WARNING") as captor:
+            # the pair "yacute semicolon" is redefined in the enum pos
+            font = self.build(
+                "@Y_LC = [y yacute ydieresis];"
+                "@SMALL_PUNC = [comma semicolon period];"
+                "feature kern {"
+                "  pos yacute semicolon -70;"
+                "  enum pos @Y_LC semicolon -80;"
+                "  pos @Y_LC @SMALL_PUNC -100;"
+                "} kern;")
+
+        captor.assertRegex("Already defined position for pair yacute semicolon")
+
+        # the first definition prevails: yacute semicolon -70
+        st = font["GPOS"].table.LookupList.Lookup[0].SubTable[0]
+        self.assertEqual(st.Coverage.glyphs[2], "yacute")
+        self.assertEqual(st.PairSet[2].PairValueRecord[0].SecondGlyph,
+                         "semicolon")
+        self.assertEqual(vars(st.PairSet[2].PairValueRecord[0].Value1),
+                         {"XAdvance": -70})
+
+    def test_singleSubst_multipleSubstitutionsForSameGlyph(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'Already defined rule for replacing glyph "e" by "E.sc"',
+            self.build,
+            "feature test {"
+            "    sub [a-z] by [A.sc-Z.sc];"
+            "    sub e by e.fina;"
+            "} test;")
+
+    def test_singlePos_redefinition(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Already defined different position for glyph \"A\"",
+            self.build, "feature test { pos A 123; pos A 456; } test;")
+
+    def test_feature_outside_aalt(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'Feature references are only allowed inside "feature aalt"',
+            self.build, "feature test { feature test; } test;")
+
+    def test_feature_undefinedReference(self):
+        self.assertRaisesRegex(
+            FeatureLibError, 'Feature none has not been defined',
+            self.build, "feature aalt { feature none; } aalt;")
+
+    def test_GlyphClassDef_conflictingClasses(self):
+        self.assertRaisesRegex(
+            FeatureLibError, "Glyph X was assigned to a different class",
+            self.build,
+            "table GDEF {"
+            "    GlyphClassDef [a b], [X], , ;"
+            "    GlyphClassDef [a b X], , , ;"
+            "} GDEF;")
+
+    def test_languagesystem(self):
+        builder = Builder(makeTTFont(), (None, None))
+        builder.add_language_system(None, 'latn', 'FRA')
+        builder.add_language_system(None, 'cyrl', 'RUS')
+        builder.start_feature(location=None, name='test')
+        self.assertEqual(builder.language_systems,
+                         {('latn', 'FRA'), ('cyrl', 'RUS')})
+
+    def test_languagesystem_duplicate(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            '"languagesystem cyrl RUS" has already been specified',
+            self.build, "languagesystem cyrl RUS; languagesystem cyrl RUS;")
+
+    def test_languagesystem_none_specified(self):
+        builder = Builder(makeTTFont(), (None, None))
+        builder.start_feature(location=None, name='test')
+        self.assertEqual(builder.language_systems, {('DFLT', 'dflt')})
+
+    def test_languagesystem_DFLT_dflt_not_first(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "If \"languagesystem DFLT dflt\" is present, "
+            "it must be the first of the languagesystem statements",
+            self.build, "languagesystem latn TRK; languagesystem DFLT dflt;")
+
+    def test_script(self):
+        builder = Builder(makeTTFont(), (None, None))
+        builder.start_feature(location=None, name='test')
+        builder.set_script(location=None, script='cyrl')
+        self.assertEqual(builder.language_systems, {('cyrl', 'dflt')})
+
+    def test_script_in_aalt_feature(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Script statements are not allowed within \"feature aalt\"",
+            self.build, "feature aalt { script latn; } aalt;")
+
+    def test_script_in_size_feature(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Script statements are not allowed within \"feature size\"",
+            self.build, "feature size { script latn; } size;")
+
+    def test_language(self):
+        builder = Builder(makeTTFont(), (None, None))
+        builder.add_language_system(None, 'latn', 'FRA ')
+        builder.start_feature(location=None, name='test')
+        builder.set_script(location=None, script='cyrl')
+        builder.set_language(location=None, language='RUS ',
+                             include_default=False, required=False)
+        self.assertEqual(builder.language_systems, {('cyrl', 'RUS ')})
+        builder.set_language(location=None, language='BGR ',
+                             include_default=True, required=False)
+        self.assertEqual(builder.language_systems,
+                         {('cyrl', 'BGR ')})
+        builder.start_feature(location=None, name='test2')
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Need non-DFLT script when using non-dflt language "
+            "\(was: \"FRA \"\)",
+            builder.set_language, None, 'FRA ', True, False)
+
+    def test_language_in_aalt_feature(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Language statements are not allowed within \"feature aalt\"",
+            self.build, "feature aalt { language FRA; } aalt;")
+
+    def test_language_in_size_feature(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Language statements are not allowed within \"feature size\"",
+            self.build, "feature size { language FRA; } size;")
+
+    def test_language_required_duplicate(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            r"Language FRA \(script latn\) has already specified "
+            "feature scmp as its required feature",
+            self.build,
+            "feature scmp {"
+            "    script latn;"
+            "    language FRA required;"
+            "    language DEU required;"
+            "    substitute [a-z] by [A.sc-Z.sc];"
+            "} scmp;"
+            "feature test {"
+            "    script latn;"
+            "    language FRA required;"
+            "    substitute [a-z] by [A.sc-Z.sc];"
+            "} test;")
+
+    def test_lookup_already_defined(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Lookup \"foo\" has already been defined",
+            self.build, "lookup foo {} foo; lookup foo {} foo;")
+
+    def test_lookup_multiple_flags(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Within a named lookup block, all rules must be "
+            "of the same lookup type and flag",
+            self.build,
+            "lookup foo {"
+            "    lookupflag 1;"
+            "    sub f i by f_i;"
+            "    lookupflag 2;"
+            "    sub f f i by f_f_i;"
+            "} foo;")
+
+    def test_lookup_multiple_types(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Within a named lookup block, all rules must be "
+            "of the same lookup type and flag",
+            self.build,
+            "lookup foo {"
+            "    sub f f i by f_f_i;"
+            "    sub A from [A.alt1 A.alt2];"
+            "} foo;")
+
+    def test_lookup_inside_feature_aalt(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Lookup blocks cannot be placed inside 'aalt' features",
+            self.build, "feature aalt {lookup L {} L;} aalt;")
+
+    def test_extensions(self):
+        class ast_BaseClass(ast.MarkClass):
+            def asFea(self, indent=""):
+                return ""
+
+        class ast_BaseClassDefinition(ast.MarkClassDefinition):
+            def asFea(self, indent=""):
+                return ""
+
+        class ast_MarkBasePosStatement(ast.MarkBasePosStatement):
+            def asFea(self, indent=""):
+                if isinstance(self.base, ast.MarkClassName):
+                    res = ""
+                    for bcd in self.base.markClass.definitions:
+                        if res != "":
+                            res += "\n{}".format(indent)
+                        res += "pos base {} {}".format(bcd.glyphs.asFea(), bcd.anchor.asFea())
+                        for m in self.marks:
+                            res += " mark @{}".format(m.name)
+                        res += ";"
+                else:
+                    res = "pos base {}".format(self.base.asFea())
+                    for a, m in self.marks:
+                        res += " {} mark @{}".format(a.asFea(), m.name)
+                    res += ";"
+                return res
+
+        class testAst(object):
+            MarkBasePosStatement = ast_MarkBasePosStatement
+            def __getattr__(self, name):
+                return getattr(ast, name)
+
+        class testParser(Parser):
+            def parse_position_base_(self, enumerated, vertical):
+                location = self.cur_token_location_
+                self.expect_keyword_("base")
+                if enumerated:
+                    raise FeatureLibError(
+                        '"enumerate" is not allowed with '
+                        'mark-to-base attachment positioning',
+                        location)
+                base = self.parse_glyphclass_(accept_glyphname=True)
+                if self.next_token_ == "<":
+                    marks = self.parse_anchor_marks_()
+                else:
+                    marks = []
+                    while self.next_token_ == "mark":
+                        self.expect_keyword_("mark")
+                        m = self.expect_markClass_reference_()
+                        marks.append(m)
+                self.expect_symbol_(";")
+                return self.ast.MarkBasePosStatement(base, marks,
+                                                     location=location)
+
+            def parseBaseClass(self):
+                if not hasattr(self.doc_, 'baseClasses'):
+                    self.doc_.baseClasses = {}
+                location = self.cur_token_location_
+                glyphs = self.parse_glyphclass_(accept_glyphname=True)
+                anchor = self.parse_anchor_()
+                name = self.expect_class_name_()
+                self.expect_symbol_(";")
+                baseClass = self.doc_.baseClasses.get(name)
+                if baseClass is None:
+                    baseClass = ast_BaseClass(name)
+                    self.doc_.baseClasses[name] = baseClass
+                    self.glyphclasses_.define(name, baseClass)
+                bcdef = ast_BaseClassDefinition(baseClass, anchor, glyphs,
+                                                location=location)
+                baseClass.addDefinition(bcdef)
+                return bcdef
+
+            extensions = {
+                'baseClass' : lambda s : s.parseBaseClass()
+            }
+            ast = testAst()
+
+        self.check_fea2fea_file(
+            "baseClass.feax", base="baseClass.fea", parser=testParser)
+
+    def test_markClass_same_glyph_redefined(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Glyph acute already defined",
+            self.build,
+            "markClass [acute] <anchor 350 0> @TOP_MARKS;"*2)
+
+    def test_markClass_same_glyph_multiple_classes(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'Glyph uni0327 cannot be in both @ogonek and @cedilla',
+            self.build,
+            "feature mark {"
+            "    markClass [uni0327 uni0328] <anchor 0 0> @ogonek;"
+            "    pos base [a] <anchor 399 0> mark @ogonek;"
+            "    markClass [uni0327] <anchor 0 0> @cedilla;"
+            "    pos base [a] <anchor 244 0> mark @cedilla;"
+            "} mark;")
+
+    def test_build_specific_tables(self):
+        features = "feature liga {sub f i by f_i;} liga;"
+        font = self.build(features)
+        assert "GSUB" in font
+
+        font2 = self.build(features, tables=set())
+        assert "GSUB" not in font2
+
+    def test_build_unsupported_tables(self):
+        self.assertRaises(AssertionError, self.build, "", tables={"FOO"})
+
+    def test_build_pre_parsed_ast_featurefile(self):
+        f = UnicodeIO("feature liga {sub f i by f_i;} liga;")
+        tree = Parser(f).parse()
+        font = makeTTFont()
+        addOpenTypeFeatures(font, tree)
+        assert "GSUB" in font
+
+
+def generate_feature_file_test(name):
+    return lambda self: self.check_feature_file(name)
+
+
+for name in BuilderTest.TEST_FEATURE_FILES:
+    setattr(BuilderTest, "test_FeatureFile_%s" % name,
+            generate_feature_file_test(name))
+
+
+def generate_fea2fea_file_test(name):
+    return lambda self: self.check_fea2fea_file(name)
+
+
+for name in BuilderTest.TEST_FEATURE_FILES:
+    setattr(BuilderTest, "test_Fea2feaFile_{}".format(name),
+            generate_fea2fea_file_test(name))
+
+
+if __name__ == "__main__":
+    sys.exit(unittest.main())
diff --git a/Tests/feaLib/data/Attach.fea b/Tests/feaLib/data/Attach.fea
new file mode 100644
index 0000000..86266ee
--- /dev/null
+++ b/Tests/feaLib/data/Attach.fea
@@ -0,0 +1,5 @@
+table GDEF {
+    Attach [a e] 7;
+    Attach a 23;
+    Attach a 23;
+} GDEF;
diff --git a/Tests/feaLib/data/Attach.ttx b/Tests/feaLib/data/Attach.ttx
new file mode 100644
index 0000000..f72d6d8
--- /dev/null
+++ b/Tests/feaLib/data/Attach.ttx
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <AttachList>
+      <Coverage>
+        <Glyph value="a"/>
+        <Glyph value="e"/>
+      </Coverage>
+      <!-- GlyphCount=2 -->
+      <AttachPoint index="0">
+        <!-- PointCount=2 -->
+        <PointIndex index="0" value="7"/>
+        <PointIndex index="1" value="23"/>
+      </AttachPoint>
+      <AttachPoint index="1">
+        <!-- PointCount=1 -->
+        <PointIndex index="0" value="7"/>
+      </AttachPoint>
+    </AttachList>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/feaLib/data/GPOS_1.fea b/Tests/feaLib/data/GPOS_1.fea
new file mode 100644
index 0000000..a9bccf2
--- /dev/null
+++ b/Tests/feaLib/data/GPOS_1.fea
@@ -0,0 +1,42 @@
+languagesystem DFLT dflt;
+
+@sevenEightNine = [seven eight nine];
+
+feature kern {
+    pos zero 0;
+
+    pos [one two three] <-80 0 -160 0>;
+    pos A <1 2 3 4 <device 11 111, 12 112> <device 13 113, 14 114> <device 16 116> <device NULL>>;
+    pos B <1 2 3 4 <device 11 111, 12 112> <device 13 113, 14 114> <device 16 116> <device NULL>>;
+    pos C <1 2 3 4 <device 11 -2, 14 1> <device 13 -3, 15 1> <device 11 -8, 14 7> <device 13 8, 15 1>>;
+    pos four 400;
+    pos four.oldstyle 401;
+    pos five <-80 0 -160 0>;
+    pos six -200;
+    pos @sevenEightNine -100;
+
+    pos P <1 0 800 0>;
+    pos Q <1 0 801 0>;
+    pos R <1 0 802 0>;
+    pos S <1 1 803 0>;
+    pos T <1 1 804 0>;
+    pos U <1 1 805 0>;
+
+    # The AFDKO makeotf tool accepts re-definitions of previously defined
+    # single adjustment positionings, provided the re-definition is using
+    # the same value.  We replicate this behavior.
+    pos four 400;
+    pos four <0 0 400 0>;
+    pos nine -100;
+} kern;
+
+# According to the OpenType Feature File specification section 2.e.iv,
+# the following should be interpreted as vertical advance adjustment
+# because -100 (a value record format A) appears within a ‘vkrn’ feature.
+# However, the AFDKO makeotf tool v2.0.90 (built on Nov 19, 2015) still
+# makes it a horizontal advance adjustment.  In our implementation,
+# we follow the specification, so we produce different output than makeotf.
+# https://github.com/adobe-type-tools/afdko/issues/85
+feature vkrn {
+    pos A -100;
+} vkrn;
diff --git a/Tests/feaLib/data/GPOS_1.ttx b/Tests/feaLib/data/GPOS_1.ttx
new file mode 100644
index 0000000..bd32013
--- /dev/null
+++ b/Tests/feaLib/data/GPOS_1.ttx
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=2 -->
+            <FeatureIndex index="0" value="0"/>
+            <FeatureIndex index="1" value="1"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=2 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="vkrn"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=8 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="one"/>
+            <Glyph value="two"/>
+            <Glyph value="three"/>
+            <Glyph value="five"/>
+          </Coverage>
+          <ValueFormat value="5"/>
+          <Value XPlacement="-80" XAdvance="-160"/>
+        </SinglePos>
+        <SinglePos index="1" Format="2">
+          <Coverage>
+            <Glyph value="four"/>
+            <Glyph value="six"/>
+            <Glyph value="four.oldstyle"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <!-- ValueCount=3 -->
+          <Value index="0" XAdvance="400"/>
+          <Value index="1" XAdvance="-200"/>
+          <Value index="2" XAdvance="401"/>
+        </SinglePos>
+        <SinglePos index="2" Format="1">
+          <Coverage>
+            <Glyph value="seven"/>
+            <Glyph value="eight"/>
+            <Glyph value="nine"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="-100"/>
+        </SinglePos>
+        <SinglePos index="3" Format="2">
+          <Coverage>
+            <Glyph value="P"/>
+            <Glyph value="Q"/>
+            <Glyph value="R"/>
+          </Coverage>
+          <ValueFormat value="5"/>
+          <!-- ValueCount=3 -->
+          <Value index="0" XPlacement="1" XAdvance="800"/>
+          <Value index="1" XPlacement="1" XAdvance="801"/>
+          <Value index="2" XPlacement="1" XAdvance="802"/>
+        </SinglePos>
+        <SinglePos index="4" Format="2">
+          <Coverage>
+            <Glyph value="S"/>
+            <Glyph value="T"/>
+            <Glyph value="U"/>
+          </Coverage>
+          <ValueFormat value="7"/>
+          <!-- ValueCount=3 -->
+          <Value index="0" XPlacement="1" YPlacement="1" XAdvance="803"/>
+          <Value index="1" XPlacement="1" YPlacement="1" XAdvance="804"/>
+          <Value index="2" XPlacement="1" YPlacement="1" XAdvance="805"/>
+        </SinglePos>
+        <SinglePos index="5" Format="1">
+          <Coverage>
+            <Glyph value="A"/>
+            <Glyph value="B"/>
+          </Coverage>
+          <ValueFormat value="127"/>
+          <Value XPlacement="1" YPlacement="2" XAdvance="3" YAdvance="4">
+            <XPlaDevice>
+              <StartSize value="11"/>
+              <EndSize value="12"/>
+              <DeltaFormat value="3"/>
+              <DeltaValue value="[111, 112]"/>
+            </XPlaDevice>
+            <YPlaDevice>
+              <StartSize value="13"/>
+              <EndSize value="14"/>
+              <DeltaFormat value="3"/>
+              <DeltaValue value="[113, 114]"/>
+            </YPlaDevice>
+            <XAdvDevice>
+              <StartSize value="16"/>
+              <EndSize value="16"/>
+              <DeltaFormat value="3"/>
+              <DeltaValue value="[116]"/>
+            </XAdvDevice>
+          </Value>
+        </SinglePos>
+        <SinglePos index="6" Format="1">
+          <Coverage>
+            <Glyph value="zero"/>
+          </Coverage>
+          <ValueFormat value="0"/>
+        </SinglePos>
+        <SinglePos index="7" Format="1">
+          <Coverage>
+            <Glyph value="C"/>
+          </Coverage>
+          <ValueFormat value="255"/>
+          <Value XPlacement="1" YPlacement="2" XAdvance="3" YAdvance="4">
+            <XPlaDevice>
+              <StartSize value="11"/>
+              <EndSize value="14"/>
+              <DeltaFormat value="1"/>
+              <DeltaValue value="[-2, 0, 0, 1]"/>
+            </XPlaDevice>
+            <YPlaDevice>
+              <StartSize value="13"/>
+              <EndSize value="15"/>
+              <DeltaFormat value="2"/>
+              <DeltaValue value="[-3, 0, 1]"/>
+            </YPlaDevice>
+            <XAdvDevice>
+              <StartSize value="11"/>
+              <EndSize value="14"/>
+              <DeltaFormat value="2"/>
+              <DeltaValue value="[-8, 0, 0, 7]"/>
+            </XAdvDevice>
+            <YAdvDevice>
+              <StartSize value="13"/>
+              <EndSize value="15"/>
+              <DeltaFormat value="3"/>
+              <DeltaValue value="[8, 0, 1]"/>
+            </YAdvDevice>
+          </Value>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="A"/>
+          </Coverage>
+          <ValueFormat value="8"/>
+          <Value YAdvance="-100"/>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/GPOS_1_zero.fea b/Tests/feaLib/data/GPOS_1_zero.fea
new file mode 100644
index 0000000..bfb51db
--- /dev/null
+++ b/Tests/feaLib/data/GPOS_1_zero.fea
@@ -0,0 +1,5 @@
+# https://github.com/behdad/fonttools/issues/471
+feature test {
+    pos zero 0;
+    pos four 500;
+} test;
\ No newline at end of file
diff --git a/Tests/feaLib/data/GPOS_1_zero.ttx b/Tests/feaLib/data/GPOS_1_zero.ttx
new file mode 100644
index 0000000..b02db67
--- /dev/null
+++ b/Tests/feaLib/data/GPOS_1_zero.ttx
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+ 
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=2 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="zero"/>
+          </Coverage>
+          <ValueFormat value="0"/>
+        </SinglePos>
+        <SinglePos index="1" Format="1">
+          <Coverage>
+            <Glyph value="four"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="500"/>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/GPOS_2.fea b/Tests/feaLib/data/GPOS_2.fea
new file mode 100644
index 0000000..b1f26d1
--- /dev/null
+++ b/Tests/feaLib/data/GPOS_2.fea
@@ -0,0 +1,30 @@
+languagesystem DFLT dflt;
+
+# Mixes kerning between single glyphs, and class-based kerning.
+# https://github.com/behdad/fonttools/issues/456
+lookup MixedKerning {
+    pos v v 14;
+    pos [D O Q] [T V W] -26;
+} MixedKerning;
+
+lookup GlyphKerning {
+    pos T one 100;
+    pos T two 200;
+    pos T two.oldstyle 200;
+    pos T three 300;
+    pos T four 400;
+    pos X a 100;
+    pos X b 200;
+    pos Y a 100;
+    pos Y b 200;
+    pos Y c <3 3 3 3>;
+} GlyphKerning;
+
+feature kern {
+    lookup GlyphKerning;
+    lookup MixedKerning;
+} kern;
+
+feature vkrn {
+    pos T one 100;
+} vkrn;
diff --git a/Tests/feaLib/data/GPOS_2.ttx b/Tests/feaLib/data/GPOS_2.ttx
new file mode 100644
index 0000000..84dc819
--- /dev/null
+++ b/Tests/feaLib/data/GPOS_2.ttx
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=2 -->
+            <FeatureIndex index="0" value="0"/>
+            <FeatureIndex index="1" value="1"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=2 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=2 -->
+          <LookupListIndex index="0" value="1"/>
+          <LookupListIndex index="1" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="vkrn"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="2"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=3 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=2 -->
+        <PairPos index="0" Format="1">
+          <Coverage>
+            <Glyph value="v"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="v"/>
+              <Value1 XAdvance="14"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+        <PairPos index="1" Format="2">
+          <Coverage>
+            <Glyph value="D"/>
+            <Glyph value="O"/>
+            <Glyph value="Q"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1>
+          </ClassDef1>
+          <ClassDef2>
+            <ClassDef glyph="T" class="1"/>
+            <ClassDef glyph="V" class="1"/>
+            <ClassDef glyph="W" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=1 -->
+          <!-- Class2Count=2 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="-26"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=2 -->
+        <PairPos index="0" Format="1">
+          <Coverage>
+            <Glyph value="T"/>
+            <Glyph value="X"/>
+            <Glyph value="Y"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=3 -->
+          <PairSet index="0">
+            <!-- PairValueCount=5 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="one"/>
+              <Value1 XAdvance="100"/>
+            </PairValueRecord>
+            <PairValueRecord index="1">
+              <SecondGlyph value="two"/>
+              <Value1 XAdvance="200"/>
+            </PairValueRecord>
+            <PairValueRecord index="2">
+              <SecondGlyph value="three"/>
+              <Value1 XAdvance="300"/>
+            </PairValueRecord>
+            <PairValueRecord index="3">
+              <SecondGlyph value="four"/>
+              <Value1 XAdvance="400"/>
+            </PairValueRecord>
+            <PairValueRecord index="4">
+              <SecondGlyph value="two.oldstyle"/>
+              <Value1 XAdvance="200"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="1">
+            <!-- PairValueCount=2 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="a"/>
+              <Value1 XAdvance="100"/>
+            </PairValueRecord>
+            <PairValueRecord index="1">
+              <SecondGlyph value="b"/>
+              <Value1 XAdvance="200"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="2">
+            <!-- PairValueCount=2 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="a"/>
+              <Value1 XAdvance="100"/>
+            </PairValueRecord>
+            <PairValueRecord index="1">
+              <SecondGlyph value="b"/>
+              <Value1 XAdvance="200"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+        <PairPos index="1" Format="1">
+          <Coverage>
+            <Glyph value="Y"/>
+          </Coverage>
+          <ValueFormat1 value="15"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="c"/>
+              <Value1 XPlacement="3" YPlacement="3" XAdvance="3" YAdvance="3"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage>
+            <Glyph value="T"/>
+          </Coverage>
+          <ValueFormat1 value="8"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="one"/>
+              <Value1 YAdvance="100"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/GPOS_2b.fea b/Tests/feaLib/data/GPOS_2b.fea
new file mode 100644
index 0000000..da9fd84
--- /dev/null
+++ b/Tests/feaLib/data/GPOS_2b.fea
@@ -0,0 +1,9 @@
+@PUNC = [comma semicolon period];
+
+feature kern {
+  pos [A] @PUNC 1;
+  pos [B C] [comma] 2;
+  pos [D E F] [comma] 3;
+  pos [D E F] [semicolon period] 4;
+  pos [G] @PUNC <5 5 5 5>;
+} kern;
diff --git a/Tests/feaLib/data/GPOS_2b.ttx b/Tests/feaLib/data/GPOS_2b.ttx
new file mode 100644
index 0000000..40f458f
--- /dev/null
+++ b/Tests/feaLib/data/GPOS_2b.ttx
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=3 -->
+        <PairPos index="0" Format="2">
+          <Coverage>
+            <Glyph value="A"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1>
+          </ClassDef1>
+          <ClassDef2>
+            <ClassDef glyph="comma" class="1"/>
+            <ClassDef glyph="period" class="1"/>
+            <ClassDef glyph="semicolon" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=1 -->
+          <!-- Class2Count=2 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="1"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+        <PairPos index="1" Format="2">
+          <Coverage>
+            <Glyph value="B"/>
+            <Glyph value="C"/>
+            <Glyph value="D"/>
+            <Glyph value="E"/>
+            <Glyph value="F"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1>
+            <ClassDef glyph="B" class="1"/>
+            <ClassDef glyph="C" class="1"/>
+          </ClassDef1>
+          <ClassDef2>
+            <ClassDef glyph="comma" class="2"/>
+            <ClassDef glyph="period" class="1"/>
+            <ClassDef glyph="semicolon" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=2 -->
+          <!-- Class2Count=3 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="4"/>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="3"/>
+            </Class2Record>
+          </Class1Record>
+          <Class1Record index="1">
+            <Class2Record index="0">
+            </Class2Record>
+            <Class2Record index="1">
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="2"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+        <PairPos index="2" Format="2">
+          <Coverage>
+            <Glyph value="G"/>
+          </Coverage>
+          <ValueFormat1 value="15"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1>
+          </ClassDef1>
+          <ClassDef2>
+            <ClassDef glyph="comma" class="1"/>
+            <ClassDef glyph="period" class="1"/>
+            <ClassDef glyph="semicolon" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=1 -->
+          <!-- Class2Count=2 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XPlacement="5" YPlacement="5" XAdvance="5" YAdvance="5"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/GPOS_3.fea b/Tests/feaLib/data/GPOS_3.fea
new file mode 100644
index 0000000..114ee16
--- /dev/null
+++ b/Tests/feaLib/data/GPOS_3.fea
@@ -0,0 +1,12 @@
+languagesystem DFLT dflt;
+
+anchorDef 3 4 contourpoint 2 ANCH342;
+
+feature kern {
+    pos cursive zero <anchor NULL> <anchor NULL>;
+    pos cursive one <anchor 121 -1> <anchor ANCH342>;
+    pos cursive two <anchor 122 -2> <anchor 3 4>;
+    pos cursive three <anchor 123 -3> <anchor NULL>;
+    pos cursive four <anchor 124 -4 contourpoint 7> <anchor 3 4>;
+    pos cursive five <anchor 124 -4 <device 8 1, 9 2> <device 7 3>> <anchor ANCH342>;
+} kern;
diff --git a/Tests/feaLib/data/GPOS_3.ttx b/Tests/feaLib/data/GPOS_3.ttx
new file mode 100644
index 0000000..a29f30e
--- /dev/null
+++ b/Tests/feaLib/data/GPOS_3.ttx
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="3"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <CursivePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="zero"/>
+            <Glyph value="one"/>
+            <Glyph value="two"/>
+            <Glyph value="three"/>
+            <Glyph value="four"/>
+            <Glyph value="five"/>
+          </Coverage>
+          <!-- EntryExitCount=6 -->
+          <EntryExitRecord index="0">
+          </EntryExitRecord>
+          <EntryExitRecord index="1">
+            <EntryAnchor Format="1">
+              <XCoordinate value="121"/>
+              <YCoordinate value="-1"/>
+            </EntryAnchor>
+            <ExitAnchor Format="2">
+              <XCoordinate value="3"/>
+              <YCoordinate value="4"/>
+              <AnchorPoint value="2"/>
+            </ExitAnchor>
+          </EntryExitRecord>
+          <EntryExitRecord index="2">
+            <EntryAnchor Format="1">
+              <XCoordinate value="122"/>
+              <YCoordinate value="-2"/>
+            </EntryAnchor>
+            <ExitAnchor Format="1">
+              <XCoordinate value="3"/>
+              <YCoordinate value="4"/>
+            </ExitAnchor>
+          </EntryExitRecord>
+          <EntryExitRecord index="3">
+            <EntryAnchor Format="1">
+              <XCoordinate value="123"/>
+              <YCoordinate value="-3"/>
+            </EntryAnchor>
+          </EntryExitRecord>
+          <EntryExitRecord index="4">
+            <EntryAnchor Format="2">
+              <XCoordinate value="124"/>
+              <YCoordinate value="-4"/>
+              <AnchorPoint value="7"/>
+            </EntryAnchor>
+            <ExitAnchor Format="1">
+              <XCoordinate value="3"/>
+              <YCoordinate value="4"/>
+            </ExitAnchor>
+          </EntryExitRecord>
+          <EntryExitRecord index="5">
+            <EntryAnchor Format="3">
+              <XCoordinate value="124"/>
+              <YCoordinate value="-4"/>
+              <XDeviceTable>
+                <StartSize value="8"/>
+                <EndSize value="9"/>
+                <DeltaFormat value="2"/>
+                <DeltaValue value="[1, 2]"/>
+              </XDeviceTable>
+              <YDeviceTable>
+                <StartSize value="7"/>
+                <EndSize value="7"/>
+                <DeltaFormat value="2"/>
+                <DeltaValue value="[3]"/>
+              </YDeviceTable>
+            </EntryAnchor>
+            <ExitAnchor Format="2">
+              <XCoordinate value="3"/>
+              <YCoordinate value="4"/>
+              <AnchorPoint value="2"/>
+            </ExitAnchor>
+          </EntryExitRecord>
+        </CursivePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/GPOS_4.fea b/Tests/feaLib/data/GPOS_4.fea
new file mode 100644
index 0000000..cfd2d75
--- /dev/null
+++ b/Tests/feaLib/data/GPOS_4.fea
@@ -0,0 +1,12 @@
+languagesystem DFLT dflt;
+
+markClass [acute grave] <anchor 111 -11> @TOP_MARKS;
+markClass macron <anchor 112 -12> @TOP_MARKS;
+markClass [cedilla] <anchor 222 22> @BOTTOM_MARKS;
+markClass [ogonek] <anchor 333 33> @SIDE_MARKS;
+
+feature test {
+    pos base a <anchor 11 1> mark @TOP_MARKS <anchor 12 -1> mark @BOTTOM_MARKS;
+    pos base [b c] <anchor 22 -2> mark @BOTTOM_MARKS;
+    pos base d <anchor 33 3> mark @SIDE_MARKS;
+} test;
diff --git a/Tests/feaLib/data/GPOS_4.ttx b/Tests/feaLib/data/GPOS_4.ttx
new file mode 100644
index 0000000..65cf498
--- /dev/null
+++ b/Tests/feaLib/data/GPOS_4.ttx
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef>
+      <ClassDef glyph="a" class="1"/>
+      <ClassDef glyph="acute" class="3"/>
+      <ClassDef glyph="b" class="1"/>
+      <ClassDef glyph="c" class="1"/>
+      <ClassDef glyph="cedilla" class="3"/>
+      <ClassDef glyph="d" class="1"/>
+      <ClassDef glyph="grave" class="3"/>
+      <ClassDef glyph="macron" class="3"/>
+      <ClassDef glyph="ogonek" class="3"/>
+    </GlyphClassDef>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkBasePos index="0" Format="1">
+          <MarkCoverage>
+            <Glyph value="grave"/>
+            <Glyph value="acute"/>
+            <Glyph value="macron"/>
+            <Glyph value="cedilla"/>
+            <Glyph value="ogonek"/>
+          </MarkCoverage>
+          <BaseCoverage>
+            <Glyph value="a"/>
+            <Glyph value="b"/>
+            <Glyph value="c"/>
+            <Glyph value="d"/>
+          </BaseCoverage>
+          <!-- ClassCount=3 -->
+          <MarkArray>
+            <!-- MarkCount=5 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="111"/>
+                <YCoordinate value="-11"/>
+              </MarkAnchor>
+            </MarkRecord>
+            <MarkRecord index="1">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="111"/>
+                <YCoordinate value="-11"/>
+              </MarkAnchor>
+            </MarkRecord>
+            <MarkRecord index="2">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="112"/>
+                <YCoordinate value="-12"/>
+              </MarkAnchor>
+            </MarkRecord>
+            <MarkRecord index="3">
+              <Class value="1"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="222"/>
+                <YCoordinate value="22"/>
+              </MarkAnchor>
+            </MarkRecord>
+            <MarkRecord index="4">
+              <Class value="2"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="333"/>
+                <YCoordinate value="33"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=4 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="11"/>
+                <YCoordinate value="1"/>
+              </BaseAnchor>
+              <BaseAnchor index="1" Format="1">
+                <XCoordinate value="12"/>
+                <YCoordinate value="-1"/>
+              </BaseAnchor>
+              <BaseAnchor index="2" empty="1"/>
+            </BaseRecord>
+            <BaseRecord index="1">
+              <BaseAnchor index="0" empty="1"/>
+              <BaseAnchor index="1" Format="1">
+                <XCoordinate value="22"/>
+                <YCoordinate value="-2"/>
+              </BaseAnchor>
+              <BaseAnchor index="2" empty="1"/>
+            </BaseRecord>
+            <BaseRecord index="2">
+              <BaseAnchor index="0" empty="1"/>
+              <BaseAnchor index="1" Format="1">
+                <XCoordinate value="22"/>
+                <YCoordinate value="-2"/>
+              </BaseAnchor>
+              <BaseAnchor index="2" empty="1"/>
+            </BaseRecord>
+            <BaseRecord index="3">
+              <BaseAnchor index="0" empty="1"/>
+              <BaseAnchor index="1" empty="1"/>
+              <BaseAnchor index="2" Format="1">
+                <XCoordinate value="33"/>
+                <YCoordinate value="3"/>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/GPOS_5.fea b/Tests/feaLib/data/GPOS_5.fea
new file mode 100644
index 0000000..b116539
--- /dev/null
+++ b/Tests/feaLib/data/GPOS_5.fea
@@ -0,0 +1,18 @@
+markClass [acute grave] <anchor 500 700> @TOP_MARKS;
+markClass macron <anchor 500 750> @TOP_MARKS;
+markClass [cedilla] <anchor 500 -10> @BOTTOM_MARKS;
+markClass [ogonek] <anchor 800 -10> @OGONEK;
+
+feature test {
+
+    pos ligature [c_t s_t] <anchor 500 800> mark @TOP_MARKS <anchor 500 -200> mark @BOTTOM_MARKS
+        ligComponent <anchor 1500 800> mark @TOP_MARKS <anchor 1500 -200> mark @BOTTOM_MARKS <anchor 1550 0> mark @OGONEK;
+
+    pos ligature f_l <anchor 300 800> mark @TOP_MARKS <anchor 300 -200> mark @BOTTOM_MARKS
+        ligComponent <anchor 600 800> mark @TOP_MARKS <anchor 600 -200> mark @BOTTOM_MARKS;
+
+    pos ligature [f_f_l] <anchor 300 800> mark @TOP_MARKS <anchor 300 -200> mark @BOTTOM_MARKS
+        ligComponent <anchor 600 800> mark @TOP_MARKS <anchor 600 -200> mark @BOTTOM_MARKS
+        ligComponent <anchor 900 800> mark @TOP_MARKS <anchor 900 -200> mark @BOTTOM_MARKS;
+
+} test;
diff --git a/Tests/feaLib/data/GPOS_5.ttx b/Tests/feaLib/data/GPOS_5.ttx
new file mode 100644
index 0000000..f63ce30
--- /dev/null
+++ b/Tests/feaLib/data/GPOS_5.ttx
@@ -0,0 +1,229 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef>
+      <ClassDef glyph="acute" class="3"/>
+      <ClassDef glyph="c_t" class="2"/>
+      <ClassDef glyph="cedilla" class="3"/>
+      <ClassDef glyph="f_f_l" class="2"/>
+      <ClassDef glyph="f_l" class="2"/>
+      <ClassDef glyph="grave" class="3"/>
+      <ClassDef glyph="macron" class="3"/>
+      <ClassDef glyph="ogonek" class="3"/>
+      <ClassDef glyph="s_t" class="2"/>
+    </GlyphClassDef>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkLigPos index="0" Format="1">
+          <MarkCoverage>
+            <Glyph value="grave"/>
+            <Glyph value="acute"/>
+            <Glyph value="macron"/>
+            <Glyph value="cedilla"/>
+            <Glyph value="ogonek"/>
+          </MarkCoverage>
+          <LigatureCoverage>
+            <Glyph value="f_l"/>
+            <Glyph value="c_t"/>
+            <Glyph value="f_f_l"/>
+            <Glyph value="s_t"/>
+          </LigatureCoverage>
+          <!-- ClassCount=3 -->
+          <MarkArray>
+            <!-- MarkCount=5 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="500"/>
+                <YCoordinate value="700"/>
+              </MarkAnchor>
+            </MarkRecord>
+            <MarkRecord index="1">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="500"/>
+                <YCoordinate value="700"/>
+              </MarkAnchor>
+            </MarkRecord>
+            <MarkRecord index="2">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="500"/>
+                <YCoordinate value="750"/>
+              </MarkAnchor>
+            </MarkRecord>
+            <MarkRecord index="3">
+              <Class value="1"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="500"/>
+                <YCoordinate value="-10"/>
+              </MarkAnchor>
+            </MarkRecord>
+            <MarkRecord index="4">
+              <Class value="2"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="800"/>
+                <YCoordinate value="-10"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <LigatureArray>
+            <!-- LigatureCount=4 -->
+            <LigatureAttach index="0">
+              <!-- ComponentCount=2 -->
+              <ComponentRecord index="0">
+                <LigatureAnchor index="0" Format="1">
+                  <XCoordinate value="300"/>
+                  <YCoordinate value="800"/>
+                </LigatureAnchor>
+                <LigatureAnchor index="1" Format="1">
+                  <XCoordinate value="300"/>
+                  <YCoordinate value="-200"/>
+                </LigatureAnchor>
+                <LigatureAnchor index="2" empty="1"/>
+              </ComponentRecord>
+              <ComponentRecord index="1">
+                <LigatureAnchor index="0" Format="1">
+                  <XCoordinate value="600"/>
+                  <YCoordinate value="800"/>
+                </LigatureAnchor>
+                <LigatureAnchor index="1" Format="1">
+                  <XCoordinate value="600"/>
+                  <YCoordinate value="-200"/>
+                </LigatureAnchor>
+                <LigatureAnchor index="2" empty="1"/>
+              </ComponentRecord>
+            </LigatureAttach>
+            <LigatureAttach index="1">
+              <!-- ComponentCount=2 -->
+              <ComponentRecord index="0">
+                <LigatureAnchor index="0" Format="1">
+                  <XCoordinate value="500"/>
+                  <YCoordinate value="800"/>
+                </LigatureAnchor>
+                <LigatureAnchor index="1" Format="1">
+                  <XCoordinate value="500"/>
+                  <YCoordinate value="-200"/>
+                </LigatureAnchor>
+                <LigatureAnchor index="2" empty="1"/>
+              </ComponentRecord>
+              <ComponentRecord index="1">
+                <LigatureAnchor index="0" Format="1">
+                  <XCoordinate value="1500"/>
+                  <YCoordinate value="800"/>
+                </LigatureAnchor>
+                <LigatureAnchor index="1" Format="1">
+                  <XCoordinate value="1500"/>
+                  <YCoordinate value="-200"/>
+                </LigatureAnchor>
+                <LigatureAnchor index="2" Format="1">
+                  <XCoordinate value="1550"/>
+                  <YCoordinate value="0"/>
+                </LigatureAnchor>
+              </ComponentRecord>
+            </LigatureAttach>
+            <LigatureAttach index="2">
+              <!-- ComponentCount=3 -->
+              <ComponentRecord index="0">
+                <LigatureAnchor index="0" Format="1">
+                  <XCoordinate value="300"/>
+                  <YCoordinate value="800"/>
+                </LigatureAnchor>
+                <LigatureAnchor index="1" Format="1">
+                  <XCoordinate value="300"/>
+                  <YCoordinate value="-200"/>
+                </LigatureAnchor>
+                <LigatureAnchor index="2" empty="1"/>
+              </ComponentRecord>
+              <ComponentRecord index="1">
+                <LigatureAnchor index="0" Format="1">
+                  <XCoordinate value="600"/>
+                  <YCoordinate value="800"/>
+                </LigatureAnchor>
+                <LigatureAnchor index="1" Format="1">
+                  <XCoordinate value="600"/>
+                  <YCoordinate value="-200"/>
+                </LigatureAnchor>
+                <LigatureAnchor index="2" empty="1"/>
+              </ComponentRecord>
+              <ComponentRecord index="2">
+                <LigatureAnchor index="0" Format="1">
+                  <XCoordinate value="900"/>
+                  <YCoordinate value="800"/>
+                </LigatureAnchor>
+                <LigatureAnchor index="1" Format="1">
+                  <XCoordinate value="900"/>
+                  <YCoordinate value="-200"/>
+                </LigatureAnchor>
+                <LigatureAnchor index="2" empty="1"/>
+              </ComponentRecord>
+            </LigatureAttach>
+            <LigatureAttach index="3">
+              <!-- ComponentCount=2 -->
+              <ComponentRecord index="0">
+                <LigatureAnchor index="0" Format="1">
+                  <XCoordinate value="500"/>
+                  <YCoordinate value="800"/>
+                </LigatureAnchor>
+                <LigatureAnchor index="1" Format="1">
+                  <XCoordinate value="500"/>
+                  <YCoordinate value="-200"/>
+                </LigatureAnchor>
+                <LigatureAnchor index="2" empty="1"/>
+              </ComponentRecord>
+              <ComponentRecord index="1">
+                <LigatureAnchor index="0" Format="1">
+                  <XCoordinate value="1500"/>
+                  <YCoordinate value="800"/>
+                </LigatureAnchor>
+                <LigatureAnchor index="1" Format="1">
+                  <XCoordinate value="1500"/>
+                  <YCoordinate value="-200"/>
+                </LigatureAnchor>
+                <LigatureAnchor index="2" Format="1">
+                  <XCoordinate value="1550"/>
+                  <YCoordinate value="0"/>
+                </LigatureAnchor>
+              </ComponentRecord>
+            </LigatureAttach>
+          </LigatureArray>
+        </MarkLigPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/GPOS_6.fea b/Tests/feaLib/data/GPOS_6.fea
new file mode 100644
index 0000000..37b2936
--- /dev/null
+++ b/Tests/feaLib/data/GPOS_6.fea
@@ -0,0 +1,10 @@
+languagesystem DFLT dflt;
+
+markClass [acute grave] <anchor 1 1 contourpoint 11> @TOP_MARKS;
+markClass macron <anchor 2 2 contourpoint 22> @TOP_MARKS;
+markClass [cedilla] <anchor 3 3 contourpoint 33> @BOTTOM_MARKS;
+
+feature test {
+    pos mark [acute grave macron ogonek] <anchor 500 200> mark @TOP_MARKS <anchor 500 -80> mark @BOTTOM_MARKS;
+    pos mark [dieresis caron] <anchor 500 200> mark @TOP_MARKS;
+} test;
diff --git a/Tests/feaLib/data/GPOS_6.ttx b/Tests/feaLib/data/GPOS_6.ttx
new file mode 100644
index 0000000..c09fdf3
--- /dev/null
+++ b/Tests/feaLib/data/GPOS_6.ttx
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef>
+      <ClassDef glyph="acute" class="3"/>
+      <ClassDef glyph="caron" class="3"/>
+      <ClassDef glyph="cedilla" class="3"/>
+      <ClassDef glyph="dieresis" class="3"/>
+      <ClassDef glyph="grave" class="3"/>
+      <ClassDef glyph="macron" class="3"/>
+      <ClassDef glyph="ogonek" class="3"/>
+    </GlyphClassDef>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkMarkPos index="0" Format="1">
+          <Mark1Coverage>
+            <Glyph value="grave"/>
+            <Glyph value="acute"/>
+            <Glyph value="macron"/>
+            <Glyph value="cedilla"/>
+          </Mark1Coverage>
+          <Mark2Coverage>
+            <Glyph value="grave"/>
+            <Glyph value="acute"/>
+            <Glyph value="dieresis"/>
+            <Glyph value="macron"/>
+            <Glyph value="ogonek"/>
+            <Glyph value="caron"/>
+          </Mark2Coverage>
+          <!-- ClassCount=2 -->
+          <Mark1Array>
+            <!-- MarkCount=4 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="2">
+                <XCoordinate value="1"/>
+                <YCoordinate value="1"/>
+                <AnchorPoint value="11"/>
+              </MarkAnchor>
+            </MarkRecord>
+            <MarkRecord index="1">
+              <Class value="0"/>
+              <MarkAnchor Format="2">
+                <XCoordinate value="1"/>
+                <YCoordinate value="1"/>
+                <AnchorPoint value="11"/>
+              </MarkAnchor>
+            </MarkRecord>
+            <MarkRecord index="2">
+              <Class value="0"/>
+              <MarkAnchor Format="2">
+                <XCoordinate value="2"/>
+                <YCoordinate value="2"/>
+                <AnchorPoint value="22"/>
+              </MarkAnchor>
+            </MarkRecord>
+            <MarkRecord index="3">
+              <Class value="1"/>
+              <MarkAnchor Format="2">
+                <XCoordinate value="3"/>
+                <YCoordinate value="3"/>
+                <AnchorPoint value="33"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </Mark1Array>
+          <Mark2Array>
+            <!-- Mark2Count=6 -->
+            <Mark2Record index="0">
+              <Mark2Anchor index="0" Format="1">
+                <XCoordinate value="500"/>
+                <YCoordinate value="200"/>
+              </Mark2Anchor>
+              <Mark2Anchor index="1" Format="1">
+                <XCoordinate value="500"/>
+                <YCoordinate value="-80"/>
+              </Mark2Anchor>
+            </Mark2Record>
+            <Mark2Record index="1">
+              <Mark2Anchor index="0" Format="1">
+                <XCoordinate value="500"/>
+                <YCoordinate value="200"/>
+              </Mark2Anchor>
+              <Mark2Anchor index="1" Format="1">
+                <XCoordinate value="500"/>
+                <YCoordinate value="-80"/>
+              </Mark2Anchor>
+            </Mark2Record>
+            <Mark2Record index="2">
+              <Mark2Anchor index="0" Format="1">
+                <XCoordinate value="500"/>
+                <YCoordinate value="200"/>
+              </Mark2Anchor>
+              <Mark2Anchor index="1" empty="1"/>
+            </Mark2Record>
+            <Mark2Record index="3">
+              <Mark2Anchor index="0" Format="1">
+                <XCoordinate value="500"/>
+                <YCoordinate value="200"/>
+              </Mark2Anchor>
+              <Mark2Anchor index="1" Format="1">
+                <XCoordinate value="500"/>
+                <YCoordinate value="-80"/>
+              </Mark2Anchor>
+            </Mark2Record>
+            <Mark2Record index="4">
+              <Mark2Anchor index="0" Format="1">
+                <XCoordinate value="500"/>
+                <YCoordinate value="200"/>
+              </Mark2Anchor>
+              <Mark2Anchor index="1" Format="1">
+                <XCoordinate value="500"/>
+                <YCoordinate value="-80"/>
+              </Mark2Anchor>
+            </Mark2Record>
+            <Mark2Record index="5">
+              <Mark2Anchor index="0" Format="1">
+                <XCoordinate value="500"/>
+                <YCoordinate value="200"/>
+              </Mark2Anchor>
+              <Mark2Anchor index="1" empty="1"/>
+            </Mark2Record>
+          </Mark2Array>
+        </MarkMarkPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/GPOS_8.fea b/Tests/feaLib/data/GPOS_8.fea
new file mode 100644
index 0000000..2351ee2
--- /dev/null
+++ b/Tests/feaLib/data/GPOS_8.fea
@@ -0,0 +1,22 @@
+languagesystem DFLT dflt;
+
+lookup ChainedSinglePos {
+    pos A one' 1 two' 2 one' -1 two' -2;
+} ChainedSinglePos;
+
+lookup L1 {
+    pos one 100;
+} L1;
+
+lookup L2 {
+    pos two 200;
+} L2;
+
+lookup ChainedContextualPos {
+    pos [A a] [B b] I' lookup L1 N' lookup L2 P' [Y y] [Z z];
+} ChainedContextualPos;
+
+feature test {
+    lookup ChainedSinglePos;
+    lookup ChainedContextualPos;
+} test;
diff --git a/Tests/feaLib/data/GPOS_8.ttx b/Tests/feaLib/data/GPOS_8.ttx
new file mode 100644
index 0000000..86f9756
--- /dev/null
+++ b/Tests/feaLib/data/GPOS_8.ttx
@@ -0,0 +1,176 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=2 -->
+          <LookupListIndex index="0" value="0"/>
+          <LookupListIndex index="1" value="5"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=6 -->
+      <Lookup index="0">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="A"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=4 -->
+          <InputCoverage index="0">
+            <Glyph value="one"/>
+          </InputCoverage>
+          <InputCoverage index="1">
+            <Glyph value="two"/>
+          </InputCoverage>
+          <InputCoverage index="2">
+            <Glyph value="one"/>
+          </InputCoverage>
+          <InputCoverage index="3">
+            <Glyph value="two"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- PosCount=4 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </PosLookupRecord>
+          <PosLookupRecord index="1">
+            <SequenceIndex value="1"/>
+            <LookupListIndex value="1"/>
+          </PosLookupRecord>
+          <PosLookupRecord index="2">
+            <SequenceIndex value="2"/>
+            <LookupListIndex value="2"/>
+          </PosLookupRecord>
+          <PosLookupRecord index="3">
+            <SequenceIndex value="3"/>
+            <LookupListIndex value="2"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="2">
+          <Coverage>
+            <Glyph value="one"/>
+            <Glyph value="two"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <!-- ValueCount=2 -->
+          <Value index="0" XAdvance="1"/>
+          <Value index="1" XAdvance="2"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="2">
+          <Coverage>
+            <Glyph value="one"/>
+            <Glyph value="two"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <!-- ValueCount=2 -->
+          <Value index="0" XAdvance="-1"/>
+          <Value index="1" XAdvance="-2"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="one"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="100"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="two"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="200"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="5">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="3">
+          <!-- BacktrackGlyphCount=2 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="B"/>
+            <Glyph value="b"/>
+          </BacktrackCoverage>
+          <BacktrackCoverage index="1">
+            <Glyph value="A"/>
+            <Glyph value="a"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=3 -->
+          <InputCoverage index="0">
+            <Glyph value="I"/>
+          </InputCoverage>
+          <InputCoverage index="1">
+            <Glyph value="N"/>
+          </InputCoverage>
+          <InputCoverage index="2">
+            <Glyph value="P"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=2 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="Y"/>
+            <Glyph value="y"/>
+          </LookAheadCoverage>
+          <LookAheadCoverage index="1">
+            <Glyph value="Z"/>
+            <Glyph value="z"/>
+          </LookAheadCoverage>
+          <!-- PosCount=2 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="3"/>
+          </PosLookupRecord>
+          <PosLookupRecord index="1">
+            <SequenceIndex value="1"/>
+            <LookupListIndex value="4"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/GSUB_2.fea b/Tests/feaLib/data/GSUB_2.fea
new file mode 100644
index 0000000..d2a3cb1
--- /dev/null
+++ b/Tests/feaLib/data/GSUB_2.fea
@@ -0,0 +1,14 @@
+feature f1 {
+    sub c_t by c t;
+    sub f_i by f i;
+    sub f_f_i by f f i;
+} f1;
+
+
+# Even if it has exactly the same content as feature f1,
+# the lookup should not be shared.
+feature f2 {
+    sub c_t by c t;
+    sub f_i by f i;
+    sub f_f_i by f f i;
+} f2;
diff --git a/Tests/feaLib/data/GSUB_2.ttx b/Tests/feaLib/data/GSUB_2.ttx
new file mode 100644
index 0000000..b91c20f
--- /dev/null
+++ b/Tests/feaLib/data/GSUB_2.ttx
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=2 -->
+            <FeatureIndex index="0" value="0"/>
+            <FeatureIndex index="1" value="1"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=2 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="f1  "/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="f2  "/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0">
+          <Substitution in="c_t" out="c,t"/>
+          <Substitution in="f_f_i" out="f,f,i"/>
+          <Substitution in="f_i" out="f,i"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0">
+          <Substitution in="c_t" out="c,t"/>
+          <Substitution in="f_f_i" out="f,f,i"/>
+          <Substitution in="f_i" out="f,i"/>
+        </MultipleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/GSUB_3.fea b/Tests/feaLib/data/GSUB_3.fea
new file mode 100644
index 0000000..e128305
--- /dev/null
+++ b/Tests/feaLib/data/GSUB_3.fea
@@ -0,0 +1,14 @@
+feature f1 {
+    sub A from [A.alt1 A.alt2];
+    sub B from [B.alt1 B.alt2 B.alt3];
+    sub C from [C.alt1];
+} f1;
+
+
+# Even if it has exactly the same content as feature f1,
+# the lookup should not be shared.
+feature f2 {
+    sub A from [A.alt1 A.alt2];
+    sub B from [B.alt1 B.alt2 B.alt3];
+    sub C from [C.alt1];
+} f2;
diff --git a/Tests/feaLib/data/GSUB_3.ttx b/Tests/feaLib/data/GSUB_3.ttx
new file mode 100644
index 0000000..77cb4c6
--- /dev/null
+++ b/Tests/feaLib/data/GSUB_3.ttx
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=2 -->
+            <FeatureIndex index="0" value="0"/>
+            <FeatureIndex index="1" value="1"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=2 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="f1  "/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="f2  "/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="3"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <AlternateSubst index="0">
+          <AlternateSet glyph="A">
+            <Alternate glyph="A.alt1"/>
+            <Alternate glyph="A.alt2"/>
+          </AlternateSet>
+          <AlternateSet glyph="B">
+            <Alternate glyph="B.alt1"/>
+            <Alternate glyph="B.alt2"/>
+            <Alternate glyph="B.alt3"/>
+          </AlternateSet>
+          <AlternateSet glyph="C">
+            <Alternate glyph="C.alt1"/>
+          </AlternateSet>
+        </AlternateSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="3"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <AlternateSubst index="0">
+          <AlternateSet glyph="A">
+            <Alternate glyph="A.alt1"/>
+            <Alternate glyph="A.alt2"/>
+          </AlternateSet>
+          <AlternateSet glyph="B">
+            <Alternate glyph="B.alt1"/>
+            <Alternate glyph="B.alt2"/>
+            <Alternate glyph="B.alt3"/>
+          </AlternateSet>
+          <AlternateSet glyph="C">
+            <Alternate glyph="C.alt1"/>
+          </AlternateSet>
+        </AlternateSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/GSUB_6.fea b/Tests/feaLib/data/GSUB_6.fea
new file mode 100644
index 0000000..82fdac2
--- /dev/null
+++ b/Tests/feaLib/data/GSUB_6.fea
@@ -0,0 +1,28 @@
+lookup ChainedSingleSubst {
+    sub [one two] three A' by A.sc;
+    sub [B-D]' seven [eight nine] by [B.sc-D.sc];
+} ChainedSingleSubst;
+
+lookup ChainedMultipleSubst {
+    sub [A-C a-c] [D d] E c_t' V [W w] [X-Z x-z] by c t;
+} ChainedMultipleSubst;
+
+lookup ChainedAlternateSubst {
+    sub [space comma semicolon] e' from [e e.begin];
+} ChainedAlternateSubst;
+
+lookup ChainedLigatureSubst {
+    sub A [C c]' [T t]' Z by c_t;
+} ChainedLigatureSubst;
+
+lookup ChainedContextualSubst {
+    sub A D E c_t' lookup ChainedMultipleSubst V W X;
+} ChainedContextualSubst;
+
+feature test {
+    lookup ChainedSingleSubst;
+    lookup ChainedMultipleSubst;
+    lookup ChainedAlternateSubst;
+    lookup ChainedLigatureSubst;
+    lookup ChainedContextualSubst;
+} test;
diff --git a/Tests/feaLib/data/GSUB_6.ttx b/Tests/feaLib/data/GSUB_6.ttx
new file mode 100644
index 0000000..f32e47d
--- /dev/null
+++ b/Tests/feaLib/data/GSUB_6.ttx
@@ -0,0 +1,267 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=5 -->
+          <LookupListIndex index="0" value="0"/>
+          <LookupListIndex index="1" value="2"/>
+          <LookupListIndex index="2" value="4"/>
+          <LookupListIndex index="3" value="6"/>
+          <LookupListIndex index="4" value="8"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=9 -->
+      <Lookup index="0">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=2 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=2 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="three"/>
+          </BacktrackCoverage>
+          <BacktrackCoverage index="1">
+            <Glyph value="one"/>
+            <Glyph value="two"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="A"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+        <ChainContextSubst index="1" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="B"/>
+            <Glyph value="C"/>
+            <Glyph value="D"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=2 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="seven"/>
+          </LookAheadCoverage>
+          <LookAheadCoverage index="1">
+            <Glyph value="eight"/>
+            <Glyph value="nine"/>
+          </LookAheadCoverage>
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="A" out="A.sc"/>
+          <Substitution in="B" out="B.sc"/>
+          <Substitution in="C" out="C.sc"/>
+          <Substitution in="D" out="D.sc"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=3 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="E"/>
+          </BacktrackCoverage>
+          <BacktrackCoverage index="1">
+            <Glyph value="D"/>
+            <Glyph value="d"/>
+          </BacktrackCoverage>
+          <BacktrackCoverage index="2">
+            <Glyph value="A"/>
+            <Glyph value="B"/>
+            <Glyph value="C"/>
+            <Glyph value="a"/>
+            <Glyph value="b"/>
+            <Glyph value="c"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="c_t"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=3 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="V"/>
+          </LookAheadCoverage>
+          <LookAheadCoverage index="1">
+            <Glyph value="W"/>
+            <Glyph value="w"/>
+          </LookAheadCoverage>
+          <LookAheadCoverage index="2">
+            <Glyph value="X"/>
+            <Glyph value="Y"/>
+            <Glyph value="Z"/>
+            <Glyph value="x"/>
+            <Glyph value="y"/>
+            <Glyph value="z"/>
+          </LookAheadCoverage>
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="3"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0">
+          <Substitution in="c_t" out="c,t"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="space"/>
+            <Glyph value="semicolon"/>
+            <Glyph value="comma"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="e"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="5"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+      <Lookup index="5">
+        <LookupType value="3"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <AlternateSubst index="0">
+          <AlternateSet glyph="e">
+            <Alternate glyph="e"/>
+            <Alternate glyph="e.begin"/>
+          </AlternateSet>
+        </AlternateSubst>
+      </Lookup>
+      <Lookup index="6">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="A"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=2 -->
+          <InputCoverage index="0">
+            <Glyph value="C"/>
+            <Glyph value="c"/>
+          </InputCoverage>
+          <InputCoverage index="1">
+            <Glyph value="T"/>
+            <Glyph value="t"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="Z"/>
+          </LookAheadCoverage>
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="7"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+      <Lookup index="7">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="C">
+            <Ligature components="T" glyph="c_t"/>
+            <Ligature components="t" glyph="c_t"/>
+          </LigatureSet>
+          <LigatureSet glyph="c">
+            <Ligature components="T" glyph="c_t"/>
+            <Ligature components="t" glyph="c_t"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="8">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=3 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="E"/>
+          </BacktrackCoverage>
+          <BacktrackCoverage index="1">
+            <Glyph value="D"/>
+          </BacktrackCoverage>
+          <BacktrackCoverage index="2">
+            <Glyph value="A"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="c_t"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=3 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="V"/>
+          </LookAheadCoverage>
+          <LookAheadCoverage index="1">
+            <Glyph value="W"/>
+          </LookAheadCoverage>
+          <LookAheadCoverage index="2">
+            <Glyph value="X"/>
+          </LookAheadCoverage>
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="2"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/GSUB_8.fea b/Tests/feaLib/data/GSUB_8.fea
new file mode 100644
index 0000000..62c7bee
--- /dev/null
+++ b/Tests/feaLib/data/GSUB_8.fea
@@ -0,0 +1,11 @@
+languagesystem DFLT dflt;
+
+feature test {
+    rsub [a A] [b B] [c C] q' [d D] [e E] [f F] by Q;
+    rsub [a A] [b B] [c C] [s-z]' [d D] [e E] [f F] by [S-Z];
+
+    # Having no context for a reverse chaining substitution rule
+    # is a little degenerate (we define a chain without linking it
+    # to anything else), but makeotf accepts this.
+    rsub p by P;
+} test;
diff --git a/Tests/feaLib/data/GSUB_8.ttx b/Tests/feaLib/data/GSUB_8.ttx
new file mode 100644
index 0000000..1b10d25
--- /dev/null
+++ b/Tests/feaLib/data/GSUB_8.ttx
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=3 -->
+        <ReverseChainSingleSubst index="0" Format="1">
+          <Coverage>
+            <Glyph value="q"/>
+          </Coverage>
+          <!-- BacktrackGlyphCount=3 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="C"/>
+            <Glyph value="c"/>
+          </BacktrackCoverage>
+          <BacktrackCoverage index="1">
+            <Glyph value="B"/>
+            <Glyph value="b"/>
+          </BacktrackCoverage>
+          <BacktrackCoverage index="2">
+            <Glyph value="A"/>
+            <Glyph value="a"/>
+          </BacktrackCoverage>
+          <!-- LookAheadGlyphCount=3 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="D"/>
+            <Glyph value="d"/>
+          </LookAheadCoverage>
+          <LookAheadCoverage index="1">
+            <Glyph value="E"/>
+            <Glyph value="e"/>
+          </LookAheadCoverage>
+          <LookAheadCoverage index="2">
+            <Glyph value="F"/>
+            <Glyph value="f"/>
+          </LookAheadCoverage>
+          <!-- GlyphCount=1 -->
+          <Substitute index="0" value="Q"/>
+        </ReverseChainSingleSubst>
+        <ReverseChainSingleSubst index="1" Format="1">
+          <Coverage>
+            <Glyph value="s"/>
+            <Glyph value="t"/>
+            <Glyph value="u"/>
+            <Glyph value="v"/>
+            <Glyph value="w"/>
+            <Glyph value="x"/>
+            <Glyph value="y"/>
+            <Glyph value="z"/>
+          </Coverage>
+          <!-- BacktrackGlyphCount=3 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="C"/>
+            <Glyph value="c"/>
+          </BacktrackCoverage>
+          <BacktrackCoverage index="1">
+            <Glyph value="B"/>
+            <Glyph value="b"/>
+          </BacktrackCoverage>
+          <BacktrackCoverage index="2">
+            <Glyph value="A"/>
+            <Glyph value="a"/>
+          </BacktrackCoverage>
+          <!-- LookAheadGlyphCount=3 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="D"/>
+            <Glyph value="d"/>
+          </LookAheadCoverage>
+          <LookAheadCoverage index="1">
+            <Glyph value="E"/>
+            <Glyph value="e"/>
+          </LookAheadCoverage>
+          <LookAheadCoverage index="2">
+            <Glyph value="F"/>
+            <Glyph value="f"/>
+          </LookAheadCoverage>
+          <!-- GlyphCount=8 -->
+          <Substitute index="0" value="S"/>
+          <Substitute index="1" value="T"/>
+          <Substitute index="2" value="U"/>
+          <Substitute index="3" value="V"/>
+          <Substitute index="4" value="W"/>
+          <Substitute index="5" value="X"/>
+          <Substitute index="6" value="Y"/>
+          <Substitute index="7" value="Z"/>
+        </ReverseChainSingleSubst>
+        <ReverseChainSingleSubst index="2" Format="1">
+          <Coverage>
+            <Glyph value="p"/>
+          </Coverage>
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- GlyphCount=1 -->
+          <Substitute index="0" value="P"/>
+        </ReverseChainSingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/GlyphClassDef.fea b/Tests/feaLib/data/GlyphClassDef.fea
new file mode 100644
index 0000000..8e53536
--- /dev/null
+++ b/Tests/feaLib/data/GlyphClassDef.fea
@@ -0,0 +1,5 @@
+table GDEF {
+    GlyphClassDef [a], [b], [c], [d];
+    GlyphClassDef [e], [f], [g], [h];
+    GlyphClassDef [i], [j], [k], [l];
+} GDEF;
diff --git a/Tests/feaLib/data/GlyphClassDef.ttx b/Tests/feaLib/data/GlyphClassDef.ttx
new file mode 100644
index 0000000..d7d8b90
--- /dev/null
+++ b/Tests/feaLib/data/GlyphClassDef.ttx
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef>
+      <ClassDef glyph="a" class="1"/>
+      <ClassDef glyph="b" class="2"/>
+      <ClassDef glyph="c" class="3"/>
+      <ClassDef glyph="d" class="4"/>
+      <ClassDef glyph="e" class="1"/>
+      <ClassDef glyph="f" class="2"/>
+      <ClassDef glyph="g" class="3"/>
+      <ClassDef glyph="h" class="4"/>
+      <ClassDef glyph="i" class="1"/>
+      <ClassDef glyph="j" class="2"/>
+      <ClassDef glyph="k" class="3"/>
+      <ClassDef glyph="l" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/feaLib/data/LigatureCaretByIndex.fea b/Tests/feaLib/data/LigatureCaretByIndex.fea
new file mode 100644
index 0000000..1968172
--- /dev/null
+++ b/Tests/feaLib/data/LigatureCaretByIndex.fea
@@ -0,0 +1,10 @@
+table GDEF {
+    LigatureCaretByIndex [c_t s_t] 11;
+
+    # The OpenType Feature File specification does not define what should
+    # happen when there are multiple LigatureCaretByIndex statements for
+    # the same glyph. Our behavior matches that of Adobe makeotf v2.0.90.
+    # https://github.com/adobe-type-tools/afdko/issues/95
+    LigatureCaretByIndex o_f_f_i 66 33;
+    LigatureCaretByIndex o_f_f_i 55;
+} GDEF;
diff --git a/Tests/feaLib/data/LigatureCaretByIndex.ttx b/Tests/feaLib/data/LigatureCaretByIndex.ttx
new file mode 100644
index 0000000..a758077
--- /dev/null
+++ b/Tests/feaLib/data/LigatureCaretByIndex.ttx
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <LigCaretList>
+      <Coverage>
+        <Glyph value="c_t"/>
+        <Glyph value="o_f_f_i"/>
+        <Glyph value="s_t"/>
+      </Coverage>
+      <!-- LigGlyphCount=3 -->
+      <LigGlyph index="0">
+        <!-- CaretCount=1 -->
+        <CaretValue index="0" Format="2">
+          <CaretValuePoint value="11"/>
+        </CaretValue>
+      </LigGlyph>
+      <LigGlyph index="1">
+        <!-- CaretCount=3 -->
+        <CaretValue index="0" Format="2">
+          <CaretValuePoint value="33"/>
+        </CaretValue>
+        <CaretValue index="1" Format="2">
+          <CaretValuePoint value="55"/>
+        </CaretValue>
+        <CaretValue index="2" Format="2">
+          <CaretValuePoint value="66"/>
+        </CaretValue>
+      </LigGlyph>
+      <LigGlyph index="2">
+        <!-- CaretCount=1 -->
+        <CaretValue index="0" Format="2">
+          <CaretValuePoint value="11"/>
+        </CaretValue>
+      </LigGlyph>
+    </LigCaretList>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/feaLib/data/LigatureCaretByPos.fea b/Tests/feaLib/data/LigatureCaretByPos.fea
new file mode 100644
index 0000000..a8ccf6f
--- /dev/null
+++ b/Tests/feaLib/data/LigatureCaretByPos.fea
@@ -0,0 +1,10 @@
+table GDEF {
+    LigatureCaretByPos [c_h c_k] 500;
+
+    # The OpenType Feature File specification does not define what should
+    # happen when there are multiple LigatureCaretByPos statements for
+    # the same glyph. Our behavior matches that of Adobe makeotf v2.0.90.
+    # https://github.com/adobe-type-tools/afdko/issues/95
+    LigatureCaretByPos o_f_f_i 700 300;
+    LigatureCaretByPos o_f_f_i 900;
+} GDEF;
diff --git a/Tests/feaLib/data/LigatureCaretByPos.ttx b/Tests/feaLib/data/LigatureCaretByPos.ttx
new file mode 100644
index 0000000..911db6b
--- /dev/null
+++ b/Tests/feaLib/data/LigatureCaretByPos.ttx
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <LigCaretList>
+      <Coverage>
+        <Glyph value="c_h"/>
+        <Glyph value="c_k"/>
+        <Glyph value="o_f_f_i"/>
+      </Coverage>
+      <!-- LigGlyphCount=3 -->
+      <LigGlyph index="0">
+        <!-- CaretCount=1 -->
+        <CaretValue index="0" Format="1">
+          <Coordinate value="500"/>
+        </CaretValue>
+      </LigGlyph>
+      <LigGlyph index="1">
+        <!-- CaretCount=1 -->
+        <CaretValue index="0" Format="1">
+          <Coordinate value="500"/>
+        </CaretValue>
+      </LigGlyph>
+      <LigGlyph index="2">
+        <!-- CaretCount=3 -->
+        <CaretValue index="0" Format="1">
+          <Coordinate value="300"/>
+        </CaretValue>
+        <CaretValue index="1" Format="1">
+          <Coordinate value="700"/>
+        </CaretValue>
+        <CaretValue index="2" Format="1">
+          <Coordinate value="900"/>
+        </CaretValue>
+      </LigGlyph>
+    </LigCaretList>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/feaLib/data/PairPosSubtable.fea b/Tests/feaLib/data/PairPosSubtable.fea
new file mode 100644
index 0000000..021f3cc
--- /dev/null
+++ b/Tests/feaLib/data/PairPosSubtable.fea
@@ -0,0 +1,20 @@
+languagesystem DFLT dflt;
+languagesystem latn dflt;
+
+lookup kernlookup {
+    pos A V -34;
+    subtable;
+    @group1 = [b o];
+    @group2 = [c d];
+    pos @group1 @group2 -12;
+} kernlookup;
+
+feature kern {
+ script DFLT;
+ language dflt;
+ lookup kernlookup;
+
+ script latn;
+ language dflt;
+ lookup kernlookup;
+} kern;
diff --git a/Tests/feaLib/data/PairPosSubtable.ttx b/Tests/feaLib/data/PairPosSubtable.ttx
new file mode 100644
index 0000000..13a77aa
--- /dev/null
+++ b/Tests/feaLib/data/PairPosSubtable.ttx
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=2 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="1">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="1"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=2 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=2 -->
+          <LookupListIndex index="0" value="0"/>
+          <LookupListIndex index="1" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=2 -->
+        <PairPos index="0" Format="1">
+          <Coverage>
+            <Glyph value="A"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="V"/>
+              <Value1 XAdvance="-34"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+        <PairPos index="1" Format="2">
+          <Coverage>
+            <Glyph value="b"/>
+            <Glyph value="o"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1>
+          </ClassDef1>
+          <ClassDef2>
+            <ClassDef glyph="c" class="1"/>
+            <ClassDef glyph="d" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=1 -->
+          <!-- Class2Count=2 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="-12"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/ZeroValue_ChainSinglePos_horizontal.fea b/Tests/feaLib/data/ZeroValue_ChainSinglePos_horizontal.fea
new file mode 100644
index 0000000..480fc76
--- /dev/null
+++ b/Tests/feaLib/data/ZeroValue_ChainSinglePos_horizontal.fea
@@ -0,0 +1,9 @@
+# For contextual positioning statements with in-line single positioning rules,
+# zeroes should get compiled to ValueRecord format 0.
+# https://github.com/fonttools/fonttools/issues/633
+
+# Zero value in a horizontal context.
+feature kern {
+    pos A G' 0 A;          # value format A
+    pos B G' <0 0 0 0> B;  # value format B
+} kern;
diff --git a/Tests/feaLib/data/ZeroValue_ChainSinglePos_horizontal.ttx b/Tests/feaLib/data/ZeroValue_ChainSinglePos_horizontal.ttx
new file mode 100644
index 0000000..ce76370
--- /dev/null
+++ b/Tests/feaLib/data/ZeroValue_ChainSinglePos_horizontal.ttx
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.7">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=2 -->
+        <ChainContextPos index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="A"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="G"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="A"/>
+          </LookAheadCoverage>
+          <!-- PosCount=1 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+        <ChainContextPos index="1" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="B"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="G"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="B"/>
+          </LookAheadCoverage>
+          <!-- PosCount=1 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="G"/>
+          </Coverage>
+          <ValueFormat value="0"/>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/ZeroValue_ChainSinglePos_vertical.fea b/Tests/feaLib/data/ZeroValue_ChainSinglePos_vertical.fea
new file mode 100644
index 0000000..a9fb7d1
--- /dev/null
+++ b/Tests/feaLib/data/ZeroValue_ChainSinglePos_vertical.fea
@@ -0,0 +1,9 @@
+# For contextual positioning statements with in-line single positioning rules,
+# zeroes should get compiled to ValueRecord format 0.
+# https://github.com/fonttools/fonttools/issues/633
+
+# Zero value in a vertical context.
+feature vkrn {
+    pos A G' 0 A;          # value format A
+    pos B G' <0 0 0 0> B;  # value format B
+} vkrn;
diff --git a/Tests/feaLib/data/ZeroValue_ChainSinglePos_vertical.ttx b/Tests/feaLib/data/ZeroValue_ChainSinglePos_vertical.ttx
new file mode 100644
index 0000000..e1cb5d6
--- /dev/null
+++ b/Tests/feaLib/data/ZeroValue_ChainSinglePos_vertical.ttx
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.7">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="vkrn"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=2 -->
+        <ChainContextPos index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="A"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="G"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="A"/>
+          </LookAheadCoverage>
+          <!-- PosCount=1 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+        <ChainContextPos index="1" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="B"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="G"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="B"/>
+          </LookAheadCoverage>
+          <!-- PosCount=1 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="G"/>
+          </Coverage>
+          <ValueFormat value="0"/>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/ZeroValue_PairPos_horizontal.fea b/Tests/feaLib/data/ZeroValue_PairPos_horizontal.fea
new file mode 100644
index 0000000..b5e48f4
--- /dev/null
+++ b/Tests/feaLib/data/ZeroValue_PairPos_horizontal.fea
@@ -0,0 +1,9 @@
+# For PairPos statements in horizontal compilation contexts,
+# zero values should get compiled to ValueRecord format 4.
+# https://github.com/fonttools/fonttools/issues/633
+feature kern {
+  pos A 0 A 0;
+  pos A 0 B <0 0 0 0>;
+  pos B <0 0 0 0> A 0;
+  pos B <0 0 0 0> B <0 0 0 0>;
+} kern;
diff --git a/Tests/feaLib/data/ZeroValue_PairPos_horizontal.ttx b/Tests/feaLib/data/ZeroValue_PairPos_horizontal.ttx
new file mode 100644
index 0000000..6537852
--- /dev/null
+++ b/Tests/feaLib/data/ZeroValue_PairPos_horizontal.ttx
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage>
+            <Glyph value="A"/>
+            <Glyph value="B"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="4"/>
+          <!-- PairSetCount=2 -->
+          <PairSet index="0">
+            <!-- PairValueCount=2 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="A"/>
+              <Value1 XAdvance="0"/>
+              <Value2 XAdvance="0"/>
+            </PairValueRecord>
+            <PairValueRecord index="1">
+              <SecondGlyph value="B"/>
+              <Value1 XAdvance="0"/>
+              <Value2 XAdvance="0"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="1">
+            <!-- PairValueCount=2 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="A"/>
+              <Value1 XAdvance="0"/>
+              <Value2 XAdvance="0"/>
+            </PairValueRecord>
+            <PairValueRecord index="1">
+              <SecondGlyph value="B"/>
+              <Value1 XAdvance="0"/>
+              <Value2 XAdvance="0"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/ZeroValue_PairPos_vertical.fea b/Tests/feaLib/data/ZeroValue_PairPos_vertical.fea
new file mode 100644
index 0000000..1ab33a9
--- /dev/null
+++ b/Tests/feaLib/data/ZeroValue_PairPos_vertical.fea
@@ -0,0 +1,9 @@
+# For PairPos statements in vertical compilation contexts,
+# zero values should get compiled to ValueRecord format 8.
+# https://github.com/fonttools/fonttools/issues/633
+feature vkrn {
+  pos A 0 A 0;
+  pos A 0 B <0 0 0 0>;
+  pos B <0 0 0 0> A 0;
+  pos B <0 0 0 0> B <0 0 0 0>;
+} vkrn;
diff --git a/Tests/feaLib/data/ZeroValue_PairPos_vertical.ttx b/Tests/feaLib/data/ZeroValue_PairPos_vertical.ttx
new file mode 100644
index 0000000..774bae0
--- /dev/null
+++ b/Tests/feaLib/data/ZeroValue_PairPos_vertical.ttx
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="vkrn"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage>
+            <Glyph value="A"/>
+            <Glyph value="B"/>
+          </Coverage>
+          <ValueFormat1 value="8"/>
+          <ValueFormat2 value="8"/>
+          <!-- PairSetCount=2 -->
+          <PairSet index="0">
+            <!-- PairValueCount=2 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="A"/>
+              <Value1 YAdvance="0"/>
+              <Value2 YAdvance="0"/>
+            </PairValueRecord>
+            <PairValueRecord index="1">
+              <SecondGlyph value="B"/>
+              <Value1 YAdvance="0"/>
+              <Value2 YAdvance="0"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="1">
+            <!-- PairValueCount=2 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="A"/>
+              <Value1 YAdvance="0"/>
+              <Value2 YAdvance="0"/>
+            </PairValueRecord>
+            <PairValueRecord index="1">
+              <SecondGlyph value="B"/>
+              <Value1 YAdvance="0"/>
+              <Value2 YAdvance="0"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/ZeroValue_SinglePos_horizontal.fea b/Tests/feaLib/data/ZeroValue_SinglePos_horizontal.fea
new file mode 100644
index 0000000..44fe683
--- /dev/null
+++ b/Tests/feaLib/data/ZeroValue_SinglePos_horizontal.fea
@@ -0,0 +1,8 @@
+# For SinglePos statements, zeroes should get compiled to ValueRecord format 0.
+# https://github.com/fonttools/fonttools/issues/633
+
+# Zero value in a horizontal context.
+feature kern {
+    pos A 0;          # format A
+    pos B <0 0 0 0>;  # format B
+} kern;
diff --git a/Tests/feaLib/data/ZeroValue_SinglePos_horizontal.ttx b/Tests/feaLib/data/ZeroValue_SinglePos_horizontal.ttx
new file mode 100644
index 0000000..d5fe639
--- /dev/null
+++ b/Tests/feaLib/data/ZeroValue_SinglePos_horizontal.ttx
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="A"/>
+            <Glyph value="B"/>
+          </Coverage>
+          <ValueFormat value="0"/>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/ZeroValue_SinglePos_vertical.fea b/Tests/feaLib/data/ZeroValue_SinglePos_vertical.fea
new file mode 100644
index 0000000..3ee9a1b
--- /dev/null
+++ b/Tests/feaLib/data/ZeroValue_SinglePos_vertical.fea
@@ -0,0 +1,8 @@
+# For SinglePos statements, zeroes should get compiled to ValueRecord format 0.
+# https://github.com/fonttools/fonttools/issues/633
+
+# Zero value in a vertical context.
+feature vkrn {
+    pos A 0;          # format A
+    pos B <0 0 0 0>;  # format B
+} vkrn;
diff --git a/Tests/feaLib/data/ZeroValue_SinglePos_vertical.ttx b/Tests/feaLib/data/ZeroValue_SinglePos_vertical.ttx
new file mode 100644
index 0000000..3879c36
--- /dev/null
+++ b/Tests/feaLib/data/ZeroValue_SinglePos_vertical.ttx
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="vkrn"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="A"/>
+            <Glyph value="B"/>
+          </Coverage>
+          <ValueFormat value="0"/>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/baseClass.fea b/Tests/feaLib/data/baseClass.fea
new file mode 100644
index 0000000..2a370c1
--- /dev/null
+++ b/Tests/feaLib/data/baseClass.fea
@@ -0,0 +1,10 @@
+languagesystem DFLT dflt;
+
+markClass [acute] <anchor 350 0> @TOP_MARKS;
+
+feature test {
+    pos base [a] <anchor 500 500> mark @TOP_MARKS;
+    pos base b <anchor 500 750> mark @TOP_MARKS;
+} test;
+
+
diff --git a/Tests/feaLib/data/baseClass.feax b/Tests/feaLib/data/baseClass.feax
new file mode 100644
index 0000000..2950e30
--- /dev/null
+++ b/Tests/feaLib/data/baseClass.feax
@@ -0,0 +1,10 @@
+languagesystem DFLT dflt;
+
+markClass [acute] <anchor 350 0> @TOP_MARKS;
+baseClass [a] <anchor 500 500> @BASE_TOPS;
+baseClass b <anchor 500 750> @BASE_TOPS;
+
+feature test {
+    pos base @BASE_TOPS mark @TOP_MARKS;
+} test;
+
diff --git a/Tests/feaLib/data/bug453.fea b/Tests/feaLib/data/bug453.fea
new file mode 100644
index 0000000..fc5072e
--- /dev/null
+++ b/Tests/feaLib/data/bug453.fea
@@ -0,0 +1,11 @@
+# https://github.com/behdad/fonttools/issues/453
+feature mark {
+    lookup mark1 {
+        markClass [acute] <anchor 150 -10> @TOP_MARKS;
+        pos base [e] <anchor 250 450> mark @TOP_MARKS;
+    } mark1;
+    lookup mark2 {
+        markClass [acute] <anchor 150 -20> @TOP_MARKS_2;
+        pos base [e] <anchor 250 450> mark @TOP_MARKS_2;
+    } mark2;
+} mark;
diff --git a/Tests/feaLib/data/bug453.ttx b/Tests/feaLib/data/bug453.ttx
new file mode 100644
index 0000000..748e13d
--- /dev/null
+++ b/Tests/feaLib/data/bug453.ttx
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef>
+      <ClassDef glyph="acute" class="3"/>
+      <ClassDef glyph="e" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="mark"/>
+        <Feature>
+          <!-- LookupCount=2 -->
+          <LookupListIndex index="0" value="0"/>
+          <LookupListIndex index="1" value="1"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkBasePos index="0" Format="1">
+          <MarkCoverage>
+            <Glyph value="acute"/>
+          </MarkCoverage>
+          <BaseCoverage>
+            <Glyph value="e"/>
+          </BaseCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="150"/>
+                <YCoordinate value="-10"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=1 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="250"/>
+                <YCoordinate value="450"/>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkBasePos index="0" Format="1">
+          <MarkCoverage>
+            <Glyph value="acute"/>
+          </MarkCoverage>
+          <BaseCoverage>
+            <Glyph value="e"/>
+          </BaseCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="150"/>
+                <YCoordinate value="-20"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=1 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="250"/>
+                <YCoordinate value="450"/>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/bug457.fea b/Tests/feaLib/data/bug457.fea
new file mode 100644
index 0000000..9ab083b
--- /dev/null
+++ b/Tests/feaLib/data/bug457.fea
@@ -0,0 +1,5 @@
+@group = [A \sub \lookup \feature \by \table];
+
+feature liga {
+    sub @group by G;
+} liga;
diff --git a/Tests/feaLib/data/bug457.ttx b/Tests/feaLib/data/bug457.ttx
new file mode 100644
index 0000000..1967f1e
--- /dev/null
+++ b/Tests/feaLib/data/bug457.ttx
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="liga"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="A" out="G"/>
+          <Substitution in="by" out="G"/>
+          <Substitution in="feature" out="G"/>
+          <Substitution in="lookup" out="G"/>
+          <Substitution in="sub" out="G"/>
+          <Substitution in="table" out="G"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/bug463.fea b/Tests/feaLib/data/bug463.fea
new file mode 100644
index 0000000..e7e21af
--- /dev/null
+++ b/Tests/feaLib/data/bug463.fea
@@ -0,0 +1,6 @@
+# https://github.com/behdad/fonttools/issues/463
+feature ordn {
+    @DIGIT = [zero one two three four five six seven eight nine];
+    sub @DIGIT [A a]' by ordfeminine;
+    sub @DIGIT [O o]' by ordmasculine;
+} ordn;
diff --git a/Tests/feaLib/data/bug463.ttx b/Tests/feaLib/data/bug463.ttx
new file mode 100644
index 0000000..eba1d14
--- /dev/null
+++ b/Tests/feaLib/data/bug463.ttx
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="ordn"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=2 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="zero"/>
+            <Glyph value="one"/>
+            <Glyph value="two"/>
+            <Glyph value="three"/>
+            <Glyph value="four"/>
+            <Glyph value="five"/>
+            <Glyph value="six"/>
+            <Glyph value="seven"/>
+            <Glyph value="eight"/>
+            <Glyph value="nine"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="A"/>
+            <Glyph value="a"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+        <ChainContextSubst index="1" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="zero"/>
+            <Glyph value="one"/>
+            <Glyph value="two"/>
+            <Glyph value="three"/>
+            <Glyph value="four"/>
+            <Glyph value="five"/>
+            <Glyph value="six"/>
+            <Glyph value="seven"/>
+            <Glyph value="eight"/>
+            <Glyph value="nine"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="O"/>
+            <Glyph value="o"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="A" out="ordfeminine"/>
+          <Substitution in="O" out="ordmasculine"/>
+          <Substitution in="a" out="ordfeminine"/>
+          <Substitution in="o" out="ordmasculine"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/bug501.fea b/Tests/feaLib/data/bug501.fea
new file mode 100644
index 0000000..58a4081
--- /dev/null
+++ b/Tests/feaLib/data/bug501.fea
@@ -0,0 +1,8 @@
+# https://github.com/behdad/fonttools/issues/501
+languagesystem DFLT dflt;
+feature test {
+    lookup L {
+        script grek;
+        pos T 100;
+    } L;
+} test;
diff --git a/Tests/feaLib/data/bug501.ttx b/Tests/feaLib/data/bug501.ttx
new file mode 100644
index 0000000..032d21b
--- /dev/null
+++ b/Tests/feaLib/data/bug501.ttx
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="grek"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="T"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="100"/>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/bug502.fea b/Tests/feaLib/data/bug502.fea
new file mode 100644
index 0000000..b8130c4
--- /dev/null
+++ b/Tests/feaLib/data/bug502.fea
@@ -0,0 +1,7 @@
+# https://github.com/behdad/fonttools/issues/502
+feature aalt {
+    sub A by A.alt1;
+    sub Eng by Eng.alt1;
+    sub Eng by Eng.alt2;
+    sub Eng by Eng.alt3;
+} aalt;
diff --git a/Tests/feaLib/data/bug502.ttx b/Tests/feaLib/data/bug502.ttx
new file mode 100644
index 0000000..c952fba
--- /dev/null
+++ b/Tests/feaLib/data/bug502.ttx
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="aalt"/>
+        <Feature>
+          <!-- LookupCount=2 -->
+          <LookupListIndex index="0" value="0"/>
+          <LookupListIndex index="1" value="1"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="A" out="A.alt1"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="3"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <AlternateSubst index="0">
+          <AlternateSet glyph="Eng">
+            <Alternate glyph="Eng.alt1"/>
+            <Alternate glyph="Eng.alt2"/>
+            <Alternate glyph="Eng.alt3"/>
+          </AlternateSet>
+        </AlternateSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/bug504.fea b/Tests/feaLib/data/bug504.fea
new file mode 100644
index 0000000..64d05ba
--- /dev/null
+++ b/Tests/feaLib/data/bug504.fea
@@ -0,0 +1,5 @@
+# https://github.com/behdad/fonttools/issues/504
+
+feature test {
+    sub [a b c d] by [T E S T];
+} test;
diff --git a/Tests/feaLib/data/bug504.ttx b/Tests/feaLib/data/bug504.ttx
new file mode 100644
index 0000000..4d678b7
--- /dev/null
+++ b/Tests/feaLib/data/bug504.ttx
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="a" out="T"/>
+          <Substitution in="b" out="E"/>
+          <Substitution in="c" out="S"/>
+          <Substitution in="d" out="T"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/bug505.fea b/Tests/feaLib/data/bug505.fea
new file mode 100644
index 0000000..016ed1f
--- /dev/null
+++ b/Tests/feaLib/data/bug505.fea
@@ -0,0 +1,12 @@
+# https://github.com/behdad/fonttools/issues/505
+
+languagesystem armn dflt;
+languagesystem avst dflt;
+languagesystem bali dflt;
+languagesystem bamu dflt;
+
+feature test {
+    script linb;
+    script vai;
+    sub T by t;
+} test;
diff --git a/Tests/feaLib/data/bug505.ttx b/Tests/feaLib/data/bug505.ttx
new file mode 100644
index 0000000..e0a556f
--- /dev/null
+++ b/Tests/feaLib/data/bug505.ttx
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="vai "/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="T" out="t"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/bug506.fea b/Tests/feaLib/data/bug506.fea
new file mode 100644
index 0000000..62e4bbb
--- /dev/null
+++ b/Tests/feaLib/data/bug506.fea
@@ -0,0 +1,4 @@
+# https://github.com/behdad/fonttools/issues/506
+feature test {
+    sub f' i' by f_i;
+} test;
diff --git a/Tests/feaLib/data/bug506.ttx b/Tests/feaLib/data/bug506.ttx
new file mode 100644
index 0000000..9aff913
--- /dev/null
+++ b/Tests/feaLib/data/bug506.ttx
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=2 -->
+          <InputCoverage index="0">
+            <Glyph value="f"/>
+          </InputCoverage>
+          <InputCoverage index="1">
+            <Glyph value="i"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="f">
+            <Ligature components="i" glyph="f_i"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/bug509.fea b/Tests/feaLib/data/bug509.fea
new file mode 100644
index 0000000..b7af056
--- /dev/null
+++ b/Tests/feaLib/data/bug509.fea
@@ -0,0 +1,5 @@
+@LETTER_A = [A A.sc A.alt1];
+feature test {
+    ignore sub A;
+    sub @LETTER_A' by a;
+} test;
diff --git a/Tests/feaLib/data/bug509.ttx b/Tests/feaLib/data/bug509.ttx
new file mode 100644
index 0000000..e5a36af
--- /dev/null
+++ b/Tests/feaLib/data/bug509.ttx
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=2 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="A"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=0 -->
+        </ChainContextSubst>
+        <ChainContextSubst index="1" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="A"/>
+            <Glyph value="A.sc"/>
+            <Glyph value="A.alt1"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="A" out="a"/>
+          <Substitution in="A.alt1" out="a"/>
+          <Substitution in="A.sc" out="a"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/bug512.fea b/Tests/feaLib/data/bug512.fea
new file mode 100644
index 0000000..310b294
--- /dev/null
+++ b/Tests/feaLib/data/bug512.fea
@@ -0,0 +1,6 @@
+feature test {
+    sub G' by G.swash;
+    sub H' by H.swash;
+    sub G' by g;
+    sub H' by H.swash;
+} test;
diff --git a/Tests/feaLib/data/bug512.ttx b/Tests/feaLib/data/bug512.ttx
new file mode 100644
index 0000000..1dfe63f
--- /dev/null
+++ b/Tests/feaLib/data/bug512.ttx
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=3 -->
+      <Lookup index="0">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=4 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="G"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+        <ChainContextSubst index="1" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="H"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+        <ChainContextSubst index="2" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="G"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="2"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+        <ChainContextSubst index="3" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="H"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="2"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="G" out="G.swash"/>
+          <Substitution in="H" out="H.swash"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="G" out="g"/>
+          <Substitution in="H" out="H.swash"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/bug514.fea b/Tests/feaLib/data/bug514.fea
new file mode 100644
index 0000000..26da865
--- /dev/null
+++ b/Tests/feaLib/data/bug514.fea
@@ -0,0 +1,11 @@
+# The many chain targets for this feature should get combined into
+# two separate SinglePos lookups: {A:-40, B:-40, C:-40} and {A:-111}.
+# https://github.com/fonttools/fonttools/issues/514
+#
+# makeotf produces {A:-40, B:-40, C:-40} and {A:-111, B:-40} which
+# is redundant. https://github.com/adobe-type-tools/afdko/issues/169
+feature test {
+    pos X [A-B]' -40 B' -40 A' -40 Y;
+    pos X A' -111 Y;
+    pos X B' -40 A' -111 [A-C]' -40 Y;
+} test;
diff --git a/Tests/feaLib/data/bug514.ttx b/Tests/feaLib/data/bug514.ttx
new file mode 100644
index 0000000..f6b14f6
--- /dev/null
+++ b/Tests/feaLib/data/bug514.ttx
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.7">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=3 -->
+      <Lookup index="0">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=3 -->
+        <ChainContextPos index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="X"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=3 -->
+          <InputCoverage index="0">
+            <Glyph value="A"/>
+            <Glyph value="B"/>
+          </InputCoverage>
+          <InputCoverage index="1">
+            <Glyph value="B"/>
+          </InputCoverage>
+          <InputCoverage index="2">
+            <Glyph value="A"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="Y"/>
+          </LookAheadCoverage>
+          <!-- PosCount=3 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </PosLookupRecord>
+          <PosLookupRecord index="1">
+            <SequenceIndex value="1"/>
+            <LookupListIndex value="1"/>
+          </PosLookupRecord>
+          <PosLookupRecord index="2">
+            <SequenceIndex value="2"/>
+            <LookupListIndex value="1"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+        <ChainContextPos index="1" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="X"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="A"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="Y"/>
+          </LookAheadCoverage>
+          <!-- PosCount=1 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="2"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+        <ChainContextPos index="2" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="X"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=3 -->
+          <InputCoverage index="0">
+            <Glyph value="B"/>
+          </InputCoverage>
+          <InputCoverage index="1">
+            <Glyph value="A"/>
+          </InputCoverage>
+          <InputCoverage index="2">
+            <Glyph value="A"/>
+            <Glyph value="B"/>
+            <Glyph value="C"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="Y"/>
+          </LookAheadCoverage>
+          <!-- PosCount=3 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </PosLookupRecord>
+          <PosLookupRecord index="1">
+            <SequenceIndex value="1"/>
+            <LookupListIndex value="2"/>
+          </PosLookupRecord>
+          <PosLookupRecord index="2">
+            <SequenceIndex value="2"/>
+            <LookupListIndex value="1"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="A"/>
+            <Glyph value="B"/>
+            <Glyph value="C"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="-40"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="A"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="-111"/>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/bug568.fea b/Tests/feaLib/data/bug568.fea
new file mode 100644
index 0000000..3e879b1
--- /dev/null
+++ b/Tests/feaLib/data/bug568.fea
@@ -0,0 +1,11 @@
+# https://github.com/behdad/fonttools/issues/568
+
+feature tst1 {
+  script latn;
+  pos T -20;
+} tst1;
+
+feature tst2 {
+  script cyrl;
+  pos T -80;
+} tst2;
diff --git a/Tests/feaLib/data/bug568.ttx b/Tests/feaLib/data/bug568.ttx
new file mode 100644
index 0000000..b47e14a
--- /dev/null
+++ b/Tests/feaLib/data/bug568.ttx
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=2 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="cyrl"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="1"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="1">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=2 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="tst1"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="tst2"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="T"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="-20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="T"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="-80"/>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/bug633.fea b/Tests/feaLib/data/bug633.fea
new file mode 100644
index 0000000..4f47168
--- /dev/null
+++ b/Tests/feaLib/data/bug633.fea
@@ -0,0 +1,10 @@
+# https://github.com/fonttools/fonttools/issues/633
+
+@public.kern1.K = [K X];
+@public.kern2.O = [C O];
+@public.kern2.V = [V W];
+
+feature kern {
+    pos @public.kern1.K @public.kern2.O -20;
+    pos @public.kern1.K @public.kern2.V 0;
+} kern;
diff --git a/Tests/feaLib/data/bug633.ttx b/Tests/feaLib/data/bug633.ttx
new file mode 100644
index 0000000..b119ebb
--- /dev/null
+++ b/Tests/feaLib/data/bug633.ttx
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage>
+            <Glyph value="K"/>
+            <Glyph value="X"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1>
+          </ClassDef1>
+          <ClassDef2>
+            <ClassDef glyph="C" class="2"/>
+            <ClassDef glyph="O" class="2"/>
+            <ClassDef glyph="V" class="1"/>
+            <ClassDef glyph="W" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=1 -->
+          <!-- Class2Count=3 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="-20"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/enum.fea b/Tests/feaLib/data/enum.fea
new file mode 100644
index 0000000..124fb62
--- /dev/null
+++ b/Tests/feaLib/data/enum.fea
@@ -0,0 +1,7 @@
+languagesystem DFLT dflt;
+
+feature kern {
+    @Y_LC = [y yacute ydieresis];
+    @SMALL_PUNC = [comma semicolon period];
+    enum pos @Y_LC @SMALL_PUNC -100;
+} kern;
diff --git a/Tests/feaLib/data/enum.ttx b/Tests/feaLib/data/enum.ttx
new file mode 100644
index 0000000..7852aec
--- /dev/null
+++ b/Tests/feaLib/data/enum.ttx
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage>
+            <Glyph value="y"/>
+            <Glyph value="ydieresis"/>
+            <Glyph value="yacute"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=3 -->
+          <PairSet index="0">
+            <!-- PairValueCount=3 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="semicolon"/>
+              <Value1 XAdvance="-100"/>
+            </PairValueRecord>
+            <PairValueRecord index="1">
+              <SecondGlyph value="period"/>
+              <Value1 XAdvance="-100"/>
+            </PairValueRecord>
+            <PairValueRecord index="2">
+              <SecondGlyph value="comma"/>
+              <Value1 XAdvance="-100"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="1">
+            <!-- PairValueCount=3 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="semicolon"/>
+              <Value1 XAdvance="-100"/>
+            </PairValueRecord>
+            <PairValueRecord index="1">
+              <SecondGlyph value="period"/>
+              <Value1 XAdvance="-100"/>
+            </PairValueRecord>
+            <PairValueRecord index="2">
+              <SecondGlyph value="comma"/>
+              <Value1 XAdvance="-100"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="2">
+            <!-- PairValueCount=3 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="semicolon"/>
+              <Value1 XAdvance="-100"/>
+            </PairValueRecord>
+            <PairValueRecord index="1">
+              <SecondGlyph value="period"/>
+              <Value1 XAdvance="-100"/>
+            </PairValueRecord>
+            <PairValueRecord index="2">
+              <SecondGlyph value="comma"/>
+              <Value1 XAdvance="-100"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/feature_aalt.fea b/Tests/feaLib/data/feature_aalt.fea
new file mode 100644
index 0000000..1905ca9
--- /dev/null
+++ b/Tests/feaLib/data/feature_aalt.fea
@@ -0,0 +1,29 @@
+languagesystem DFLT dflt;
+
+feature aalt {
+    feature sups;
+    feature frac;
+    feature ordn;
+} aalt;
+
+feature sups {
+    sub one by onesuperior;
+    sub two by twosuperior;
+    sub three by threesuperior;
+} sups;
+
+feature frac {
+    sub one slash four by onequarter;
+    sub one slash two by onehalf;
+    sub three slash four by threequarters;
+} frac;
+
+feature ordn {
+    sub [zero one two three four five six seven eight nine] [A a]' by ordfeminine;
+    sub [zero one two three four five six seven eight nine] [O o]' by ordmasculine;
+} ordn;
+
+feature liga {
+    sub f i by f_i;
+    sub f l by f_l;
+} liga;
diff --git a/Tests/feaLib/data/feature_aalt.ttx b/Tests/feaLib/data/feature_aalt.ttx
new file mode 100644
index 0000000..eac2c3f
--- /dev/null
+++ b/Tests/feaLib/data/feature_aalt.ttx
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=5 -->
+            <FeatureIndex index="0" value="0"/>
+            <FeatureIndex index="1" value="1"/>
+            <FeatureIndex index="2" value="2"/>
+            <FeatureIndex index="3" value="3"/>
+            <FeatureIndex index="4" value="4"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=5 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="aalt"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="frac"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="2"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="2">
+        <FeatureTag value="liga"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="5"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="3">
+        <FeatureTag value="ordn"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="3"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="4">
+        <FeatureTag value="sups"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=6 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="A" out="ordfeminine"/>
+          <Substitution in="O" out="ordmasculine"/>
+          <Substitution in="a" out="ordfeminine"/>
+          <Substitution in="o" out="ordmasculine"/>
+          <Substitution in="one" out="onesuperior"/>
+          <Substitution in="three" out="threesuperior"/>
+          <Substitution in="two" out="twosuperior"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="one" out="onesuperior"/>
+          <Substitution in="three" out="threesuperior"/>
+          <Substitution in="two" out="twosuperior"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="one">
+            <Ligature components="slash,four" glyph="onequarter"/>
+            <Ligature components="slash,two" glyph="onehalf"/>
+          </LigatureSet>
+          <LigatureSet glyph="three">
+            <Ligature components="slash,four" glyph="threequarters"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=2 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="zero"/>
+            <Glyph value="one"/>
+            <Glyph value="two"/>
+            <Glyph value="three"/>
+            <Glyph value="four"/>
+            <Glyph value="five"/>
+            <Glyph value="six"/>
+            <Glyph value="seven"/>
+            <Glyph value="eight"/>
+            <Glyph value="nine"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="A"/>
+            <Glyph value="a"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="4"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+        <ChainContextSubst index="1" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="zero"/>
+            <Glyph value="one"/>
+            <Glyph value="two"/>
+            <Glyph value="three"/>
+            <Glyph value="four"/>
+            <Glyph value="five"/>
+            <Glyph value="six"/>
+            <Glyph value="seven"/>
+            <Glyph value="eight"/>
+            <Glyph value="nine"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="O"/>
+            <Glyph value="o"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="4"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="A" out="ordfeminine"/>
+          <Substitution in="O" out="ordmasculine"/>
+          <Substitution in="a" out="ordfeminine"/>
+          <Substitution in="o" out="ordmasculine"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="5">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="f">
+            <Ligature components="i" glyph="f_i"/>
+            <Ligature components="l" glyph="f_l"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/ignore_pos.fea b/Tests/feaLib/data/ignore_pos.fea
new file mode 100644
index 0000000..58e217e
--- /dev/null
+++ b/Tests/feaLib/data/ignore_pos.fea
@@ -0,0 +1,5 @@
+feature test {
+    ignore pos f [a e] d';
+    ignore pos a d' d;
+    pos [a e n] d' -20;
+} test;
diff --git a/Tests/feaLib/data/ignore_pos.ttx b/Tests/feaLib/data/ignore_pos.ttx
new file mode 100644
index 0000000..6b03b59
--- /dev/null
+++ b/Tests/feaLib/data/ignore_pos.ttx
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=3 -->
+        <ChainContextPos index="0" Format="3">
+          <!-- BacktrackGlyphCount=2 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="a"/>
+            <Glyph value="e"/>
+          </BacktrackCoverage>
+          <BacktrackCoverage index="1">
+            <Glyph value="f"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="d"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- PosCount=0 -->
+        </ChainContextPos>
+        <ChainContextPos index="1" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="a"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="d"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="d"/>
+          </LookAheadCoverage>
+          <!-- PosCount=0 -->
+        </ChainContextPos>
+        <ChainContextPos index="2" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="a"/>
+            <Glyph value="e"/>
+            <Glyph value="n"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="d"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- PosCount=1 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="d"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="-20"/>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/include/include1.fea b/Tests/feaLib/data/include/include1.fea
new file mode 100644
index 0000000..b88d1f5
--- /dev/null
+++ b/Tests/feaLib/data/include/include1.fea
@@ -0,0 +1,3 @@
+I1a
+include(../include0.fea)
+I1b
diff --git a/Tests/feaLib/data/include/include3.fea b/Tests/feaLib/data/include/include3.fea
new file mode 100644
index 0000000..09cbbbe
--- /dev/null
+++ b/Tests/feaLib/data/include/include3.fea
@@ -0,0 +1,4 @@
+I3a
+include(subdir/include2.fea);
+I3b
+
diff --git a/Tests/feaLib/data/include/include4.fea b/Tests/feaLib/data/include/include4.fea
new file mode 100644
index 0000000..677b460
--- /dev/null
+++ b/Tests/feaLib/data/include/include4.fea
@@ -0,0 +1,4 @@
+I4a
+include(include3.fea);
+I4b
+
diff --git a/Tests/feaLib/data/include/include5.fea b/Tests/feaLib/data/include/include5.fea
new file mode 100644
index 0000000..553967b
--- /dev/null
+++ b/Tests/feaLib/data/include/include5.fea
@@ -0,0 +1,3 @@
+I5a
+include(include4.fea);
+I5b
diff --git a/Tests/feaLib/data/include/include6.fea b/Tests/feaLib/data/include/include6.fea
new file mode 100644
index 0000000..75a64be
--- /dev/null
+++ b/Tests/feaLib/data/include/include6.fea
@@ -0,0 +1,3 @@
+I6a
+include(include5.fea);
+I6b
diff --git a/Tests/feaLib/data/include/includemissingfile.fea b/Tests/feaLib/data/include/includemissingfile.fea
new file mode 100644
index 0000000..524c293
--- /dev/null
+++ b/Tests/feaLib/data/include/includemissingfile.fea
@@ -0,0 +1 @@
+include(missingfile.fea);
diff --git a/Tests/feaLib/data/include/includeself.fea b/Tests/feaLib/data/include/includeself.fea
new file mode 100644
index 0000000..6caad09
--- /dev/null
+++ b/Tests/feaLib/data/include/includeself.fea
@@ -0,0 +1 @@
+include(includeself.fea);
diff --git a/Tests/feaLib/data/include/subdir/include2.fea b/Tests/feaLib/data/include/subdir/include2.fea
new file mode 100644
index 0000000..52c078a
--- /dev/null
+++ b/Tests/feaLib/data/include/subdir/include2.fea
@@ -0,0 +1,3 @@
+I2a
+include(include1.fea);
+I2b
diff --git a/Tests/feaLib/data/include0.fea b/Tests/feaLib/data/include0.fea
new file mode 100644
index 0000000..8f177e1
--- /dev/null
+++ b/Tests/feaLib/data/include0.fea
@@ -0,0 +1 @@
+I0
diff --git a/Tests/feaLib/data/language_required.fea b/Tests/feaLib/data/language_required.fea
new file mode 100644
index 0000000..4005a78
--- /dev/null
+++ b/Tests/feaLib/data/language_required.fea
@@ -0,0 +1,22 @@
+languagesystem latn DEU;
+languagesystem latn FRA;
+languagesystem latn ITA;
+
+feature hlig {
+  script latn;
+  language DEU exclude_dflt required;
+  sub D E U by D.sc;
+
+  language FRA exclude_dflt;
+  sub F R A by F.sc;
+} hlig;
+
+feature liga {
+  script latn;
+  language ITA exclude_dflt required;
+  sub I T A by I.sc;
+} liga;
+
+feature scmp {
+  sub [a-z] by [A.sc-Z.sc];
+} scmp;
diff --git a/Tests/feaLib/data/language_required.ttx b/Tests/feaLib/data/language_required.ttx
new file mode 100644
index 0000000..5481439
--- /dev/null
+++ b/Tests/feaLib/data/language_required.ttx
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <!-- LangSysCount=3 -->
+          <LangSysRecord index="0">
+            <LangSysTag value="DEU "/>
+            <LangSys>
+              <ReqFeatureIndex value="0"/>
+              <!-- FeatureCount=1 -->
+              <FeatureIndex index="0" value="3"/>
+            </LangSys>
+          </LangSysRecord>
+          <LangSysRecord index="1">
+            <LangSysTag value="FRA "/>
+            <LangSys>
+              <ReqFeatureIndex value="65535"/>
+              <!-- FeatureCount=2 -->
+              <FeatureIndex index="0" value="1"/>
+              <FeatureIndex index="1" value="3"/>
+            </LangSys>
+          </LangSysRecord>
+          <LangSysRecord index="2">
+            <LangSysTag value="ITA "/>
+            <LangSys>
+              <ReqFeatureIndex value="2"/>
+              <!-- FeatureCount=1 -->
+              <FeatureIndex index="0" value="3"/>
+            </LangSys>
+          </LangSysRecord>
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=4 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="hlig"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="hlig"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="2">
+        <FeatureTag value="liga"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="2"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="3">
+        <FeatureTag value="scmp"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="3"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=4 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="D">
+            <Ligature components="E,U" glyph="D.sc"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="F">
+            <Ligature components="R,A" glyph="F.sc"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="I">
+            <Ligature components="T,A" glyph="I.sc"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="a" out="A.sc"/>
+          <Substitution in="b" out="B.sc"/>
+          <Substitution in="c" out="C.sc"/>
+          <Substitution in="d" out="D.sc"/>
+          <Substitution in="e" out="E.sc"/>
+          <Substitution in="f" out="F.sc"/>
+          <Substitution in="g" out="G.sc"/>
+          <Substitution in="h" out="H.sc"/>
+          <Substitution in="i" out="I.sc"/>
+          <Substitution in="j" out="J.sc"/>
+          <Substitution in="k" out="K.sc"/>
+          <Substitution in="l" out="L.sc"/>
+          <Substitution in="m" out="M.sc"/>
+          <Substitution in="n" out="N.sc"/>
+          <Substitution in="o" out="O.sc"/>
+          <Substitution in="p" out="P.sc"/>
+          <Substitution in="q" out="Q.sc"/>
+          <Substitution in="r" out="R.sc"/>
+          <Substitution in="s" out="S.sc"/>
+          <Substitution in="t" out="T.sc"/>
+          <Substitution in="u" out="U.sc"/>
+          <Substitution in="v" out="V.sc"/>
+          <Substitution in="w" out="W.sc"/>
+          <Substitution in="x" out="X.sc"/>
+          <Substitution in="y" out="Y.sc"/>
+          <Substitution in="z" out="Z.sc"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/lookup.fea b/Tests/feaLib/data/lookup.fea
new file mode 100644
index 0000000..ad1fb7b
--- /dev/null
+++ b/Tests/feaLib/data/lookup.fea
@@ -0,0 +1,24 @@
+# Three features. In the output, they should all point to the same lookup.
+
+lookup SomeLookup {
+    sub f f i by f_f_i;
+    sub f i by f_i;
+} SomeLookup;
+
+feature tst1 {
+    lookup SomeLookup;
+} tst1;
+
+feature tst2 {
+    lookup SomeLookup;
+} tst2;
+
+feature tst3 {
+    lookup EmbeddedLookup {
+        sub A by A.sc;
+    } EmbeddedLookup;
+} tst3;
+
+feature tst4 {
+    lookup EmbeddedLookup;
+} tst4;
diff --git a/Tests/feaLib/data/lookup.ttx b/Tests/feaLib/data/lookup.ttx
new file mode 100644
index 0000000..35ab04c
--- /dev/null
+++ b/Tests/feaLib/data/lookup.ttx
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=4 -->
+            <FeatureIndex index="0" value="0"/>
+            <FeatureIndex index="1" value="1"/>
+            <FeatureIndex index="2" value="2"/>
+            <FeatureIndex index="3" value="3"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=4 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="tst1"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="tst2"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="2">
+        <FeatureTag value="tst3"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="3">
+        <FeatureTag value="tst4"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="f">
+            <Ligature components="f,i" glyph="f_f_i"/>
+            <Ligature components="i" glyph="f_i"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="A" out="A.sc"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/lookupflag.fea b/Tests/feaLib/data/lookupflag.fea
new file mode 100644
index 0000000..651dcd0
--- /dev/null
+++ b/Tests/feaLib/data/lookupflag.fea
@@ -0,0 +1,97 @@
+languagesystem DFLT dflt;
+
+@TOP_MARKS = [acute grave macron];
+markClass [cedilla ogonek] <anchor 350 -20> @BOTTOM_MARKS;
+@FRENCH_MARKS = [acute grave cedilla dieresis circumflex];
+@MARKS_WITH_DUPLICATES = [breve caron umlaut breve caron umlaut];
+
+lookup A {
+    lookupflag RightToLeft;
+    pos one 1;
+} A;
+
+lookup B {
+    lookupflag IgnoreBaseGlyphs;
+    pos two 2;
+} B;
+
+lookup C {
+    lookupflag IgnoreLigatures;
+    pos four 4;
+} C;
+
+lookup D {
+#test-fea2fea: lookupflag RightToLeft IgnoreBaseGlyphs IgnoreLigatures;
+    lookupflag 7;
+    pos seven 7;
+} D;
+
+lookup E {
+    lookupflag IgnoreMarks;
+    pos eight 8;
+} E;
+
+lookup F {
+    lookupflag MarkAttachmentType @TOP_MARKS;
+    pos F 1;
+} F;
+
+lookup G {
+    lookupflag MarkAttachmentType @BOTTOM_MARKS;
+    pos G 1;
+} G;
+
+lookup H {
+    lookupflag IgnoreLigatures MarkAttachmentType @TOP_MARKS;
+    pos H 1;
+} H;
+
+lookup I {
+    lookupflag UseMarkFilteringSet @TOP_MARKS;
+    pos I 1;
+} I;
+
+lookup J {
+    # @FRENCH_MARKS overlaps with @TOP_MARKS.
+    # Other than with MarkAttachmentType, the same glyph may appear
+    # in multiple glyphsets for UseMarkFilteringSet. Make sure that
+    # our implementation can handle this.
+    lookupflag UseMarkFilteringSet @FRENCH_MARKS;
+    pos J 1;
+} J;
+
+lookup K {
+    lookupflag IgnoreLigatures UseMarkFilteringSet @TOP_MARKS;
+    pos K 1;
+} K;
+
+lookup L {
+    pos L 1;
+} L;
+
+lookup M {
+    lookupflag UseMarkFilteringSet @MARKS_WITH_DUPLICATES;
+    pos M 1;
+} M;
+
+lookup N {
+    lookupflag MarkAttachmentType @MARKS_WITH_DUPLICATES;
+    pos N 1;
+} N;
+
+feature test {
+    lookup A;
+    lookup B;
+    lookup C;
+    lookup D;
+    lookup E;
+    lookup F;
+    lookup G;
+    lookup H;
+    lookup I;
+    lookup J;
+    lookup K;
+    lookup L;
+    lookup M;
+    lookup N;
+} test;
diff --git a/Tests/feaLib/data/lookupflag.ttx b/Tests/feaLib/data/lookupflag.ttx
new file mode 100644
index 0000000..bb05b9a
--- /dev/null
+++ b/Tests/feaLib/data/lookupflag.ttx
@@ -0,0 +1,259 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GDEF>
+    <Version value="0x00010002"/>
+    <GlyphClassDef>
+      <ClassDef glyph="cedilla" class="3"/>
+      <ClassDef glyph="ogonek" class="3"/>
+    </GlyphClassDef>
+    <MarkAttachClassDef>
+      <ClassDef glyph="acute" class="1"/>
+      <ClassDef glyph="breve" class="3"/>
+      <ClassDef glyph="caron" class="3"/>
+      <ClassDef glyph="cedilla" class="2"/>
+      <ClassDef glyph="grave" class="1"/>
+      <ClassDef glyph="macron" class="1"/>
+      <ClassDef glyph="ogonek" class="2"/>
+      <ClassDef glyph="umlaut" class="3"/>
+    </MarkAttachClassDef>
+    <MarkGlyphSetsDef>
+      <MarkSetTableFormat value="1"/>
+      <!-- MarkSetCount=3 -->
+      <Coverage index="0">
+        <Glyph value="grave"/>
+        <Glyph value="acute"/>
+        <Glyph value="macron"/>
+      </Coverage>
+      <Coverage index="1">
+        <Glyph value="grave"/>
+        <Glyph value="acute"/>
+        <Glyph value="dieresis"/>
+        <Glyph value="circumflex"/>
+        <Glyph value="cedilla"/>
+      </Coverage>
+      <Coverage index="2">
+        <Glyph value="breve"/>
+        <Glyph value="umlaut"/>
+        <Glyph value="caron"/>
+      </Coverage>
+    </MarkGlyphSetsDef>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=14 -->
+          <LookupListIndex index="0" value="0"/>
+          <LookupListIndex index="1" value="1"/>
+          <LookupListIndex index="2" value="2"/>
+          <LookupListIndex index="3" value="3"/>
+          <LookupListIndex index="4" value="4"/>
+          <LookupListIndex index="5" value="5"/>
+          <LookupListIndex index="6" value="6"/>
+          <LookupListIndex index="7" value="7"/>
+          <LookupListIndex index="8" value="8"/>
+          <LookupListIndex index="9" value="9"/>
+          <LookupListIndex index="10" value="10"/>
+          <LookupListIndex index="11" value="11"/>
+          <LookupListIndex index="12" value="12"/>
+          <LookupListIndex index="13" value="13"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=14 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="1"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="one"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="2"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="two"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="2"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="1"/>
+        <LookupFlag value="4"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="four"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="4"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="7"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="seven"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="7"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="1"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="eight"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="8"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="5">
+        <LookupType value="1"/>
+        <LookupFlag value="256"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="F"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="6">
+        <LookupType value="1"/>
+        <LookupFlag value="512"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="G"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="7">
+        <LookupType value="1"/>
+        <LookupFlag value="260"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="H"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="8">
+        <LookupType value="1"/>
+        <LookupFlag value="16"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="I"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+        <MarkFilteringSet value="0"/>
+      </Lookup>
+      <Lookup index="9">
+        <LookupType value="1"/>
+        <LookupFlag value="16"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="J"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+        <MarkFilteringSet value="1"/>
+      </Lookup>
+      <Lookup index="10">
+        <LookupType value="1"/>
+        <LookupFlag value="20"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="K"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+        <MarkFilteringSet value="0"/>
+      </Lookup>
+      <Lookup index="11">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="L"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="12">
+        <LookupType value="1"/>
+        <LookupFlag value="16"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="M"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+        <MarkFilteringSet value="2"/>
+      </Lookup>
+      <Lookup index="13">
+        <LookupType value="1"/>
+        <LookupFlag value="768"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="N"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/markClass.fea b/Tests/feaLib/data/markClass.fea
new file mode 100644
index 0000000..855de6f
--- /dev/null
+++ b/Tests/feaLib/data/markClass.fea
@@ -0,0 +1,12 @@
+languagesystem DFLT dflt;
+
+markClass [acute] <anchor 350 0> @TOP_MARKS;
+
+feature foo {
+    markClass [grave] <anchor 350 0> @TOP_MARKS;
+    markClass cedilla <anchor 300 0> @BOTTOM_MARKS;
+} foo;
+
+feature bar {
+    markClass [dieresis breve] <anchor 400 0> @TOP_MARKS;
+} bar;
diff --git a/Tests/feaLib/data/markClass.ttx b/Tests/feaLib/data/markClass.ttx
new file mode 100644
index 0000000..88ed5f9
--- /dev/null
+++ b/Tests/feaLib/data/markClass.ttx
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef>
+      <ClassDef glyph="acute" class="3"/>
+      <ClassDef glyph="breve" class="3"/>
+      <ClassDef glyph="cedilla" class="3"/>
+      <ClassDef glyph="dieresis" class="3"/>
+      <ClassDef glyph="grave" class="3"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/feaLib/data/mini.fea b/Tests/feaLib/data/mini.fea
new file mode 100644
index 0000000..9cf4b2f
--- /dev/null
+++ b/Tests/feaLib/data/mini.fea
@@ -0,0 +1,19 @@
+# Example file from OpenType Feature File specification, section 1.
+# http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
+
+# Script and language coverage
+languagesystem DFLT dflt;
+languagesystem latn dflt;
+
+# Ligature formation
+feature liga {
+    substitute f i by f_i;
+    substitute f l by f_l;
+} liga;
+
+# Kerning
+feature kern {
+    position A Y -100;
+    position a y -80;
+    position s f' <0 0 10 0> t;
+} kern;
diff --git a/Tests/feaLib/data/multiple_feature_blocks.fea b/Tests/feaLib/data/multiple_feature_blocks.fea
new file mode 100644
index 0000000..2f9902a
--- /dev/null
+++ b/Tests/feaLib/data/multiple_feature_blocks.fea
@@ -0,0 +1,18 @@
+languagesystem DFLT dflt;
+languagesystem latn dflt;
+languagesystem latn TRK;
+
+feature liga {
+    lookup liga_fl {
+        sub f l by f_l;
+    } liga_fl;
+} liga;
+
+feature liga {
+    lookup liga_fi {
+        sub f i by f_i;
+    } liga_fi;
+
+    script latn;
+    language TRK exclude_dflt;
+} liga;
diff --git a/Tests/feaLib/data/multiple_feature_blocks.ttx b/Tests/feaLib/data/multiple_feature_blocks.ttx
new file mode 100644
index 0000000..4f9a389
--- /dev/null
+++ b/Tests/feaLib/data/multiple_feature_blocks.ttx
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=2 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="1"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="1">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="1"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=1 -->
+          <LangSysRecord index="0">
+            <LangSysTag value="TRK "/>
+            <LangSys>
+              <ReqFeatureIndex value="65535"/>
+              <!-- FeatureCount=1 -->
+              <FeatureIndex index="0" value="0"/>
+            </LangSys>
+          </LangSysRecord>
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=2 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="liga"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="liga"/>
+        <Feature>
+          <!-- LookupCount=2 -->
+          <LookupListIndex index="0" value="0"/>
+          <LookupListIndex index="1" value="1"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="f">
+            <Ligature components="l" glyph="f_l"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="f">
+            <Ligature components="i" glyph="f_i"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/name.fea b/Tests/feaLib/data/name.fea
new file mode 100644
index 0000000..17727ed
--- /dev/null
+++ b/Tests/feaLib/data/name.fea
@@ -0,0 +1,23 @@
+table name {
+#test-fea2fea:
+  nameid 1 "Ignored-1";
+#test-fea2fea:
+  nameid 2 "Ignored-2";
+#test-fea2fea:
+  nameid 3 "Ignored-3";
+#test-fea2fea:
+  nameid 4 "Ignored-4";
+#test-fea2fea:
+  nameid 5 "Ignored-5";
+#test-fea2fea:
+    nameid 6 "Ignored-6";
+#test-fea2fea: nameid 7 "Test7";
+  nameid 7 3 "Test7";
+    nameid 8 1 "Test8";
+#test-fea2fea: nameid 9 "Test9";
+    nameid 9 3 1 0x0409 "Test9";
+    nameid 10 1 "Test10";
+#test-fea2fea: nameid 11 1 "Test11";
+    nameid 11 1 0 0 "Test11";
+} name;
+
diff --git a/Tests/feaLib/data/name.ttx b/Tests/feaLib/data/name.ttx
new file mode 100644
index 0000000..cdb6038
--- /dev/null
+++ b/Tests/feaLib/data/name.ttx
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <name>
+    <namerecord nameID="7" platformID="3" platEncID="1" langID="0x409">
+      Test7
+    </namerecord>
+    <namerecord nameID="8" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Test8
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Test9
+    </namerecord>
+    <namerecord nameID="10" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Test10
+    </namerecord>
+    <namerecord nameID="11" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Test11
+    </namerecord>
+  </name>
+
+</ttFont>
diff --git a/Tests/feaLib/data/omitted_GlyphClassDef.fea b/Tests/feaLib/data/omitted_GlyphClassDef.fea
new file mode 100644
index 0000000..d580e22
--- /dev/null
+++ b/Tests/feaLib/data/omitted_GlyphClassDef.fea
@@ -0,0 +1,6 @@
+@BASE = [f i];
+@MARKS = [acute grave];
+
+table GDEF {
+    GlyphClassDef @BASE, , @MARKS, ;
+} GDEF;
diff --git a/Tests/feaLib/data/omitted_GlyphClassDef.ttx b/Tests/feaLib/data/omitted_GlyphClassDef.ttx
new file mode 100644
index 0000000..5ad040b
--- /dev/null
+++ b/Tests/feaLib/data/omitted_GlyphClassDef.ttx
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef>
+      <ClassDef glyph="acute" class="3"/>
+      <ClassDef glyph="f" class="1"/>
+      <ClassDef glyph="grave" class="3"/>
+      <ClassDef glyph="i" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/feaLib/data/size.fea b/Tests/feaLib/data/size.fea
new file mode 100644
index 0000000..6a30063
--- /dev/null
+++ b/Tests/feaLib/data/size.fea
@@ -0,0 +1,4 @@
+feature size {
+#test-fea2fea: parameters 10.0 0;
+  parameters 10.0 0 0 0;
+} size;
diff --git a/Tests/feaLib/data/size.ttx b/Tests/feaLib/data/size.ttx
new file mode 100644
index 0000000..1a12ddf
--- /dev/null
+++ b/Tests/feaLib/data/size.ttx
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="size"/>
+        <Feature>
+          <FeatureParamsSize>
+            <DesignSize value="10.0"/>
+            <SubfamilyID value="0"/>
+            <SubfamilyNameID value="0"/>
+            <RangeStart value="0.0"/>
+            <RangeEnd value="0.0"/>
+          </FeatureParamsSize>
+          <!-- LookupCount=0 -->
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/size2.fea b/Tests/feaLib/data/size2.fea
new file mode 100644
index 0000000..a5a08a5
--- /dev/null
+++ b/Tests/feaLib/data/size2.fea
@@ -0,0 +1,3 @@
+feature size {
+  parameters 10.0 0;
+} size;
diff --git a/Tests/feaLib/data/size2.ttx b/Tests/feaLib/data/size2.ttx
new file mode 100644
index 0000000..a822af3
--- /dev/null
+++ b/Tests/feaLib/data/size2.ttx
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="size"/>
+        <Feature>
+          <FeatureParamsSize>
+            <DesignSize value="10.0"/>
+            <SubfamilyID value="0"/>
+            <SubfamilyNameID value="0"/>
+            <RangeStart value="0"/>
+            <RangeEnd value="0"/>
+          </FeatureParamsSize>
+          <!-- LookupCount=0 -->
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec10.fea b/Tests/feaLib/data/spec10.fea
new file mode 100644
index 0000000..9d1be02
--- /dev/null
+++ b/Tests/feaLib/data/spec10.fea
@@ -0,0 +1,12 @@
+# OpenType Feature File specification, section 10.
+# http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
+
+anon sbit {
+    /* sbit table specifications */
+    72  % dpi
+    sizes {
+        10, 12, 14 source {
+            all "Generic/JGeneric"
+        }
+    }
+} sbit;
diff --git a/Tests/feaLib/data/spec10.ttx b/Tests/feaLib/data/spec10.ttx
new file mode 100644
index 0000000..5db8ac2
--- /dev/null
+++ b/Tests/feaLib/data/spec10.ttx
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec4h1.fea b/Tests/feaLib/data/spec4h1.fea
new file mode 100644
index 0000000..b43e13b
--- /dev/null
+++ b/Tests/feaLib/data/spec4h1.fea
@@ -0,0 +1,64 @@
+# OpenType Feature File specification, section 4.h, example 1.
+# http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
+
+languagesystem DFLT dflt;
+languagesystem latn dflt;
+languagesystem latn DEU;
+languagesystem latn TRK;
+languagesystem cyrl dflt;
+
+feature smcp {
+    sub [a-z] by [A.sc-Z.sc];
+
+    # Since all the rules in this feature are of the same type, they
+    # will be grouped in a single lookup.  Since no script or language
+    # keyword has been specified yet, the lookup will be registered
+    # for this feature under all the language systems.
+} smcp;
+
+feature liga {
+    sub f f by f_f;
+    sub f i by f_i;
+    sub f l by f_l;
+
+    # Since all the rules in this feature are of the same type, they
+    # will be grouped in a single lookup.  Since no script or language
+    # keyword has been specified yet, the lookup will be registered
+    # for this feature under all the language systems.
+
+    script latn;
+    language dflt;
+    # lookupflag 0;      (implicit)
+    sub c t by c_t;
+    sub c s by c_s;
+
+    # The rules above will be placed in a lookup that is registered
+    # for all the specified languages for the script latn, but not any
+    # other scripts.
+
+    language DEU;
+    # script latn;       (stays the same)
+    # lookupflag 0;      (stays the same)
+    sub c h by c_h;
+    sub c k by c_k;
+
+    # The rules above will be placed in a lookup that is registered
+    # only under the script latn, language DEU.
+
+    language TRK;
+
+    # This will inherit both the top level default rules - the rules
+    # defined before the first 'script' statement, and the
+    # script-level default rules for 'latn': all the lookups of this
+    # feature defined after the 'script latn' statement, and before
+    # the language DEU statement.  If TRK were not named here, it
+    # would not inherit the default rules for the script latn.
+} liga;
+
+# TODO(sascha): Uncomment once we support 'pos' statements.
+# feature kern {
+#     pos a y -150; 
+#     # [more pos statements]
+#     # All the rules in this feature will be grouped in a single lookup
+#     # that is is registered under all the language systems.
+# } kern;
diff --git a/Tests/feaLib/data/spec4h1.ttx b/Tests/feaLib/data/spec4h1.ttx
new file mode 100644
index 0000000..0e42fc5
--- /dev/null
+++ b/Tests/feaLib/data/spec4h1.ttx
@@ -0,0 +1,169 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=3 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=2 -->
+            <FeatureIndex index="0" value="2"/>
+            <FeatureIndex index="1" value="3"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="1">
+        <ScriptTag value="cyrl"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=2 -->
+            <FeatureIndex index="0" value="2"/>
+            <FeatureIndex index="1" value="3"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="2">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=2 -->
+            <FeatureIndex index="0" value="1"/>
+            <FeatureIndex index="1" value="3"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=2 -->
+          <LangSysRecord index="0">
+            <LangSysTag value="DEU "/>
+            <LangSys>
+              <ReqFeatureIndex value="65535"/>
+              <!-- FeatureCount=2 -->
+              <FeatureIndex index="0" value="0"/>
+              <FeatureIndex index="1" value="3"/>
+            </LangSys>
+          </LangSysRecord>
+          <LangSysRecord index="1">
+            <LangSysTag value="TRK "/>
+            <LangSys>
+              <ReqFeatureIndex value="65535"/>
+              <!-- FeatureCount=2 -->
+              <FeatureIndex index="0" value="1"/>
+              <FeatureIndex index="1" value="3"/>
+            </LangSys>
+          </LangSysRecord>
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=4 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="liga"/>
+        <Feature>
+          <!-- LookupCount=3 -->
+          <LookupListIndex index="0" value="1"/>
+          <LookupListIndex index="1" value="2"/>
+          <LookupListIndex index="2" value="3"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="liga"/>
+        <Feature>
+          <!-- LookupCount=2 -->
+          <LookupListIndex index="0" value="1"/>
+          <LookupListIndex index="1" value="2"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="2">
+        <FeatureTag value="liga"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="3">
+        <FeatureTag value="smcp"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=4 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="a" out="A.sc"/>
+          <Substitution in="b" out="B.sc"/>
+          <Substitution in="c" out="C.sc"/>
+          <Substitution in="d" out="D.sc"/>
+          <Substitution in="e" out="E.sc"/>
+          <Substitution in="f" out="F.sc"/>
+          <Substitution in="g" out="G.sc"/>
+          <Substitution in="h" out="H.sc"/>
+          <Substitution in="i" out="I.sc"/>
+          <Substitution in="j" out="J.sc"/>
+          <Substitution in="k" out="K.sc"/>
+          <Substitution in="l" out="L.sc"/>
+          <Substitution in="m" out="M.sc"/>
+          <Substitution in="n" out="N.sc"/>
+          <Substitution in="o" out="O.sc"/>
+          <Substitution in="p" out="P.sc"/>
+          <Substitution in="q" out="Q.sc"/>
+          <Substitution in="r" out="R.sc"/>
+          <Substitution in="s" out="S.sc"/>
+          <Substitution in="t" out="T.sc"/>
+          <Substitution in="u" out="U.sc"/>
+          <Substitution in="v" out="V.sc"/>
+          <Substitution in="w" out="W.sc"/>
+          <Substitution in="x" out="X.sc"/>
+          <Substitution in="y" out="Y.sc"/>
+          <Substitution in="z" out="Z.sc"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="f">
+            <Ligature components="f" glyph="f_f"/>
+            <Ligature components="i" glyph="f_i"/>
+            <Ligature components="l" glyph="f_l"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="c">
+            <Ligature components="s" glyph="c_s"/>
+            <Ligature components="t" glyph="c_t"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="c">
+            <Ligature components="h" glyph="c_h"/>
+            <Ligature components="k" glyph="c_k"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec4h2.fea b/Tests/feaLib/data/spec4h2.fea
new file mode 100644
index 0000000..0384804
--- /dev/null
+++ b/Tests/feaLib/data/spec4h2.fea
@@ -0,0 +1,40 @@
+# OpenType Feature File specification, section 4.h, example 2.
+# http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
+
+languagesystem DFLT dflt;
+languagesystem latn dflt;
+languagesystem latn DEU;
+languagesystem cyrl dflt;
+languagesystem cyrl SRB;
+languagesystem grek dflt;
+
+feature liga {
+    # start of default rules that are applied under all language systems.
+    lookup HAS_I {
+        sub f f i by f_f_i;
+        sub f i by f_i;
+    } HAS_I;
+
+    lookup NO_I {
+        sub f f l by f_f_l;
+        sub f f by f_f;
+    } NO_I;
+
+    # end of default rules that are applied under all language systems.
+
+    script latn;
+    language dflt;
+    # default lookup for latn included under all languages for the latn script
+    sub f l by f_l;
+
+    language DEU;
+    # default lookups included under the DEU language
+    sub s s by germandbls;  # This is also included.
+
+    language TRK exclude_dflt;  # default lookups are excluded.
+    lookup NO_I;  # Only this lookup is included under the TRK language
+
+    script cyrl;
+    language SRB;
+    sub c t by c_t;  # this rule will apply only under script cyrl language SRB.
+} liga;
diff --git a/Tests/feaLib/data/spec4h2.ttx b/Tests/feaLib/data/spec4h2.ttx
new file mode 100644
index 0000000..266c487
--- /dev/null
+++ b/Tests/feaLib/data/spec4h2.ttx
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=4 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="3"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="1">
+        <ScriptTag value="cyrl"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="3"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=1 -->
+          <LangSysRecord index="0">
+            <LangSysTag value="SRB "/>
+            <LangSys>
+              <ReqFeatureIndex value="65535"/>
+              <!-- FeatureCount=1 -->
+              <FeatureIndex index="0" value="1"/>
+            </LangSys>
+          </LangSysRecord>
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="2">
+        <ScriptTag value="grek"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="3"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="3">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="4"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=2 -->
+          <LangSysRecord index="0">
+            <LangSysTag value="DEU "/>
+            <LangSys>
+              <ReqFeatureIndex value="65535"/>
+              <!-- FeatureCount=1 -->
+              <FeatureIndex index="0" value="0"/>
+            </LangSys>
+          </LangSysRecord>
+          <LangSysRecord index="1">
+            <LangSysTag value="TRK "/>
+            <LangSys>
+              <ReqFeatureIndex value="65535"/>
+              <!-- FeatureCount=1 -->
+              <FeatureIndex index="0" value="2"/>
+            </LangSys>
+          </LangSysRecord>
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=5 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="liga"/>
+        <Feature>
+          <!-- LookupCount=4 -->
+          <LookupListIndex index="0" value="0"/>
+          <LookupListIndex index="1" value="1"/>
+          <LookupListIndex index="2" value="2"/>
+          <LookupListIndex index="3" value="3"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="liga"/>
+        <Feature>
+          <!-- LookupCount=3 -->
+          <LookupListIndex index="0" value="0"/>
+          <LookupListIndex index="1" value="1"/>
+          <LookupListIndex index="2" value="4"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="2">
+        <FeatureTag value="liga"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="3">
+        <FeatureTag value="liga"/>
+        <Feature>
+          <!-- LookupCount=2 -->
+          <LookupListIndex index="0" value="0"/>
+          <LookupListIndex index="1" value="1"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="4">
+        <FeatureTag value="liga"/>
+        <Feature>
+          <!-- LookupCount=3 -->
+          <LookupListIndex index="0" value="0"/>
+          <LookupListIndex index="1" value="1"/>
+          <LookupListIndex index="2" value="2"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="f">
+            <Ligature components="f,i" glyph="f_f_i"/>
+            <Ligature components="i" glyph="f_i"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="f">
+            <Ligature components="f,l" glyph="f_f_l"/>
+            <Ligature components="f" glyph="f_f"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="f">
+            <Ligature components="l" glyph="f_l"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="s">
+            <Ligature components="s" glyph="germandbls"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="c">
+            <Ligature components="t" glyph="c_t"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec5d1.fea b/Tests/feaLib/data/spec5d1.fea
new file mode 100644
index 0000000..cf7d76f
--- /dev/null
+++ b/Tests/feaLib/data/spec5d1.fea
@@ -0,0 +1,23 @@
+# OpenType Feature File specification, section 5.d, example 1.
+# http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
+
+feature F1 {
+  sub [one one.oldstyle] [slash fraction] [two two.oldstyle] by onehalf;
+} F1;
+
+# 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 in <glyph sequence>.  Thus, the above
+# example produces an identical representation in the font as if all
+# the sequences were manually enumerated by the font editor:
+feature F2 {
+  sub one slash two by onehalf;
+  sub one.oldstyle slash two by onehalf;
+  sub one fraction two by onehalf;
+  sub one.oldstyle fraction two by onehalf;
+  sub one slash two.oldstyle by onehalf;
+  sub one.oldstyle slash two.oldstyle by onehalf;
+  sub one fraction two.oldstyle by onehalf;
+  sub one.oldstyle fraction two.oldstyle by onehalf;
+} F2;
diff --git a/Tests/feaLib/data/spec5d1.ttx b/Tests/feaLib/data/spec5d1.ttx
new file mode 100644
index 0000000..77dfc93
--- /dev/null
+++ b/Tests/feaLib/data/spec5d1.ttx
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=2 -->
+            <FeatureIndex index="0" value="0"/>
+            <FeatureIndex index="1" value="1"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=2 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="F1  "/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="F2  "/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="one">
+            <Ligature components="fraction,two" glyph="onehalf"/>
+            <Ligature components="fraction,two.oldstyle" glyph="onehalf"/>
+            <Ligature components="slash,two" glyph="onehalf"/>
+            <Ligature components="slash,two.oldstyle" glyph="onehalf"/>
+          </LigatureSet>
+          <LigatureSet glyph="one.oldstyle">
+            <Ligature components="fraction,two" glyph="onehalf"/>
+            <Ligature components="fraction,two.oldstyle" glyph="onehalf"/>
+            <Ligature components="slash,two" glyph="onehalf"/>
+            <Ligature components="slash,two.oldstyle" glyph="onehalf"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="one">
+            <Ligature components="fraction,two" glyph="onehalf"/>
+            <Ligature components="fraction,two.oldstyle" glyph="onehalf"/>
+            <Ligature components="slash,two" glyph="onehalf"/>
+            <Ligature components="slash,two.oldstyle" glyph="onehalf"/>
+          </LigatureSet>
+          <LigatureSet glyph="one.oldstyle">
+            <Ligature components="fraction,two" glyph="onehalf"/>
+            <Ligature components="fraction,two.oldstyle" glyph="onehalf"/>
+            <Ligature components="slash,two" glyph="onehalf"/>
+            <Ligature components="slash,two.oldstyle" glyph="onehalf"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec5d2.fea b/Tests/feaLib/data/spec5d2.fea
new file mode 100644
index 0000000..7fd7eab
--- /dev/null
+++ b/Tests/feaLib/data/spec5d2.fea
@@ -0,0 +1,22 @@
+# OpenType Feature File specification, section 5.d, example 2.
+# http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
+
+# A contiguous set of ligature rules does not need to be ordered in
+# any particular way by the font editor; the implementation software
+# must do the appropriate sorting.
+
+# So:
+feature F1 {
+    sub f f by f_f;
+    sub f i by f_i;
+    sub f f i by f_f_i;
+    sub o f f i by o_f_f_i;
+} F1;
+
+# will produce an identical representation in the font as:
+feature F2 {
+    sub o f f i by o_f_f_i;
+    sub f f i by f_f_i;
+    sub f f by f_f;
+    sub f i by f_i;
+} F2;
diff --git a/Tests/feaLib/data/spec5d2.ttx b/Tests/feaLib/data/spec5d2.ttx
new file mode 100644
index 0000000..7bcd4da
--- /dev/null
+++ b/Tests/feaLib/data/spec5d2.ttx
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=2 -->
+            <FeatureIndex index="0" value="0"/>
+            <FeatureIndex index="1" value="1"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=2 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="F1  "/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="F2  "/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="f">
+            <Ligature components="f,i" glyph="f_f_i"/>
+            <Ligature components="f" glyph="f_f"/>
+            <Ligature components="i" glyph="f_i"/>
+          </LigatureSet>
+          <LigatureSet glyph="o">
+            <Ligature components="f,f,i" glyph="o_f_f_i"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="f">
+            <Ligature components="f,i" glyph="f_f_i"/>
+            <Ligature components="f" glyph="f_f"/>
+            <Ligature components="i" glyph="f_i"/>
+          </LigatureSet>
+          <LigatureSet glyph="o">
+            <Ligature components="f,f,i" glyph="o_f_f_i"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec5f_ii_1.fea b/Tests/feaLib/data/spec5f_ii_1.fea
new file mode 100644
index 0000000..2d5c0df
--- /dev/null
+++ b/Tests/feaLib/data/spec5f_ii_1.fea
@@ -0,0 +1,9 @@
+# OpenType Feature File specification, section 5.f.ii, example 1
+# "Specifying exceptions to the Chain Sub rule"
+# http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
+
+feature test {
+    ignore sub f [a e] d';
+    ignore sub a d' d;
+    sub [a e n] d' by d.alt;
+} test;
diff --git a/Tests/feaLib/data/spec5f_ii_1.ttx b/Tests/feaLib/data/spec5f_ii_1.ttx
new file mode 100644
index 0000000..8f7f94b
--- /dev/null
+++ b/Tests/feaLib/data/spec5f_ii_1.ttx
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=3 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=2 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="a"/>
+            <Glyph value="e"/>
+          </BacktrackCoverage>
+          <BacktrackCoverage index="1">
+            <Glyph value="f"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="d"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=0 -->
+        </ChainContextSubst>
+        <ChainContextSubst index="1" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="a"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="d"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="d"/>
+          </LookAheadCoverage>
+          <!-- SubstCount=0 -->
+        </ChainContextSubst>
+        <ChainContextSubst index="2" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="a"/>
+            <Glyph value="e"/>
+            <Glyph value="n"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="d"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="d" out="d.alt"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec5f_ii_2.fea b/Tests/feaLib/data/spec5f_ii_2.fea
new file mode 100644
index 0000000..b20a74c
--- /dev/null
+++ b/Tests/feaLib/data/spec5f_ii_2.fea
@@ -0,0 +1,9 @@
+# OpenType Feature File specification, section 5.f.ii, example 2
+# "Specifying exceptions to the Chain Sub rule"
+# http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
+
+feature test {
+    @LETTER = [a-z];
+    ignore sub @LETTER f' i';
+    sub f' i' by f_i.begin;
+} test;
diff --git a/Tests/feaLib/data/spec5f_ii_2.ttx b/Tests/feaLib/data/spec5f_ii_2.ttx
new file mode 100644
index 0000000..0a1511d
--- /dev/null
+++ b/Tests/feaLib/data/spec5f_ii_2.ttx
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=2 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="a"/>
+            <Glyph value="b"/>
+            <Glyph value="c"/>
+            <Glyph value="d"/>
+            <Glyph value="e"/>
+            <Glyph value="f"/>
+            <Glyph value="g"/>
+            <Glyph value="h"/>
+            <Glyph value="i"/>
+            <Glyph value="j"/>
+            <Glyph value="k"/>
+            <Glyph value="l"/>
+            <Glyph value="m"/>
+            <Glyph value="n"/>
+            <Glyph value="o"/>
+            <Glyph value="p"/>
+            <Glyph value="q"/>
+            <Glyph value="r"/>
+            <Glyph value="s"/>
+            <Glyph value="t"/>
+            <Glyph value="u"/>
+            <Glyph value="v"/>
+            <Glyph value="w"/>
+            <Glyph value="x"/>
+            <Glyph value="y"/>
+            <Glyph value="z"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=2 -->
+          <InputCoverage index="0">
+            <Glyph value="f"/>
+          </InputCoverage>
+          <InputCoverage index="1">
+            <Glyph value="i"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=0 -->
+        </ChainContextSubst>
+        <ChainContextSubst index="1" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=2 -->
+          <InputCoverage index="0">
+            <Glyph value="f"/>
+          </InputCoverage>
+          <InputCoverage index="1">
+            <Glyph value="i"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="f">
+            <Ligature components="i" glyph="f_i.begin"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec5f_ii_3.fea b/Tests/feaLib/data/spec5f_ii_3.fea
new file mode 100644
index 0000000..5fd1991
--- /dev/null
+++ b/Tests/feaLib/data/spec5f_ii_3.fea
@@ -0,0 +1,9 @@
+# OpenType Feature File specification, section 5.f.ii, example 3
+# "Specifying exceptions to the Chain Sub rule"
+# http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
+
+feature test {
+    @LETTER = [a-z];
+    ignore sub @LETTER a' n' d', a' n' d' @LETTER;
+    sub a' n' d' by a_n_d;
+} test;
diff --git a/Tests/feaLib/data/spec5f_ii_3.ttx b/Tests/feaLib/data/spec5f_ii_3.ttx
new file mode 100644
index 0000000..a550f0b
--- /dev/null
+++ b/Tests/feaLib/data/spec5f_ii_3.ttx
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=3 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="a"/>
+            <Glyph value="b"/>
+            <Glyph value="c"/>
+            <Glyph value="d"/>
+            <Glyph value="e"/>
+            <Glyph value="f"/>
+            <Glyph value="g"/>
+            <Glyph value="h"/>
+            <Glyph value="i"/>
+            <Glyph value="j"/>
+            <Glyph value="k"/>
+            <Glyph value="l"/>
+            <Glyph value="m"/>
+            <Glyph value="n"/>
+            <Glyph value="o"/>
+            <Glyph value="p"/>
+            <Glyph value="q"/>
+            <Glyph value="r"/>
+            <Glyph value="s"/>
+            <Glyph value="t"/>
+            <Glyph value="u"/>
+            <Glyph value="v"/>
+            <Glyph value="w"/>
+            <Glyph value="x"/>
+            <Glyph value="y"/>
+            <Glyph value="z"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=3 -->
+          <InputCoverage index="0">
+            <Glyph value="a"/>
+          </InputCoverage>
+          <InputCoverage index="1">
+            <Glyph value="n"/>
+          </InputCoverage>
+          <InputCoverage index="2">
+            <Glyph value="d"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=0 -->
+        </ChainContextSubst>
+        <ChainContextSubst index="1" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=3 -->
+          <InputCoverage index="0">
+            <Glyph value="a"/>
+          </InputCoverage>
+          <InputCoverage index="1">
+            <Glyph value="n"/>
+          </InputCoverage>
+          <InputCoverage index="2">
+            <Glyph value="d"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="a"/>
+            <Glyph value="b"/>
+            <Glyph value="c"/>
+            <Glyph value="d"/>
+            <Glyph value="e"/>
+            <Glyph value="f"/>
+            <Glyph value="g"/>
+            <Glyph value="h"/>
+            <Glyph value="i"/>
+            <Glyph value="j"/>
+            <Glyph value="k"/>
+            <Glyph value="l"/>
+            <Glyph value="m"/>
+            <Glyph value="n"/>
+            <Glyph value="o"/>
+            <Glyph value="p"/>
+            <Glyph value="q"/>
+            <Glyph value="r"/>
+            <Glyph value="s"/>
+            <Glyph value="t"/>
+            <Glyph value="u"/>
+            <Glyph value="v"/>
+            <Glyph value="w"/>
+            <Glyph value="x"/>
+            <Glyph value="y"/>
+            <Glyph value="z"/>
+          </LookAheadCoverage>
+          <!-- SubstCount=0 -->
+        </ChainContextSubst>
+        <ChainContextSubst index="2" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=3 -->
+          <InputCoverage index="0">
+            <Glyph value="a"/>
+          </InputCoverage>
+          <InputCoverage index="1">
+            <Glyph value="n"/>
+          </InputCoverage>
+          <InputCoverage index="2">
+            <Glyph value="d"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="a">
+            <Ligature components="n,d" glyph="a_n_d"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec5f_ii_4.fea b/Tests/feaLib/data/spec5f_ii_4.fea
new file mode 100644
index 0000000..731a1f6
--- /dev/null
+++ b/Tests/feaLib/data/spec5f_ii_4.fea
@@ -0,0 +1,23 @@
+# OpenType Feature File specification, section 5.f.ii, example 4
+# "Specifying exceptions to the Chain Sub rule"
+# http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
+
+@LETTER = [A-Z a-z];
+
+feature cswh {
+
+    # --- Glyph classes used in this feature:
+    @BEGINNINGS = [A-N P-Z T_h m];
+    @BEGINNINGS_SWASH = [A.swash-N.swash P.swash-Z.swash T_h.swash m.begin];
+    @ENDINGS = [a e z];
+    @ENDINGS_SWASH = [a.end e.end z.end];
+
+    # --- Beginning-of-word swashes:
+    ignore sub @LETTER @BEGINNINGS';
+    sub @BEGINNINGS' by @BEGINNINGS_SWASH;
+ 
+    # --- End-of-word swashes:
+    ignore sub @ENDINGS' @LETTER;
+    sub @ENDINGS' by @ENDINGS_SWASH;
+
+} cswh;
diff --git a/Tests/feaLib/data/spec5f_ii_4.ttx b/Tests/feaLib/data/spec5f_ii_4.ttx
new file mode 100644
index 0000000..f358161
--- /dev/null
+++ b/Tests/feaLib/data/spec5f_ii_4.ttx
@@ -0,0 +1,285 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="cswh"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=4 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="A"/>
+            <Glyph value="B"/>
+            <Glyph value="C"/>
+            <Glyph value="D"/>
+            <Glyph value="E"/>
+            <Glyph value="F"/>
+            <Glyph value="G"/>
+            <Glyph value="H"/>
+            <Glyph value="I"/>
+            <Glyph value="J"/>
+            <Glyph value="K"/>
+            <Glyph value="L"/>
+            <Glyph value="M"/>
+            <Glyph value="N"/>
+            <Glyph value="O"/>
+            <Glyph value="P"/>
+            <Glyph value="Q"/>
+            <Glyph value="R"/>
+            <Glyph value="S"/>
+            <Glyph value="T"/>
+            <Glyph value="U"/>
+            <Glyph value="V"/>
+            <Glyph value="W"/>
+            <Glyph value="X"/>
+            <Glyph value="Y"/>
+            <Glyph value="Z"/>
+            <Glyph value="a"/>
+            <Glyph value="b"/>
+            <Glyph value="c"/>
+            <Glyph value="d"/>
+            <Glyph value="e"/>
+            <Glyph value="f"/>
+            <Glyph value="g"/>
+            <Glyph value="h"/>
+            <Glyph value="i"/>
+            <Glyph value="j"/>
+            <Glyph value="k"/>
+            <Glyph value="l"/>
+            <Glyph value="m"/>
+            <Glyph value="n"/>
+            <Glyph value="o"/>
+            <Glyph value="p"/>
+            <Glyph value="q"/>
+            <Glyph value="r"/>
+            <Glyph value="s"/>
+            <Glyph value="t"/>
+            <Glyph value="u"/>
+            <Glyph value="v"/>
+            <Glyph value="w"/>
+            <Glyph value="x"/>
+            <Glyph value="y"/>
+            <Glyph value="z"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="A"/>
+            <Glyph value="B"/>
+            <Glyph value="C"/>
+            <Glyph value="D"/>
+            <Glyph value="E"/>
+            <Glyph value="F"/>
+            <Glyph value="G"/>
+            <Glyph value="H"/>
+            <Glyph value="I"/>
+            <Glyph value="J"/>
+            <Glyph value="K"/>
+            <Glyph value="L"/>
+            <Glyph value="M"/>
+            <Glyph value="N"/>
+            <Glyph value="P"/>
+            <Glyph value="Q"/>
+            <Glyph value="R"/>
+            <Glyph value="S"/>
+            <Glyph value="T"/>
+            <Glyph value="U"/>
+            <Glyph value="V"/>
+            <Glyph value="W"/>
+            <Glyph value="X"/>
+            <Glyph value="Y"/>
+            <Glyph value="Z"/>
+            <Glyph value="m"/>
+            <Glyph value="T_h"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=0 -->
+        </ChainContextSubst>
+        <ChainContextSubst index="1" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="A"/>
+            <Glyph value="B"/>
+            <Glyph value="C"/>
+            <Glyph value="D"/>
+            <Glyph value="E"/>
+            <Glyph value="F"/>
+            <Glyph value="G"/>
+            <Glyph value="H"/>
+            <Glyph value="I"/>
+            <Glyph value="J"/>
+            <Glyph value="K"/>
+            <Glyph value="L"/>
+            <Glyph value="M"/>
+            <Glyph value="N"/>
+            <Glyph value="P"/>
+            <Glyph value="Q"/>
+            <Glyph value="R"/>
+            <Glyph value="S"/>
+            <Glyph value="T"/>
+            <Glyph value="U"/>
+            <Glyph value="V"/>
+            <Glyph value="W"/>
+            <Glyph value="X"/>
+            <Glyph value="Y"/>
+            <Glyph value="Z"/>
+            <Glyph value="m"/>
+            <Glyph value="T_h"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+        <ChainContextSubst index="2" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="a"/>
+            <Glyph value="e"/>
+            <Glyph value="z"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="A"/>
+            <Glyph value="B"/>
+            <Glyph value="C"/>
+            <Glyph value="D"/>
+            <Glyph value="E"/>
+            <Glyph value="F"/>
+            <Glyph value="G"/>
+            <Glyph value="H"/>
+            <Glyph value="I"/>
+            <Glyph value="J"/>
+            <Glyph value="K"/>
+            <Glyph value="L"/>
+            <Glyph value="M"/>
+            <Glyph value="N"/>
+            <Glyph value="O"/>
+            <Glyph value="P"/>
+            <Glyph value="Q"/>
+            <Glyph value="R"/>
+            <Glyph value="S"/>
+            <Glyph value="T"/>
+            <Glyph value="U"/>
+            <Glyph value="V"/>
+            <Glyph value="W"/>
+            <Glyph value="X"/>
+            <Glyph value="Y"/>
+            <Glyph value="Z"/>
+            <Glyph value="a"/>
+            <Glyph value="b"/>
+            <Glyph value="c"/>
+            <Glyph value="d"/>
+            <Glyph value="e"/>
+            <Glyph value="f"/>
+            <Glyph value="g"/>
+            <Glyph value="h"/>
+            <Glyph value="i"/>
+            <Glyph value="j"/>
+            <Glyph value="k"/>
+            <Glyph value="l"/>
+            <Glyph value="m"/>
+            <Glyph value="n"/>
+            <Glyph value="o"/>
+            <Glyph value="p"/>
+            <Glyph value="q"/>
+            <Glyph value="r"/>
+            <Glyph value="s"/>
+            <Glyph value="t"/>
+            <Glyph value="u"/>
+            <Glyph value="v"/>
+            <Glyph value="w"/>
+            <Glyph value="x"/>
+            <Glyph value="y"/>
+            <Glyph value="z"/>
+          </LookAheadCoverage>
+          <!-- SubstCount=0 -->
+        </ChainContextSubst>
+        <ChainContextSubst index="3" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="a"/>
+            <Glyph value="e"/>
+            <Glyph value="z"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="A" out="A.swash"/>
+          <Substitution in="B" out="B.swash"/>
+          <Substitution in="C" out="C.swash"/>
+          <Substitution in="D" out="D.swash"/>
+          <Substitution in="E" out="E.swash"/>
+          <Substitution in="F" out="F.swash"/>
+          <Substitution in="G" out="G.swash"/>
+          <Substitution in="H" out="H.swash"/>
+          <Substitution in="I" out="I.swash"/>
+          <Substitution in="J" out="J.swash"/>
+          <Substitution in="K" out="K.swash"/>
+          <Substitution in="L" out="L.swash"/>
+          <Substitution in="M" out="M.swash"/>
+          <Substitution in="N" out="N.swash"/>
+          <Substitution in="P" out="P.swash"/>
+          <Substitution in="Q" out="Q.swash"/>
+          <Substitution in="R" out="R.swash"/>
+          <Substitution in="S" out="S.swash"/>
+          <Substitution in="T" out="T.swash"/>
+          <Substitution in="T_h" out="T_h.swash"/>
+          <Substitution in="U" out="U.swash"/>
+          <Substitution in="V" out="V.swash"/>
+          <Substitution in="W" out="W.swash"/>
+          <Substitution in="X" out="X.swash"/>
+          <Substitution in="Y" out="Y.swash"/>
+          <Substitution in="Z" out="Z.swash"/>
+          <Substitution in="a" out="a.end"/>
+          <Substitution in="e" out="e.end"/>
+          <Substitution in="m" out="m.begin"/>
+          <Substitution in="z" out="z.end"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec5fi1.fea b/Tests/feaLib/data/spec5fi1.fea
new file mode 100644
index 0000000..f5de2dc
--- /dev/null
+++ b/Tests/feaLib/data/spec5fi1.fea
@@ -0,0 +1,20 @@
+# OpenType Feature File specification, section 5.f.i, example 1
+# "Specifying a Chain Sub rule and marking sub-runs"
+# http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
+
+languagesystem latn dflt;
+
+lookup CNTXT_LIGS {
+    sub f i by f_i;
+    sub c t by c_t;
+} CNTXT_LIGS;
+
+lookup CNTXT_SUB {
+    sub n by n.end;
+    sub s by s.end;
+} CNTXT_SUB;
+ 
+feature test {
+    sub [a e i o u] f' lookup CNTXT_LIGS i' n' lookup CNTXT_SUB;
+    sub [a e i o u] c' lookup CNTXT_LIGS t' s' lookup CNTXT_SUB;
+} test;
diff --git a/Tests/feaLib/data/spec5fi1.ttx b/Tests/feaLib/data/spec5fi1.ttx
new file mode 100644
index 0000000..356ddc7
--- /dev/null
+++ b/Tests/feaLib/data/spec5fi1.ttx
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="2"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=3 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="c">
+            <Ligature components="t" glyph="c_t"/>
+          </LigatureSet>
+          <LigatureSet glyph="f">
+            <Ligature components="i" glyph="f_i"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="n" out="n.end"/>
+          <Substitution in="s" out="s.end"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=2 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="a"/>
+            <Glyph value="e"/>
+            <Glyph value="i"/>
+            <Glyph value="o"/>
+            <Glyph value="u"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=3 -->
+          <InputCoverage index="0">
+            <Glyph value="f"/>
+          </InputCoverage>
+          <InputCoverage index="1">
+            <Glyph value="i"/>
+          </InputCoverage>
+          <InputCoverage index="2">
+            <Glyph value="n"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=2 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </SubstLookupRecord>
+          <SubstLookupRecord index="1">
+            <SequenceIndex value="2"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+        <ChainContextSubst index="1" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="a"/>
+            <Glyph value="e"/>
+            <Glyph value="i"/>
+            <Glyph value="o"/>
+            <Glyph value="u"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=3 -->
+          <InputCoverage index="0">
+            <Glyph value="c"/>
+          </InputCoverage>
+          <InputCoverage index="1">
+            <Glyph value="t"/>
+          </InputCoverage>
+          <InputCoverage index="2">
+            <Glyph value="s"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=2 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </SubstLookupRecord>
+          <SubstLookupRecord index="1">
+            <SequenceIndex value="2"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec5fi2.fea b/Tests/feaLib/data/spec5fi2.fea
new file mode 100644
index 0000000..87dbf2d
--- /dev/null
+++ b/Tests/feaLib/data/spec5fi2.fea
@@ -0,0 +1,11 @@
+# OpenType Feature File specification, section 5.f.i, example 2
+# "Specifying a Chain Sub rule and marking sub-runs"
+# http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
+
+languagesystem latn dflt;
+
+feature test {
+#test-fea2fea: lookupflag RightToLeft IgnoreBaseGlyphs IgnoreLigatures;
+    lookupflag 7;
+    sub [a e n] d' by d.alt;
+} test;
diff --git a/Tests/feaLib/data/spec5fi2.ttx b/Tests/feaLib/data/spec5fi2.ttx
new file mode 100644
index 0000000..0842cf9
--- /dev/null
+++ b/Tests/feaLib/data/spec5fi2.ttx
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="6"/>
+        <LookupFlag value="7"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="a"/>
+            <Glyph value="e"/>
+            <Glyph value="n"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="d"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="7"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="d" out="d.alt"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec5fi3.fea b/Tests/feaLib/data/spec5fi3.fea
new file mode 100644
index 0000000..e47a6f8
--- /dev/null
+++ b/Tests/feaLib/data/spec5fi3.fea
@@ -0,0 +1,9 @@
+# OpenType Feature File specification, section 5.f.i, example 3
+# "Specifying a Chain Sub rule and marking sub-runs"
+# http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
+
+languagesystem latn dflt;
+
+feature test {
+    sub [A-Z] [A.sc-Z.sc]' by [a-z];
+} test;
diff --git a/Tests/feaLib/data/spec5fi3.ttx b/Tests/feaLib/data/spec5fi3.ttx
new file mode 100644
index 0000000..1b3cd26
--- /dev/null
+++ b/Tests/feaLib/data/spec5fi3.ttx
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="A"/>
+            <Glyph value="B"/>
+            <Glyph value="C"/>
+            <Glyph value="D"/>
+            <Glyph value="E"/>
+            <Glyph value="F"/>
+            <Glyph value="G"/>
+            <Glyph value="H"/>
+            <Glyph value="I"/>
+            <Glyph value="J"/>
+            <Glyph value="K"/>
+            <Glyph value="L"/>
+            <Glyph value="M"/>
+            <Glyph value="N"/>
+            <Glyph value="O"/>
+            <Glyph value="P"/>
+            <Glyph value="Q"/>
+            <Glyph value="R"/>
+            <Glyph value="S"/>
+            <Glyph value="T"/>
+            <Glyph value="U"/>
+            <Glyph value="V"/>
+            <Glyph value="W"/>
+            <Glyph value="X"/>
+            <Glyph value="Y"/>
+            <Glyph value="Z"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="A.sc"/>
+            <Glyph value="B.sc"/>
+            <Glyph value="C.sc"/>
+            <Glyph value="D.sc"/>
+            <Glyph value="E.sc"/>
+            <Glyph value="F.sc"/>
+            <Glyph value="G.sc"/>
+            <Glyph value="H.sc"/>
+            <Glyph value="I.sc"/>
+            <Glyph value="J.sc"/>
+            <Glyph value="K.sc"/>
+            <Glyph value="L.sc"/>
+            <Glyph value="M.sc"/>
+            <Glyph value="N.sc"/>
+            <Glyph value="O.sc"/>
+            <Glyph value="P.sc"/>
+            <Glyph value="Q.sc"/>
+            <Glyph value="R.sc"/>
+            <Glyph value="S.sc"/>
+            <Glyph value="T.sc"/>
+            <Glyph value="U.sc"/>
+            <Glyph value="V.sc"/>
+            <Glyph value="W.sc"/>
+            <Glyph value="X.sc"/>
+            <Glyph value="Y.sc"/>
+            <Glyph value="Z.sc"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="A.sc" out="a"/>
+          <Substitution in="B.sc" out="b"/>
+          <Substitution in="C.sc" out="c"/>
+          <Substitution in="D.sc" out="d"/>
+          <Substitution in="E.sc" out="e"/>
+          <Substitution in="F.sc" out="f"/>
+          <Substitution in="G.sc" out="g"/>
+          <Substitution in="H.sc" out="h"/>
+          <Substitution in="I.sc" out="i"/>
+          <Substitution in="J.sc" out="j"/>
+          <Substitution in="K.sc" out="k"/>
+          <Substitution in="L.sc" out="l"/>
+          <Substitution in="M.sc" out="m"/>
+          <Substitution in="N.sc" out="n"/>
+          <Substitution in="O.sc" out="o"/>
+          <Substitution in="P.sc" out="p"/>
+          <Substitution in="Q.sc" out="q"/>
+          <Substitution in="R.sc" out="r"/>
+          <Substitution in="S.sc" out="s"/>
+          <Substitution in="T.sc" out="t"/>
+          <Substitution in="U.sc" out="u"/>
+          <Substitution in="V.sc" out="v"/>
+          <Substitution in="W.sc" out="w"/>
+          <Substitution in="X.sc" out="x"/>
+          <Substitution in="Y.sc" out="y"/>
+          <Substitution in="Z.sc" out="z"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec5fi4.fea b/Tests/feaLib/data/spec5fi4.fea
new file mode 100644
index 0000000..845c560
--- /dev/null
+++ b/Tests/feaLib/data/spec5fi4.fea
@@ -0,0 +1,9 @@
+# OpenType Feature File specification, section 5.f.i, example 4
+# "Specifying a Chain Sub rule and marking sub-runs"
+# http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
+
+languagesystem latn dflt;
+
+feature test {
+    sub [e e.begin]' t' c by ampersand;
+} test;
diff --git a/Tests/feaLib/data/spec5fi4.ttx b/Tests/feaLib/data/spec5fi4.ttx
new file mode 100644
index 0000000..a0701db
--- /dev/null
+++ b/Tests/feaLib/data/spec5fi4.ttx
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=2 -->
+          <InputCoverage index="0">
+            <Glyph value="e"/>
+            <Glyph value="e.begin"/>
+          </InputCoverage>
+          <InputCoverage index="1">
+            <Glyph value="t"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="c"/>
+          </LookAheadCoverage>
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="e">
+            <Ligature components="t" glyph="ampersand"/>
+          </LigatureSet>
+          <LigatureSet glyph="e.begin">
+            <Ligature components="t" glyph="ampersand"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec5h1.fea b/Tests/feaLib/data/spec5h1.fea
new file mode 100644
index 0000000..8d2f516
--- /dev/null
+++ b/Tests/feaLib/data/spec5h1.fea
@@ -0,0 +1,9 @@
+# OpenType Feature File specification, section 5.h, example 1.
+# http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
+
+languagesystem DFLT dflt;
+
+feature test {
+#test-fea2fea: rsub [a e n] d' by d.alt;
+    reversesub [a e n] d' by d.alt;
+} test;
diff --git a/Tests/feaLib/data/spec5h1.ttx b/Tests/feaLib/data/spec5h1.ttx
new file mode 100644
index 0000000..20278f5
--- /dev/null
+++ b/Tests/feaLib/data/spec5h1.ttx
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ReverseChainSingleSubst index="0" Format="1">
+          <Coverage>
+            <Glyph value="d"/>
+          </Coverage>
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="a"/>
+            <Glyph value="e"/>
+            <Glyph value="n"/>
+          </BacktrackCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- GlyphCount=1 -->
+          <Substitute index="0" value="d.alt"/>
+        </ReverseChainSingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec6b_ii.fea b/Tests/feaLib/data/spec6b_ii.fea
new file mode 100644
index 0000000..59fc7d5
--- /dev/null
+++ b/Tests/feaLib/data/spec6b_ii.fea
@@ -0,0 +1,12 @@
+# OpenType Feature File specification, section 6.b.ii:
+# [GPOS LookupType 2] Enumerating pairs
+# http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
+
+@Y_LC = [y yacute ydieresis];
+@SMALL_PUNC = [comma semicolon period];
+
+feature kern {
+  enum pos @Y_LC semicolon -80;    # specific pairs
+  pos f quoteright 30;         # specific pair
+  pos @Y_LC @SMALL_PUNC -100;  # class pair
+} kern;
diff --git a/Tests/feaLib/data/spec6b_ii.ttx b/Tests/feaLib/data/spec6b_ii.ttx
new file mode 100644
index 0000000..a7131de
--- /dev/null
+++ b/Tests/feaLib/data/spec6b_ii.ttx
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=2 -->
+        <PairPos index="0" Format="1">
+          <Coverage>
+            <Glyph value="f"/>
+            <Glyph value="y"/>
+            <Glyph value="ydieresis"/>
+            <Glyph value="yacute"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=4 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="quoteright"/>
+              <Value1 XAdvance="30"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="1">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="semicolon"/>
+              <Value1 XAdvance="-80"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="2">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="semicolon"/>
+              <Value1 XAdvance="-80"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="3">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="semicolon"/>
+              <Value1 XAdvance="-80"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+        <PairPos index="1" Format="2">
+          <Coverage>
+            <Glyph value="y"/>
+            <Glyph value="ydieresis"/>
+            <Glyph value="yacute"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1>
+          </ClassDef1>
+          <ClassDef2>
+            <ClassDef glyph="comma" class="1"/>
+            <ClassDef glyph="period" class="1"/>
+            <ClassDef glyph="semicolon" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=1 -->
+          <!-- Class2Count=2 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="-100"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec6d2.fea b/Tests/feaLib/data/spec6d2.fea
new file mode 100644
index 0000000..ead224f
--- /dev/null
+++ b/Tests/feaLib/data/spec6d2.fea
@@ -0,0 +1,15 @@
+# OpenType Feature File specification, section 6.d, example 1:
+# [GPOS LookupType 4] Mark-to-Base attachment positioning
+# http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
+
+languagesystem DFLT dflt;
+
+markClass [acute grave] <anchor 150 -10> @TOP_MARKS;
+markClass [dieresis umlaut] <anchor 300 -10> @TOP_MARKS;
+markClass [cedilla] <anchor 300 600> @BOTTOM_MARKS;
+
+feature test {
+    pos base [e o] <anchor 250 450> mark @TOP_MARKS <anchor 250 -12> mark @BOTTOM_MARKS;
+#test-fea2fea: pos base [a u] <anchor 265 450> mark @TOP_MARKS <anchor 250 -10> mark @BOTTOM_MARKS;
+    position base [a u] <anchor 265 450> mark @TOP_MARKS <anchor 250-10> mark @BOTTOM_MARKS;
+} test;
diff --git a/Tests/feaLib/data/spec6d2.ttx b/Tests/feaLib/data/spec6d2.ttx
new file mode 100644
index 0000000..11a09f2
--- /dev/null
+++ b/Tests/feaLib/data/spec6d2.ttx
@@ -0,0 +1,152 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef>
+      <ClassDef glyph="a" class="1"/>
+      <ClassDef glyph="acute" class="3"/>
+      <ClassDef glyph="cedilla" class="3"/>
+      <ClassDef glyph="dieresis" class="3"/>
+      <ClassDef glyph="e" class="1"/>
+      <ClassDef glyph="grave" class="3"/>
+      <ClassDef glyph="o" class="1"/>
+      <ClassDef glyph="u" class="1"/>
+      <ClassDef glyph="umlaut" class="3"/>
+    </GlyphClassDef>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkBasePos index="0" Format="1">
+          <MarkCoverage>
+            <Glyph value="grave"/>
+            <Glyph value="acute"/>
+            <Glyph value="dieresis"/>
+            <Glyph value="cedilla"/>
+            <Glyph value="umlaut"/>
+          </MarkCoverage>
+          <BaseCoverage>
+            <Glyph value="a"/>
+            <Glyph value="e"/>
+            <Glyph value="o"/>
+            <Glyph value="u"/>
+          </BaseCoverage>
+          <!-- ClassCount=2 -->
+          <MarkArray>
+            <!-- MarkCount=5 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="150"/>
+                <YCoordinate value="-10"/>
+              </MarkAnchor>
+            </MarkRecord>
+            <MarkRecord index="1">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="150"/>
+                <YCoordinate value="-10"/>
+              </MarkAnchor>
+            </MarkRecord>
+            <MarkRecord index="2">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="300"/>
+                <YCoordinate value="-10"/>
+              </MarkAnchor>
+            </MarkRecord>
+            <MarkRecord index="3">
+              <Class value="1"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="300"/>
+                <YCoordinate value="600"/>
+              </MarkAnchor>
+            </MarkRecord>
+            <MarkRecord index="4">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="300"/>
+                <YCoordinate value="-10"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=4 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="265"/>
+                <YCoordinate value="450"/>
+              </BaseAnchor>
+              <BaseAnchor index="1" Format="1">
+                <XCoordinate value="250"/>
+                <YCoordinate value="-10"/>
+              </BaseAnchor>
+            </BaseRecord>
+            <BaseRecord index="1">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="250"/>
+                <YCoordinate value="450"/>
+              </BaseAnchor>
+              <BaseAnchor index="1" Format="1">
+                <XCoordinate value="250"/>
+                <YCoordinate value="-12"/>
+              </BaseAnchor>
+            </BaseRecord>
+            <BaseRecord index="2">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="250"/>
+                <YCoordinate value="450"/>
+              </BaseAnchor>
+              <BaseAnchor index="1" Format="1">
+                <XCoordinate value="250"/>
+                <YCoordinate value="-12"/>
+              </BaseAnchor>
+            </BaseRecord>
+            <BaseRecord index="3">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="265"/>
+                <YCoordinate value="450"/>
+              </BaseAnchor>
+              <BaseAnchor index="1" Format="1">
+                <XCoordinate value="250"/>
+                <YCoordinate value="-10"/>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec6e.fea b/Tests/feaLib/data/spec6e.fea
new file mode 100644
index 0000000..ed956c8
--- /dev/null
+++ b/Tests/feaLib/data/spec6e.fea
@@ -0,0 +1,10 @@
+languagesystem DFLT dflt;
+
+markClass sukun <anchor 261 488> @TOP_MARKS;
+markClass kasratan <anchor 346 -98> @BOTTOM_MARKS;
+
+feature test {
+    pos ligature lam_meem_jeem <anchor 625 1800> mark @TOP_MARKS    # mark above lam
+        ligComponent <anchor 376 -368> mark @BOTTOM_MARKS    # mark below meem
+        ligComponent <anchor NULL>;   # jeem has no marks
+} test;
diff --git a/Tests/feaLib/data/spec6e.ttx b/Tests/feaLib/data/spec6e.ttx
new file mode 100644
index 0000000..9631910
--- /dev/null
+++ b/Tests/feaLib/data/spec6e.ttx
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef>
+      <ClassDef glyph="kasratan" class="3"/>
+      <ClassDef glyph="lam_meem_jeem" class="2"/>
+      <ClassDef glyph="sukun" class="3"/>
+    </GlyphClassDef>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkLigPos index="0" Format="1">
+          <MarkCoverage>
+            <Glyph value="sukun"/>
+            <Glyph value="kasratan"/>
+          </MarkCoverage>
+          <LigatureCoverage>
+            <Glyph value="lam_meem_jeem"/>
+          </LigatureCoverage>
+          <!-- ClassCount=2 -->
+          <MarkArray>
+            <!-- MarkCount=2 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="261"/>
+                <YCoordinate value="488"/>
+              </MarkAnchor>
+            </MarkRecord>
+            <MarkRecord index="1">
+              <Class value="1"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="346"/>
+                <YCoordinate value="-98"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <LigatureArray>
+            <!-- LigatureCount=1 -->
+            <LigatureAttach index="0">
+              <!-- ComponentCount=3 -->
+              <ComponentRecord index="0">
+                <LigatureAnchor index="0" Format="1">
+                  <XCoordinate value="625"/>
+                  <YCoordinate value="1800"/>
+                </LigatureAnchor>
+                <LigatureAnchor index="1" empty="1"/>
+              </ComponentRecord>
+              <ComponentRecord index="1">
+                <LigatureAnchor index="0" empty="1"/>
+                <LigatureAnchor index="1" Format="1">
+                  <XCoordinate value="376"/>
+                  <YCoordinate value="-368"/>
+                </LigatureAnchor>
+              </ComponentRecord>
+              <ComponentRecord index="2">
+                <LigatureAnchor index="0" empty="1"/>
+                <LigatureAnchor index="1" empty="1"/>
+              </ComponentRecord>
+            </LigatureAttach>
+          </LigatureArray>
+        </MarkLigPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec6f.fea b/Tests/feaLib/data/spec6f.fea
new file mode 100644
index 0000000..8d32008
--- /dev/null
+++ b/Tests/feaLib/data/spec6f.fea
@@ -0,0 +1,6 @@
+languagesystem DFLT dflt;
+
+feature test {
+    markClass damma <anchor 189 -103> @MARK_CLASS_1;
+    pos mark hamza <anchor 221 301> mark @MARK_CLASS_1;
+} test;
diff --git a/Tests/feaLib/data/spec6f.ttx b/Tests/feaLib/data/spec6f.ttx
new file mode 100644
index 0000000..18a956b
--- /dev/null
+++ b/Tests/feaLib/data/spec6f.ttx
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef>
+      <ClassDef glyph="damma" class="3"/>
+      <ClassDef glyph="hamza" class="3"/>
+    </GlyphClassDef>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkMarkPos index="0" Format="1">
+          <Mark1Coverage>
+            <Glyph value="damma"/>
+          </Mark1Coverage>
+          <Mark2Coverage>
+            <Glyph value="hamza"/>
+          </Mark2Coverage>
+          <!-- ClassCount=1 -->
+          <Mark1Array>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="189"/>
+                <YCoordinate value="-103"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </Mark1Array>
+          <Mark2Array>
+            <!-- Mark2Count=1 -->
+            <Mark2Record index="0">
+              <Mark2Anchor index="0" Format="1">
+                <XCoordinate value="221"/>
+                <YCoordinate value="301"/>
+              </Mark2Anchor>
+            </Mark2Record>
+          </Mark2Array>
+        </MarkMarkPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec6h_ii.fea b/Tests/feaLib/data/spec6h_ii.fea
new file mode 100644
index 0000000..36a1f03
--- /dev/null
+++ b/Tests/feaLib/data/spec6h_ii.fea
@@ -0,0 +1,21 @@
+# OpenType Feature File specification, section 6.h.ii:
+# Specifying Contextual Positioning with explicit lookup references
+# http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
+
+languagesystem DFLT dflt;
+
+markClass [acute grave] <anchor 150 -10> @ALL_MARKS;
+
+lookup CNTXT_PAIR_POS {
+    pos T o -10;
+    pos T c -12;
+} CNTXT_PAIR_POS;
+ 
+lookup CNTXT_MARK_TO_BASE {
+    pos base o <anchor 250 450> mark @ALL_MARKS;
+    pos base c <anchor 250 450> mark @ALL_MARKS;
+} CNTXT_MARK_TO_BASE;
+
+feature test {
+    pos T' lookup CNTXT_PAIR_POS [o c]' @ALL_MARKS' lookup CNTXT_MARK_TO_BASE;
+} test;
diff --git a/Tests/feaLib/data/spec6h_ii.ttx b/Tests/feaLib/data/spec6h_ii.ttx
new file mode 100644
index 0000000..e8ec85f
--- /dev/null
+++ b/Tests/feaLib/data/spec6h_ii.ttx
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef>
+      <ClassDef glyph="acute" class="3"/>
+      <ClassDef glyph="c" class="1"/>
+      <ClassDef glyph="grave" class="3"/>
+      <ClassDef glyph="o" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="2"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=3 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage>
+            <Glyph value="T"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=2 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="c"/>
+              <Value1 XAdvance="-12"/>
+            </PairValueRecord>
+            <PairValueRecord index="1">
+              <SecondGlyph value="o"/>
+              <Value1 XAdvance="-10"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkBasePos index="0" Format="1">
+          <MarkCoverage>
+            <Glyph value="grave"/>
+            <Glyph value="acute"/>
+          </MarkCoverage>
+          <BaseCoverage>
+            <Glyph value="c"/>
+            <Glyph value="o"/>
+          </BaseCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=2 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="150"/>
+                <YCoordinate value="-10"/>
+              </MarkAnchor>
+            </MarkRecord>
+            <MarkRecord index="1">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="150"/>
+                <YCoordinate value="-10"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=2 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="250"/>
+                <YCoordinate value="450"/>
+              </BaseAnchor>
+            </BaseRecord>
+            <BaseRecord index="1">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="250"/>
+                <YCoordinate value="450"/>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=3 -->
+          <InputCoverage index="0">
+            <Glyph value="T"/>
+          </InputCoverage>
+          <InputCoverage index="1">
+            <Glyph value="c"/>
+            <Glyph value="o"/>
+          </InputCoverage>
+          <InputCoverage index="2">
+            <Glyph value="grave"/>
+            <Glyph value="acute"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- PosCount=2 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </PosLookupRecord>
+          <PosLookupRecord index="1">
+            <SequenceIndex value="2"/>
+            <LookupListIndex value="1"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec6h_iii_1.fea b/Tests/feaLib/data/spec6h_iii_1.fea
new file mode 100644
index 0000000..276fa2f
--- /dev/null
+++ b/Tests/feaLib/data/spec6h_iii_1.fea
@@ -0,0 +1,9 @@
+# OpenType Feature File specification, section 6.h.iii, example 1:
+# Specifying Contextual Positioning with in-line single positioning rules
+# http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
+
+languagesystem DFLT dflt;
+
+feature test {
+    pos [quoteleft quotedblleft] [Y T]' <0 0 20 0> [quoteright quotedblright];
+} test;
diff --git a/Tests/feaLib/data/spec6h_iii_1.ttx b/Tests/feaLib/data/spec6h_iii_1.ttx
new file mode 100644
index 0000000..a12b0f5
--- /dev/null
+++ b/Tests/feaLib/data/spec6h_iii_1.ttx
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="quotedblleft"/>
+            <Glyph value="quoteleft"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="T"/>
+            <Glyph value="Y"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="quotedblright"/>
+            <Glyph value="quoteright"/>
+          </LookAheadCoverage>
+          <!-- PosCount=1 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="T"/>
+            <Glyph value="Y"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="20"/>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec6h_iii_3d.fea b/Tests/feaLib/data/spec6h_iii_3d.fea
new file mode 100644
index 0000000..7b7d8e9
--- /dev/null
+++ b/Tests/feaLib/data/spec6h_iii_3d.fea
@@ -0,0 +1,7 @@
+# OpenType Feature File specification, section 6.h.iii, example 3d:
+# Specifying Contextual Positioning with in-line single positioning rules
+# http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
+
+feature test {
+    pos L' quoteright' -150;
+} test;
diff --git a/Tests/feaLib/data/spec6h_iii_3d.ttx b/Tests/feaLib/data/spec6h_iii_3d.ttx
new file mode 100644
index 0000000..a608f0e
--- /dev/null
+++ b/Tests/feaLib/data/spec6h_iii_3d.ttx
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=2 -->
+          <InputCoverage index="0">
+            <Glyph value="L"/>
+          </InputCoverage>
+          <InputCoverage index="1">
+            <Glyph value="quoteright"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- PosCount=1 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="1"/>
+            <LookupListIndex value="1"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="quoteright"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="-150"/>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec8a.fea b/Tests/feaLib/data/spec8a.fea
new file mode 100644
index 0000000..b4d7dd2
--- /dev/null
+++ b/Tests/feaLib/data/spec8a.fea
@@ -0,0 +1,21 @@
+languagesystem DFLT dflt;
+languagesystem latn dflt;
+languagesystem latn TRK;
+languagesystem cyrl dflt;
+
+feature aalt {
+    feature salt;
+    feature smcp;
+    sub d by d.alt;
+} aalt;
+
+feature smcp {
+    sub [a-c] by [A.sc-C.sc];
+    sub f i by f_i;  # not considered for aalt
+} smcp;
+
+feature salt {
+    sub a from [a.alt1 a.alt2 a.alt3];
+    sub e [c d e]' f by [c.mid d.mid e.mid];
+    sub b by b.alt;
+} salt;
diff --git a/Tests/feaLib/data/spec8a.ttx b/Tests/feaLib/data/spec8a.ttx
new file mode 100644
index 0000000..787ecfa
--- /dev/null
+++ b/Tests/feaLib/data/spec8a.ttx
@@ -0,0 +1,200 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=3 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=3 -->
+            <FeatureIndex index="0" value="0"/>
+            <FeatureIndex index="1" value="1"/>
+            <FeatureIndex index="2" value="2"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="1">
+        <ScriptTag value="cyrl"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=3 -->
+            <FeatureIndex index="0" value="0"/>
+            <FeatureIndex index="1" value="1"/>
+            <FeatureIndex index="2" value="2"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="2">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=3 -->
+            <FeatureIndex index="0" value="0"/>
+            <FeatureIndex index="1" value="1"/>
+            <FeatureIndex index="2" value="2"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=1 -->
+          <LangSysRecord index="0">
+            <LangSysTag value="TRK "/>
+            <LangSys>
+              <ReqFeatureIndex value="65535"/>
+              <!-- FeatureCount=3 -->
+              <FeatureIndex index="0" value="0"/>
+              <FeatureIndex index="1" value="1"/>
+              <FeatureIndex index="2" value="2"/>
+            </LangSys>
+          </LangSysRecord>
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=3 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="aalt"/>
+        <Feature>
+          <!-- LookupCount=2 -->
+          <LookupListIndex index="0" value="0"/>
+          <LookupListIndex index="1" value="1"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="salt"/>
+        <Feature>
+          <!-- LookupCount=3 -->
+          <LookupListIndex index="0" value="4"/>
+          <LookupListIndex index="1" value="5"/>
+          <LookupListIndex index="2" value="7"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="2">
+        <FeatureTag value="smcp"/>
+        <Feature>
+          <!-- LookupCount=2 -->
+          <LookupListIndex index="0" value="2"/>
+          <LookupListIndex index="1" value="3"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=8 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="e" out="e.mid"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="3"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <AlternateSubst index="0">
+          <AlternateSet glyph="a">
+            <Alternate glyph="A.sc"/>
+            <Alternate glyph="a.alt1"/>
+            <Alternate glyph="a.alt2"/>
+            <Alternate glyph="a.alt3"/>
+          </AlternateSet>
+          <AlternateSet glyph="b">
+            <Alternate glyph="B.sc"/>
+            <Alternate glyph="b.alt"/>
+          </AlternateSet>
+          <AlternateSet glyph="c">
+            <Alternate glyph="C.sc"/>
+            <Alternate glyph="c.mid"/>
+          </AlternateSet>
+          <AlternateSet glyph="d">
+            <Alternate glyph="d.alt"/>
+            <Alternate glyph="d.mid"/>
+          </AlternateSet>
+        </AlternateSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="a" out="A.sc"/>
+          <Substitution in="b" out="B.sc"/>
+          <Substitution in="c" out="C.sc"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="f">
+            <Ligature components="i" glyph="f_i"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="3"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <AlternateSubst index="0">
+          <AlternateSet glyph="a">
+            <Alternate glyph="a.alt1"/>
+            <Alternate glyph="a.alt2"/>
+            <Alternate glyph="a.alt3"/>
+          </AlternateSet>
+        </AlternateSubst>
+      </Lookup>
+      <Lookup index="5">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="e"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="c"/>
+            <Glyph value="d"/>
+            <Glyph value="e"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="f"/>
+          </LookAheadCoverage>
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="6"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+      <Lookup index="6">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="c" out="c.mid"/>
+          <Substitution in="d" out="d.mid"/>
+          <Substitution in="e" out="e.mid"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="7">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="b" out="b.alt"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec8b.fea b/Tests/feaLib/data/spec8b.fea
new file mode 100644
index 0000000..538ac39
--- /dev/null
+++ b/Tests/feaLib/data/spec8b.fea
@@ -0,0 +1,12 @@
+feature size {
+    parameters 10.0 3 80 139;
+# 10.0 - design size, 3 - subfamily identifier, 80 - range start (exclusive, decipoints)
+# 139 - range end (inclusive, decipoints)
+   sizemenuname "Win MinionPro Size Name";
+   sizemenuname 1 "Mac MinionPro Size Name";
+   # The specification says: sizemenuname 1 21 0 "Mac MinionPro Size Name";
+   # which means Macintosh platform, MacOS Thai encoding, English language.
+   # Since fonttools currently does not support the MacOS Thai encoding,
+   # we use instead MacOS Roman encoding (0), Swedish language (5) for our test.
+   sizemenuname 1 0 5 "Mac MinionPro Size Name";
+} size;
diff --git a/Tests/feaLib/data/spec8b.ttx b/Tests/feaLib/data/spec8b.ttx
new file mode 100644
index 0000000..6e66c16
--- /dev/null
+++ b/Tests/feaLib/data/spec8b.ttx
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <name>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Win MinionPro Size Name
+    </namerecord>
+    <namerecord nameID="256" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Mac MinionPro Size Name
+    </namerecord>
+    <namerecord nameID="256" platformID="1" platEncID="0" langID="0x5" unicode="True">
+      Mac MinionPro Size Name
+    </namerecord>
+  </name>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="size"/>
+        <Feature>
+          <FeatureParamsSize>
+            <DesignSize value="10.0"/>
+            <SubfamilyID value="3"/>
+            <SubfamilyNameID value="256"/>  <!-- Win MinionPro Size Name -->
+            <RangeStart value="8.0"/>
+            <RangeEnd value="13.9"/>
+          </FeatureParamsSize>
+          <!-- LookupCount=0 -->
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec8c.fea b/Tests/feaLib/data/spec8c.fea
new file mode 100644
index 0000000..be3d31b
--- /dev/null
+++ b/Tests/feaLib/data/spec8c.fea
@@ -0,0 +1,13 @@
+feature ss01 {
+    featureNames {
+        name "Feature description for MS Platform, script Unicode, language English";
+# With no platform ID, script ID, or language ID specified, the implementation assumes (3,1,0x409).
+#test-fea2fea: name 3 1 1041 "Feature description for MS Platform, script Unicode, language Japanese";
+        name 3 1 0x411 "Feature description for MS Platform, script Unicode, language Japanese";
+        name 1 "Feature description for Apple Platform, script Roman, language unspecified";
+# With only the platform ID specified, the implementation assumes script and language = Latin. For Apple this is (1,0,0).
+        name 1 1 12 "Feature description for Apple Platform, script Japanese, language Japanese";
+    };
+# --- rules for this feature ---
+    sub A by B;
+} ss01;
diff --git a/Tests/feaLib/data/spec8c.ttx b/Tests/feaLib/data/spec8c.ttx
new file mode 100644
index 0000000..a5b5517
--- /dev/null
+++ b/Tests/feaLib/data/spec8c.ttx
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <name>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Feature description for MS Platform, script Unicode, language English
+    </namerecord>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x411">
+      Feature description for MS Platform, script Unicode, language Japanese
+    </namerecord>
+    <namerecord nameID="256" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Feature description for Apple Platform, script Roman, language unspecified
+    </namerecord>
+    <namerecord nameID="256" platformID="1" platEncID="1" langID="0xc" unicode="True">
+      Feature description for Apple Platform, script Japanese, language Japanese
+    </namerecord>
+  </name>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="ss01"/>
+        <Feature>
+          <FeatureParamsStylisticSet>
+            <Version value="0"/>
+            <UINameID value="256"/>  <!-- Feature description for MS Platform, script Unicode, language English -->
+          </FeatureParamsStylisticSet>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="A" out="B"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec8d.fea b/Tests/feaLib/data/spec8d.fea
new file mode 100644
index 0000000..2b68b03
--- /dev/null
+++ b/Tests/feaLib/data/spec8d.fea
@@ -0,0 +1,57 @@
+# The cvParameters block must precede any of the rules in the feature.
+# The ParamUILabelNameID entry may be omitted or repeated as often as needed.
+# The other NameID types may be omitted, or defined only once.
+# The NameID entries must be specified in the order listed below.
+
+# Following the set of NameID entries, a series of 24-bit Unicode values may be specified.
+# These provide Unicode values for the base glyphs referenced by the feature.
+# The developer may specify none, some, or all of the Unicode values for the base glyphs.
+# The Unicode value may be written with either decimal or hexadecimal notation.
+# The value must be preceded by '0x' if it is a hexadecimal value.
+
+# NOTE: The ParamUILabelNameID entries are used when one base glyph is mapped to more than
+# one variant; the font designer may then specify one ParamUILabelNameID for each variant, in
+# order to uniquely describe that variant. If any ParamUILabelNameID entries are specified,
+# the number of ParamUILabelNameID entries must match the number of variants for each base
+# glyph. If the Character Variant feature specifies more than one base glyph, then the set
+# of NameID entries in the parameter block will be used for each base glyph and its variants.
+feature cv01 {
+	cvParameters {
+		FeatUILabelNameID {
+#test-fea2fea: name "uilabel simple a";
+			name 3 1 0x0409 "uilabel simple a"; # English US
+#test-fea2fea: name 1 "uilabel simple a";
+			name 1 0 0 "uilabel simple a"; # Mac English
+		};
+		FeatUITooltipTextNameID {
+#test-fea2fea: name "tool tip simple a";
+			name 3 1 0x0409 "tool tip simple a"; # English US
+#test-fea2fea: name 1 "tool tip simple a";
+			name 1 0 0 "tool tip simple a"; # Mac English
+		};
+		SampleTextNameID {
+#test-fea2fea: name "sample text simple a";
+			name 3 1 0x0409 "sample text simple a"; # English US
+#test-fea2fea: name 1 "sample text simple a";
+			name 1 0 0 "sample text simple a"; # Mac English
+		};
+		ParamUILabelNameID {
+#test-fea2fea: name "param1 text simple a";
+			name 3 1 0x0409 "param1 text simple a"; # English US
+#test-fea2fea: name 1 "param1 text simple a";
+			name 1 0 0 "param1 text simple a"; # Mac English
+		};
+		ParamUILabelNameID {
+#test-fea2fea: name "param2 text simple a";
+			name 3 1 0x0409 "param2 text simple a"; # English US
+#test-fea2fea: name 1 "param2 text simple a";
+			name 1 0 0 "param2 text simple a"; # Mac English
+		};
+#test-fea2fea: Character 0xa;
+		Character 10;
+#test-fea2fea: Character 0x5dde;
+		Character 0x5DDE;
+	};
+# --- rules for this feature ---
+    sub A by B;
+} cv01;
diff --git a/Tests/feaLib/data/spec8d.ttx b/Tests/feaLib/data/spec8d.ttx
new file mode 100644
index 0000000..9848a69
--- /dev/null
+++ b/Tests/feaLib/data/spec8d.ttx
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <name>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      uilabel simple a
+    </namerecord>
+    <namerecord nameID="256" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      uilabel simple a
+    </namerecord>
+    <namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
+      tool tip simple a
+    </namerecord>
+    <namerecord nameID="257" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      tool tip simple a
+    </namerecord>
+    <namerecord nameID="258" platformID="3" platEncID="1" langID="0x409">
+      sample text simple a
+    </namerecord>
+    <namerecord nameID="258" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      sample text simple a
+    </namerecord>
+    <namerecord nameID="259" platformID="3" platEncID="1" langID="0x409">
+      param1 text simple a
+    </namerecord>
+    <namerecord nameID="259" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      param1 text simple a
+    </namerecord>
+    <namerecord nameID="260" platformID="3" platEncID="1" langID="0x409">
+      param2 text simple a
+    </namerecord>
+    <namerecord nameID="260" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      param2 text simple a
+    </namerecord>
+  </name>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="cv01"/>
+        <Feature>
+          <FeatureParamsCharacterVariants Format="0">
+            <Format value="0"/>
+            <FeatUILabelNameID value="256"/>  <!-- uilabel simple a -->
+            <FeatUITooltipTextNameID value="257"/>  <!-- tool tip simple a -->
+            <SampleTextNameID value="258"/>  <!-- sample text simple a -->
+            <NumNamedParameters value="2"/>
+            <FirstParamUILabelNameID value="259"/>  <!-- param1 text simple a -->
+            <!-- CharCount=2 -->
+            <Character index="0" value="10"/>
+            <Character index="1" value="24030"/>
+          </FeatureParamsCharacterVariants>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="A" out="B"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec9a.fea b/Tests/feaLib/data/spec9a.fea
new file mode 100644
index 0000000..84cca57
--- /dev/null
+++ b/Tests/feaLib/data/spec9a.fea
@@ -0,0 +1,4 @@
+table BASE {
+   HorizAxis.BaseTagList ideo romn;
+   HorizAxis.BaseScriptList latn romn -120 0, cyrl romn -120 0, grek romn -120 0, hani ideo -120 0, kana ideo -120 0, hang ideo -120 0;
+} BASE;
diff --git a/Tests/feaLib/data/spec9a.ttx b/Tests/feaLib/data/spec9a.ttx
new file mode 100644
index 0000000..c6bb896
--- /dev/null
+++ b/Tests/feaLib/data/spec9a.ttx
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <BASE>
+    <Version value="0x00010000"/>
+    <HorizAxis>
+      <BaseTagList>
+        <!-- BaseTagCount=2 -->
+        <BaselineTag index="0" value="ideo"/>
+        <BaselineTag index="1" value="romn"/>
+      </BaseTagList>
+      <BaseScriptList>
+        <!-- BaseScriptCount=6 -->
+        <BaseScriptRecord index="0">
+          <BaseScriptTag value="cyrl"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-120"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="1">
+          <BaseScriptTag value="grek"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-120"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="2">
+          <BaseScriptTag value="hang"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="0"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-120"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="3">
+          <BaseScriptTag value="hani"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="0"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-120"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="4">
+          <BaseScriptTag value="kana"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="0"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-120"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="5">
+          <BaseScriptTag value="latn"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-120"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+      </BaseScriptList>
+    </HorizAxis>
+  </BASE>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec9b.fea b/Tests/feaLib/data/spec9b.fea
new file mode 100644
index 0000000..c72d3f0
--- /dev/null
+++ b/Tests/feaLib/data/spec9b.fea
@@ -0,0 +1,13 @@
+@BASE = [f i];
+@LIGATURES = [c_s c_t f_i f_f_i s_t];
+@MARKS = [acute grave];
+@COMPONENT = [noon.final noon.initial];
+
+table GDEF {
+    GlyphClassDef @BASE, @LIGATURES, @MARKS, @COMPONENT;
+    Attach noon.final 5;
+    Attach noon.initial 4;
+    LigatureCaretByPos f_i 400 380;
+    LigatureCaretByPos [c_t s_t] 500;
+    LigatureCaretByIndex f_f_i 23 46;
+} GDEF;
diff --git a/Tests/feaLib/data/spec9b.ttx b/Tests/feaLib/data/spec9b.ttx
new file mode 100644
index 0000000..eb77bbb
--- /dev/null
+++ b/Tests/feaLib/data/spec9b.ttx
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="true" ttLibVersion="3.0">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef>
+      <ClassDef glyph="acute" class="3"/>
+      <ClassDef glyph="c_s" class="2"/>
+      <ClassDef glyph="c_t" class="2"/>
+      <ClassDef glyph="f" class="1"/>
+      <ClassDef glyph="f_f_i" class="2"/>
+      <ClassDef glyph="f_i" class="2"/>
+      <ClassDef glyph="grave" class="3"/>
+      <ClassDef glyph="i" class="1"/>
+      <ClassDef glyph="noon.final" class="4"/>
+      <ClassDef glyph="noon.initial" class="4"/>
+      <ClassDef glyph="s_t" class="2"/>
+    </GlyphClassDef>
+    <AttachList>
+      <Coverage>
+        <Glyph value="noon.final"/>
+        <Glyph value="noon.initial"/>
+      </Coverage>
+      <!-- GlyphCount=2 -->
+      <AttachPoint index="0">
+        <!-- PointCount=1 -->
+        <PointIndex index="0" value="5"/>
+      </AttachPoint>
+      <AttachPoint index="1">
+        <!-- PointCount=1 -->
+        <PointIndex index="0" value="4"/>
+      </AttachPoint>
+    </AttachList>
+    <LigCaretList>
+      <Coverage>
+        <Glyph value="c_t"/>
+        <Glyph value="f_f_i"/>
+        <Glyph value="f_i"/>
+        <Glyph value="s_t"/>
+      </Coverage>
+      <!-- LigGlyphCount=4 -->
+      <LigGlyph index="0">
+        <!-- CaretCount=1 -->
+        <CaretValue index="0" Format="1">
+          <Coordinate value="500"/>
+        </CaretValue>
+      </LigGlyph>
+      <LigGlyph index="1">
+        <!-- CaretCount=2 -->
+        <CaretValue index="0" Format="2">
+          <CaretValuePoint value="23"/>
+        </CaretValue>
+        <CaretValue index="1" Format="2">
+          <CaretValuePoint value="46"/>
+        </CaretValue>
+      </LigGlyph>
+      <LigGlyph index="2">
+        <!-- CaretCount=2 -->
+        <CaretValue index="0" Format="1">
+          <Coordinate value="380"/>
+        </CaretValue>
+        <CaretValue index="1" Format="1">
+          <Coordinate value="400"/>
+        </CaretValue>
+      </LigGlyph>
+      <LigGlyph index="3">
+        <!-- CaretCount=1 -->
+        <CaretValue index="0" Format="1">
+          <Coordinate value="500"/>
+        </CaretValue>
+      </LigGlyph>
+    </LigCaretList>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec9c1.fea b/Tests/feaLib/data/spec9c1.fea
new file mode 100644
index 0000000..57ed119
--- /dev/null
+++ b/Tests/feaLib/data/spec9c1.fea
@@ -0,0 +1,4 @@
+table head {
+#test-fea2fea: FontRevision 1.100;
+    FontRevision 1.1;
+} head;
diff --git a/Tests/feaLib/data/spec9c1.ttx b/Tests/feaLib/data/spec9c1.ttx
new file mode 100644
index 0000000..b60e0f5
--- /dev/null
+++ b/Tests/feaLib/data/spec9c1.ttx
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.1"/>
+    <checkSumAdjustment value="0x0"/>
+    <magicNumber value="0x0"/>
+    <flags value="00000000 00000000"/>
+    <unitsPerEm value="0"/>
+    <created value="Tue Dec 13 11:22:33 2011"/>
+    <modified value="Tue Dec 13 11:22:33 2011"/>
+    <xMin value="0"/>
+    <yMin value="0"/>
+    <xMax value="0"/>
+    <yMax value="0"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="0"/>
+    <fontDirectionHint value="0"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec9c2.fea b/Tests/feaLib/data/spec9c2.fea
new file mode 100644
index 0000000..333fa16
--- /dev/null
+++ b/Tests/feaLib/data/spec9c2.fea
@@ -0,0 +1,3 @@
+table head {
+    FontRevision 1.001;
+} head;
diff --git a/Tests/feaLib/data/spec9c2.ttx b/Tests/feaLib/data/spec9c2.ttx
new file mode 100644
index 0000000..ed3d740
--- /dev/null
+++ b/Tests/feaLib/data/spec9c2.ttx
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.001"/>
+    <checkSumAdjustment value="0x0"/>
+    <magicNumber value="0x0"/>
+    <flags value="00000000 00000000"/>
+    <unitsPerEm value="0"/>
+    <created value="Tue Dec 13 11:22:33 2011"/>
+    <modified value="Tue Dec 13 11:22:33 2011"/>
+    <xMin value="0"/>
+    <yMin value="0"/>
+    <xMax value="0"/>
+    <yMax value="0"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="0"/>
+    <fontDirectionHint value="0"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec9c3.fea b/Tests/feaLib/data/spec9c3.fea
new file mode 100644
index 0000000..e9433a8
--- /dev/null
+++ b/Tests/feaLib/data/spec9c3.fea
@@ -0,0 +1,3 @@
+table head {
+    FontRevision 1.500;
+} head;
diff --git a/Tests/feaLib/data/spec9c3.ttx b/Tests/feaLib/data/spec9c3.ttx
new file mode 100644
index 0000000..e20186c
--- /dev/null
+++ b/Tests/feaLib/data/spec9c3.ttx
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.5"/>
+    <checkSumAdjustment value="0x0"/>
+    <magicNumber value="0x0"/>
+    <flags value="00000000 00000000"/>
+    <unitsPerEm value="0"/>
+    <created value="Tue Dec 13 11:22:33 2011"/>
+    <modified value="Tue Dec 13 11:22:33 2011"/>
+    <xMin value="0"/>
+    <yMin value="0"/>
+    <xMax value="0"/>
+    <yMax value="0"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="0"/>
+    <fontDirectionHint value="0"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec9d.fea b/Tests/feaLib/data/spec9d.fea
new file mode 100644
index 0000000..231e7bd
--- /dev/null
+++ b/Tests/feaLib/data/spec9d.fea
@@ -0,0 +1,6 @@
+table hhea {
+   CaretOffset -50;
+   Ascender 800;
+   Descender 200;
+   LineGap 200;
+} hhea;
diff --git a/Tests/feaLib/data/spec9d.ttx b/Tests/feaLib/data/spec9d.ttx
new file mode 100644
index 0000000..76863d8
--- /dev/null
+++ b/Tests/feaLib/data/spec9d.ttx
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="800"/>
+    <descent value="200"/>
+    <lineGap value="200"/>
+    <advanceWidthMax value="0"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="0"/>
+    <xMaxExtent value="0"/>
+    <caretSlopeRise value="0"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="-50"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="0"/>
+  </hhea>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec9e.fea b/Tests/feaLib/data/spec9e.fea
new file mode 100644
index 0000000..58f59e9
--- /dev/null
+++ b/Tests/feaLib/data/spec9e.fea
@@ -0,0 +1,4 @@
+table name {
+    nameid 9 "Joachim M\00fcller-Lanc\00e9";    # Windows (Unicode)
+    nameid 9 1 "Joachim M\9fller-Lanc\8e";      # Macintosh (Mac Roman)
+} name;
diff --git a/Tests/feaLib/data/spec9e.ttx b/Tests/feaLib/data/spec9e.ttx
new file mode 100644
index 0000000..5119a5f
--- /dev/null
+++ b/Tests/feaLib/data/spec9e.ttx
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <name>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Joachim Müller-Lancé
+    </namerecord>
+    <namerecord nameID="9" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Joachim Müller-Lancé
+    </namerecord>
+  </name>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec9f.fea b/Tests/feaLib/data/spec9f.fea
new file mode 100644
index 0000000..90cf6d1
--- /dev/null
+++ b/Tests/feaLib/data/spec9f.fea
@@ -0,0 +1,19 @@
+table OS/2 {
+    FSType 4;
+    Panose 2 15 0 0 2 2 8 2 9 4;
+    TypoAscender 800;
+    TypoDescender -200; # Note that TypoDescender is negative for descent below the baseline.
+    winAscent 832;
+    winDescent 321; # Note that winDescent is positive for descent below the baseline.
+    UnicodeRange 0 1 9 55 59 60;
+#   0  - Basic Latin,            1  - Latin-1 Supplement
+#   9  - Cyrillic,               55 - CJK Compatibility
+#   59 - CJK Unified Ideographs, 60 - Private Use Area
+   CodePageRange 1252 1251 932;
+#   1252 - Latin 1, 1251 - Cyrllic, 932 - JIS/Japan
+   XHeight 400;
+   CapHeight 600;
+   WeightClass 800;
+   WidthClass 3;
+   Vendor "ADBE";
+} OS/2;
diff --git a/Tests/feaLib/data/spec9f.ttx b/Tests/feaLib/data/spec9f.ttx
new file mode 100644
index 0000000..9e147d1
--- /dev/null
+++ b/Tests/feaLib/data/spec9f.ttx
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.0">
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="2"/>
+    <xAvgCharWidth value="0"/>
+    <usWeightClass value="800"/>
+    <usWidthClass value="3"/>
+    <fsType value="00000000 00000100"/>
+    <ySubscriptXSize value="0"/>
+    <ySubscriptYSize value="0"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="0"/>
+    <ySuperscriptXSize value="0"/>
+    <ySuperscriptYSize value="0"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="0"/>
+    <yStrikeoutSize value="0"/>
+    <yStrikeoutPosition value="0"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="15"/>
+      <bWeight value="0"/>
+      <bProportion value="0"/>
+      <bContrast value="2"/>
+      <bStrokeVariation value="2"/>
+      <bArmStyle value="8"/>
+      <bLetterForm value="2"/>
+      <bMidline value="9"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000010 00000011"/>
+    <ulUnicodeRange2 value="00011000 10000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBE"/>
+    <fsSelection value="00000000 00000000"/>
+    <usFirstCharIndex value="0"/>
+    <usLastCharIndex value="0"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="832"/>
+    <usWinDescent value="321"/>
+    <ulCodePageRange1 value="00000000 00000010 00000000 00000101"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="400"/>
+    <sCapHeight value="600"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="0"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+</ttFont>
diff --git a/Tests/feaLib/data/spec9g.fea b/Tests/feaLib/data/spec9g.fea
new file mode 100644
index 0000000..3b3e0c1
--- /dev/null
+++ b/Tests/feaLib/data/spec9g.fea
@@ -0,0 +1,5 @@
+table vhea {
+   VertTypoAscender 500;
+   VertTypoDescender -500;
+   VertTypoLineGap 1000;
+} vhea;
diff --git a/Tests/feaLib/data/spec9g.ttx b/Tests/feaLib/data/spec9g.ttx
new file mode 100644
index 0000000..e5541f0
--- /dev/null
+++ b/Tests/feaLib/data/spec9g.ttx
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <vhea>
+    <tableVersion value="0x00011000"/>
+    <ascent value="500"/>
+    <descent value="-500"/>
+    <lineGap value="1000"/>
+    <advanceHeightMax value="0"/>
+    <minTopSideBearing value="0"/>
+    <minBottomSideBearing value="0"/>
+    <yMaxExtent value="0"/>
+    <caretSlopeRise value="0"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <reserved4 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfVMetrics value="0"/>
+  </vhea>
+
+</ttFont>
diff --git a/Tests/feaLib/error_test.py b/Tests/feaLib/error_test.py
new file mode 100644
index 0000000..87cbecb
--- /dev/null
+++ b/Tests/feaLib/error_test.py
@@ -0,0 +1,19 @@
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+from fontTools.feaLib.error import FeatureLibError
+import unittest
+
+
+class FeatureLibErrorTest(unittest.TestCase):
+    def test_str(self):
+        err = FeatureLibError("Squeak!", ("foo.fea", 23, 42))
+        self.assertEqual(str(err), "foo.fea:23:42: Squeak!")
+
+    def test_str_nolocation(self):
+        err = FeatureLibError("Squeak!", None)
+        self.assertEqual(str(err), "Squeak!")
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/feaLib/lexer_test.py b/Tests/feaLib/lexer_test.py
new file mode 100644
index 0000000..27e2da6
--- /dev/null
+++ b/Tests/feaLib/lexer_test.py
@@ -0,0 +1,237 @@
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.feaLib.error import FeatureLibError, IncludedFeaNotFound
+from fontTools.feaLib.lexer import IncludingLexer, Lexer
+import os
+import shutil
+import tempfile
+import unittest
+
+
+def lex(s):
+    return [(typ, tok) for (typ, tok, _) in Lexer(s, "test.fea")]
+
+
+class LexerTest(unittest.TestCase):
+    def __init__(self, methodName):
+        unittest.TestCase.__init__(self, methodName)
+        # Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
+        # and fires deprecation warnings if a program uses the old name.
+        if not hasattr(self, "assertRaisesRegex"):
+            self.assertRaisesRegex = self.assertRaisesRegexp
+
+    def test_empty(self):
+        self.assertEqual(lex(""), [])
+        self.assertEqual(lex(" \t "), [])
+
+    def test_name(self):
+        self.assertEqual(lex("a17"), [(Lexer.NAME, "a17")])
+        self.assertEqual(lex(".notdef"), [(Lexer.NAME, ".notdef")])
+        self.assertEqual(lex("two.oldstyle"), [(Lexer.NAME, "two.oldstyle")])
+        self.assertEqual(lex("_"), [(Lexer.NAME, "_")])
+        self.assertEqual(lex("\\table"), [(Lexer.NAME, "\\table")])
+        self.assertEqual(lex("a+*:^~!"), [(Lexer.NAME, "a+*:^~!")])
+        self.assertEqual(lex("with-dash"), [(Lexer.NAME, "with-dash")])
+
+    def test_cid(self):
+        self.assertEqual(lex("\\0 \\987"), [(Lexer.CID, 0), (Lexer.CID, 987)])
+
+    def test_glyphclass(self):
+        self.assertEqual(lex("@Vowel.sc"), [(Lexer.GLYPHCLASS, "Vowel.sc")])
+        self.assertRaisesRegex(FeatureLibError,
+                               "Expected glyph class", lex, "@(a)")
+        self.assertRaisesRegex(FeatureLibError,
+                               "Expected glyph class", lex, "@ A")
+        self.assertRaisesRegex(FeatureLibError,
+                               "not be longer than 63 characters",
+                               lex, "@" + ("A" * 64))
+        self.assertRaisesRegex(FeatureLibError,
+                               "Glyph class names must consist of",
+                               lex, "@Ab:c")
+
+    def test_include(self):
+        self.assertEqual(lex("include (~/foo/bar baz.fea);"), [
+            (Lexer.NAME, "include"),
+            (Lexer.FILENAME, "~/foo/bar baz.fea"),
+            (Lexer.SYMBOL, ";")
+        ])
+        self.assertEqual(lex("include # Comment\n    (foo) \n;"), [
+            (Lexer.NAME, "include"),
+            (Lexer.COMMENT, "# Comment"),
+            (Lexer.FILENAME, "foo"),
+            (Lexer.SYMBOL, ";")
+        ])
+        self.assertRaises(FeatureLibError, lex, "include blah")
+        self.assertRaises(FeatureLibError, lex, "include (blah")
+
+    def test_number(self):
+        self.assertEqual(lex("123 -456"),
+                         [(Lexer.NUMBER, 123), (Lexer.NUMBER, -456)])
+        self.assertEqual(lex("0xCAFED00D"), [(Lexer.NUMBER, 0xCAFED00D)])
+        self.assertEqual(lex("0xcafed00d"), [(Lexer.NUMBER, 0xCAFED00D)])
+
+    def test_float(self):
+        self.assertEqual(lex("1.23 -4.5"),
+                         [(Lexer.FLOAT, 1.23), (Lexer.FLOAT, -4.5)])
+
+    def test_symbol(self):
+        self.assertEqual(lex("a'"), [(Lexer.NAME, "a"), (Lexer.SYMBOL, "'")])
+        self.assertEqual(lex("-A-B"),
+                         [(Lexer.SYMBOL, "-"), (Lexer.NAME, "A-B")])
+        self.assertEqual(
+            lex("foo - -2"),
+            [(Lexer.NAME, "foo"), (Lexer.SYMBOL, "-"), (Lexer.NUMBER, -2)])
+
+    def test_comment(self):
+        self.assertEqual(lex("# Comment\n#"),
+                         [(Lexer.COMMENT, "# Comment"), (Lexer.COMMENT, "#")])
+
+    def test_string(self):
+        self.assertEqual(lex('"foo" "bar"'),
+                         [(Lexer.STRING, "foo"), (Lexer.STRING, "bar")])
+        self.assertEqual(lex('"foo \nbar\r baz \r\nqux\n\n "'),
+                         [(Lexer.STRING, "foo bar baz qux ")])
+        # The lexer should preserve escape sequences because they have
+        # different interpretations depending on context. For better
+        # or for worse, that is how the OpenType Feature File Syntax
+        # has been specified; see section 9.e (name table) for examples.
+        self.assertEqual(lex(r'"M\00fcller-Lanc\00e9"'),  # 'nameid 9'
+                         [(Lexer.STRING, r"M\00fcller-Lanc\00e9")])
+        self.assertEqual(lex(r'"M\9fller-Lanc\8e"'),  # 'nameid 9 1'
+                         [(Lexer.STRING, r"M\9fller-Lanc\8e")])
+        self.assertRaises(FeatureLibError, lex, '"foo\n bar')
+
+    def test_bad_character(self):
+        self.assertRaises(FeatureLibError, lambda: lex("123 \u0001"))
+
+    def test_newline(self):
+        def lines(s):
+            return [loc[1] for (_, _, loc) in Lexer(s, "test.fea")]
+        self.assertEqual(lines("FOO\n\nBAR\nBAZ"), [1, 3, 4])  # Unix
+        self.assertEqual(lines("FOO\r\rBAR\rBAZ"), [1, 3, 4])  # Macintosh
+        self.assertEqual(lines("FOO\r\n\r\n BAR\r\nBAZ"), [1, 3, 4])  # Windows
+        self.assertEqual(lines("FOO\n\rBAR\r\nBAZ"), [1, 3, 4])  # mixed
+
+    def test_location(self):
+        def locs(s):
+            return ["%s:%d:%d" % loc for (_, _, loc) in Lexer(s, "test.fea")]
+        self.assertEqual(locs("a b # Comment\n12 @x"), [
+            "test.fea:1:1", "test.fea:1:3", "test.fea:1:5", "test.fea:2:1",
+            "test.fea:2:4"
+        ])
+
+    def test_scan_over_(self):
+        lexer = Lexer("abbacabba12", "test.fea")
+        self.assertEqual(lexer.pos_, 0)
+        lexer.scan_over_("xyz")
+        self.assertEqual(lexer.pos_, 0)
+        lexer.scan_over_("abc")
+        self.assertEqual(lexer.pos_, 9)
+        lexer.scan_over_("abc")
+        self.assertEqual(lexer.pos_, 9)
+        lexer.scan_over_("0123456789")
+        self.assertEqual(lexer.pos_, 11)
+
+    def test_scan_until_(self):
+        lexer = Lexer("foo'bar", "test.fea")
+        self.assertEqual(lexer.pos_, 0)
+        lexer.scan_until_("'")
+        self.assertEqual(lexer.pos_, 3)
+        lexer.scan_until_("'")
+        self.assertEqual(lexer.pos_, 3)
+
+
+class IncludingLexerTest(unittest.TestCase):
+    @staticmethod
+    def getpath(filename):
+        path, _ = os.path.split(__file__)
+        return os.path.join(path, "data", filename)
+
+    def test_include(self):
+        lexer = IncludingLexer(self.getpath("include/include4.fea"))
+        result = ['%s %s:%d' % (token, os.path.split(loc[0])[1], loc[1])
+                  for _, token, loc in lexer]
+        self.assertEqual(result, [
+            "I4a include4.fea:1",
+            "I3a include3.fea:1",
+            "I2a include2.fea:1",
+            "I1a include1.fea:1",
+            "I0 include0.fea:1",
+            "I1b include1.fea:3",
+            "; include2.fea:2",
+            "I2b include2.fea:3",
+            "; include3.fea:2",
+            "I3b include3.fea:3",
+            "; include4.fea:2",
+            "I4b include4.fea:3"
+        ])
+
+    def test_include_limit(self):
+        lexer = IncludingLexer(self.getpath("include/include6.fea"))
+        self.assertRaises(FeatureLibError, lambda: list(lexer))
+
+    def test_include_self(self):
+        lexer = IncludingLexer(self.getpath("include/includeself.fea"))
+        self.assertRaises(FeatureLibError, lambda: list(lexer))
+
+    def test_include_missing_file(self):
+        lexer = IncludingLexer(self.getpath("include/includemissingfile.fea"))
+        self.assertRaisesRegex(IncludedFeaNotFound,
+                               "includemissingfile.fea:1:8: missingfile.fea",
+                               lambda: list(lexer))
+
+    def test_featurefilepath_None(self):
+        lexer = IncludingLexer(UnicodeIO("# foobar"))
+        self.assertIsNone(lexer.featurefilepath)
+        files = set(loc[0] for _, _, loc in lexer)
+        self.assertIn("<features>", files)
+
+    def test_include_absolute_path(self):
+        with tempfile.NamedTemporaryFile(delete=False) as included:
+            included.write(tobytes("""
+                feature kern {
+                    pos A B -40;
+                } kern;
+                """, encoding="utf-8"))
+        including = UnicodeIO("include(%s);" % included.name)
+        try:
+            lexer = IncludingLexer(including)
+            files = set(loc[0] for _, _, loc in lexer)
+            self.assertIn(included.name, files)
+        finally:
+            os.remove(included.name)
+
+    def test_include_relative_to_cwd(self):
+        # save current working directory, to be restored later
+        cwd = os.getcwd()
+        tmpdir = tempfile.mkdtemp()
+        try:
+            # create new feature file in a temporary directory
+            with open(os.path.join(tmpdir, "included.fea"), "w",
+                      encoding="utf-8") as included:
+                included.write("""
+                    feature kern {
+                        pos A B -40;
+                    } kern;
+                    """)
+            # change current folder to the temporary dir
+            os.chdir(tmpdir)
+            # instantiate a new lexer that includes the above file
+            # using a relative path; the IncludingLexer does not
+            # itself have a path, because it was initialized from
+            # an in-memory stream, so it will use the current working
+            # directory to resolve relative include statements
+            lexer = IncludingLexer(UnicodeIO("include(included.fea);"))
+            files = set(loc[0] for _, _, loc in lexer)
+            expected = os.path.realpath(included.name)
+            self.assertIn(expected, files)
+        finally:
+            # remove temporary folder and restore previous working directory
+            os.chdir(cwd)
+            shutil.rmtree(tmpdir)
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/feaLib/parser_test.py b/Tests/feaLib/parser_test.py
new file mode 100644
index 0000000..91744f0
--- /dev/null
+++ b/Tests/feaLib/parser_test.py
@@ -0,0 +1,1623 @@
+# -*- coding: utf-8 -*-
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+from fontTools.feaLib.error import FeatureLibError
+from fontTools.feaLib.parser import Parser, SymbolTable
+from fontTools.misc.py23 import *
+import warnings
+import fontTools.feaLib.ast as ast
+import os
+import unittest
+
+
+def glyphstr(glyphs):
+    def f(x):
+        if len(x) == 1:
+            return list(x)[0]
+        else:
+            return '[%s]' % ' '.join(sorted(list(x)))
+    return ' '.join(f(g.glyphSet()) for g in glyphs)
+
+
+def mapping(s):
+    b = []
+    for a in s.glyphs:
+        b.extend(a.glyphSet())
+    c = []
+    for a in s.replacements:
+        c.extend(a.glyphSet())
+    if len(c) == 1:
+        c = c * len(b)
+    return dict(zip(b, c))
+
+
+GLYPHNAMES = ("""
+    .notdef space 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
+    A.sc B.sc C.sc D.sc E.sc F.sc G.sc H.sc I.sc J.sc K.sc L.sc M.sc
+    N.sc O.sc P.sc Q.sc R.sc S.sc T.sc U.sc V.sc W.sc X.sc Y.sc Z.sc
+    A.swash B.swash X.swash Y.swash Z.swash
+    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
+    a.sc b.sc c.sc d.sc e.sc f.sc g.sc h.sc i.sc j.sc k.sc l.sc m.sc
+    n.sc o.sc p.sc q.sc r.sc s.sc t.sc u.sc v.sc w.sc x.sc y.sc z.sc
+    a.swash b.swash x.swash y.swash z.swash
+    foobar foo.09 foo.1234 foo.9876
+""").split() + ["foo.%d" % i for i in range(1, 200)]
+
+
+class ParserTest(unittest.TestCase):
+    def __init__(self, methodName):
+        unittest.TestCase.__init__(self, methodName)
+        # Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
+        # and fires deprecation warnings if a program uses the old name.
+        if not hasattr(self, "assertRaisesRegex"):
+            self.assertRaisesRegex = self.assertRaisesRegexp
+
+    def test_glyphMap_deprecated(self):
+        glyphMap = {'a': 0, 'b': 1, 'c': 2}
+        with warnings.catch_warnings(record=True) as w:
+            warnings.simplefilter("always")
+            parser = Parser(UnicodeIO(), glyphMap=glyphMap)
+
+            self.assertEqual(len(w), 1)
+            self.assertEqual(w[-1].category, UserWarning)
+            self.assertIn("deprecated", str(w[-1].message))
+            self.assertEqual(parser.glyphNames_, {'a', 'b', 'c'})
+
+            self.assertRaisesRegex(
+                TypeError, "mutually exclusive",
+                Parser, UnicodeIO(), ("a",), glyphMap={"a": 0})
+
+            self.assertRaisesRegex(
+                TypeError, "unsupported keyword argument",
+                Parser, UnicodeIO(), foo="bar")
+
+    def test_comments(self):
+        doc = self.parse(
+            """ # Initial
+                feature test {
+                    sub A by B; # simple
+                } test;""")
+        c1 = doc.statements[0]
+        c2 = doc.statements[1].statements[1]
+        self.assertEqual(type(c1), ast.Comment)
+        self.assertEqual(c1.text, "# Initial")
+        self.assertEqual(str(c1), "# Initial")
+        self.assertEqual(type(c2), ast.Comment)
+        self.assertEqual(c2.text, "# simple")
+        self.assertEqual(doc.statements[1].name, "test")
+
+    def test_only_comments(self):
+        doc = self.parse("""\
+            # Initial
+        """)
+        c1 = doc.statements[0]
+        self.assertEqual(type(c1), ast.Comment)
+        self.assertEqual(c1.text, "# Initial")
+        self.assertEqual(str(c1), "# Initial")
+
+    def test_anchor_format_a(self):
+        doc = self.parse(
+            "feature test {"
+            "    pos cursive A <anchor 120 -20> <anchor NULL>;"
+            "} test;")
+        anchor = doc.statements[0].statements[0].entryAnchor
+        self.assertEqual(type(anchor), ast.Anchor)
+        self.assertEqual(anchor.x, 120)
+        self.assertEqual(anchor.y, -20)
+        self.assertIsNone(anchor.contourpoint)
+        self.assertIsNone(anchor.xDeviceTable)
+        self.assertIsNone(anchor.yDeviceTable)
+
+    def test_anchor_format_b(self):
+        doc = self.parse(
+            "feature test {"
+            "    pos cursive A <anchor 120 -20 contourpoint 5> <anchor NULL>;"
+            "} test;")
+        anchor = doc.statements[0].statements[0].entryAnchor
+        self.assertEqual(type(anchor), ast.Anchor)
+        self.assertEqual(anchor.x, 120)
+        self.assertEqual(anchor.y, -20)
+        self.assertEqual(anchor.contourpoint, 5)
+        self.assertIsNone(anchor.xDeviceTable)
+        self.assertIsNone(anchor.yDeviceTable)
+
+    def test_anchor_format_c(self):
+        doc = self.parse(
+            "feature test {"
+            "    pos cursive A "
+            "        <anchor 120 -20 <device 11 111, 12 112> <device NULL>>"
+            "        <anchor NULL>;"
+            "} test;")
+        anchor = doc.statements[0].statements[0].entryAnchor
+        self.assertEqual(type(anchor), ast.Anchor)
+        self.assertEqual(anchor.x, 120)
+        self.assertEqual(anchor.y, -20)
+        self.assertIsNone(anchor.contourpoint)
+        self.assertEqual(anchor.xDeviceTable, ((11, 111), (12, 112)))
+        self.assertIsNone(anchor.yDeviceTable)
+
+    def test_anchor_format_d(self):
+        doc = self.parse(
+            "feature test {"
+            "    pos cursive A <anchor 120 -20> <anchor NULL>;"
+            "} test;")
+        anchor = doc.statements[0].statements[0].exitAnchor
+        self.assertIsNone(anchor)
+
+    def test_anchor_format_e(self):
+        doc = self.parse(
+            "feature test {"
+            "    anchorDef 120 -20 contourpoint 7 Foo;"
+            "    pos cursive A <anchor Foo> <anchor NULL>;"
+            "} test;")
+        anchor = doc.statements[0].statements[1].entryAnchor
+        self.assertEqual(type(anchor), ast.Anchor)
+        self.assertEqual(anchor.x, 120)
+        self.assertEqual(anchor.y, -20)
+        self.assertEqual(anchor.contourpoint, 7)
+        self.assertIsNone(anchor.xDeviceTable)
+        self.assertIsNone(anchor.yDeviceTable)
+
+    def test_anchor_format_e_undefined(self):
+        self.assertRaisesRegex(
+            FeatureLibError, 'Unknown anchor "UnknownName"', self.parse,
+            "feature test {"
+            "    position cursive A <anchor UnknownName> <anchor NULL>;"
+            "} test;")
+
+    def test_anchordef(self):
+        [foo] = self.parse("anchorDef 123 456 foo;").statements
+        self.assertEqual(type(foo), ast.AnchorDefinition)
+        self.assertEqual(foo.name, "foo")
+        self.assertEqual(foo.x, 123)
+        self.assertEqual(foo.y, 456)
+        self.assertEqual(foo.contourpoint, None)
+
+    def test_anchordef_contourpoint(self):
+        [foo] = self.parse("anchorDef 123 456 contourpoint 5 foo;").statements
+        self.assertEqual(type(foo), ast.AnchorDefinition)
+        self.assertEqual(foo.name, "foo")
+        self.assertEqual(foo.x, 123)
+        self.assertEqual(foo.y, 456)
+        self.assertEqual(foo.contourpoint, 5)
+
+    def test_anon(self):
+        anon = self.parse("anon TEST { # a\nfoo\n } TEST; # qux").statements[0]
+        self.assertIsInstance(anon, ast.AnonymousBlock)
+        self.assertEqual(anon.tag, "TEST")
+        self.assertEqual(anon.content, "foo\n ")
+
+    def test_anonymous(self):
+        anon = self.parse("anonymous TEST {\nbar\n} TEST;").statements[0]
+        self.assertIsInstance(anon, ast.AnonymousBlock)
+        self.assertEqual(anon.tag, "TEST")
+        # feature file spec requires passing the final end-of-line
+        self.assertEqual(anon.content, "bar\n")
+
+    def test_anon_missingBrace(self):
+        self.assertRaisesRegex(
+            FeatureLibError, "Expected '} TEST;' to terminate anonymous block",
+            self.parse, "anon TEST { \n no end in sight")
+
+    def test_attach(self):
+        doc = self.parse("table GDEF {Attach [a e] 2;} GDEF;")
+        s = doc.statements[0].statements[0]
+        self.assertIsInstance(s, ast.AttachStatement)
+        self.assertEqual(glyphstr([s.glyphs]), "[a e]")
+        self.assertEqual(s.contourPoints, {2})
+
+    def test_feature_block(self):
+        [liga] = self.parse("feature liga {} liga;").statements
+        self.assertEqual(liga.name, "liga")
+        self.assertFalse(liga.use_extension)
+
+    def test_feature_block_useExtension(self):
+        [liga] = self.parse("feature liga useExtension {} liga;").statements
+        self.assertEqual(liga.name, "liga")
+        self.assertTrue(liga.use_extension)
+
+    def test_feature_comment(self):
+        [liga] = self.parse("feature liga { # Comment\n } liga;").statements
+        [comment] = liga.statements
+        self.assertIsInstance(comment, ast.Comment)
+        self.assertEqual(comment.text, "# Comment")
+
+    def test_feature_reference(self):
+        doc = self.parse("feature aalt { feature salt; } aalt;")
+        ref = doc.statements[0].statements[0]
+        self.assertIsInstance(ref, ast.FeatureReferenceStatement)
+        self.assertEqual(ref.featureName, "salt")
+
+    def test_FeatureNames_bad(self):
+        self.assertRaisesRegex(
+            FeatureLibError, 'Expected "name"',
+            self.parse, "feature ss01 { featureNames { feature test; } ss01;")
+
+    def test_FeatureNames_comment(self):
+        [feature] = self.parse(
+            "feature ss01 { featureNames { # Comment\n }; } ss01;").statements
+        [featureNames] = feature.statements
+        self.assertIsInstance(featureNames, ast.NestedBlock)
+        [comment] = featureNames.statements
+        self.assertIsInstance(comment, ast.Comment)
+        self.assertEqual(comment.text, "# Comment")
+
+    def test_FeatureNames_emptyStatements(self):
+        [feature] = self.parse(
+            "feature ss01 { featureNames { ;;; }; } ss01;").statements
+        [featureNames] = feature.statements
+        self.assertIsInstance(featureNames, ast.NestedBlock)
+        self.assertEqual(featureNames.statements, [])
+
+    def test_FontRevision(self):
+        doc = self.parse("table head {FontRevision 2.5;} head;")
+        s = doc.statements[0].statements[0]
+        self.assertIsInstance(s, ast.FontRevisionStatement)
+        self.assertEqual(s.revision, 2.5)
+
+    def test_FontRevision_negative(self):
+        self.assertRaisesRegex(
+            FeatureLibError, "Font revision numbers must be positive",
+            self.parse, "table head {FontRevision -17.2;} head;")
+
+    def test_glyphclass(self):
+        [gc] = self.parse("@dash = [endash emdash figuredash];").statements
+        self.assertEqual(gc.name, "dash")
+        self.assertEqual(gc.glyphSet(), ("endash", "emdash", "figuredash"))
+
+    def test_glyphclass_glyphNameTooLong(self):
+        self.assertRaisesRegex(
+            FeatureLibError, "must not be longer than 63 characters",
+            self.parse, "@GlyphClass = [%s];" % ("G" * 64))
+
+    def test_glyphclass_bad(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Expected glyph name, glyph range, or glyph class reference",
+            self.parse, "@bad = [a 123];")
+
+    def test_glyphclass_duplicate(self):
+        # makeotf accepts this, so we should too
+        ab, xy = self.parse("@dup = [a b]; @dup = [x y];").statements
+        self.assertEqual(glyphstr([ab]), "[a b]")
+        self.assertEqual(glyphstr([xy]), "[x y]")
+
+    def test_glyphclass_empty(self):
+        [gc] = self.parse("@empty_set = [];").statements
+        self.assertEqual(gc.name, "empty_set")
+        self.assertEqual(gc.glyphSet(), tuple())
+
+    def test_glyphclass_equality(self):
+        [foo, bar] = self.parse("@foo = [a b]; @bar = @foo;").statements
+        self.assertEqual(foo.glyphSet(), ("a", "b"))
+        self.assertEqual(bar.glyphSet(), ("a", "b"))
+
+    def test_glyphclass_from_markClass(self):
+        doc = self.parse(
+            "markClass [acute grave] <anchor 500 800> @TOP_MARKS;"
+            "markClass cedilla <anchor 500 -100> @BOTTOM_MARKS;"
+            "@MARKS = [@TOP_MARKS @BOTTOM_MARKS ogonek];"
+            "@ALL = @MARKS;")
+        self.assertEqual(doc.statements[-1].glyphSet(),
+                         ("acute", "grave", "cedilla", "ogonek"))
+
+    def test_glyphclass_range_cid(self):
+        [gc] = self.parse(r"@GlyphClass = [\999-\1001];").statements
+        self.assertEqual(gc.name, "GlyphClass")
+        self.assertEqual(gc.glyphSet(), ("cid00999", "cid01000", "cid01001"))
+
+    def test_glyphclass_range_cid_bad(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Bad range: start should be less than limit",
+            self.parse, r"@bad = [\998-\995];")
+
+    def test_glyphclass_range_uppercase(self):
+        [gc] = self.parse("@swashes = [X.swash-Z.swash];").statements
+        self.assertEqual(gc.name, "swashes")
+        self.assertEqual(gc.glyphSet(), ("X.swash", "Y.swash", "Z.swash"))
+
+    def test_glyphclass_range_lowercase(self):
+        [gc] = self.parse("@defg.sc = [d.sc-g.sc];").statements
+        self.assertEqual(gc.name, "defg.sc")
+        self.assertEqual(gc.glyphSet(), ("d.sc", "e.sc", "f.sc", "g.sc"))
+
+    def test_glyphclass_range_dash(self):
+        glyphNames = "A-foo.sc B-foo.sc C-foo.sc".split()
+        [gc] = self.parse("@range = [A-foo.sc-C-foo.sc];", glyphNames).statements
+        self.assertEqual(gc.glyphSet(), ("A-foo.sc", "B-foo.sc", "C-foo.sc"))
+
+    def test_glyphclass_range_dash_with_space(self):
+        gn = "A-foo.sc B-foo.sc C-foo.sc".split()
+        [gc] = self.parse("@range = [A-foo.sc - C-foo.sc];", gn).statements
+        self.assertEqual(gc.glyphSet(), ("A-foo.sc", "B-foo.sc", "C-foo.sc"))
+
+    def test_glyphclass_glyph_name_should_win_over_range(self):
+        # The OpenType Feature File Specification v1.20 makes it clear
+        # that if a dashed name could be interpreted either as a glyph name
+        # or as a range, then the semantics should be the single dashed name.
+        glyphNames = (
+            "A-foo.sc-C-foo.sc A-foo.sc B-foo.sc C-foo.sc".split())
+        [gc] = self.parse("@range = [A-foo.sc-C-foo.sc];", glyphNames).statements
+        self.assertEqual(gc.glyphSet(), ("A-foo.sc-C-foo.sc",))
+
+    def test_glyphclass_range_dash_ambiguous(self):
+        glyphNames = "A B C A-B B-C".split()
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'Ambiguous glyph range "A-B-C"; '
+            'please use "A - B-C" or "A-B - C" to clarify what you mean',
+            self.parse, r"@bad = [A-B-C];", glyphNames)
+
+    def test_glyphclass_range_digit1(self):
+        [gc] = self.parse("@range = [foo.2-foo.5];").statements
+        self.assertEqual(gc.glyphSet(), ("foo.2", "foo.3", "foo.4", "foo.5"))
+
+    def test_glyphclass_range_digit2(self):
+        [gc] = self.parse("@range = [foo.09-foo.11];").statements
+        self.assertEqual(gc.glyphSet(), ("foo.09", "foo.10", "foo.11"))
+
+    def test_glyphclass_range_digit3(self):
+        [gc] = self.parse("@range = [foo.123-foo.125];").statements
+        self.assertEqual(gc.glyphSet(), ("foo.123", "foo.124", "foo.125"))
+
+    def test_glyphclass_range_bad(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Bad range: \"a\" and \"foobar\" should have the same length",
+            self.parse, "@bad = [a-foobar];")
+        self.assertRaisesRegex(
+            FeatureLibError, "Bad range: \"A.swash-z.swash\"",
+            self.parse, "@bad = [A.swash-z.swash];")
+        self.assertRaisesRegex(
+            FeatureLibError, "Start of range must be smaller than its end",
+            self.parse, "@bad = [B.swash-A.swash];")
+        self.assertRaisesRegex(
+            FeatureLibError, "Bad range: \"foo.1234-foo.9876\"",
+            self.parse, "@bad = [foo.1234-foo.9876];")
+
+    def test_glyphclass_range_mixed(self):
+        [gc] = self.parse("@range = [a foo.09-foo.11 X.sc-Z.sc];").statements
+        self.assertEqual(gc.glyphSet(), (
+            "a", "foo.09", "foo.10", "foo.11", "X.sc", "Y.sc", "Z.sc"
+        ))
+
+    def test_glyphclass_reference(self):
+        [vowels_lc, vowels_uc, vowels] = self.parse(
+            "@Vowels.lc = [a e i o u]; @Vowels.uc = [A E I O U];"
+            "@Vowels = [@Vowels.lc @Vowels.uc y Y];").statements
+        self.assertEqual(vowels_lc.glyphSet(), tuple("aeiou"))
+        self.assertEqual(vowels_uc.glyphSet(), tuple("AEIOU"))
+        self.assertEqual(vowels.glyphSet(), tuple("aeiouAEIOUyY"))
+        self.assertEqual(vowels.asFea(),
+            "@Vowels = [@Vowels.lc @Vowels.uc y Y];")
+        self.assertRaisesRegex(
+            FeatureLibError, "Unknown glyph class @unknown",
+            self.parse, "@bad = [@unknown];")
+
+    def test_glyphclass_scoping(self):
+        [foo, liga, smcp] = self.parse(
+            "@foo = [a b];"
+            "feature liga { @bar = [@foo l]; } liga;"
+            "feature smcp { @bar = [@foo s]; } smcp;"
+        ).statements
+        self.assertEqual(foo.glyphSet(), ("a", "b"))
+        self.assertEqual(liga.statements[0].glyphSet(), ("a", "b", "l"))
+        self.assertEqual(smcp.statements[0].glyphSet(), ("a", "b", "s"))
+
+    def test_glyphclass_scoping_bug496(self):
+        # https://github.com/behdad/fonttools/issues/496
+        f1, f2 = self.parse(
+            "feature F1 { lookup L { @GLYPHCLASS = [A B C];} L; } F1;"
+            "feature F2 { sub @GLYPHCLASS by D; } F2;"
+        ).statements
+        self.assertEqual(list(f2.statements[0].glyphs[0].glyphSet()),
+                         ["A", "B", "C"])
+
+    def test_GlyphClassDef(self):
+        doc = self.parse("table GDEF {GlyphClassDef [b],[l],[m],[C c];} GDEF;")
+        s = doc.statements[0].statements[0]
+        self.assertIsInstance(s, ast.GlyphClassDefStatement)
+        self.assertEqual(glyphstr([s.baseGlyphs]), "b")
+        self.assertEqual(glyphstr([s.ligatureGlyphs]), "l")
+        self.assertEqual(glyphstr([s.markGlyphs]), "m")
+        self.assertEqual(glyphstr([s.componentGlyphs]), "[C c]")
+
+    def test_GlyphClassDef_noCLassesSpecified(self):
+        doc = self.parse("table GDEF {GlyphClassDef ,,,;} GDEF;")
+        s = doc.statements[0].statements[0]
+        self.assertIsNone(s.baseGlyphs)
+        self.assertIsNone(s.ligatureGlyphs)
+        self.assertIsNone(s.markGlyphs)
+        self.assertIsNone(s.componentGlyphs)
+
+    def test_ignore_pos(self):
+        doc = self.parse("feature test {ignore pos e t' c, q u' u' x;} test;")
+        sub = doc.statements[0].statements[0]
+        self.assertIsInstance(sub, ast.IgnorePosStatement)
+        [(pref1, glyphs1, suff1), (pref2, glyphs2, suff2)] = sub.chainContexts
+        self.assertEqual(glyphstr(pref1), "e")
+        self.assertEqual(glyphstr(glyphs1), "t")
+        self.assertEqual(glyphstr(suff1), "c")
+        self.assertEqual(glyphstr(pref2), "q")
+        self.assertEqual(glyphstr(glyphs2), "u u")
+        self.assertEqual(glyphstr(suff2), "x")
+
+    def test_ignore_position(self):
+        doc = self.parse(
+            "feature test {"
+            "    ignore position f [a e] d' [a u]' [e y];"
+            "} test;")
+        sub = doc.statements[0].statements[0]
+        self.assertIsInstance(sub, ast.IgnorePosStatement)
+        [(prefix, glyphs, suffix)] = sub.chainContexts
+        self.assertEqual(glyphstr(prefix), "f [a e]")
+        self.assertEqual(glyphstr(glyphs), "d [a u]")
+        self.assertEqual(glyphstr(suffix), "[e y]")
+
+    def test_ignore_position_with_lookup(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'No lookups can be specified for "ignore pos"',
+            self.parse,
+            "lookup L { pos [A A.sc] -100; } L;"
+            "feature test { ignore pos f' i', A' lookup L; } test;")
+
+    def test_ignore_sub(self):
+        doc = self.parse("feature test {ignore sub e t' c, q u' u' x;} test;")
+        sub = doc.statements[0].statements[0]
+        self.assertIsInstance(sub, ast.IgnoreSubstStatement)
+        [(pref1, glyphs1, suff1), (pref2, glyphs2, suff2)] = sub.chainContexts
+        self.assertEqual(glyphstr(pref1), "e")
+        self.assertEqual(glyphstr(glyphs1), "t")
+        self.assertEqual(glyphstr(suff1), "c")
+        self.assertEqual(glyphstr(pref2), "q")
+        self.assertEqual(glyphstr(glyphs2), "u u")
+        self.assertEqual(glyphstr(suff2), "x")
+
+    def test_ignore_substitute(self):
+        doc = self.parse(
+            "feature test {"
+            "    ignore substitute f [a e] d' [a u]' [e y];"
+            "} test;")
+        sub = doc.statements[0].statements[0]
+        self.assertIsInstance(sub, ast.IgnoreSubstStatement)
+        [(prefix, glyphs, suffix)] = sub.chainContexts
+        self.assertEqual(glyphstr(prefix), "f [a e]")
+        self.assertEqual(glyphstr(glyphs), "d [a u]")
+        self.assertEqual(glyphstr(suffix), "[e y]")
+
+    def test_ignore_substitute_with_lookup(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'No lookups can be specified for "ignore sub"',
+            self.parse,
+            "lookup L { sub [A A.sc] by a; } L;"
+            "feature test { ignore sub f' i', A' lookup L; } test;")
+
+    def test_include_statement(self):
+        doc = self.parse("""\
+            include(../family.fea);
+            include # Comment
+                (foo)
+                  ;
+            """, followIncludes=False)
+        s1, s2, s3 = doc.statements
+        self.assertEqual(type(s1), ast.IncludeStatement)
+        self.assertEqual(s1.filename, "../family.fea")
+        self.assertEqual(s1.asFea(), "include(../family.fea);")
+        self.assertEqual(type(s2), ast.IncludeStatement)
+        self.assertEqual(s2.filename, "foo")
+        self.assertEqual(s2.asFea(), "include(foo);")
+        self.assertEqual(type(s3), ast.Comment)
+        self.assertEqual(s3.text, "# Comment")
+
+    def test_include_statement_no_semicolon(self):
+        doc = self.parse("""\
+            include(../family.fea)
+            """, followIncludes=False)
+        s1 = doc.statements[0]
+        self.assertEqual(type(s1), ast.IncludeStatement)
+        self.assertEqual(s1.filename, "../family.fea")
+        self.assertEqual(s1.asFea(), "include(../family.fea);")
+
+    def test_language(self):
+        doc = self.parse("feature test {language DEU;} test;")
+        s = doc.statements[0].statements[0]
+        self.assertEqual(type(s), ast.LanguageStatement)
+        self.assertEqual(s.language, "DEU ")
+        self.assertTrue(s.include_default)
+        self.assertFalse(s.required)
+
+    def test_language_exclude_dflt(self):
+        doc = self.parse("feature test {language DEU exclude_dflt;} test;")
+        s = doc.statements[0].statements[0]
+        self.assertEqual(type(s), ast.LanguageStatement)
+        self.assertEqual(s.language, "DEU ")
+        self.assertFalse(s.include_default)
+        self.assertFalse(s.required)
+
+    def test_language_exclude_dflt_required(self):
+        doc = self.parse("feature test {"
+                         "  language DEU exclude_dflt required;"
+                         "} test;")
+        s = doc.statements[0].statements[0]
+        self.assertEqual(type(s), ast.LanguageStatement)
+        self.assertEqual(s.language, "DEU ")
+        self.assertFalse(s.include_default)
+        self.assertTrue(s.required)
+
+    def test_language_include_dflt(self):
+        doc = self.parse("feature test {language DEU include_dflt;} test;")
+        s = doc.statements[0].statements[0]
+        self.assertEqual(type(s), ast.LanguageStatement)
+        self.assertEqual(s.language, "DEU ")
+        self.assertTrue(s.include_default)
+        self.assertFalse(s.required)
+
+    def test_language_include_dflt_required(self):
+        doc = self.parse("feature test {"
+                         "  language DEU include_dflt required;"
+                         "} test;")
+        s = doc.statements[0].statements[0]
+        self.assertEqual(type(s), ast.LanguageStatement)
+        self.assertEqual(s.language, "DEU ")
+        self.assertTrue(s.include_default)
+        self.assertTrue(s.required)
+
+    def test_language_DFLT(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            '"DFLT" is not a valid language tag; use "dflt" instead',
+            self.parse, "feature test { language DFLT; } test;")
+
+    def test_ligatureCaretByIndex_glyphClass(self):
+        doc = self.parse("table GDEF{LigatureCaretByIndex [c_t f_i] 2;}GDEF;")
+        s = doc.statements[0].statements[0]
+        self.assertIsInstance(s, ast.LigatureCaretByIndexStatement)
+        self.assertEqual(glyphstr([s.glyphs]), "[c_t f_i]")
+        self.assertEqual(s.carets, [2])
+
+    def test_ligatureCaretByIndex_singleGlyph(self):
+        doc = self.parse("table GDEF{LigatureCaretByIndex f_f_i 3 7;}GDEF;")
+        s = doc.statements[0].statements[0]
+        self.assertIsInstance(s, ast.LigatureCaretByIndexStatement)
+        self.assertEqual(glyphstr([s.glyphs]), "f_f_i")
+        self.assertEqual(s.carets, [3, 7])
+
+    def test_ligatureCaretByPos_glyphClass(self):
+        doc = self.parse("table GDEF {LigatureCaretByPos [c_t f_i] 7;} GDEF;")
+        s = doc.statements[0].statements[0]
+        self.assertIsInstance(s, ast.LigatureCaretByPosStatement)
+        self.assertEqual(glyphstr([s.glyphs]), "[c_t f_i]")
+        self.assertEqual(s.carets, [7])
+
+    def test_ligatureCaretByPos_singleGlyph(self):
+        doc = self.parse("table GDEF {LigatureCaretByPos f_i 400 380;} GDEF;")
+        s = doc.statements[0].statements[0]
+        self.assertIsInstance(s, ast.LigatureCaretByPosStatement)
+        self.assertEqual(glyphstr([s.glyphs]), "f_i")
+        self.assertEqual(s.carets, [400, 380])
+
+    def test_lookup_block(self):
+        [lookup] = self.parse("lookup Ligatures {} Ligatures;").statements
+        self.assertEqual(lookup.name, "Ligatures")
+        self.assertFalse(lookup.use_extension)
+
+    def test_lookup_block_useExtension(self):
+        [lookup] = self.parse("lookup Foo useExtension {} Foo;").statements
+        self.assertEqual(lookup.name, "Foo")
+        self.assertTrue(lookup.use_extension)
+
+    def test_lookup_block_name_mismatch(self):
+        self.assertRaisesRegex(
+            FeatureLibError, 'Expected "Foo"',
+            self.parse, "lookup Foo {} Bar;")
+
+    def test_lookup_block_with_horizontal_valueRecordDef(self):
+        doc = self.parse("feature liga {"
+                         "  lookup look {"
+                         "    valueRecordDef 123 foo;"
+                         "  } look;"
+                         "} liga;")
+        [liga] = doc.statements
+        [look] = liga.statements
+        [foo] = look.statements
+        self.assertEqual(foo.value.xAdvance, 123)
+        self.assertIsNone(foo.value.yAdvance)
+
+    def test_lookup_block_with_vertical_valueRecordDef(self):
+        doc = self.parse("feature vkrn {"
+                         "  lookup look {"
+                         "    valueRecordDef 123 foo;"
+                         "  } look;"
+                         "} vkrn;")
+        [vkrn] = doc.statements
+        [look] = vkrn.statements
+        [foo] = look.statements
+        self.assertIsNone(foo.value.xAdvance)
+        self.assertEqual(foo.value.yAdvance, 123)
+
+    def test_lookup_comment(self):
+        [lookup] = self.parse("lookup L { # Comment\n } L;").statements
+        [comment] = lookup.statements
+        self.assertIsInstance(comment, ast.Comment)
+        self.assertEqual(comment.text, "# Comment")
+
+    def test_lookup_reference(self):
+        [foo, bar] = self.parse("lookup Foo {} Foo;"
+                                "feature Bar {lookup Foo;} Bar;").statements
+        [ref] = bar.statements
+        self.assertEqual(type(ref), ast.LookupReferenceStatement)
+        self.assertEqual(ref.lookup, foo)
+
+    def test_lookup_reference_to_lookup_inside_feature(self):
+        [qux, bar] = self.parse("feature Qux {lookup Foo {} Foo;} Qux;"
+                                "feature Bar {lookup Foo;} Bar;").statements
+        [foo] = qux.statements
+        [ref] = bar.statements
+        self.assertIsInstance(ref, ast.LookupReferenceStatement)
+        self.assertEqual(ref.lookup, foo)
+
+    def test_lookup_reference_unknown(self):
+        self.assertRaisesRegex(
+            FeatureLibError, 'Unknown lookup "Huh"',
+            self.parse, "feature liga {lookup Huh;} liga;")
+
+    def parse_lookupflag_(self, s):
+        return self.parse("lookup L {%s} L;" % s).statements[0].statements[-1]
+
+    def test_lookupflag_format_A(self):
+        flag = self.parse_lookupflag_("lookupflag RightToLeft IgnoreMarks;")
+        self.assertIsInstance(flag, ast.LookupFlagStatement)
+        self.assertEqual(flag.value, 9)
+        self.assertIsNone(flag.markAttachment)
+        self.assertIsNone(flag.markFilteringSet)
+
+    def test_lookupflag_format_A_MarkAttachmentType(self):
+        flag = self.parse_lookupflag_(
+            "@TOP_MARKS = [acute grave macron];"
+            "lookupflag RightToLeft MarkAttachmentType @TOP_MARKS;")
+        self.assertIsInstance(flag, ast.LookupFlagStatement)
+        self.assertEqual(flag.value, 1)
+        self.assertIsInstance(flag.markAttachment, ast.GlyphClassName)
+        self.assertEqual(flag.markAttachment.glyphSet(),
+                         ("acute", "grave", "macron"))
+        self.assertIsNone(flag.markFilteringSet)
+
+    def test_lookupflag_format_A_UseMarkFilteringSet(self):
+        flag = self.parse_lookupflag_(
+            "@BOTTOM_MARKS = [cedilla ogonek];"
+            "lookupflag UseMarkFilteringSet @BOTTOM_MARKS IgnoreLigatures;")
+        self.assertIsInstance(flag, ast.LookupFlagStatement)
+        self.assertEqual(flag.value, 4)
+        self.assertIsNone(flag.markAttachment)
+        self.assertIsInstance(flag.markFilteringSet, ast.GlyphClassName)
+        self.assertEqual(flag.markFilteringSet.glyphSet(),
+                         ("cedilla", "ogonek"))
+
+    def test_lookupflag_format_B(self):
+        flag = self.parse_lookupflag_("lookupflag 7;")
+        self.assertIsInstance(flag, ast.LookupFlagStatement)
+        self.assertEqual(flag.value, 7)
+        self.assertIsNone(flag.markAttachment)
+        self.assertIsNone(flag.markFilteringSet)
+
+    def test_lookupflag_repeated(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'RightToLeft can be specified only once',
+            self.parse,
+            "feature test {lookupflag RightToLeft RightToLeft;} test;")
+
+    def test_lookupflag_unrecognized(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            '"IgnoreCookies" is not a recognized lookupflag',
+            self.parse, "feature test {lookupflag IgnoreCookies;} test;")
+
+    def test_gpos_type_1_glyph(self):
+        doc = self.parse("feature kern {pos one <1 2 3 4>;} kern;")
+        pos = doc.statements[0].statements[0]
+        self.assertIsInstance(pos, ast.SinglePosStatement)
+        [(glyphs, value)] = pos.pos
+        self.assertEqual(glyphstr([glyphs]), "one")
+        self.assertEqual(value.makeString(vertical=False), "<1 2 3 4>")
+
+    def test_gpos_type_1_glyphclass_horizontal(self):
+        doc = self.parse("feature kern {pos [one two] -300;} kern;")
+        pos = doc.statements[0].statements[0]
+        self.assertIsInstance(pos, ast.SinglePosStatement)
+        [(glyphs, value)] = pos.pos
+        self.assertEqual(glyphstr([glyphs]), "[one two]")
+        self.assertEqual(value.makeString(vertical=False), "-300")
+
+    def test_gpos_type_1_glyphclass_vertical(self):
+        doc = self.parse("feature vkrn {pos [one two] -300;} vkrn;")
+        pos = doc.statements[0].statements[0]
+        self.assertIsInstance(pos, ast.SinglePosStatement)
+        [(glyphs, value)] = pos.pos
+        self.assertEqual(glyphstr([glyphs]), "[one two]")
+        self.assertEqual(value.makeString(vertical=True), "-300")
+
+    def test_gpos_type_1_multiple(self):
+        doc = self.parse("feature f {pos one'1 two'2 [five six]'56;} f;")
+        pos = doc.statements[0].statements[0]
+        self.assertIsInstance(pos, ast.SinglePosStatement)
+        [(glyphs1, val1), (glyphs2, val2), (glyphs3, val3)] = pos.pos
+        self.assertEqual(glyphstr([glyphs1]), "one")
+        self.assertEqual(val1.makeString(vertical=False), "1")
+        self.assertEqual(glyphstr([glyphs2]), "two")
+        self.assertEqual(val2.makeString(vertical=False), "2")
+        self.assertEqual(glyphstr([glyphs3]), "[five six]")
+        self.assertEqual(val3.makeString(vertical=False), "56")
+        self.assertEqual(pos.prefix, [])
+        self.assertEqual(pos.suffix, [])
+
+    def test_gpos_type_1_enumerated(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            '"enumerate" is only allowed with pair positionings',
+            self.parse, "feature test {enum pos T 100;} test;")
+        self.assertRaisesRegex(
+            FeatureLibError,
+            '"enumerate" is only allowed with pair positionings',
+            self.parse, "feature test {enumerate pos T 100;} test;")
+
+    def test_gpos_type_1_chained(self):
+        doc = self.parse("feature kern {pos [A B] [T Y]' 20 comma;} kern;")
+        pos = doc.statements[0].statements[0]
+        self.assertIsInstance(pos, ast.SinglePosStatement)
+        [(glyphs, value)] = pos.pos
+        self.assertEqual(glyphstr([glyphs]), "[T Y]")
+        self.assertEqual(value.makeString(vertical=False), "20")
+        self.assertEqual(glyphstr(pos.prefix), "[A B]")
+        self.assertEqual(glyphstr(pos.suffix), "comma")
+
+    def test_gpos_type_2_format_a(self):
+        doc = self.parse("feature kern {"
+                         "    pos [T V] -60 [a b c] <1 2 3 4>;"
+                         "} kern;")
+        pos = doc.statements[0].statements[0]
+        self.assertEqual(type(pos), ast.PairPosStatement)
+        self.assertFalse(pos.enumerated)
+        self.assertEqual(glyphstr([pos.glyphs1]), "[T V]")
+        self.assertEqual(pos.valuerecord1.makeString(vertical=False), "-60")
+        self.assertEqual(glyphstr([pos.glyphs2]), "[a b c]")
+        self.assertEqual(pos.valuerecord2.makeString(vertical=False),
+                         "<1 2 3 4>")
+
+    def test_gpos_type_2_format_a_enumerated(self):
+        doc = self.parse("feature kern {"
+                         "    enum pos [T V] -60 [a b c] <1 2 3 4>;"
+                         "} kern;")
+        pos = doc.statements[0].statements[0]
+        self.assertEqual(type(pos), ast.PairPosStatement)
+        self.assertTrue(pos.enumerated)
+        self.assertEqual(glyphstr([pos.glyphs1]), "[T V]")
+        self.assertEqual(pos.valuerecord1.makeString(vertical=False), "-60")
+        self.assertEqual(glyphstr([pos.glyphs2]), "[a b c]")
+        self.assertEqual(pos.valuerecord2.makeString(vertical=False),
+                         "<1 2 3 4>")
+
+    def test_gpos_type_2_format_a_with_null(self):
+        doc = self.parse("feature kern {"
+                         "    pos [T V] <1 2 3 4> [a b c] <NULL>;"
+                         "} kern;")
+        pos = doc.statements[0].statements[0]
+        self.assertEqual(type(pos), ast.PairPosStatement)
+        self.assertFalse(pos.enumerated)
+        self.assertEqual(glyphstr([pos.glyphs1]), "[T V]")
+        self.assertEqual(pos.valuerecord1.makeString(vertical=False),
+                         "<1 2 3 4>")
+        self.assertEqual(glyphstr([pos.glyphs2]), "[a b c]")
+        self.assertIsNone(pos.valuerecord2)
+
+    def test_gpos_type_2_format_b(self):
+        doc = self.parse("feature kern {"
+                         "    pos [T V] [a b c] <1 2 3 4>;"
+                         "} kern;")
+        pos = doc.statements[0].statements[0]
+        self.assertEqual(type(pos), ast.PairPosStatement)
+        self.assertFalse(pos.enumerated)
+        self.assertEqual(glyphstr([pos.glyphs1]), "[T V]")
+        self.assertEqual(pos.valuerecord1.makeString(vertical=False),
+                         "<1 2 3 4>")
+        self.assertEqual(glyphstr([pos.glyphs2]), "[a b c]")
+        self.assertIsNone(pos.valuerecord2)
+
+    def test_gpos_type_2_format_b_enumerated(self):
+        doc = self.parse("feature kern {"
+                         "    enumerate position [T V] [a b c] <1 2 3 4>;"
+                         "} kern;")
+        pos = doc.statements[0].statements[0]
+        self.assertEqual(type(pos), ast.PairPosStatement)
+        self.assertTrue(pos.enumerated)
+        self.assertEqual(glyphstr([pos.glyphs1]), "[T V]")
+        self.assertEqual(pos.valuerecord1.makeString(vertical=False),
+                         "<1 2 3 4>")
+        self.assertEqual(glyphstr([pos.glyphs2]), "[a b c]")
+        self.assertIsNone(pos.valuerecord2)
+
+    def test_gpos_type_3(self):
+        doc = self.parse("feature kern {"
+                         "    position cursive A <anchor 12 -2> <anchor 2 3>;"
+                         "} kern;")
+        pos = doc.statements[0].statements[0]
+        self.assertEqual(type(pos), ast.CursivePosStatement)
+        self.assertEqual(pos.glyphclass.glyphSet(), ("A",))
+        self.assertEqual((pos.entryAnchor.x, pos.entryAnchor.y), (12, -2))
+        self.assertEqual((pos.exitAnchor.x, pos.exitAnchor.y), (2, 3))
+
+    def test_gpos_type_3_enumerated(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            '"enumerate" is not allowed with cursive attachment positioning',
+            self.parse,
+            "feature kern {"
+            "    enumerate position cursive A <anchor 12 -2> <anchor 2 3>;"
+            "} kern;")
+
+    def test_gpos_type_4(self):
+        doc = self.parse(
+            "markClass [acute grave] <anchor 150 -10> @TOP_MARKS;"
+            "markClass [dieresis umlaut] <anchor 300 -10> @TOP_MARKS;"
+            "markClass [cedilla] <anchor 300 600> @BOTTOM_MARKS;"
+            "feature test {"
+            "    position base [a e o u] "
+            "        <anchor 250 450> mark @TOP_MARKS "
+            "        <anchor 210 -10> mark @BOTTOM_MARKS;"
+            "} test;")
+        pos = doc.statements[-1].statements[0]
+        self.assertEqual(type(pos), ast.MarkBasePosStatement)
+        self.assertEqual(pos.base.glyphSet(), ("a", "e", "o", "u"))
+        (a1, m1), (a2, m2) = pos.marks
+        self.assertEqual((a1.x, a1.y, m1.name), (250, 450, "TOP_MARKS"))
+        self.assertEqual((a2.x, a2.y, m2.name), (210, -10, "BOTTOM_MARKS"))
+
+    def test_gpos_type_4_enumerated(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            '"enumerate" is not allowed with '
+            'mark-to-base attachment positioning',
+            self.parse,
+            "feature kern {"
+            "    markClass cedilla <anchor 300 600> @BOTTOM_MARKS;"
+            "    enumerate position base A <anchor 12 -2> mark @BOTTOM_MARKS;"
+            "} kern;")
+
+    def test_gpos_type_4_not_markClass(self):
+        self.assertRaisesRegex(
+            FeatureLibError, "@MARKS is not a markClass", self.parse,
+            "@MARKS = [acute grave];"
+            "feature test {"
+            "    position base [a e o u] <anchor 250 450> mark @MARKS;"
+            "} test;")
+
+    def test_gpos_type_5(self):
+        doc = self.parse(
+            "markClass [grave acute] <anchor 150 500> @TOP_MARKS;"
+            "markClass [cedilla] <anchor 300 -100> @BOTTOM_MARKS;"
+            "feature test {"
+            "    position "
+            "        ligature [a_f_f_i o_f_f_i] "
+            "            <anchor 50 600> mark @TOP_MARKS "
+            "            <anchor 50 -10> mark @BOTTOM_MARKS "
+            "        ligComponent "
+            "            <anchor 30 800> mark @TOP_MARKS "
+            "        ligComponent "
+            "            <anchor NULL> "
+            "        ligComponent "
+            "            <anchor 30 -10> mark @BOTTOM_MARKS;"
+            "} test;")
+        pos = doc.statements[-1].statements[0]
+        self.assertEqual(type(pos), ast.MarkLigPosStatement)
+        self.assertEqual(pos.ligatures.glyphSet(), ("a_f_f_i", "o_f_f_i"))
+        [(a11, m11), (a12, m12)], [(a2, m2)], [], [(a4, m4)] = pos.marks
+        self.assertEqual((a11.x, a11.y, m11.name), (50, 600, "TOP_MARKS"))
+        self.assertEqual((a12.x, a12.y, m12.name), (50, -10, "BOTTOM_MARKS"))
+        self.assertEqual((a2.x, a2.y, m2.name), (30, 800, "TOP_MARKS"))
+        self.assertEqual((a4.x, a4.y, m4.name), (30, -10, "BOTTOM_MARKS"))
+
+    def test_gpos_type_5_enumerated(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            '"enumerate" is not allowed with '
+            'mark-to-ligature attachment positioning',
+            self.parse,
+            "feature test {"
+            "    markClass cedilla <anchor 300 600> @MARKS;"
+            "    enumerate position "
+            "        ligature f_i <anchor 100 0> mark @MARKS"
+            "        ligComponent <anchor NULL>;"
+            "} test;")
+
+    def test_gpos_type_5_not_markClass(self):
+        self.assertRaisesRegex(
+            FeatureLibError, "@MARKS is not a markClass", self.parse,
+            "@MARKS = [acute grave];"
+            "feature test {"
+            "    position ligature f_i <anchor 250 450> mark @MARKS;"
+            "} test;")
+
+    def test_gpos_type_6(self):
+        doc = self.parse(
+            "markClass damma <anchor 189 -103> @MARK_CLASS_1;"
+            "feature test {"
+            "    position mark hamza <anchor 221 301> mark @MARK_CLASS_1;"
+            "} test;")
+        pos = doc.statements[-1].statements[0]
+        self.assertEqual(type(pos), ast.MarkMarkPosStatement)
+        self.assertEqual(pos.baseMarks.glyphSet(), ("hamza",))
+        [(a1, m1)] = pos.marks
+        self.assertEqual((a1.x, a1.y, m1.name), (221, 301, "MARK_CLASS_1"))
+
+    def test_gpos_type_6_enumerated(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            '"enumerate" is not allowed with '
+            'mark-to-mark attachment positioning',
+            self.parse,
+            "markClass damma <anchor 189 -103> @MARK_CLASS_1;"
+            "feature test {"
+            "    enum pos mark hamza <anchor 221 301> mark @MARK_CLASS_1;"
+            "} test;")
+
+    def test_gpos_type_6_not_markClass(self):
+        self.assertRaisesRegex(
+            FeatureLibError, "@MARKS is not a markClass", self.parse,
+            "@MARKS = [acute grave];"
+            "feature test {"
+            "    position mark cedilla <anchor 250 450> mark @MARKS;"
+            "} test;")
+
+    def test_gpos_type_8(self):
+        doc = self.parse(
+            "lookup L1 {pos one 100;} L1; lookup L2 {pos two 200;} L2;"
+            "feature test {"
+            "    pos [A a] [B b] I' lookup L1 [N n]' lookup L2 P' [Y y] [Z z];"
+            "} test;")
+        lookup1, lookup2 = doc.statements[0:2]
+        pos = doc.statements[-1].statements[0]
+        self.assertEqual(type(pos), ast.ChainContextPosStatement)
+        self.assertEqual(glyphstr(pos.prefix), "[A a] [B b]")
+        self.assertEqual(glyphstr(pos.glyphs), "I [N n] P")
+        self.assertEqual(glyphstr(pos.suffix), "[Y y] [Z z]")
+        self.assertEqual(pos.lookups, [lookup1, lookup2, None])
+
+    def test_gpos_type_8_lookup_with_values(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'If "lookup" is present, no values must be specified',
+            self.parse,
+            "lookup L1 {pos one 100;} L1;"
+            "feature test {"
+            "    pos A' lookup L1 B' 20;"
+            "} test;")
+
+    def test_markClass(self):
+        doc = self.parse("markClass [acute grave] <anchor 350 3> @MARKS;")
+        mc = doc.statements[0]
+        self.assertIsInstance(mc, ast.MarkClassDefinition)
+        self.assertEqual(mc.markClass.name, "MARKS")
+        self.assertEqual(mc.glyphSet(), ("acute", "grave"))
+        self.assertEqual((mc.anchor.x, mc.anchor.y), (350, 3))
+
+    def test_nameid_windows_utf16(self):
+        doc = self.parse(
+            r'table name { nameid 9 "M\00fcller-Lanc\00e9"; } name;')
+        name = doc.statements[0].statements[0]
+        self.assertIsInstance(name, ast.NameRecord)
+        self.assertEqual(name.nameID, 9)
+        self.assertEqual(name.platformID, 3)
+        self.assertEqual(name.platEncID, 1)
+        self.assertEqual(name.langID, 0x0409)
+        self.assertEqual(name.string, "Müller-Lancé")
+        self.assertEqual(name.asFea(), r'nameid 9 "M\00fcller-Lanc\00e9";')
+
+    def test_nameid_windows_utf16_backslash(self):
+        doc = self.parse(r'table name { nameid 9 "Back\005cslash"; } name;')
+        name = doc.statements[0].statements[0]
+        self.assertEqual(name.string, r"Back\slash")
+        self.assertEqual(name.asFea(), r'nameid 9 "Back\005cslash";')
+
+    def test_nameid_windows_utf16_quotation_mark(self):
+        doc = self.parse(
+            r'table name { nameid 9 "Quotation \0022Mark\0022"; } name;')
+        name = doc.statements[0].statements[0]
+        self.assertEqual(name.string, 'Quotation "Mark"')
+        self.assertEqual(name.asFea(), r'nameid 9 "Quotation \0022Mark\0022";')
+
+    def test_nameid_windows_utf16_surroates(self):
+        doc = self.parse(r'table name { nameid 9 "Carrot \D83E\DD55"; } name;')
+        name = doc.statements[0].statements[0]
+        self.assertEqual(name.string, r"Carrot 🥕")
+        self.assertEqual(name.asFea(), r'nameid 9 "Carrot \d83e\dd55";')
+
+    def test_nameid_mac_roman(self):
+        doc = self.parse(
+            r'table name { nameid 9 1 "Joachim M\9fller-Lanc\8e"; } name;')
+        name = doc.statements[0].statements[0]
+        self.assertIsInstance(name, ast.NameRecord)
+        self.assertEqual(name.nameID, 9)
+        self.assertEqual(name.platformID, 1)
+        self.assertEqual(name.platEncID, 0)
+        self.assertEqual(name.langID, 0)
+        self.assertEqual(name.string, "Joachim Müller-Lancé")
+        self.assertEqual(name.asFea(),
+                         r'nameid 9 1 "Joachim M\9fller-Lanc\8e";')
+
+    def test_nameid_mac_croatian(self):
+        doc = self.parse(
+            r'table name { nameid 9 1 0 18 "Jovica Veljovi\e6"; } name;')
+        name = doc.statements[0].statements[0]
+        self.assertEqual(name.nameID, 9)
+        self.assertEqual(name.platformID, 1)
+        self.assertEqual(name.platEncID, 0)
+        self.assertEqual(name.langID, 18)
+        self.assertEqual(name.string, "Jovica Veljović")
+        self.assertEqual(name.asFea(), r'nameid 9 1 0 18 "Jovica Veljovi\e6";')
+
+    def test_nameid_unsupported_platform(self):
+        self.assertRaisesRegex(
+            FeatureLibError, "Expected platform id 1 or 3",
+            self.parse, 'table name { nameid 9 666 "Foo"; } name;')
+
+    def test_rsub_format_a(self):
+        doc = self.parse("feature test {rsub a [b B] c' d [e E] by C;} test;")
+        rsub = doc.statements[0].statements[0]
+        self.assertEqual(type(rsub), ast.ReverseChainSingleSubstStatement)
+        self.assertEqual(glyphstr(rsub.old_prefix), "a [B b]")
+        self.assertEqual(rsub.glyphs[0].glyphSet(), ("c",))
+        self.assertEqual(rsub.replacements[0].glyphSet(), ("C",))
+        self.assertEqual(glyphstr(rsub.old_suffix), "d [E e]")
+
+    def test_rsub_format_a_cid(self):
+        doc = self.parse(r"feature test {rsub \1 [\2 \3] \4' \5 by \6;} test;")
+        rsub = doc.statements[0].statements[0]
+        self.assertEqual(type(rsub), ast.ReverseChainSingleSubstStatement)
+        self.assertEqual(glyphstr(rsub.old_prefix),
+                         "cid00001 [cid00002 cid00003]")
+        self.assertEqual(rsub.glyphs[0].glyphSet(), ("cid00004",))
+        self.assertEqual(rsub.replacements[0].glyphSet(), ("cid00006",))
+        self.assertEqual(glyphstr(rsub.old_suffix), "cid00005")
+
+    def test_rsub_format_b(self):
+        doc = self.parse(
+            "feature smcp {"
+            "    reversesub A B [one.fitted one.oldstyle]' C [d D] by one;"
+            "} smcp;")
+        rsub = doc.statements[0].statements[0]
+        self.assertEqual(type(rsub), ast.ReverseChainSingleSubstStatement)
+        self.assertEqual(glyphstr(rsub.old_prefix), "A B")
+        self.assertEqual(glyphstr(rsub.old_suffix), "C [D d]")
+        self.assertEqual(mapping(rsub), {
+            "one.fitted": "one",
+            "one.oldstyle": "one"
+        })
+
+    def test_rsub_format_c(self):
+        doc = self.parse(
+            "feature test {"
+            "    reversesub BACK TRACK [a-d]' LOOK AHEAD by [A.sc-D.sc];"
+            "} test;")
+        rsub = doc.statements[0].statements[0]
+        self.assertEqual(type(rsub), ast.ReverseChainSingleSubstStatement)
+        self.assertEqual(glyphstr(rsub.old_prefix), "BACK TRACK")
+        self.assertEqual(glyphstr(rsub.old_suffix), "LOOK AHEAD")
+        self.assertEqual(mapping(rsub), {
+            "a": "A.sc",
+            "b": "B.sc",
+            "c": "C.sc",
+            "d": "D.sc"
+        })
+
+    def test_rsub_from(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'Reverse chaining substitutions do not support "from"',
+            self.parse, "feature test {rsub a from [a.1 a.2 a.3];} test;")
+
+    def test_rsub_nonsingle(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "In reverse chaining single substitutions, only a single glyph "
+            "or glyph class can be replaced",
+            self.parse, "feature test {rsub c d by c_d;} test;")
+
+    def test_rsub_multiple_replacement_glyphs(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'In reverse chaining single substitutions, the replacement '
+            '\(after "by"\) must be a single glyph or glyph class',
+            self.parse, "feature test {rsub f_i by f i;} test;")
+
+    def test_script(self):
+        doc = self.parse("feature test {script cyrl;} test;")
+        s = doc.statements[0].statements[0]
+        self.assertEqual(type(s), ast.ScriptStatement)
+        self.assertEqual(s.script, "cyrl")
+
+    def test_script_dflt(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            '"dflt" is not a valid script tag; use "DFLT" instead',
+            self.parse, "feature test {script dflt;} test;")
+
+    def test_sub_single_format_a(self):  # GSUB LookupType 1
+        doc = self.parse("feature smcp {substitute a by a.sc;} smcp;")
+        sub = doc.statements[0].statements[0]
+        self.assertIsInstance(sub, ast.SingleSubstStatement)
+        self.assertEqual(glyphstr(sub.prefix), "")
+        self.assertEqual(mapping(sub), {"a": "a.sc"})
+        self.assertEqual(glyphstr(sub.suffix), "")
+
+    def test_sub_single_format_a_chained(self):  # chain to GSUB LookupType 1
+        doc = self.parse("feature test {sub [A a] d' [C] by d.alt;} test;")
+        sub = doc.statements[0].statements[0]
+        self.assertIsInstance(sub, ast.SingleSubstStatement)
+        self.assertEqual(mapping(sub), {"d": "d.alt"})
+        self.assertEqual(glyphstr(sub.prefix), "[A a]")
+        self.assertEqual(glyphstr(sub.suffix), "C")
+
+    def test_sub_single_format_a_cid(self):  # GSUB LookupType 1
+        doc = self.parse(r"feature smcp {substitute \12345 by \78987;} smcp;")
+        sub = doc.statements[0].statements[0]
+        self.assertIsInstance(sub, ast.SingleSubstStatement)
+        self.assertEqual(glyphstr(sub.prefix), "")
+        self.assertEqual(mapping(sub), {"cid12345": "cid78987"})
+        self.assertEqual(glyphstr(sub.suffix), "")
+
+    def test_sub_single_format_b(self):  # GSUB LookupType 1
+        doc = self.parse(
+            "feature smcp {"
+            "    substitute [one.fitted one.oldstyle] by one;"
+            "} smcp;")
+        sub = doc.statements[0].statements[0]
+        self.assertIsInstance(sub, ast.SingleSubstStatement)
+        self.assertEqual(mapping(sub), {
+            "one.fitted": "one",
+            "one.oldstyle": "one"
+        })
+        self.assertEqual(glyphstr(sub.prefix), "")
+        self.assertEqual(glyphstr(sub.suffix), "")
+
+    def test_sub_single_format_b_chained(self):  # chain to GSUB LookupType 1
+        doc = self.parse(
+            "feature smcp {"
+            "    substitute PRE FIX [one.fitted one.oldstyle]' SUF FIX by one;"
+            "} smcp;")
+        sub = doc.statements[0].statements[0]
+        self.assertIsInstance(sub, ast.SingleSubstStatement)
+        self.assertEqual(mapping(sub), {
+            "one.fitted": "one",
+            "one.oldstyle": "one"
+        })
+        self.assertEqual(glyphstr(sub.prefix), "PRE FIX")
+        self.assertEqual(glyphstr(sub.suffix), "SUF FIX")
+
+    def test_sub_single_format_c(self):  # GSUB LookupType 1
+        doc = self.parse(
+            "feature smcp {"
+            "    substitute [a-d] by [A.sc-D.sc];"
+            "} smcp;")
+        sub = doc.statements[0].statements[0]
+        self.assertIsInstance(sub, ast.SingleSubstStatement)
+        self.assertEqual(mapping(sub), {
+            "a": "A.sc",
+            "b": "B.sc",
+            "c": "C.sc",
+            "d": "D.sc"
+        })
+        self.assertEqual(glyphstr(sub.prefix), "")
+        self.assertEqual(glyphstr(sub.suffix), "")
+
+    def test_sub_single_format_c_chained(self):  # chain to GSUB LookupType 1
+        doc = self.parse(
+            "feature smcp {"
+            "    substitute [a-d]' X Y [Z z] by [A.sc-D.sc];"
+            "} smcp;")
+        sub = doc.statements[0].statements[0]
+        self.assertIsInstance(sub, ast.SingleSubstStatement)
+        self.assertEqual(mapping(sub), {
+            "a": "A.sc",
+            "b": "B.sc",
+            "c": "C.sc",
+            "d": "D.sc"
+        })
+        self.assertEqual(glyphstr(sub.prefix), "")
+        self.assertEqual(glyphstr(sub.suffix), "X Y [Z z]")
+
+    def test_sub_single_format_c_different_num_elements(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'Expected a glyph class with 4 elements after "by", '
+            'but found a glyph class with 26 elements',
+            self.parse, "feature smcp {sub [a-d] by [A.sc-Z.sc];} smcp;")
+
+    def test_sub_with_values(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Substitution statements cannot contain values",
+            self.parse, "feature smcp {sub A' 20 by A.sc;} smcp;")
+
+    def test_substitute_multiple(self):  # GSUB LookupType 2
+        doc = self.parse("lookup Look {substitute f_f_i by f f i;} Look;")
+        sub = doc.statements[0].statements[0]
+        self.assertIsInstance(sub, ast.MultipleSubstStatement)
+        self.assertEqual(sub.glyph, "f_f_i")
+        self.assertEqual(sub.replacement, ("f", "f", "i"))
+
+    def test_substitute_multiple_chained(self):  # chain to GSUB LookupType 2
+        doc = self.parse("lookup L {sub [A-C] f_f_i' [X-Z] by f f i;} L;")
+        sub = doc.statements[0].statements[0]
+        self.assertIsInstance(sub, ast.MultipleSubstStatement)
+        self.assertEqual(sub.glyph, "f_f_i")
+        self.assertEqual(sub.replacement, ("f", "f", "i"))
+
+    def test_substitute_multiple_by_mutliple(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Direct substitution of multiple glyphs by multiple glyphs "
+            "is not supported",
+            self.parse,
+            "lookup MxM {sub a b c by d e f;} MxM;")
+
+    def test_split_marked_glyphs_runs(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Unsupported contextual target sequence",
+            self.parse, "feature test{"
+                        "    ignore pos a' x x A';"
+                        "} test;")
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Unsupported contextual target sequence",
+            self.parse, "lookup shift {"
+                        "    pos a <0 -10 0 0>;"
+                        "    pos A <0 10 0 0>;"
+                        "} shift;"
+                        "feature test {"
+                        "    sub a' lookup shift x x A' lookup shift;"
+                        "} test;")
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Unsupported contextual target sequence",
+            self.parse, "feature test {"
+                        "    ignore sub a' x x A';"
+                        "} test;")
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Unsupported contextual target sequence",
+            self.parse, "lookup upper {"
+                        "    sub a by A;"
+                        "} upper;"
+                        "lookup lower {"
+                        "    sub A by a;"
+                        "} lower;"
+                        "feature test {"
+                        "    sub a' lookup upper x x A' lookup lower;"
+                        "} test;")
+
+    def test_substitute_mix_single_multiple(self):
+        doc = self.parse("lookup Look {"
+                         "  sub f_f   by f f;"
+                         "  sub f     by f;"
+                         "  sub f_f_i by f f i;"
+                         "} Look;")
+        statements = doc.statements[0].statements
+        for sub in statements:
+            self.assertIsInstance(sub, ast.MultipleSubstStatement)
+        self.assertEqual(statements[1].glyph, "f")
+        self.assertEqual(statements[1].replacement, ["f"])
+
+    def test_substitute_from(self):  # GSUB LookupType 3
+        doc = self.parse("feature test {"
+                         "  substitute a from [a.1 a.2 a.3];"
+                         "} test;")
+        sub = doc.statements[0].statements[0]
+        self.assertIsInstance(sub, ast.AlternateSubstStatement)
+        self.assertEqual(glyphstr(sub.prefix), "")
+        self.assertEqual(glyphstr([sub.glyph]), "a")
+        self.assertEqual(glyphstr(sub.suffix), "")
+        self.assertEqual(glyphstr([sub.replacement]), "[a.1 a.2 a.3]")
+
+    def test_substitute_from_chained(self):  # chain to GSUB LookupType 3
+        doc = self.parse("feature test {"
+                         "  substitute A B a' [Y y] Z from [a.1 a.2 a.3];"
+                         "} test;")
+        sub = doc.statements[0].statements[0]
+        self.assertIsInstance(sub, ast.AlternateSubstStatement)
+        self.assertEqual(glyphstr(sub.prefix), "A B")
+        self.assertEqual(glyphstr([sub.glyph]), "a")
+        self.assertEqual(glyphstr(sub.suffix), "[Y y] Z")
+        self.assertEqual(glyphstr([sub.replacement]), "[a.1 a.2 a.3]")
+
+    def test_substitute_from_cid(self):  # GSUB LookupType 3
+        doc = self.parse(r"feature test {"
+                         r"  substitute \7 from [\111 \222];"
+                         r"} test;")
+        sub = doc.statements[0].statements[0]
+        self.assertIsInstance(sub, ast.AlternateSubstStatement)
+        self.assertEqual(glyphstr(sub.prefix), "")
+        self.assertEqual(glyphstr([sub.glyph]), "cid00007")
+        self.assertEqual(glyphstr(sub.suffix), "")
+        self.assertEqual(glyphstr([sub.replacement]), "[cid00111 cid00222]")
+
+    def test_substitute_from_glyphclass(self):  # GSUB LookupType 3
+        doc = self.parse("feature test {"
+                         "  @Ampersands = [ampersand.1 ampersand.2];"
+                         "  substitute ampersand from @Ampersands;"
+                         "} test;")
+        [glyphclass, sub] = doc.statements[0].statements
+        self.assertIsInstance(sub, ast.AlternateSubstStatement)
+        self.assertEqual(glyphstr(sub.prefix), "")
+        self.assertEqual(glyphstr([sub.glyph]), "ampersand")
+        self.assertEqual(glyphstr(sub.suffix), "")
+        self.assertEqual(glyphstr([sub.replacement]),
+                         "[ampersand.1 ampersand.2]")
+
+    def test_substitute_ligature(self):  # GSUB LookupType 4
+        doc = self.parse("feature liga {substitute f f i by f_f_i;} liga;")
+        sub = doc.statements[0].statements[0]
+        self.assertIsInstance(sub, ast.LigatureSubstStatement)
+        self.assertEqual(glyphstr(sub.glyphs), "f f i")
+        self.assertEqual(sub.replacement, "f_f_i")
+        self.assertEqual(glyphstr(sub.prefix), "")
+        self.assertEqual(glyphstr(sub.suffix), "")
+
+    def test_substitute_ligature_chained(self):  # chain to GSUB LookupType 4
+        doc = self.parse("feature F {substitute A B f' i' Z by f_i;} F;")
+        sub = doc.statements[0].statements[0]
+        self.assertIsInstance(sub, ast.LigatureSubstStatement)
+        self.assertEqual(glyphstr(sub.glyphs), "f i")
+        self.assertEqual(sub.replacement, "f_i")
+        self.assertEqual(glyphstr(sub.prefix), "A B")
+        self.assertEqual(glyphstr(sub.suffix), "Z")
+
+    def test_substitute_lookups(self):  # GSUB LookupType 6
+        doc = Parser(self.getpath("spec5fi1.fea"), GLYPHNAMES).parse()
+        [_, _, _, langsys, ligs, sub, feature] = doc.statements
+        self.assertEqual(feature.statements[0].lookups, [ligs, None, sub])
+        self.assertEqual(feature.statements[1].lookups, [ligs, None, sub])
+
+    def test_substitute_missing_by(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'Expected "by", "from" or explicit lookup references',
+            self.parse, "feature liga {substitute f f i;} liga;")
+
+    def test_subtable(self):
+        doc = self.parse("feature test {subtable;} test;")
+        s = doc.statements[0].statements[0]
+        self.assertIsInstance(s, ast.SubtableStatement)
+
+    def test_table_badEnd(self):
+        self.assertRaisesRegex(
+            FeatureLibError, 'Expected "GDEF"', self.parse,
+            "table GDEF {LigatureCaretByPos f_i 400;} ABCD;")
+
+    def test_table_comment(self):
+        for table in "BASE GDEF OS/2 head hhea name vhea".split():
+            doc = self.parse("table %s { # Comment\n } %s;" % (table, table))
+            comment = doc.statements[0].statements[0]
+            self.assertIsInstance(comment, ast.Comment)
+            self.assertEqual(comment.text, "# Comment")
+
+    def test_table_unsupported(self):
+        self.assertRaisesRegex(
+            FeatureLibError, '"table Foo" is not supported', self.parse,
+            "table Foo {LigatureCaretByPos f_i 400;} Foo;")
+
+    def test_valuerecord_format_a_horizontal(self):
+        doc = self.parse("feature liga {valueRecordDef 123 foo;} liga;")
+        value = doc.statements[0].statements[0].value
+        self.assertIsNone(value.xPlacement)
+        self.assertIsNone(value.yPlacement)
+        self.assertEqual(value.xAdvance, 123)
+        self.assertIsNone(value.yAdvance)
+        self.assertIsNone(value.xPlaDevice)
+        self.assertIsNone(value.yPlaDevice)
+        self.assertIsNone(value.xAdvDevice)
+        self.assertIsNone(value.yAdvDevice)
+        self.assertEqual(value.makeString(vertical=False), "123")
+
+    def test_valuerecord_format_a_vertical(self):
+        doc = self.parse("feature vkrn {valueRecordDef 123 foo;} vkrn;")
+        value = doc.statements[0].statements[0].value
+        self.assertIsNone(value.xPlacement)
+        self.assertIsNone(value.yPlacement)
+        self.assertIsNone(value.xAdvance)
+        self.assertEqual(value.yAdvance, 123)
+        self.assertIsNone(value.xPlaDevice)
+        self.assertIsNone(value.yPlaDevice)
+        self.assertIsNone(value.xAdvDevice)
+        self.assertIsNone(value.yAdvDevice)
+        self.assertEqual(value.makeString(vertical=True), "123")
+
+    def test_valuerecord_format_a_zero_horizontal(self):
+        doc = self.parse("feature liga {valueRecordDef 0 foo;} liga;")
+        value = doc.statements[0].statements[0].value
+        self.assertIsNone(value.xPlacement)
+        self.assertIsNone(value.yPlacement)
+        self.assertEqual(value.xAdvance, 0)
+        self.assertIsNone(value.yAdvance)
+        self.assertIsNone(value.xPlaDevice)
+        self.assertIsNone(value.yPlaDevice)
+        self.assertIsNone(value.xAdvDevice)
+        self.assertIsNone(value.yAdvDevice)
+        self.assertEqual(value.makeString(vertical=False), "0")
+
+    def test_valuerecord_format_a_zero_vertical(self):
+        doc = self.parse("feature vkrn {valueRecordDef 0 foo;} vkrn;")
+        value = doc.statements[0].statements[0].value
+        self.assertIsNone(value.xPlacement)
+        self.assertIsNone(value.yPlacement)
+        self.assertIsNone(value.xAdvance)
+        self.assertEqual(value.yAdvance, 0)
+        self.assertIsNone(value.xPlaDevice)
+        self.assertIsNone(value.yPlaDevice)
+        self.assertIsNone(value.xAdvDevice)
+        self.assertIsNone(value.yAdvDevice)
+        self.assertEqual(value.makeString(vertical=True), "0")
+
+    def test_valuerecord_format_a_vertical_contexts_(self):
+        for tag in "vkrn vpal vhal valt".split():
+            doc = self.parse(
+                "feature %s {valueRecordDef 77 foo;} %s;" % (tag, tag))
+            value = doc.statements[0].statements[0].value
+            if value.yAdvance != 77:
+                self.fail(msg="feature %s should be a vertical context "
+                          "for ValueRecord format A" % tag)
+
+    def test_valuerecord_format_b(self):
+        doc = self.parse("feature liga {valueRecordDef <1 2 3 4> foo;} liga;")
+        value = doc.statements[0].statements[0].value
+        self.assertEqual(value.xPlacement, 1)
+        self.assertEqual(value.yPlacement, 2)
+        self.assertEqual(value.xAdvance, 3)
+        self.assertEqual(value.yAdvance, 4)
+        self.assertIsNone(value.xPlaDevice)
+        self.assertIsNone(value.yPlaDevice)
+        self.assertIsNone(value.xAdvDevice)
+        self.assertIsNone(value.yAdvDevice)
+        self.assertEqual(value.makeString(vertical=False), "<1 2 3 4>")
+
+    def test_valuerecord_format_b_zero(self):
+        doc = self.parse("feature liga {valueRecordDef <0 0 0 0> foo;} liga;")
+        value = doc.statements[0].statements[0].value
+        self.assertEqual(value.xPlacement, 0)
+        self.assertEqual(value.yPlacement, 0)
+        self.assertEqual(value.xAdvance, 0)
+        self.assertEqual(value.yAdvance, 0)
+        self.assertIsNone(value.xPlaDevice)
+        self.assertIsNone(value.yPlaDevice)
+        self.assertIsNone(value.xAdvDevice)
+        self.assertIsNone(value.yAdvDevice)
+        self.assertEqual(value.makeString(vertical=False), "<0 0 0 0>")
+
+    def test_valuerecord_format_c(self):
+        doc = self.parse(
+            "feature liga {"
+            "    valueRecordDef <"
+            "        1 2 3 4"
+            "        <device 8 88>"
+            "        <device 11 111, 12 112>"
+            "        <device NULL>"
+            "        <device 33 -113, 44 -114, 55 115>"
+            "    > foo;"
+            "} liga;")
+        value = doc.statements[0].statements[0].value
+        self.assertEqual(value.xPlacement, 1)
+        self.assertEqual(value.yPlacement, 2)
+        self.assertEqual(value.xAdvance, 3)
+        self.assertEqual(value.yAdvance, 4)
+        self.assertEqual(value.xPlaDevice, ((8, 88),))
+        self.assertEqual(value.yPlaDevice, ((11, 111), (12, 112)))
+        self.assertIsNone(value.xAdvDevice)
+        self.assertEqual(value.yAdvDevice, ((33, -113), (44, -114), (55, 115)))
+        self.assertEqual(value.makeString(vertical=False),
+                         "<1 2 3 4 <device 8 88> <device 11 111, 12 112>"
+                         " <device NULL> <device 33 -113, 44 -114, 55 115>>")
+
+    def test_valuerecord_format_d(self):
+        doc = self.parse("feature test {valueRecordDef <NULL> foo;} test;")
+        value = doc.statements[0].statements[0].value
+        self.assertIsNone(value)
+
+    def test_valuerecord_named(self):
+        doc = self.parse("valueRecordDef <1 2 3 4> foo;"
+                         "feature liga {valueRecordDef <foo> bar;} liga;")
+        value = doc.statements[1].statements[0].value
+        self.assertEqual(value.xPlacement, 1)
+        self.assertEqual(value.yPlacement, 2)
+        self.assertEqual(value.xAdvance, 3)
+        self.assertEqual(value.yAdvance, 4)
+
+    def test_valuerecord_named_unknown(self):
+        self.assertRaisesRegex(
+            FeatureLibError, "Unknown valueRecordDef \"unknown\"",
+            self.parse, "valueRecordDef <unknown> foo;")
+
+    def test_valuerecord_scoping(self):
+        [foo, liga, smcp] = self.parse(
+            "valueRecordDef 789 foo;"
+            "feature liga {valueRecordDef <foo> bar;} liga;"
+            "feature smcp {valueRecordDef <foo> bar;} smcp;"
+        ).statements
+        self.assertEqual(foo.value.xAdvance, 789)
+        self.assertEqual(liga.statements[0].value.xAdvance, 789)
+        self.assertEqual(smcp.statements[0].value.xAdvance, 789)
+
+    def test_valuerecord_device_value_out_of_range(self):
+        self.assertRaisesRegex(
+            FeatureLibError, r"Device value out of valid range \(-128..127\)",
+            self.parse,
+            "valueRecordDef <1 2 3 4 <device NULL> <device NULL> "
+            "<device NULL> <device 11 128>> foo;")
+
+    def test_languagesystem(self):
+        [langsys] = self.parse("languagesystem latn DEU;").statements
+        self.assertEqual(langsys.script, "latn")
+        self.assertEqual(langsys.language, "DEU ")
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'For script "DFLT", the language must be "dflt"',
+            self.parse, "languagesystem DFLT DEU;")
+        self.assertRaisesRegex(
+            FeatureLibError,
+            '"dflt" is not a valid script tag; use "DFLT" instead',
+            self.parse, "languagesystem dflt dflt;")
+        self.assertRaisesRegex(
+            FeatureLibError,
+            '"DFLT" is not a valid language tag; use "dflt" instead',
+            self.parse, "languagesystem latn DFLT;")
+        self.assertRaisesRegex(
+            FeatureLibError, "Expected ';'",
+            self.parse, "languagesystem latn DEU")
+        self.assertRaisesRegex(
+            FeatureLibError, "longer than 4 characters",
+            self.parse, "languagesystem foobar DEU;")
+        self.assertRaisesRegex(
+            FeatureLibError, "longer than 4 characters",
+            self.parse, "languagesystem latn FOOBAR;")
+
+    def test_empty_statement_ignored(self):
+        doc = self.parse("feature test {;} test;")
+        self.assertFalse(doc.statements[0].statements)
+        doc = self.parse(";;;")
+        self.assertFalse(doc.statements)
+        for table in "BASE GDEF OS/2 head hhea name vhea".split():
+            doc = self.parse("table %s { ;;; } %s;" % (table, table))
+            self.assertEqual(doc.statements[0].statements, [])
+
+    def parse(self, text, glyphNames=GLYPHNAMES, followIncludes=True):
+        featurefile = UnicodeIO(text)
+        p = Parser(featurefile, glyphNames, followIncludes=followIncludes)
+        return p.parse()
+
+    @staticmethod
+    def getpath(testfile):
+        path, _ = os.path.split(__file__)
+        return os.path.join(path, "data", testfile)
+
+
+class SymbolTableTest(unittest.TestCase):
+    def test_scopes(self):
+        symtab = SymbolTable()
+        symtab.define("foo", 23)
+        self.assertEqual(symtab.resolve("foo"), 23)
+        symtab.enter_scope()
+        self.assertEqual(symtab.resolve("foo"), 23)
+        symtab.define("foo", 42)
+        self.assertEqual(symtab.resolve("foo"), 42)
+        symtab.exit_scope()
+        self.assertEqual(symtab.resolve("foo"), 23)
+
+    def test_resolve_undefined(self):
+        self.assertEqual(SymbolTable().resolve("abc"), None)
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/merge_test.py b/Tests/merge_test.py
new file mode 100644
index 0000000..00e719b
--- /dev/null
+++ b/Tests/merge_test.py
@@ -0,0 +1,118 @@
+from fontTools.misc.py23 import *
+from fontTools import ttLib
+from fontTools.merge import *
+import unittest
+
+
+class MergeIntegrationTest(unittest.TestCase):
+	# TODO
+	pass
+
+class gaspMergeUnitTest(unittest.TestCase):
+	def setUp(self):
+		self.merger = Merger()
+
+		self.table1 = ttLib.newTable('gasp')
+		self.table1.version = 1
+		self.table1.gaspRange = {
+			0x8: 0xA ,
+			0x10: 0x5,
+		}
+
+		self.table2 = ttLib.newTable('gasp')
+		self.table2.version = 1
+		self.table2.gaspRange = {
+			0x6: 0xB ,
+			0xFF: 0x4,
+		}
+
+		self.result = ttLib.newTable('gasp')
+
+	def test_gasp_merge_basic(self):
+		result = self.result.merge(self.merger, [self.table1, self.table2])
+		self.assertEqual(result, self.table1)
+
+		result = self.result.merge(self.merger, [self.table2, self.table1])
+		self.assertEqual(result, self.table2)
+
+	def test_gasp_merge_notImplemented(self):
+		result = self.result.merge(self.merger, [NotImplemented, self.table1])
+		self.assertEqual(result, NotImplemented)
+
+		result = self.result.merge(self.merger, [self.table1, NotImplemented])
+		self.assertEqual(result, self.table1)
+
+
+class CmapMergeUnitTest(unittest.TestCase):
+	def setUp(self):
+		self.merger = Merger()
+		self.table1 = ttLib.newTable('cmap')
+		self.table2 = ttLib.newTable('cmap')
+		self.mergedTable = ttLib.newTable('cmap')
+		pass
+
+	def tearDown(self):
+		pass
+
+
+	def makeSubtable(self, format, platformID, platEncID, cmap):
+		module = ttLib.getTableModule('cmap')
+		subtable = module.cmap_classes[format](format)
+		(subtable.platformID,
+			subtable.platEncID,
+			subtable.language,
+			subtable.cmap) = (platformID, platEncID, 0, cmap)
+		return subtable
+
+	# 4-3-1 table merged with 12-3-10 table with no dupes with codepoints outside BMP
+	def test_cmap_merge_no_dupes(self):
+		table1 = self.table1
+		table2 = self.table2
+		mergedTable = self.mergedTable
+
+		cmap1 = {0x2603: 'SNOWMAN'}
+		table1.tables = [self.makeSubtable(4,3,1, cmap1)]
+
+		cmap2 = {0x26C4: 'SNOWMAN WITHOUT SNOW'}
+		cmap2Extended = {0x1F93C: 'WRESTLERS'}
+		cmap2Extended.update(cmap2)
+		table2.tables = [self.makeSubtable(4,3,1, cmap2), self.makeSubtable(12,3,10, cmap2Extended)]
+
+		self.merger.alternateGlyphsPerFont = [{},{}]
+		mergedTable.merge(self.merger, [table1, table2])
+
+		expectedCmap = cmap2.copy()
+		expectedCmap.update(cmap1)
+		expectedCmapExtended = cmap2Extended.copy()
+		expectedCmapExtended.update(cmap1)
+		self.assertEqual(mergedTable.numSubTables, 2)
+		self.assertEqual([(table.format, table.platformID, table.platEncID, table.language) for table in mergedTable.tables],
+			[(4,3,1,0),(12,3,10,0)])
+		self.assertEqual(mergedTable.tables[0].cmap, expectedCmap)
+		self.assertEqual(mergedTable.tables[1].cmap, expectedCmapExtended)
+
+	# Tests Issue #322
+	def test_cmap_merge_three_dupes(self):
+		table1 = self.table1
+		table2 = self.table2
+		mergedTable = self.mergedTable
+
+		cmap1 = {0x20: 'space#0', 0xA0: 'space#0'}
+		table1.tables = [self.makeSubtable(4,3,1,cmap1)]
+		cmap2 = {0x20: 'space#1', 0xA0: 'uni00A0#1'}
+		table2.tables = [self.makeSubtable(4,3,1,cmap2)]
+
+		self.merger.duplicateGlyphsPerFont = [{},{}]
+		mergedTable.merge(self.merger, [table1, table2])
+
+		expectedCmap = cmap1.copy()
+		self.assertEqual(mergedTable.numSubTables, 1)
+		table = mergedTable.tables[0]
+		self.assertEqual((table.format, table.platformID, table.platEncID, table.language), (4,3,1,0))
+		self.assertEqual(table.cmap, expectedCmap)
+		self.assertEqual(self.merger.duplicateGlyphsPerFont, [{}, {'space#0': 'space#1'}])
+
+
+if __name__ == "__main__":
+	import sys
+	sys.exit(unittest.main())
diff --git a/Tests/misc/arrayTools_test.py b/Tests/misc/arrayTools_test.py
new file mode 100644
index 0000000..108b50d
--- /dev/null
+++ b/Tests/misc/arrayTools_test.py
@@ -0,0 +1,85 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.arrayTools import (
+    calcBounds, calcIntBounds, updateBounds, pointInRect, pointsInRect,
+    vectorLength, asInt16, normRect, scaleRect, offsetRect, insetRect,
+    sectRect, unionRect, rectCenter, intRect)
+import math
+
+
+def test_calcBounds():
+    assert calcBounds([]) == (0, 0, 0, 0)
+    assert calcBounds(
+        [(0, 40), (0, 100), (50, 50), (80, 10)]) == (0, 10, 80, 100)
+
+
+def test_calcIntBounds():
+    assert calcIntBounds(
+        [(0.1, 40.1), (0.1, 100.1), (49.9, 49.9), (79.5, 9.5)]
+    ) == (0, 10, 80, 100)
+
+
+def test_updateBounds():
+    assert updateBounds((0, 0, 0, 0), (100, 100)) == (0, 0, 100, 100)
+
+
+def test_pointInRect():
+    assert pointInRect((50, 50), (0, 0, 100, 100))
+    assert pointInRect((0, 0), (0, 0, 100, 100))
+    assert pointInRect((100, 100), (0, 0, 100, 100))
+    assert not pointInRect((101, 100), (0, 0, 100, 100))
+
+
+def test_pointsInRect():
+    assert pointsInRect([], (0, 0, 100, 100)) == []
+    assert pointsInRect(
+        [(50, 50), (0, 0), (100, 100), (101, 100)],
+        (0, 0, 100, 100)) == [True, True, True, False]
+
+
+def test_vectorLength():
+    assert vectorLength((1, 1)) == math.sqrt(2)
+
+
+def test_asInt16():
+    assert asInt16([0, 0.1, 0.5, 0.9]) == [0, 0, 1, 1]
+
+
+def test_normRect():
+    assert normRect((0, 10, 100, 200)) == (0, 10, 100, 200)
+    assert normRect((100, 200, 0, 10)) == (0, 10, 100, 200)
+
+
+def test_scaleRect():
+    assert scaleRect((10, 20, 50, 150), 1.5, 2) == (15.0, 40, 75.0, 300)
+
+
+def test_offsetRect():
+    assert offsetRect((10, 20, 30, 40), 5, 6) == (15, 26, 35, 46)
+
+
+def test_insetRect():
+    assert insetRect((10, 20, 50, 60), 5, 10) == (15, 30, 45, 50)
+    assert insetRect((10, 20, 50, 60), -5, -10) == (5, 10, 55, 70)
+
+
+def test_sectRect():
+    intersects, rect = sectRect((0, 10, 20, 30), (0, 40, 20, 50))
+    assert not intersects
+
+    intersects, rect = sectRect((0, 10, 20, 30), (5, 20, 35, 50))
+    assert intersects
+    assert rect == (5, 20, 20, 30)
+
+
+def test_unionRect():
+    assert unionRect((0, 10, 20, 30), (0, 40, 20, 50)) == (0, 10, 20, 50)
+
+
+def test_rectCenter():
+    assert rectCenter((0, 0, 100, 200)) == (50.0, 100.0)
+    assert rectCenter((0, 0, 100, 199.0)) == (50.0, 99.5)
+
+
+def test_intRect():
+    assert intRect((0.9, 2.9, 3.1, 4.1)) == (0, 2, 4, 5)
diff --git a/Tests/misc/bezierTools_test.py b/Tests/misc/bezierTools_test.py
new file mode 100644
index 0000000..1519543
--- /dev/null
+++ b/Tests/misc/bezierTools_test.py
@@ -0,0 +1,133 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.bezierTools import (
+    calcQuadraticBounds, calcCubicBounds, splitLine, splitQuadratic,
+    splitCubic, splitQuadraticAtT, splitCubicAtT, solveCubic)
+import pytest
+
+
+def test_calcQuadraticBounds():
+    assert calcQuadraticBounds(
+        (0, 0), (50, 100), (100, 0)) == (0, 0, 100, 50.0)
+    assert calcQuadraticBounds(
+        (0, 0), (100, 0), (100, 100)) == (0.0, 0.0, 100, 100)
+
+
+def test_calcCubicBounds():
+    assert calcCubicBounds(
+        (0, 0), (25, 100), (75, 100), (100, 0)) == ((0, 0, 100, 75.0))
+    assert calcCubicBounds(
+        (0, 0), (50, 0), (100, 50), (100, 100)) == (0.0, 0.0, 100, 100)
+    assert calcCubicBounds(
+        (50, 0), (0, 100), (100, 100), (50, 0)
+    ) == pytest.approx((35.566243, 0.000000, 64.433757, 75.000000))
+
+
+def test_splitLine():
+    assert splitLine(
+        (0, 0), (100, 100), where=50, isHorizontal=True
+    ) == [((0, 0), (50.0, 50.0)), ((50.0, 50.0), (100, 100))]
+    assert splitLine(
+        (0, 0), (100, 100), where=100, isHorizontal=True
+    ) == [((0, 0), (100, 100))]
+    assert splitLine(
+        (0, 0), (100, 100), where=0, isHorizontal=True
+    ) == [((0, 0), (0, 0)), ((0, 0), (100, 100))]
+    assert splitLine(
+        (0, 0), (100, 100), where=0, isHorizontal=False
+    ) == [((0, 0), (0, 0)), ((0, 0), (100, 100))]
+    assert splitLine(
+        (100, 0), (0, 0), where=50, isHorizontal=False
+    ) == [((100, 0), (50, 0)), ((50, 0), (0, 0))]
+    assert splitLine(
+        (0, 100), (0, 0), where=50, isHorizontal=True
+    ) == [((0, 100), (0, 50)), ((0, 50), (0, 0))]
+    assert splitLine(
+        (0, 100), (100, 100), where=50, isHorizontal=True
+    ) == [((0, 100), (100, 100))]
+
+
+def assert_curves_approx_equal(actual_curves, expected_curves):
+    assert len(actual_curves) == len(expected_curves)
+    for acurve, ecurve in zip(actual_curves, expected_curves):
+        assert len(acurve) == len(ecurve)
+        for apt, ept in zip(acurve, ecurve):
+            assert apt == pytest.approx(ept)
+
+
+def test_splitQuadratic():
+    assert splitQuadratic(
+        (0, 0), (50, 100), (100, 0), where=150, isHorizontal=False
+    ) == [((0, 0), (50, 100), (100, 0))]
+    assert splitQuadratic(
+        (0, 0), (50, 100), (100, 0), where=50, isHorizontal=False
+    ) == [((0, 0), (25, 50), (50, 50)),
+          ((50, 50), (75, 50), (100, 0))]
+    assert splitQuadratic(
+        (0, 0), (50, 100), (100, 0), where=25, isHorizontal=False
+    ) == [((0, 0), (12.5, 25), (25, 37.5)),
+          ((25, 37.5), (62.5, 75), (100, 0))]
+    assert_curves_approx_equal(
+        splitQuadratic(
+            (0, 0), (50, 100), (100, 0), where=25, isHorizontal=True),
+        [((0, 0), (7.32233, 14.64466), (14.64466, 25)),
+         ((14.64466, 25), (50, 75), (85.3553, 25)),
+         ((85.3553, 25), (92.6777, 14.64466), (100, -7.10543e-15))])
+    # XXX I'm not at all sure if the following behavior is desirable
+    assert splitQuadratic(
+        (0, 0), (50, 100), (100, 0), where=50, isHorizontal=True
+    ) == [((0, 0), (25, 50), (50, 50)),
+          ((50, 50), (50, 50), (50, 50)),
+          ((50, 50), (75, 50), (100, 0))]
+
+
+def test_splitCubic():
+    assert splitCubic(
+        (0, 0), (25, 100), (75, 100), (100, 0), where=150, isHorizontal=False
+    ) == [((0, 0), (25, 100), (75, 100), (100, 0))]
+    assert splitCubic(
+        (0, 0), (25, 100), (75, 100), (100, 0), where=50, isHorizontal=False
+    ) == [((0, 0), (12.5, 50), (31.25, 75), (50, 75)),
+          ((50, 75), (68.75, 75), (87.5, 50), (100, 0))]
+    assert_curves_approx_equal(
+        splitCubic(
+            (0, 0), (25, 100), (75, 100), (100, 0), where=25,
+            isHorizontal=True),
+        [((0, 0), (2.293792, 9.17517), (4.798045, 17.5085), (7.47414, 25)),
+         ((7.47414, 25), (31.2886, 91.6667), (68.7114, 91.6667),
+          (92.5259, 25)),
+         ((92.5259, 25), (95.202, 17.5085), (97.7062, 9.17517),
+          (100, 1.77636e-15))])
+
+
+def test_splitQuadraticAtT():
+    assert splitQuadraticAtT(
+        (0, 0), (50, 100), (100, 0), 0.5
+    ) == [((0, 0), (25, 50), (50, 50)),
+          ((50, 50), (75, 50), (100, 0))]
+    assert splitQuadraticAtT(
+        (0, 0), (50, 100), (100, 0), 0.5, 0.75
+    ) == [((0, 0), (25, 50), (50, 50)),
+          ((50, 50), (62.5, 50), (75, 37.5)),
+          ((75, 37.5), (87.5, 25), (100, 0))]
+
+
+def test_splitCubicAtT():
+    assert splitCubicAtT(
+        (0, 0), (25, 100), (75, 100), (100, 0), 0.5
+    ) == [((0, 0), (12.5, 50), (31.25, 75), (50, 75)),
+          ((50, 75), (68.75, 75), (87.5, 50), (100, 0))]
+    assert splitCubicAtT(
+        (0, 0), (25, 100), (75, 100), (100, 0), 0.5, 0.75
+    ) == [((0, 0), (12.5, 50), (31.25, 75), (50, 75)),
+          ((50, 75), (59.375, 75), (68.75, 68.75), (77.34375, 56.25)),
+          ((77.34375, 56.25), (85.9375, 43.75), (93.75, 25), (100, 0))]
+
+
+def test_solveCubic():
+    assert solveCubic(1, 1, -6, 0) == [-3.0, -0.0, 2.0]
+    assert solveCubic(-10.0, -9.0, 48.0, -29.0) == [-2.9, 1.0, 1.0]
+    assert solveCubic(-9.875, -9.0, 47.625, -28.75) == [-2.911392, 1.0, 1.0]
+    assert solveCubic(1.0, -4.5, 6.75, -3.375) == [1.5, 1.5, 1.5]
+    assert solveCubic(-12.0, 18.0, -9.0, 1.50023651123) == [0.5, 0.5, 0.5]
+    assert solveCubic(9.0, 0.0, 0.0, -7.62939453125e-05) == [-0.0, -0.0, -0.0]
diff --git a/Tests/misc/classifyTools_test.py b/Tests/misc/classifyTools_test.py
new file mode 100644
index 0000000..ac1f656
--- /dev/null
+++ b/Tests/misc/classifyTools_test.py
@@ -0,0 +1,30 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.classifyTools import classify
+
+
+def test_classify():
+    assert classify([]) == ([], {})
+    assert classify([[]]) == ([], {})
+    assert classify([[], []]) == ([], {})
+    assert classify([[1]]) == ([{1}], {1: {1}})
+    assert classify([[1,2]]) == ([{1, 2}], {1: {1, 2}, 2: {1, 2}})
+    assert classify([[1],[2]]) == ([{1}, {2}], {1: {1}, 2: {2}})
+    assert classify([[1,2],[2]]) == ([{1}, {2}], {1: {1}, 2: {2}})
+    assert classify([[1,2],[2,4]]) == (
+        [{1}, {2}, {4}], {1: {1}, 2: {2}, 4: {4}})
+    assert classify([[1,2],[2,4,5]]) == (
+        [{4, 5}, {1}, {2}], {1: {1}, 2: {2}, 4: {4, 5}, 5: {4, 5}})
+    assert classify([[1,2],[2,4,5]], sort=False) == (
+        [{1}, {4, 5}, {2}], {1: {1}, 2: {2}, 4: {4, 5}, 5: {4, 5}})
+    assert classify([[1,2,9],[2,4,5]], sort=False) == (
+        [{1, 9}, {4, 5}, {2}],
+        {1: {1, 9}, 2: {2}, 4: {4, 5}, 5: {4, 5}, 9: {1, 9}})
+    assert classify([[1,2,9,15],[2,4,5]], sort=False) == (
+        [{1, 9, 15}, {4, 5}, {2}],
+        {1: {1, 9, 15}, 2: {2}, 4: {4, 5}, 5: {4, 5}, 9: {1, 9, 15},
+         15: {1, 9, 15}})
+    classes, mapping = classify([[1,2,9,15],[2,4,5],[15,5]], sort=False)
+    assert set([frozenset(c) for c in classes]) == set(
+        [frozenset(s) for s in ({1, 9}, {4}, {2}, {5}, {15})])
+    assert mapping == {1: {1, 9}, 2: {2}, 4: {4}, 5: {5}, 9: {1, 9}, 15: {15}}
diff --git a/Tests/misc/eexec_test.py b/Tests/misc/eexec_test.py
new file mode 100644
index 0000000..2043095
--- /dev/null
+++ b/Tests/misc/eexec_test.py
@@ -0,0 +1,17 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.eexec import decrypt, encrypt
+
+
+def test_decrypt():
+    testStr = b"\0\0asdadads asds\265"
+    decryptedStr, R = decrypt(testStr, 12321)
+    assert decryptedStr == b'0d\nh\x15\xe8\xc4\xb2\x15\x1d\x108\x1a<6\xa1'
+    assert R == 36142
+
+
+def test_encrypt():
+    testStr = b'0d\nh\x15\xe8\xc4\xb2\x15\x1d\x108\x1a<6\xa1'
+    encryptedStr, R = encrypt(testStr, 12321)
+    assert encryptedStr == b"\0\0asdadads asds\265"
+    assert R == 36142
diff --git a/Tests/misc/encodingTools_test.py b/Tests/misc/encodingTools_test.py
new file mode 100644
index 0000000..4c9628b
--- /dev/null
+++ b/Tests/misc/encodingTools_test.py
@@ -0,0 +1,32 @@
+from __future__ import print_function, division, absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+import unittest
+from fontTools.misc.encodingTools import getEncoding
+
+class EncodingTest(unittest.TestCase):
+
+	def test_encoding_unicode(self):
+
+		self.assertEqual(getEncoding(3, 0, None), "utf_16_be") # MS Symbol is Unicode as well
+		self.assertEqual(getEncoding(3, 1, None), "utf_16_be")
+		self.assertEqual(getEncoding(3, 10, None), "utf_16_be")
+		self.assertEqual(getEncoding(0, 3, None), "utf_16_be")
+
+	def test_encoding_macroman_misc(self):
+		self.assertEqual(getEncoding(1, 0, 17), "mac_turkish")
+		self.assertEqual(getEncoding(1, 0, 37), "mac_romanian")
+		self.assertEqual(getEncoding(1, 0, 45), "mac_roman")
+
+	def test_extended_mac_encodings(self):
+		encoding = getEncoding(1, 1, 0) # Mac Japanese
+		decoded = b'\xfe'.decode(encoding)
+		self.assertEqual(decoded, unichr(0x2122))
+
+	def test_extended_unknown(self):
+		self.assertEqual(getEncoding(10, 11, 12), None)
+		self.assertEqual(getEncoding(10, 11, 12, "ascii"), "ascii")
+		self.assertEqual(getEncoding(10, 11, 12, default="ascii"), "ascii")
+
+if __name__ == "__main__":
+	import sys
+	sys.exit(unittest.main())
diff --git a/Tests/misc/filenames_test.py b/Tests/misc/filenames_test.py
new file mode 100644
index 0000000..47d137f
--- /dev/null
+++ b/Tests/misc/filenames_test.py
@@ -0,0 +1,137 @@
+from __future__ import unicode_literals
+import unittest
+from fontTools.misc.filenames import (
+	userNameToFileName, handleClash1, handleClash2)
+
+
+class UserNameToFilenameTest(unittest.TestCase):
+
+	def test_names(self):
+		self.assertEqual(userNameToFileName("a"),"a")
+		self.assertEqual(userNameToFileName("A"), "A_")
+		self.assertEqual(userNameToFileName("AE"), "A_E_")
+		self.assertEqual(userNameToFileName("Ae"), "A_e")
+		self.assertEqual(userNameToFileName("ae"), "ae")
+		self.assertEqual(userNameToFileName("aE"), "aE_")
+		self.assertEqual(userNameToFileName("a.alt"), "a.alt")
+		self.assertEqual(userNameToFileName("A.alt"), "A_.alt")
+		self.assertEqual(userNameToFileName("A.Alt"), "A_.A_lt")
+		self.assertEqual(userNameToFileName("A.aLt"), "A_.aL_t")
+		self.assertEqual(userNameToFileName(u"A.alT"), "A_.alT_")
+		self.assertEqual(userNameToFileName("T_H"), "T__H_")
+		self.assertEqual(userNameToFileName("T_h"), "T__h")
+		self.assertEqual(userNameToFileName("t_h"), "t_h")
+		self.assertEqual(userNameToFileName("F_F_I"), "F__F__I_")
+		self.assertEqual(userNameToFileName("f_f_i"), "f_f_i")
+		self.assertEqual(
+			userNameToFileName("Aacute_V.swash"),
+			"A_acute_V_.swash")
+		self.assertEqual(userNameToFileName(".notdef"), "_notdef")
+		self.assertEqual(userNameToFileName("con"), "_con")
+		self.assertEqual(userNameToFileName("CON"), "C_O_N_")
+		self.assertEqual(userNameToFileName("con.alt"), "_con.alt")
+		self.assertEqual(userNameToFileName("alt.con"), "alt._con")
+
+	def test_prefix_suffix(self):
+		prefix = "TEST_PREFIX"
+		suffix = "TEST_SUFFIX"
+		name = "NAME"
+		name_file = "N_A_M_E_"
+		self.assertEqual(
+			userNameToFileName(name, prefix=prefix, suffix=suffix),
+			prefix + name_file + suffix)
+
+	def test_collide(self):
+		prefix = "TEST_PREFIX"
+		suffix = "TEST_SUFFIX"
+		name = "NAME"
+		name_file = "N_A_M_E_"
+		collision_avoidance1 = "000000000000001"
+		collision_avoidance2 = "000000000000002"
+		exist = set()
+		generated = userNameToFileName(
+			name, exist, prefix=prefix, suffix=suffix)
+		exist.add(generated.lower())
+		self.assertEqual(generated, prefix + name_file + suffix)
+		generated = userNameToFileName(
+			name, exist, prefix=prefix, suffix=suffix)
+		exist.add(generated.lower())
+		self.assertEqual(
+			generated,
+			prefix + name_file + collision_avoidance1 + suffix)
+		generated = userNameToFileName(
+			name, exist, prefix=prefix, suffix=suffix)
+		self.assertEqual(
+			generated,
+			prefix + name_file + collision_avoidance2+ suffix)
+
+	def test_ValueError(self):
+		with self.assertRaises(ValueError):
+			userNameToFileName(b"a")
+		with self.assertRaises(ValueError):
+			userNameToFileName({"a"})
+		with self.assertRaises(ValueError):
+			userNameToFileName(("a",))
+		with self.assertRaises(ValueError):
+			userNameToFileName(["a"])
+		with self.assertRaises(ValueError):
+			userNameToFileName(["a"])
+		with self.assertRaises(ValueError):
+			userNameToFileName(b"\xd8\x00")
+
+	def test_handleClash1(self):
+		prefix = ("0" * 5) + "."
+		suffix = "." + ("0" * 10)
+		existing = ["a" * 5]
+
+		e = list(existing)
+		self.assertEqual(
+			handleClash1(userName="A" * 5, existing=e, prefix=prefix,
+						 suffix=suffix),
+			'00000.AAAAA000000000000001.0000000000'
+		)
+
+		e = list(existing)
+		e.append(prefix + "aaaaa" + "1".zfill(15) + suffix)
+		self.assertEqual(
+		handleClash1(userName="A" * 5, existing=e, prefix=prefix,
+					 suffix=suffix),
+		'00000.AAAAA000000000000002.0000000000'
+		)
+
+		e = list(existing)
+		e.append(prefix + "AAAAA" + "2".zfill(15) + suffix)
+		self.assertEqual(
+			handleClash1(userName="A" * 5, existing=e, prefix=prefix,
+						 suffix=suffix),
+			'00000.AAAAA000000000000001.0000000000'
+		)
+
+	def test_handleClash2(self):
+		prefix = ("0" * 5) + "."
+		suffix = "." + ("0" * 10)
+		existing = [prefix + str(i) + suffix for i in range(100)]
+
+		e = list(existing)
+		self.assertEqual(
+			handleClash2(existing=e, prefix=prefix, suffix=suffix),
+			'00000.100.0000000000'
+		)
+
+		e = list(existing)
+		e.remove(prefix + "1" + suffix)
+		self.assertEqual(
+			handleClash2(existing=e, prefix=prefix, suffix=suffix),
+			'00000.1.0000000000'
+		)
+
+		e = list(existing)
+		e.remove(prefix + "2" + suffix)
+		self.assertEqual(
+			handleClash2(existing=e, prefix=prefix, suffix=suffix),
+			'00000.2.0000000000'
+		)
+
+if __name__ == "__main__":
+	import sys
+	sys.exit(unittest.main())
diff --git a/Tests/misc/fixedTools_test.py b/Tests/misc/fixedTools_test.py
new file mode 100644
index 0000000..5ef1777
--- /dev/null
+++ b/Tests/misc/fixedTools_test.py
@@ -0,0 +1,43 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.fixedTools import fixedToFloat, floatToFixed
+import unittest
+
+
+class FixedToolsTest(unittest.TestCase):
+
+    def test_roundtrip(self):
+        for bits in range(0, 15):
+            for value in range(-(2**(bits+1)), 2**(bits+1)):
+                self.assertEqual(value, floatToFixed(fixedToFloat(value, bits), bits))
+
+    def test_fixedToFloat_precision14(self):
+        self.assertEqual(0.8, fixedToFloat(13107, 14))
+        self.assertEqual(0.0, fixedToFloat(0, 14))
+        self.assertEqual(1.0, fixedToFloat(16384, 14))
+        self.assertEqual(-1.0, fixedToFloat(-16384, 14))
+        self.assertEqual(0.99994, fixedToFloat(16383, 14))
+        self.assertEqual(-0.99994, fixedToFloat(-16383, 14))
+
+    def test_fixedToFloat_precision6(self):
+        self.assertAlmostEqual(-9.98, fixedToFloat(-639, 6))
+        self.assertAlmostEqual(-10.0, fixedToFloat(-640, 6))
+        self.assertAlmostEqual(9.98, fixedToFloat(639, 6))
+        self.assertAlmostEqual(10.0, fixedToFloat(640, 6))
+
+    def test_floatToFixed_precision14(self):
+        self.assertEqual(13107, floatToFixed(0.8, 14))
+        self.assertEqual(16384, floatToFixed(1.0, 14))
+        self.assertEqual(16384, floatToFixed(1, 14))
+        self.assertEqual(-16384, floatToFixed(-1.0, 14))
+        self.assertEqual(-16384, floatToFixed(-1, 14))
+        self.assertEqual(0, floatToFixed(0, 14))
+
+    def test_fixedToFloat_return_float(self):
+        value = fixedToFloat(16384, 14)
+        self.assertIsInstance(value, float)
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/misc/loggingTools_test.py b/Tests/misc/loggingTools_test.py
new file mode 100644
index 0000000..694c109
--- /dev/null
+++ b/Tests/misc/loggingTools_test.py
@@ -0,0 +1,210 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.loggingTools import (
+    LevelFormatter,
+    Timer,
+    configLogger,
+    ChannelsFilter,
+    LogMixin,
+    StderrHandler,
+    LastResortLogger,
+    _resetExistingLoggers,
+)
+import logging
+import textwrap
+import time
+import re
+import sys
+import pytest
+
+
+def logger_name_generator():
+    basename = "fontTools.test#"
+    num = 1
+    while True:
+        yield basename+str(num)
+        num += 1
+
+unique_logger_name = logger_name_generator()
+
+
+@pytest.fixture
+def logger():
+    log = logging.getLogger(next(unique_logger_name))
+    configLogger(logger=log, level="DEBUG", stream=StringIO())
+    return log
+
+
+def test_LevelFormatter():
+    stream = StringIO()
+    handler = logging.StreamHandler(stream)
+    formatter = LevelFormatter(
+        fmt={
+            '*':     '[%(levelname)s] %(message)s',
+            'DEBUG': '%(name)s [%(levelname)s] %(message)s',
+            'INFO':  '%(message)s',
+        })
+    handler.setFormatter(formatter)
+    name = next(unique_logger_name)
+    log = logging.getLogger(name)
+    log.setLevel(logging.DEBUG)
+    log.addHandler(handler)
+
+    log.debug("this uses a custom format string")
+    log.info("this also uses a custom format string")
+    log.warning("this one uses the default format string")
+
+    assert stream.getvalue() == textwrap.dedent("""\
+        %s [DEBUG] this uses a custom format string
+        this also uses a custom format string
+        [WARNING] this one uses the default format string
+        """ % name)
+
+
+class TimerTest(object):
+
+    def test_split(self):
+        timer = Timer()
+        time.sleep(0.01)
+        fist_lap =  timer.split()
+        assert timer.elapsed == fist_lap
+        time.sleep(0.1)
+        second_lap = timer.split()
+        assert second_lap > fist_lap
+        assert timer.elapsed == second_lap
+
+    def test_time(self):
+        timer = Timer()
+        time.sleep(0.01)
+        overall_time = timer.time()
+        assert overall_time > 0
+
+    def test_context_manager(self):
+        with Timer() as t:
+            time.sleep(0.01)
+        assert t.elapsed > 0
+
+    def test_using_logger(self, logger):
+        with Timer(logger, 'do something'):
+            time.sleep(0.01)
+
+        assert re.match(
+            "Took [0-9]\.[0-9]{3}s to do something",
+            logger.handlers[0].stream.getvalue())
+
+    def test_using_logger_calling_instance(self, logger):
+        timer = Timer(logger)
+        with timer():
+            time.sleep(0.01)
+
+        assert re.match(
+            "elapsed time: [0-9]\.[0-9]{3}s",
+            logger.handlers[0].stream.getvalue())
+
+        # do it again but with custom level
+        with timer('redo it', level=logging.WARNING):
+            time.sleep(0.02)
+
+        assert re.search(
+            "WARNING: Took [0-9]\.[0-9]{3}s to redo it",
+            logger.handlers[0].stream.getvalue())
+
+    def test_function_decorator(self, logger):
+        timer = Timer(logger)
+
+        @timer()
+        def test1():
+            time.sleep(0.01)
+        @timer('run test 2', level=logging.INFO)
+        def test2():
+            time.sleep(0.02)
+
+        test1()
+
+        assert re.match(
+            "Took [0-9]\.[0-9]{3}s to run 'test1'",
+            logger.handlers[0].stream.getvalue())
+
+        test2()
+
+        assert re.search(
+            "Took [0-9]\.[0-9]{3}s to run test 2",
+            logger.handlers[0].stream.getvalue())
+
+
+def test_ChannelsFilter(logger):
+    n = logger.name
+    filtr = ChannelsFilter(n+".A.B", n+".C.D")
+    handler = logger.handlers[0]
+    handler.addFilter(filtr)
+    stream = handler.stream
+
+    logging.getLogger(n+".A.B").debug('this record passes through')
+    assert 'this record passes through' in stream.getvalue()
+
+    logging.getLogger(n+'.A.B.C').debug('records from children also pass')
+    assert 'records from children also pass' in stream.getvalue()
+
+    logging.getLogger(n+'.C.D').debug('this one as well')
+    assert 'this one as well' in stream.getvalue()
+
+    logging.getLogger(n+'.A.B.').debug('also this one')
+    assert 'also this one' in stream.getvalue()
+
+    before = stream.getvalue()
+    logging.getLogger(n+'.A.F').debug('but this one does not!')
+    assert before == stream.getvalue()
+
+    logging.getLogger(n+'.C.DE').debug('neither this one!')
+    assert before == stream.getvalue()
+
+
+def test_LogMixin():
+
+    class Base(object):
+        pass
+
+    class A(LogMixin, Base):
+        pass
+
+    class B(A):
+        pass
+
+    a = A()
+    b = B()
+
+    assert hasattr(a, 'log')
+    assert hasattr(b, 'log')
+    assert isinstance(a.log, logging.Logger)
+    assert isinstance(b.log, logging.Logger)
+    assert a.log.name == "loggingTools_test.A"
+    assert b.log.name == "loggingTools_test.B"
+
+
+@pytest.mark.skipif(sys.version_info[:2] > (2, 7), reason="only for python2.7")
+@pytest.mark.parametrize(
+    "reset", [True, False], ids=["reset", "no-reset"]
+)
+def test_LastResortLogger(reset, capsys, caplog):
+    current = logging.getLoggerClass()
+    msg = "The quick brown fox jumps over the lazy dog"
+    try:
+        if reset:
+            _resetExistingLoggers()
+        else:
+            caplog.set_level(logging.ERROR, logger="myCustomLogger")
+        logging.lastResort = StderrHandler(logging.WARNING)
+        logging.setLoggerClass(LastResortLogger)
+        logger = logging.getLogger("myCustomLogger")
+        logger.error(msg)
+    finally:
+        del logging.lastResort
+        logging.setLoggerClass(current)
+
+    captured = capsys.readouterr()
+    if reset:
+        assert msg in captured.err
+        msg not in caplog.text
+    else:
+        msg in caplog.text
+        msg not in captured.err
diff --git a/Tests/misc/macRes_test.py b/Tests/misc/macRes_test.py
new file mode 100644
index 0000000..fde13f8
--- /dev/null
+++ b/Tests/misc/macRes_test.py
@@ -0,0 +1,97 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+import sys
+import os
+import tempfile
+import unittest
+from fontTools.misc.textTools import deHexStr
+from fontTools.misc.macRes import ResourceReader
+
+
+# test resource data in DeRez notation
+"""
+data 'TEST' (128, "name1") { $"4865 6C6C 6F" };                   /* Hello */
+data 'TEST' (129, "name2") { $"576F 726C 64" };                   /* World */
+data 'test' (130, "name3") { $"486F 7720 6172 6520 796F 753F" };  /* How are you? */
+"""
+# the same data, compiled using Rez
+# $ /usr/bin/Rez testdata.rez -o compiled
+# $ hexdump -v compiled/..namedfork/rsrc
+TEST_RSRC_FORK = deHexStr(
+	"00 00 01 00 00 00 01 22 00 00 00 22 00 00 00 64 "  # 0x00000000
+	"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "  # 0x00000010
+	"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "  # 0x00000020
+	"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "  # 0x00000030
+	"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "  # 0x00000040
+	"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "  # 0x00000050
+	"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "  # 0x00000060
+	"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "  # 0x00000070
+	"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "  # 0x00000080
+	"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "  # 0x00000090
+	"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "  # 0x000000A0
+	"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "  # 0x000000B0
+	"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "  # 0x000000C0
+	"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "  # 0x000000D0
+	"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "  # 0x000000E0
+	"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "  # 0x000000F0
+	"00 00 00 05 48 65 6c 6c 6f 00 00 00 05 57 6f 72 "  # 0x00000100
+	"6c 64 00 00 00 0c 48 6f 77 20 61 72 65 20 79 6f "  # 0x00000110
+	"75 3f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "  # 0x00000120
+	"00 00 00 00 00 00 00 00 00 00 00 1c 00 52 00 01 "  # 0x00000130
+	"54 45 53 54 00 01 00 12 74 65 73 74 00 00 00 2a "  # 0x00000140
+	"00 80 00 00 00 00 00 00 00 00 00 00 00 81 00 06 "  # 0x00000150
+	"00 00 00 09 00 00 00 00 00 82 00 0c 00 00 00 12 "  # 0x00000160
+	"00 00 00 00 05 6e 61 6d 65 31 05 6e 61 6d 65 32 "  # 0x00000170
+	"05 6e 61 6d 65 33                               "  # 0x00000180
+)
+
+
+class ResourceReaderTest(unittest.TestCase):
+
+	def test_read_file(self):
+		infile = BytesIO(TEST_RSRC_FORK)
+		reader = ResourceReader(infile)
+		resources = [res for typ in reader.keys() for res in reader[typ]]
+		self.assertExpected(resources)
+
+	def test_read_datafork(self):
+		with tempfile.NamedTemporaryFile(delete=False) as tmp:
+			tmp.write(TEST_RSRC_FORK)
+		try:
+			reader = ResourceReader(tmp.name)
+			resources = [res for typ in reader.keys() for res in reader[typ]]
+			reader.close()
+			self.assertExpected(resources)
+		finally:
+			os.remove(tmp.name)
+
+	def test_read_namedfork_rsrc(self):
+		if sys.platform != 'darwin':
+			self.skipTest('Not supported on "%s"' % sys.platform)
+		tmp = tempfile.NamedTemporaryFile(delete=False)
+		tmp.close()
+		try:
+			with open(tmp.name + '/..namedfork/rsrc', 'wb') as fork:
+				fork.write(TEST_RSRC_FORK)
+			reader = ResourceReader(tmp.name)
+			resources = [res for typ in reader.keys() for res in reader[typ]]
+			reader.close()
+			self.assertExpected(resources)
+		finally:
+			os.remove(tmp.name)
+
+	def assertExpected(self, resources):
+		self.assertRezEqual(resources[0], 'TEST', b'Hello', 128, 'name1')
+		self.assertRezEqual(resources[1], 'TEST', b'World', 129, 'name2')
+		self.assertRezEqual(
+			resources[2], 'test', b'How are you?', 130, 'name3')
+
+	def assertRezEqual(self, res, type_, data, id, name):
+		self.assertEqual(res.type, type_)
+		self.assertEqual(res.data, data)
+		self.assertEqual(res.id, id)
+		self.assertEqual(res.name, name)
+
+
+if __name__ == '__main__':
+	sys.exit(unittest.main())
diff --git a/Tests/misc/psCharStrings_test.py b/Tests/misc/psCharStrings_test.py
new file mode 100644
index 0000000..1e36246
--- /dev/null
+++ b/Tests/misc/psCharStrings_test.py
@@ -0,0 +1,32 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.cffLib import PrivateDict
+from fontTools.cffLib.specializer import stringToProgram
+from fontTools.misc.psCharStrings import T2CharString
+import unittest
+
+
+class T2CharStringTest(unittest.TestCase):
+
+    @classmethod
+    def stringToT2CharString(cls, string):
+        return T2CharString(program=stringToProgram(string), private=PrivateDict())
+
+    def test_calcBounds_empty(self):
+        cs = self.stringToT2CharString("endchar")
+        bounds = cs.calcBounds(None)
+        self.assertEqual(bounds, None)
+
+    def test_calcBounds_line(self):
+        cs = self.stringToT2CharString("100 100 rmoveto 40 10 rlineto -20 50 rlineto endchar")
+        bounds = cs.calcBounds(None)
+        self.assertEqual(bounds, (100, 100, 140, 160))
+
+    def test_calcBounds_curve(self):
+        cs = self.stringToT2CharString("100 100 rmoveto -50 -150 200 0 -50 150 rrcurveto endchar")
+        bounds = cs.calcBounds(None)
+        self.assertEqual(bounds, (91.90524980688875, -12.5, 208.09475019311125, 100))
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/misc/py23_test.py b/Tests/misc/py23_test.py
new file mode 100644
index 0000000..22b8044
--- /dev/null
+++ b/Tests/misc/py23_test.py
@@ -0,0 +1,485 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.textTools import deHexStr
+import filecmp
+import tempfile
+from subprocess import check_call
+import sys
+import os
+import unittest
+
+from fontTools.misc.py23 import (
+	round2, round3, isclose, redirect_stdout, redirect_stderr)
+
+
+PIPE_SCRIPT = """\
+import sys
+binary_stdin = open(sys.stdin.fileno(), mode='rb', closefd=False)
+binary_stdout = open(sys.stdout.fileno(), mode='wb', closefd=False)
+binary_stdout.write(binary_stdin.read())
+"""
+
+# the string contains a mix of line endings, plus the Win "EOF" charater (0x1A)
+# 'hello\rworld\r\n\x1a\r\n'
+TEST_BIN_DATA = deHexStr(
+	"68 65 6c 6c 6f 0d 77 6f 72 6c 64 0d 0a 1a 0d 0a"
+)
+
+class OpenFuncWrapperTest(unittest.TestCase):
+
+	@staticmethod
+	def make_temp(data):
+		with tempfile.NamedTemporaryFile(delete=False) as f:
+			f.write(tobytes(data))
+		return f.name
+
+	def diff_piped(self, data, import_statement):
+		script = self.make_temp("\n".join([import_statement, PIPE_SCRIPT]))
+		datafile = self.make_temp(data)
+		try:
+			with open(datafile, 'rb') as infile, \
+					tempfile.NamedTemporaryFile(delete=False) as outfile:
+				env = dict(os.environ)
+				env["PYTHONPATH"] = os.pathsep.join(sys.path)
+				check_call(
+					[sys.executable, script], stdin=infile, stdout=outfile,
+					env=env)
+			result = not filecmp.cmp(infile.name, outfile.name, shallow=False)
+		finally:
+			os.remove(script)
+			os.remove(datafile)
+			os.remove(outfile.name)
+		return result
+
+	def test_binary_pipe_py23_open_wrapper(self):
+		if self.diff_piped(
+				TEST_BIN_DATA, "from fontTools.misc.py23 import open"):
+			self.fail("Input and output data differ!")
+
+	def test_binary_pipe_built_in_io_open(self):
+		if sys.version_info.major < 3 and sys.platform == 'win32':
+			# On Windows Python 2.x, the piped input and output data are
+			# expected to be different when using io.open, because of issue
+			# https://bugs.python.org/issue10841.
+			expected = True
+		else:
+			expected = False
+		result = self.diff_piped(TEST_BIN_DATA, "from io import open")
+		self.assertEqual(result, expected)
+
+
+class Round2Test(unittest.TestCase):
+	"""
+	Test cases taken from cpython 2.7 test suite:
+
+	https://github.com/python/cpython/blob/2.7/Lib/test/test_float.py#L748
+
+	Excludes the test cases that are not supported when using the `decimal`
+	module's `quantize` method.
+	"""
+
+	def test_second_argument_type(self):
+		# floats should be illegal
+		self.assertRaises(TypeError, round2, 3.14159, 2.0)
+
+	def test_halfway_cases(self):
+		# Halfway cases need special attention, since the current
+		# implementation has to deal with them specially.  Note that
+		# 2.x rounds halfway values up (i.e., away from zero) while
+		# 3.x does round-half-to-even.
+		self.assertAlmostEqual(round2(0.125, 2), 0.13)
+		self.assertAlmostEqual(round2(0.375, 2), 0.38)
+		self.assertAlmostEqual(round2(0.625, 2), 0.63)
+		self.assertAlmostEqual(round2(0.875, 2), 0.88)
+		self.assertAlmostEqual(round2(-0.125, 2), -0.13)
+		self.assertAlmostEqual(round2(-0.375, 2), -0.38)
+		self.assertAlmostEqual(round2(-0.625, 2), -0.63)
+		self.assertAlmostEqual(round2(-0.875, 2), -0.88)
+
+		self.assertAlmostEqual(round2(0.25, 1), 0.3)
+		self.assertAlmostEqual(round2(0.75, 1), 0.8)
+		self.assertAlmostEqual(round2(-0.25, 1), -0.3)
+		self.assertAlmostEqual(round2(-0.75, 1), -0.8)
+
+		self.assertEqual(round2(-6.5, 0), -7.0)
+		self.assertEqual(round2(-5.5, 0), -6.0)
+		self.assertEqual(round2(-1.5, 0), -2.0)
+		self.assertEqual(round2(-0.5, 0), -1.0)
+		self.assertEqual(round2(0.5, 0), 1.0)
+		self.assertEqual(round2(1.5, 0), 2.0)
+		self.assertEqual(round2(2.5, 0), 3.0)
+		self.assertEqual(round2(3.5, 0), 4.0)
+		self.assertEqual(round2(4.5, 0), 5.0)
+		self.assertEqual(round2(5.5, 0), 6.0)
+		self.assertEqual(round2(6.5, 0), 7.0)
+
+		# same but without an explicit second argument; in 3.x these
+		# will give integers
+		self.assertEqual(round2(-6.5), -7.0)
+		self.assertEqual(round2(-5.5), -6.0)
+		self.assertEqual(round2(-1.5), -2.0)
+		self.assertEqual(round2(-0.5), -1.0)
+		self.assertEqual(round2(0.5), 1.0)
+		self.assertEqual(round2(1.5), 2.0)
+		self.assertEqual(round2(2.5), 3.0)
+		self.assertEqual(round2(3.5), 4.0)
+		self.assertEqual(round2(4.5), 5.0)
+		self.assertEqual(round2(5.5), 6.0)
+		self.assertEqual(round2(6.5), 7.0)
+
+		self.assertEqual(round2(-25.0, -1), -30.0)
+		self.assertEqual(round2(-15.0, -1), -20.0)
+		self.assertEqual(round2(-5.0, -1), -10.0)
+		self.assertEqual(round2(5.0, -1), 10.0)
+		self.assertEqual(round2(15.0, -1), 20.0)
+		self.assertEqual(round2(25.0, -1), 30.0)
+		self.assertEqual(round2(35.0, -1), 40.0)
+		self.assertEqual(round2(45.0, -1), 50.0)
+		self.assertEqual(round2(55.0, -1), 60.0)
+		self.assertEqual(round2(65.0, -1), 70.0)
+		self.assertEqual(round2(75.0, -1), 80.0)
+		self.assertEqual(round2(85.0, -1), 90.0)
+		self.assertEqual(round2(95.0, -1), 100.0)
+		self.assertEqual(round2(12325.0, -1), 12330.0)
+		self.assertEqual(round2(0, -1), 0.0)
+
+		self.assertEqual(round2(350.0, -2), 400.0)
+		self.assertEqual(round2(450.0, -2), 500.0)
+
+		self.assertAlmostEqual(round2(0.5e21, -21), 1e21)
+		self.assertAlmostEqual(round2(1.5e21, -21), 2e21)
+		self.assertAlmostEqual(round2(2.5e21, -21), 3e21)
+		self.assertAlmostEqual(round2(5.5e21, -21), 6e21)
+		self.assertAlmostEqual(round2(8.5e21, -21), 9e21)
+
+		self.assertAlmostEqual(round2(-1.5e22, -22), -2e22)
+		self.assertAlmostEqual(round2(-0.5e22, -22), -1e22)
+		self.assertAlmostEqual(round2(0.5e22, -22), 1e22)
+		self.assertAlmostEqual(round2(1.5e22, -22), 2e22)
+
+
+class Round3Test(unittest.TestCase):
+	""" Same as above but results adapted for Python 3 round() """
+
+	def test_second_argument_type(self):
+		# floats should be illegal
+		self.assertRaises(TypeError, round3, 3.14159, 2.0)
+
+		# None should be allowed
+		self.assertEqual(round3(1.0, None), 1)
+		# the following would raise an error with the built-in Python3.5 round:
+		# TypeError: 'NoneType' object cannot be interpreted as an integer
+		self.assertEqual(round3(1, None), 1)
+
+	def test_halfway_cases(self):
+		self.assertAlmostEqual(round3(0.125, 2), 0.12)
+		self.assertAlmostEqual(round3(0.375, 2), 0.38)
+		self.assertAlmostEqual(round3(0.625, 2), 0.62)
+		self.assertAlmostEqual(round3(0.875, 2), 0.88)
+		self.assertAlmostEqual(round3(-0.125, 2), -0.12)
+		self.assertAlmostEqual(round3(-0.375, 2), -0.38)
+		self.assertAlmostEqual(round3(-0.625, 2), -0.62)
+		self.assertAlmostEqual(round3(-0.875, 2), -0.88)
+
+		self.assertAlmostEqual(round3(0.25, 1), 0.2)
+		self.assertAlmostEqual(round3(0.75, 1), 0.8)
+		self.assertAlmostEqual(round3(-0.25, 1), -0.2)
+		self.assertAlmostEqual(round3(-0.75, 1), -0.8)
+
+		self.assertEqual(round3(-6.5, 0), -6.0)
+		self.assertEqual(round3(-5.5, 0), -6.0)
+		self.assertEqual(round3(-1.5, 0), -2.0)
+		self.assertEqual(round3(-0.5, 0), 0.0)
+		self.assertEqual(round3(0.5, 0), 0.0)
+		self.assertEqual(round3(1.5, 0), 2.0)
+		self.assertEqual(round3(2.5, 0), 2.0)
+		self.assertEqual(round3(3.5, 0), 4.0)
+		self.assertEqual(round3(4.5, 0), 4.0)
+		self.assertEqual(round3(5.5, 0), 6.0)
+		self.assertEqual(round3(6.5, 0), 6.0)
+
+		# same but without an explicit second argument; in 2.x these
+		# will give floats
+		self.assertEqual(round3(-6.5), -6)
+		self.assertEqual(round3(-5.5), -6)
+		self.assertEqual(round3(-1.5), -2.0)
+		self.assertEqual(round3(-0.5), 0)
+		self.assertEqual(round3(0.5), 0)
+		self.assertEqual(round3(1.5), 2)
+		self.assertEqual(round3(2.5), 2)
+		self.assertEqual(round3(3.5), 4)
+		self.assertEqual(round3(4.5), 4)
+		self.assertEqual(round3(5.5), 6)
+		self.assertEqual(round3(6.5), 6)
+
+		# no ndigits and input is already an integer: output == input
+		rv = round3(1)
+		self.assertEqual(rv, 1)
+		self.assertTrue(isinstance(rv, int))
+		rv = round3(1.0)
+		self.assertEqual(rv, 1)
+		self.assertTrue(isinstance(rv, int))
+
+		self.assertEqual(round3(-25.0, -1), -20.0)
+		self.assertEqual(round3(-15.0, -1), -20.0)
+		self.assertEqual(round3(-5.0, -1), 0.0)
+		self.assertEqual(round3(5.0, -1), 0.0)
+		self.assertEqual(round3(15.0, -1), 20.0)
+		self.assertEqual(round3(25.0, -1), 20.0)
+		self.assertEqual(round3(35.0, -1), 40.0)
+		self.assertEqual(round3(45.0, -1), 40.0)
+		self.assertEqual(round3(55.0, -1), 60.0)
+		self.assertEqual(round3(65.0, -1), 60.0)
+		self.assertEqual(round3(75.0, -1), 80.0)
+		self.assertEqual(round3(85.0, -1), 80.0)
+		self.assertEqual(round3(95.0, -1), 100.0)
+		self.assertEqual(round3(12325.0, -1), 12320.0)
+		self.assertEqual(round3(0, -1), 0.0)
+
+		self.assertEqual(round3(350.0, -2), 400.0)
+		self.assertEqual(round3(450.0, -2), 400.0)
+
+		self.assertAlmostEqual(round3(0.5e21, -21), 0.0)
+		self.assertAlmostEqual(round3(1.5e21, -21), 2e21)
+		self.assertAlmostEqual(round3(2.5e21, -21), 2e21)
+		self.assertAlmostEqual(round3(5.5e21, -21), 6e21)
+		self.assertAlmostEqual(round3(8.5e21, -21), 8e21)
+
+		self.assertAlmostEqual(round3(-1.5e22, -22), -2e22)
+		self.assertAlmostEqual(round3(-0.5e22, -22), 0.0)
+		self.assertAlmostEqual(round3(0.5e22, -22), 0.0)
+		self.assertAlmostEqual(round3(1.5e22, -22), 2e22)
+
+
+NAN = float('nan')
+INF = float('inf')
+NINF = float('-inf')
+
+
+class IsCloseTests(unittest.TestCase):
+	"""
+	Tests taken from Python 3.5 test_math.py:
+	https://hg.python.org/cpython/file/v3.5.2/Lib/test/test_math.py
+	"""
+	isclose = staticmethod(isclose)
+
+	def assertIsClose(self, a, b, *args, **kwargs):
+		self.assertTrue(
+			self.isclose(a, b, *args, **kwargs),
+			msg="%s and %s should be close!" % (a, b))
+
+	def assertIsNotClose(self, a, b, *args, **kwargs):
+		self.assertFalse(
+			self.isclose(a, b, *args, **kwargs),
+			msg="%s and %s should not be close!" % (a, b))
+
+	def assertAllClose(self, examples, *args, **kwargs):
+		for a, b in examples:
+			self.assertIsClose(a, b, *args, **kwargs)
+
+	def assertAllNotClose(self, examples, *args, **kwargs):
+		for a, b in examples:
+			self.assertIsNotClose(a, b, *args, **kwargs)
+
+	def test_negative_tolerances(self):
+		# ValueError should be raised if either tolerance is less than zero
+		with self.assertRaises(ValueError):
+			self.assertIsClose(1, 1, rel_tol=-1e-100)
+		with self.assertRaises(ValueError):
+			self.assertIsClose(1, 1, rel_tol=1e-100, abs_tol=-1e10)
+
+	def test_identical(self):
+		# identical values must test as close
+		identical_examples = [
+			(2.0, 2.0),
+			(0.1e200, 0.1e200),
+			(1.123e-300, 1.123e-300),
+			(12345, 12345.0),
+			(0.0, -0.0),
+			(345678, 345678)]
+		self.assertAllClose(identical_examples, rel_tol=0.0, abs_tol=0.0)
+
+	def test_eight_decimal_places(self):
+		# examples that are close to 1e-8, but not 1e-9
+		eight_decimal_places_examples = [
+			(1e8, 1e8 + 1),
+			(-1e-8, -1.000000009e-8),
+			(1.12345678, 1.12345679)]
+		self.assertAllClose(eight_decimal_places_examples, rel_tol=1e-8)
+		self.assertAllNotClose(eight_decimal_places_examples, rel_tol=1e-9)
+
+	def test_near_zero(self):
+		# values close to zero
+		near_zero_examples = [
+			(1e-9, 0.0),
+			(-1e-9, 0.0),
+			(-1e-150, 0.0)]
+		# these should not be close to any rel_tol
+		self.assertAllNotClose(near_zero_examples, rel_tol=0.9)
+		# these should be close to abs_tol=1e-8
+		self.assertAllClose(near_zero_examples, abs_tol=1e-8)
+
+	def test_identical_infinite(self):
+		# these are close regardless of tolerance -- i.e. they are equal
+		self.assertIsClose(INF, INF)
+		self.assertIsClose(INF, INF, abs_tol=0.0)
+		self.assertIsClose(NINF, NINF)
+		self.assertIsClose(NINF, NINF, abs_tol=0.0)
+
+	def test_inf_ninf_nan(self):
+		# these should never be close (following IEEE 754 rules for equality)
+		not_close_examples = [
+			(NAN, NAN),
+			(NAN, 1e-100),
+			(1e-100, NAN),
+			(INF, NAN),
+			(NAN, INF),
+			(INF, NINF),
+			(INF, 1.0),
+			(1.0, INF),
+			(INF, 1e308),
+			(1e308, INF)]
+		# use largest reasonable tolerance
+		self.assertAllNotClose(not_close_examples, abs_tol=0.999999999999999)
+
+	def test_zero_tolerance(self):
+		# test with zero tolerance
+		zero_tolerance_close_examples = [
+			(1.0, 1.0),
+			(-3.4, -3.4),
+			(-1e-300, -1e-300)]
+		self.assertAllClose(zero_tolerance_close_examples, rel_tol=0.0)
+
+		zero_tolerance_not_close_examples = [
+			(1.0, 1.000000000000001),
+			(0.99999999999999, 1.0),
+			(1.0e200, .999999999999999e200)]
+		self.assertAllNotClose(zero_tolerance_not_close_examples, rel_tol=0.0)
+
+	def test_assymetry(self):
+		# test the assymetry example from PEP 485
+		self.assertAllClose([(9, 10), (10, 9)], rel_tol=0.1)
+
+	def test_integers(self):
+		# test with integer values
+		integer_examples = [
+			(100000001, 100000000),
+			(123456789, 123456788)]
+
+		self.assertAllClose(integer_examples, rel_tol=1e-8)
+		self.assertAllNotClose(integer_examples, rel_tol=1e-9)
+
+	def test_decimals(self):
+		# test with Decimal values
+		from decimal import Decimal
+
+		decimal_examples = [
+			(Decimal('1.00000001'), Decimal('1.0')),
+			(Decimal('1.00000001e-20'), Decimal('1.0e-20')),
+			(Decimal('1.00000001e-100'), Decimal('1.0e-100'))]
+		self.assertAllClose(decimal_examples, rel_tol=1e-8)
+		self.assertAllNotClose(decimal_examples, rel_tol=1e-9)
+
+	def test_fractions(self):
+		# test with Fraction values
+		from fractions import Fraction
+
+		# could use some more examples here!
+		fraction_examples = [(Fraction(1, 100000000) + 1, Fraction(1))]
+		self.assertAllClose(fraction_examples, rel_tol=1e-8)
+		self.assertAllNotClose(fraction_examples, rel_tol=1e-9)
+
+
+@unittest.skipUnless(
+	(sys.version_info[0] == 2 and sys.maxunicode < 0x10FFFF),
+	"requires 'narrow' Python 2.7 build")
+class NarrowUnicodeBuildTest(unittest.TestCase):
+
+	def test_unichr(self):
+		from __builtin__ import unichr as narrow_unichr
+
+		self.assertRaises(
+			ValueError,
+			narrow_unichr, 0xFFFF + 1)
+
+		self.assertEqual(unichr(1114111), u'\U0010FFFF')
+
+		self.assertRaises(
+			ValueError,
+			unichr, 0x10FFFF + 1)
+
+	def test_byteord(self):
+		from __builtin__ import ord as narrow_ord
+
+		self.assertRaises(
+			TypeError,
+			narrow_ord, u'\U00010000')
+
+		self.assertEqual(byteord(u'\U00010000'), 0xFFFF + 1)
+		self.assertEqual(byteord(u'\U0010FFFF'), 1114111)
+
+
+class TestRedirectStream:
+
+    redirect_stream = None
+    orig_stream = None
+
+    def test_no_redirect_in_init(self):
+        orig_stdout = getattr(sys, self.orig_stream)
+        self.redirect_stream(None)
+        self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
+
+    def test_redirect_to_string_io(self):
+        f = StringIO()
+        msg = "Consider an API like help(), which prints directly to stdout"
+        orig_stdout = getattr(sys, self.orig_stream)
+        with self.redirect_stream(f):
+            print(msg, file=getattr(sys, self.orig_stream))
+        self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
+        s = f.getvalue().strip()
+        self.assertEqual(s, msg)
+
+    def test_enter_result_is_target(self):
+        f = StringIO()
+        with self.redirect_stream(f) as enter_result:
+            self.assertIs(enter_result, f)
+
+    def test_cm_is_reusable(self):
+        f = StringIO()
+        write_to_f = self.redirect_stream(f)
+        orig_stdout = getattr(sys, self.orig_stream)
+        with write_to_f:
+            print("Hello", end=" ", file=getattr(sys, self.orig_stream))
+        with write_to_f:
+            print("World!", file=getattr(sys, self.orig_stream))
+        self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
+        s = f.getvalue()
+        self.assertEqual(s, "Hello World!\n")
+
+    def test_cm_is_reentrant(self):
+        f = StringIO()
+        write_to_f = self.redirect_stream(f)
+        orig_stdout = getattr(sys, self.orig_stream)
+        with write_to_f:
+            print("Hello", end=" ", file=getattr(sys, self.orig_stream))
+            with write_to_f:
+                print("World!", file=getattr(sys, self.orig_stream))
+        self.assertIs(getattr(sys, self.orig_stream), orig_stdout)
+        s = f.getvalue()
+        self.assertEqual(s, "Hello World!\n")
+
+
+class TestRedirectStdout(TestRedirectStream, unittest.TestCase):
+
+    redirect_stream = redirect_stdout
+    orig_stream = "stdout"
+
+
+class TestRedirectStderr(TestRedirectStream, unittest.TestCase):
+
+    redirect_stream = redirect_stderr
+    orig_stream = "stderr"
+
+
+if __name__ == "__main__":
+	sys.exit(unittest.main())
diff --git a/Tests/misc/testTools_test.py b/Tests/misc/testTools_test.py
new file mode 100644
index 0000000..11c04b1
--- /dev/null
+++ b/Tests/misc/testTools_test.py
@@ -0,0 +1,83 @@
+# -*- coding: utf-8 -*-
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+from fontTools.misc.py23 import *
+import fontTools.misc.testTools as testTools
+import unittest
+
+
+class TestToolsTest(unittest.TestCase):
+
+    def test_parseXML_str(self):
+        self.assertEqual(testTools.parseXML(
+            '<Foo n="1"/>'
+            '<Foo n="2">'
+            '    some ünıcòðe text'
+            '    <Bar color="red"/>'
+            '    some more text'
+            '</Foo>'
+            '<Foo n="3"/>'), [
+                ("Foo", {"n": "1"}, []),
+                ("Foo", {"n": "2"}, [
+                    "    some ünıcòðe text    ",
+                    ("Bar", {"color": "red"}, []),
+                    "    some more text",
+                ]),
+                ("Foo", {"n": "3"}, [])
+            ])
+
+    def test_parseXML_bytes(self):
+        self.assertEqual(testTools.parseXML(
+            b'<Foo n="1"/>'
+            b'<Foo n="2">'
+            b'    some \xc3\xbcn\xc4\xb1c\xc3\xb2\xc3\xb0e text'
+            b'    <Bar color="red"/>'
+            b'    some more text'
+            b'</Foo>'
+            b'<Foo n="3"/>'), [
+                ("Foo", {"n": "1"}, []),
+                ("Foo", {"n": "2"}, [
+                    "    some ünıcòðe text    ",
+                    ("Bar", {"color": "red"}, []),
+                    "    some more text",
+                ]),
+                ("Foo", {"n": "3"}, [])
+            ])
+
+    def test_parseXML_str_list(self):
+        self.assertEqual(testTools.parseXML(
+            ['<Foo n="1"/>'
+             '<Foo n="2"/>']), [
+                ("Foo", {"n": "1"}, []),
+                ("Foo", {"n": "2"}, [])
+            ])
+
+    def test_parseXML_bytes_list(self):
+        self.assertEqual(testTools.parseXML(
+            [b'<Foo n="1"/>'
+             b'<Foo n="2"/>']), [
+                ("Foo", {"n": "1"}, []),
+                ("Foo", {"n": "2"}, [])
+            ])
+
+    def test_getXML(self):
+        def toXML(writer, ttFont):
+            writer.simpletag("simple")
+            writer.newline()
+            writer.begintag("tag", attr='value')
+            writer.newline()
+            writer.write("hello world")
+            writer.newline()
+            writer.endtag("tag")
+            writer.newline()  # toXML always ends with a newline
+
+        self.assertEqual(testTools.getXML(toXML),
+                         ['<simple/>',
+                          '<tag attr="value">',
+                          '  hello world',
+                          '</tag>'])
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/misc/textTools_test.py b/Tests/misc/textTools_test.py
new file mode 100644
index 0000000..547569a
--- /dev/null
+++ b/Tests/misc/textTools_test.py
@@ -0,0 +1,11 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.textTools import pad
+
+
+def test_pad():
+    assert len(pad(b'abcd', 4)) == 4
+    assert len(pad(b'abcde', 2)) == 6
+    assert len(pad(b'abcde', 4)) == 8
+    assert pad(b'abcdef', 4) == b'abcdef\x00\x00'
+    assert pad(b'abcdef', 1) == b'abcdef'
diff --git a/Tests/misc/timeTools_test.py b/Tests/misc/timeTools_test.py
new file mode 100644
index 0000000..6c98f64
--- /dev/null
+++ b/Tests/misc/timeTools_test.py
@@ -0,0 +1,24 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.timeTools import asctime, timestampNow, epoch_diff
+import os
+import time
+import pytest
+
+
+def test_asctime():
+    assert isinstance(asctime(), basestring)
+    assert asctime(time.gmtime(0)) == 'Thu Jan  1 00:00:00 1970'
+
+
+def test_source_date_epoch():
+    os.environ["SOURCE_DATE_EPOCH"] = "150687315"
+    assert timestampNow() + epoch_diff == 150687315
+
+    # Check that malformed value fail, any better way?
+    os.environ["SOURCE_DATE_EPOCH"] = "ABCDEFGHI"
+    with pytest.raises(ValueError):
+        timestampNow()
+
+    del os.environ["SOURCE_DATE_EPOCH"]
+    assert timestampNow() + epoch_diff != 150687315
diff --git a/Tests/misc/transform_test.py b/Tests/misc/transform_test.py
new file mode 100644
index 0000000..7a7a67d
--- /dev/null
+++ b/Tests/misc/transform_test.py
@@ -0,0 +1,101 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.transform import Transform, Identity, Offset, Scale
+import math
+import pytest
+
+
+class TransformTest(object):
+
+    def test_examples(self):
+        t = Transform()
+        assert repr(t) == "<Transform [1 0 0 1 0 0]>"
+        assert t.scale(2) == Transform(2, 0, 0, 2, 0, 0)
+        assert t.scale(2.5, 5.5) == Transform(2.5, 0, 0, 5.5, 0, 0)
+        assert t.scale(2, 3).transformPoint((100, 100)) == (200, 300)
+
+    def test__init__(self):
+        assert Transform(12) == Transform(12, 0, 0, 1, 0, 0)
+        assert Transform(dx=12) == Transform(1, 0, 0, 1, 12, 0)
+        assert Transform(yx=12) == Transform(1, 0, 12, 1, 0, 0)
+
+    def test_transformPoints(self):
+        t = Transform(2, 0, 0, 3, 0, 0)
+        assert t.transformPoints(
+            [(0, 0), (0, 100), (100, 100), (100, 0)]
+        ) == [(0, 0), (0, 300), (200, 300), (200, 0)]
+
+    def test_translate(self):
+        t = Transform()
+        assert t.translate(20, 30) == Transform(1, 0, 0, 1, 20, 30)
+
+    def test_scale(self):
+        t = Transform()
+        assert t.scale(5) == Transform(5, 0, 0, 5, 0, 0)
+        assert t.scale(5, 6) == Transform(5, 0, 0, 6, 0, 0)
+
+    def test_rotate(self):
+        t = Transform()
+        assert t.rotate(math.pi / 2) == Transform(0, 1, -1, 0, 0, 0)
+        t = Transform()
+        assert t.rotate(-math.pi / 2) == Transform(0, -1, 1, 0, 0, 0)
+        t = Transform()
+        assert tuple(t.rotate(math.radians(30))) == pytest.approx(
+            tuple(Transform(0.866025, 0.5, -0.5, 0.866025, 0, 0)))
+
+    def test_skew(self):
+        t = Transform().skew(math.pi / 4)
+        assert tuple(t) == pytest.approx(tuple(Transform(1, 0, 1, 1, 0, 0)))
+
+    def test_transform(self):
+        t = Transform(2, 0, 0, 3, 1, 6)
+        assert t.transform((4, 3, 2, 1, 5, 6)) == Transform(8, 9, 4, 3, 11, 24)
+
+    def test_reverseTransform(self):
+        t = Transform(2, 0, 0, 3, 1, 6)
+        reverse_t = t.reverseTransform((4, 3, 2, 1, 5, 6))
+        assert reverse_t == Transform(8, 6, 6, 3, 21, 15)
+        t = Transform(4, 3, 2, 1, 5, 6)
+        reverse_t = t.transform((2, 0, 0, 3, 1, 6))
+        assert reverse_t == Transform(8, 6, 6, 3, 21, 15)
+
+    def test_inverse(self):
+        t = Transform().translate(2, 3).scale(4, 5)
+        assert t.transformPoint((10, 20)) == (42, 103)
+        it = t.inverse()
+        assert it.transformPoint((42, 103)) == (10.0, 20.0)
+        assert Transform().inverse() == Transform()
+
+    def test_toPS(self):
+        t = Transform().scale(2, 3).translate(4, 5)
+        assert t.toPS() == '[2 0 0 3 8 15]'
+
+    def test__ne__(self):
+        assert Transform() != Transform(2, 0, 0, 2, 0, 0)
+
+    def test__hash__(self):
+        t = Transform(12, 0, 0, 13, 0, 0)
+        d = {t: None}
+        assert t in d.keys()
+
+    def test__bool__(self):
+        assert not bool(Transform())
+        assert Transform(2, 0, 0, 2, 0, 0)
+        assert Transform(1, 0, 0, 1, 1, 0)
+
+    def test__repr__(self):
+        assert repr(Transform(1, 2, 3, 4, 5, 6)) == '<Transform [1 2 3 4 5 6]>'
+
+    def test_Identity(self):
+        assert isinstance(Identity, Transform)
+        assert Identity == Transform(1, 0, 0, 1, 0, 0)
+
+    def test_Offset(self):
+        assert Offset() == Transform(1, 0, 0, 1, 0, 0)
+        assert Offset(1) == Transform(1, 0, 0, 1, 1, 0)
+        assert Offset(1, 2) == Transform(1, 0, 0, 1, 1, 2)
+
+    def test_Scale(self):
+        assert Scale(1) == Transform(1, 0, 0, 1, 0, 0)
+        assert Scale(2) == Transform(2, 0, 0, 2, 0, 0)
+        assert Scale(1, 2) == Transform(1, 0, 0, 2, 0, 0)
diff --git a/Tests/misc/xmlReader_test.py b/Tests/misc/xmlReader_test.py
new file mode 100644
index 0000000..33fddcc
--- /dev/null
+++ b/Tests/misc/xmlReader_test.py
@@ -0,0 +1,190 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import print_function, division, absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+import os
+import unittest
+from fontTools.ttLib import TTFont
+from fontTools.misc.xmlReader import XMLReader, ProgressPrinter, BUFSIZE
+import tempfile
+
+
+class TestXMLReader(unittest.TestCase):
+
+	def test_decode_utf8(self):
+
+		class DebugXMLReader(XMLReader):
+
+			def __init__(self, fileOrPath, ttFont, progress=None):
+				super(DebugXMLReader, self).__init__(
+					fileOrPath, ttFont, progress)
+				self.contents = []
+
+			def _endElementHandler(self, name):
+				if self.stackSize == 3:
+					name, attrs, content = self.root
+					self.contents.append(content)
+				super(DebugXMLReader, self)._endElementHandler(name)
+
+		expected = 'fôôbär'
+		data = '''\
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      %s
+    </namerecord>
+  </name>
+</ttFont>
+''' % expected
+
+		with BytesIO(data.encode('utf-8')) as tmp:
+			reader = DebugXMLReader(tmp, TTFont())
+			reader.read()
+		content = strjoin(reader.contents[0]).strip()
+		self.assertEqual(expected, content)
+
+	def test_normalise_newlines(self):
+
+		class DebugXMLReader(XMLReader):
+
+			def __init__(self, fileOrPath, ttFont, progress=None):
+				super(DebugXMLReader, self).__init__(
+					fileOrPath, ttFont, progress)
+				self.newlines = []
+
+			def _characterDataHandler(self, data):
+				self.newlines.extend([c for c in data if c in ('\r', '\n')])
+
+		# notice how when CR is escaped, it is not normalised by the XML parser
+		data = (
+			'<ttFont>\r'                                    #        \r -> \n
+			'  <test>\r\n'                                  #      \r\n -> \n 
+			'    a line of text\n'                          #              \n
+			'    escaped CR and unix newline &#13;\n'       #   &#13;\n -> \r\n
+			'    escaped CR and macintosh newline &#13;\r'  #   &#13;\r -> \r\n
+			'    escaped CR and windows newline &#13;\r\n'  # &#13;\r\n -> \r\n
+			'  </test>\n'                                   #              \n
+			'</ttFont>')
+
+		with BytesIO(data.encode('utf-8')) as tmp:
+			reader = DebugXMLReader(tmp, TTFont())
+			reader.read()
+		expected = ['\n'] * 3 + ['\r', '\n'] * 3 + ['\n']
+		self.assertEqual(expected, reader.newlines)
+
+	def test_progress(self):
+
+		class DummyProgressPrinter(ProgressPrinter):
+
+			def __init__(self, title, maxval=100):
+				self.label = title
+				self.maxval = maxval
+				self.pos = 0
+
+			def set(self, val, maxval=None):
+				if maxval is not None:
+					self.maxval = maxval
+				self.pos = val
+
+			def increment(self, val=1):
+				self.pos += val
+
+			def setLabel(self, text):
+				self.label = text
+
+		data = (
+			'<ttFont>\n'
+			'  <test>\n'
+			'    %s\n'
+			'  </test>\n'
+			'</ttFont>\n'
+			% ("z" * 2 * BUFSIZE)
+			).encode('utf-8')
+
+		dataSize = len(data)
+		progressBar = DummyProgressPrinter('test')
+		with BytesIO(data) as tmp:
+			reader = XMLReader(tmp, TTFont(), progress=progressBar)
+			self.assertEqual(progressBar.pos, 0)
+			reader.read()
+		self.assertEqual(progressBar.pos, dataSize // 100)
+		self.assertEqual(progressBar.maxval, dataSize // 100)
+		self.assertTrue('test' in progressBar.label)
+		with BytesIO(b"<ttFont></ttFont>") as tmp:
+			reader = XMLReader(tmp, TTFont(), progress=progressBar)
+			reader.read()
+		# when data size is less than 100 bytes, 'maxval' is 1
+		self.assertEqual(progressBar.maxval, 1)
+
+	def test_close_file_path(self):
+		with tempfile.NamedTemporaryFile(delete=False) as tmp:
+			tmp.write(b'<ttFont></ttFont>')
+		reader = XMLReader(tmp.name, TTFont())
+		reader.read()
+		# when reading from path, the file is closed automatically at the end
+		self.assertTrue(reader.file.closed)
+		# this does nothing
+		reader.close()
+		self.assertTrue(reader.file.closed)
+		os.remove(tmp.name)
+
+	def test_close_file_obj(self):
+		with tempfile.NamedTemporaryFile(delete=False) as tmp:
+			tmp.write(b'<ttFont>"hello"</ttFont>')
+		with open(tmp.name, "rb") as f:
+			reader = XMLReader(f, TTFont())
+			reader.read()
+			# when reading from a file or file-like object, the latter is kept open
+			self.assertFalse(reader.file.closed)
+		# ... until the user explicitly closes it
+		reader.close()
+		self.assertTrue(reader.file.closed)
+		os.remove(tmp.name)
+
+	def test_read_sub_file(self):
+		# Verifies that sub-file content is able to be read to a table.
+		expectedContent = 'testContent'
+		expectedNameID = '1'
+		expectedPlatform = '3'
+		expectedLangId = '0x409'
+
+		with tempfile.NamedTemporaryFile(delete=False) as tmp:
+			subFileData = (
+				'<ttFont ttLibVersion="3.15">'
+					'<name>'
+						'<namerecord nameID="%s" platformID="%s" platEncID="1" langID="%s">'
+							'%s'
+						'</namerecord>'
+					'</name>'
+				'</ttFont>'
+			) % (expectedNameID, expectedPlatform, expectedLangId, expectedContent)
+			tmp.write(subFileData.encode("utf-8"))
+
+		with tempfile.NamedTemporaryFile(delete=False) as tmp2:
+			fileData = (
+				'<ttFont ttLibVersion="3.15">'
+					'<name>'
+						'<namerecord src="%s"/>'
+					'</name>'
+				'</ttFont>'
+			) % tmp.name
+			tmp2.write(fileData.encode('utf-8'))
+
+		ttf = TTFont()
+		with open(tmp2.name, "rb") as f:
+			reader = XMLReader(f, ttf)
+			reader.read()
+			reader.close()
+			nameTable = ttf['name']
+			self.assertTrue(int(expectedNameID) == nameTable.names[0].nameID)
+			self.assertTrue(int(expectedLangId, 16) == nameTable.names[0].langID)
+			self.assertTrue(int(expectedPlatform) == nameTable.names[0].platformID)
+			self.assertEqual(expectedContent, nameTable.names[0].string.decode(nameTable.names[0].getEncoding()))
+
+		os.remove(tmp.name)
+		os.remove(tmp2.name)
+
+if __name__ == '__main__':
+	import sys
+	sys.exit(unittest.main())
diff --git a/Tests/misc/xmlWriter_test.py b/Tests/misc/xmlWriter_test.py
new file mode 100644
index 0000000..0930e1c
--- /dev/null
+++ b/Tests/misc/xmlWriter_test.py
@@ -0,0 +1,128 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+import os
+import unittest
+from fontTools.misc.xmlWriter import XMLWriter
+
+linesep = tobytes(os.linesep)
+HEADER = b'<?xml version="1.0" encoding="UTF-8"?>' + linesep
+
+class TestXMLWriter(unittest.TestCase):
+
+	def test_comment_escaped(self):
+		writer = XMLWriter(BytesIO())
+		writer.comment("This&that are <comments>")
+		self.assertEqual(HEADER + b"<!-- This&amp;that are &lt;comments&gt; -->", writer.file.getvalue())
+
+	def test_comment_multiline(self):
+		writer = XMLWriter(BytesIO())
+		writer.comment("Hello world\nHow are you?")
+		self.assertEqual(HEADER + b"<!-- Hello world" + linesep + b"     How are you? -->",
+				 writer.file.getvalue())
+
+	def test_encoding_default(self):
+		writer = XMLWriter(BytesIO())
+		self.assertEqual(b'<?xml version="1.0" encoding="UTF-8"?>' + linesep,
+				 writer.file.getvalue())
+
+	def test_encoding_utf8(self):
+		# https://github.com/behdad/fonttools/issues/246
+		writer = XMLWriter(BytesIO(), encoding="utf8")
+		self.assertEqual(b'<?xml version="1.0" encoding="UTF-8"?>' + linesep,
+				 writer.file.getvalue())
+
+	def test_encoding_UTF_8(self):
+		# https://github.com/behdad/fonttools/issues/246
+		writer = XMLWriter(BytesIO(), encoding="UTF-8")
+		self.assertEqual(b'<?xml version="1.0" encoding="UTF-8"?>' + linesep,
+				 writer.file.getvalue())
+
+	def test_encoding_UTF8(self):
+		# https://github.com/behdad/fonttools/issues/246
+		writer = XMLWriter(BytesIO(), encoding="UTF8")
+		self.assertEqual(b'<?xml version="1.0" encoding="UTF-8"?>' + linesep,
+				 writer.file.getvalue())
+
+	def test_encoding_other(self):
+		self.assertRaises(Exception, XMLWriter, BytesIO(),
+				  encoding="iso-8859-1")
+
+	def test_write(self):
+		writer = XMLWriter(BytesIO())
+		writer.write("foo&bar")
+		self.assertEqual(HEADER + b"foo&amp;bar", writer.file.getvalue())
+
+	def test_indent_dedent(self):
+		writer = XMLWriter(BytesIO())
+		writer.write("foo")
+		writer.newline()
+		writer.indent()
+		writer.write("bar")
+		writer.newline()
+		writer.dedent()
+		writer.write("baz")
+		self.assertEqual(HEADER + bytesjoin(["foo", "  bar", "baz"], linesep),
+				 writer.file.getvalue())
+
+	def test_writecdata(self):
+		writer = XMLWriter(BytesIO())
+		writer.writecdata("foo&bar")
+		self.assertEqual(HEADER + b"<![CDATA[foo&bar]]>", writer.file.getvalue())
+
+	def test_simpletag(self):
+		writer = XMLWriter(BytesIO())
+		writer.simpletag("tag", a="1", b="2")
+		self.assertEqual(HEADER + b'<tag a="1" b="2"/>', writer.file.getvalue())
+
+	def test_begintag_endtag(self):
+		writer = XMLWriter(BytesIO())
+		writer.begintag("tag", attr="value")
+		writer.write("content")
+		writer.endtag("tag")
+		self.assertEqual(HEADER + b'<tag attr="value">content</tag>', writer.file.getvalue())
+
+	def test_dumphex(self):
+		writer = XMLWriter(BytesIO())
+		writer.dumphex("Type is a beautiful group of letters, not a group of beautiful letters.")
+		self.assertEqual(HEADER + bytesjoin([
+		    "54797065 20697320 61206265 61757469",
+		    "66756c20 67726f75 70206f66 206c6574",
+		    "74657273 2c206e6f 74206120 67726f75",
+		    "70206f66 20626561 75746966 756c206c",
+		    "65747465 72732e  ", ""], joiner=linesep), writer.file.getvalue())
+
+	def test_stringifyattrs(self):
+		writer = XMLWriter(BytesIO())
+		expected = ' attr="0"'
+		self.assertEqual(expected, writer.stringifyattrs(attr=0))
+		self.assertEqual(expected, writer.stringifyattrs(attr=b'0'))
+		self.assertEqual(expected, writer.stringifyattrs(attr='0'))
+		self.assertEqual(expected, writer.stringifyattrs(attr=u'0'))
+
+	def test_carriage_return_escaped(self):
+		writer = XMLWriter(BytesIO())
+		writer.write("two lines\r\nseparated by Windows line endings")
+		self.assertEqual(
+			HEADER + b'two lines&#13;\nseparated by Windows line endings',
+			writer.file.getvalue())
+
+	def test_newlinestr(self):
+		header = b'<?xml version="1.0" encoding="UTF-8"?>'
+
+		for nls in (None, '\n', '\r\n', '\r', ''):
+			writer = XMLWriter(BytesIO(), newlinestr=nls)
+			writer.write("hello")
+			writer.newline()
+			writer.write("world")
+			writer.newline()
+
+			linesep = tobytes(os.linesep) if nls is None else tobytes(nls)
+
+			self.assertEqual(
+				header + linesep + b"hello" + linesep + b"world" + linesep,
+				writer.file.getvalue())
+
+
+if __name__ == '__main__':
+	import sys
+	sys.exit(unittest.main())
diff --git a/Tests/mtiLib/data/featurename-backward.ttx.GSUB b/Tests/mtiLib/data/featurename-backward.ttx.GSUB
new file mode 100644
index 0000000..9469c79
--- /dev/null
+++ b/Tests/mtiLib/data/featurename-backward.ttx.GSUB
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GSUB>
+  <Version value="0x00010000"/>
+  <ScriptList>
+    <!-- ScriptCount=2 -->
+    <ScriptRecord index="0">
+      <ScriptTag value="tel2"/>
+      <Script>
+        <DefaultLangSys>
+          <ReqFeatureIndex value="65535"/>
+          <!-- FeatureCount=2 -->
+          <FeatureIndex index="0" value="0"/>
+          <FeatureIndex index="1" value="1"/>
+        </DefaultLangSys>
+        <!-- LangSysCount=0 -->
+      </Script>
+    </ScriptRecord>
+    <ScriptRecord index="1">
+      <ScriptTag value="telu"/>
+      <Script>
+        <DefaultLangSys>
+          <ReqFeatureIndex value="65535"/>
+          <!-- FeatureCount=2 -->
+          <FeatureIndex index="0" value="0"/>
+          <FeatureIndex index="1" value="1"/>
+        </DefaultLangSys>
+        <!-- LangSysCount=0 -->
+      </Script>
+    </ScriptRecord>
+  </ScriptList>
+  <FeatureList>
+    <!-- FeatureCount=2 -->
+    <FeatureRecord index="0">
+      <FeatureTag value="akhn"/>
+      <Feature>
+        <!-- LookupCount=1 -->
+        <LookupListIndex index="0" value="0"/>
+      </Feature>
+    </FeatureRecord>
+    <FeatureRecord index="1">
+      <FeatureTag value="akh2"/>
+      <Feature>
+        <!-- LookupCount=1 -->
+        <LookupListIndex index="0" value="0"/>
+      </Feature>
+    </FeatureRecord>
+  </FeatureList>
+  <LookupList>
+    <!-- LookupCount=1 -->
+    <Lookup index="0">
+      <LookupType value="1"/>
+      <LookupFlag value="0"/>
+      <!-- SubTableCount=1 -->
+      <SingleSubst index="0" Format="1">
+        <Substitution in="a" out="b"/>
+      </SingleSubst>
+    </Lookup>
+  </LookupList>
+</GSUB>
diff --git a/Tests/mtiLib/data/featurename-backward.txt b/Tests/mtiLib/data/featurename-backward.txt
new file mode 100644
index 0000000..7e3d5d6
--- /dev/null
+++ b/Tests/mtiLib/data/featurename-backward.txt
@@ -0,0 +1,14 @@
+
+feature table begin
+f0	akhn	l1
+1	akh2	l1
+feature table end
+
+script table begin
+telu	default		0, 1
+tel2	default		f0, 1
+script table end
+
+lookup	l1	single
+a	b
+lookup end
diff --git a/Tests/mtiLib/data/featurename-forward.ttx.GSUB b/Tests/mtiLib/data/featurename-forward.ttx.GSUB
new file mode 100644
index 0000000..9469c79
--- /dev/null
+++ b/Tests/mtiLib/data/featurename-forward.ttx.GSUB
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GSUB>
+  <Version value="0x00010000"/>
+  <ScriptList>
+    <!-- ScriptCount=2 -->
+    <ScriptRecord index="0">
+      <ScriptTag value="tel2"/>
+      <Script>
+        <DefaultLangSys>
+          <ReqFeatureIndex value="65535"/>
+          <!-- FeatureCount=2 -->
+          <FeatureIndex index="0" value="0"/>
+          <FeatureIndex index="1" value="1"/>
+        </DefaultLangSys>
+        <!-- LangSysCount=0 -->
+      </Script>
+    </ScriptRecord>
+    <ScriptRecord index="1">
+      <ScriptTag value="telu"/>
+      <Script>
+        <DefaultLangSys>
+          <ReqFeatureIndex value="65535"/>
+          <!-- FeatureCount=2 -->
+          <FeatureIndex index="0" value="0"/>
+          <FeatureIndex index="1" value="1"/>
+        </DefaultLangSys>
+        <!-- LangSysCount=0 -->
+      </Script>
+    </ScriptRecord>
+  </ScriptList>
+  <FeatureList>
+    <!-- FeatureCount=2 -->
+    <FeatureRecord index="0">
+      <FeatureTag value="akhn"/>
+      <Feature>
+        <!-- LookupCount=1 -->
+        <LookupListIndex index="0" value="0"/>
+      </Feature>
+    </FeatureRecord>
+    <FeatureRecord index="1">
+      <FeatureTag value="akh2"/>
+      <Feature>
+        <!-- LookupCount=1 -->
+        <LookupListIndex index="0" value="0"/>
+      </Feature>
+    </FeatureRecord>
+  </FeatureList>
+  <LookupList>
+    <!-- LookupCount=1 -->
+    <Lookup index="0">
+      <LookupType value="1"/>
+      <LookupFlag value="0"/>
+      <!-- SubTableCount=1 -->
+      <SingleSubst index="0" Format="1">
+        <Substitution in="a" out="b"/>
+      </SingleSubst>
+    </Lookup>
+  </LookupList>
+</GSUB>
diff --git a/Tests/mtiLib/data/featurename-forward.txt b/Tests/mtiLib/data/featurename-forward.txt
new file mode 100644
index 0000000..971ca76
--- /dev/null
+++ b/Tests/mtiLib/data/featurename-forward.txt
@@ -0,0 +1,14 @@
+
+script table begin
+telu	default		0, 1
+tel2	default		f0, 1
+script table end
+
+feature table begin
+f0	akhn	l1
+1	akh2	l1
+feature table end
+
+lookup	l1	single
+a	b
+lookup end
diff --git a/Tests/mtiLib/data/lookupnames-backward.ttx.GSUB b/Tests/mtiLib/data/lookupnames-backward.ttx.GSUB
new file mode 100644
index 0000000..698012c
--- /dev/null
+++ b/Tests/mtiLib/data/lookupnames-backward.ttx.GSUB
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GSUB>
+  <Version value="0x00010000"/>
+  <ScriptList>
+    <!-- ScriptCount=1 -->
+    <ScriptRecord index="0">
+      <ScriptTag value="telu"/>
+      <Script>
+        <DefaultLangSys>
+          <ReqFeatureIndex value="65535"/>
+          <!-- FeatureCount=2 -->
+          <FeatureIndex index="0" value="0"/>
+          <FeatureIndex index="1" value="1"/>
+        </DefaultLangSys>
+        <!-- LangSysCount=0 -->
+      </Script>
+    </ScriptRecord>
+  </ScriptList>
+  <FeatureList>
+    <!-- FeatureCount=2 -->
+    <FeatureRecord index="0">
+      <FeatureTag value="akhn"/>
+      <Feature>
+        <!-- LookupCount=1 -->
+        <LookupListIndex index="0" value="0"/>
+      </Feature>
+    </FeatureRecord>
+    <FeatureRecord index="1">
+      <FeatureTag value="akh2"/>
+      <Feature>
+        <!-- LookupCount=1 -->
+        <LookupListIndex index="0" value="1"/>
+      </Feature>
+    </FeatureRecord>
+  </FeatureList>
+  <LookupList>
+    <!-- LookupCount=2 -->
+    <Lookup index="0">
+      <LookupType value="1"/>
+      <LookupFlag value="0"/>
+      <!-- SubTableCount=1 -->
+      <SingleSubst index="0" Format="1">
+        <Substitution in="uuvowelsignkannada" out="uuvowelsignaltkannada"/>
+        <Substitution in="uvowelsignkannada" out="uvowelsignaltkannada"/>
+      </SingleSubst>
+    </Lookup>
+    <Lookup index="1">
+      <LookupType value="6"/>
+      <LookupFlag value="0"/>
+      <!-- SubTableCount=1 -->
+      <ChainContextSubst index="0" Format="2">
+        <Coverage Format="1">
+          <Glyph value="uvowelsignkannada"/>
+          <Glyph value="uuvowelsignkannada"/>
+        </Coverage>
+        <BacktrackClassDef Format="2">
+          <ClassDef glyph="pakannada" class="1"/>
+          <ClassDef glyph="pevowelkannada" class="1"/>
+          <ClassDef glyph="phakannada" class="1"/>
+          <ClassDef glyph="phevowelkannada" class="1"/>
+          <ClassDef glyph="vakannada" class="1"/>
+          <ClassDef glyph="vevowelkannada" class="1"/>
+        </BacktrackClassDef>
+        <InputClassDef Format="1">
+          <ClassDef glyph="uuvowelsignkannada" class="1"/>
+          <ClassDef glyph="uvowelsignkannada" class="1"/>
+        </InputClassDef>
+        <!-- ChainSubClassSetCount=2 -->
+        <ChainSubClassSet index="0" empty="1"/>
+        <ChainSubClassSet index="1">
+          <!-- ChainSubClassRuleCount=1 -->
+          <ChainSubClassRule index="0">
+            <!-- BacktrackGlyphCount=1 -->
+            <Backtrack index="0" value="1"/>
+            <!-- InputGlyphCount=1 -->
+            <!-- LookAheadGlyphCount=0 -->
+            <!-- SubstCount=1 -->
+            <SubstLookupRecord index="0">
+              <SequenceIndex value="0"/>
+              <LookupListIndex value="0"/>
+            </SubstLookupRecord>
+          </ChainSubClassRule>
+        </ChainSubClassSet>
+      </ChainContextSubst>
+    </Lookup>
+  </LookupList>
+</GSUB>
diff --git a/Tests/mtiLib/data/lookupnames-backward.txt b/Tests/mtiLib/data/lookupnames-backward.txt
new file mode 100644
index 0000000..067d2c1
--- /dev/null
+++ b/Tests/mtiLib/data/lookupnames-backward.txt
@@ -0,0 +1,36 @@
+
+lookup	l1	single
+
+uvowelsignkannada	uvowelsignaltkannada
+uuvowelsignkannada	uuvowelsignaltkannada
+
+lookup end
+
+lookup	l0	chained
+
+backtrackclass definition begin
+pakannada	1
+phakannada	1
+vakannada	1
+pevowelkannada	1
+phevowelkannada	1
+vevowelkannada	1
+class definition end
+
+class definition begin
+uvowelsignkannada	1
+uuvowelsignkannada	1
+class definition end
+
+class-chain	1	1		1,l1
+
+lookup end
+
+script table begin
+telu	default		0, 1
+script table end
+
+feature table begin
+0	akhn	l1
+1	akh2	l0
+feature table end
diff --git a/Tests/mtiLib/data/lookupnames-forward.ttx.GSUB b/Tests/mtiLib/data/lookupnames-forward.ttx.GSUB
new file mode 100644
index 0000000..15e48d0
--- /dev/null
+++ b/Tests/mtiLib/data/lookupnames-forward.ttx.GSUB
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GSUB>
+  <Version value="0x00010000"/>
+  <ScriptList>
+    <!-- ScriptCount=1 -->
+    <ScriptRecord index="0">
+      <ScriptTag value="telu"/>
+      <Script>
+        <DefaultLangSys>
+          <ReqFeatureIndex value="65535"/>
+          <!-- FeatureCount=2 -->
+          <FeatureIndex index="0" value="0"/>
+          <FeatureIndex index="1" value="1"/>
+        </DefaultLangSys>
+        <!-- LangSysCount=0 -->
+      </Script>
+    </ScriptRecord>
+  </ScriptList>
+  <FeatureList>
+    <!-- FeatureCount=2 -->
+    <FeatureRecord index="0">
+      <FeatureTag value="akhn"/>
+      <Feature>
+        <!-- LookupCount=1 -->
+        <LookupListIndex index="0" value="1"/>
+      </Feature>
+    </FeatureRecord>
+    <FeatureRecord index="1">
+      <FeatureTag value="akh2"/>
+      <Feature>
+        <!-- LookupCount=1 -->
+        <LookupListIndex index="0" value="0"/>
+      </Feature>
+    </FeatureRecord>
+  </FeatureList>
+  <LookupList>
+    <!-- LookupCount=2 -->
+    <Lookup index="0">
+      <LookupType value="6"/>
+      <LookupFlag value="0"/>
+      <!-- SubTableCount=1 -->
+      <ChainContextSubst index="0" Format="2">
+        <Coverage Format="1">
+          <Glyph value="uvowelsignkannada"/>
+          <Glyph value="uuvowelsignkannada"/>
+        </Coverage>
+        <BacktrackClassDef Format="2">
+          <ClassDef glyph="pakannada" class="1"/>
+          <ClassDef glyph="pevowelkannada" class="1"/>
+          <ClassDef glyph="phakannada" class="1"/>
+          <ClassDef glyph="phevowelkannada" class="1"/>
+          <ClassDef glyph="vakannada" class="1"/>
+          <ClassDef glyph="vevowelkannada" class="1"/>
+        </BacktrackClassDef>
+        <InputClassDef Format="1">
+          <ClassDef glyph="uuvowelsignkannada" class="1"/>
+          <ClassDef glyph="uvowelsignkannada" class="1"/>
+        </InputClassDef>
+        <!-- ChainSubClassSetCount=2 -->
+        <ChainSubClassSet index="0" empty="1"/>
+        <ChainSubClassSet index="1">
+          <!-- ChainSubClassRuleCount=1 -->
+          <ChainSubClassRule index="0">
+            <!-- BacktrackGlyphCount=1 -->
+            <Backtrack index="0" value="1"/>
+            <!-- InputGlyphCount=1 -->
+            <!-- LookAheadGlyphCount=0 -->
+            <!-- SubstCount=1 -->
+            <SubstLookupRecord index="0">
+              <SequenceIndex value="0"/>
+              <LookupListIndex value="1"/>
+            </SubstLookupRecord>
+          </ChainSubClassRule>
+        </ChainSubClassSet>
+      </ChainContextSubst>
+    </Lookup>
+    <Lookup index="1">
+      <LookupType value="1"/>
+      <LookupFlag value="0"/>
+      <!-- SubTableCount=1 -->
+      <SingleSubst index="0" Format="1">
+        <Substitution in="uuvowelsignkannada" out="uuvowelsignaltkannada"/>
+        <Substitution in="uvowelsignkannada" out="uvowelsignaltkannada"/>
+      </SingleSubst>
+    </Lookup>
+  </LookupList>
+</GSUB>
diff --git a/Tests/mtiLib/data/lookupnames-forward.txt b/Tests/mtiLib/data/lookupnames-forward.txt
new file mode 100644
index 0000000..16c9819
--- /dev/null
+++ b/Tests/mtiLib/data/lookupnames-forward.txt
@@ -0,0 +1,36 @@
+
+lookup	l0	chained
+
+backtrackclass definition begin
+pakannada	1
+phakannada	1
+vakannada	1
+pevowelkannada	1
+phevowelkannada	1
+vevowelkannada	1
+class definition end
+
+class definition begin
+uvowelsignkannada	1
+uuvowelsignkannada	1
+class definition end
+
+class-chain	1	1		1,l1
+
+lookup end
+
+script table begin
+telu	default		0, 1
+script table end
+
+lookup	l1	single
+
+uvowelsignkannada	uvowelsignaltkannada
+uuvowelsignkannada	uuvowelsignaltkannada
+
+lookup end
+
+feature table begin
+0	akhn	l1
+1	akh2	l0
+feature table end
diff --git a/Tests/mtiLib/data/mixed-toplevels.ttx.GSUB b/Tests/mtiLib/data/mixed-toplevels.ttx.GSUB
new file mode 100644
index 0000000..15e48d0
--- /dev/null
+++ b/Tests/mtiLib/data/mixed-toplevels.ttx.GSUB
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GSUB>
+  <Version value="0x00010000"/>
+  <ScriptList>
+    <!-- ScriptCount=1 -->
+    <ScriptRecord index="0">
+      <ScriptTag value="telu"/>
+      <Script>
+        <DefaultLangSys>
+          <ReqFeatureIndex value="65535"/>
+          <!-- FeatureCount=2 -->
+          <FeatureIndex index="0" value="0"/>
+          <FeatureIndex index="1" value="1"/>
+        </DefaultLangSys>
+        <!-- LangSysCount=0 -->
+      </Script>
+    </ScriptRecord>
+  </ScriptList>
+  <FeatureList>
+    <!-- FeatureCount=2 -->
+    <FeatureRecord index="0">
+      <FeatureTag value="akhn"/>
+      <Feature>
+        <!-- LookupCount=1 -->
+        <LookupListIndex index="0" value="1"/>
+      </Feature>
+    </FeatureRecord>
+    <FeatureRecord index="1">
+      <FeatureTag value="akh2"/>
+      <Feature>
+        <!-- LookupCount=1 -->
+        <LookupListIndex index="0" value="0"/>
+      </Feature>
+    </FeatureRecord>
+  </FeatureList>
+  <LookupList>
+    <!-- LookupCount=2 -->
+    <Lookup index="0">
+      <LookupType value="6"/>
+      <LookupFlag value="0"/>
+      <!-- SubTableCount=1 -->
+      <ChainContextSubst index="0" Format="2">
+        <Coverage Format="1">
+          <Glyph value="uvowelsignkannada"/>
+          <Glyph value="uuvowelsignkannada"/>
+        </Coverage>
+        <BacktrackClassDef Format="2">
+          <ClassDef glyph="pakannada" class="1"/>
+          <ClassDef glyph="pevowelkannada" class="1"/>
+          <ClassDef glyph="phakannada" class="1"/>
+          <ClassDef glyph="phevowelkannada" class="1"/>
+          <ClassDef glyph="vakannada" class="1"/>
+          <ClassDef glyph="vevowelkannada" class="1"/>
+        </BacktrackClassDef>
+        <InputClassDef Format="1">
+          <ClassDef glyph="uuvowelsignkannada" class="1"/>
+          <ClassDef glyph="uvowelsignkannada" class="1"/>
+        </InputClassDef>
+        <!-- ChainSubClassSetCount=2 -->
+        <ChainSubClassSet index="0" empty="1"/>
+        <ChainSubClassSet index="1">
+          <!-- ChainSubClassRuleCount=1 -->
+          <ChainSubClassRule index="0">
+            <!-- BacktrackGlyphCount=1 -->
+            <Backtrack index="0" value="1"/>
+            <!-- InputGlyphCount=1 -->
+            <!-- LookAheadGlyphCount=0 -->
+            <!-- SubstCount=1 -->
+            <SubstLookupRecord index="0">
+              <SequenceIndex value="0"/>
+              <LookupListIndex value="1"/>
+            </SubstLookupRecord>
+          </ChainSubClassRule>
+        </ChainSubClassSet>
+      </ChainContextSubst>
+    </Lookup>
+    <Lookup index="1">
+      <LookupType value="1"/>
+      <LookupFlag value="0"/>
+      <!-- SubTableCount=1 -->
+      <SingleSubst index="0" Format="1">
+        <Substitution in="uuvowelsignkannada" out="uuvowelsignaltkannada"/>
+        <Substitution in="uvowelsignkannada" out="uvowelsignaltkannada"/>
+      </SingleSubst>
+    </Lookup>
+  </LookupList>
+</GSUB>
diff --git a/Tests/mtiLib/data/mixed-toplevels.txt b/Tests/mtiLib/data/mixed-toplevels.txt
new file mode 100644
index 0000000..dc09057
--- /dev/null
+++ b/Tests/mtiLib/data/mixed-toplevels.txt
@@ -0,0 +1,36 @@
+
+lookup	0	chained
+
+backtrackclass definition begin
+pakannada	1
+phakannada	1
+vakannada	1
+pevowelkannada	1
+phevowelkannada	1
+vevowelkannada	1
+class definition end
+
+class definition begin
+uvowelsignkannada	1
+uuvowelsignkannada	1
+class definition end
+
+class-chain	1	1		1,1
+
+lookup end
+
+script table begin
+telu	default		0, 1
+script table end
+
+lookup	1	single
+
+uvowelsignkannada	uvowelsignaltkannada
+uuvowelsignkannada	uuvowelsignaltkannada
+
+lookup end
+
+feature table begin
+0	akhn	1
+1	akh2	0
+feature table end
diff --git a/Tests/mtiLib/data/mti/README b/Tests/mtiLib/data/mti/README
new file mode 100644
index 0000000..edf6120
--- /dev/null
+++ b/Tests/mtiLib/data/mti/README
@@ -0,0 +1,12 @@
+The *.txt data in this directory was imported from:
+
+https://github.com/Monotype/OpenType_Table_Source/tree/gh-pages/downloads
+
+at the following revision:
+
+8a4db481c63efe468092d7e9cd149d2e9786369a
+
+plus fixes for the following issues:
+
+https://github.com/Monotype/OpenType_Table_Source/issues/11
+https://github.com/Monotype/OpenType_Table_Source/issues/13
diff --git a/Tests/mtiLib/data/mti/chained-glyph.ttx.GPOS b/Tests/mtiLib/data/mti/chained-glyph.ttx.GPOS
new file mode 100644
index 0000000..811b1df
--- /dev/null
+++ b/Tests/mtiLib/data/mti/chained-glyph.ttx.GPOS
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GPOS>
+  <Version value="0x00010000"/>
+  <LookupList>
+    <!-- LookupCount=2 -->
+    <Lookup index="0">
+      <LookupType value="8"/>
+      <LookupFlag value="512"/>
+      <!-- SubTableCount=1 -->
+      <ChainContextPos index="0" Format="1">
+        <Coverage Format="1">
+          <Glyph value="uuvowelsignsinh"/>
+          <Glyph value="uvowelsignsinh"/>
+        </Coverage>
+        <!-- ChainPosRuleSetCount=2 -->
+        <ChainPosRuleSet index="0">
+          <!-- ChainPosRuleCount=1 -->
+          <ChainPosRule index="0">
+            <!-- BacktrackGlyphCount=1 -->
+            <Backtrack index="0" value="rakarsinh"/>
+            <!-- InputGlyphCount=1 -->
+            <!-- LookAheadGlyphCount=0 -->
+            <!-- PosCount=1 -->
+            <PosLookupRecord index="0">
+              <SequenceIndex value="0"/>
+              <LookupListIndex value="1"/>
+            </PosLookupRecord>
+          </ChainPosRule>
+        </ChainPosRuleSet>
+        <ChainPosRuleSet index="1">
+          <!-- ChainPosRuleCount=1 -->
+          <ChainPosRule index="0">
+            <!-- BacktrackGlyphCount=1 -->
+            <Backtrack index="0" value="rakarsinh"/>
+            <!-- InputGlyphCount=1 -->
+            <!-- LookAheadGlyphCount=0 -->
+            <!-- PosCount=1 -->
+            <PosLookupRecord index="0">
+              <SequenceIndex value="0"/>
+              <LookupListIndex value="1"/>
+            </PosLookupRecord>
+          </ChainPosRule>
+        </ChainPosRuleSet>
+      </ChainContextPos>
+    </Lookup>
+    <Lookup index="1" empty="1"/>
+  </LookupList>
+</GPOS>
diff --git a/Tests/mtiLib/data/mti/chained-glyph.ttx.GSUB b/Tests/mtiLib/data/mti/chained-glyph.ttx.GSUB
new file mode 100644
index 0000000..67aa902
--- /dev/null
+++ b/Tests/mtiLib/data/mti/chained-glyph.ttx.GSUB
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GSUB>
+  <Version value="0x00010000"/>
+  <LookupList>
+    <!-- LookupCount=2 -->
+    <Lookup index="0">
+      <LookupType value="6"/>
+      <LookupFlag value="512"/>
+      <!-- SubTableCount=1 -->
+      <ChainContextSubst index="0" Format="1">
+        <Coverage Format="1">
+          <Glyph value="uuvowelsignsinh"/>
+          <Glyph value="uvowelsignsinh"/>
+        </Coverage>
+        <!-- ChainSubRuleSetCount=2 -->
+        <ChainSubRuleSet index="0">
+          <!-- ChainSubRuleCount=1 -->
+          <ChainSubRule index="0">
+            <!-- BacktrackGlyphCount=1 -->
+            <Backtrack index="0" value="rakarsinh"/>
+            <!-- InputGlyphCount=1 -->
+            <!-- LookAheadGlyphCount=0 -->
+            <!-- SubstCount=1 -->
+            <SubstLookupRecord index="0">
+              <SequenceIndex value="0"/>
+              <LookupListIndex value="1"/>
+            </SubstLookupRecord>
+          </ChainSubRule>
+        </ChainSubRuleSet>
+        <ChainSubRuleSet index="1">
+          <!-- ChainSubRuleCount=1 -->
+          <ChainSubRule index="0">
+            <!-- BacktrackGlyphCount=1 -->
+            <Backtrack index="0" value="rakarsinh"/>
+            <!-- InputGlyphCount=1 -->
+            <!-- LookAheadGlyphCount=0 -->
+            <!-- SubstCount=1 -->
+            <SubstLookupRecord index="0">
+              <SequenceIndex value="0"/>
+              <LookupListIndex value="1"/>
+            </SubstLookupRecord>
+          </ChainSubRule>
+        </ChainSubRuleSet>
+      </ChainContextSubst>
+    </Lookup>
+    <Lookup index="1" empty="1"/>
+  </LookupList>
+</GSUB>
diff --git a/Tests/mtiLib/data/mti/chained-glyph.txt b/Tests/mtiLib/data/mti/chained-glyph.txt
new file mode 100644
index 0000000..ca0f304
--- /dev/null
+++ b/Tests/mtiLib/data/mti/chained-glyph.txt
@@ -0,0 +1,11 @@
+lookup	raucontext-sinh	chained
+
+markattachmenttype	2
+
+glyph	rakarsinh	uvowelsignsinh		1,u2aelow-sinh
+glyph	rakarsinh	uuvowelsignsinh		1,u2aelow-sinh
+
+lookup end
+
+lookup	u2aelow-sinh	single
+lookup end
diff --git a/Tests/mtiLib/data/mti/chainedclass.ttx.GSUB b/Tests/mtiLib/data/mti/chainedclass.ttx.GSUB
new file mode 100644
index 0000000..cfa391f
--- /dev/null
+++ b/Tests/mtiLib/data/mti/chainedclass.ttx.GSUB
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GSUB>
+  <Version value="0x00010000"/>
+  <LookupList>
+    <!-- LookupCount=2 -->
+    <Lookup index="0">
+      <LookupType value="6"/>
+      <LookupFlag value="0"/>
+      <!-- SubTableCount=1 -->
+      <ChainContextSubst index="0" Format="2">
+        <Coverage Format="1">
+          <Glyph value="uvowelsignkannada"/>
+          <Glyph value="uuvowelsignkannada"/>
+        </Coverage>
+        <BacktrackClassDef Format="2">
+          <ClassDef glyph="pakannada" class="1"/>
+          <ClassDef glyph="pevowelkannada" class="1"/>
+          <ClassDef glyph="phakannada" class="1"/>
+          <ClassDef glyph="phevowelkannada" class="1"/>
+          <ClassDef glyph="vakannada" class="1"/>
+          <ClassDef glyph="vevowelkannada" class="1"/>
+        </BacktrackClassDef>
+        <InputClassDef Format="1">
+          <ClassDef glyph="uuvowelsignkannada" class="1"/>
+          <ClassDef glyph="uvowelsignkannada" class="1"/>
+        </InputClassDef>
+        <!-- ChainSubClassSetCount=2 -->
+        <ChainSubClassSet index="0" empty="1"/>
+        <ChainSubClassSet index="1">
+          <!-- ChainSubClassRuleCount=1 -->
+          <ChainSubClassRule index="0">
+            <!-- BacktrackGlyphCount=1 -->
+            <Backtrack index="0" value="1"/>
+            <!-- InputGlyphCount=1 -->
+            <!-- LookAheadGlyphCount=0 -->
+            <!-- SubstCount=1 -->
+            <SubstLookupRecord index="0">
+              <SequenceIndex value="0"/>
+              <LookupListIndex value="1"/>
+            </SubstLookupRecord>
+          </ChainSubClassRule>
+        </ChainSubClassSet>
+      </ChainContextSubst>
+    </Lookup>
+    <Lookup index="1">
+      <LookupType value="1"/>
+      <LookupFlag value="0"/>
+      <!-- SubTableCount=1 -->
+      <SingleSubst index="0" Format="1">
+        <Substitution in="uuvowelsignkannada" out="uuvowelsignaltkannada"/>
+        <Substitution in="uvowelsignkannada" out="uvowelsignaltkannada"/>
+      </SingleSubst>
+    </Lookup>
+  </LookupList>
+</GSUB>
diff --git a/Tests/mtiLib/data/mti/chainedclass.txt b/Tests/mtiLib/data/mti/chainedclass.txt
new file mode 100644
index 0000000..6c73c6e
--- /dev/null
+++ b/Tests/mtiLib/data/mti/chainedclass.txt
Binary files differ
diff --git a/Tests/mtiLib/data/mti/chainedcoverage.ttx.GSUB b/Tests/mtiLib/data/mti/chainedcoverage.ttx.GSUB
new file mode 100644
index 0000000..7c807a4
--- /dev/null
+++ b/Tests/mtiLib/data/mti/chainedcoverage.ttx.GSUB
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GSUB>
+  <Version value="0x00010000"/>
+  <LookupList>
+    <!-- LookupCount=2 -->
+    <Lookup index="0">
+      <LookupType value="6"/>
+      <LookupFlag value="0"/>
+      <!-- SubTableCount=1 -->
+      <ChainContextSubst index="0" Format="3">
+        <!-- BacktrackGlyphCount=1 -->
+        <BacktrackCoverage index="0" Format="2">
+          <Glyph value="zero"/>
+          <Glyph value="one"/>
+          <Glyph value="two"/>
+          <Glyph value="three"/>
+          <Glyph value="four"/>
+          <Glyph value="five"/>
+          <Glyph value="six"/>
+          <Glyph value="seven"/>
+          <Glyph value="eight"/>
+          <Glyph value="nine"/>
+        </BacktrackCoverage>
+        <!-- InputGlyphCount=1 -->
+        <InputCoverage index="0" Format="1">
+          <Glyph value="slash"/>
+        </InputCoverage>
+        <!-- LookAheadGlyphCount=1 -->
+        <LookAheadCoverage index="0" Format="2">
+          <Glyph value="zero"/>
+          <Glyph value="one"/>
+          <Glyph value="two"/>
+          <Glyph value="three"/>
+          <Glyph value="four"/>
+          <Glyph value="five"/>
+          <Glyph value="six"/>
+          <Glyph value="seven"/>
+          <Glyph value="eight"/>
+          <Glyph value="nine"/>
+        </LookAheadCoverage>
+        <!-- SubstCount=1 -->
+        <SubstLookupRecord index="0">
+          <SequenceIndex value="0"/>
+          <LookupListIndex value="1"/>
+        </SubstLookupRecord>
+      </ChainContextSubst>
+    </Lookup>
+    <Lookup index="1">
+      <LookupType value="1"/>
+      <LookupFlag value="0"/>
+      <!-- SubTableCount=1 -->
+      <SingleSubst index="0" Format="1">
+        <Substitution in="slash" out="fraction"/>
+      </SingleSubst>
+    </Lookup>
+  </LookupList>
+</GSUB>
diff --git a/Tests/mtiLib/data/mti/chainedcoverage.txt b/Tests/mtiLib/data/mti/chainedcoverage.txt
new file mode 100644
index 0000000..b07d939
--- /dev/null
+++ b/Tests/mtiLib/data/mti/chainedcoverage.txt
Binary files differ
diff --git a/Tests/mtiLib/data/mti/cmap.ttx b/Tests/mtiLib/data/mti/cmap.ttx
new file mode 100644
index 0000000..9c1931b
--- /dev/null
+++ b/Tests/mtiLib/data/mti/cmap.ttx
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<cmap>
+  <tableVersion version="0"/>
+  <cmap_format_4 platformID="0" platEncID="3" language="0">
+    <map code="0x0" name="null"/><!-- ???? -->
+    <map code="0xd" name="CR"/><!-- ???? -->
+    <map code="0x20" name="space"/><!-- SPACE -->
+    <map code="0x21" name="exclam"/><!-- EXCLAMATION MARK -->
+    <map code="0x22" name="quotedbl"/><!-- QUOTATION MARK -->
+    <map code="0x23" name="numbersign"/><!-- NUMBER SIGN -->
+    <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+    <map code="0x42" name="B"/><!-- LATIN CAPITAL LETTER B -->
+    <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+    <map code="0x62" name="b"/><!-- LATIN SMALL LETTER B -->
+  </cmap_format_4>
+  <cmap_format_6 platformID="1" platEncID="0" language="0">
+    <map code="0x0" name="null"/>
+    <map code="0xd" name="CR"/>
+    <map code="0x20" name="space"/>
+    <map code="0x21" name="exclam"/>
+    <map code="0x22" name="quotedbl"/>
+    <map code="0x23" name="numbersign"/>
+    <map code="0x41" name="A"/>
+    <map code="0x42" name="B"/>
+    <map code="0x61" name="a"/>
+    <map code="0x62" name="b"/>
+  </cmap_format_6>
+  <cmap_format_4 platformID="3" platEncID="1" language="0">
+    <map code="0x0" name="null"/><!-- ???? -->
+    <map code="0xd" name="CR"/><!-- ???? -->
+    <map code="0x20" name="space"/><!-- SPACE -->
+    <map code="0x21" name="exclam"/><!-- EXCLAMATION MARK -->
+    <map code="0x22" name="quotedbl"/><!-- QUOTATION MARK -->
+    <map code="0x23" name="numbersign"/><!-- NUMBER SIGN -->
+    <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+    <map code="0x42" name="B"/><!-- LATIN CAPITAL LETTER B -->
+    <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+    <map code="0x62" name="b"/><!-- LATIN SMALL LETTER B -->
+  </cmap_format_4>
+</cmap>
diff --git a/Tests/mtiLib/data/mti/cmap.ttx.cmap b/Tests/mtiLib/data/mti/cmap.ttx.cmap
new file mode 100644
index 0000000..9c1931b
--- /dev/null
+++ b/Tests/mtiLib/data/mti/cmap.ttx.cmap
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<cmap>
+  <tableVersion version="0"/>
+  <cmap_format_4 platformID="0" platEncID="3" language="0">
+    <map code="0x0" name="null"/><!-- ???? -->
+    <map code="0xd" name="CR"/><!-- ???? -->
+    <map code="0x20" name="space"/><!-- SPACE -->
+    <map code="0x21" name="exclam"/><!-- EXCLAMATION MARK -->
+    <map code="0x22" name="quotedbl"/><!-- QUOTATION MARK -->
+    <map code="0x23" name="numbersign"/><!-- NUMBER SIGN -->
+    <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+    <map code="0x42" name="B"/><!-- LATIN CAPITAL LETTER B -->
+    <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+    <map code="0x62" name="b"/><!-- LATIN SMALL LETTER B -->
+  </cmap_format_4>
+  <cmap_format_6 platformID="1" platEncID="0" language="0">
+    <map code="0x0" name="null"/>
+    <map code="0xd" name="CR"/>
+    <map code="0x20" name="space"/>
+    <map code="0x21" name="exclam"/>
+    <map code="0x22" name="quotedbl"/>
+    <map code="0x23" name="numbersign"/>
+    <map code="0x41" name="A"/>
+    <map code="0x42" name="B"/>
+    <map code="0x61" name="a"/>
+    <map code="0x62" name="b"/>
+  </cmap_format_6>
+  <cmap_format_4 platformID="3" platEncID="1" language="0">
+    <map code="0x0" name="null"/><!-- ???? -->
+    <map code="0xd" name="CR"/><!-- ???? -->
+    <map code="0x20" name="space"/><!-- SPACE -->
+    <map code="0x21" name="exclam"/><!-- EXCLAMATION MARK -->
+    <map code="0x22" name="quotedbl"/><!-- QUOTATION MARK -->
+    <map code="0x23" name="numbersign"/><!-- NUMBER SIGN -->
+    <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+    <map code="0x42" name="B"/><!-- LATIN CAPITAL LETTER B -->
+    <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+    <map code="0x62" name="b"/><!-- LATIN SMALL LETTER B -->
+  </cmap_format_4>
+</cmap>
diff --git a/Tests/mtiLib/data/mti/cmap.txt b/Tests/mtiLib/data/mti/cmap.txt
new file mode 100644
index 0000000..18760dd
--- /dev/null
+++ b/Tests/mtiLib/data/mti/cmap.txt
@@ -0,0 +1,52 @@
+Font Chef Table cmap
+
+cmap subtable 0
+platformID	0
+encodingID	3
+format	4
+language	0
+0x0000	null	\#1
+0x000D	CR	\#2
+0x0020	space	\#3
+0x0021	exclam	\#4
+0x0022	quotedbl	\#5
+0x0023	numbersign	\#6
+0x0041	A
+0x0042	B
+0x0061	a
+0x0062	b
+end subtable
+
+cmap subtable 1
+platformID	1
+encodingID	0
+format	6
+language	0
+0x0000	null	\#1
+0x000D	CR	\#2
+0x0020	space	\#3
+0x0021	exclam	\#4
+0x0022	quotedbl	\#5
+0x0023	numbersign	\#6
+0x0041	A
+0x0042	B
+0x0061	a
+0x0062	b
+end subtable
+
+cmap subtable 2
+platformID	3
+encodingID	1
+format	4
+language	0
+0x0000	null	\#1
+0x000D	CR	\#2
+0x0020	space	\#3
+0x0021	exclam	\#4
+0x0022	quotedbl	\#5
+0x0023	numbersign	\#6
+0x0041	A
+0x0042	B
+0x0061	a
+0x0062	b
+end subtable
diff --git a/Tests/mtiLib/data/mti/context-glyph.txt b/Tests/mtiLib/data/mti/context-glyph.txt
new file mode 100644
index 0000000..59863eb
--- /dev/null
+++ b/Tests/mtiLib/data/mti/context-glyph.txt
@@ -0,0 +1,14 @@
+
+lookup	7 context
+
+RightToLeft	no
+IgnoreBaseGlyphs	no
+IgnoreLigatures	no
+IgnoreMarks	no
+
+glyph gabardeva, viramadeva, radeva	2, 8
+glyph jabardeva, viramadeva, radeva	2, 8
+glyph ddabardeva, viramadeva, radeva	2, 8
+glyph babardeva, viramadeva, radeva	2, 8
+
+lookup end
diff --git a/Tests/mtiLib/data/mti/contextclass.txt b/Tests/mtiLib/data/mti/contextclass.txt
new file mode 100644
index 0000000..4f5d3ec
--- /dev/null
+++ b/Tests/mtiLib/data/mti/contextclass.txt
Binary files differ
diff --git a/Tests/mtiLib/data/mti/contextcoverage.txt b/Tests/mtiLib/data/mti/contextcoverage.txt
new file mode 100644
index 0000000..a22d7b2
--- /dev/null
+++ b/Tests/mtiLib/data/mti/contextcoverage.txt
Binary files differ
diff --git a/Tests/mtiLib/data/mti/featuretable.txt b/Tests/mtiLib/data/mti/featuretable.txt
new file mode 100644
index 0000000..af13d72
--- /dev/null
+++ b/Tests/mtiLib/data/mti/featuretable.txt
Binary files differ
diff --git a/Tests/mtiLib/data/mti/gdefattach.ttx.GDEF b/Tests/mtiLib/data/mti/gdefattach.ttx.GDEF
new file mode 100644
index 0000000..cc4c70d
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gdefattach.ttx.GDEF
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GDEF>
+  <Version value="0x00010000"/>
+  <AttachList>
+    <Coverage Format="1">
+      <Glyph value="A"/>
+      <Glyph value="B"/>
+    </Coverage>
+    <!-- GlyphCount=2 -->
+    <AttachPoint index="0">
+      <!-- PointCount=1 -->
+      <PointIndex index="0" value="5"/>
+    </AttachPoint>
+    <AttachPoint index="1">
+      <!-- PointCount=3 -->
+      <PointIndex index="0" value="0"/>
+      <PointIndex index="1" value="3"/>
+      <PointIndex index="2" value="9"/>
+    </AttachPoint>
+  </AttachList>
+</GDEF>
diff --git a/Tests/mtiLib/data/mti/gdefattach.txt b/Tests/mtiLib/data/mti/gdefattach.txt
new file mode 100644
index 0000000..a1859cd
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gdefattach.txt
@@ -0,0 +1,7 @@
+
+attachment list begin
+
+A	5
+B	0	3	9
+
+attachment list end
diff --git a/Tests/mtiLib/data/mti/gdefclasses.ttx.GDEF b/Tests/mtiLib/data/mti/gdefclasses.ttx.GDEF
new file mode 100644
index 0000000..e298c28
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gdefclasses.ttx.GDEF
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GDEF>
+  <Version value="0x00010000"/>
+  <GlyphClassDef Format="1">
+    <ClassDef glyph="A" class="1"/>
+    <ClassDef glyph="C" class="1"/>
+    <ClassDef glyph="acute" class="3"/>
+    <ClassDef glyph="breve" class="3"/>
+    <ClassDef glyph="fi" class="2"/>
+    <ClassDef glyph="fl" class="2"/>
+  </GlyphClassDef>
+</GDEF>
diff --git a/Tests/mtiLib/data/mti/gdefclasses.txt b/Tests/mtiLib/data/mti/gdefclasses.txt
new file mode 100644
index 0000000..e22a609
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gdefclasses.txt
Binary files differ
diff --git a/Tests/mtiLib/data/mti/gdefligcaret.ttx.GDEF b/Tests/mtiLib/data/mti/gdefligcaret.ttx.GDEF
new file mode 100644
index 0000000..48f7347
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gdefligcaret.ttx.GDEF
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GDEF>
+  <Version value="0x00010000"/>
+  <LigCaretList>
+    <Coverage Format="1">
+      <Glyph value="uniFB01"/>
+      <Glyph value="ffi"/>
+    </Coverage>
+    <!-- LigGlyphCount=2 -->
+    <LigGlyph index="0">
+      <!-- CaretCount=1 -->
+      <CaretValue index="0" Format="1">
+        <Coordinate value="236"/>
+      </CaretValue>
+    </LigGlyph>
+    <LigGlyph index="1">
+      <!-- CaretCount=2 -->
+      <CaretValue index="0" Format="1">
+        <Coordinate value="210"/>
+      </CaretValue>
+      <CaretValue index="1" Format="1">
+        <Coordinate value="450"/>
+      </CaretValue>
+    </LigGlyph>
+  </LigCaretList>
+</GDEF>
diff --git a/Tests/mtiLib/data/mti/gdefligcaret.txt b/Tests/mtiLib/data/mti/gdefligcaret.txt
new file mode 100644
index 0000000..926cdb6
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gdefligcaret.txt
@@ -0,0 +1,7 @@
+
+carets begin
+
+uniFB01	1	236
+ffi	2	210	450
+
+carets end
diff --git a/Tests/mtiLib/data/mti/gdefmarkattach.ttx.GDEF b/Tests/mtiLib/data/mti/gdefmarkattach.ttx.GDEF
new file mode 100644
index 0000000..b6cbe10
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gdefmarkattach.ttx.GDEF
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GDEF>
+  <Version value="0x00010000"/>
+  <MarkAttachClassDef Format="1">
+    <ClassDef glyph="breve" class="1"/>
+    <ClassDef glyph="commaacent" class="2"/>
+    <ClassDef glyph="dotbelow" class="2"/>
+    <ClassDef glyph="grave" class="1"/>
+  </MarkAttachClassDef>
+</GDEF>
diff --git a/Tests/mtiLib/data/mti/gdefmarkattach.txt b/Tests/mtiLib/data/mti/gdefmarkattach.txt
new file mode 100644
index 0000000..b729927
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gdefmarkattach.txt
Binary files differ
diff --git a/Tests/mtiLib/data/mti/gdefmarkfilter.ttx.GDEF b/Tests/mtiLib/data/mti/gdefmarkfilter.ttx.GDEF
new file mode 100644
index 0000000..3b23481
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gdefmarkfilter.ttx.GDEF
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GDEF>
+  <Version value="0x00010002"/>
+  <MarkGlyphSetsDef>
+    <MarkSetTableFormat value="1"/>
+    <!-- MarkSetCount=4 -->
+    <Coverage index="0" empty="1"/>
+    <Coverage index="1" Format="1">
+      <Glyph value="breve"/>
+      <Glyph value="acute"/>
+      <Glyph value="dotabove"/>
+    </Coverage>
+    <Coverage index="2" Format="1">
+      <Glyph value="dotbelow"/>
+      <Glyph value="cedilla"/>
+      <Glyph value="commaaccent"/>
+    </Coverage>
+    <Coverage index="3" Format="1">
+      <Glyph value="dotbelow"/>
+      <Glyph value="dotabove"/>
+    </Coverage>
+  </MarkGlyphSetsDef>
+</GDEF>
diff --git a/Tests/mtiLib/data/mti/gdefmarkfilter.txt b/Tests/mtiLib/data/mti/gdefmarkfilter.txt
new file mode 100644
index 0000000..bbda48a
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gdefmarkfilter.txt
Binary files differ
diff --git a/Tests/mtiLib/data/mti/gposcursive.ttx.GPOS b/Tests/mtiLib/data/mti/gposcursive.ttx.GPOS
new file mode 100644
index 0000000..2965139
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gposcursive.ttx.GPOS
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GPOS>
+  <Version value="0x00010000"/>
+  <LookupList>
+    <!-- LookupCount=1 -->
+    <Lookup index="0">
+      <LookupType value="3"/>
+      <LookupFlag value="0"/>
+      <!-- SubTableCount=1 -->
+      <CursivePos index="0" Format="1">
+        <Coverage Format="1">
+          <Glyph value="A"/>
+          <Glyph value="B"/>
+        </Coverage>
+        <!-- EntryExitCount=2 -->
+        <EntryExitRecord index="0">
+          <EntryAnchor Format="2">
+            <XCoordinate value="560"/>
+            <YCoordinate value="1466"/>
+            <AnchorPoint value="1"/>
+          </EntryAnchor>
+          <ExitAnchor Format="2">
+            <XCoordinate value="769"/>
+            <YCoordinate value="1466"/>
+            <AnchorPoint value="2"/>
+          </ExitAnchor>
+        </EntryExitRecord>
+        <EntryExitRecord index="1">
+          <EntryAnchor Format="2">
+            <XCoordinate value="150"/>
+            <YCoordinate value="1466"/>
+            <AnchorPoint value="1"/>
+          </EntryAnchor>
+          <ExitAnchor Format="2">
+            <XCoordinate value="1186"/>
+            <YCoordinate value="1091"/>
+            <AnchorPoint value="6"/>
+          </ExitAnchor>
+        </EntryExitRecord>
+      </CursivePos>
+    </Lookup>
+  </LookupList>
+</GPOS>
diff --git a/Tests/mtiLib/data/mti/gposcursive.txt b/Tests/mtiLib/data/mti/gposcursive.txt
new file mode 100644
index 0000000..7668a82
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gposcursive.txt
Binary files differ
diff --git a/Tests/mtiLib/data/mti/gposkernset.ttx.GPOS b/Tests/mtiLib/data/mti/gposkernset.ttx.GPOS
new file mode 100644
index 0000000..edfea10
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gposkernset.ttx.GPOS
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GPOS>
+  <Version value="0x00010000"/>
+  <LookupList>
+    <!-- LookupCount=1 -->
+    <Lookup index="0">
+      <LookupType value="2"/>
+      <LookupFlag value="0"/>
+      <!-- SubTableCount=2 -->
+      <PairPos index="0" Format="1">
+        <Coverage Format="1">
+          <Glyph value="Acircumflex"/>
+          <Glyph value="T"/>
+        </Coverage>
+        <ValueFormat1 value="4"/>
+        <ValueFormat2 value="0"/>
+        <!-- PairSetCount=2 -->
+        <PairSet index="0">
+          <!-- PairValueCount=1 -->
+          <PairValueRecord index="0">
+            <SecondGlyph value="V"/>
+            <Value1 XAdvance="-10"/>
+          </PairValueRecord>
+        </PairSet>
+        <PairSet index="1">
+          <!-- PairValueCount=1 -->
+          <PairValueRecord index="0">
+            <SecondGlyph value="acircumflex"/>
+            <Value1 XAdvance="-18"/>
+          </PairValueRecord>
+        </PairSet>
+      </PairPos>
+      <PairPos index="1" Format="2">
+        <Coverage Format="1">
+          <Glyph value="A"/>
+          <Glyph value="Acircumflex"/>
+          <Glyph value="T"/>
+          <Glyph value="Aacute"/>
+          <Glyph value="Agrave"/>
+          <Glyph value="O"/>
+          <Glyph value="Oacute"/>
+          <Glyph value="Ograve"/>
+          <Glyph value="Ocircumflex"/>
+        </Coverage>
+        <ValueFormat1 value="4"/>
+        <ValueFormat2 value="0"/>
+        <ClassDef1 Format="2">
+          <ClassDef glyph="A" class="1"/>
+          <ClassDef glyph="Aacute" class="1"/>
+          <ClassDef glyph="Acircumflex" class="1"/>
+          <ClassDef glyph="Agrave" class="1"/>
+          <ClassDef glyph="O" class="2"/>
+          <ClassDef glyph="Oacute" class="2"/>
+          <ClassDef glyph="Ocircumflex" class="2"/>
+          <ClassDef glyph="Ograve" class="2"/>
+          <ClassDef glyph="T" class="3"/>
+        </ClassDef1>
+        <ClassDef2 Format="2">
+          <ClassDef glyph="V" class="1"/>
+          <ClassDef glyph="a" class="2"/>
+          <ClassDef glyph="aacute" class="2"/>
+          <ClassDef glyph="acircumflex" class="2"/>
+          <ClassDef glyph="agrave" class="2"/>
+        </ClassDef2>
+        <!-- Class1Count=4 -->
+        <!-- Class2Count=3 -->
+        <Class1Record index="0">
+          <Class2Record index="0">
+            <Value1 XAdvance="0"/>
+          </Class2Record>
+          <Class2Record index="1">
+            <Value1 XAdvance="0"/>
+          </Class2Record>
+          <Class2Record index="2">
+            <Value1 XAdvance="0"/>
+          </Class2Record>
+        </Class1Record>
+        <Class1Record index="1">
+          <Class2Record index="0">
+            <Value1 XAdvance="0"/>
+          </Class2Record>
+          <Class2Record index="1">
+            <Value1 XAdvance="-50"/>
+          </Class2Record>
+          <Class2Record index="2">
+            <Value1 XAdvance="0"/>
+          </Class2Record>
+        </Class1Record>
+        <Class1Record index="2">
+          <Class2Record index="0">
+            <Value1 XAdvance="0"/>
+          </Class2Record>
+          <Class2Record index="1">
+            <Value1 XAdvance="-10"/>
+          </Class2Record>
+          <Class2Record index="2">
+            <Value1 XAdvance="0"/>
+          </Class2Record>
+        </Class1Record>
+        <Class1Record index="3">
+          <Class2Record index="0">
+            <Value1 XAdvance="0"/>
+          </Class2Record>
+          <Class2Record index="1">
+            <Value1 XAdvance="0"/>
+          </Class2Record>
+          <Class2Record index="2">
+            <Value1 XAdvance="-35"/>
+          </Class2Record>
+        </Class1Record>
+      </PairPos>
+    </Lookup>
+  </LookupList>
+</GPOS>
diff --git a/Tests/mtiLib/data/mti/gposkernset.txt b/Tests/mtiLib/data/mti/gposkernset.txt
new file mode 100644
index 0000000..20571ce
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gposkernset.txt
@@ -0,0 +1,37 @@
+lookup	 0 	kernset
+
+RightToLeft	no
+IgnoreBaseGlyphs	no
+IgnoreLigatures	no
+IgnoreMarks	no
+
+left x advance	Acircumflex	V	-10
+left x advance	T	acircumflex	-18
+% Below are the class definitions. Above are the exceptions.
+subtable end
+
+firstclass definition begin
+A	1
+Aacute	1
+Agrave	1
+Acircumflex	1
+O	2
+Oacute	2
+Ograve	2
+Ocircumflex	2
+T	3
+class definition end
+
+secondclass definition begin
+V	1
+a	2
+aacute	2
+agrave	2
+acircumflex	2
+class definition end
+
+left x advance	 1	 1	-50
+left x advance	 2	 1	-10
+left x advance	 3	 2	-35
+
+lookup end
diff --git a/Tests/mtiLib/data/mti/gposmarktobase.ttx.GPOS b/Tests/mtiLib/data/mti/gposmarktobase.ttx.GPOS
new file mode 100644
index 0000000..b0a74e7
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gposmarktobase.ttx.GPOS
@@ -0,0 +1,213 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GPOS>
+  <Version value="0x00010000"/>
+  <LookupList>
+    <!-- LookupCount=1 -->
+    <Lookup index="0">
+      <LookupType value="4"/>
+      <LookupFlag value="0"/>
+      <!-- SubTableCount=1 -->
+      <MarkBasePos index="0" Format="1">
+        <MarkCoverage Format="2">
+          <Glyph value="aimatrabindigurmukhi"/>
+          <Glyph value="aimatragurmukhi"/>
+          <Glyph value="aimatratippigurmukhi"/>
+          <Glyph value="aumatrabindigurmukhi"/>
+          <Glyph value="aumatragurmukhi"/>
+          <Glyph value="bindigurmukhi"/>
+          <Glyph value="eematrabindigurmukhi"/>
+          <Glyph value="eematragurmukhi"/>
+          <Glyph value="eematratippigurmukhi"/>
+          <Glyph value="oomatrabindigurmukhi"/>
+          <Glyph value="oomatragurmukhi"/>
+          <Glyph value="oomatratippigurmukhi"/>
+        </MarkCoverage>
+        <BaseCoverage Format="2">
+          <Glyph value="lagurmukhi"/>
+          <Glyph value="lanuktagurmukhi"/>
+          <Glyph value="nagurmukhi"/>
+          <Glyph value="nanuktagurmukhi"/>
+          <Glyph value="ngagurmukhi"/>
+          <Glyph value="nganuktagurmukhi"/>
+          <Glyph value="nnagurmukhi"/>
+          <Glyph value="nnanuktagurmukhi"/>
+          <Glyph value="tthagurmukhi"/>
+          <Glyph value="tthanuktagurmukhi"/>
+        </BaseCoverage>
+        <!-- ClassCount=1 -->
+        <MarkArray>
+          <!-- MarkCount=12 -->
+          <MarkRecord index="0">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="-184"/>
+              <YCoordinate value="1183"/>
+              <AnchorPoint value="40"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="1">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="-184"/>
+              <YCoordinate value="1183"/>
+              <AnchorPoint value="28"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="2">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="-184"/>
+              <YCoordinate value="1183"/>
+              <AnchorPoint value="28"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="3">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="-184"/>
+              <YCoordinate value="1183"/>
+              <AnchorPoint value="54"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="4">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="-184"/>
+              <YCoordinate value="1183"/>
+              <AnchorPoint value="38"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="5">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="-184"/>
+              <YCoordinate value="1183"/>
+              <AnchorPoint value="16"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="6">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="-184"/>
+              <YCoordinate value="1183"/>
+              <AnchorPoint value="27"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="7">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="-184"/>
+              <YCoordinate value="1183"/>
+              <AnchorPoint value="15"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="8">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="-184"/>
+              <YCoordinate value="1183"/>
+              <AnchorPoint value="15"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="9">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="-184"/>
+              <YCoordinate value="1183"/>
+              <AnchorPoint value="36"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="10">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="-184"/>
+              <YCoordinate value="1183"/>
+              <AnchorPoint value="20"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="11">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="-184"/>
+              <YCoordinate value="1183"/>
+              <AnchorPoint value="20"/>
+            </MarkAnchor>
+          </MarkRecord>
+        </MarkArray>
+        <BaseArray>
+          <!-- BaseCount=10 -->
+          <BaseRecord index="0">
+            <BaseAnchor index="0" Format="2">
+              <XCoordinate value="996"/>
+              <YCoordinate value="1183"/>
+              <AnchorPoint value="46"/>
+            </BaseAnchor>
+          </BaseRecord>
+          <BaseRecord index="1">
+            <BaseAnchor index="0" Format="2">
+              <XCoordinate value="996"/>
+              <YCoordinate value="1183"/>
+              <AnchorPoint value="46"/>
+            </BaseAnchor>
+          </BaseRecord>
+          <BaseRecord index="2">
+            <BaseAnchor index="0" Format="2">
+              <XCoordinate value="816"/>
+              <YCoordinate value="1183"/>
+              <AnchorPoint value="32"/>
+            </BaseAnchor>
+          </BaseRecord>
+          <BaseRecord index="3">
+            <BaseAnchor index="0" Format="2">
+              <XCoordinate value="816"/>
+              <YCoordinate value="1183"/>
+              <AnchorPoint value="32"/>
+            </BaseAnchor>
+          </BaseRecord>
+          <BaseRecord index="4">
+            <BaseAnchor index="0" Format="2">
+              <XCoordinate value="816"/>
+              <YCoordinate value="1183"/>
+              <AnchorPoint value="41"/>
+            </BaseAnchor>
+          </BaseRecord>
+          <BaseRecord index="5">
+            <BaseAnchor index="0" Format="2">
+              <XCoordinate value="816"/>
+              <YCoordinate value="1183"/>
+              <AnchorPoint value="41"/>
+            </BaseAnchor>
+          </BaseRecord>
+          <BaseRecord index="6">
+            <BaseAnchor index="0" Format="2">
+              <XCoordinate value="976"/>
+              <YCoordinate value="1183"/>
+              <AnchorPoint value="35"/>
+            </BaseAnchor>
+          </BaseRecord>
+          <BaseRecord index="7">
+            <BaseAnchor index="0" Format="2">
+              <XCoordinate value="976"/>
+              <YCoordinate value="1183"/>
+              <AnchorPoint value="35"/>
+            </BaseAnchor>
+          </BaseRecord>
+          <BaseRecord index="8">
+            <BaseAnchor index="0" Format="2">
+              <XCoordinate value="816"/>
+              <YCoordinate value="1183"/>
+              <AnchorPoint value="30"/>
+            </BaseAnchor>
+          </BaseRecord>
+          <BaseRecord index="9">
+            <BaseAnchor index="0" Format="2">
+              <XCoordinate value="816"/>
+              <YCoordinate value="1183"/>
+              <AnchorPoint value="30"/>
+            </BaseAnchor>
+          </BaseRecord>
+        </BaseArray>
+      </MarkBasePos>
+    </Lookup>
+  </LookupList>
+</GPOS>
diff --git a/Tests/mtiLib/data/mti/gposmarktobase.txt b/Tests/mtiLib/data/mti/gposmarktobase.txt
new file mode 100644
index 0000000..8b177f3
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gposmarktobase.txt
Binary files differ
diff --git a/Tests/mtiLib/data/mti/gpospairclass.ttx.GPOS b/Tests/mtiLib/data/mti/gpospairclass.ttx.GPOS
new file mode 100644
index 0000000..567b2a7
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gpospairclass.ttx.GPOS
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GPOS>
+  <Version value="0x00010000"/>
+  <LookupList>
+    <!-- LookupCount=1 -->
+    <Lookup index="0">
+      <LookupType value="2"/>
+      <LookupFlag value="0"/>
+      <!-- SubTableCount=1 -->
+      <PairPos index="0" Format="2">
+        <Coverage Format="1">
+          <Glyph value="A"/>
+          <Glyph value="Acircumflex"/>
+          <Glyph value="T"/>
+          <Glyph value="Aacute"/>
+          <Glyph value="Agrave"/>
+          <Glyph value="O"/>
+          <Glyph value="Oacute"/>
+          <Glyph value="Ograve"/>
+          <Glyph value="Ocircumflex"/>
+        </Coverage>
+        <ValueFormat1 value="4"/>
+        <ValueFormat2 value="0"/>
+        <ClassDef1 Format="2">
+          <ClassDef glyph="A" class="1"/>
+          <ClassDef glyph="Aacute" class="1"/>
+          <ClassDef glyph="Acircumflex" class="1"/>
+          <ClassDef glyph="Agrave" class="1"/>
+          <ClassDef glyph="O" class="2"/>
+          <ClassDef glyph="Oacute" class="2"/>
+          <ClassDef glyph="Ocircumflex" class="2"/>
+          <ClassDef glyph="Ograve" class="2"/>
+          <ClassDef glyph="T" class="3"/>
+        </ClassDef1>
+        <ClassDef2 Format="2">
+          <ClassDef glyph="V" class="1"/>
+          <ClassDef glyph="a" class="2"/>
+          <ClassDef glyph="aacute" class="2"/>
+          <ClassDef glyph="acircumflex" class="2"/>
+          <ClassDef glyph="agrave" class="2"/>
+        </ClassDef2>
+        <!-- Class1Count=4 -->
+        <!-- Class2Count=3 -->
+        <Class1Record index="0">
+          <Class2Record index="0">
+            <Value1 XAdvance="0"/>
+          </Class2Record>
+          <Class2Record index="1">
+            <Value1 XAdvance="0"/>
+          </Class2Record>
+          <Class2Record index="2">
+            <Value1 XAdvance="0"/>
+          </Class2Record>
+        </Class1Record>
+        <Class1Record index="1">
+          <Class2Record index="0">
+            <Value1 XAdvance="0"/>
+          </Class2Record>
+          <Class2Record index="1">
+            <Value1 XAdvance="-50"/>
+          </Class2Record>
+          <Class2Record index="2">
+            <Value1 XAdvance="0"/>
+          </Class2Record>
+        </Class1Record>
+        <Class1Record index="2">
+          <Class2Record index="0">
+            <Value1 XAdvance="0"/>
+          </Class2Record>
+          <Class2Record index="1">
+            <Value1 XAdvance="-10"/>
+          </Class2Record>
+          <Class2Record index="2">
+            <Value1 XAdvance="0"/>
+          </Class2Record>
+        </Class1Record>
+        <Class1Record index="3">
+          <Class2Record index="0">
+            <Value1 XAdvance="0"/>
+          </Class2Record>
+          <Class2Record index="1">
+            <Value1 XAdvance="0"/>
+          </Class2Record>
+          <Class2Record index="2">
+            <Value1 XAdvance="-35"/>
+          </Class2Record>
+        </Class1Record>
+      </PairPos>
+    </Lookup>
+  </LookupList>
+</GPOS>
diff --git a/Tests/mtiLib/data/mti/gpospairclass.txt b/Tests/mtiLib/data/mti/gpospairclass.txt
new file mode 100644
index 0000000..21a47fd
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gpospairclass.txt
@@ -0,0 +1,27 @@
+lookup	0	pair
+
+firstclass definition begin
+A	1
+Aacute	1
+Agrave	1
+Acircumflex	1
+O	2
+Oacute	2
+Ograve	2
+Ocircumflex	2
+T	3
+class definition end
+
+secondclass definition begin
+V	1
+a	2
+aacute	2
+agrave	2
+acircumflex	2
+class definition end
+
+left x advance	 1	 1	-50
+left x advance	 2	 1	-10
+left x advance	 3	 2	-35
+
+lookup end
diff --git a/Tests/mtiLib/data/mti/gpospairglyph.ttx.GPOS b/Tests/mtiLib/data/mti/gpospairglyph.ttx.GPOS
new file mode 100644
index 0000000..ea0161b
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gpospairglyph.ttx.GPOS
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GPOS>
+  <Version value="0x00010000"/>
+  <LookupList>
+    <!-- LookupCount=1 -->
+    <Lookup index="0">
+      <LookupType value="2"/>
+      <LookupFlag value="0"/>
+      <!-- SubTableCount=1 -->
+      <PairPos index="0" Format="1">
+        <Coverage Format="1">
+          <Glyph value="A"/>
+          <Glyph value="Acircumflex"/>
+          <Glyph value="T"/>
+          <Glyph value="Aacute"/>
+          <Glyph value="Agrave"/>
+          <Glyph value="O"/>
+          <Glyph value="Oacute"/>
+          <Glyph value="Ograve"/>
+          <Glyph value="Ocircumflex"/>
+        </Coverage>
+        <ValueFormat1 value="4"/>
+        <ValueFormat2 value="0"/>
+        <!-- PairSetCount=9 -->
+        <PairSet index="0">
+          <!-- PairValueCount=1 -->
+          <PairValueRecord index="0">
+            <SecondGlyph value="V"/>
+            <Value1 XAdvance="-50"/>
+          </PairValueRecord>
+        </PairSet>
+        <PairSet index="1">
+          <!-- PairValueCount=1 -->
+          <PairValueRecord index="0">
+            <SecondGlyph value="V"/>
+            <Value1 XAdvance="-50"/>
+          </PairValueRecord>
+        </PairSet>
+        <PairSet index="2">
+          <!-- PairValueCount=4 -->
+          <PairValueRecord index="0">
+            <SecondGlyph value="a"/>
+            <Value1 XAdvance="-35"/>
+          </PairValueRecord>
+          <PairValueRecord index="1">
+            <SecondGlyph value="acircumflex"/>
+            <Value1 XAdvance="-35"/>
+          </PairValueRecord>
+          <PairValueRecord index="2">
+            <SecondGlyph value="aacute"/>
+            <Value1 XAdvance="-35"/>
+          </PairValueRecord>
+          <PairValueRecord index="3">
+            <SecondGlyph value="agrave"/>
+            <Value1 XAdvance="-35"/>
+          </PairValueRecord>
+        </PairSet>
+        <PairSet index="3">
+          <!-- PairValueCount=1 -->
+          <PairValueRecord index="0">
+            <SecondGlyph value="V"/>
+            <Value1 XAdvance="-50"/>
+          </PairValueRecord>
+        </PairSet>
+        <PairSet index="4">
+          <!-- PairValueCount=1 -->
+          <PairValueRecord index="0">
+            <SecondGlyph value="V"/>
+            <Value1 XAdvance="-50"/>
+          </PairValueRecord>
+        </PairSet>
+        <PairSet index="5">
+          <!-- PairValueCount=1 -->
+          <PairValueRecord index="0">
+            <SecondGlyph value="V"/>
+            <Value1 XAdvance="-10"/>
+          </PairValueRecord>
+        </PairSet>
+        <PairSet index="6">
+          <!-- PairValueCount=1 -->
+          <PairValueRecord index="0">
+            <SecondGlyph value="V"/>
+            <Value1 XAdvance="-10"/>
+          </PairValueRecord>
+        </PairSet>
+        <PairSet index="7">
+          <!-- PairValueCount=1 -->
+          <PairValueRecord index="0">
+            <SecondGlyph value="V"/>
+            <Value1 XAdvance="-10"/>
+          </PairValueRecord>
+        </PairSet>
+        <PairSet index="8">
+          <!-- PairValueCount=1 -->
+          <PairValueRecord index="0">
+            <SecondGlyph value="V"/>
+            <Value1 XAdvance="-10"/>
+          </PairValueRecord>
+        </PairSet>
+      </PairPos>
+    </Lookup>
+  </LookupList>
+</GPOS>
diff --git a/Tests/mtiLib/data/mti/gpospairglyph.txt b/Tests/mtiLib/data/mti/gpospairglyph.txt
new file mode 100644
index 0000000..b943ec0
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gpospairglyph.txt
@@ -0,0 +1,17 @@
+
+lookup	0	pair
+
+left x advance	A	V	-50
+left x advance	Aacute	V	-50
+left x advance	Agrave	V	-50
+left x advance	Acircumflex	V	-50
+left x advance	O	V	-10
+left x advance	Oacute	V	-10
+left x advance	Ograve	V	-10
+left x advance	Ocircumflex	V	-10
+left x advance	T	a	-35
+left x advance	T	aacute	-35
+left x advance	T	agrave	-35
+left x advance	T	acircumflex	-35
+
+lookup end
diff --git a/Tests/mtiLib/data/mti/gpossingle.ttx.GPOS b/Tests/mtiLib/data/mti/gpossingle.ttx.GPOS
new file mode 100644
index 0000000..adbb44f
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gpossingle.ttx.GPOS
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GPOS>
+  <Version value="0x00010000"/>
+  <LookupList>
+    <!-- LookupCount=1 -->
+    <Lookup index="0">
+      <LookupType value="1"/>
+      <LookupFlag value="0"/>
+      <!-- SubTableCount=1 -->
+      <SinglePos index="0" Format="1">
+        <Coverage Format="2">
+          <Glyph value="bsuperior"/>
+          <Glyph value="isuperior"/>
+          <Glyph value="vsuperior"/>
+          <Glyph value="wsuperior"/>
+          <Glyph value="periodsuperior"/>
+          <Glyph value="osuperior"/>
+          <Glyph value="tsuperior"/>
+          <Glyph value="dollarsuperior"/>
+          <Glyph value="fsuperior"/>
+          <Glyph value="gsuperior"/>
+          <Glyph value="zsuperior"/>
+          <Glyph value="dsuperior"/>
+          <Glyph value="psuperior"/>
+          <Glyph value="hsuperior"/>
+          <Glyph value="oesuperior"/>
+          <Glyph value="aesuperior"/>
+          <Glyph value="centsuperior"/>
+          <Glyph value="esuperior"/>
+          <Glyph value="lsuperior"/>
+          <Glyph value="qsuperior"/>
+          <Glyph value="csuperior"/>
+          <Glyph value="asuperior"/>
+          <Glyph value="commasuperior"/>
+          <Glyph value="xsuperior"/>
+          <Glyph value="egravesuperior"/>
+          <Glyph value="usuperior"/>
+          <Glyph value="rsuperior"/>
+          <Glyph value="nsuperior"/>
+          <Glyph value="ssuperior"/>
+          <Glyph value="msuperior"/>
+          <Glyph value="jsuperior"/>
+          <Glyph value="ysuperior"/>
+          <Glyph value="ksuperior"/>
+        </Coverage>
+        <ValueFormat value="2"/>
+        <Value YPlacement="-560"/>
+      </SinglePos>
+    </Lookup>
+  </LookupList>
+</GPOS>
diff --git a/Tests/mtiLib/data/mti/gpossingle.txt b/Tests/mtiLib/data/mti/gpossingle.txt
new file mode 100644
index 0000000..9e6f429
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gpossingle.txt
Binary files differ
diff --git a/Tests/mtiLib/data/mti/gsubalternate.ttx.GSUB b/Tests/mtiLib/data/mti/gsubalternate.ttx.GSUB
new file mode 100644
index 0000000..4184325
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gsubalternate.ttx.GSUB
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GSUB>
+  <Version value="0x00010000"/>
+  <LookupList>
+    <!-- LookupCount=1 -->
+    <Lookup index="0">
+      <LookupType value="3"/>
+      <LookupFlag value="0"/>
+      <!-- SubTableCount=1 -->
+      <AlternateSubst index="0" Format="1">
+        <AlternateSet glyph="eight">
+          <Alternate glyph="uniF738"/>
+          <Alternate glyph="uniE0C0"/>
+          <Alternate glyph="uniE0C1"/>
+          <Alternate glyph="uniE0BE"/>
+          <Alternate glyph="uni2078"/>
+          <Alternate glyph="uni2088"/>
+          <Alternate glyph="uniE0BF"/>
+          <Alternate glyph="uniE0BD"/>
+        </AlternateSet>
+        <AlternateSet glyph="five">
+          <Alternate glyph="uniF735"/>
+          <Alternate glyph="uniE0CD"/>
+          <Alternate glyph="uniE0CE"/>
+          <Alternate glyph="uniE0CB"/>
+          <Alternate glyph="uni2075"/>
+          <Alternate glyph="uni2085"/>
+          <Alternate glyph="uniE0CC"/>
+          <Alternate glyph="uniE0CA"/>
+        </AlternateSet>
+        <AlternateSet glyph="four">
+          <Alternate glyph="uniF734"/>
+          <Alternate glyph="uniE0D4"/>
+          <Alternate glyph="uniE0D5"/>
+          <Alternate glyph="uniE0D2"/>
+          <Alternate glyph="uni2074"/>
+          <Alternate glyph="uni2084"/>
+          <Alternate glyph="uniE0D3"/>
+          <Alternate glyph="uniE0D1"/>
+        </AlternateSet>
+        <AlternateSet glyph="guilsinglleft">
+          <Alternate glyph="uniE0DB"/>
+          <Alternate glyph="uniE0DC"/>
+        </AlternateSet>
+        <AlternateSet glyph="guilsinglright">
+          <Alternate glyph="uniE0DD"/>
+          <Alternate glyph="uniE0DE"/>
+        </AlternateSet>
+        <AlternateSet glyph="nine">
+          <Alternate glyph="uniF739"/>
+          <Alternate glyph="uniE0EC"/>
+          <Alternate glyph="uniE0ED"/>
+          <Alternate glyph="uniE0EA"/>
+          <Alternate glyph="uni2079"/>
+          <Alternate glyph="uni2089"/>
+          <Alternate glyph="uniE0EB"/>
+          <Alternate glyph="uniE0E9"/>
+        </AlternateSet>
+        <AlternateSet glyph="one">
+          <Alternate glyph="uniF731"/>
+          <Alternate glyph="uniE0F3"/>
+          <Alternate glyph="uniE0F4"/>
+          <Alternate glyph="uniE0F1"/>
+          <Alternate glyph="uni00B9"/>
+          <Alternate glyph="uni2081"/>
+          <Alternate glyph="uniE0F2"/>
+          <Alternate glyph="uniE0F0"/>
+          <Alternate glyph="uniE0F8"/>
+        </AlternateSet>
+        <AlternateSet glyph="seven">
+          <Alternate glyph="uniF737"/>
+          <Alternate glyph="uniE11C"/>
+          <Alternate glyph="uniE11D"/>
+          <Alternate glyph="uniE11A"/>
+          <Alternate glyph="uni2077"/>
+          <Alternate glyph="uni2087"/>
+          <Alternate glyph="uniE11B"/>
+          <Alternate glyph="uniE119"/>
+        </AlternateSet>
+        <AlternateSet glyph="six">
+          <Alternate glyph="uniF736"/>
+          <Alternate glyph="uniE121"/>
+          <Alternate glyph="uniE122"/>
+          <Alternate glyph="uniE11F"/>
+          <Alternate glyph="uni2076"/>
+          <Alternate glyph="uni2086"/>
+          <Alternate glyph="uniE120"/>
+          <Alternate glyph="uniE11E"/>
+        </AlternateSet>
+        <AlternateSet glyph="three">
+          <Alternate glyph="uniF733"/>
+          <Alternate glyph="uniE12B"/>
+          <Alternate glyph="uniE12C"/>
+          <Alternate glyph="uniE129"/>
+          <Alternate glyph="uni00B3"/>
+          <Alternate glyph="uni2083"/>
+          <Alternate glyph="uniE12A"/>
+          <Alternate glyph="uniE128"/>
+        </AlternateSet>
+        <AlternateSet glyph="two">
+          <Alternate glyph="uniF732"/>
+          <Alternate glyph="uniE133"/>
+          <Alternate glyph="uniE134"/>
+          <Alternate glyph="uniE131"/>
+          <Alternate glyph="uni00B2"/>
+          <Alternate glyph="uni2082"/>
+          <Alternate glyph="uniE132"/>
+          <Alternate glyph="uniE130"/>
+          <Alternate glyph="uniE0F9"/>
+        </AlternateSet>
+        <AlternateSet glyph="zero">
+          <Alternate glyph="uniF730"/>
+          <Alternate glyph="uniE13D"/>
+          <Alternate glyph="uniE13E"/>
+          <Alternate glyph="uniE13A"/>
+          <Alternate glyph="uni2070"/>
+          <Alternate glyph="uni2080"/>
+          <Alternate glyph="uniE13B"/>
+          <Alternate glyph="uniE139"/>
+          <Alternate glyph="uniE13C"/>
+        </AlternateSet>
+      </AlternateSubst>
+    </Lookup>
+  </LookupList>
+</GSUB>
diff --git a/Tests/mtiLib/data/mti/gsubalternate.txt b/Tests/mtiLib/data/mti/gsubalternate.txt
new file mode 100644
index 0000000..66e0997
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gsubalternate.txt
@@ -0,0 +1,25 @@
+
+Comment: taken from Adobe garamond Pro, showing Unicode glyph references
+
+
+lookup	 27 	alternate
+
+RightToLeft	no
+IgnoreBaseGlyphs	no
+IgnoreLigatures	no
+IgnoreMarks	no
+
+U 30	U F730	U E13D	U E13E	U E13A	U 2070	U 2080	U E13B	U E139	U E13C
+U 31	U F731	U E0F3	U E0F4	U E0F1	U B9	U 2081	U E0F2	U E0F0	U E0F8
+U 32	U F732	U E133	U E134	U E131	U B2	U 2082	U E132	U E130	U E0F9
+U 33	U F733	U E12B	U E12C	U E129	U B3	U 2083	U E12A	U E128
+U 34	U F734	U E0D4	U E0D5	U E0D2	U 2074	U 2084	U E0D3	U E0D1
+U 35	U F735	U E0CD	U E0CE	U E0CB	U 2075	U 2085	U E0CC	U E0CA
+U 36	U F736	U E121	U E122	U E11F	U 2076	U 2086	U E120	U E11E
+U 37	U F737	U E11C	U E11D	U E11A	U 2077	U 2087	U E11B	U E119
+U 38	U F738	U E0C0	U E0C1	U E0BE	U 2078	U 2088	U E0BF	U E0BD
+U 39	U F739	U E0EC	U E0ED	U E0EA	U 2079	U 2089	U E0EB	U E0E9
+U 2039	U E0DB	U E0DC
+U 203A	U E0DD	U E0DE
+
+lookup end
diff --git a/Tests/mtiLib/data/mti/gsubligature.ttx.GSUB b/Tests/mtiLib/data/mti/gsubligature.ttx.GSUB
new file mode 100644
index 0000000..ad8f505
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gsubligature.ttx.GSUB
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GSUB>
+  <Version value="0x00010000"/>
+  <LookupList>
+    <!-- LookupCount=1 -->
+    <Lookup index="0">
+      <LookupType value="4"/>
+      <LookupFlag value="0"/>
+      <!-- SubTableCount=1 -->
+      <LigatureSubst index="0" Format="1">
+        <LigatureSet glyph="I">
+          <Ligature components="J" glyph="IJ"/>
+        </LigatureSet>
+        <LigatureSet glyph="Ismall">
+          <Ligature components="Jsmall" glyph="IJsmall"/>
+        </LigatureSet>
+        <LigatureSet glyph="f">
+          <Ligature components="f,b" glyph="ffb"/>
+          <Ligature components="f,h" glyph="ffh"/>
+          <Ligature components="f,i" glyph="ffi"/>
+          <Ligature components="f,k" glyph="ffk"/>
+          <Ligature components="f,l" glyph="ffl"/>
+          <Ligature components="f,t" glyph="fft"/>
+          <Ligature components="b" glyph="fb"/>
+          <Ligature components="f" glyph="ff"/>
+          <Ligature components="h" glyph="fh"/>
+          <Ligature components="i" glyph="fi"/>
+          <Ligature components="j" glyph="fj"/>
+          <Ligature components="k" glyph="fk"/>
+          <Ligature components="l" glyph="fl"/>
+          <Ligature components="t" glyph="ft"/>
+        </LigatureSet>
+        <LigatureSet glyph="i">
+          <Ligature components="j" glyph="ij"/>
+        </LigatureSet>
+        <LigatureSet glyph="t">
+          <Ligature components="t" glyph="tt"/>
+        </LigatureSet>
+      </LigatureSubst>
+    </Lookup>
+  </LookupList>
+</GSUB>
diff --git a/Tests/mtiLib/data/mti/gsubligature.txt b/Tests/mtiLib/data/mti/gsubligature.txt
new file mode 100644
index 0000000..23d56b0
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gsubligature.txt
Binary files differ
diff --git a/Tests/mtiLib/data/mti/gsubmultiple.ttx.GSUB b/Tests/mtiLib/data/mti/gsubmultiple.ttx.GSUB
new file mode 100644
index 0000000..a68a45a
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gsubmultiple.ttx.GSUB
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GSUB>
+  <Version value="0x00010000"/>
+  <LookupList>
+    <!-- LookupCount=1 -->
+    <Lookup index="0">
+      <LookupType value="2"/>
+      <LookupFlag value="0"/>
+      <!-- SubTableCount=1 -->
+      <MultipleSubst index="0" Format="1">
+        <Substitution in="janyevoweltelugu" out="jaivoweltelugu,nyasubscripttelugu"/>
+        <Substitution in="kassevoweltelugu" out="kaivoweltelugu,ssasubscripttelugu"/>
+      </MultipleSubst>
+    </Lookup>
+  </LookupList>
+</GSUB>
diff --git a/Tests/mtiLib/data/mti/gsubmultiple.txt b/Tests/mtiLib/data/mti/gsubmultiple.txt
new file mode 100644
index 0000000..d4c301c
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gsubmultiple.txt
Binary files differ
diff --git a/Tests/mtiLib/data/mti/gsubreversechanined.ttx.GSUB b/Tests/mtiLib/data/mti/gsubreversechanined.ttx.GSUB
new file mode 100644
index 0000000..62ba14f
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gsubreversechanined.ttx.GSUB
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GSUB>
+  <Version value="0x00010000"/>
+  <LookupList>
+    <!-- LookupCount=1 -->
+    <Lookup index="0">
+      <LookupType value="8"/>
+      <LookupFlag value="9"/>
+      <!-- SubTableCount=3 -->
+      <ReverseChainSingleSubst index="0" Format="1">
+        <Coverage Format="2">
+          <Glyph value="rayf2"/>
+          <Glyph value="reyf2"/>
+          <Glyph value="yayf2"/>
+          <Glyph value="zayf2"/>
+        </Coverage>
+        <!-- BacktrackGlyphCount=1 -->
+        <BacktrackCoverage index="0" Format="2">
+          <Glyph value="bayi1"/>
+          <Glyph value="jeemi1"/>
+          <Glyph value="kafi1"/>
+          <Glyph value="ghafi1"/>
+          <Glyph value="laami1"/>
+          <Glyph value="kafm1"/>
+          <Glyph value="ghafm1"/>
+          <Glyph value="laamm1"/>
+        </BacktrackCoverage>
+        <!-- LookAheadGlyphCount=0 -->
+        <!-- GlyphCount=4 -->
+        <Substitute index="0" value="rayf1"/>
+        <Substitute index="1" value="reyf1"/>
+        <Substitute index="2" value="yayf1"/>
+        <Substitute index="3" value="zayf1"/>
+      </ReverseChainSingleSubst>
+      <ReverseChainSingleSubst index="1" Format="1">
+        <Coverage Format="2">
+          <Glyph value="ayehf2"/>
+          <Glyph value="hamzayeharabf2"/>
+          <Glyph value="hamzayehf2"/>
+          <Glyph value="yehf2"/>
+        </Coverage>
+        <!-- BacktrackGlyphCount=1 -->
+        <BacktrackCoverage index="0" Format="1">
+          <Glyph value="bayi1"/>
+          <Glyph value="kafi1"/>
+          <Glyph value="ghafi1"/>
+          <Glyph value="laami1"/>
+          <Glyph value="kafm1"/>
+          <Glyph value="ghafm1"/>
+          <Glyph value="laamm1"/>
+          <Glyph value="fayi1"/>
+        </BacktrackCoverage>
+        <!-- LookAheadGlyphCount=0 -->
+        <!-- GlyphCount=4 -->
+        <Substitute index="0" value="ayehf1"/>
+        <Substitute index="1" value="hamzayeharabf1"/>
+        <Substitute index="2" value="hamzayehf1"/>
+        <Substitute index="3" value="yehf1"/>
+      </ReverseChainSingleSubst>
+      <ReverseChainSingleSubst index="2" Format="1">
+        <Coverage Format="1">
+          <Glyph value="dal"/>
+          <Glyph value="del"/>
+          <Glyph value="zal"/>
+        </Coverage>
+        <!-- BacktrackGlyphCount=0 -->
+        <!-- LookAheadGlyphCount=1 -->
+        <LookAheadCoverage index="0" Format="2">
+          <Glyph value="ray"/>
+          <Glyph value="rey"/>
+          <Glyph value="zay"/>
+          <Glyph value="yay"/>
+        </LookAheadCoverage>
+        <!-- GlyphCount=3 -->
+        <Substitute index="0" value="dal1"/>
+        <Substitute index="1" value="del1"/>
+        <Substitute index="2" value="zal1"/>
+      </ReverseChainSingleSubst>
+    </Lookup>
+  </LookupList>
+</GSUB>
diff --git a/Tests/mtiLib/data/mti/gsubreversechanined.txt b/Tests/mtiLib/data/mti/gsubreversechanined.txt
new file mode 100644
index 0000000..8444a07
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gsubreversechanined.txt
Binary files differ
diff --git a/Tests/mtiLib/data/mti/gsubsingle.ttx.GSUB b/Tests/mtiLib/data/mti/gsubsingle.ttx.GSUB
new file mode 100644
index 0000000..525b365
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gsubsingle.ttx.GSUB
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GSUB>
+  <Version value="0x00010000"/>
+  <LookupList>
+    <!-- LookupCount=1 -->
+    <Lookup index="0">
+      <LookupType value="1"/>
+      <LookupFlag value="0"/>
+      <!-- SubTableCount=1 -->
+      <SingleSubst index="0" Format="1">
+        <Substitution in="onehalf" out="onehalf.alt"/>
+        <Substitution in="onequarter" out="onequarter.alt"/>
+        <Substitution in="threequarters" out="threequarters.alt"/>
+      </SingleSubst>
+    </Lookup>
+  </LookupList>
+</GSUB>
diff --git a/Tests/mtiLib/data/mti/gsubsingle.txt b/Tests/mtiLib/data/mti/gsubsingle.txt
new file mode 100644
index 0000000..76e7459
--- /dev/null
+++ b/Tests/mtiLib/data/mti/gsubsingle.txt
Binary files differ
diff --git a/Tests/mtiLib/data/mti/mark-to-ligature.ttx.GPOS b/Tests/mtiLib/data/mti/mark-to-ligature.ttx.GPOS
new file mode 100644
index 0000000..7e02fe0
--- /dev/null
+++ b/Tests/mtiLib/data/mti/mark-to-ligature.ttx.GPOS
@@ -0,0 +1,826 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GPOS>
+  <Version value="0x00010000"/>
+  <LookupList>
+    <!-- LookupCount=1 -->
+    <Lookup index="0">
+      <LookupType value="5"/>
+      <LookupFlag value="0"/>
+      <!-- SubTableCount=1 -->
+      <MarkLigPos index="0" Format="1">
+        <MarkCoverage Format="2">
+          <Glyph value="AlefSuperiorNS"/>
+          <Glyph value="DammaNS"/>
+          <Glyph value="DammaRflxNS"/>
+          <Glyph value="DammatanNS"/>
+          <Glyph value="Fatha2dotsNS"/>
+          <Glyph value="FathaNS"/>
+          <Glyph value="FathatanNS"/>
+          <Glyph value="FourDotsAboveNS"/>
+          <Glyph value="HamzaAboveNS"/>
+          <Glyph value="MaddaNS"/>
+          <Glyph value="OneDotAbove2NS"/>
+          <Glyph value="OneDotAboveNS"/>
+          <Glyph value="ShaddaAlefNS"/>
+          <Glyph value="ShaddaDammaNS"/>
+          <Glyph value="ShaddaDammatanNS"/>
+          <Glyph value="ShaddaFathatanNS"/>
+          <Glyph value="ShaddaKasraNS"/>
+          <Glyph value="ShaddaKasratanNS"/>
+          <Glyph value="ShaddaNS"/>
+          <Glyph value="SharetKafNS"/>
+          <Glyph value="SukunNS"/>
+          <Glyph value="ThreeDotsDownAboveNS"/>
+          <Glyph value="ThreeDotsUpAboveNS"/>
+          <Glyph value="TwoDotsAboveNS"/>
+          <Glyph value="TwoDotsVerticalAboveNS"/>
+          <Glyph value="UltapeshNS"/>
+          <Glyph value="WaslaNS"/>
+        </MarkCoverage>
+        <LigatureCoverage Format="2">
+          <Glyph value="AinIni.12m_MeemFin.02"/>
+          <Glyph value="AinIni_YehBarreeFin"/>
+          <Glyph value="AinMed_YehBarreeFin"/>
+          <Glyph value="BehxIni_MeemFin"/>
+          <Glyph value="BehxIni_NoonGhunnaFin"/>
+          <Glyph value="BehxIni_RehFin"/>
+          <Glyph value="BehxIni_RehFin.b"/>
+          <Glyph value="BehxMed_MeemFin.py"/>
+          <Glyph value="BehxMed_NoonGhunnaFin"/>
+          <Glyph value="BehxMed_NoonGhunnaFin.cup"/>
+          <Glyph value="BehxMed_RehFin"/>
+          <Glyph value="BehxMed_RehFin.cup"/>
+          <Glyph value="BehxMed_YehxFin"/>
+          <Glyph value="FehxMed_YehBarreeFin"/>
+          <Glyph value="HahIni_YehBarreeFin"/>
+          <Glyph value="KafIni_YehBarreeFin"/>
+          <Glyph value="KafMed.12_YehxFin.01"/>
+          <Glyph value="KafMed_MeemFin"/>
+          <Glyph value="KafMed_YehBarreeFin"/>
+          <Glyph value="LamAlefFin"/>
+          <Glyph value="LamAlefFin.cup"/>
+          <Glyph value="LamAlefFin.cut"/>
+          <Glyph value="LamAlefFin.short"/>
+          <Glyph value="LamAlefSep"/>
+          <Glyph value="LamIni_MeemFin"/>
+          <Glyph value="LamIni_YehBarreeFin"/>
+          <Glyph value="LamMed_MeemFin"/>
+          <Glyph value="LamMed_MeemFin.b"/>
+          <Glyph value="LamMed_YehxFin"/>
+          <Glyph value="LamMed_YehxFin.cup"/>
+          <Glyph value="TahIni_YehBarreeFin"/>
+        </LigatureCoverage>
+        <!-- ClassCount=1 -->
+        <MarkArray>
+          <!-- MarkCount=27 -->
+          <MarkRecord index="0">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="141"/>
+              <YCoordinate value="874"/>
+              <AnchorPoint value="1"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="1">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="394"/>
+              <YCoordinate value="1444"/>
+              <AnchorPoint value="0"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="2">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="454"/>
+              <YCoordinate value="1128"/>
+              <AnchorPoint value="1"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="3">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="354"/>
+              <YCoordinate value="1409"/>
+              <AnchorPoint value="0"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="4">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="272"/>
+              <YCoordinate value="1097"/>
+              <AnchorPoint value="0"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="5">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="277"/>
+              <YCoordinate value="1379"/>
+              <AnchorPoint value="0"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="6">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="281"/>
+              <YCoordinate value="1388"/>
+              <AnchorPoint value="0"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="7">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="347"/>
+              <YCoordinate value="860"/>
+              <AnchorPoint value="0"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="8">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="266"/>
+              <YCoordinate value="1425"/>
+              <AnchorPoint value="2"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="9">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="397"/>
+              <YCoordinate value="1472"/>
+              <AnchorPoint value="1"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="10">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="215"/>
+              <YCoordinate value="1001"/>
+              <AnchorPoint value="3"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="11">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="215"/>
+              <YCoordinate value="1001"/>
+              <AnchorPoint value="3"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="12">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="283"/>
+              <YCoordinate value="1581"/>
+              <AnchorPoint value="0"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="13">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="283"/>
+              <YCoordinate value="1581"/>
+              <AnchorPoint value="0"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="14">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="283"/>
+              <YCoordinate value="1581"/>
+              <AnchorPoint value="0"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="15">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="369"/>
+              <YCoordinate value="1604"/>
+              <AnchorPoint value="0"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="16">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="426"/>
+              <YCoordinate value="1340"/>
+              <AnchorPoint value="55"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="17">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="315"/>
+              <YCoordinate value="1164"/>
+              <AnchorPoint value="55"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="18">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="283"/>
+              <YCoordinate value="1581"/>
+              <AnchorPoint value="0"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="19">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="382"/>
+              <YCoordinate value="520"/>
+              <AnchorPoint value="1"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="20">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="220"/>
+              <YCoordinate value="1474"/>
+              <AnchorPoint value="1"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="21">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="346"/>
+              <YCoordinate value="687"/>
+              <AnchorPoint value="0"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="22">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="346"/>
+              <YCoordinate value="1003"/>
+              <AnchorPoint value="0"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="23">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="346"/>
+              <YCoordinate value="1003"/>
+              <AnchorPoint value="0"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="24">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="357"/>
+              <YCoordinate value="707"/>
+              <AnchorPoint value="1"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="25">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="454"/>
+              <YCoordinate value="1128"/>
+              <AnchorPoint value="1"/>
+            </MarkAnchor>
+          </MarkRecord>
+          <MarkRecord index="26">
+            <Class value="0"/>
+            <MarkAnchor Format="2">
+              <XCoordinate value="357"/>
+              <YCoordinate value="1470"/>
+              <AnchorPoint value="0"/>
+            </MarkAnchor>
+          </MarkRecord>
+        </MarkArray>
+        <LigatureArray>
+          <!-- LigatureCount=31 -->
+          <LigatureAttach index="0">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="720"/>
+                <YCoordinate value="1281"/>
+                <AnchorPoint value="160"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="75"/>
+                <YCoordinate value="631"/>
+                <AnchorPoint value="158"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="1">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="766"/>
+                <YCoordinate value="1036"/>
+                <AnchorPoint value="82"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="194"/>
+                <YCoordinate value="312"/>
+                <AnchorPoint value="151"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="2">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="774"/>
+                <YCoordinate value="860"/>
+                <AnchorPoint value="105"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="312"/>
+                <YCoordinate value="618"/>
+                <AnchorPoint value="108"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="3">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="785"/>
+                <YCoordinate value="1255"/>
+                <AnchorPoint value="90"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="269"/>
+                <YCoordinate value="952"/>
+                <AnchorPoint value="93"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="4">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="1205"/>
+                <YCoordinate value="871"/>
+                <AnchorPoint value="78"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="516"/>
+                <YCoordinate value="565"/>
+                <AnchorPoint value="74"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="5">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="618"/>
+                <YCoordinate value="813"/>
+                <AnchorPoint value="59"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="282"/>
+                <YCoordinate value="523"/>
+                <AnchorPoint value="58"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="6">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="708"/>
+                <YCoordinate value="813"/>
+                <AnchorPoint value="65"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="282"/>
+                <YCoordinate value="543"/>
+                <AnchorPoint value="64"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="7">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="777"/>
+                <YCoordinate value="699"/>
+                <AnchorPoint value="99"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="194"/>
+                <YCoordinate value="481"/>
+                <AnchorPoint value="102"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="8">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="1205"/>
+                <YCoordinate value="1061"/>
+                <AnchorPoint value="78"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="516"/>
+                <YCoordinate value="755"/>
+                <AnchorPoint value="74"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="9">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="1205"/>
+                <YCoordinate value="1061"/>
+                <AnchorPoint value="78"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="516"/>
+                <YCoordinate value="755"/>
+                <AnchorPoint value="74"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="10">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="708"/>
+                <YCoordinate value="1083"/>
+                <AnchorPoint value="65"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="282"/>
+                <YCoordinate value="813"/>
+                <AnchorPoint value="64"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="11">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="708"/>
+                <YCoordinate value="1083"/>
+                <AnchorPoint value="65"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="282"/>
+                <YCoordinate value="813"/>
+                <AnchorPoint value="64"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="12">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="913"/>
+                <YCoordinate value="-285"/>
+                <AnchorPoint value="117"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="1223"/>
+                <YCoordinate value="-305"/>
+                <AnchorPoint value="112"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="13">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="397"/>
+                <YCoordinate value="804"/>
+                <AnchorPoint value="158"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="6"/>
+                <YCoordinate value="-65"/>
+                <AnchorPoint value="161"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="14">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="1017"/>
+                <YCoordinate value="732"/>
+                <AnchorPoint value="83"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="344"/>
+                <YCoordinate value="743"/>
+                <AnchorPoint value="86"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="15">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="496"/>
+                <YCoordinate value="1549"/>
+                <AnchorPoint value="81"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="328"/>
+                <YCoordinate value="339"/>
+                <AnchorPoint value="171"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="16">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="807"/>
+                <YCoordinate value="1457"/>
+                <AnchorPoint value="106"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="440"/>
+                <YCoordinate value="418"/>
+                <AnchorPoint value="176"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="17">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="238"/>
+                <YCoordinate value="1435"/>
+                <AnchorPoint value="182"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="84"/>
+                <YCoordinate value="308"/>
+                <AnchorPoint value="186"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="18">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="465"/>
+                <YCoordinate value="1407"/>
+                <AnchorPoint value="106"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="328"/>
+                <YCoordinate value="251"/>
+                <AnchorPoint value="197"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="19">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="1122"/>
+                <YCoordinate value="1620"/>
+                <AnchorPoint value="98"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="162"/>
+                <YCoordinate value="1487"/>
+                <AnchorPoint value="99"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="20">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="1122"/>
+                <YCoordinate value="1620"/>
+                <AnchorPoint value="105"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="162"/>
+                <YCoordinate value="1487"/>
+                <AnchorPoint value="106"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="21">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="1122"/>
+                <YCoordinate value="1620"/>
+                <AnchorPoint value="110"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="162"/>
+                <YCoordinate value="1487"/>
+                <AnchorPoint value="108"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="22">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="1122"/>
+                <YCoordinate value="1620"/>
+                <AnchorPoint value="96"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="162"/>
+                <YCoordinate value="1487"/>
+                <AnchorPoint value="99"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="23">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="1055"/>
+                <YCoordinate value="1583"/>
+                <AnchorPoint value="105"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="198"/>
+                <YCoordinate value="1528"/>
+                <AnchorPoint value="106"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="24">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="386"/>
+                <YCoordinate value="1808"/>
+                <AnchorPoint value="70"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="130"/>
+                <YCoordinate value="701"/>
+                <AnchorPoint value="150"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="25">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="719"/>
+                <YCoordinate value="1633"/>
+                <AnchorPoint value="70"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="328"/>
+                <YCoordinate value="339"/>
+                <AnchorPoint value="160"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="26">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="555"/>
+                <YCoordinate value="1627"/>
+                <AnchorPoint value="154"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="175"/>
+                <YCoordinate value="472"/>
+                <AnchorPoint value="155"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="27">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="555"/>
+                <YCoordinate value="1627"/>
+                <AnchorPoint value="156"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="175"/>
+                <YCoordinate value="472"/>
+                <AnchorPoint value="157"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="28">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="925"/>
+                <YCoordinate value="1620"/>
+                <AnchorPoint value="157"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="490"/>
+                <YCoordinate value="196"/>
+                <AnchorPoint value="152"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="29">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="935"/>
+                <YCoordinate value="1620"/>
+                <AnchorPoint value="159"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="500"/>
+                <YCoordinate value="196"/>
+                <AnchorPoint value="155"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+          <LigatureAttach index="30">
+            <!-- ComponentCount=2 -->
+            <ComponentRecord index="0">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="1253"/>
+                <YCoordinate value="1065"/>
+                <AnchorPoint value="143"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+            <ComponentRecord index="1">
+              <LigatureAnchor index="0" Format="2">
+                <XCoordinate value="263"/>
+                <YCoordinate value="419"/>
+                <AnchorPoint value="142"/>
+              </LigatureAnchor>
+            </ComponentRecord>
+          </LigatureAttach>
+        </LigatureArray>
+      </MarkLigPos>
+    </Lookup>
+  </LookupList>
+</GPOS>
diff --git a/Tests/mtiLib/data/mti/mark-to-ligature.txt b/Tests/mtiLib/data/mti/mark-to-ligature.txt
new file mode 100644
index 0000000..efdd2cf
--- /dev/null
+++ b/Tests/mtiLib/data/mti/mark-to-ligature.txt
@@ -0,0 +1,93 @@
+lookup	LigMk0	mark to ligature
+
+mark	FathatanNS	0	281, 1388	0
+mark	DammatanNS	0	354, 1409	0
+mark	FathaNS	0	277, 1379	0
+mark	DammaNS	0	394, 1444	0
+mark	ShaddaNS	0	283, 1581	0
+mark	SukunNS	0	220, 1474	1
+mark	MaddaNS	0	397, 1472	1
+mark	HamzaAboveNS	0	266, 1425	2
+mark	UltapeshNS	0	454, 1128	1
+mark	DammaRflxNS	0	454, 1128	1
+mark	Fatha2dotsNS	0	272, 1097	0
+mark	AlefSuperiorNS	0	141, 874	1
+mark	ShaddaAlefNS	0	283, 1581	0
+mark	WaslaNS	0	357, 1470	0
+mark	OneDotAboveNS	0	215, 1001	3
+mark	TwoDotsAboveNS	0	346, 1003	0
+mark	ThreeDotsUpAboveNS	0	346, 1003	0
+mark	ThreeDotsDownAboveNS	0	346, 687	0
+mark	FourDotsAboveNS	0	347, 860	0
+mark	TwoDotsVerticalAboveNS	0	357, 707	1
+mark	OneDotAbove2NS	0	215, 1001	3
+mark	SharetKafNS	0	382, 520	1
+mark	ShaddaKasratanNS	0	315, 1164	55
+mark	ShaddaKasraNS	0	426, 1340	55
+mark	ShaddaFathatanNS	0	369, 1604	0
+mark	ShaddaDammatanNS	0	283, 1581	0
+mark	ShaddaDammaNS	0	283, 1581	0
+ligature	LamAlefFin.short	1	2	0	1122, 1620	96
+ligature	LamAlefFin.short	2	2	0	162, 1487	99
+ligature	LamAlefFin.cup	1	2	0	1122, 1620	105
+ligature	LamAlefFin.cup	2	2	0	162, 1487	106
+ligature	LamAlefFin.cut	1	2	0	1122, 1620	110
+ligature	LamAlefFin.cut	2	2	0	162, 1487	108
+ligature	BehxIni_RehFin	1	2	0	618, 813	59
+ligature	BehxIni_RehFin	2	2	0	282, 523	58
+ligature	BehxIni_RehFin.b	1	2	0	708, 813	65
+ligature	BehxIni_RehFin.b	2	2	0	282, 543	64
+ligature	BehxIni_NoonGhunnaFin	1	2	0	1205, 871	78
+ligature	BehxIni_NoonGhunnaFin	2	2	0	516, 565	74
+ligature	BehxIni_MeemFin	1	2	0	785, 1255	90
+ligature	BehxIni_MeemFin	2	2	0	269, 952	93
+ligature	HahIni_YehBarreeFin	1	2	0	1017, 732	83
+ligature	HahIni_YehBarreeFin	2	2	0	344, 743	86
+ligature	AinMed_YehBarreeFin	1	2	0	774, 860	105
+ligature	AinMed_YehBarreeFin	2	2	0	312, 618	108
+ligature	TahIni_YehBarreeFin	1	2	0	1253, 1065	143
+ligature	TahIni_YehBarreeFin	2	2	0	263, 419	142
+ligature	BehxMed_NoonGhunnaFin	1	2	0	1205, 1061	78
+ligature	BehxMed_NoonGhunnaFin	2	2	0	516, 755	74
+ligature	KafMed_MeemFin	1	2	0	238, 1435	182
+ligature	KafMed_MeemFin	2	2	0	84, 308	186
+ligature	LamMed_MeemFin	1	2	0	555, 1627	154
+ligature	LamMed_MeemFin	2	2	0	175, 472	155
+ligature	LamMed_MeemFin.b	1	2	0	555, 1627	156
+ligature	LamMed_MeemFin.b	2	2	0	175, 472	157
+ligature	LamIni_MeemFin	1	2	0	386, 1808	70
+ligature	LamIni_MeemFin	2	2	0	130, 701	150
+ligature	AinIni.12m_MeemFin.02	1	2	0	720, 1281	160
+ligature	AinIni.12m_MeemFin.02	2	2	0	75, 631	158
+ligature	KafMed.12_YehxFin.01	1	2	0	807, 1457	106
+ligature	KafMed.12_YehxFin.01	2	2	0	440, 418	176
+ligature	LamMed_YehxFin	1	2	0	925, 1620	157
+ligature	LamMed_YehxFin	2	2	0	490, 196	152
+ligature	LamMed_YehxFin.cup	1	2	0	935, 1620	159
+ligature	LamMed_YehxFin.cup	2	2	0	500, 196	155
+ligature	FehxMed_YehBarreeFin	1	2	0	397, 804	158
+ligature	FehxMed_YehBarreeFin	2	2	0	6,-65	161
+ligature	KafIni_YehBarreeFin	1	2	0	496, 1549	81
+ligature	KafIni_YehBarreeFin	2	2	0	328, 339	171
+ligature	KafMed_YehBarreeFin	1	2	0	465, 1407	106
+ligature	KafMed_YehBarreeFin	2	2	0	328, 251	197
+ligature	LamIni_YehBarreeFin	1	2	0	719, 1633	70
+ligature	LamIni_YehBarreeFin	2	2	0	328, 339	160
+ligature	AinIni_YehBarreeFin	1	2	0	766, 1036	82
+ligature	AinIni_YehBarreeFin	2	2	0	194, 312	151
+ligature	BehxMed_YehxFin	1	2	0	913,-285	117
+ligature	BehxMed_YehxFin	2	2	0	1223,-305	112
+ligature	BehxMed_MeemFin.py	1	2	0	777, 699	99
+ligature	BehxMed_MeemFin.py	2	2	0	194, 481	102
+ligature	BehxMed_RehFin	1	2	0	708, 1083	65
+ligature	BehxMed_RehFin	2	2	0	282, 813	64
+ligature	BehxMed_RehFin.cup	1	2	0	708, 1083	65
+ligature	BehxMed_RehFin.cup	2	2	0	282, 813	64
+ligature	BehxMed_NoonGhunnaFin.cup	1	2	0	1205, 1061	78
+ligature	BehxMed_NoonGhunnaFin.cup	2	2	0	516, 755	74
+ligature	LamAlefSep	1	2	0	1055, 1583	105
+ligature	LamAlefSep	2	2	0	198, 1528	106
+ligature	LamAlefFin	1	2	0	1122, 1620	98
+ligature	LamAlefFin	2	2	0	162, 1487	99
+
+lookup end
\ No newline at end of file
diff --git a/Tests/mtiLib/data/mti/scripttable.ttx.GPOS b/Tests/mtiLib/data/mti/scripttable.ttx.GPOS
new file mode 100644
index 0000000..7615bb7
--- /dev/null
+++ b/Tests/mtiLib/data/mti/scripttable.ttx.GPOS
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GPOS>
+  <Version value="0x00010000"/>
+  <ScriptList>
+    <!-- ScriptCount=3 -->
+    <ScriptRecord index="0">
+      <ScriptTag value="cyrl"/>
+      <Script>
+        <DefaultLangSys>
+          <ReqFeatureIndex value="65535"/>
+          <!-- FeatureCount=3 -->
+          <FeatureIndex index="0" value="3"/>
+          <FeatureIndex index="1" value="4"/>
+          <FeatureIndex index="2" value="1"/>
+        </DefaultLangSys>
+        <!-- LangSysCount=0 -->
+      </Script>
+    </ScriptRecord>
+    <ScriptRecord index="1">
+      <ScriptTag value="grek"/>
+      <Script>
+        <DefaultLangSys>
+          <ReqFeatureIndex value="65535"/>
+          <!-- FeatureCount=3 -->
+          <FeatureIndex index="0" value="3"/>
+          <FeatureIndex index="1" value="4"/>
+          <FeatureIndex index="2" value="2"/>
+        </DefaultLangSys>
+        <!-- LangSysCount=0 -->
+      </Script>
+    </ScriptRecord>
+    <ScriptRecord index="2">
+      <ScriptTag value="latn"/>
+      <Script>
+        <DefaultLangSys>
+          <ReqFeatureIndex value="65535"/>
+          <!-- FeatureCount=3 -->
+          <FeatureIndex index="0" value="3"/>
+          <FeatureIndex index="1" value="4"/>
+          <FeatureIndex index="2" value="0"/>
+        </DefaultLangSys>
+        <!-- LangSysCount=4 -->
+        <LangSysRecord index="0">
+          <LangSysTag value="DEU "/>
+          <LangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=3 -->
+            <FeatureIndex index="0" value="3"/>
+            <FeatureIndex index="1" value="4"/>
+            <FeatureIndex index="2" value="0"/>
+          </LangSys>
+        </LangSysRecord>
+        <LangSysRecord index="1">
+          <LangSysTag value="ROM "/>
+          <LangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=3 -->
+            <FeatureIndex index="0" value="3"/>
+            <FeatureIndex index="1" value="4"/>
+            <FeatureIndex index="2" value="0"/>
+          </LangSys>
+        </LangSysRecord>
+        <LangSysRecord index="2">
+          <LangSysTag value="TRK "/>
+          <LangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=3 -->
+            <FeatureIndex index="0" value="3"/>
+            <FeatureIndex index="1" value="4"/>
+            <FeatureIndex index="2" value="0"/>
+          </LangSys>
+        </LangSysRecord>
+        <LangSysRecord index="3">
+          <LangSysTag value="VIT "/>
+          <LangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=4 -->
+            <FeatureIndex index="0" value="3"/>
+            <FeatureIndex index="1" value="4"/>
+            <FeatureIndex index="2" value="5"/>
+            <FeatureIndex index="3" value="0"/>
+          </LangSys>
+        </LangSysRecord>
+      </Script>
+    </ScriptRecord>
+  </ScriptList>
+</GPOS>
diff --git a/Tests/mtiLib/data/mti/scripttable.ttx.GSUB b/Tests/mtiLib/data/mti/scripttable.ttx.GSUB
new file mode 100644
index 0000000..2e2ef71
--- /dev/null
+++ b/Tests/mtiLib/data/mti/scripttable.ttx.GSUB
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<GSUB>
+  <Version value="0x00010000"/>
+  <ScriptList>
+    <!-- ScriptCount=3 -->
+    <ScriptRecord index="0">
+      <ScriptTag value="cyrl"/>
+      <Script>
+        <DefaultLangSys>
+          <ReqFeatureIndex value="65535"/>
+          <!-- FeatureCount=3 -->
+          <FeatureIndex index="0" value="3"/>
+          <FeatureIndex index="1" value="4"/>
+          <FeatureIndex index="2" value="1"/>
+        </DefaultLangSys>
+        <!-- LangSysCount=0 -->
+      </Script>
+    </ScriptRecord>
+    <ScriptRecord index="1">
+      <ScriptTag value="grek"/>
+      <Script>
+        <DefaultLangSys>
+          <ReqFeatureIndex value="65535"/>
+          <!-- FeatureCount=3 -->
+          <FeatureIndex index="0" value="3"/>
+          <FeatureIndex index="1" value="4"/>
+          <FeatureIndex index="2" value="2"/>
+        </DefaultLangSys>
+        <!-- LangSysCount=0 -->
+      </Script>
+    </ScriptRecord>
+    <ScriptRecord index="2">
+      <ScriptTag value="latn"/>
+      <Script>
+        <DefaultLangSys>
+          <ReqFeatureIndex value="65535"/>
+          <!-- FeatureCount=3 -->
+          <FeatureIndex index="0" value="3"/>
+          <FeatureIndex index="1" value="4"/>
+          <FeatureIndex index="2" value="0"/>
+        </DefaultLangSys>
+        <!-- LangSysCount=4 -->
+        <LangSysRecord index="0">
+          <LangSysTag value="DEU "/>
+          <LangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=3 -->
+            <FeatureIndex index="0" value="3"/>
+            <FeatureIndex index="1" value="4"/>
+            <FeatureIndex index="2" value="0"/>
+          </LangSys>
+        </LangSysRecord>
+        <LangSysRecord index="1">
+          <LangSysTag value="ROM "/>
+          <LangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=3 -->
+            <FeatureIndex index="0" value="3"/>
+            <FeatureIndex index="1" value="4"/>
+            <FeatureIndex index="2" value="0"/>
+          </LangSys>
+        </LangSysRecord>
+        <LangSysRecord index="2">
+          <LangSysTag value="TRK "/>
+          <LangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=3 -->
+            <FeatureIndex index="0" value="3"/>
+            <FeatureIndex index="1" value="4"/>
+            <FeatureIndex index="2" value="0"/>
+          </LangSys>
+        </LangSysRecord>
+        <LangSysRecord index="3">
+          <LangSysTag value="VIT "/>
+          <LangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=4 -->
+            <FeatureIndex index="0" value="3"/>
+            <FeatureIndex index="1" value="4"/>
+            <FeatureIndex index="2" value="5"/>
+            <FeatureIndex index="3" value="0"/>
+          </LangSys>
+        </LangSysRecord>
+      </Script>
+    </ScriptRecord>
+  </ScriptList>
+</GSUB>
diff --git a/Tests/mtiLib/data/mti/scripttable.txt b/Tests/mtiLib/data/mti/scripttable.txt
new file mode 100644
index 0000000..3955ff4
--- /dev/null
+++ b/Tests/mtiLib/data/mti/scripttable.txt
Binary files differ
diff --git a/Tests/mtiLib/mti_test.py b/Tests/mtiLib/mti_test.py
new file mode 100644
index 0000000..4916828
--- /dev/null
+++ b/Tests/mtiLib/mti_test.py
@@ -0,0 +1,236 @@
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.misc.xmlWriter import XMLWriter
+from fontTools.ttLib import TTFont
+from fontTools import mtiLib
+import difflib
+import os
+import sys
+import unittest
+
+
+class MtiTest(unittest.TestCase):
+
+    GLYPH_ORDER = ['.notdef',
+        'a', 'b', 'pakannada', 'phakannada', 'vakannada', 'pevowelkannada',
+        'phevowelkannada', 'vevowelkannada', 'uvowelsignkannada', 'uuvowelsignkannada',
+        'uvowelsignaltkannada', 'uuvowelsignaltkannada', 'uuvowelsignsinh',
+        'uvowelsignsinh', 'rakarsinh', 'zero', 'one', 'two', 'three', 'four', 'five',
+        'six', 'seven', 'eight', 'nine', 'slash', 'fraction', 'A', 'B', 'C', 'fi',
+        'fl', 'breve', 'acute', 'uniFB01', 'ffi', 'grave', 'commaacent', 'dotbelow',
+        'dotabove', 'cedilla', 'commaaccent', 'Acircumflex', 'V', 'T', 'acircumflex',
+        'Aacute', 'Agrave', 'O', 'Oacute', 'Ograve', 'Ocircumflex', 'aacute', 'agrave',
+        'aimatrabindigurmukhi', 'aimatragurmukhi', 'aimatratippigurmukhi',
+        'aumatrabindigurmukhi', 'aumatragurmukhi', 'bindigurmukhi',
+        'eematrabindigurmukhi', 'eematragurmukhi', 'eematratippigurmukhi',
+        'oomatrabindigurmukhi', 'oomatragurmukhi', 'oomatratippigurmukhi',
+        'lagurmukhi', 'lanuktagurmukhi', 'nagurmukhi', 'nanuktagurmukhi',
+        'ngagurmukhi', 'nganuktagurmukhi', 'nnagurmukhi', 'nnanuktagurmukhi',
+        'tthagurmukhi', 'tthanuktagurmukhi', 'bsuperior', 'isuperior', 'vsuperior',
+        'wsuperior', 'periodsuperior', 'osuperior', 'tsuperior', 'dollarsuperior',
+        'fsuperior', 'gsuperior', 'zsuperior', 'dsuperior', 'psuperior', 'hsuperior',
+        'oesuperior', 'aesuperior', 'centsuperior', 'esuperior', 'lsuperior',
+        'qsuperior', 'csuperior', 'asuperior', 'commasuperior', 'xsuperior',
+        'egravesuperior', 'usuperior', 'rsuperior', 'nsuperior', 'ssuperior',
+        'msuperior', 'jsuperior', 'ysuperior', 'ksuperior', 'guilsinglright',
+        'guilsinglleft', 'uniF737', 'uniE11C', 'uniE11D', 'uniE11A', 'uni2077',
+        'uni2087', 'uniE11B', 'uniE119', 'uniE0DD', 'uniE0DE', 'uniF736', 'uniE121',
+        'uniE122', 'uniE11F', 'uni2076', 'uni2086', 'uniE120', 'uniE11E', 'uniE0DB',
+        'uniE0DC', 'uniF733', 'uniE12B', 'uniE12C', 'uniE129', 'uni00B3', 'uni2083',
+        'uniE12A', 'uniE128', 'uniF732', 'uniE133', 'uniE134', 'uniE131', 'uni00B2',
+        'uni2082', 'uniE132', 'uniE130', 'uniE0F9', 'uniF734', 'uniE0D4', 'uniE0D5',
+        'uniE0D2', 'uni2074', 'uni2084', 'uniE0D3', 'uniE0D1', 'uniF730', 'uniE13D',
+        'uniE13E', 'uniE13A', 'uni2070', 'uni2080', 'uniE13B', 'uniE139', 'uniE13C',
+        'uniF739', 'uniE0EC', 'uniE0ED', 'uniE0EA', 'uni2079', 'uni2089', 'uniE0EB',
+        'uniE0E9', 'uniF735', 'uniE0CD', 'uniE0CE', 'uniE0CB', 'uni2075', 'uni2085',
+        'uniE0CC', 'uniE0CA', 'uniF731', 'uniE0F3', 'uniE0F4', 'uniE0F1', 'uni00B9',
+        'uni2081', 'uniE0F2', 'uniE0F0', 'uniE0F8', 'uniF738', 'uniE0C0', 'uniE0C1',
+        'uniE0BE', 'uni2078', 'uni2088', 'uniE0BF', 'uniE0BD', 'I', 'Ismall', 't', 'i',
+        'f', 'IJ', 'J', 'IJsmall', 'Jsmall', 'tt', 'ij', 'j', 'ffb', 'ffh', 'h', 'ffk',
+        'k', 'ffl', 'l', 'fft', 'fb', 'ff', 'fh', 'fj', 'fk', 'ft', 'janyevoweltelugu',
+        'kassevoweltelugu', 'jaivoweltelugu', 'nyasubscripttelugu', 'kaivoweltelugu',
+        'ssasubscripttelugu', 'bayi1', 'jeemi1', 'kafi1', 'ghafi1', 'laami1', 'kafm1',
+        'ghafm1', 'laamm1', 'rayf2', 'reyf2', 'yayf2', 'zayf2', 'fayi1', 'ayehf2',
+        'hamzayeharabf2', 'hamzayehf2', 'yehf2', 'ray', 'rey', 'zay', 'yay', 'dal',
+        'del', 'zal', 'rayf1', 'reyf1', 'yayf1', 'zayf1', 'ayehf1', 'hamzayeharabf1',
+        'hamzayehf1', 'yehf1', 'dal1', 'del1', 'zal1', 'onehalf', 'onehalf.alt',
+        'onequarter', 'onequarter.alt', 'threequarters', 'threequarters.alt',
+        'AlefSuperiorNS', 'DammaNS', 'DammaRflxNS', 'DammatanNS', 'Fatha2dotsNS',
+        'FathaNS', 'FathatanNS', 'FourDotsAboveNS', 'HamzaAboveNS', 'MaddaNS',
+        'OneDotAbove2NS', 'OneDotAboveNS', 'ShaddaAlefNS', 'ShaddaDammaNS',
+        'ShaddaDammatanNS', 'ShaddaFathatanNS', 'ShaddaKasraNS', 'ShaddaKasratanNS',
+        'ShaddaNS', 'SharetKafNS', 'SukunNS', 'ThreeDotsDownAboveNS',
+        'ThreeDotsUpAboveNS', 'TwoDotsAboveNS', 'TwoDotsVerticalAboveNS', 'UltapeshNS',
+        'WaslaNS', 'AinIni.12m_MeemFin.02', 'AinIni_YehBarreeFin',
+        'AinMed_YehBarreeFin', 'BehxIni_MeemFin', 'BehxIni_NoonGhunnaFin',
+        'BehxIni_RehFin', 'BehxIni_RehFin.b', 'BehxMed_MeemFin.py',
+        'BehxMed_NoonGhunnaFin', 'BehxMed_NoonGhunnaFin.cup', 'BehxMed_RehFin',
+        'BehxMed_RehFin.cup', 'BehxMed_YehxFin', 'FehxMed_YehBarreeFin',
+        'HahIni_YehBarreeFin', 'KafIni_YehBarreeFin', 'KafMed.12_YehxFin.01',
+        'KafMed_MeemFin', 'KafMed_YehBarreeFin', 'LamAlefFin', 'LamAlefFin.cup',
+        'LamAlefFin.cut', 'LamAlefFin.short', 'LamAlefSep', 'LamIni_MeemFin',
+        'LamIni_YehBarreeFin', 'LamMed_MeemFin', 'LamMed_MeemFin.b', 'LamMed_YehxFin',
+        'LamMed_YehxFin.cup', 'TahIni_YehBarreeFin', 'null', 'CR', 'space',
+        'exclam', 'quotedbl', 'numbersign',
+    ]
+
+    # Feature files in data/*.txt; output gets compared to data/*.ttx.
+    TESTS = {
+        None: (
+            'mti/cmap',
+        ),
+        'cmap': (
+            'mti/cmap',
+        ),
+        'GSUB': (
+            'featurename-backward',
+            'featurename-forward',
+            'lookupnames-backward',
+            'lookupnames-forward',
+            'mixed-toplevels',
+
+            'mti/scripttable',
+            'mti/chainedclass',
+            'mti/chainedcoverage',
+            'mti/chained-glyph',
+            'mti/gsubalternate',
+            'mti/gsubligature',
+            'mti/gsubmultiple',
+            'mti/gsubreversechanined',
+            'mti/gsubsingle',
+        ),
+        'GPOS': (
+            'mti/scripttable',
+            'mti/chained-glyph',
+            'mti/gposcursive',
+            'mti/gposkernset',
+            'mti/gposmarktobase',
+            'mti/gpospairclass',
+            'mti/gpospairglyph',
+            'mti/gpossingle',
+            'mti/mark-to-ligature',
+        ),
+        'GDEF': (
+            'mti/gdefattach',
+            'mti/gdefclasses',
+            'mti/gdefligcaret',
+            'mti/gdefmarkattach',
+            'mti/gdefmarkfilter',
+        ),
+    }
+    # TODO:
+    # https://github.com/Monotype/OpenType_Table_Source/issues/12
+    #
+    #        'mti/featuretable'
+    #        'mti/contextclass'
+    #        'mti/contextcoverage'
+    #        'mti/context-glyph'
+
+    def __init__(self, methodName):
+        unittest.TestCase.__init__(self, methodName)
+        # Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
+        # and fires deprecation warnings if a program uses the old name.
+        if not hasattr(self, "assertRaisesRegex"):
+            self.assertRaisesRegex = self.assertRaisesRegexp
+
+    def setUp(self):
+        pass
+
+    def tearDown(self):
+        pass
+
+    @staticmethod
+    def getpath(testfile):
+        path, _ = os.path.split(__file__)
+        return os.path.join(path, "data", testfile)
+
+    def expect_ttx(self, expected_ttx, actual_ttx, fromfile=None, tofile=None):
+        expected = [l+'\n' for l in expected_ttx.split('\n')]
+        actual = [l+'\n' for l in actual_ttx.split('\n')]
+        if actual != expected:
+            sys.stderr.write('\n')
+            for line in difflib.unified_diff(
+                    expected, actual, fromfile=fromfile, tofile=tofile):
+                sys.stderr.write(line)
+            self.fail("TTX output is different from expected")
+
+    @classmethod
+    def create_font(celf):
+        font = TTFont()
+        font.setGlyphOrder(celf.GLYPH_ORDER)
+        return font
+
+    def check_mti_file(self, name, tableTag=None):
+
+        xml_expected_path = self.getpath("%s.ttx" % name + ('.'+tableTag if tableTag is not None else ''))
+        with open(xml_expected_path, 'rt', encoding="utf-8") as xml_expected_file:
+            xml_expected = xml_expected_file.read()
+
+        font = self.create_font()
+
+        with open(self.getpath("%s.txt" % name), 'rt', encoding="utf-8") as f:
+            table = mtiLib.build(f, font, tableTag=tableTag)
+
+        if tableTag is not None:
+            self.assertEqual(tableTag, table.tableTag)
+        tableTag = table.tableTag
+
+        # Make sure it compiles.
+        blob = table.compile(font)
+
+        # Make sure it decompiles.
+        decompiled = table.__class__()
+        decompiled.decompile(blob, font)
+
+        # XML from built object.
+        writer = XMLWriter(StringIO(), newlinestr='\n')
+        writer.begintag(tableTag); writer.newline()
+        table.toXML(writer, font)
+        writer.endtag(tableTag); writer.newline()
+        xml_built = writer.file.getvalue()
+
+        # XML from decompiled object.
+        writer = XMLWriter(StringIO(), newlinestr='\n')
+        writer.begintag(tableTag); writer.newline()
+        decompiled.toXML(writer, font)
+        writer.endtag(tableTag); writer.newline()
+        xml_binary = writer.file.getvalue()
+
+        self.expect_ttx(xml_binary,   xml_built, fromfile='decompiled',      tofile='built')
+        self.expect_ttx(xml_expected, xml_built, fromfile=xml_expected_path, tofile='built')
+
+        from fontTools.misc import xmlReader
+        f = StringIO()
+        f.write(xml_expected)
+        f.seek(0)
+        font2 = TTFont()
+        font2.setGlyphOrder(font.getGlyphOrder())
+        reader = xmlReader.XMLReader(f, font2)
+        reader.read(rootless=True)
+
+        # XML from object read from XML.
+        writer = XMLWriter(StringIO(), newlinestr='\n')
+        writer.begintag(tableTag); writer.newline()
+        font2[tableTag].toXML(writer, font)
+        writer.endtag(tableTag); writer.newline()
+        xml_fromxml = writer.file.getvalue()
+
+        self.expect_ttx(xml_expected, xml_fromxml, fromfile=xml_expected_path, tofile='fromxml')
+
+def generate_mti_file_test(name, tableTag=None):
+    return lambda self: self.check_mti_file(os.path.join(*name.split('/')), tableTag=tableTag)
+
+
+for tableTag,tests in MtiTest.TESTS.items():
+    for name in tests:
+        setattr(MtiTest, "test_MtiFile_%s%s" % (name, '_'+tableTag if tableTag else ''),
+                generate_mti_file_test(name, tableTag=tableTag))
+
+
+if __name__ == "__main__":
+    if len(sys.argv) > 1:
+        from fontTools.mtiLib import main
+        font = MtiTest.create_font()
+        sys.exit(main(sys.argv[1:], font))
+    sys.exit(unittest.main())
diff --git a/Tests/otlLib/builder_test.py b/Tests/otlLib/builder_test.py
new file mode 100644
index 0000000..63a35d6
--- /dev/null
+++ b/Tests/otlLib/builder_test.py
@@ -0,0 +1,1062 @@
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+from fontTools.misc.testTools import getXML
+from fontTools.otlLib import builder
+from fontTools.ttLib.tables import otTables
+from itertools import chain
+import unittest
+
+
+class BuilderTest(unittest.TestCase):
+    GLYPHS = (".notdef space zero one two three four five six "
+              "A B C a b c grave acute cedilla f_f_i f_i c_t").split()
+    GLYPHMAP = {name: num for num, name in enumerate(GLYPHS)}
+
+    ANCHOR1 = builder.buildAnchor(11, -11)
+    ANCHOR2 = builder.buildAnchor(22, -22)
+    ANCHOR3 = builder.buildAnchor(33, -33)
+
+    def __init__(self, methodName):
+        unittest.TestCase.__init__(self, methodName)
+        # Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
+        # and fires deprecation warnings if a program uses the old name.
+        if not hasattr(self, "assertRaisesRegex"):
+            self.assertRaisesRegex = self.assertRaisesRegexp
+
+    @classmethod
+    def setUpClass(cls):
+        cls.maxDiff = None
+
+    def test_buildAnchor_format1(self):
+        anchor = builder.buildAnchor(23, 42)
+        self.assertEqual(getXML(anchor.toXML),
+                         ['<Anchor Format="1">',
+                          '  <XCoordinate value="23"/>',
+                          '  <YCoordinate value="42"/>',
+                          '</Anchor>'])
+
+    def test_buildAnchor_format2(self):
+        anchor = builder.buildAnchor(23, 42, point=17)
+        self.assertEqual(getXML(anchor.toXML),
+                         ['<Anchor Format="2">',
+                          '  <XCoordinate value="23"/>',
+                          '  <YCoordinate value="42"/>',
+                          '  <AnchorPoint value="17"/>',
+                          '</Anchor>'])
+
+    def test_buildAnchor_format3(self):
+        anchor = builder.buildAnchor(
+            23, 42,
+            deviceX=builder.buildDevice({1: 1, 0: 0}),
+            deviceY=builder.buildDevice({7: 7}))
+        self.assertEqual(getXML(anchor.toXML),
+                         ['<Anchor Format="3">',
+                          '  <XCoordinate value="23"/>',
+                          '  <YCoordinate value="42"/>',
+                          '  <XDeviceTable>',
+                          '    <StartSize value="0"/>',
+                          '    <EndSize value="1"/>',
+                          '    <DeltaFormat value="1"/>',
+                          '    <DeltaValue value="[0, 1]"/>',
+                          '  </XDeviceTable>',
+                          '  <YDeviceTable>',
+                          '    <StartSize value="7"/>',
+                          '    <EndSize value="7"/>',
+                          '    <DeltaFormat value="2"/>',
+                          '    <DeltaValue value="[7]"/>',
+                          '  </YDeviceTable>',
+                          '</Anchor>'])
+
+    def test_buildAttachList(self):
+        attachList = builder.buildAttachList({
+            "zero": [23, 7], "one": [1],
+        }, self.GLYPHMAP)
+        self.assertEqual(getXML(attachList.toXML),
+                         ['<AttachList>',
+                          '  <Coverage>',
+                          '    <Glyph value="zero"/>',
+                          '    <Glyph value="one"/>',
+                          '  </Coverage>',
+                          '  <!-- GlyphCount=2 -->',
+                          '  <AttachPoint index="0">',
+                          '    <!-- PointCount=2 -->',
+                          '    <PointIndex index="0" value="7"/>',
+                          '    <PointIndex index="1" value="23"/>',
+                          '  </AttachPoint>',
+                          '  <AttachPoint index="1">',
+                          '    <!-- PointCount=1 -->',
+                          '    <PointIndex index="0" value="1"/>',
+                          '  </AttachPoint>',
+                          '</AttachList>'])
+
+    def test_buildAttachList_empty(self):
+        self.assertIsNone(builder.buildAttachList({}, self.GLYPHMAP))
+
+    def test_buildAttachPoint(self):
+        attachPoint = builder.buildAttachPoint([7, 3])
+        self.assertEqual(getXML(attachPoint.toXML),
+                         ['<AttachPoint>',
+                          '  <!-- PointCount=2 -->',
+                          '  <PointIndex index="0" value="3"/>',
+                          '  <PointIndex index="1" value="7"/>',
+                          '</AttachPoint>'])
+
+    def test_buildAttachPoint_empty(self):
+        self.assertIsNone(builder.buildAttachPoint([]))
+
+    def test_buildAttachPoint_duplicate(self):
+        attachPoint = builder.buildAttachPoint([7, 3, 7])
+        self.assertEqual(getXML(attachPoint.toXML),
+                         ['<AttachPoint>',
+                          '  <!-- PointCount=2 -->',
+                          '  <PointIndex index="0" value="3"/>',
+                          '  <PointIndex index="1" value="7"/>',
+                          '</AttachPoint>'])
+
+
+    def test_buildBaseArray(self):
+        anchor = builder.buildAnchor
+        baseArray = builder.buildBaseArray({
+            "a": {2: anchor(300, 80)},
+            "c": {1: anchor(300, 80), 2: anchor(300, -20)}
+        }, numMarkClasses=4, glyphMap=self.GLYPHMAP)
+        self.assertEqual(getXML(baseArray.toXML),
+                         ['<BaseArray>',
+                          '  <!-- BaseCount=2 -->',
+                          '  <BaseRecord index="0">',
+                          '    <BaseAnchor index="0" empty="1"/>',
+                          '    <BaseAnchor index="1" empty="1"/>',
+                          '    <BaseAnchor index="2" Format="1">',
+                          '      <XCoordinate value="300"/>',
+                          '      <YCoordinate value="80"/>',
+                          '    </BaseAnchor>',
+                          '    <BaseAnchor index="3" empty="1"/>',
+                          '  </BaseRecord>',
+                          '  <BaseRecord index="1">',
+                          '    <BaseAnchor index="0" empty="1"/>',
+                          '    <BaseAnchor index="1" Format="1">',
+                          '      <XCoordinate value="300"/>',
+                          '      <YCoordinate value="80"/>',
+                          '    </BaseAnchor>',
+                          '    <BaseAnchor index="2" Format="1">',
+                          '      <XCoordinate value="300"/>',
+                          '      <YCoordinate value="-20"/>',
+                          '    </BaseAnchor>',
+                          '    <BaseAnchor index="3" empty="1"/>',
+                          '  </BaseRecord>',
+                          '</BaseArray>'])
+
+    def test_buildBaseRecord(self):
+        a = builder.buildAnchor
+        rec = builder.buildBaseRecord([a(500, -20), None, a(300, -15)])
+        self.assertEqual(getXML(rec.toXML),
+                         ['<BaseRecord>',
+                          '  <BaseAnchor index="0" Format="1">',
+                          '    <XCoordinate value="500"/>',
+                          '    <YCoordinate value="-20"/>',
+                          '  </BaseAnchor>',
+                          '  <BaseAnchor index="1" empty="1"/>',
+                          '  <BaseAnchor index="2" Format="1">',
+                          '    <XCoordinate value="300"/>',
+                          '    <YCoordinate value="-15"/>',
+                          '  </BaseAnchor>',
+                          '</BaseRecord>'])
+
+    def test_buildCaretValueForCoord(self):
+        caret = builder.buildCaretValueForCoord(500)
+        self.assertEqual(getXML(caret.toXML),
+                         ['<CaretValue Format="1">',
+                          '  <Coordinate value="500"/>',
+                          '</CaretValue>'])
+
+    def test_buildCaretValueForPoint(self):
+        caret = builder.buildCaretValueForPoint(23)
+        self.assertEqual(getXML(caret.toXML),
+                         ['<CaretValue Format="2">',
+                          '  <CaretValuePoint value="23"/>',
+                          '</CaretValue>'])
+
+    def test_buildComponentRecord(self):
+        a = builder.buildAnchor
+        rec = builder.buildComponentRecord([a(500, -20), None, a(300, -15)])
+        self.assertEqual(getXML(rec.toXML),
+                         ['<ComponentRecord>',
+                          '  <LigatureAnchor index="0" Format="1">',
+                          '    <XCoordinate value="500"/>',
+                          '    <YCoordinate value="-20"/>',
+                          '  </LigatureAnchor>',
+                          '  <LigatureAnchor index="1" empty="1"/>',
+                          '  <LigatureAnchor index="2" Format="1">',
+                          '    <XCoordinate value="300"/>',
+                          '    <YCoordinate value="-15"/>',
+                          '  </LigatureAnchor>',
+                          '</ComponentRecord>'])
+
+    def test_buildComponentRecord_empty(self):
+        self.assertIsNone(builder.buildComponentRecord([]))
+
+    def test_buildComponentRecord_None(self):
+        self.assertIsNone(builder.buildComponentRecord(None))
+
+    def test_buildCoverage(self):
+        cov = builder.buildCoverage({"two", "four"}, {"two": 2, "four": 4})
+        self.assertEqual(getXML(cov.toXML),
+                         ['<Coverage>',
+                          '  <Glyph value="two"/>',
+                          '  <Glyph value="four"/>',
+                          '</Coverage>'])
+
+    def test_buildCursivePos(self):
+        pos = builder.buildCursivePosSubtable({
+            "two": (self.ANCHOR1, self.ANCHOR2),
+            "four": (self.ANCHOR3, self.ANCHOR1)
+        }, self.GLYPHMAP)
+        self.assertEqual(getXML(pos.toXML),
+                         ['<CursivePos Format="1">',
+                          '  <Coverage>',
+                          '    <Glyph value="two"/>',
+                          '    <Glyph value="four"/>',
+                          '  </Coverage>',
+                          '  <!-- EntryExitCount=2 -->',
+                          '  <EntryExitRecord index="0">',
+                          '    <EntryAnchor Format="1">',
+                          '      <XCoordinate value="11"/>',
+                          '      <YCoordinate value="-11"/>',
+                          '    </EntryAnchor>',
+                          '    <ExitAnchor Format="1">',
+                          '      <XCoordinate value="22"/>',
+                          '      <YCoordinate value="-22"/>',
+                          '    </ExitAnchor>',
+                          '  </EntryExitRecord>',
+                          '  <EntryExitRecord index="1">',
+                          '    <EntryAnchor Format="1">',
+                          '      <XCoordinate value="33"/>',
+                          '      <YCoordinate value="-33"/>',
+                          '    </EntryAnchor>',
+                          '    <ExitAnchor Format="1">',
+                          '      <XCoordinate value="11"/>',
+                          '      <YCoordinate value="-11"/>',
+                          '    </ExitAnchor>',
+                          '  </EntryExitRecord>',
+                          '</CursivePos>'])
+
+    def test_buildDevice_format1(self):
+        device = builder.buildDevice({1:1, 0:0})
+        self.assertEqual(getXML(device.toXML),
+                         ['<Device>',
+                          '  <StartSize value="0"/>',
+                          '  <EndSize value="1"/>',
+                          '  <DeltaFormat value="1"/>',
+                          '  <DeltaValue value="[0, 1]"/>',
+                          '</Device>'])
+
+    def test_buildDevice_format2(self):
+        device = builder.buildDevice({2:2, 0:1, 1:0})
+        self.assertEqual(getXML(device.toXML),
+                         ['<Device>',
+                          '  <StartSize value="0"/>',
+                          '  <EndSize value="2"/>',
+                          '  <DeltaFormat value="2"/>',
+                          '  <DeltaValue value="[1, 0, 2]"/>',
+                          '</Device>'])
+
+    def test_buildDevice_format3(self):
+        device = builder.buildDevice({5:3, 1:77})
+        self.assertEqual(getXML(device.toXML),
+                         ['<Device>',
+                          '  <StartSize value="1"/>',
+                          '  <EndSize value="5"/>',
+                          '  <DeltaFormat value="3"/>',
+                          '  <DeltaValue value="[77, 0, 0, 0, 3]"/>',
+                          '</Device>'])
+
+    def test_buildLigatureArray(self):
+        anchor = builder.buildAnchor
+        ligatureArray = builder.buildLigatureArray({
+            "f_i": [{2: anchor(300, -20)}, {}],
+            "c_t": [{}, {1: anchor(500, 350), 2: anchor(1300, -20)}]
+        }, numMarkClasses=4, glyphMap=self.GLYPHMAP)
+        self.assertEqual(getXML(ligatureArray.toXML),
+                         ['<LigatureArray>',
+                          '  <!-- LigatureCount=2 -->',
+                          '  <LigatureAttach index="0">',  # f_i
+                          '    <!-- ComponentCount=2 -->',
+                          '    <ComponentRecord index="0">',
+                          '      <LigatureAnchor index="0" empty="1"/>',
+                          '      <LigatureAnchor index="1" empty="1"/>',
+                          '      <LigatureAnchor index="2" Format="1">',
+                          '        <XCoordinate value="300"/>',
+                          '        <YCoordinate value="-20"/>',
+                          '      </LigatureAnchor>',
+                          '      <LigatureAnchor index="3" empty="1"/>',
+                          '    </ComponentRecord>',
+                          '    <ComponentRecord index="1">',
+                          '      <LigatureAnchor index="0" empty="1"/>',
+                          '      <LigatureAnchor index="1" empty="1"/>',
+                          '      <LigatureAnchor index="2" empty="1"/>',
+                          '      <LigatureAnchor index="3" empty="1"/>',
+                          '    </ComponentRecord>',
+                          '  </LigatureAttach>',
+                          '  <LigatureAttach index="1">',
+                          '    <!-- ComponentCount=2 -->',
+                          '    <ComponentRecord index="0">',
+                          '      <LigatureAnchor index="0" empty="1"/>',
+                          '      <LigatureAnchor index="1" empty="1"/>',
+                          '      <LigatureAnchor index="2" empty="1"/>',
+                          '      <LigatureAnchor index="3" empty="1"/>',
+                          '    </ComponentRecord>',
+                          '    <ComponentRecord index="1">',
+                          '      <LigatureAnchor index="0" empty="1"/>',
+                          '      <LigatureAnchor index="1" Format="1">',
+                          '        <XCoordinate value="500"/>',
+                          '        <YCoordinate value="350"/>',
+                          '      </LigatureAnchor>',
+                          '      <LigatureAnchor index="2" Format="1">',
+                          '        <XCoordinate value="1300"/>',
+                          '        <YCoordinate value="-20"/>',
+                          '      </LigatureAnchor>',
+                          '      <LigatureAnchor index="3" empty="1"/>',
+                          '    </ComponentRecord>',
+                          '  </LigatureAttach>',
+                          '</LigatureArray>'])
+
+    def test_buildLigatureAttach(self):
+        anchor = builder.buildAnchor
+        attach = builder.buildLigatureAttach([
+            [anchor(500, -10), None],
+            [None, anchor(300, -20), None]])
+        self.assertEqual(getXML(attach.toXML),
+                         ['<LigatureAttach>',
+                          '  <!-- ComponentCount=2 -->',
+                          '  <ComponentRecord index="0">',
+                          '    <LigatureAnchor index="0" Format="1">',
+                          '      <XCoordinate value="500"/>',
+                          '      <YCoordinate value="-10"/>',
+                          '    </LigatureAnchor>',
+                          '    <LigatureAnchor index="1" empty="1"/>',
+                          '  </ComponentRecord>',
+                          '  <ComponentRecord index="1">',
+                          '    <LigatureAnchor index="0" empty="1"/>',
+                          '    <LigatureAnchor index="1" Format="1">',
+                          '      <XCoordinate value="300"/>',
+                          '      <YCoordinate value="-20"/>',
+                          '    </LigatureAnchor>',
+                          '    <LigatureAnchor index="2" empty="1"/>',
+                          '  </ComponentRecord>',
+                          '</LigatureAttach>'])
+
+    def test_buildLigatureAttach_emptyComponents(self):
+        attach = builder.buildLigatureAttach([[], None])
+        self.assertEqual(getXML(attach.toXML),
+                         ['<LigatureAttach>',
+                          '  <!-- ComponentCount=2 -->',
+                          '  <ComponentRecord index="0" empty="1"/>',
+                          '  <ComponentRecord index="1" empty="1"/>',
+                          '</LigatureAttach>'])
+
+    def test_buildLigatureAttach_noComponents(self):
+        attach = builder.buildLigatureAttach([])
+        self.assertEqual(getXML(attach.toXML),
+                         ['<LigatureAttach>',
+                          '  <!-- ComponentCount=0 -->',
+                          '</LigatureAttach>'])
+
+    def test_buildLigCaretList(self):
+        carets = builder.buildLigCaretList(
+            {"f_f_i": [300, 600]}, {"c_t": [42]}, self.GLYPHMAP)
+        self.assertEqual(getXML(carets.toXML),
+                         ['<LigCaretList>',
+                          '  <Coverage>',
+                          '    <Glyph value="f_f_i"/>',
+                          '    <Glyph value="c_t"/>',
+                          '  </Coverage>',
+                          '  <!-- LigGlyphCount=2 -->',
+                          '  <LigGlyph index="0">',
+                          '    <!-- CaretCount=2 -->',
+                          '    <CaretValue index="0" Format="1">',
+                          '      <Coordinate value="300"/>',
+                          '    </CaretValue>',
+                          '    <CaretValue index="1" Format="1">',
+                          '      <Coordinate value="600"/>',
+                          '    </CaretValue>',
+                          '  </LigGlyph>',
+                          '  <LigGlyph index="1">',
+                          '    <!-- CaretCount=1 -->',
+                          '    <CaretValue index="0" Format="2">',
+                          '      <CaretValuePoint value="42"/>',
+                          '    </CaretValue>',
+                          '  </LigGlyph>',
+                          '</LigCaretList>'])
+
+    def test_buildLigCaretList_bothCoordsAndPointsForSameGlyph(self):
+        carets = builder.buildLigCaretList(
+            {"f_f_i": [300]}, {"f_f_i": [7]}, self.GLYPHMAP)
+        self.assertEqual(getXML(carets.toXML),
+                         ['<LigCaretList>',
+                          '  <Coverage>',
+                          '    <Glyph value="f_f_i"/>',
+                          '  </Coverage>',
+                          '  <!-- LigGlyphCount=1 -->',
+                          '  <LigGlyph index="0">',
+                          '    <!-- CaretCount=2 -->',
+                          '    <CaretValue index="0" Format="1">',
+                          '      <Coordinate value="300"/>',
+                          '    </CaretValue>',
+                          '    <CaretValue index="1" Format="2">',
+                          '      <CaretValuePoint value="7"/>',
+                          '    </CaretValue>',
+                          '  </LigGlyph>',
+                          '</LigCaretList>'])
+
+    def test_buildLigCaretList_empty(self):
+        self.assertIsNone(builder.buildLigCaretList({}, {}, self.GLYPHMAP))
+
+    def test_buildLigCaretList_None(self):
+        self.assertIsNone(builder.buildLigCaretList(None, None, self.GLYPHMAP))
+
+    def test_buildLigGlyph_coords(self):
+        lig = builder.buildLigGlyph([500, 800], None)
+        self.assertEqual(getXML(lig.toXML),
+                         ['<LigGlyph>',
+                          '  <!-- CaretCount=2 -->',
+                          '  <CaretValue index="0" Format="1">',
+                          '    <Coordinate value="500"/>',
+                          '  </CaretValue>',
+                          '  <CaretValue index="1" Format="1">',
+                          '    <Coordinate value="800"/>',
+                          '  </CaretValue>',
+                          '</LigGlyph>'])
+
+    def test_buildLigGlyph_empty(self):
+        self.assertIsNone(builder.buildLigGlyph([], []))
+
+    def test_buildLigGlyph_None(self):
+        self.assertIsNone(builder.buildLigGlyph(None, None))
+
+    def test_buildLigGlyph_points(self):
+        lig = builder.buildLigGlyph(None, [2])
+        self.assertEqual(getXML(lig.toXML),
+                         ['<LigGlyph>',
+                          '  <!-- CaretCount=1 -->',
+                          '  <CaretValue index="0" Format="2">',
+                          '    <CaretValuePoint value="2"/>',
+                          '  </CaretValue>',
+                          '</LigGlyph>'])
+
+    def test_buildLookup(self):
+        s1 = builder.buildSingleSubstSubtable({"one": "two"})
+        s2 = builder.buildSingleSubstSubtable({"three": "four"})
+        lookup = builder.buildLookup([s1, s2], flags=7)
+        self.assertEqual(getXML(lookup.toXML),
+                         ['<Lookup>',
+                          '  <LookupType value="1"/>',
+                          '  <LookupFlag value="7"/>',
+                          '  <!-- SubTableCount=2 -->',
+                          '  <SingleSubst index="0">',
+                          '    <Substitution in="one" out="two"/>',
+                          '  </SingleSubst>',
+                          '  <SingleSubst index="1">',
+                          '    <Substitution in="three" out="four"/>',
+                          '  </SingleSubst>',
+                          '</Lookup>'])
+
+    def test_buildLookup_badFlags(self):
+        s = builder.buildSingleSubstSubtable({"one": "two"})
+        self.assertRaisesRegex(
+            AssertionError, "if markFilterSet is None, "
+            "flags must not set LOOKUP_FLAG_USE_MARK_FILTERING_SET; "
+            "flags=0x0010",
+            builder.buildLookup, [s],
+            builder.LOOKUP_FLAG_USE_MARK_FILTERING_SET, None)
+        self.assertRaisesRegex(
+            AssertionError, "if markFilterSet is not None, "
+            "flags must set LOOKUP_FLAG_USE_MARK_FILTERING_SET; "
+            "flags=0x0004",
+            builder.buildLookup, [s],
+            builder.LOOKUP_FLAG_IGNORE_LIGATURES, 777)
+
+    def test_buildLookup_conflictingSubtableTypes(self):
+        s1 = builder.buildSingleSubstSubtable({"one": "two"})
+        s2 = builder.buildAlternateSubstSubtable({"one": ["two", "three"]})
+        self.assertRaisesRegex(
+            AssertionError, "all subtables must have the same LookupType",
+            builder.buildLookup, [s1, s2])
+
+    def test_buildLookup_noSubtables(self):
+        self.assertIsNone(builder.buildLookup([]))
+        self.assertIsNone(builder.buildLookup(None))
+        self.assertIsNone(builder.buildLookup([None]))
+        self.assertIsNone(builder.buildLookup([None, None]))
+
+    def test_buildLookup_markFilterSet(self):
+        s = builder.buildSingleSubstSubtable({"one": "two"})
+        flags = (builder.LOOKUP_FLAG_RIGHT_TO_LEFT |
+                 builder.LOOKUP_FLAG_USE_MARK_FILTERING_SET)
+        lookup = builder.buildLookup([s], flags, markFilterSet=999)
+        self.assertEqual(getXML(lookup.toXML),
+                         ['<Lookup>',
+                          '  <LookupType value="1"/>',
+                          '  <LookupFlag value="17"/>',
+                          '  <!-- SubTableCount=1 -->',
+                          '  <SingleSubst index="0">',
+                          '    <Substitution in="one" out="two"/>',
+                          '  </SingleSubst>',
+                          '  <MarkFilteringSet value="999"/>',
+                          '</Lookup>'])
+
+    def test_buildMarkArray(self):
+        markArray = builder.buildMarkArray({
+            "acute": (7, builder.buildAnchor(300, 800)),
+            "grave": (2, builder.buildAnchor(10, 80))
+        }, self.GLYPHMAP)
+        self.assertLess(self.GLYPHMAP["grave"], self.GLYPHMAP["acute"])
+        self.assertEqual(getXML(markArray.toXML),
+                         ['<MarkArray>',
+                          '  <!-- MarkCount=2 -->',
+                          '  <MarkRecord index="0">',
+                          '    <Class value="2"/>',
+                          '    <MarkAnchor Format="1">',
+                          '      <XCoordinate value="10"/>',
+                          '      <YCoordinate value="80"/>',
+                          '    </MarkAnchor>',
+                          '  </MarkRecord>',
+                          '  <MarkRecord index="1">',
+                          '    <Class value="7"/>',
+                          '    <MarkAnchor Format="1">',
+                          '      <XCoordinate value="300"/>',
+                          '      <YCoordinate value="800"/>',
+                          '    </MarkAnchor>',
+                          '  </MarkRecord>',
+                          '</MarkArray>'])
+
+    def test_buildMarkBasePosSubtable(self):
+        anchor = builder.buildAnchor
+        marks = {
+            "acute": (0, anchor(300, 700)),
+            "cedilla": (1, anchor(300, -100)),
+            "grave": (0, anchor(300, 700))
+        }
+        bases = {
+            # Make sure we can handle missing entries.
+            "A": {},  # no entry for any markClass
+            "B": {0: anchor(500, 900)},  # only markClass 0 specified
+            "C": {1: anchor(500, -10)},  # only markClass 1 specified
+
+            "a": {0: anchor(500, 400), 1: anchor(500, -20)},
+            "b": {0: anchor(500, 800), 1: anchor(500, -20)}
+        }
+        table = builder.buildMarkBasePosSubtable(marks, bases, self.GLYPHMAP)
+        self.assertEqual(getXML(table.toXML),
+                         ['<MarkBasePos Format="1">',
+                          '  <MarkCoverage>',
+                          '    <Glyph value="grave"/>',
+                          '    <Glyph value="acute"/>',
+                          '    <Glyph value="cedilla"/>',
+                          '  </MarkCoverage>',
+                          '  <BaseCoverage>',
+                          '    <Glyph value="A"/>',
+                          '    <Glyph value="B"/>',
+                          '    <Glyph value="C"/>',
+                          '    <Glyph value="a"/>',
+                          '    <Glyph value="b"/>',
+                          '  </BaseCoverage>',
+                          '  <!-- ClassCount=2 -->',
+                          '  <MarkArray>',
+                          '    <!-- MarkCount=3 -->',
+                          '    <MarkRecord index="0">',  # grave
+                          '      <Class value="0"/>',
+                          '      <MarkAnchor Format="1">',
+                          '        <XCoordinate value="300"/>',
+                          '        <YCoordinate value="700"/>',
+                          '      </MarkAnchor>',
+                          '    </MarkRecord>',
+                          '    <MarkRecord index="1">',  # acute
+                          '      <Class value="0"/>',
+                          '      <MarkAnchor Format="1">',
+                          '        <XCoordinate value="300"/>',
+                          '        <YCoordinate value="700"/>',
+                          '      </MarkAnchor>',
+                          '    </MarkRecord>',
+                          '    <MarkRecord index="2">',  # cedilla
+                          '      <Class value="1"/>',
+                          '      <MarkAnchor Format="1">',
+                          '        <XCoordinate value="300"/>',
+                          '        <YCoordinate value="-100"/>',
+                          '      </MarkAnchor>',
+                          '    </MarkRecord>',
+                          '  </MarkArray>',
+                          '  <BaseArray>',
+                          '    <!-- BaseCount=5 -->',
+                          '    <BaseRecord index="0">',  # A
+                          '      <BaseAnchor index="0" empty="1"/>',
+                          '      <BaseAnchor index="1" empty="1"/>',
+                          '    </BaseRecord>',
+                          '    <BaseRecord index="1">',  # B
+                          '      <BaseAnchor index="0" Format="1">',
+                          '        <XCoordinate value="500"/>',
+                          '        <YCoordinate value="900"/>',
+                          '      </BaseAnchor>',
+                          '      <BaseAnchor index="1" empty="1"/>',
+                          '    </BaseRecord>',
+                          '    <BaseRecord index="2">',  # C
+                          '      <BaseAnchor index="0" empty="1"/>',
+                          '      <BaseAnchor index="1" Format="1">',
+                          '        <XCoordinate value="500"/>',
+                          '        <YCoordinate value="-10"/>',
+                          '      </BaseAnchor>',
+                          '    </BaseRecord>',
+                          '    <BaseRecord index="3">',  # a
+                          '      <BaseAnchor index="0" Format="1">',
+                          '        <XCoordinate value="500"/>',
+                          '        <YCoordinate value="400"/>',
+                          '      </BaseAnchor>',
+                          '      <BaseAnchor index="1" Format="1">',
+                          '        <XCoordinate value="500"/>',
+                          '        <YCoordinate value="-20"/>',
+                          '      </BaseAnchor>',
+                          '    </BaseRecord>',
+                          '    <BaseRecord index="4">',  # b
+                          '      <BaseAnchor index="0" Format="1">',
+                          '        <XCoordinate value="500"/>',
+                          '        <YCoordinate value="800"/>',
+                          '      </BaseAnchor>',
+                          '      <BaseAnchor index="1" Format="1">',
+                          '        <XCoordinate value="500"/>',
+                          '        <YCoordinate value="-20"/>',
+                          '      </BaseAnchor>',
+                          '    </BaseRecord>',
+                          '  </BaseArray>',
+                          '</MarkBasePos>'])
+
+    def test_buildMarkGlyphSetsDef(self):
+        marksets = builder.buildMarkGlyphSetsDef(
+            [{"acute", "grave"}, {"cedilla", "grave"}], self.GLYPHMAP)
+        self.assertEqual(getXML(marksets.toXML),
+                         ['<MarkGlyphSetsDef>',
+                          '  <MarkSetTableFormat value="1"/>',
+                          '  <!-- MarkSetCount=2 -->',
+                          '  <Coverage index="0">',
+                          '    <Glyph value="grave"/>',
+                          '    <Glyph value="acute"/>',
+                          '  </Coverage>',
+                          '  <Coverage index="1">',
+                          '    <Glyph value="grave"/>',
+                          '    <Glyph value="cedilla"/>',
+                          '  </Coverage>',
+                          '</MarkGlyphSetsDef>'])
+
+    def test_buildMarkGlyphSetsDef_empty(self):
+        self.assertIsNone(builder.buildMarkGlyphSetsDef([], self.GLYPHMAP))
+
+    def test_buildMarkGlyphSetsDef_None(self):
+        self.assertIsNone(builder.buildMarkGlyphSetsDef(None, self.GLYPHMAP))
+
+    def test_buildMarkLigPosSubtable(self):
+        anchor = builder.buildAnchor
+        marks = {
+            "acute": (0, anchor(300, 700)),
+            "cedilla": (1, anchor(300, -100)),
+            "grave": (0, anchor(300, 700))
+        }
+        bases = {
+            "f_i": [{}, {0: anchor(200, 400)}],  # nothing on f; only 1 on i
+            "c_t": [
+                {0: anchor(500, 600), 1: anchor(500, -20)},   # c
+                {0: anchor(1300, 800), 1: anchor(1300, -20)}  # t
+            ]
+        }
+        table = builder.buildMarkLigPosSubtable(marks, bases, self.GLYPHMAP)
+        self.assertEqual(getXML(table.toXML),
+                         ['<MarkLigPos Format="1">',
+                          '  <MarkCoverage>',
+                          '    <Glyph value="grave"/>',
+                          '    <Glyph value="acute"/>',
+                          '    <Glyph value="cedilla"/>',
+                          '  </MarkCoverage>',
+                          '  <LigatureCoverage>',
+                          '    <Glyph value="f_i"/>',
+                          '    <Glyph value="c_t"/>',
+                          '  </LigatureCoverage>',
+                          '  <!-- ClassCount=2 -->',
+                          '  <MarkArray>',
+                          '    <!-- MarkCount=3 -->',
+                          '    <MarkRecord index="0">',
+                          '      <Class value="0"/>',
+                          '      <MarkAnchor Format="1">',
+                          '        <XCoordinate value="300"/>',
+                          '        <YCoordinate value="700"/>',
+                          '      </MarkAnchor>',
+                          '    </MarkRecord>',
+                          '    <MarkRecord index="1">',
+                          '      <Class value="0"/>',
+                          '      <MarkAnchor Format="1">',
+                          '        <XCoordinate value="300"/>',
+                          '        <YCoordinate value="700"/>',
+                          '      </MarkAnchor>',
+                          '    </MarkRecord>',
+                          '    <MarkRecord index="2">',
+                          '      <Class value="1"/>',
+                          '      <MarkAnchor Format="1">',
+                          '        <XCoordinate value="300"/>',
+                          '        <YCoordinate value="-100"/>',
+                          '      </MarkAnchor>',
+                          '    </MarkRecord>',
+                          '  </MarkArray>',
+                          '  <LigatureArray>',
+                          '    <!-- LigatureCount=2 -->',
+                          '    <LigatureAttach index="0">',
+                          '      <!-- ComponentCount=2 -->',
+                          '      <ComponentRecord index="0">',
+                          '        <LigatureAnchor index="0" empty="1"/>',
+                          '        <LigatureAnchor index="1" empty="1"/>',
+                          '      </ComponentRecord>',
+                          '      <ComponentRecord index="1">',
+                          '        <LigatureAnchor index="0" Format="1">',
+                          '          <XCoordinate value="200"/>',
+                          '          <YCoordinate value="400"/>',
+                          '        </LigatureAnchor>',
+                          '        <LigatureAnchor index="1" empty="1"/>',
+                          '      </ComponentRecord>',
+                          '    </LigatureAttach>',
+                          '    <LigatureAttach index="1">',
+                          '      <!-- ComponentCount=2 -->',
+                          '      <ComponentRecord index="0">',
+                          '        <LigatureAnchor index="0" Format="1">',
+                          '          <XCoordinate value="500"/>',
+                          '          <YCoordinate value="600"/>',
+                          '        </LigatureAnchor>',
+                          '        <LigatureAnchor index="1" Format="1">',
+                          '          <XCoordinate value="500"/>',
+                          '          <YCoordinate value="-20"/>',
+                          '        </LigatureAnchor>',
+                          '      </ComponentRecord>',
+                          '      <ComponentRecord index="1">',
+                          '        <LigatureAnchor index="0" Format="1">',
+                          '          <XCoordinate value="1300"/>',
+                          '          <YCoordinate value="800"/>',
+                          '        </LigatureAnchor>',
+                          '        <LigatureAnchor index="1" Format="1">',
+                          '          <XCoordinate value="1300"/>',
+                          '          <YCoordinate value="-20"/>',
+                          '        </LigatureAnchor>',
+                          '      </ComponentRecord>',
+                          '    </LigatureAttach>',
+                          '  </LigatureArray>',
+                          '</MarkLigPos>'])
+
+    def test_buildMarkRecord(self):
+        rec = builder.buildMarkRecord(17, builder.buildAnchor(500, -20))
+        self.assertEqual(getXML(rec.toXML),
+                         ['<MarkRecord>',
+                          '  <Class value="17"/>',
+                          '  <MarkAnchor Format="1">',
+                          '    <XCoordinate value="500"/>',
+                          '    <YCoordinate value="-20"/>',
+                          '  </MarkAnchor>',
+                          '</MarkRecord>'])
+
+    def test_buildMark2Record(self):
+        a = builder.buildAnchor
+        rec = builder.buildMark2Record([a(500, -20), None, a(300, -15)])
+        self.assertEqual(getXML(rec.toXML),
+                         ['<Mark2Record>',
+                          '  <Mark2Anchor index="0" Format="1">',
+                          '    <XCoordinate value="500"/>',
+                          '    <YCoordinate value="-20"/>',
+                          '  </Mark2Anchor>',
+                          '  <Mark2Anchor index="1" empty="1"/>',
+                          '  <Mark2Anchor index="2" Format="1">',
+                          '    <XCoordinate value="300"/>',
+                          '    <YCoordinate value="-15"/>',
+                          '  </Mark2Anchor>',
+                          '</Mark2Record>'])
+
+    def test_buildPairPosClassesSubtable(self):
+        d20 = builder.buildValue({"XPlacement": -20})
+        d50 = builder.buildValue({"XPlacement": -50})
+        d0 = builder.buildValue({})
+        d8020 = builder.buildValue({"XPlacement": -80, "YPlacement": -20})
+        subtable = builder.buildPairPosClassesSubtable({
+            (tuple("A",), tuple(["zero"])): (d0, d50),
+            (tuple("A",), tuple(["one", "two"])):  (None, d20),
+            (tuple(["B", "C"]), tuple(["zero"])): (d8020, d50),
+        }, self.GLYPHMAP)
+        self.assertEqual(getXML(subtable.toXML),
+                         ['<PairPos Format="2">',
+                          '  <Coverage>',
+                          '    <Glyph value="A"/>',
+                          '    <Glyph value="B"/>',
+                          '    <Glyph value="C"/>',
+                          '  </Coverage>',
+                          '  <ValueFormat1 value="3"/>',
+                          '  <ValueFormat2 value="1"/>',
+                          '  <ClassDef1>',
+                          '    <ClassDef glyph="A" class="1"/>',
+                          '  </ClassDef1>',
+                          '  <ClassDef2>',
+                          '    <ClassDef glyph="one" class="1"/>',
+                          '    <ClassDef glyph="two" class="1"/>',
+                          '    <ClassDef glyph="zero" class="2"/>',
+                          '  </ClassDef2>',
+                          '  <!-- Class1Count=2 -->',
+                          '  <!-- Class2Count=3 -->',
+                          '  <Class1Record index="0">',
+                          '    <Class2Record index="0">',
+                          '    </Class2Record>',
+                          '    <Class2Record index="1">',
+                          '    </Class2Record>',
+                          '    <Class2Record index="2">',
+                          '      <Value1 XPlacement="-80" YPlacement="-20"/>',
+                          '      <Value2 XPlacement="-50"/>',
+                          '    </Class2Record>',
+                          '  </Class1Record>',
+                          '  <Class1Record index="1">',
+                          '    <Class2Record index="0">',
+                          '    </Class2Record>',
+                          '    <Class2Record index="1">',
+                          '      <Value2 XPlacement="-20"/>',
+                          '    </Class2Record>',
+                          '    <Class2Record index="2">',
+                          '      <Value1/>',
+                          '      <Value2 XPlacement="-50"/>',
+                          '    </Class2Record>',
+                          '  </Class1Record>',
+                          '</PairPos>'])
+
+    def test_buildPairPosGlyphs(self):
+        d50 = builder.buildValue({"XPlacement": -50})
+        d8020 = builder.buildValue({"XPlacement": -80, "YPlacement": -20})
+        subtables = builder.buildPairPosGlyphs({
+            ("A", "zero"): (None, d50),
+            ("A", "one"):  (d8020, d50),
+        }, self.GLYPHMAP)
+        self.assertEqual(sum([getXML(t.toXML) for t in subtables], []),
+                         ['<PairPos Format="1">',
+                          '  <Coverage>',
+                          '    <Glyph value="A"/>',
+                          '  </Coverage>',
+                          '  <ValueFormat1 value="0"/>',
+                          '  <ValueFormat2 value="1"/>',
+                          '  <!-- PairSetCount=1 -->',
+                          '  <PairSet index="0">',
+                          '    <!-- PairValueCount=1 -->',
+                          '    <PairValueRecord index="0">',
+                          '      <SecondGlyph value="zero"/>',
+                          '      <Value2 XPlacement="-50"/>',
+                          '    </PairValueRecord>',
+                          '  </PairSet>',
+                          '</PairPos>',
+                          '<PairPos Format="1">',
+                          '  <Coverage>',
+                          '    <Glyph value="A"/>',
+                          '  </Coverage>',
+                          '  <ValueFormat1 value="3"/>',
+                          '  <ValueFormat2 value="1"/>',
+                          '  <!-- PairSetCount=1 -->',
+                          '  <PairSet index="0">',
+                          '    <!-- PairValueCount=1 -->',
+                          '    <PairValueRecord index="0">',
+                          '      <SecondGlyph value="one"/>',
+                          '      <Value1 XPlacement="-80" YPlacement="-20"/>',
+                          '      <Value2 XPlacement="-50"/>',
+                          '    </PairValueRecord>',
+                          '  </PairSet>',
+                          '</PairPos>'])
+
+    def test_buildPairPosGlyphsSubtable(self):
+        d20 = builder.buildValue({"XPlacement": -20})
+        d50 = builder.buildValue({"XPlacement": -50})
+        d0 = builder.buildValue({})
+        d8020 = builder.buildValue({"XPlacement": -80, "YPlacement": -20})
+        subtable = builder.buildPairPosGlyphsSubtable({
+            ("A", "zero"): (d0, d50),
+            ("A", "one"):  (None, d20),
+            ("B", "five"): (d8020, d50),
+        }, self.GLYPHMAP)
+        self.assertEqual(getXML(subtable.toXML),
+                         ['<PairPos Format="1">',
+                          '  <Coverage>',
+                          '    <Glyph value="A"/>',
+                          '    <Glyph value="B"/>',
+                          '  </Coverage>',
+                          '  <ValueFormat1 value="3"/>',
+                          '  <ValueFormat2 value="1"/>',
+                          '  <!-- PairSetCount=2 -->',
+                          '  <PairSet index="0">',
+                          '    <!-- PairValueCount=2 -->',
+                          '    <PairValueRecord index="0">',
+                          '      <SecondGlyph value="zero"/>',
+                          '      <Value2 XPlacement="-50"/>',
+                          '    </PairValueRecord>',
+                          '    <PairValueRecord index="1">',
+                          '      <SecondGlyph value="one"/>',
+                          '      <Value2 XPlacement="-20"/>',
+                          '    </PairValueRecord>',
+                          '  </PairSet>',
+                          '  <PairSet index="1">',
+                          '    <!-- PairValueCount=1 -->',
+                          '    <PairValueRecord index="0">',
+                          '      <SecondGlyph value="five"/>',
+                          '      <Value1 XPlacement="-80" YPlacement="-20"/>',
+                          '      <Value2 XPlacement="-50"/>',
+                          '    </PairValueRecord>',
+                          '  </PairSet>',
+                          '</PairPos>'])
+
+    def test_buildSinglePos(self):
+        subtables = builder.buildSinglePos({
+            "one": builder.buildValue({"XPlacement": 500}),
+            "two": builder.buildValue({"XPlacement": 500}),
+            "three": builder.buildValue({"XPlacement": 200}),
+            "four": builder.buildValue({"XPlacement": 400}),
+            "five": builder.buildValue({"XPlacement": 500}),
+            "six": builder.buildValue({"YPlacement": -6}),
+        }, self.GLYPHMAP)
+        self.assertEqual(sum([getXML(t.toXML) for t in subtables], []),
+                         ['<SinglePos Format="1">',
+                          '  <Coverage>',
+                          '    <Glyph value="one"/>',
+                          '    <Glyph value="two"/>',
+                          '    <Glyph value="five"/>',
+                          '  </Coverage>',
+                          '  <ValueFormat value="1"/>',
+                          '  <Value XPlacement="500"/>',
+                          '</SinglePos>',
+                          '<SinglePos Format="2">',
+                          '  <Coverage>',
+                          '    <Glyph value="three"/>',
+                          '    <Glyph value="four"/>',
+                          '  </Coverage>',
+                          '  <ValueFormat value="1"/>',
+                          '  <!-- ValueCount=2 -->',
+                          '  <Value index="0" XPlacement="200"/>',
+                          '  <Value index="1" XPlacement="400"/>',
+                          '</SinglePos>',
+                          '<SinglePos Format="1">',
+                          '  <Coverage>',
+                          '    <Glyph value="six"/>',
+                          '  </Coverage>',
+                          '  <ValueFormat value="2"/>',
+                          '  <Value YPlacement="-6"/>',
+                          '</SinglePos>'])
+
+    def test_buildSinglePos_ValueFormat0(self):
+        subtables = builder.buildSinglePos({
+            "zero": builder.buildValue({})
+        }, self.GLYPHMAP)
+        self.assertEqual(sum([getXML(t.toXML) for t in subtables], []),
+                         ['<SinglePos Format="1">',
+                          '  <Coverage>',
+                          '    <Glyph value="zero"/>',
+                          '  </Coverage>',
+                          '  <ValueFormat value="0"/>',
+                          '</SinglePos>'])
+
+    def test_buildSinglePosSubtable_format1(self):
+        subtable = builder.buildSinglePosSubtable({
+            "one": builder.buildValue({"XPlacement": 777}),
+            "two": builder.buildValue({"XPlacement": 777}),
+        }, self.GLYPHMAP)
+        self.assertEqual(getXML(subtable.toXML),
+                         ['<SinglePos Format="1">',
+                          '  <Coverage>',
+                          '    <Glyph value="one"/>',
+                          '    <Glyph value="two"/>',
+                          '  </Coverage>',
+                          '  <ValueFormat value="1"/>',
+                          '  <Value XPlacement="777"/>',
+                          '</SinglePos>'])
+
+    def test_buildSinglePosSubtable_format2(self):
+        subtable = builder.buildSinglePosSubtable({
+            "one": builder.buildValue({"XPlacement": 777}),
+            "two": builder.buildValue({"YPlacement": -888}),
+        }, self.GLYPHMAP)
+        self.assertEqual(getXML(subtable.toXML),
+                         ['<SinglePos Format="2">',
+                          '  <Coverage>',
+                          '    <Glyph value="one"/>',
+                          '    <Glyph value="two"/>',
+                          '  </Coverage>',
+                          '  <ValueFormat value="3"/>',
+                          '  <!-- ValueCount=2 -->',
+                          '  <Value index="0" XPlacement="777"/>',
+                          '  <Value index="1" YPlacement="-888"/>',
+                          '</SinglePos>'])
+
+    def test_buildValue(self):
+        value = builder.buildValue({"XPlacement": 7, "YPlacement": 23})
+        func = lambda writer, font: value.toXML(writer, font, valueName="Val")
+        self.assertEqual(getXML(func),
+                         ['<Val XPlacement="7" YPlacement="23"/>'])
+
+    def test_getLigatureKey(self):
+        components = lambda s: [tuple(word) for word in s.split()]
+        c = components("fi fl ff ffi fff")
+        c.sort(key=builder._getLigatureKey)
+        self.assertEqual(c, components("fff ffi ff fi fl"))
+
+    def test_getSinglePosValueKey(self):
+        device = builder.buildDevice({10:1, 11:3})
+        a1 = builder.buildValue({"XPlacement": 500, "XPlaDevice": device})
+        a2 = builder.buildValue({"XPlacement": 500, "XPlaDevice": device})
+        b = builder.buildValue({"XPlacement": 500})
+        keyA1 = builder._getSinglePosValueKey(a1)
+        keyA2 = builder._getSinglePosValueKey(a1)
+        keyB = builder._getSinglePosValueKey(b)
+        self.assertEqual(keyA1, keyA2)
+        self.assertEqual(hash(keyA1), hash(keyA2))
+        self.assertNotEqual(keyA1, keyB)
+        self.assertNotEqual(hash(keyA1), hash(keyB))
+
+
+class ClassDefBuilderTest(unittest.TestCase):
+    def test_build_usingClass0(self):
+        b = builder.ClassDefBuilder(useClass0=True)
+        b.add({"aa", "bb"})
+        b.add({"a", "b"})
+        b.add({"c"})
+        b.add({"e", "f", "g", "h"})
+        cdef = b.build()
+        self.assertIsInstance(cdef, otTables.ClassDef)
+        self.assertEqual(cdef.classDefs, {
+            "a": 2,
+            "b": 2,
+            "c": 3,
+            "aa": 1,
+            "bb": 1
+        })
+
+    def test_build_notUsingClass0(self):
+        b = builder.ClassDefBuilder(useClass0=False)
+        b.add({"a", "b"})
+        b.add({"c"})
+        b.add({"e", "f", "g", "h"})
+        cdef = b.build()
+        self.assertIsInstance(cdef, otTables.ClassDef)
+        self.assertEqual(cdef.classDefs, {
+            "a": 2,
+            "b": 2,
+            "c": 3,
+            "e": 1,
+            "f": 1,
+            "g": 1,
+            "h": 1
+        })
+
+    def test_canAdd(self):
+        b = builder.ClassDefBuilder(useClass0=True)
+        b.add({"a", "b", "c", "d"})
+        b.add({"e", "f"})
+        self.assertTrue(b.canAdd({"a", "b", "c", "d"}))
+        self.assertTrue(b.canAdd({"e", "f"}))
+        self.assertTrue(b.canAdd({"g", "h", "i"}))
+        self.assertFalse(b.canAdd({"b", "c", "d"}))
+        self.assertFalse(b.canAdd({"a", "b", "c", "d", "e", "f"}))
+        self.assertFalse(b.canAdd({"d", "e", "f"}))
+        self.assertFalse(b.canAdd({"f"}))
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/pens/areaPen_test.py b/Tests/pens/areaPen_test.py
new file mode 100644
index 0000000..ae915df
--- /dev/null
+++ b/Tests/pens/areaPen_test.py
@@ -0,0 +1,180 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.pens.areaPen import AreaPen
+import unittest
+
+precision = 6
+
+def draw1_(pen):
+    pen.moveTo( (254, 360) )
+    pen.lineTo( (771, 367) )
+    pen.curveTo( (800, 393), (808, 399), (819, 412) )
+    pen.curveTo( (818, 388), (774, 138), (489, 145) )
+    pen.curveTo( (188, 145), (200, 398), (200, 421) )
+    pen.curveTo( (209, 409), (220, 394), (254, 360) )
+    pen.closePath()
+
+def draw2_(pen):
+    pen.moveTo( (254, 360) )
+    pen.curveTo( (220, 394), (209, 409), (200, 421) )
+    pen.curveTo( (200, 398), (188, 145), (489, 145) )
+    pen.curveTo( (774, 138), (818, 388), (819, 412) )
+    pen.curveTo( (808, 399), (800, 393), (771, 367) )
+    pen.closePath()
+
+def draw3_(pen):
+    pen.moveTo( (771, 367) )
+    pen.curveTo( (800, 393), (808, 399), (819, 412) )
+    pen.curveTo( (818, 388), (774, 138), (489, 145) )
+    pen.curveTo( (188, 145), (200, 398), (200, 421) )
+    pen.curveTo( (209, 409), (220, 394), (254, 360) )
+    pen.closePath()
+
+def draw4_(pen):
+    pen.moveTo( (771, 367) )
+    pen.lineTo( (254, 360) )
+    pen.curveTo( (220, 394), (209, 409), (200, 421) )
+    pen.curveTo( (200, 398), (188, 145), (489, 145) )
+    pen.curveTo( (774, 138), (818, 388), (819, 412) )
+    pen.curveTo( (808, 399), (800, 393), (771, 367) )
+    pen.closePath()
+
+def draw5_(pen):
+    pen.moveTo( (254, 360) )
+    pen.lineTo( (771, 367) )
+    pen.qCurveTo( (793, 386), (802, 394) )
+    pen.qCurveTo( (811, 402), (819, 412) )
+    pen.qCurveTo( (819, 406), (814, 383.5) )
+    pen.qCurveTo( (809, 361), (796, 330.5) )
+    pen.qCurveTo( (783, 300), (760.5, 266.5) )
+    pen.qCurveTo( (738, 233), (701, 205.5) )
+    pen.qCurveTo( (664, 178), (612, 160.5) )
+    pen.qCurveTo( (560, 143), (489, 145) )
+    pen.qCurveTo( (414, 145), (363, 164) )
+    pen.qCurveTo( (312, 183), (280, 211.5) )
+    pen.qCurveTo( (248, 240), (231.5, 274.5) )
+    pen.qCurveTo( (215, 309), (208, 339.5) )
+    pen.qCurveTo( (201, 370), (200.5, 392.5) )
+    pen.qCurveTo( (200, 415), (200, 421) )
+    pen.qCurveTo( (207, 412), (217.5, 399) )
+    pen.qCurveTo( (228, 386), (254, 360) )
+    pen.closePath()
+
+def draw6_(pen):
+    pen.moveTo( (254, 360) )
+    pen.qCurveTo( (228, 386), (217.5, 399) )
+    pen.qCurveTo( (207, 412), (200, 421) )
+    pen.qCurveTo( (200, 415), (200.5, 392.5) )
+    pen.qCurveTo( (201, 370), (208, 339.5) )
+    pen.qCurveTo( (215, 309), (231.5, 274.5) )
+    pen.qCurveTo( (248, 240), (280, 211.5) )
+    pen.qCurveTo( (312, 183), (363, 164) )
+    pen.qCurveTo( (414, 145), (489, 145) )
+    pen.qCurveTo( (560, 143), (612, 160.5) )
+    pen.qCurveTo( (664, 178), (701, 205.5) )
+    pen.qCurveTo( (738, 233), (760.5, 266.5) )
+    pen.qCurveTo( (783, 300), (796, 330.5) )
+    pen.qCurveTo( (809, 361), (814, 383.5) )
+    pen.qCurveTo( (819, 406), (819, 412) )
+    pen.qCurveTo( (811, 402), (802, 394) )
+    pen.qCurveTo( (793, 386), (771, 367) )
+    pen.closePath()
+
+def draw7_(pen):
+    pen.moveTo( (771, 367) )
+    pen.qCurveTo( (793, 386), (802, 394) )
+    pen.qCurveTo( (811, 402), (819, 412) )
+    pen.qCurveTo( (819, 406), (814, 383.5) )
+    pen.qCurveTo( (809, 361), (796, 330.5) )
+    pen.qCurveTo( (783, 300), (760.5, 266.5) )
+    pen.qCurveTo( (738, 233), (701, 205.5) )
+    pen.qCurveTo( (664, 178), (612, 160.5) )
+    pen.qCurveTo( (560, 143), (489, 145) )
+    pen.qCurveTo( (414, 145), (363, 164) )
+    pen.qCurveTo( (312, 183), (280, 211.5) )
+    pen.qCurveTo( (248, 240), (231.5, 274.5) )
+    pen.qCurveTo( (215, 309), (208, 339.5) )
+    pen.qCurveTo( (201, 370), (200.5, 392.5) )
+    pen.qCurveTo( (200, 415), (200, 421) )
+    pen.qCurveTo( (207, 412), (217.5, 399) )
+    pen.qCurveTo( (228, 386), (254, 360) )
+    pen.closePath()
+
+def draw8_(pen):
+    pen.moveTo( (771, 367) )
+    pen.lineTo( (254, 360) )
+    pen.qCurveTo( (228, 386), (217.5, 399) )
+    pen.qCurveTo( (207, 412), (200, 421) )
+    pen.qCurveTo( (200, 415), (200.5, 392.5) )
+    pen.qCurveTo( (201, 370), (208, 339.5) )
+    pen.qCurveTo( (215, 309), (231.5, 274.5) )
+    pen.qCurveTo( (248, 240), (280, 211.5) )
+    pen.qCurveTo( (312, 183), (363, 164) )
+    pen.qCurveTo( (414, 145), (489, 145) )
+    pen.qCurveTo( (560, 143), (612, 160.5) )
+    pen.qCurveTo( (664, 178), (701, 205.5) )
+    pen.qCurveTo( (738, 233), (760.5, 266.5) )
+    pen.qCurveTo( (783, 300), (796, 330.5) )
+    pen.qCurveTo( (809, 361), (814, 383.5) )
+    pen.qCurveTo( (819, 406), (819, 412) )
+    pen.qCurveTo( (811, 402), (802, 394) )
+    pen.qCurveTo( (793, 386), (771, 367) )
+    pen.closePath()
+
+
+class AreaPenTest(unittest.TestCase):
+    def test_PScontour_clockwise_line_first(self):
+        pen = AreaPen(None)
+        draw1_(pen)
+        self.assertEqual(-104561.35, round(pen.value, precision))
+
+    def test_PScontour_counterclockwise_line_last(self):
+        pen = AreaPen(None)
+        draw2_(pen)
+        self.assertEqual(104561.35, round(pen.value, precision))
+
+    def test_PScontour_clockwise_line_last(self):
+        pen = AreaPen(None)
+        draw3_(pen)
+        self.assertEqual(-104561.35, round(pen.value, precision))
+
+    def test_PScontour_counterclockwise_line_first(self):
+        pen = AreaPen(None)
+        draw4_(pen)
+        self.assertEqual(104561.35, round(pen.value, precision))
+
+    def test_TTcontour_clockwise_line_first(self):
+        pen = AreaPen(None)
+        draw5_(pen)
+        self.assertEqual(-104602.791667, round(pen.value, precision))
+
+    def test_TTcontour_counterclockwise_line_last(self):
+        pen = AreaPen(None)
+        draw6_(pen)
+        self.assertEqual(104602.791667, round(pen.value, precision))
+
+    def test_TTcontour_clockwise_line_last(self):
+        pen = AreaPen(None)
+        draw7_(pen)
+        self.assertEqual(-104602.791667, round(pen.value, precision))
+
+    def test_TTcontour_counterclockwise_line_first(self):
+        pen = AreaPen(None)
+        draw8_(pen)
+        self.assertEqual(104602.791667, round(pen.value, precision))
+
+    def test_openPaths(self):
+        pen = AreaPen()
+        pen.moveTo((0, 0))
+        pen.endPath()
+        self.assertEqual(0, pen.value)
+
+        pen.moveTo((0, 0))
+        pen.lineTo((1, 0))
+        with self.assertRaises(NotImplementedError):
+            pen.endPath()
+
+
+if __name__ == '__main__':
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/pens/basePen_test.py b/Tests/pens/basePen_test.py
new file mode 100644
index 0000000..2a7e43c
--- /dev/null
+++ b/Tests/pens/basePen_test.py
@@ -0,0 +1,179 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.pens.basePen import \
+    BasePen, decomposeSuperBezierSegment, decomposeQuadraticSegment
+from fontTools.misc.loggingTools import CapturingLogHandler
+import unittest
+
+
+class _TestPen(BasePen):
+    def __init__(self):
+        BasePen.__init__(self, glyphSet={})
+        self._commands = []
+
+    def __repr__(self):
+        return " ".join(self._commands)
+
+    def getCurrentPoint(self):
+        return self._getCurrentPoint()
+
+    def _moveTo(self, pt):
+        self._commands.append("%s %s moveto" % (pt[0], pt[1]))
+
+    def _lineTo(self, pt):
+        self._commands.append("%s %s lineto" % (pt[0], pt[1]))
+
+    def _curveToOne(self, bcp1, bcp2, pt):
+        self._commands.append("%s %s %s %s %s %s curveto" %
+                              (bcp1[0], bcp1[1],
+                               bcp2[0], bcp2[1],
+                               pt[0], pt[1]))
+
+    def _closePath(self):
+        self._commands.append("closepath")
+
+    def _endPath(self):
+        self._commands.append("endpath")
+
+
+class _TestGlyph:
+    def draw(self, pen):
+        pen.moveTo((0.0, 0.0))
+        pen.lineTo((0.0, 100.0))
+        pen.curveTo((50.0, 75.0), (60.0, 50.0), (50.0, 25.0), (0.0, 0.0))
+        pen.closePath()
+
+
+class BasePenTest(unittest.TestCase):
+    def test_moveTo(self):
+        pen = _TestPen()
+        pen.moveTo((0.5, -4.3))
+        self.assertEqual("0.5 -4.3 moveto", repr(pen))
+        self.assertEqual((0.5, -4.3), pen.getCurrentPoint())
+
+    def test_lineTo(self):
+        pen = _TestPen()
+        pen.moveTo((4, 5))
+        pen.lineTo((7, 8))
+        self.assertEqual("4 5 moveto 7 8 lineto", repr(pen))
+        self.assertEqual((7, 8), pen.getCurrentPoint())
+
+    def test_curveTo_zeroPoints(self):
+        pen = _TestPen()
+        pen.moveTo((0.0, 0.0))
+        self.assertRaises(AssertionError, pen.curveTo)
+
+    def test_curveTo_onePoint(self):
+        pen = _TestPen()
+        pen.moveTo((0.0, 0.0))
+        pen.curveTo((1.0, 1.1))
+        self.assertEqual("0.0 0.0 moveto 1.0 1.1 lineto", repr(pen))
+        self.assertEqual((1.0, 1.1), pen.getCurrentPoint())
+
+    def test_curveTo_twoPoints(self):
+        pen = _TestPen()
+        pen.moveTo((0.0, 0.0))
+        pen.curveTo((6.0, 3.0), (3.0, 6.0))
+        self.assertEqual("0.0 0.0 moveto 4.0 2.0 5.0 4.0 3.0 6.0 curveto",
+                         repr(pen))
+        self.assertEqual((3.0, 6.0), pen.getCurrentPoint())
+
+    def test_curveTo_manyPoints(self):
+        pen = _TestPen()
+        pen.moveTo((0.0, 0.0))
+        pen.curveTo((1.0, 1.1), (2.0, 2.1), (3.0, 3.1), (4.0, 4.1))
+        self.assertEqual("0.0 0.0 moveto "
+                         "1.0 1.1 1.5 1.6 2.0 2.1 curveto "
+                         "2.5 2.6 3.0 3.1 4.0 4.1 curveto", repr(pen))
+        self.assertEqual((4.0, 4.1), pen.getCurrentPoint())
+
+    def test_qCurveTo_zeroPoints(self):
+        pen = _TestPen()
+        pen.moveTo((0.0, 0.0))
+        self.assertRaises(AssertionError, pen.qCurveTo)
+
+    def test_qCurveTo_onePoint(self):
+        pen = _TestPen()
+        pen.moveTo((0.0, 0.0))
+        pen.qCurveTo((77.7, 99.9))
+        self.assertEqual("0.0 0.0 moveto 77.7 99.9 lineto", repr(pen))
+        self.assertEqual((77.7, 99.9), pen.getCurrentPoint())
+
+    def test_qCurveTo_manyPoints(self):
+        pen = _TestPen()
+        pen.moveTo((0.0, 0.0))
+        pen.qCurveTo((6.0, 3.0), (3.0, 6.0))
+        self.assertEqual("0.0 0.0 moveto 4.0 2.0 5.0 4.0 3.0 6.0 curveto",
+                         repr(pen))
+        self.assertEqual((3.0, 6.0), pen.getCurrentPoint())
+
+    def test_qCurveTo_onlyOffCurvePoints(self):
+        pen = _TestPen()
+        pen.moveTo((0.0, 0.0))
+        pen.qCurveTo((6.0, -6.0), (12.0, 12.0), (18.0, -18.0), None)
+        self.assertEqual("0.0 0.0 moveto "
+                         "12.0 -12.0 moveto "
+                         "8.0 -8.0 7.0 -3.0 9.0 3.0 curveto "
+                         "11.0 9.0 13.0 7.0 15.0 -3.0 curveto "
+                         "17.0 -13.0 16.0 -16.0 12.0 -12.0 curveto", repr(pen))
+        self.assertEqual((12.0, -12.0), pen.getCurrentPoint())
+
+    def test_closePath(self):
+        pen = _TestPen()
+        pen.lineTo((3, 4))
+        pen.closePath()
+        self.assertEqual("3 4 lineto closepath", repr(pen))
+        self.assertEqual(None, pen.getCurrentPoint())
+
+    def test_endPath(self):
+        pen = _TestPen()
+        pen.lineTo((3, 4))
+        pen.endPath()
+        self.assertEqual("3 4 lineto endpath", repr(pen))
+        self.assertEqual(None, pen.getCurrentPoint())
+
+    def test_addComponent(self):
+        pen = _TestPen()
+        pen.glyphSet["oslash"] = _TestGlyph()
+        pen.addComponent("oslash", (2, 3, 0.5, 2, -10, 0))
+        self.assertEqual("-10.0 0.0 moveto "
+                         "40.0 200.0 lineto "
+                         "127.5 300.0 131.25 290.0 125.0 265.0 curveto "
+                         "118.75 240.0 102.5 200.0 -10.0 0.0 curveto "
+                         "closepath", repr(pen))
+        self.assertEqual(None, pen.getCurrentPoint())
+
+    def test_addComponent_skip_missing(self):
+        pen = _TestPen()
+        with CapturingLogHandler(pen.log, "WARNING") as captor:
+            pen.addComponent("nonexistent", (1, 0, 0, 1, 0, 0))
+        captor.assertRegex("glyph '.*' is missing from glyphSet; skipped")
+
+
+class DecomposeSegmentTest(unittest.TestCase):
+    def test_decomposeSuperBezierSegment(self):
+        decompose = decomposeSuperBezierSegment
+        self.assertRaises(AssertionError, decompose, [])
+        self.assertRaises(AssertionError, decompose, [(0, 0)])
+        self.assertRaises(AssertionError, decompose, [(0, 0), (1, 1)])
+        self.assertEqual([((0, 0), (1, 1), (2, 2))],
+                         decompose([(0, 0), (1, 1), (2, 2)]))
+        self.assertEqual(
+            [((0, 0), (2, -2), (4, 0)), ((6, 2), (8, 8), (12, -12))],
+            decompose([(0, 0), (4, -4), (8, 8), (12, -12)]))
+
+    def test_decomposeQuadraticSegment(self):
+        decompose = decomposeQuadraticSegment
+        self.assertRaises(AssertionError, decompose, [])
+        self.assertRaises(AssertionError, decompose, [(0, 0)])
+        self.assertEqual([((0,0), (4, 8))], decompose([(0, 0), (4, 8)]))
+        self.assertEqual([((0,0), (2, 4)), ((4, 8), (9, -9))],
+                         decompose([(0, 0), (4, 8), (9, -9)]))
+        self.assertEqual(
+            [((0, 0), (2.0, 4.0)), ((4, 8), (6.5, -0.5)), ((9, -9), (10, 10))],
+            decompose([(0, 0), (4, 8), (9, -9), (10, 10)]))
+
+
+if __name__ == '__main__':
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/pens/boundsPen_test.py b/Tests/pens/boundsPen_test.py
new file mode 100644
index 0000000..533c575
--- /dev/null
+++ b/Tests/pens/boundsPen_test.py
@@ -0,0 +1,77 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.pens.boundsPen import BoundsPen, ControlBoundsPen
+import unittest
+
+
+def draw_(pen):
+    pen.moveTo((0, 0))
+    pen.lineTo((0, 100))
+    pen.qCurveTo((50, 75), (60, 50), (50, 25), (0, 0))
+    pen.curveTo((-50, 25), (-60, 50), (-50, 75), (0, 100))
+    pen.closePath()
+
+
+def bounds_(pen):
+    return " ".join(["%.0f" % c for c in pen.bounds])
+
+
+class BoundsPenTest(unittest.TestCase):
+    def test_draw(self):
+        pen = BoundsPen(None)
+        draw_(pen)
+        self.assertEqual("-55 0 58 100", bounds_(pen))
+
+    def test_empty(self):
+        pen = BoundsPen(None)
+        self.assertEqual(None, pen.bounds)
+
+    def test_curve(self):
+        pen = BoundsPen(None)
+        pen.moveTo((0, 0))
+        pen.curveTo((20, 10), (90, 40), (0, 0))
+        self.assertEqual("0 0 45 20", bounds_(pen))
+
+    def test_quadraticCurve(self):
+        pen = BoundsPen(None)
+        pen.moveTo((0, 0))
+        pen.qCurveTo((6, 6), (10, 0))
+        self.assertEqual("0 0 10 3", bounds_(pen))
+
+
+class ControlBoundsPenTest(unittest.TestCase):
+    def test_draw(self):
+        pen = ControlBoundsPen(None)
+        draw_(pen)
+        self.assertEqual("-55 0 60 100", bounds_(pen))
+
+    def test_empty(self):
+        pen = ControlBoundsPen(None)
+        self.assertEqual(None, pen.bounds)
+
+    def test_curve(self):
+        pen = ControlBoundsPen(None)
+        pen.moveTo((0, 0))
+        pen.curveTo((20, 10), (90, 40), (0, 0))
+        self.assertEqual("0 0 90 40", bounds_(pen))
+
+    def test_quadraticCurve(self):
+        pen = ControlBoundsPen(None)
+        pen.moveTo((0, 0))
+        pen.qCurveTo((6, 6), (10, 0))
+        self.assertEqual("0 0 10 6", bounds_(pen))
+
+    def test_singlePoint(self):
+        pen = ControlBoundsPen(None)
+        pen.moveTo((-5, 10))
+        self.assertEqual("-5 10 -5 10", bounds_(pen))
+
+    def test_ignoreSinglePoint(self):
+        pen = ControlBoundsPen(None, ignoreSinglePoints=True)
+        pen.moveTo((0, 10))
+        self.assertEqual(None, pen.bounds)
+
+
+if __name__ == '__main__':
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/pens/perimeterPen_test.py b/Tests/pens/perimeterPen_test.py
new file mode 100644
index 0000000..aa73c3d
--- /dev/null
+++ b/Tests/pens/perimeterPen_test.py
@@ -0,0 +1,167 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.pens.perimeterPen import PerimeterPen
+import unittest
+
+def draw1_(pen):
+    pen.moveTo( (254, 360) )
+    pen.lineTo( (771, 367) )
+    pen.curveTo( (800, 393), (808, 399), (819, 412) )
+    pen.curveTo( (818, 388), (774, 138), (489, 145) )
+    pen.curveTo( (188, 145), (200, 398), (200, 421) )
+    pen.curveTo( (209, 409), (220, 394), (254, 360) )
+    pen.closePath()
+
+def draw2_(pen):
+    pen.moveTo( (254, 360) )
+    pen.curveTo( (220, 394), (209, 409), (200, 421) )
+    pen.curveTo( (200, 398), (188, 145), (489, 145) )
+    pen.curveTo( (774, 138), (818, 388), (819, 412) )
+    pen.curveTo( (808, 399), (800, 393), (771, 367) )
+    pen.closePath()
+
+def draw3_(pen):
+    pen.moveTo( (771, 367) )
+    pen.curveTo( (800, 393), (808, 399), (819, 412) )
+    pen.curveTo( (818, 388), (774, 138), (489, 145) )
+    pen.curveTo( (188, 145), (200, 398), (200, 421) )
+    pen.curveTo( (209, 409), (220, 394), (254, 360) )
+    pen.closePath()
+
+def draw4_(pen):
+    pen.moveTo( (771, 367) )
+    pen.lineTo( (254, 360) )
+    pen.curveTo( (220, 394), (209, 409), (200, 421) )
+    pen.curveTo( (200, 398), (188, 145), (489, 145) )
+    pen.curveTo( (774, 138), (818, 388), (819, 412) )
+    pen.curveTo( (808, 399), (800, 393), (771, 367) )
+    pen.closePath()
+
+def draw5_(pen):
+    pen.moveTo( (254, 360) )
+    pen.lineTo( (771, 367) )
+    pen.qCurveTo( (793, 386), (802, 394) )
+    pen.qCurveTo( (811, 402), (819, 412) )
+    pen.qCurveTo( (819, 406), (814, 383.5) )
+    pen.qCurveTo( (809, 361), (796, 330.5) )
+    pen.qCurveTo( (783, 300), (760.5, 266.5) )
+    pen.qCurveTo( (738, 233), (701, 205.5) )
+    pen.qCurveTo( (664, 178), (612, 160.5) )
+    pen.qCurveTo( (560, 143), (489, 145) )
+    pen.qCurveTo( (414, 145), (363, 164) )
+    pen.qCurveTo( (312, 183), (280, 211.5) )
+    pen.qCurveTo( (248, 240), (231.5, 274.5) )
+    pen.qCurveTo( (215, 309), (208, 339.5) )
+    pen.qCurveTo( (201, 370), (200.5, 392.5) )
+    pen.qCurveTo( (200, 415), (200, 421) )
+    pen.qCurveTo( (207, 412), (217.5, 399) )
+    pen.qCurveTo( (228, 386), (254, 360) )
+    pen.closePath()
+
+def draw6_(pen):
+    pen.moveTo( (254, 360) )
+    pen.qCurveTo( (228, 386), (217.5, 399) )
+    pen.qCurveTo( (207, 412), (200, 421) )
+    pen.qCurveTo( (200, 415), (200.5, 392.5) )
+    pen.qCurveTo( (201, 370), (208, 339.5) )
+    pen.qCurveTo( (215, 309), (231.5, 274.5) )
+    pen.qCurveTo( (248, 240), (280, 211.5) )
+    pen.qCurveTo( (312, 183), (363, 164) )
+    pen.qCurveTo( (414, 145), (489, 145) )
+    pen.qCurveTo( (560, 143), (612, 160.5) )
+    pen.qCurveTo( (664, 178), (701, 205.5) )
+    pen.qCurveTo( (738, 233), (760.5, 266.5) )
+    pen.qCurveTo( (783, 300), (796, 330.5) )
+    pen.qCurveTo( (809, 361), (814, 383.5) )
+    pen.qCurveTo( (819, 406), (819, 412) )
+    pen.qCurveTo( (811, 402), (802, 394) )
+    pen.qCurveTo( (793, 386), (771, 367) )
+    pen.closePath()
+
+def draw7_(pen):
+    pen.moveTo( (771, 367) )
+    pen.qCurveTo( (793, 386), (802, 394) )
+    pen.qCurveTo( (811, 402), (819, 412) )
+    pen.qCurveTo( (819, 406), (814, 383.5) )
+    pen.qCurveTo( (809, 361), (796, 330.5) )
+    pen.qCurveTo( (783, 300), (760.5, 266.5) )
+    pen.qCurveTo( (738, 233), (701, 205.5) )
+    pen.qCurveTo( (664, 178), (612, 160.5) )
+    pen.qCurveTo( (560, 143), (489, 145) )
+    pen.qCurveTo( (414, 145), (363, 164) )
+    pen.qCurveTo( (312, 183), (280, 211.5) )
+    pen.qCurveTo( (248, 240), (231.5, 274.5) )
+    pen.qCurveTo( (215, 309), (208, 339.5) )
+    pen.qCurveTo( (201, 370), (200.5, 392.5) )
+    pen.qCurveTo( (200, 415), (200, 421) )
+    pen.qCurveTo( (207, 412), (217.5, 399) )
+    pen.qCurveTo( (228, 386), (254, 360) )
+    pen.closePath()
+
+def draw8_(pen):
+    pen.moveTo( (771, 367) )
+    pen.lineTo( (254, 360) )
+    pen.qCurveTo( (228, 386), (217.5, 399) )
+    pen.qCurveTo( (207, 412), (200, 421) )
+    pen.qCurveTo( (200, 415), (200.5, 392.5) )
+    pen.qCurveTo( (201, 370), (208, 339.5) )
+    pen.qCurveTo( (215, 309), (231.5, 274.5) )
+    pen.qCurveTo( (248, 240), (280, 211.5) )
+    pen.qCurveTo( (312, 183), (363, 164) )
+    pen.qCurveTo( (414, 145), (489, 145) )
+    pen.qCurveTo( (560, 143), (612, 160.5) )
+    pen.qCurveTo( (664, 178), (701, 205.5) )
+    pen.qCurveTo( (738, 233), (760.5, 266.5) )
+    pen.qCurveTo( (783, 300), (796, 330.5) )
+    pen.qCurveTo( (809, 361), (814, 383.5) )
+    pen.qCurveTo( (819, 406), (819, 412) )
+    pen.qCurveTo( (811, 402), (802, 394) )
+    pen.qCurveTo( (793, 386), (771, 367) )
+    pen.closePath()
+
+
+class PerimeterPenTest(unittest.TestCase):
+    def test_PScontour_clockwise_line_first(self):
+        pen = PerimeterPen(None)
+        draw1_(pen)
+        self.assertEqual(1589, round(pen.value))
+
+    def test_PScontour_counterclockwise_line_last(self):
+        pen = PerimeterPen(None)
+        draw2_(pen)
+        self.assertEqual(1589, round(pen.value))
+
+    def test_PScontour_clockwise_line_last(self):
+        pen = PerimeterPen(None)
+        draw3_(pen)
+        self.assertEqual(1589, round(pen.value))
+
+    def test_PScontour_counterclockwise_line_first(self):
+        pen = PerimeterPen(None)
+        draw4_(pen)
+        self.assertEqual(1589, round(pen.value))
+
+    def test_TTcontour_clockwise_line_first(self):
+        pen = PerimeterPen(None)
+        draw5_(pen)
+        self.assertEqual(1589, round(pen.value))
+
+    def test_TTcontour_counterclockwise_line_last(self):
+        pen = PerimeterPen(None)
+        draw6_(pen)
+        self.assertEqual(1589, round(pen.value))
+
+    def test_TTcontour_clockwise_line_last(self):
+        pen = PerimeterPen(None)
+        draw7_(pen)
+        self.assertEqual(1589, round(pen.value))
+
+    def test_TTcontour_counterclockwise_line_first(self):
+        pen = PerimeterPen(None)
+        draw8_(pen)
+        self.assertEqual(1589, round(pen.value))
+
+
+if __name__ == '__main__':
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/pens/pointInsidePen_test.py b/Tests/pens/pointInsidePen_test.py
new file mode 100644
index 0000000..3696ece
--- /dev/null
+++ b/Tests/pens/pointInsidePen_test.py
@@ -0,0 +1,225 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.pens.pointInsidePen import PointInsidePen
+import unittest
+
+
+class PointInsidePenTest(unittest.TestCase):
+    def test_line(self):
+        def draw_triangles(pen):
+            pen.moveTo((0,0)); pen.lineTo((10,5)); pen.lineTo((10,0))
+            pen.moveTo((9,1)); pen.lineTo((4,1)); pen.lineTo((9,4))
+            pen.closePath()
+
+        self.assertEqual(
+            " *********"
+            "   **    *"
+            "     **  *"
+            "       * *"
+            "         *",
+            self.render(draw_triangles, even_odd=True))
+
+        self.assertEqual(
+            " *********"
+            "   *******"
+            "     *****"
+            "       ***"
+            "         *",
+            self.render(draw_triangles, even_odd=False))
+
+    def test_curve(self):
+        def draw_curves(pen):
+            pen.moveTo((0,0)); pen.curveTo((9,1), (9,4), (0,5))
+            pen.moveTo((10,5)); pen.curveTo((1,4), (1,1), (10,0))
+            pen.closePath()
+
+        self.assertEqual(
+            "***    ***"
+            "****  ****"
+            "***    ***"
+            "****  ****"
+            "***    ***",
+            self.render(draw_curves, even_odd=True))
+
+        self.assertEqual(
+            "***    ***"
+            "**********"
+            "**********"
+            "**********"
+            "***    ***",
+            self.render(draw_curves, even_odd=False))
+
+    def test_qCurve(self):
+        def draw_qCurves(pen):
+            pen.moveTo((0,0)); pen.qCurveTo((15,2), (0,5))
+            pen.moveTo((10,5)); pen.qCurveTo((-5,3), (10,0))
+            pen.closePath()
+
+        self.assertEqual(
+            "***     **"
+            "****   ***"
+            "***    ***"
+            "***   ****"
+            "**     ***",
+            self.render(draw_qCurves, even_odd=True))
+
+        self.assertEqual(
+            "***     **"
+            "**********"
+            "**********"
+            "**********"
+            "**     ***",
+            self.render(draw_qCurves, even_odd=False))
+
+    @staticmethod
+    def render(draw_function, even_odd):
+        result = BytesIO()
+        for y in range(5):
+            for x in range(10):
+                pen = PointInsidePen(None, (x + 0.5, y + 0.5), even_odd)
+                draw_function(pen)
+                if pen.getResult():
+                    result.write(b"*")
+                else:
+                    result.write(b" ")
+        return tounicode(result.getvalue())
+
+
+    def test_contour_no_solutions(self):
+        def draw_contour(pen):
+            pen.moveTo( (969, 230) )
+            pen.curveTo( (825, 348) , (715, 184) , (614, 202) )
+            pen.lineTo( (614, 160) )
+            pen.lineTo( (969, 160) )
+            pen.closePath()
+
+        piPen = PointInsidePen(None, (750, 295)) # this point is outside
+        draw_contour(piPen)
+        self.assertEqual(piPen.getWinding(), 0)
+        self.assertEqual(piPen.getResult(), False)
+
+        piPen = PointInsidePen(None, (835, 190)) # this point is inside
+        draw_contour(piPen)
+        self.assertEqual(piPen.getWinding(), 1)
+        self.assertEqual(piPen.getResult(), True)
+
+    def test_contour_square_closed(self):
+        def draw_contour(pen):
+            pen.moveTo( (100, 100) )
+            pen.lineTo( (-100, 100) )
+            pen.lineTo( (-100, -100) )
+            pen.lineTo( (100, -100) )
+            pen.closePath()
+
+        piPen = PointInsidePen(None, (0, 0)) # this point is inside
+        draw_contour(piPen)
+        self.assertEqual(piPen.getWinding(), 1)
+        self.assertEqual(piPen.getResult(), True)
+
+    def test_contour_square_opened(self):
+        def draw_contour(pen):
+            pen.moveTo( (100, 100) )
+            pen.lineTo( (-100, 100) )
+            pen.lineTo( (-100, -100) )
+            pen.lineTo( (100, -100) )
+            # contour not explicitly closed
+
+        piPen = PointInsidePen(None, (0, 0)) # this point is inside
+        draw_contour(piPen)
+        self.assertEqual(piPen.getWinding(), 1)
+        self.assertEqual(piPen.getResult(), True)
+
+    def test_contour_circle(self):
+        def draw_contour(pen):
+            pen.moveTo( (0, 100) )
+            pen.curveTo( (-55, 100) , (-100, 55) , (-100, 0) )
+            pen.curveTo( (-100, -55) , (-55, -100) , (0, -100) )
+            pen.curveTo( (55, -100) , (100, -55) , (100, 0) )
+            pen.curveTo( (100, 55) , (55, 100) , (0, 100) )
+
+        piPen = PointInsidePen(None, (50, 50)) # this point is inside
+        draw_contour(piPen)
+        self.assertEqual(piPen.getResult(), True)
+
+        piPen = PointInsidePen(None, (50, -50)) # this point is inside
+        draw_contour(piPen)
+        self.assertEqual(piPen.getResult(), True)
+
+    def test_contour_diamond(self):
+        def draw_contour(pen):
+            pen.moveTo( (0, 100) )
+            pen.lineTo( (100, 0) )
+            pen.lineTo( (0, -100) )
+            pen.lineTo( (-100, 0) )
+            pen.closePath()
+
+        piPen = PointInsidePen(None, (-200, 0)) # this point is outside
+        draw_contour(piPen)
+        self.assertEqual(piPen.getWinding(), 0)
+
+        piPen = PointInsidePen(None, (-200, 100)) # this point is outside
+        draw_contour(piPen)
+        self.assertEqual(piPen.getWinding(), 0)
+
+        piPen = PointInsidePen(None, (-200, -100)) # this point is outside
+        draw_contour(piPen)
+        self.assertEqual(piPen.getWinding(), 0)
+
+        piPen = PointInsidePen(None, (-200, 50)) # this point is outside
+        draw_contour(piPen)
+        self.assertEqual(piPen.getWinding(), 0)
+
+    def test_contour_integers(self):
+        def draw_contour(pen):
+            pen.moveTo( (728, 697) )
+            pen.lineTo( (504, 699) )
+            pen.curveTo( (487, 719) , (508, 783) , (556, 783) )
+            pen.lineTo( (718, 783) )
+            pen.curveTo( (739, 783) , (749, 712) , (728, 697) )
+            pen.closePath()
+
+        piPen = PointInsidePen(None, (416, 783)) # this point is outside
+        draw_contour(piPen)
+        self.assertEqual(piPen.getWinding(), 0)
+
+    def test_contour_decimals(self):
+        def draw_contour(pen):
+            pen.moveTo( (727.546875, 697.0) )
+            pen.lineTo( (504.375, 698.515625) )
+            pen.curveTo( (487.328125, 719.359375), (507.84375, 783.140625), (555.796875, 783.140625) )
+            pen.lineTo( (717.96875, 783.140625) )
+            pen.curveTo( (738.890625, 783.140625), (748.796875, 711.5), (727.546875, 697.0) )
+            pen.closePath()
+
+        piPen = PointInsidePen(None, (416.625, 783.140625)) # this point is outside
+        draw_contour(piPen)
+        self.assertEqual(piPen.getWinding(), 0)
+
+    def test_contour2_integers(self):
+        def draw_contour(pen):
+            pen.moveTo( (51, 22) )
+            pen.lineTo( (51, 74) )
+            pen.lineTo( (83, 50) )
+            pen.curveTo( (83, 49) , (82, 48) , (82, 47) )
+            pen.closePath()
+
+        piPen = PointInsidePen(None, (21, 50)) # this point is outside
+        draw_contour(piPen)
+        self.assertEqual(piPen.getWinding(), 0)
+
+    def test_contour2_decimals(self):
+        def draw_contour(pen):
+            pen.moveTo( (51.25, 21.859375) )
+            pen.lineTo( (51.25, 73.828125) )
+            pen.lineTo( (82.5, 50.0) )
+            pen.curveTo( (82.5, 49.09375) , (82.265625, 48.265625) , (82.234375, 47.375) )
+            pen.closePath()
+
+        piPen = PointInsidePen(None, (21.25, 50.0)) # this point is outside
+        draw_contour(piPen)
+        self.assertEqual(piPen.getWinding(), 0)
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
+
diff --git a/Tests/pens/recordingPen_test.py b/Tests/pens/recordingPen_test.py
new file mode 100644
index 0000000..fdc5d06
--- /dev/null
+++ b/Tests/pens/recordingPen_test.py
@@ -0,0 +1,39 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.pens.recordingPen import RecordingPen, DecomposingRecordingPen
+import pytest
+
+
+class _TestGlyph(object):
+
+    def draw(self, pen):
+        pen.moveTo((0.0, 0.0))
+        pen.lineTo((0.0, 100.0))
+        pen.curveTo((50.0, 75.0), (60.0, 50.0), (50.0, 0.0))
+        pen.closePath()
+
+
+class RecordingPenTest(object):
+
+    def test_addComponent(self):
+        pen = RecordingPen()
+        pen.addComponent("a", (2, 0, 0, 3, -10, 5))
+        assert pen.value == [("addComponent", ("a", (2, 0, 0, 3, -10, 5)))]
+
+
+class DecomposingRecordingPenTest(object):
+
+    def test_addComponent_decomposed(self):
+        pen = DecomposingRecordingPen({"a": _TestGlyph()})
+        pen.addComponent("a", (2, 0, 0, 3, -10, 5))
+        assert pen.value == [
+            ('moveTo', ((-10.0, 5.0),)),
+            ('lineTo', ((-10.0, 305.0),)),
+            ('curveTo', ((90.0, 230.0), (110.0, 155.0), (90.0, 5.0),)),
+            ('closePath', ())]
+
+    def test_addComponent_missing_raises(self):
+        pen = DecomposingRecordingPen(dict())
+        with pytest.raises(KeyError) as excinfo:
+            pen.addComponent("a", (1, 0, 0, 1, 0, 0))
+        assert excinfo.value.args[0] == "a"
diff --git a/Tests/pens/reverseContourPen_test.py b/Tests/pens/reverseContourPen_test.py
new file mode 100644
index 0000000..bace806
--- /dev/null
+++ b/Tests/pens/reverseContourPen_test.py
@@ -0,0 +1,319 @@
+from fontTools.pens.recordingPen import RecordingPen
+from fontTools.pens.reverseContourPen import ReverseContourPen
+import pytest
+
+
+TEST_DATA = [
+    (
+        [
+            ('moveTo', ((0, 0),)),
+            ('lineTo', ((1, 1),)),
+            ('lineTo', ((2, 2),)),
+            ('lineTo', ((3, 3),)),  # last not on move, line is implied
+            ('closePath', ()),
+        ],
+        [
+            ('moveTo', ((0, 0),)),
+            ('lineTo', ((3, 3),)),
+            ('lineTo', ((2, 2),)),
+            ('lineTo', ((1, 1),)),
+            ('closePath', ()),
+        ]
+    ),
+    (
+        [
+            ('moveTo', ((0, 0),)),
+            ('lineTo', ((1, 1),)),
+            ('lineTo', ((2, 2),)),
+            ('lineTo', ((0, 0),)),  # last on move, no implied line
+            ('closePath', ()),
+        ],
+        [
+            ('moveTo', ((0, 0),)),
+            ('lineTo', ((2, 2),)),
+            ('lineTo', ((1, 1),)),
+            ('closePath', ()),
+        ]
+    ),
+    (
+        [
+            ('moveTo', ((0, 0),)),
+            ('lineTo', ((0, 0),)),
+            ('lineTo', ((1, 1),)),
+            ('lineTo', ((2, 2),)),
+            ('closePath', ()),
+        ],
+        [
+            ('moveTo', ((0, 0),)),
+            ('lineTo', ((2, 2),)),
+            ('lineTo', ((1, 1),)),
+            ('lineTo', ((0, 0),)),
+            ('lineTo', ((0, 0),)),
+            ('closePath', ()),
+        ]
+    ),
+    (
+        [
+            ('moveTo', ((0, 0),)),
+            ('lineTo', ((1, 1),)),
+            ('closePath', ()),
+        ],
+        [
+            ('moveTo', ((0, 0),)),
+            ('lineTo', ((1, 1),)),
+            ('closePath', ()),
+        ]
+    ),
+    (
+        [
+            ('moveTo', ((0, 0),)),
+            ('curveTo', ((1, 1), (2, 2), (3, 3))),
+            ('curveTo', ((4, 4), (5, 5), (0, 0))),
+            ('closePath', ()),
+        ],
+        [
+            ('moveTo', ((0, 0),)),
+            ('curveTo', ((5, 5), (4, 4), (3, 3))),
+            ('curveTo', ((2, 2), (1, 1), (0, 0))),
+            ('closePath', ()),
+        ]
+    ),
+    (
+        [
+            ('moveTo', ((0, 0),)),
+            ('curveTo', ((1, 1), (2, 2), (3, 3))),
+            ('curveTo', ((4, 4), (5, 5), (6, 6))),
+            ('closePath', ()),
+        ],
+        [
+            ('moveTo', ((0, 0),)),
+            ('lineTo', ((6, 6),)),  # implied line
+            ('curveTo', ((5, 5), (4, 4), (3, 3))),
+            ('curveTo', ((2, 2), (1, 1), (0, 0))),
+            ('closePath', ()),
+        ]
+    ),
+    (
+        [
+            ('moveTo', ((0, 0),)),
+            ('lineTo', ((1, 1),)),  # this line becomes implied
+            ('curveTo', ((2, 2), (3, 3), (4, 4))),
+            ('curveTo', ((5, 5), (6, 6), (7, 7))),
+            ('closePath', ()),
+        ],
+        [
+            ('moveTo', ((0, 0),)),
+            ('lineTo', ((7, 7),)),
+            ('curveTo', ((6, 6), (5, 5), (4, 4))),
+            ('curveTo', ((3, 3), (2, 2), (1, 1))),
+            ('closePath', ()),
+        ]
+    ),
+    (
+        [
+            ('moveTo', ((0, 0),)),
+            ('qCurveTo', ((1, 1), (2, 2))),
+            ('qCurveTo', ((3, 3), (0, 0))),
+            ('closePath', ()),
+        ],
+        [
+            ('moveTo', ((0, 0),)),
+            ('qCurveTo', ((3, 3), (2, 2))),
+            ('qCurveTo', ((1, 1), (0, 0))),
+            ('closePath', ()),
+        ]
+    ),
+    (
+        [
+            ('moveTo', ((0, 0),)),
+            ('qCurveTo', ((1, 1), (2, 2))),
+            ('qCurveTo', ((3, 3), (4, 4))),
+            ('closePath', ()),
+        ],
+        [
+            ('moveTo', ((0, 0),)),
+            ('lineTo', ((4, 4),)),
+            ('qCurveTo', ((3, 3), (2, 2))),
+            ('qCurveTo', ((1, 1), (0, 0))),
+            ('closePath', ()),
+        ]
+    ),
+    (
+        [
+            ('moveTo', ((0, 0),)),
+            ('lineTo', ((1, 1),)),
+            ('qCurveTo', ((2, 2), (3, 3))),
+            ('closePath', ()),
+        ],
+        [
+            ('moveTo', ((0, 0),)),
+            ('lineTo', ((3, 3),)),
+            ('qCurveTo', ((2, 2), (1, 1))),
+            ('closePath', ()),
+        ]
+    ),
+    (
+        [
+            ('addComponent', ('a', (1, 0, 0, 1, 0, 0)))
+        ],
+        [
+            ('addComponent', ('a', (1, 0, 0, 1, 0, 0)))
+        ]
+    ),
+    (
+        [], []
+    ),
+    (
+        [
+            ('moveTo', ((0, 0),)),
+            ('endPath', ()),
+        ],
+        [
+            ('moveTo', ((0, 0),)),
+            ('endPath', ()),
+        ],
+    ),
+    (
+        [
+            ('moveTo', ((0, 0),)),
+            ('closePath', ()),
+        ],
+        [
+            ('moveTo', ((0, 0),)),
+            ('endPath', ()),  # single-point paths is always open
+        ],
+    ),
+    (
+        [
+            ('moveTo', ((0, 0),)),
+            ('lineTo', ((1, 1),)),
+            ('endPath', ())
+        ],
+        [
+            ('moveTo', ((1, 1),)),
+            ('lineTo', ((0, 0),)),
+            ('endPath', ())
+        ]
+    ),
+    (
+        [
+            ('moveTo', ((0, 0),)),
+            ('curveTo', ((1, 1), (2, 2), (3, 3))),
+            ('endPath', ())
+        ],
+        [
+            ('moveTo', ((3, 3),)),
+            ('curveTo', ((2, 2), (1, 1), (0, 0))),
+            ('endPath', ())
+        ]
+    ),
+    (
+        [
+            ('moveTo', ((0, 0),)),
+            ('curveTo', ((1, 1), (2, 2), (3, 3))),
+            ('lineTo', ((4, 4),)),
+            ('endPath', ())
+        ],
+        [
+            ('moveTo', ((4, 4),)),
+            ('lineTo', ((3, 3),)),
+            ('curveTo', ((2, 2), (1, 1), (0, 0))),
+            ('endPath', ())
+        ]
+    ),
+    (
+        [
+            ('moveTo', ((0, 0),)),
+            ('lineTo', ((1, 1),)),
+            ('curveTo', ((2, 2), (3, 3), (4, 4))),
+            ('endPath', ())
+        ],
+        [
+            ('moveTo', ((4, 4),)),
+            ('curveTo', ((3, 3), (2, 2), (1, 1))),
+            ('lineTo', ((0, 0),)),
+            ('endPath', ())
+        ]
+    ),
+    (
+        [
+            ('qCurveTo', ((0, 0), (1, 1), (2, 2), None)),
+            ('closePath', ())
+        ],
+        [
+            ('qCurveTo', ((0, 0), (2, 2), (1, 1), None)),
+            ('closePath', ())
+        ]
+    ),
+    (
+        [
+            ('qCurveTo', ((0, 0), (1, 1), (2, 2), None)),
+            ('endPath', ())
+        ],
+        [
+            ('qCurveTo', ((0, 0), (2, 2), (1, 1), None)),
+            ('closePath', ())  # this is always "closed"
+        ]
+    ),
+    # Test case from:
+    # https://github.com/googlei18n/cu2qu/issues/51#issue-179370514
+    (
+        [
+            ('moveTo', ((848, 348),)),
+            ('lineTo', ((848, 348),)),  # duplicate lineTo point after moveTo
+            ('qCurveTo', ((848, 526), (649, 704), (449, 704))),
+            ('qCurveTo', ((449, 704), (248, 704), (50, 526), (50, 348))),
+            ('lineTo', ((50, 348),)),
+            ('qCurveTo', ((50, 348), (50, 171), (248, -3), (449, -3))),
+            ('qCurveTo', ((449, -3), (649, -3), (848, 171), (848, 348))),
+            ('closePath', ())
+        ],
+        [
+            ('moveTo', ((848, 348),)),
+            ('qCurveTo', ((848, 171), (649, -3), (449, -3), (449, -3))),
+            ('qCurveTo', ((248, -3), (50, 171), (50, 348), (50, 348))),
+            ('lineTo', ((50, 348),)),
+            ('qCurveTo', ((50, 526), (248, 704), (449, 704), (449, 704))),
+            ('qCurveTo', ((649, 704), (848, 526), (848, 348))),
+            ('lineTo', ((848, 348),)),  # the duplicate point is kept
+            ('closePath', ())
+        ]
+    )
+]
+
+
+@pytest.mark.parametrize("contour, expected", TEST_DATA)
+def test_reverse_pen(contour, expected):
+    recpen = RecordingPen()
+    revpen = ReverseContourPen(recpen)
+    for operator, operands in contour:
+        getattr(revpen, operator)(*operands)
+    assert recpen.value == expected
+
+
+@pytest.mark.parametrize("contour, expected", TEST_DATA)
+def test_reverse_point_pen(contour, expected):
+    try:
+        from ufoLib.pointPen import (
+            ReverseContourPointPen, PointToSegmentPen, SegmentToPointPen)
+    except ImportError:
+        pytest.skip("ufoLib not installed")
+
+    recpen = RecordingPen()
+    pt2seg = PointToSegmentPen(recpen, outputImpliedClosingLine=True)
+    revpen = ReverseContourPointPen(pt2seg)
+    seg2pt = SegmentToPointPen(revpen)
+    for operator, operands in contour:
+        getattr(seg2pt, operator)(*operands)
+
+    # for closed contours that have a lineTo following the moveTo,
+    # and whose points don't overlap, our current implementation diverges
+    # from the ReverseContourPointPen as wrapped by ufoLib's pen converters.
+    # In the latter case, an extra lineTo is added because of
+    # outputImpliedClosingLine=True. This is redundant but not incorrect,
+    # as the number of points is the same in both.
+    if (contour and contour[-1][0] == "closePath" and
+            contour[1][0] == "lineTo" and contour[1][1] != contour[0][1]):
+        expected = expected[:-1] + [("lineTo", contour[0][1])] + expected[-1:]
+
+    assert recpen.value == expected
diff --git a/Tests/pens/t2CharStringPen_test.py b/Tests/pens/t2CharStringPen_test.py
new file mode 100644
index 0000000..c09d810
--- /dev/null
+++ b/Tests/pens/t2CharStringPen_test.py
@@ -0,0 +1,184 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.pens.t2CharStringPen import T2CharStringPen
+import unittest
+
+
+class T2CharStringPenTest(unittest.TestCase):
+
+    def __init__(self, methodName):
+        unittest.TestCase.__init__(self, methodName)
+        # Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
+        # and fires deprecation warnings if a program uses the old name.
+        if not hasattr(self, "assertRaisesRegex"):
+            self.assertRaisesRegex = self.assertRaisesRegexp
+
+    def assertAlmostEqualProgram(self, expected, actual):
+        self.assertEqual(len(expected), len(actual))
+        for i1, i2 in zip(expected, actual):
+            if isinstance(i1, basestring):
+                self.assertIsInstance(i2, basestring)
+                self.assertEqual(i1, i2)
+            else:
+                self.assertAlmostEqual(i1, i2)
+
+    def test_draw_h_v_lines(self):
+        pen = T2CharStringPen(100, {})
+        pen.moveTo((0, 0))
+        pen.lineTo((10, 0))
+        pen.lineTo((10, 10))
+        pen.lineTo((0, 10))
+        pen.closePath()  # no-op
+        pen.moveTo((10, 10))
+        pen.lineTo((10, 20))
+        pen.lineTo((0, 20))
+        pen.lineTo((0, 10))
+        pen.closePath()
+        charstring = pen.getCharString(None, None)
+
+        self.assertEqual(
+            [100,
+             0, 'hmoveto',
+             10, 10, -10, 'hlineto',
+             10, 'hmoveto',
+             10, -10, -10, 'vlineto',
+             'endchar'],
+            charstring.program)
+
+    def test_draw_lines(self):
+        pen = T2CharStringPen(100, {})
+        pen.moveTo((5, 5))
+        pen.lineTo((25, 15))
+        pen.lineTo((35, 35))
+        pen.lineTo((15, 25))
+        pen.closePath()  # no-op
+        charstring = pen.getCharString(None, None)
+
+        self.assertEqual(
+            [100,
+             5, 5, 'rmoveto',
+             20, 10, 10, 20, -20, -10, 'rlineto',
+             'endchar'],
+            charstring.program)
+
+    def test_draw_h_v_curves(self):
+        pen = T2CharStringPen(100, {})
+        pen.moveTo((0, 0))
+        pen.curveTo((10, 0), (20, 10), (20, 20))
+        pen.curveTo((20, 30), (10, 40), (0, 40))
+        pen.endPath()  # no-op
+        charstring = pen.getCharString(None, None)
+
+        self.assertEqual(
+            [100,
+             0, 'hmoveto',
+             10, 10, 10, 10, 10, -10, 10, -10, 'hvcurveto',
+             'endchar'],
+            charstring.program)
+
+    def test_draw_curves(self):
+        pen = T2CharStringPen(100, {})
+        pen.moveTo((95, 25))
+        pen.curveTo((115, 44), (115, 76), (95, 95))
+        pen.curveTo((76, 114), (44, 115), (25, 95))
+        pen.endPath()  # no-op
+        charstring = pen.getCharString(None, None)
+
+        self.assertEqual(
+            [100,
+             95, 25, 'rmoveto',
+             20, 19, 0, 32, -20, 19, -19, 19, -32, 1, -19, -20, 'rrcurveto',
+             'endchar'],
+            charstring.program)
+
+    def test_draw_more_curves(self):
+        pen = T2CharStringPen(100, {})
+        pen.moveTo((10, 10))
+        pen.curveTo((20, 10), (50, 10), (60, 10))
+        pen.curveTo((60, 20), (60, 50), (60, 60))
+        pen.curveTo((50, 50), (40, 60), (30, 60))
+        pen.curveTo((40, 50), (30, 40), (30, 30))
+        pen.curveTo((30, 25), (25, 19), (20, 20))
+        pen.curveTo((15, 20), (9, 25), (10, 30))
+        pen.curveTo((7, 25), (6, 15), (10, 10))
+        pen.endPath()  # no-op
+        charstring = pen.getCharString(None, None)
+
+        self.assertEqual(
+            [100,
+             10, 10, 'rmoveto',
+             10, 30, 0, 10, 'hhcurveto',
+             10, 0, 30, 10, 'vvcurveto',
+             -10, -10, -10, 10, -10, 'hhcurveto',
+             10, -10, -10, -10, -10, 'vvcurveto',
+             -5, -5, -6, -5, 1, 'vhcurveto',
+             -5, -6, 5, 5, 1, 'hvcurveto',
+             -3, -5, -1, -10, 4, -5, 'rrcurveto',
+             'endchar'],
+            charstring.program)
+
+    def test_default_width(self):
+        pen = T2CharStringPen(None, {})
+        charstring = pen.getCharString(None, None)
+        self.assertEqual(['endchar'], charstring.program)
+
+    def test_no_round(self):
+        pen = T2CharStringPen(100.1, {}, roundTolerance=0.0)
+        pen.moveTo((0, 0))
+        pen.curveTo((10.1, 0.1), (19.9, 9.9), (20.49, 20.49))
+        pen.curveTo((20.49, 30.49), (9.9, 39.9), (0.1, 40.1))
+        pen.closePath()
+        charstring = pen.getCharString(None, None)
+
+        self.assertAlmostEqualProgram(
+            [100,  # we always round the advance width
+             0, 'hmoveto',
+             10.1, 0.1, 9.8, 9.8, 0.59, 10.59, 'rrcurveto',
+             10, -10.59, 9.41, -9.8, 0.2, 'vhcurveto',
+             'endchar'],
+            charstring.program)
+
+    def test_round_all(self):
+        pen = T2CharStringPen(100.1, {}, roundTolerance=0.5)
+        pen.moveTo((0, 0))
+        pen.curveTo((10.1, 0.1), (19.9, 9.9), (20.49, 20.49))
+        pen.curveTo((20.49, 30.5), (9.9, 39.9), (0.1, 40.1))
+        pen.closePath()
+        charstring = pen.getCharString(None, None)
+
+        self.assertEqual(
+            [100,
+             0, 'hmoveto',
+             10, 10, 10, 10, 11, -10, 9, -10, 'hvcurveto',
+             'endchar'],
+            charstring.program)
+
+    def test_round_some(self):
+        pen = T2CharStringPen(100, {}, roundTolerance=0.2)
+        pen.moveTo((0, 0))
+        # the following two are rounded as within the tolerance
+        pen.lineTo((10.1, 0.1))
+        pen.lineTo((19.9, 9.9))
+        # this one is not rounded as it exceeds the tolerance
+        pen.lineTo((20.49, 20.49))
+        pen.closePath()
+        charstring = pen.getCharString(None, None)
+
+        self.assertAlmostEqualProgram(
+            [100,
+             0, 'hmoveto',
+             10, 'hlineto',
+             10, 10, 0.49, 10.49, 'rlineto',
+             'endchar'],
+            charstring.program)
+
+    def test_invalid_tolerance(self):
+        self.assertRaisesRegex(
+            ValueError,
+            "Rounding tolerance must be positive",
+            T2CharStringPen, None, {}, roundTolerance=-0.1)
+
+
+if __name__ == '__main__':
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/pens/ttGlyphPen_test.py b/Tests/pens/ttGlyphPen_test.py
new file mode 100644
index 0000000..ede240a
--- /dev/null
+++ b/Tests/pens/ttGlyphPen_test.py
@@ -0,0 +1,257 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+
+import os
+import unittest
+import struct
+
+from fontTools import ttLib
+from fontTools.misc.testTools import TestCase
+from fontTools.pens.ttGlyphPen import TTGlyphPen, MAX_F2DOT14
+
+
+class TTGlyphPenTest(TestCase):
+
+    def runEndToEnd(self, filename):
+        font = ttLib.TTFont()
+        ttx_path = os.path.join(
+            os.path.abspath(os.path.dirname(os.path.realpath(__file__))),
+            '..', 'ttLib', 'data', filename)
+        font.importXML(ttx_path)
+
+        glyphSet = font.getGlyphSet()
+        glyfTable = font['glyf']
+        pen = TTGlyphPen(font.getGlyphSet())
+
+        for name in font.getGlyphOrder():
+            oldGlyph = glyphSet[name]
+            oldGlyph.draw(pen)
+            oldGlyph = oldGlyph._glyph
+            newGlyph = pen.glyph()
+
+            if hasattr(oldGlyph, 'program'):
+                newGlyph.program = oldGlyph.program
+
+            self.assertEqual(
+                oldGlyph.compile(glyfTable), newGlyph.compile(glyfTable))
+
+    def test_e2e_linesAndSimpleComponents(self):
+        self.runEndToEnd('TestTTF-Regular.ttx')
+
+    def test_e2e_curvesAndComponentTransforms(self):
+        self.runEndToEnd('TestTTFComplex-Regular.ttx')
+
+    def test_moveTo_errorWithinContour(self):
+        pen = TTGlyphPen(None)
+        pen.moveTo((0, 0))
+        with self.assertRaises(AssertionError):
+            pen.moveTo((1, 0))
+
+    def test_closePath_ignoresAnchors(self):
+        pen = TTGlyphPen(None)
+        pen.moveTo((0, 0))
+        pen.closePath()
+        self.assertFalse(pen.points)
+        self.assertFalse(pen.types)
+        self.assertFalse(pen.endPts)
+
+    def test_endPath_sameAsClosePath(self):
+        pen = TTGlyphPen(None)
+
+        pen.moveTo((0, 0))
+        pen.lineTo((0, 1))
+        pen.lineTo((1, 0))
+        pen.closePath()
+        closePathGlyph = pen.glyph()
+
+        pen.moveTo((0, 0))
+        pen.lineTo((0, 1))
+        pen.lineTo((1, 0))
+        pen.endPath()
+        endPathGlyph = pen.glyph()
+
+        self.assertEqual(closePathGlyph, endPathGlyph)
+
+    def test_glyph_errorOnUnendedContour(self):
+        pen = TTGlyphPen(None)
+        pen.moveTo((0, 0))
+        with self.assertRaises(AssertionError):
+            pen.glyph()
+
+    def test_glyph_decomposes(self):
+        componentName = 'a'
+        glyphSet = {}
+        pen = TTGlyphPen(glyphSet)
+
+        pen.moveTo((0, 0))
+        pen.lineTo((0, 1))
+        pen.lineTo((1, 0))
+        pen.closePath()
+        glyphSet[componentName] = _TestGlyph(pen.glyph())
+
+        pen.moveTo((0, 0))
+        pen.lineTo((0, 1))
+        pen.lineTo((1, 0))
+        pen.closePath()
+        pen.addComponent(componentName, (1, 0, 0, 1, 2, 0))
+        pen.addComponent("missing", (1, 0, 0, 1, 0, 0))  # skipped
+        compositeGlyph = pen.glyph()
+
+        pen.moveTo((0, 0))
+        pen.lineTo((0, 1))
+        pen.lineTo((1, 0))
+        pen.closePath()
+        pen.moveTo((2, 0))
+        pen.lineTo((2, 1))
+        pen.lineTo((3, 0))
+        pen.closePath()
+        plainGlyph = pen.glyph()
+
+        self.assertEqual(plainGlyph, compositeGlyph)
+
+    def test_remove_extra_move_points(self):
+        pen = TTGlyphPen(None)
+        pen.moveTo((0, 0))
+        pen.lineTo((100, 0))
+        pen.qCurveTo((100, 50), (50, 100), (0, 0))
+        pen.closePath()
+        self.assertEqual(len(pen.points), 4)
+        self.assertEqual(pen.points[0], (0, 0))
+
+    def test_keep_move_point(self):
+        pen = TTGlyphPen(None)
+        pen.moveTo((0, 0))
+        pen.lineTo((100, 0))
+        pen.qCurveTo((100, 50), (50, 100), (30, 30))
+        # when last and move pts are different, closePath() implies a lineTo
+        pen.closePath()
+        self.assertEqual(len(pen.points), 5)
+        self.assertEqual(pen.points[0], (0, 0))
+
+    def test_keep_duplicate_end_point(self):
+        pen = TTGlyphPen(None)
+        pen.moveTo((0, 0))
+        pen.lineTo((100, 0))
+        pen.qCurveTo((100, 50), (50, 100), (0, 0))
+        pen.lineTo((0, 0))  # the duplicate point is not removed
+        pen.closePath()
+        self.assertEqual(len(pen.points), 5)
+        self.assertEqual(pen.points[0], (0, 0))
+
+    def test_within_range_component_transform(self):
+        componentName = 'a'
+        glyphSet = {}
+        pen = TTGlyphPen(glyphSet)
+
+        pen.moveTo((0, 0))
+        pen.lineTo((0, 1))
+        pen.lineTo((1, 0))
+        pen.closePath()
+        glyphSet[componentName] = _TestGlyph(pen.glyph())
+
+        pen.addComponent(componentName, (1.5, 0, 0, 1, 0, 0))
+        pen.addComponent(componentName, (1, 0, 0, -1.5, 0, 0))
+        compositeGlyph = pen.glyph()
+
+        pen.addComponent(componentName, (1.5, 0, 0, 1, 0, 0))
+        pen.addComponent(componentName, (1, 0, 0, -1.5, 0, 0))
+        expectedGlyph = pen.glyph()
+
+        self.assertEqual(expectedGlyph, compositeGlyph)
+
+    def test_clamp_to_almost_2_component_transform(self):
+        componentName = 'a'
+        glyphSet = {}
+        pen = TTGlyphPen(glyphSet)
+
+        pen.moveTo((0, 0))
+        pen.lineTo((0, 1))
+        pen.lineTo((1, 0))
+        pen.closePath()
+        glyphSet[componentName] = _TestGlyph(pen.glyph())
+
+        pen.addComponent(componentName, (1.99999, 0, 0, 1, 0, 0))
+        pen.addComponent(componentName, (1, 2, 0, 1, 0, 0))
+        pen.addComponent(componentName, (1, 0, 2, 1, 0, 0))
+        pen.addComponent(componentName, (1, 0, 0, 2, 0, 0))
+        pen.addComponent(componentName, (-2, 0, 0, -2, 0, 0))
+        compositeGlyph = pen.glyph()
+
+        almost2 = MAX_F2DOT14  # 0b1.11111111111111
+        pen.addComponent(componentName, (almost2, 0, 0, 1, 0, 0))
+        pen.addComponent(componentName, (1, almost2, 0, 1, 0, 0))
+        pen.addComponent(componentName, (1, 0, almost2, 1, 0, 0))
+        pen.addComponent(componentName, (1, 0, 0, almost2, 0, 0))
+        pen.addComponent(componentName, (-2, 0, 0, -2, 0, 0))
+        expectedGlyph = pen.glyph()
+
+        self.assertEqual(expectedGlyph, compositeGlyph)
+
+    def test_out_of_range_transform_decomposed(self):
+        componentName = 'a'
+        glyphSet = {}
+        pen = TTGlyphPen(glyphSet)
+
+        pen.moveTo((0, 0))
+        pen.lineTo((0, 1))
+        pen.lineTo((1, 0))
+        pen.closePath()
+        glyphSet[componentName] = _TestGlyph(pen.glyph())
+
+        pen.addComponent(componentName, (3, 0, 0, 2, 0, 0))
+        pen.addComponent(componentName, (1, 0, 0, 1, -1, 2))
+        pen.addComponent(componentName, (2, 0, 0, -3, 0, 0))
+        compositeGlyph = pen.glyph()
+
+        pen.moveTo((0, 0))
+        pen.lineTo((0, 2))
+        pen.lineTo((3, 0))
+        pen.closePath()
+        pen.moveTo((-1, 2))
+        pen.lineTo((-1, 3))
+        pen.lineTo((0, 2))
+        pen.closePath()
+        pen.moveTo((0, 0))
+        pen.lineTo((0, -3))
+        pen.lineTo((2, 0))
+        pen.closePath()
+        expectedGlyph = pen.glyph()
+
+        self.assertEqual(expectedGlyph, compositeGlyph)
+
+    def test_no_handle_overflowing_transform(self):
+        componentName = 'a'
+        glyphSet = {}
+        pen = TTGlyphPen(glyphSet, handleOverflowingTransforms=False)
+
+        pen.moveTo((0, 0))
+        pen.lineTo((0, 1))
+        pen.lineTo((1, 0))
+        pen.closePath()
+        baseGlyph = pen.glyph()
+        glyphSet[componentName] = _TestGlyph(baseGlyph)
+
+        pen.addComponent(componentName, (3, 0, 0, 1, 0, 0))
+        compositeGlyph = pen.glyph()
+
+        self.assertEqual(compositeGlyph.components[0].transform,
+                         ((3, 0), (0, 1)))
+
+        with self.assertRaises(struct.error):
+            compositeGlyph.compile({'a': baseGlyph})
+
+
+class _TestGlyph(object):
+    def __init__(self, glyph):
+        self.coordinates = glyph.coordinates
+
+    def draw(self, pen):
+        pen.moveTo(self.coordinates[0])
+        for point in self.coordinates[1:]:
+            pen.lineTo(point)
+        pen.closePath()
+
+
+if __name__ == '__main__':
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/subset/data/Lobster.subset.ttx b/Tests/subset/data/Lobster.subset.ttx
new file mode 100644
index 0000000..c35e570
--- /dev/null
+++ b/Tests/subset/data/Lobster.subset.ttx
@@ -0,0 +1,661 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="A"/>
+    <GlyphID id="2" name="A.salt"/>
+    <GlyphID id="3" name="B"/>
+    <GlyphID id="4" name="B.salt"/>
+    <GlyphID id="5" name="I"/>
+    <GlyphID id="6" name="IJ"/>
+    <GlyphID id="7" name="J"/>
+    <GlyphID id="8" name="one"/>
+    <GlyphID id="9" name="three"/>
+    <GlyphID id="10" name="two"/>
+    <GlyphID id="11" name="zero"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.004"/>
+    <checkSumAdjustment value="0x10139fda"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri Jul  2 22:09:08 2010"/>
+    <modified value="Fri Jan 13 19:25:20 2017"/>
+    <xMin value="-209"/>
+    <yMin value="-250"/>
+    <xMax value="1186"/>
+    <yMax value="1000"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-250"/>
+    <lineGap value="28"/>
+    <advanceWidthMax value="1158"/>
+    <minLeftSideBearing value="-209"/>
+    <minRightSideBearing value="-284"/>
+    <xMaxExtent value="1186"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="12"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="12"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="2"/>
+    <xAvgCharWidth value="542"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="700"/>
+    <ySubscriptYSize value="650"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="140"/>
+    <ySuperscriptXSize value="700"/>
+    <ySuperscriptYSize value="650"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="477"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="250"/>
+    <sFamilyClass value="2562"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="6"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="2"/>
+      <bMidline value="0"/>
+      <bXHeight value="3"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="\x00\x00\x00\x00"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="48"/>
+    <usLastCharIndex value="74"/>
+    <sTypoAscender value="700"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="56"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="250"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="750"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="3"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (c) 2010 by Pablo Impallari. www.impallari.com. All rights reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Lobster 1.4
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      PabloImpallari.www.impallari.com: Lobster 1.4: 2010
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Lobster1.4
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.4
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      Lobster1.4
+    </namerecord>
+    <namerecord nameID="7" platformID="3" platEncID="1" langID="0x409">
+      Lobster 1.4 is a trademark of Pablo Impallari. www.impallari.com.
+    </namerecord>
+    <namerecord nameID="8" platformID="3" platEncID="1" langID="0x409">
+      Pablo Impallari. www.impallari.com
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Pablo Impallari
+    </namerecord>
+    <namerecord nameID="10" platformID="3" platEncID="1" langID="0x409">
+      Copyright (c) 2010 by Pablo Impallari. All rights reserved.
+    </namerecord>
+    <namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
+      www.impallari.com
+    </namerecord>
+    <namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
+      www.impallari.com
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Copyright (c) 2010, Pablo Impallari (www.impallari.com|impallari@gmail.com),&#13;
+with Reserved Font Name Lobster.&#13;
+This Font Software is licensed under the SIL Open Font License, Version 1.1.&#13;
+This license is available with a FAQ at: http://scripts.sil.org/OFL
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://scripts.sil.org/OFL
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x30" name="zero"/><!-- DIGIT ZERO -->
+      <map code="0x31" name="one"/><!-- DIGIT ONE -->
+      <map code="0x32" name="two"/><!-- DIGIT TWO -->
+      <map code="0x33" name="three"/><!-- DIGIT THREE -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x42" name="B"/><!-- LATIN CAPITAL LETTER B -->
+      <map code="0x49" name="I"/><!-- LATIN CAPITAL LETTER I -->
+      <map code="0x4a" name="J"/><!-- LATIN CAPITAL LETTER J -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x30" name="zero"/><!-- DIGIT ZERO -->
+      <map code="0x31" name="one"/><!-- DIGIT ONE -->
+      <map code="0x32" name="two"/><!-- DIGIT TWO -->
+      <map code="0x33" name="three"/><!-- DIGIT THREE -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x42" name="B"/><!-- LATIN CAPITAL LETTER B -->
+      <map code="0x49" name="I"/><!-- LATIN CAPITAL LETTER I -->
+      <map code="0x4a" name="J"/><!-- LATIN CAPITAL LETTER J -->
+    </cmap_format_4>
+  </cmap>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <CFF>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="Lobster1.4">
+      <version value="001.001"/>
+      <Notice value="Copyright (c) 2010 by Pablo Impallari. www.impallari.com. All rights reserved."/>
+      <Copyright value="Copyright (c) 2010 by Pablo Impallari. All rights reserved."/>
+      <FullName value="Lobster 1.4"/>
+      <FamilyName value="Lobster 1.4"/>
+      <Weight value="Regular"/>
+      <isFixedPitch value="0"/>
+      <ItalicAngle value="0"/>
+      <UnderlineThickness value="50"/>
+      <PaintType value="0"/>
+      <CharstringType value="2"/>
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FontBBox value="-209 -250 1186 1000"/>
+      <StrokeWidth value="0"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <Encoding name="StandardEncoding"/>
+      <Private>
+        <BlueValues value="0 0"/>
+        <BlueScale value="0.039625"/>
+        <BlueShift value="7"/>
+        <BlueFuzz value="1"/>
+        <ForceBold value="0"/>
+        <LanguageGroup value="0"/>
+        <ExpansionFactor value="0.06"/>
+        <initialRandomSeed value="0"/>
+        <defaultWidthX value="267"/>
+        <nominalWidthX value="448"/>
+        <Subrs>
+          <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+          <CharString index="0">
+            397 748 rmoveto
+            1 -13 -13 1 -14 hhcurveto
+            -106 callsubr
+            53 75 87 36 vhcurveto
+            -145 -679 rlineto
+            return
+          </CharString>
+          <CharString index="1">
+            -167 -184 -127 -133 -72 38 -25 69 hvcurveto
+            -1 9 -13 8 51 vvcurveto
+            107 return
+          </CharString>
+          <CharString index="2">
+            119 hintmask 01111100
+            230 636 rmoveto
+            -136 -636 rlineto
+            144 hlineto
+            return
+          </CharString>
+          <CharString index="3">
+            -67 41 -25 66 vhcurveto
+            -1 9 -13 8 51 vvcurveto
+            return
+          </CharString>
+          <CharString index="4">
+            hintmask 00110101
+            return
+          </CharString>
+          <CharString index="5">
+            hintmask 10111010
+            return
+          </CharString>
+          <CharString index="6">
+            hintmask 11100110
+            return
+          </CharString>
+        </Subrs>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          -63 endchar
+        </CharString>
+        <CharString name="A">
+          220 -93 -21 114 -20 297 181 -59 59 292 -20 hstemhm
+          9 118 -43 120 hintmask 11101100
+          535 hmoveto
+          157 736 rlineto
+          10 -24 -32 4 -23 hhcurveto
+          -117 -130 -135 -160 -101 hvcurveto
+          2 -21 -17 1 -14 hhcurveto
+          -118 -86 -55 -68 -39 28 -19 34 31 25 15 24 14 -8 17 -5 hvcurveto
+          hintmask 11011010
+          13 34 42 14 62 4 rrcurveto
+          -87 -153 -60 -164 -90 vvcurveto
+          -104 80 -2 54 vhcurveto
+          -6 9 -8 15 32 vvcurveto
+          104 55 190 75 163 vhcurveto
+          44 -4 39 -9 51 -23 -77 -363 rcurveline
+          86 407 rmoveto
+          -39 16 -43 11 -40 8 56 112 64 93 60 32 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="A.salt">
+          142 -92 -21 113 -20 386 52 333 -20 hstem
+          8 120 vstem
+          459 hmoveto
+          157 736 rlineto
+          12 -30 -26 3 -24 hhcurveto
+          -238 -290 -563 -189 -106 65 -2 69 -4 hvcurveto
+          -1 9 -13 -4 51 vvcurveto
+          97 42 172 64 154 vhcurveto
+          158 hlineto
+          -77 -366 rlineto
+          -59 418 rmoveto
+          58 126 72 106 73 32 -56 -264 rcurveline
+          endchar
+        </CharString>
+        <CharString name="B">
+          187 -17 96 -79 -20 406 48 270 46 hstemhm
+          6 93 362 139 -119 101 -101 -105 callsubr
+          82 383 rlineto
+          2 18 20 1 8 hhcurveto
+          73 22 -57 -70 hvcurveto
+          hintmask 10111001
+          -76 -26 -104 -73 -23 -19 10 26 -25 vhcurveto
+          -9 -23 -4 -19 -16 vvcurveto
+          -61 56 -13 43 167 52 192 96 75 -33 69 -85 17 vhcurveto
+          -102 callsubr
+          65 37 35 63 59 vvcurveto
+          82 -66 77 -147 -189 -174 -127 -138 -104 callsubr
+          165 133 78 117 95 37 -51 -57 -75 -64 -87 -80 vhcurveto
+          -6 hlineto
+          47 222 rlineto
+          endchar
+        </CharString>
+        <CharString name="B.salt">
+          185 -28 92 -64 -20 413 41 270 46 hstemhm
+          6 93 350 149 -119 105 -105 -105 callsubr
+          6 30 rlineto
+          hintmask 10111001
+          -41 39 41 -17 39 hhcurveto
+          125 110 175 136 72 -32 62 -82 15 hvcurveto
+          hintmask 10111010
+          64 38 36 61 58 vvcurveto
+          83 -74 78 -144 -183 -177 -126 -139 -104 callsubr
+          152 116 91 138 101 25 -49 -53 -81 -59 -87 -83 vhcurveto
+          -6 hlineto
+          47 222 rlineto
+          -59 -592 rmoveto
+          -20 -21 8 21 -20 hvcurveto
+          62 290 rlineto
+          2 18 20 1 7 hhcurveto
+          hintmask 10111100
+          63 21 -49 -57 -96 -58 -120 -72 hvcurveto
+          endchar
+        </CharString>
+        <CharString name="I">
+          -73 21 -21 750 -20 hstem
+          6 93 vstem
+          -107 callsubr
+          144 hlineto
+          endchar
+        </CharString>
+        <CharString name="IJ">
+          215 -207 50 157 -20 770 -20 hstemhm
+          6 93 13 84 -84 205 hintmask 11111000
+          -107 callsubr
+          34 hlineto
+          -11 -20 -5 -23 -27 vvcurveto
+          -79 48 -58 113 155 66 109 138 29 vhcurveto
+          150 710 -150 -33 -164 -751 rlineto
+          -100 -22 -30 -23 -40 hhcurveto
+          -44 -27 29 39 40 29 33 36 16 17 -7 -16 16 hvcurveto
+          hintmask 11110100
+          4 11 3 11 11 vvcurveto
+          34 -26 24 -41 6 vhcurveto
+          endchar
+        </CharString>
+        <CharString name="J">
+          88 -207 50 144 81 682 -20 hstemhm
+          17 84 -84 220 -50 93 hintmask 11110100
+          538 750 rmoveto
+          -106 callsubr
+          54 76 87 36 vhcurveto
+          -157 -714 rlineto
+          -103 -23 -27 -20 -45 hhcurveto
+          -29 -39 18 52 37 24 37 46 20 15 -5 -21 25 hvcurveto
+          hintmask 11101000
+          4 15 2 14 11 vvcurveto
+          64 -58 3 -40 -79 -43 -66 -68 -83 53 -58 95 164 67 94 153 32 vhcurveto
+          150 710 rlineto
+          endchar
+        </CharString>
+        <CharString name="one">
+          -131 21 -21 624 46 78 -20 hstem
+          324 748 rmoveto
+          -72 -121 -78 -6 -55 hhcurveto
+          -12 -46 rlineto
+          95 hlineto
+          -132 -624 rlineto
+          144 hlineto
+          endchar
+        </CharString>
+        <CharString name="three">
+          66 -5 65 197 51 204 237 -54 54 hstemhm
+          6 111 -12 110 117 155 -117 117 hintmask 11101001
+          205 257 rmoveto
+          38 -8 -33 13 -37 hhcurveto
+          -80 -41 -60 -83 -154 141 -16 58 171 111 136 121 71 -38 65 -88 29 hvcurveto
+          92 46 45 74 66 vvcurveto
+          78 -63 68 -123 vhcurveto
+          -101 callsubr
+          -116 -91 -61 -91 -54 32 -31 40 24 27 11 23 25 hvcurveto
+          -28 8 -10 36 27 vvcurveto
+          hintmask 11011001
+          47 31 31 48 51 25 -36 -46 -70 -58 -94 -113 -31 vhcurveto
+          hintmask 11101010
+          93 -33 40 -80 -76 vvcurveto
+          -87 -53 -82 -86 -37 -39 13 76 40 10 62 78 6 vhcurveto
+          endchar
+        </CharString>
+        <CharString name="two">
+          44 -11 125 -89 89 -89 107 380 237 -54 54 hstemhm
+          66 110 142 119 -119 144 -103 callsubr
+          111 132 rmoveto
+          -5 hlineto
+          83 135 273 98 223 vvcurveto
+          97 -53 64 -137 -151 -55 -79 -68 -58 31 -32 41 24 26 11 23 26 vhcurveto
+          -28 8 -10 37 23 vvcurveto
+          hintmask 01001110
+          50 14 31 67 29 32 -33 -49 vhcurveto
+          -266 -329 -98 -219 vvcurveto
+          -11 0 -11 2 -11 vhcurveto
+          7 20 36 21 23 hhcurveto
+          hintmask 10010110
+          102 37 -36 109 hhcurveto
+          99 20 52 98 14 0 14 -1 16 hvcurveto
+          -44 -47 -17 -25 -70 hhcurveto
+          hintmask 00110110
+          -75 -57 18 -59 hhcurveto
+          endchar
+        </CharString>
+        <CharString name="zero">
+          98 -9 84 623 52 hstem
+          30 158 236 131 vstem
+          377 750 rmoveto
+          -215 -132 -223 -273 -166 35 -97 172 205 113 299 199 168 -53 93 -125 hvcurveto
+          -189 -425 rmoveto
+          225 17 105 148 60 hhcurveto
+          47 7 -63 -82 -232 -68 -246 -114 -48 -11 77 74 37 3 35 2 27 hvcurveto
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="2">
+            <Glyph value="one"/>
+            <Glyph value="three"/>
+            <Glyph value="two"/>
+            <Glyph value="zero"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=4 -->
+          <PairSet index="0">
+            <!-- PairValueCount=3 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="one"/>
+              <Value1 XAdvance="20"/>
+            </PairValueRecord>
+            <PairValueRecord index="1">
+              <SecondGlyph value="two"/>
+              <Value1 XAdvance="20"/>
+            </PairValueRecord>
+            <PairValueRecord index="2">
+              <SecondGlyph value="zero"/>
+              <Value1 XAdvance="10"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="1">
+            <!-- PairValueCount=4 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="one"/>
+              <Value1 XAdvance="10"/>
+            </PairValueRecord>
+            <PairValueRecord index="1">
+              <SecondGlyph value="three"/>
+              <Value1 XAdvance="15"/>
+            </PairValueRecord>
+            <PairValueRecord index="2">
+              <SecondGlyph value="two"/>
+              <Value1 XAdvance="20"/>
+            </PairValueRecord>
+            <PairValueRecord index="3">
+              <SecondGlyph value="zero"/>
+              <Value1 XAdvance="10"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="2">
+            <!-- PairValueCount=3 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="one"/>
+              <Value1 XAdvance="30"/>
+            </PairValueRecord>
+            <PairValueRecord index="1">
+              <SecondGlyph value="three"/>
+              <Value1 XAdvance="10"/>
+            </PairValueRecord>
+            <PairValueRecord index="2">
+              <SecondGlyph value="two"/>
+              <Value1 XAdvance="20"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="3">
+            <!-- PairValueCount=3 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="three"/>
+              <Value1 XAdvance="3"/>
+            </PairValueRecord>
+            <PairValueRecord index="1">
+              <SecondGlyph value="two"/>
+              <Value1 XAdvance="20"/>
+            </PairValueRecord>
+            <PairValueRecord index="2">
+              <SecondGlyph value="zero"/>
+              <Value1 XAdvance="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=3 -->
+            <FeatureIndex index="0" value="0"/>
+            <FeatureIndex index="1" value="1"/>
+            <FeatureIndex index="2" value="2"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=3 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="aalt"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="liga"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="2">
+        <FeatureTag value="salt"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="2"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=3 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="A" out="A.salt"/>
+          <Substitution in="B" out="B.salt"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="I">
+            <Ligature components="J" glyph="IJ"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="A" out="A.salt"/>
+          <Substitution in="B" out="B.salt"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+  <hmtx>
+    <mtx name=".notdef" width="385" lsb="0"/>
+    <mtx name="A" width="668" lsb="9"/>
+    <mtx name="A.salt" width="590" lsb="8"/>
+    <mtx name="B" width="635" lsb="6"/>
+    <mtx name="B.salt" width="633" lsb="6"/>
+    <mtx name="I" width="375" lsb="6"/>
+    <mtx name="IJ" width="663" lsb="6"/>
+    <mtx name="J" width="536" lsb="17"/>
+    <mtx name="one" width="317" lsb="21"/>
+    <mtx name="three" width="514" lsb="6"/>
+    <mtx name="two" width="492" lsb="-11"/>
+    <mtx name="zero" width="546" lsb="30"/>
+  </hmtx>
+
+</ttFont>
diff --git a/Tests/subset/data/NotdefWidthCID-Regular.ttx b/Tests/subset/data/NotdefWidthCID-Regular.ttx
new file mode 100644
index 0000000..ddb1b0f
--- /dev/null
+++ b/Tests/subset/data/NotdefWidthCID-Regular.ttx
@@ -0,0 +1,267 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.7">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="cid00001"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x7f79b966"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Mon Jun 15 05:17:40 2015"/>
+    <modified value="Thu Feb 16 03:38:22 2017"/>
+    <xMin value="-1000"/>
+    <yMin value="-1048"/>
+    <xMax value="2928"/>
+    <yMax value="1808"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-320"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="1000"/>
+    <minLeftSideBearing value="-1000"/>
+    <minRightSideBearing value="-181"/>
+    <xMaxExtent value="2928"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="2"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="2"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="979"/>
+    <usWeightClass value="350"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="325"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="4"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 00000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="32"/>
+    <sTypoAscender value="880"/>
+    <sTypoDescender value="-120"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="1160"/>
+    <usWinDescent value="320"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="543"/>
+    <sCapHeight value="733"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="6"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Notdef Width CID
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="cid00001"/><!-- SPACE -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="cid00001"/><!-- SPACE -->
+    </cmap_format_4>
+  </cmap>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-125"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <CFF>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="NotdefWidthCID-Regular">
+      <ROS Registry="Adobe" Order="Identity" Supplement="0"/>
+      <FullName value="Notdef Width CID Regular"/>
+      <FamilyName value="Notdef Width CID"/>
+      <Weight value="Regular"/>
+      <isFixedPitch value="0"/>
+      <ItalicAngle value="0"/>
+      <UnderlinePosition value="-150"/>
+      <UnderlineThickness value="50"/>
+      <PaintType value="0"/>
+      <CharstringType value="2"/>
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FontBBox value="-1000 -1000 1000 1000"/>
+      <StrokeWidth value="0"/>
+      <CIDFontVersion value="1.0"/>
+      <CIDFontRevision value="0"/>
+      <CIDFontType value="0"/>
+      <CIDCount value="4"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <FDSelect format="3"/>
+      <FDArray>
+        <FontDict index="0">
+          <FontName value="NotdefWidthCID-Regular-Space"/>
+          <FontMatrix value="0.001 0 0 0.001 0 0"/>
+          <Private>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="0"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="602"/>
+            <nominalWidthX value="630"/>
+            <Subrs>
+              <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+            </Subrs>
+          </Private>
+        </FontDict>
+        <FontDict index="1">
+          <FontName value="NotdefWidthCID-Regular-Notdef"/>
+          <FontMatrix value="0.001 0 0 0.001 0 0"/>
+          <Private>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="0"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="107"/>
+            <Subrs>
+              <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+            </Subrs>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef" fdSelectIndex="1">
+          -120 50 900 50 hstem
+          100 50 700 50 vstem
+          100 -120 rmoveto
+          800 1000 -800 hlineto
+          400 -459 rmoveto
+          -318 409 rlineto
+          636 hlineto
+          -286 -450 rmoveto
+          318 409 rlineto
+          -818 vlineto
+          -668 -41 rmoveto
+          318 409 318 -409 rlineto
+          -668 859 rmoveto
+          318 -409 -318 -409 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid00001" fdSelectIndex="0">
+          -407 endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+  <VORG>
+    <majorVersion value="1"/>
+    <minorVersion value="0"/>
+    <defaultVertOriginY value="880"/>
+    <numVertOriginYMetrics value="0"/>
+  </VORG>
+
+  <hmtx>
+    <mtx name=".notdef" width="1000" lsb="100"/>
+    <mtx name="cid00001" width="223" lsb="0"/>
+  </hmtx>
+
+  <vhea>
+    <tableVersion value="0x00011000"/>
+    <ascent value="500"/>
+    <descent value="-500"/>
+    <lineGap value="0"/>
+    <advanceHeightMax value="3000"/>
+    <minTopSideBearing value="-1000"/>
+    <minBottomSideBearing value="-672"/>
+    <yMaxExtent value="2928"/>
+    <caretSlopeRise value="0"/>
+    <caretSlopeRun value="1"/>
+    <caretOffset value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <reserved4 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfVMetrics value="1"/>
+  </vhea>
+
+  <vmtx>
+    <mtx name=".notdef" height="1000" tsb="0"/>
+    <mtx name="cid00001" height="1000" tsb="880"/>
+  </vmtx>
+
+</ttFont>
diff --git a/Tests/subset/data/TestANKR.ttx b/Tests/subset/data/TestANKR.ttx
new file mode 100644
index 0000000..8bb37c7
--- /dev/null
+++ b/Tests/subset/data/TestANKR.ttx
@@ -0,0 +1,325 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="space"/>
+    <GlyphID id="2" name="zero"/>
+    <GlyphID id="3" name="one"/>
+    <GlyphID id="4" name="two"/>
+    <GlyphID id="5" name="three"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0xf1c8c54f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Mar 31 07:56:22 2016"/>
+    <modified value="Thu Mar 31 11:16:24 2016"/>
+    <xMin value="40"/>
+    <yMin value="-10"/>
+    <xMax value="620"/>
+    <yMax value="710"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="700"/>
+    <minLeftSideBearing value="40"/>
+    <minRightSideBearing value="40"/>
+    <xMaxExtent value="620"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="4"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="32"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="1"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="606"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="8722"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="700" lsb="80"/>
+    <mtx name="space" width="700" lsb="0"/>
+    <mtx name="zero" width="625" lsb="40"/>
+    <mtx name="one" width="660" lsb="80"/>
+    <mtx name="two" width="660" lsb="80"/>
+    <mtx name="three" width="660" lsb="80"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/>
+      <map code="0x30" name="zero"/>
+      <map code="0x31" name="one"/>
+      <map code="0x32" name="two"/>
+      <map code="0x33" name="three"/>
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="80" yMin="0" xMax="620" yMax="700">
+      <contour>
+        <pt x="620" y="700" on="1"/>
+        <pt x="620" y="0" on="1"/>
+        <pt x="80" y="0" on="1"/>
+        <pt x="80" y="700" on="1"/>
+      </contour>
+      <contour>
+        <pt x="160" y="80" on="1"/>
+        <pt x="540" y="80" on="1"/>
+        <pt x="540" y="620" on="1"/>
+        <pt x="160" y="620" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="one" xMin="80" yMin="230" xMax="580" yMax="490">
+      <contour>
+        <pt x="580" y="490" on="1"/>
+        <pt x="580" y="410" on="1"/>
+        <pt x="80" y="410" on="1"/>
+        <pt x="80" y="490" on="1"/>
+      </contour>
+      <contour>
+        <pt x="580" y="310" on="1"/>
+        <pt x="580" y="230" on="1"/>
+        <pt x="80" y="230" on="1"/>
+        <pt x="80" y="310" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="two" xMin="80" yMin="310" xMax="580" yMax="390">
+      <contour>
+        <pt x="580" y="390" on="1"/>
+        <pt x="580" y="310" on="1"/>
+        <pt x="80" y="310" on="1"/>
+        <pt x="80" y="390" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="three" xMin="80" yMin="100" xMax="580" yMax="600">
+      <contour>
+        <pt x="580" y="310" on="1"/>
+        <pt x="370" y="310" on="1"/>
+        <pt x="370" y="100" on="1"/>
+        <pt x="290" y="100" on="1"/>
+        <pt x="290" y="310" on="1"/>
+        <pt x="80" y="310" on="1"/>
+        <pt x="80" y="390" on="1"/>
+        <pt x="290" y="390" on="1"/>
+        <pt x="290" y="600" on="1"/>
+        <pt x="370" y="600" on="1"/>
+        <pt x="370" y="390" on="1"/>
+        <pt x="580" y="390" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+    <TTGlyph name="zero" xMin="40" yMin="-10" xMax="585" yMax="710">
+      <contour>
+        <pt x="237" y="-10" on="0"/>
+        <pt x="113" y="78" on="0"/>
+        <pt x="40" y="241" on="0"/>
+        <pt x="40" y="350" on="1"/>
+        <pt x="40" y="459" on="0"/>
+        <pt x="113" y="622" on="0"/>
+        <pt x="237" y="710" on="0"/>
+        <pt x="313" y="710" on="1"/>
+        <pt x="388" y="710" on="0"/>
+        <pt x="513" y="622" on="0"/>
+        <pt x="585" y="459" on="0"/>
+        <pt x="585" y="350" on="1"/>
+        <pt x="585" y="241" on="0"/>
+        <pt x="513" y="78" on="0"/>
+        <pt x="388" y="-10" on="0"/>
+        <pt x="313" y="-10" on="1"/>
+      </contour>
+      <contour>
+        <pt x="366" y="74" on="0"/>
+        <pt x="454" y="144" on="0"/>
+        <pt x="505" y="270" on="0"/>
+        <pt x="505" y="350" on="1"/>
+        <pt x="505" y="430" on="0"/>
+        <pt x="454" y="556" on="0"/>
+        <pt x="366" y="626" on="0"/>
+        <pt x="313" y="626" on="1"/>
+        <pt x="260" y="626" on="0"/>
+        <pt x="171" y="556" on="0"/>
+        <pt x="120" y="430" on="0"/>
+        <pt x="120" y="350" on="1"/>
+        <pt x="120" y="270" on="0"/>
+        <pt x="171" y="144" on="0"/>
+        <pt x="260" y="74" on="0"/>
+        <pt x="313" y="74" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestANKR
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+  </name>
+
+  <gasp>
+    <gaspRange rangeMaxPPEM="7" rangeGaspBehavior="10"/>
+    <gaspRange rangeMaxPPEM="19" rangeGaspBehavior="7"/>
+    <gaspRange rangeMaxPPEM="65535" rangeGaspBehavior="15"/>
+  </gasp>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+    </extraNames>
+  </post>
+
+  <ankr>
+    <AnchorPoints Format="0">
+      <Flags value="0"/>
+      <Anchors>
+        <Lookup glyph="one">
+          <!-- AnchorPointCount=1 -->
+          <AnchorPoint index="0">
+            <XCoordinate value="565"/>
+            <YCoordinate value="1118"/>
+          </AnchorPoint>
+        </Lookup>
+        <Lookup glyph="three">
+          <!-- AnchorPointCount=1 -->
+          <AnchorPoint index="0">
+            <XCoordinate value="-302"/>
+            <YCoordinate value="1118"/>
+          </AnchorPoint>
+        </Lookup>
+      </Anchors>
+    </AnchorPoints>
+  </ankr>
+
+</ttFont>
diff --git a/Tests/subset/data/TestBSLN-0.ttx b/Tests/subset/data/TestBSLN-0.ttx
new file mode 100644
index 0000000..9a446e1
--- /dev/null
+++ b/Tests/subset/data/TestBSLN-0.ttx
@@ -0,0 +1,342 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="space"/>
+    <GlyphID id="2" name="zero"/>
+    <GlyphID id="3" name="one"/>
+    <GlyphID id="4" name="two"/>
+    <GlyphID id="5" name="three"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0xf1c8c54f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Mar 31 07:56:22 2016"/>
+    <modified value="Thu Mar 31 11:16:24 2016"/>
+    <xMin value="40"/>
+    <yMin value="-10"/>
+    <xMax value="620"/>
+    <yMax value="710"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="700"/>
+    <minLeftSideBearing value="40"/>
+    <minRightSideBearing value="40"/>
+    <xMaxExtent value="620"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="4"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="32"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="1"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="606"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="8722"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="700" lsb="80"/>
+    <mtx name="space" width="700" lsb="0"/>
+    <mtx name="zero" width="625" lsb="40"/>
+    <mtx name="one" width="660" lsb="80"/>
+    <mtx name="two" width="660" lsb="80"/>
+    <mtx name="three" width="660" lsb="80"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/>
+      <map code="0x30" name="zero"/>
+      <map code="0x31" name="one"/>
+      <map code="0x32" name="two"/>
+      <map code="0x33" name="three"/>
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="80" yMin="0" xMax="620" yMax="700">
+      <contour>
+        <pt x="620" y="700" on="1"/>
+        <pt x="620" y="0" on="1"/>
+        <pt x="80" y="0" on="1"/>
+        <pt x="80" y="700" on="1"/>
+      </contour>
+      <contour>
+        <pt x="160" y="80" on="1"/>
+        <pt x="540" y="80" on="1"/>
+        <pt x="540" y="620" on="1"/>
+        <pt x="160" y="620" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="one" xMin="80" yMin="230" xMax="580" yMax="490">
+      <contour>
+        <pt x="580" y="490" on="1"/>
+        <pt x="580" y="410" on="1"/>
+        <pt x="80" y="410" on="1"/>
+        <pt x="80" y="490" on="1"/>
+      </contour>
+      <contour>
+        <pt x="580" y="310" on="1"/>
+        <pt x="580" y="230" on="1"/>
+        <pt x="80" y="230" on="1"/>
+        <pt x="80" y="310" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="two" xMin="80" yMin="310" xMax="580" yMax="390">
+      <contour>
+        <pt x="580" y="390" on="1"/>
+        <pt x="580" y="310" on="1"/>
+        <pt x="80" y="310" on="1"/>
+        <pt x="80" y="390" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="three" xMin="80" yMin="100" xMax="580" yMax="600">
+      <contour>
+        <pt x="580" y="310" on="1"/>
+        <pt x="370" y="310" on="1"/>
+        <pt x="370" y="100" on="1"/>
+        <pt x="290" y="100" on="1"/>
+        <pt x="290" y="310" on="1"/>
+        <pt x="80" y="310" on="1"/>
+        <pt x="80" y="390" on="1"/>
+        <pt x="290" y="390" on="1"/>
+        <pt x="290" y="600" on="1"/>
+        <pt x="370" y="600" on="1"/>
+        <pt x="370" y="390" on="1"/>
+        <pt x="580" y="390" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+    <TTGlyph name="zero" xMin="40" yMin="-10" xMax="585" yMax="710">
+      <contour>
+        <pt x="237" y="-10" on="0"/>
+        <pt x="113" y="78" on="0"/>
+        <pt x="40" y="241" on="0"/>
+        <pt x="40" y="350" on="1"/>
+        <pt x="40" y="459" on="0"/>
+        <pt x="113" y="622" on="0"/>
+        <pt x="237" y="710" on="0"/>
+        <pt x="313" y="710" on="1"/>
+        <pt x="388" y="710" on="0"/>
+        <pt x="513" y="622" on="0"/>
+        <pt x="585" y="459" on="0"/>
+        <pt x="585" y="350" on="1"/>
+        <pt x="585" y="241" on="0"/>
+        <pt x="513" y="78" on="0"/>
+        <pt x="388" y="-10" on="0"/>
+        <pt x="313" y="-10" on="1"/>
+      </contour>
+      <contour>
+        <pt x="366" y="74" on="0"/>
+        <pt x="454" y="144" on="0"/>
+        <pt x="505" y="270" on="0"/>
+        <pt x="505" y="350" on="1"/>
+        <pt x="505" y="430" on="0"/>
+        <pt x="454" y="556" on="0"/>
+        <pt x="366" y="626" on="0"/>
+        <pt x="313" y="626" on="1"/>
+        <pt x="260" y="626" on="0"/>
+        <pt x="171" y="556" on="0"/>
+        <pt x="120" y="430" on="0"/>
+        <pt x="120" y="350" on="1"/>
+        <pt x="120" y="270" on="0"/>
+        <pt x="171" y="144" on="0"/>
+        <pt x="260" y="74" on="0"/>
+        <pt x="313" y="74" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestBSLN-0
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+  </name>
+
+  <gasp>
+    <gaspRange rangeMaxPPEM="7" rangeGaspBehavior="10"/>
+    <gaspRange rangeMaxPPEM="19" rangeGaspBehavior="7"/>
+    <gaspRange rangeMaxPPEM="65535" rangeGaspBehavior="15"/>
+  </gasp>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+    </extraNames>
+  </post>
+
+  <bsln>
+    <Version value="0x00010000"/>
+    <Baseline Format="0">
+      <DefaultBaseline value="0"/>
+      <Delta index="0" value="0"/>
+      <Delta index="1" value="465"/>
+      <Delta index="2" value="0"/>
+      <Delta index="3" value="1345"/>
+      <Delta index="4" value="507"/>
+      <Delta index="5" value="0"/>
+      <Delta index="6" value="0"/>
+      <Delta index="7" value="0"/>
+      <Delta index="8" value="0"/>
+      <Delta index="9" value="0"/>
+      <Delta index="10" value="0"/>
+      <Delta index="11" value="0"/>
+      <Delta index="12" value="0"/>
+      <Delta index="13" value="0"/>
+      <Delta index="14" value="0"/>
+      <Delta index="15" value="0"/>
+      <Delta index="16" value="0"/>
+      <Delta index="17" value="0"/>
+      <Delta index="18" value="0"/>
+      <Delta index="19" value="0"/>
+      <Delta index="20" value="0"/>
+      <Delta index="21" value="0"/>
+      <Delta index="22" value="0"/>
+      <Delta index="23" value="0"/>
+      <Delta index="24" value="0"/>
+      <Delta index="25" value="0"/>
+      <Delta index="26" value="0"/>
+      <Delta index="27" value="0"/>
+      <Delta index="28" value="0"/>
+      <Delta index="29" value="0"/>
+      <Delta index="30" value="0"/>
+      <Delta index="31" value="0"/>
+    </Baseline>
+  </bsln>
+
+</ttFont>
diff --git a/Tests/subset/data/TestBSLN-1.ttx b/Tests/subset/data/TestBSLN-1.ttx
new file mode 100644
index 0000000..3ad83a2
--- /dev/null
+++ b/Tests/subset/data/TestBSLN-1.ttx
@@ -0,0 +1,348 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="space"/>
+    <GlyphID id="2" name="zero"/>
+    <GlyphID id="3" name="one"/>
+    <GlyphID id="4" name="two"/>
+    <GlyphID id="5" name="uni2EA2"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0xf1c8c54f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Mar 31 07:56:22 2016"/>
+    <modified value="Thu Mar 31 11:16:24 2016"/>
+    <xMin value="40"/>
+    <yMin value="-10"/>
+    <xMax value="620"/>
+    <yMax value="710"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="700"/>
+    <minLeftSideBearing value="40"/>
+    <minRightSideBearing value="40"/>
+    <xMaxExtent value="620"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="4"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="32"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="1"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="606"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="8722"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="700" lsb="80"/>
+    <mtx name="space" width="700" lsb="0"/>
+    <mtx name="zero" width="625" lsb="40"/>
+    <mtx name="one" width="660" lsb="80"/>
+    <mtx name="two" width="660" lsb="80"/>
+    <mtx name="uni2EA2" width="660" lsb="80"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/>
+      <map code="0x30" name="zero"/>
+      <map code="0x31" name="one"/>
+      <map code="0x32" name="two"/>
+      <map code="0x2EA2" name="uni2EA2"/>
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="80" yMin="0" xMax="620" yMax="700">
+      <contour>
+        <pt x="620" y="700" on="1"/>
+        <pt x="620" y="0" on="1"/>
+        <pt x="80" y="0" on="1"/>
+        <pt x="80" y="700" on="1"/>
+      </contour>
+      <contour>
+        <pt x="160" y="80" on="1"/>
+        <pt x="540" y="80" on="1"/>
+        <pt x="540" y="620" on="1"/>
+        <pt x="160" y="620" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="one" xMin="80" yMin="230" xMax="580" yMax="490">
+      <contour>
+        <pt x="580" y="490" on="1"/>
+        <pt x="580" y="410" on="1"/>
+        <pt x="80" y="410" on="1"/>
+        <pt x="80" y="490" on="1"/>
+      </contour>
+      <contour>
+        <pt x="580" y="310" on="1"/>
+        <pt x="580" y="230" on="1"/>
+        <pt x="80" y="230" on="1"/>
+        <pt x="80" y="310" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="two" xMin="80" yMin="310" xMax="580" yMax="390">
+      <contour>
+        <pt x="580" y="390" on="1"/>
+        <pt x="580" y="310" on="1"/>
+        <pt x="80" y="310" on="1"/>
+        <pt x="80" y="390" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="uni2EA2" xMin="80" yMin="100" xMax="580" yMax="600">
+      <contour>
+        <pt x="580" y="310" on="1"/>
+        <pt x="370" y="310" on="1"/>
+        <pt x="370" y="100" on="1"/>
+        <pt x="290" y="100" on="1"/>
+        <pt x="290" y="310" on="1"/>
+        <pt x="80" y="310" on="1"/>
+        <pt x="80" y="390" on="1"/>
+        <pt x="290" y="390" on="1"/>
+        <pt x="290" y="600" on="1"/>
+        <pt x="370" y="600" on="1"/>
+        <pt x="370" y="390" on="1"/>
+        <pt x="580" y="390" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+    <TTGlyph name="zero" xMin="40" yMin="-10" xMax="585" yMax="710">
+      <contour>
+        <pt x="237" y="-10" on="0"/>
+        <pt x="113" y="78" on="0"/>
+        <pt x="40" y="241" on="0"/>
+        <pt x="40" y="350" on="1"/>
+        <pt x="40" y="459" on="0"/>
+        <pt x="113" y="622" on="0"/>
+        <pt x="237" y="710" on="0"/>
+        <pt x="313" y="710" on="1"/>
+        <pt x="388" y="710" on="0"/>
+        <pt x="513" y="622" on="0"/>
+        <pt x="585" y="459" on="0"/>
+        <pt x="585" y="350" on="1"/>
+        <pt x="585" y="241" on="0"/>
+        <pt x="513" y="78" on="0"/>
+        <pt x="388" y="-10" on="0"/>
+        <pt x="313" y="-10" on="1"/>
+      </contour>
+      <contour>
+        <pt x="366" y="74" on="0"/>
+        <pt x="454" y="144" on="0"/>
+        <pt x="505" y="270" on="0"/>
+        <pt x="505" y="350" on="1"/>
+        <pt x="505" y="430" on="0"/>
+        <pt x="454" y="556" on="0"/>
+        <pt x="366" y="626" on="0"/>
+        <pt x="313" y="626" on="1"/>
+        <pt x="260" y="626" on="0"/>
+        <pt x="171" y="556" on="0"/>
+        <pt x="120" y="430" on="0"/>
+        <pt x="120" y="350" on="1"/>
+        <pt x="120" y="270" on="0"/>
+        <pt x="171" y="144" on="0"/>
+        <pt x="260" y="74" on="0"/>
+        <pt x="313" y="74" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestBSLN-1
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+  </name>
+
+  <gasp>
+    <gaspRange rangeMaxPPEM="7" rangeGaspBehavior="10"/>
+    <gaspRange rangeMaxPPEM="19" rangeGaspBehavior="7"/>
+    <gaspRange rangeMaxPPEM="65535" rangeGaspBehavior="15"/>
+  </gasp>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+    </extraNames>
+  </post>
+
+  <bsln>
+    <Version value="0x00010000"/>
+    <Baseline Format="1">
+      <DefaultBaseline value="1"/>
+      <Delta index="0" value="0"/>
+      <Delta index="1" value="465"/>
+      <Delta index="2" value="0"/>
+      <Delta index="3" value="1345"/>
+      <Delta index="4" value="507"/>
+      <Delta index="5" value="0"/>
+      <Delta index="6" value="0"/>
+      <Delta index="7" value="0"/>
+      <Delta index="8" value="0"/>
+      <Delta index="9" value="0"/>
+      <Delta index="10" value="0"/>
+      <Delta index="11" value="0"/>
+      <Delta index="12" value="0"/>
+      <Delta index="13" value="0"/>
+      <Delta index="14" value="0"/>
+      <Delta index="15" value="0"/>
+      <Delta index="16" value="0"/>
+      <Delta index="17" value="0"/>
+      <Delta index="18" value="0"/>
+      <Delta index="19" value="0"/>
+      <Delta index="20" value="0"/>
+      <Delta index="21" value="0"/>
+      <Delta index="22" value="0"/>
+      <Delta index="23" value="0"/>
+      <Delta index="24" value="0"/>
+      <Delta index="25" value="0"/>
+      <Delta index="26" value="0"/>
+      <Delta index="27" value="0"/>
+      <Delta index="28" value="0"/>
+      <Delta index="29" value="0"/>
+      <Delta index="30" value="0"/>
+      <Delta index="31" value="0"/>
+      <BaselineValues>
+        <Lookup glyph=".notdef" value="0"/>
+        <Lookup glyph="zero" value="0"/>
+        <Lookup glyph="one" value="0"/>
+        <Lookup glyph="two" value="0"/>
+      </BaselineValues>
+    </Baseline>
+  </bsln>
+
+</ttFont>
diff --git a/Tests/subset/data/TestBSLN-2.ttx b/Tests/subset/data/TestBSLN-2.ttx
new file mode 100644
index 0000000..7a00500
--- /dev/null
+++ b/Tests/subset/data/TestBSLN-2.ttx
@@ -0,0 +1,342 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="space"/>
+    <GlyphID id="2" name="zero"/>
+    <GlyphID id="3" name="one"/>
+    <GlyphID id="4" name="two"/>
+    <GlyphID id="5" name="P"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0xf1c8c54f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Mar 31 07:56:22 2016"/>
+    <modified value="Thu Mar 31 11:16:24 2016"/>
+    <xMin value="40"/>
+    <yMin value="-10"/>
+    <xMax value="620"/>
+    <yMax value="710"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="700"/>
+    <minLeftSideBearing value="40"/>
+    <minRightSideBearing value="40"/>
+    <xMaxExtent value="620"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="4"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="32"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="1"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="606"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="8722"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="700" lsb="80"/>
+    <mtx name="space" width="700" lsb="0"/>
+    <mtx name="zero" width="625" lsb="40"/>
+    <mtx name="one" width="660" lsb="80"/>
+    <mtx name="two" width="660" lsb="80"/>
+    <mtx name="P" width="660" lsb="80"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/>
+      <map code="0x30" name="zero"/>
+      <map code="0x31" name="one"/>
+      <map code="0x32" name="two"/>
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="80" yMin="0" xMax="620" yMax="700">
+      <contour>
+        <pt x="620" y="700" on="1"/>
+        <pt x="620" y="0" on="1"/>
+        <pt x="80" y="0" on="1"/>
+        <pt x="80" y="700" on="1"/>
+      </contour>
+      <contour>
+        <pt x="160" y="80" on="1"/>
+        <pt x="540" y="80" on="1"/>
+        <pt x="540" y="620" on="1"/>
+        <pt x="160" y="620" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="one" xMin="80" yMin="230" xMax="580" yMax="490">
+      <contour>
+        <pt x="580" y="490" on="1"/>
+        <pt x="580" y="410" on="1"/>
+        <pt x="80" y="410" on="1"/>
+        <pt x="80" y="490" on="1"/>
+      </contour>
+      <contour>
+        <pt x="580" y="310" on="1"/>
+        <pt x="580" y="230" on="1"/>
+        <pt x="80" y="230" on="1"/>
+        <pt x="80" y="310" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="two" xMin="80" yMin="310" xMax="580" yMax="390">
+      <contour>
+        <pt x="580" y="390" on="1"/>
+        <pt x="580" y="310" on="1"/>
+        <pt x="80" y="310" on="1"/>
+        <pt x="80" y="390" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="P" xMin="80" yMin="100" xMax="580" yMax="600">
+      <contour>
+        <pt x="580" y="310" on="1"/>
+        <pt x="370" y="310" on="1"/>
+        <pt x="370" y="100" on="1"/>
+        <pt x="290" y="100" on="1"/>
+        <pt x="290" y="310" on="1"/>
+        <pt x="80" y="310" on="1"/>
+        <pt x="80" y="390" on="1"/>
+        <pt x="290" y="390" on="1"/>
+        <pt x="290" y="600" on="1"/>
+        <pt x="370" y="600" on="1"/>
+        <pt x="370" y="390" on="1"/>
+        <pt x="580" y="390" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+    <TTGlyph name="zero" xMin="40" yMin="-10" xMax="585" yMax="710">
+      <contour>
+        <pt x="237" y="-10" on="0"/>
+        <pt x="113" y="78" on="0"/>
+        <pt x="40" y="241" on="0"/>
+        <pt x="40" y="350" on="1"/>
+        <pt x="40" y="459" on="0"/>
+        <pt x="113" y="622" on="0"/>
+        <pt x="237" y="710" on="0"/>
+        <pt x="313" y="710" on="1"/>
+        <pt x="388" y="710" on="0"/>
+        <pt x="513" y="622" on="0"/>
+        <pt x="585" y="459" on="0"/>
+        <pt x="585" y="350" on="1"/>
+        <pt x="585" y="241" on="0"/>
+        <pt x="513" y="78" on="0"/>
+        <pt x="388" y="-10" on="0"/>
+        <pt x="313" y="-10" on="1"/>
+      </contour>
+      <contour>
+        <pt x="366" y="74" on="0"/>
+        <pt x="454" y="144" on="0"/>
+        <pt x="505" y="270" on="0"/>
+        <pt x="505" y="350" on="1"/>
+        <pt x="505" y="430" on="0"/>
+        <pt x="454" y="556" on="0"/>
+        <pt x="366" y="626" on="0"/>
+        <pt x="313" y="626" on="1"/>
+        <pt x="260" y="626" on="0"/>
+        <pt x="171" y="556" on="0"/>
+        <pt x="120" y="430" on="0"/>
+        <pt x="120" y="350" on="1"/>
+        <pt x="120" y="270" on="0"/>
+        <pt x="171" y="144" on="0"/>
+        <pt x="260" y="74" on="0"/>
+        <pt x="313" y="74" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestBSLN-2
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+  </name>
+
+  <gasp>
+    <gaspRange rangeMaxPPEM="7" rangeGaspBehavior="10"/>
+    <gaspRange rangeMaxPPEM="19" rangeGaspBehavior="7"/>
+    <gaspRange rangeMaxPPEM="65535" rangeGaspBehavior="15"/>
+  </gasp>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+    </extraNames>
+  </post>
+
+  <bsln>
+    <Version value="0x00010000"/>
+    <Baseline Format="2">
+      <DefaultBaseline value="0"/>
+      <StandardGlyph value="P"/>
+      <ControlPoint index="0" value="7"/>
+      <ControlPoint index="1" value="8"/>
+      <ControlPoint index="2" value="9"/>
+      <ControlPoint index="3" value="10"/>
+      <ControlPoint index="4" value="11"/>
+      <ControlPoint index="5" value="65535"/>
+      <ControlPoint index="6" value="65535"/>
+      <ControlPoint index="7" value="65535"/>
+      <ControlPoint index="8" value="65535"/>
+      <ControlPoint index="9" value="65535"/>
+      <ControlPoint index="10" value="65535"/>
+      <ControlPoint index="11" value="65535"/>
+      <ControlPoint index="12" value="65535"/>
+      <ControlPoint index="13" value="65535"/>
+      <ControlPoint index="14" value="65535"/>
+      <ControlPoint index="15" value="65535"/>
+      <ControlPoint index="16" value="65535"/>
+      <ControlPoint index="17" value="65535"/>
+      <ControlPoint index="18" value="65535"/>
+      <ControlPoint index="19" value="65535"/>
+      <ControlPoint index="20" value="65535"/>
+      <ControlPoint index="21" value="65535"/>
+      <ControlPoint index="22" value="65535"/>
+      <ControlPoint index="23" value="65535"/>
+      <ControlPoint index="24" value="65535"/>
+      <ControlPoint index="25" value="65535"/>
+      <ControlPoint index="26" value="65535"/>
+      <ControlPoint index="27" value="65535"/>
+      <ControlPoint index="28" value="65535"/>
+      <ControlPoint index="29" value="65535"/>
+      <ControlPoint index="30" value="65535"/>
+      <ControlPoint index="31" value="65535"/>
+    </Baseline>
+  </bsln>
+
+</ttFont>
diff --git a/Tests/subset/data/TestBSLN-3.ttx b/Tests/subset/data/TestBSLN-3.ttx
new file mode 100644
index 0000000..4bba6b4
--- /dev/null
+++ b/Tests/subset/data/TestBSLN-3.ttx
@@ -0,0 +1,363 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="space"/>
+    <GlyphID id="2" name="zero"/>
+    <GlyphID id="3" name="one"/>
+    <GlyphID id="4" name="two"/>
+    <GlyphID id="5" name="P"/>
+    <GlyphID id="6" name="uni2EA2"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0xf1c8c54f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Mar 31 07:56:22 2016"/>
+    <modified value="Thu Mar 31 11:16:24 2016"/>
+    <xMin value="40"/>
+    <yMin value="-10"/>
+    <xMax value="620"/>
+    <yMax value="710"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="700"/>
+    <minLeftSideBearing value="40"/>
+    <minRightSideBearing value="40"/>
+    <xMaxExtent value="620"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="4"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="32"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="1"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="606"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="8722"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="700" lsb="80"/>
+    <mtx name="space" width="700" lsb="0"/>
+    <mtx name="zero" width="625" lsb="40"/>
+    <mtx name="one" width="660" lsb="80"/>
+    <mtx name="two" width="660" lsb="80"/>
+    <mtx name="P" width="660" lsb="80"/>
+    <mtx name="uni2EA2" width="660" lsb="80"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/>
+      <map code="0x30" name="zero"/>
+      <map code="0x31" name="one"/>
+      <map code="0x32" name="two"/>
+      <map code="0x2EA2" name="uni2EA2"/>
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="80" yMin="0" xMax="620" yMax="700">
+      <contour>
+        <pt x="620" y="700" on="1"/>
+        <pt x="620" y="0" on="1"/>
+        <pt x="80" y="0" on="1"/>
+        <pt x="80" y="700" on="1"/>
+      </contour>
+      <contour>
+        <pt x="160" y="80" on="1"/>
+        <pt x="540" y="80" on="1"/>
+        <pt x="540" y="620" on="1"/>
+        <pt x="160" y="620" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="one" xMin="80" yMin="230" xMax="580" yMax="490">
+      <contour>
+        <pt x="580" y="490" on="1"/>
+        <pt x="580" y="410" on="1"/>
+        <pt x="80" y="410" on="1"/>
+        <pt x="80" y="490" on="1"/>
+      </contour>
+      <contour>
+        <pt x="580" y="310" on="1"/>
+        <pt x="580" y="230" on="1"/>
+        <pt x="80" y="230" on="1"/>
+        <pt x="80" y="310" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="two" xMin="80" yMin="310" xMax="580" yMax="390">
+      <contour>
+        <pt x="580" y="390" on="1"/>
+        <pt x="580" y="310" on="1"/>
+        <pt x="80" y="310" on="1"/>
+        <pt x="80" y="390" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="P" xMin="80" yMin="100" xMax="580" yMax="600">
+      <contour>
+        <pt x="580" y="310" on="1"/>
+        <pt x="370" y="310" on="1"/>
+        <pt x="370" y="100" on="1"/>
+        <pt x="290" y="100" on="1"/>
+        <pt x="290" y="310" on="1"/>
+        <pt x="80" y="310" on="1"/>
+        <pt x="80" y="390" on="1"/>
+        <pt x="290" y="390" on="1"/>
+        <pt x="290" y="600" on="1"/>
+        <pt x="370" y="600" on="1"/>
+        <pt x="370" y="390" on="1"/>
+        <pt x="580" y="390" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+    <TTGlyph name="uni2EA2" xMin="80" yMin="310" xMax="580" yMax="390">
+      <contour>
+        <pt x="580" y="390" on="1"/>
+        <pt x="580" y="310" on="1"/>
+        <pt x="80" y="310" on="1"/>
+        <pt x="80" y="390" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="zero" xMin="40" yMin="-10" xMax="585" yMax="710">
+      <contour>
+        <pt x="237" y="-10" on="0"/>
+        <pt x="113" y="78" on="0"/>
+        <pt x="40" y="241" on="0"/>
+        <pt x="40" y="350" on="1"/>
+        <pt x="40" y="459" on="0"/>
+        <pt x="113" y="622" on="0"/>
+        <pt x="237" y="710" on="0"/>
+        <pt x="313" y="710" on="1"/>
+        <pt x="388" y="710" on="0"/>
+        <pt x="513" y="622" on="0"/>
+        <pt x="585" y="459" on="0"/>
+        <pt x="585" y="350" on="1"/>
+        <pt x="585" y="241" on="0"/>
+        <pt x="513" y="78" on="0"/>
+        <pt x="388" y="-10" on="0"/>
+        <pt x="313" y="-10" on="1"/>
+      </contour>
+      <contour>
+        <pt x="366" y="74" on="0"/>
+        <pt x="454" y="144" on="0"/>
+        <pt x="505" y="270" on="0"/>
+        <pt x="505" y="350" on="1"/>
+        <pt x="505" y="430" on="0"/>
+        <pt x="454" y="556" on="0"/>
+        <pt x="366" y="626" on="0"/>
+        <pt x="313" y="626" on="1"/>
+        <pt x="260" y="626" on="0"/>
+        <pt x="171" y="556" on="0"/>
+        <pt x="120" y="430" on="0"/>
+        <pt x="120" y="350" on="1"/>
+        <pt x="120" y="270" on="0"/>
+        <pt x="171" y="144" on="0"/>
+        <pt x="260" y="74" on="0"/>
+        <pt x="313" y="74" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestBSLN-3
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+  </name>
+
+  <gasp>
+    <gaspRange rangeMaxPPEM="7" rangeGaspBehavior="10"/>
+    <gaspRange rangeMaxPPEM="19" rangeGaspBehavior="7"/>
+    <gaspRange rangeMaxPPEM="65535" rangeGaspBehavior="15"/>
+  </gasp>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+    </extraNames>
+  </post>
+
+  <bsln>
+    <Version value="0x00010000"/>
+    <Baseline Format="3">
+      <DefaultBaseline value="1"/>
+      <StandardGlyph value="P"/>
+      <ControlPoint index="0" value="7"/>
+      <ControlPoint index="1" value="8"/>
+      <ControlPoint index="2" value="9"/>
+      <ControlPoint index="3" value="10"/>
+      <ControlPoint index="4" value="11"/>
+      <ControlPoint index="5" value="65535"/>
+      <ControlPoint index="6" value="65535"/>
+      <ControlPoint index="7" value="65535"/>
+      <ControlPoint index="8" value="65535"/>
+      <ControlPoint index="9" value="65535"/>
+      <ControlPoint index="10" value="65535"/>
+      <ControlPoint index="11" value="65535"/>
+      <ControlPoint index="12" value="65535"/>
+      <ControlPoint index="13" value="65535"/>
+      <ControlPoint index="14" value="65535"/>
+      <ControlPoint index="15" value="65535"/>
+      <ControlPoint index="16" value="65535"/>
+      <ControlPoint index="17" value="65535"/>
+      <ControlPoint index="18" value="65535"/>
+      <ControlPoint index="19" value="65535"/>
+      <ControlPoint index="20" value="65535"/>
+      <ControlPoint index="21" value="65535"/>
+      <ControlPoint index="22" value="65535"/>
+      <ControlPoint index="23" value="65535"/>
+      <ControlPoint index="24" value="65535"/>
+      <ControlPoint index="25" value="65535"/>
+      <ControlPoint index="26" value="65535"/>
+      <ControlPoint index="27" value="65535"/>
+      <ControlPoint index="28" value="65535"/>
+      <ControlPoint index="29" value="65535"/>
+      <ControlPoint index="30" value="65535"/>
+      <ControlPoint index="31" value="65535"/>
+      <BaselineValues>
+        <Lookup glyph=".notdef" value="0"/>
+        <Lookup glyph="zero" value="0"/>
+        <Lookup glyph="one" value="0"/>
+        <Lookup glyph="two" value="0"/>
+        <Lookup glyph="P" value="0"/>
+      </BaselineValues>
+    </Baseline>
+  </bsln>
+
+</ttFont>
diff --git a/Tests/subset/data/TestCID-Regular.ttx b/Tests/subset/data/TestCID-Regular.ttx
new file mode 100644
index 0000000..1ecfd39
--- /dev/null
+++ b/Tests/subset/data/TestCID-Regular.ttx
@@ -0,0 +1,389 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="2.5">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="cid00001"/>
+    <GlyphID id="2" name="cid00002"/>
+    <GlyphID id="3" name="cid00003"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.000"/>
+    <checkSumAdjustment value="0xbad6461a"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Mon Jun 15 05:17:40 2015"/>
+    <modified value="Mon Jun 15 05:17:40 2015"/>
+    <xMin value="-1000"/>
+    <yMin value="-1048"/>
+    <xMax value="2928"/>
+    <yMax value="1808"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-320"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="1000"/>
+    <minLeftSideBearing value="-1000"/>
+    <minRightSideBearing value="-181"/>
+    <xMaxExtent value="2928"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="4"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="4"/>
+  </maxp>
+
+  <OS_2>
+    <version value="3"/>
+    <xAvgCharWidth value="979"/>
+    <usWeightClass value="350"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="325"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="4"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 00000000"/>
+    <fsFirstCharIndex value="0"/>
+    <fsLastCharIndex value="65535"/>
+    <sTypoAscender value="880"/>
+    <sTypoDescender value="-120"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="1160"/>
+    <usWinDescent value="320"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="543"/>
+    <sCapHeight value="733"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContex value="6"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test CID
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="cid00001"/><!-- SPACE -->
+      <map code="0x21" name="cid00002"/><!-- EXCLAMATION MARK -->
+      <map code="0x22" name="cid00003"/><!-- QUOTATION MARK -->
+      <map code="0xa0" name="cid00001"/><!-- NO-BREAK SPACE -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="0" platEncID="4" format="12" reserved="0" length="424" language="0" nGroups="34">
+      <map code="0x20" name="cid00001"/><!-- SPACE -->
+      <map code="0x21" name="cid00002"/><!-- EXCLAMATION MARK -->
+      <map code="0x22" name="cid00003"/><!-- QUOTATION MARK -->
+      <map code="0xa0" name="cid00001"/><!-- NO-BREAK SPACE -->
+    </cmap_format_12>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="cid00001"/><!-- SPACE -->
+      <map code="0x21" name="cid00002"/><!-- EXCLAMATION MARK -->
+      <map code="0x22" name="cid00003"/><!-- QUOTATION MARK -->
+      <map code="0xa0" name="cid00001"/><!-- NO-BREAK SPACE -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="424" language="0" nGroups="34">
+      <map code="0x20" name="cid00001"/><!-- SPACE -->
+      <map code="0x21" name="cid00002"/><!-- EXCLAMATION MARK -->
+      <map code="0x22" name="cid00003"/><!-- QUOTATION MARK -->
+      <map code="0xa0" name="cid00001"/><!-- NO-BREAK SPACE -->
+    </cmap_format_12>
+  </cmap>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-125"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <CFF>
+    <CFFFont name="TestCID-Regular">
+      <ROS Registry="Adobe" Order="Identity" Supplement="0"/>
+      <FullName value="Test CID Regular"/>
+      <FamilyName value="Test CID"/>
+      <Weight value="Regular"/>
+      <isFixedPitch value="0"/>
+      <ItalicAngle value="0"/>
+      <UnderlinePosition value="-150"/>
+      <UnderlineThickness value="50"/>
+      <PaintType value="0"/>
+      <CharstringType value="2"/>
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FontBBox value="-1000 -1000 1000 1000"/>
+      <StrokeWidth value="0"/>
+      <CIDFontVersion value="1.000"/>
+      <CIDFontRevision value="0"/>
+      <CIDFontType value="0"/>
+      <CIDCount value="4"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <FDSelect format="3"/>
+      <FDArray>
+        <FontDict index="0">
+          <FontName value="TestCID-Regular-One"/>
+          <isFixedPitch value="0"/>
+          <ItalicAngle value="0"/>
+          <UnderlineThickness value="50"/>
+          <PaintType value="0"/>
+          <CharstringType value="2"/>
+          <FontMatrix value="0.001 0 0 0.001 0 0"/>
+          <FontBBox value="0 0 0 0"/>
+          <StrokeWidth value="0"/>
+          <Encoding name="StandardEncoding"/>
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="1"/>
+            <StdHW value="40"/>
+            <StdVW value="40"/>
+            <StemSnapH value="40 120"/>
+            <StemSnapV value="40 120"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="107"/>
+            <Subrs>
+              <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+            </Subrs>
+          </Private>
+        </FontDict>
+        <FontDict index="1">
+          <FontName value="TestCID-Regular-Two"/>
+          <isFixedPitch value="0"/>
+          <ItalicAngle value="0"/>
+          <UnderlineThickness value="50"/>
+          <PaintType value="0"/>
+          <CharstringType value="2"/>
+          <FontMatrix value="0.001 0 0 0.001 0 0"/>
+          <FontBBox value="0 0 0 0"/>
+          <StrokeWidth value="0"/>
+          <Encoding name="StandardEncoding"/>
+          <Private>
+            <BlueValues value="-13 0 540 554 732 745"/>
+            <OtherBlues value="-251 -232 -227 -227"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="1"/>
+            <StdHW value="58"/>
+            <StdVW value="71"/>
+            <StemSnapH value="58 73 83"/>
+            <StemSnapV value="71 81 96"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="0"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="602"/>
+            <nominalWidthX value="630"/>
+            <Subrs>
+              <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+              <CharString index="0">
+                129 216 -105 callsubr
+                -82 hlineto
+                2 -98 rlineto
+                return
+              </CharString>
+              <CharString index="1">
+                -106 callgsubr
+                67 return
+              </CharString>
+              <CharString index="2">
+                rmoveto
+                56 hlineto
+                11 433 2 98 rlineto
+                return
+              </CharString>
+              <CharString index="3">
+                -33 -28 -26 -40 vhcurveto
+                hintmask 01100000
+                -38 28 -26 33 vhcurveto
+                endchar
+              </CharString>
+              <CharString index="4">
+                -13 130 -109 -21 return
+              </CharString>
+              <CharString index="5">
+                493 277 hstem
+                91 -105 callgsubr
+                return
+              </CharString>
+            </Subrs>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef" fdSelectIndex="0">
+          -120 50 900 50 hstem
+          100 50 700 50 vstem
+          100 -120 rmoveto
+          800 1000 -800 hlineto
+          400 -459 rmoveto
+          -318 409 rlineto
+          636 hlineto
+          -286 -450 rmoveto
+          318 409 rlineto
+          -818 vlineto
+          -668 -41 rmoveto
+          318 409 318 -409 rlineto
+          -668 859 rmoveto
+          318 -409 -318 -409 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid00001" fdSelectIndex="1">
+          -407 endchar
+        </CharString>
+        <CharString name="cid00002" fdSelectIndex="1">
+          -316 -103 callsubr
+          -106 callsubr
+          hintmask 01010000
+          -107 callsubr
+          hintmask 01100000
+          39 -662 rmoveto
+          33 29 26 38 hvcurveto
+          hintmask 10100000
+          40 -29 26 -33 -104 callsubr
+        </CharString>
+        <CharString name="cid00003" fdSelectIndex="1">
+          -173 -102 callsubr
+          -104 callgsubr
+          86 vstem
+          108 493 rmoveto
+          52 hlineto
+          15 180 3 97 rlineto
+          -88 hlineto
+          2 -97 rlineto
+          203 -103 callgsubr
+          51 hlineto
+          17 180 2 97 rlineto
+          -88 hlineto
+          2 -97 rlineto
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+      <CharString index="0">
+        123 -95.5 return
+      </CharString>
+      <CharString index="1">
+        hstemhm
+        96 -107 callgsubr
+        return
+      </CharString>
+      <CharString index="2">
+        85.5 return
+      </CharString>
+      <CharString index="3">
+        101.5 return
+      </CharString>
+      <CharString index="4">
+        -180 rmoveto
+        return
+      </CharString>
+    </GlobalSubrs>
+  </CFF>
+
+  <VORG>
+    <majorVersion value="1"/>
+    <minorVersion value="0"/>
+    <defaultVertOriginY value="880"/>
+    <numVertOriginYMetrics value="0"/>
+  </VORG>
+
+  <hmtx>
+    <mtx name=".notdef" width="1000" lsb="100"/>
+    <mtx name="cid00001" width="223" lsb="0"/>
+    <mtx name="cid00002" width="314" lsb="96"/>
+    <mtx name="cid00003" width="457" lsb="90"/>
+  </hmtx>
+
+  <vhea>
+    <tableVersion value="0x00011000"/>
+    <ascent value="500"/>
+    <descent value="-500"/>
+    <lineGap value="0"/>
+    <advanceHeightMax value="3000"/>
+    <minTopSideBearing value="-1000"/>
+    <minBottomSideBearing value="-672"/>
+    <yMaxExtent value="2928"/>
+    <caretSlopeRise value="0"/>
+    <caretSlopeRun value="1"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <reserved4 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfVMetrics value="1"/>
+  </vhea>
+
+  <vmtx>
+    <mtx name=".notdef" height="1000" tsb="0"/>
+    <mtx name="cid00001" height="1000" tsb="880"/>
+    <mtx name="cid00002" height="1000" tsb="133"/>
+    <mtx name="cid00003" height="1000" tsb="110"/>
+  </vmtx>
+
+</ttFont>
diff --git a/Tests/subset/data/TestCLR-Regular.ttx b/Tests/subset/data/TestCLR-Regular.ttx
new file mode 100644
index 0000000..cb63553
--- /dev/null
+++ b/Tests/subset/data/TestCLR-Regular.ttx
@@ -0,0 +1,763 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name=".null"/>
+    <GlyphID id="2" name="nonmarkingreturn"/>
+    <GlyphID id="3" name="a"/>
+    <GlyphID id="4" name="b"/>
+    <GlyphID id="5" name="c"/>
+    <GlyphID id="6" name="smileface"/>
+    <GlyphID id="7" name="smileface.ffe08a"/>
+    <GlyphID id="8" name="smileface.e59b25"/>
+    <GlyphID id="9" name="smileface.84380d"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0xc1cca2c2"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00001011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Dec  1 06:38:41 2015"/>
+    <modified value="Tue Dec  1 06:38:41 2015"/>
+    <xMin value="33"/>
+    <yMin value="0"/>
+    <xMax value="900"/>
+    <yMax value="800"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="8"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="800"/>
+    <descent value="0"/>
+    <lineGap value="90"/>
+    <advanceWidthMax value="1000"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="0"/>
+    <xMaxExtent value="900"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="7"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="10"/>
+    <maxPoints value="50"/>
+    <maxContours value="5"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="2"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="1"/>
+    <maxFunctionDefs value="1"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="64"/>
+    <maxSizeOfInstructions value="46"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="553"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="700"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="140"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="700"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="480"/>
+    <yStrikeoutSize value="49"/>
+    <yStrikeoutPosition value="258"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="3"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 01000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="PfEd"/>
+    <fsSelection value="00000000 11000000"/>
+    <usFirstCharIndex value="97"/>
+    <usLastCharIndex value="9786"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="90"/>
+    <usWinAscent value="800"/>
+    <usWinDescent value="0"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="205"/>
+    <sCapHeight value="0"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="364" lsb="33"/>
+    <mtx name=".null" width="0" lsb="0"/>
+    <mtx name="a" width="405" lsb="100"/>
+    <mtx name="b" width="405" lsb="100"/>
+    <mtx name="c" width="405" lsb="100"/>
+    <mtx name="nonmarkingreturn" width="333" lsb="0"/>
+    <mtx name="smileface" width="1000" lsb="100"/>
+    <mtx name="smileface.84380d" width="1000" lsb="238"/>
+    <mtx name="smileface.e59b25" width="1000" lsb="100"/>
+    <mtx name="smileface.ffe08a" width="1000" lsb="100"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x62" name="b"/><!-- LATIN SMALL LETTER B -->
+      <map code="0x63" name="c"/><!-- LATIN SMALL LETTER C -->
+      <map code="0x263a" name="smileface"/><!-- WHITE SMILING FACE -->
+    </cmap_format_4>
+    <cmap_format_0 platformID="1" platEncID="0" language="0">
+      <map code="0x0" name=".null"/>
+      <map code="0x1" name=".notdef"/>
+      <map code="0x2" name=".notdef"/>
+      <map code="0x3" name=".notdef"/>
+      <map code="0x4" name=".notdef"/>
+      <map code="0x5" name=".notdef"/>
+      <map code="0x6" name=".notdef"/>
+      <map code="0x7" name=".notdef"/>
+      <map code="0x8" name=".null"/>
+      <map code="0x9" name="nonmarkingreturn"/>
+      <map code="0xa" name=".notdef"/>
+      <map code="0xb" name=".notdef"/>
+      <map code="0xc" name=".notdef"/>
+      <map code="0xd" name="nonmarkingreturn"/>
+      <map code="0xe" name=".notdef"/>
+      <map code="0xf" name=".notdef"/>
+      <map code="0x10" name=".notdef"/>
+      <map code="0x11" name=".notdef"/>
+      <map code="0x12" name=".notdef"/>
+      <map code="0x13" name=".notdef"/>
+      <map code="0x14" name=".notdef"/>
+      <map code="0x15" name=".notdef"/>
+      <map code="0x16" name=".notdef"/>
+      <map code="0x17" name=".notdef"/>
+      <map code="0x18" name=".notdef"/>
+      <map code="0x19" name=".notdef"/>
+      <map code="0x1a" name=".notdef"/>
+      <map code="0x1b" name=".notdef"/>
+      <map code="0x1c" name=".notdef"/>
+      <map code="0x1d" name=".null"/>
+      <map code="0x1e" name=".notdef"/>
+      <map code="0x1f" name=".notdef"/>
+      <map code="0x20" name=".notdef"/>
+      <map code="0x21" name=".notdef"/>
+      <map code="0x22" name=".notdef"/>
+      <map code="0x23" name=".notdef"/>
+      <map code="0x24" name=".notdef"/>
+      <map code="0x25" name=".notdef"/>
+      <map code="0x26" name=".notdef"/>
+      <map code="0x27" name=".notdef"/>
+      <map code="0x28" name=".notdef"/>
+      <map code="0x29" name=".notdef"/>
+      <map code="0x2a" name=".notdef"/>
+      <map code="0x2b" name=".notdef"/>
+      <map code="0x2c" name=".notdef"/>
+      <map code="0x2d" name=".notdef"/>
+      <map code="0x2e" name=".notdef"/>
+      <map code="0x2f" name=".notdef"/>
+      <map code="0x30" name=".notdef"/>
+      <map code="0x31" name=".notdef"/>
+      <map code="0x32" name=".notdef"/>
+      <map code="0x33" name=".notdef"/>
+      <map code="0x34" name=".notdef"/>
+      <map code="0x35" name=".notdef"/>
+      <map code="0x36" name=".notdef"/>
+      <map code="0x37" name=".notdef"/>
+      <map code="0x38" name=".notdef"/>
+      <map code="0x39" name=".notdef"/>
+      <map code="0x3a" name=".notdef"/>
+      <map code="0x3b" name=".notdef"/>
+      <map code="0x3c" name=".notdef"/>
+      <map code="0x3d" name=".notdef"/>
+      <map code="0x3e" name=".notdef"/>
+      <map code="0x3f" name=".notdef"/>
+      <map code="0x40" name=".notdef"/>
+      <map code="0x41" name=".notdef"/>
+      <map code="0x42" name=".notdef"/>
+      <map code="0x43" name=".notdef"/>
+      <map code="0x44" name=".notdef"/>
+      <map code="0x45" name=".notdef"/>
+      <map code="0x46" name=".notdef"/>
+      <map code="0x47" name=".notdef"/>
+      <map code="0x48" name=".notdef"/>
+      <map code="0x49" name=".notdef"/>
+      <map code="0x4a" name=".notdef"/>
+      <map code="0x4b" name=".notdef"/>
+      <map code="0x4c" name=".notdef"/>
+      <map code="0x4d" name=".notdef"/>
+      <map code="0x4e" name=".notdef"/>
+      <map code="0x4f" name=".notdef"/>
+      <map code="0x50" name=".notdef"/>
+      <map code="0x51" name=".notdef"/>
+      <map code="0x52" name=".notdef"/>
+      <map code="0x53" name=".notdef"/>
+      <map code="0x54" name=".notdef"/>
+      <map code="0x55" name=".notdef"/>
+      <map code="0x56" name=".notdef"/>
+      <map code="0x57" name=".notdef"/>
+      <map code="0x58" name=".notdef"/>
+      <map code="0x59" name=".notdef"/>
+      <map code="0x5a" name=".notdef"/>
+      <map code="0x5b" name=".notdef"/>
+      <map code="0x5c" name=".notdef"/>
+      <map code="0x5d" name=".notdef"/>
+      <map code="0x5e" name=".notdef"/>
+      <map code="0x5f" name=".notdef"/>
+      <map code="0x60" name=".notdef"/>
+      <map code="0x61" name="a"/>
+      <map code="0x62" name="b"/>
+      <map code="0x63" name="c"/>
+      <map code="0x64" name=".notdef"/>
+      <map code="0x65" name=".notdef"/>
+      <map code="0x66" name=".notdef"/>
+      <map code="0x67" name=".notdef"/>
+      <map code="0x68" name=".notdef"/>
+      <map code="0x69" name=".notdef"/>
+      <map code="0x6a" name=".notdef"/>
+      <map code="0x6b" name=".notdef"/>
+      <map code="0x6c" name=".notdef"/>
+      <map code="0x6d" name=".notdef"/>
+      <map code="0x6e" name=".notdef"/>
+      <map code="0x6f" name=".notdef"/>
+      <map code="0x70" name=".notdef"/>
+      <map code="0x71" name=".notdef"/>
+      <map code="0x72" name=".notdef"/>
+      <map code="0x73" name=".notdef"/>
+      <map code="0x74" name=".notdef"/>
+      <map code="0x75" name=".notdef"/>
+      <map code="0x76" name=".notdef"/>
+      <map code="0x77" name=".notdef"/>
+      <map code="0x78" name=".notdef"/>
+      <map code="0x79" name=".notdef"/>
+      <map code="0x7a" name=".notdef"/>
+      <map code="0x7b" name=".notdef"/>
+      <map code="0x7c" name=".notdef"/>
+      <map code="0x7d" name=".notdef"/>
+      <map code="0x7e" name=".notdef"/>
+      <map code="0x7f" name=".notdef"/>
+      <map code="0x80" name=".notdef"/>
+      <map code="0x81" name=".notdef"/>
+      <map code="0x82" name=".notdef"/>
+      <map code="0x83" name=".notdef"/>
+      <map code="0x84" name=".notdef"/>
+      <map code="0x85" name=".notdef"/>
+      <map code="0x86" name=".notdef"/>
+      <map code="0x87" name=".notdef"/>
+      <map code="0x88" name=".notdef"/>
+      <map code="0x89" name=".notdef"/>
+      <map code="0x8a" name=".notdef"/>
+      <map code="0x8b" name=".notdef"/>
+      <map code="0x8c" name=".notdef"/>
+      <map code="0x8d" name=".notdef"/>
+      <map code="0x8e" name=".notdef"/>
+      <map code="0x8f" name=".notdef"/>
+      <map code="0x90" name=".notdef"/>
+      <map code="0x91" name=".notdef"/>
+      <map code="0x92" name=".notdef"/>
+      <map code="0x93" name=".notdef"/>
+      <map code="0x94" name=".notdef"/>
+      <map code="0x95" name=".notdef"/>
+      <map code="0x96" name=".notdef"/>
+      <map code="0x97" name=".notdef"/>
+      <map code="0x98" name=".notdef"/>
+      <map code="0x99" name=".notdef"/>
+      <map code="0x9a" name=".notdef"/>
+      <map code="0x9b" name=".notdef"/>
+      <map code="0x9c" name=".notdef"/>
+      <map code="0x9d" name=".notdef"/>
+      <map code="0x9e" name=".notdef"/>
+      <map code="0x9f" name=".notdef"/>
+      <map code="0xa0" name=".notdef"/>
+      <map code="0xa1" name=".notdef"/>
+      <map code="0xa2" name=".notdef"/>
+      <map code="0xa3" name=".notdef"/>
+      <map code="0xa4" name=".notdef"/>
+      <map code="0xa5" name=".notdef"/>
+      <map code="0xa6" name=".notdef"/>
+      <map code="0xa7" name=".notdef"/>
+      <map code="0xa8" name=".notdef"/>
+      <map code="0xa9" name=".notdef"/>
+      <map code="0xaa" name=".notdef"/>
+      <map code="0xab" name=".notdef"/>
+      <map code="0xac" name=".notdef"/>
+      <map code="0xad" name=".notdef"/>
+      <map code="0xae" name=".notdef"/>
+      <map code="0xaf" name=".notdef"/>
+      <map code="0xb0" name=".notdef"/>
+      <map code="0xb1" name=".notdef"/>
+      <map code="0xb2" name=".notdef"/>
+      <map code="0xb3" name=".notdef"/>
+      <map code="0xb4" name=".notdef"/>
+      <map code="0xb5" name=".notdef"/>
+      <map code="0xb6" name=".notdef"/>
+      <map code="0xb7" name=".notdef"/>
+      <map code="0xb8" name=".notdef"/>
+      <map code="0xb9" name=".notdef"/>
+      <map code="0xba" name=".notdef"/>
+      <map code="0xbb" name=".notdef"/>
+      <map code="0xbc" name=".notdef"/>
+      <map code="0xbd" name=".notdef"/>
+      <map code="0xbe" name=".notdef"/>
+      <map code="0xbf" name=".notdef"/>
+      <map code="0xc0" name=".notdef"/>
+      <map code="0xc1" name=".notdef"/>
+      <map code="0xc2" name=".notdef"/>
+      <map code="0xc3" name=".notdef"/>
+      <map code="0xc4" name=".notdef"/>
+      <map code="0xc5" name=".notdef"/>
+      <map code="0xc6" name=".notdef"/>
+      <map code="0xc7" name=".notdef"/>
+      <map code="0xc8" name=".notdef"/>
+      <map code="0xc9" name=".notdef"/>
+      <map code="0xca" name=".notdef"/>
+      <map code="0xcb" name=".notdef"/>
+      <map code="0xcc" name=".notdef"/>
+      <map code="0xcd" name=".notdef"/>
+      <map code="0xce" name=".notdef"/>
+      <map code="0xcf" name=".notdef"/>
+      <map code="0xd0" name=".notdef"/>
+      <map code="0xd1" name=".notdef"/>
+      <map code="0xd2" name=".notdef"/>
+      <map code="0xd3" name=".notdef"/>
+      <map code="0xd4" name=".notdef"/>
+      <map code="0xd5" name=".notdef"/>
+      <map code="0xd6" name=".notdef"/>
+      <map code="0xd7" name=".notdef"/>
+      <map code="0xd8" name=".notdef"/>
+      <map code="0xd9" name=".notdef"/>
+      <map code="0xda" name=".notdef"/>
+      <map code="0xdb" name=".notdef"/>
+      <map code="0xdc" name=".notdef"/>
+      <map code="0xdd" name=".notdef"/>
+      <map code="0xde" name=".notdef"/>
+      <map code="0xdf" name=".notdef"/>
+      <map code="0xe0" name=".notdef"/>
+      <map code="0xe1" name=".notdef"/>
+      <map code="0xe2" name=".notdef"/>
+      <map code="0xe3" name=".notdef"/>
+      <map code="0xe4" name=".notdef"/>
+      <map code="0xe5" name=".notdef"/>
+      <map code="0xe6" name=".notdef"/>
+      <map code="0xe7" name=".notdef"/>
+      <map code="0xe8" name=".notdef"/>
+      <map code="0xe9" name=".notdef"/>
+      <map code="0xea" name=".notdef"/>
+      <map code="0xeb" name=".notdef"/>
+      <map code="0xec" name=".notdef"/>
+      <map code="0xed" name=".notdef"/>
+      <map code="0xee" name=".notdef"/>
+      <map code="0xef" name=".notdef"/>
+      <map code="0xf0" name=".notdef"/>
+      <map code="0xf1" name=".notdef"/>
+      <map code="0xf2" name=".notdef"/>
+      <map code="0xf3" name=".notdef"/>
+      <map code="0xf4" name=".notdef"/>
+      <map code="0xf5" name=".notdef"/>
+      <map code="0xf6" name=".notdef"/>
+      <map code="0xf7" name=".notdef"/>
+      <map code="0xf8" name=".notdef"/>
+      <map code="0xf9" name=".notdef"/>
+      <map code="0xfa" name=".notdef"/>
+      <map code="0xfb" name=".notdef"/>
+      <map code="0xfc" name=".notdef"/>
+      <map code="0xfd" name=".notdef"/>
+      <map code="0xfe" name=".notdef"/>
+      <map code="0xff" name=".notdef"/>
+    </cmap_format_0>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x62" name="b"/><!-- LATIN SMALL LETTER B -->
+      <map code="0x63" name="c"/><!-- LATIN SMALL LETTER C -->
+      <map code="0x263a" name="smileface"/><!-- WHITE SMILING FACE -->
+    </cmap_format_4>
+  </cmap>
+
+  <cvt>
+    <cv index="0" value="33"/>
+    <cv index="1" value="633"/>
+  </cvt>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="33" yMin="0" xMax="298" yMax="666">
+      <contour>
+        <pt x="33" y="0" on="1"/>
+        <pt x="33" y="666" on="1"/>
+        <pt x="298" y="666" on="1"/>
+        <pt x="298" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="66" y="33" on="1"/>
+        <pt x="265" y="33" on="1"/>
+        <pt x="265" y="633" on="1"/>
+        <pt x="66" y="633" on="1"/>
+      </contour>
+      <instructions><assembly>
+          PUSH[ ]	/* 2 values pushed */
+          1 0
+          MDAP[1]	/* MoveDirectAbsPt */
+          ALIGNRP[ ]	/* AlignRelativePt */
+          PUSH[ ]	/* 3 values pushed */
+          7 4 0
+          MIRP[01101]	/* MoveIndirectRelPt */
+          SHP[0]	/* ShiftPointByLastPoint */
+          PUSH[ ]	/* 2 values pushed */
+          6 5
+          MDRP[11100]	/* MoveDirectRelPt */
+          ALIGNRP[ ]	/* AlignRelativePt */
+          PUSH[ ]	/* 3 values pushed */
+          3 2 0
+          MIRP[01101]	/* MoveIndirectRelPt */
+          SHP[0]	/* ShiftPointByLastPoint */
+          SVTCA[0]	/* SetFPVectorToAxis */
+          PUSH[ ]	/* 2 values pushed */
+          3 0
+          MDAP[1]	/* MoveDirectAbsPt */
+          ALIGNRP[ ]	/* AlignRelativePt */
+          PUSH[ ]	/* 3 values pushed */
+          5 4 0
+          MIRP[01101]	/* MoveIndirectRelPt */
+          SHP[0]	/* ShiftPointByLastPoint */
+          PUSH[ ]	/* 3 values pushed */
+          7 6 1
+          MIRP[11100]	/* MoveIndirectRelPt */
+          ALIGNRP[ ]	/* AlignRelativePt */
+          PUSH[ ]	/* 3 values pushed */
+          1 2 0
+          MIRP[01101]	/* MoveIndirectRelPt */
+          SHP[0]	/* ShiftPointByLastPoint */
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name=".null"/><!-- contains no outline data -->
+
+    <TTGlyph name="a" xMin="100" yMin="0" xMax="305" yMax="205">
+      <contour>
+        <pt x="100" y="205" on="1"/>
+        <pt x="305" y="205" on="1"/>
+        <pt x="305" y="0" on="1"/>
+        <pt x="100" y="0" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="b" xMin="100" yMin="0" xMax="305" yMax="205">
+      <contour>
+        <pt x="100" y="205" on="1"/>
+        <pt x="305" y="205" on="1"/>
+        <pt x="305" y="0" on="1"/>
+        <pt x="100" y="0" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="c" xMin="100" yMin="0" xMax="305" yMax="205">
+      <contour>
+        <pt x="100" y="205" on="1"/>
+        <pt x="305" y="205" on="1"/>
+        <pt x="305" y="0" on="1"/>
+        <pt x="100" y="0" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="nonmarkingreturn"/><!-- contains no outline data -->
+
+    <TTGlyph name="smileface" xMin="100" yMin="0" xMax="900" yMax="800">
+      <contour>
+        <pt x="733" y="348" on="0"/>
+        <pt x="761" y="336" on="0"/>
+        <pt x="755" y="322" on="1"/>
+        <pt x="724" y="256" on="0"/>
+        <pt x="582" y="183" on="0"/>
+        <pt x="500" y="182" on="1"/>
+        <pt x="417" y="181" on="0"/>
+        <pt x="279" y="251" on="0"/>
+        <pt x="245" y="321" on="1"/>
+        <pt x="238" y="333" on="0"/>
+        <pt x="251" y="341" on="1"/>
+        <pt x="263" y="348" on="0"/>
+        <pt x="271" y="335" on="1"/>
+        <pt x="301" y="275" on="0"/>
+        <pt x="425" y="211" on="0"/>
+        <pt x="575" y="213" on="0"/>
+        <pt x="701" y="277" on="0"/>
+        <pt x="727" y="334" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="234" on="0"/>
+        <pt x="100" y="566" on="0"/>
+        <pt x="334" y="800" on="0"/>
+        <pt x="666" y="800" on="0"/>
+        <pt x="900" y="566" on="0"/>
+        <pt x="900" y="234" on="0"/>
+        <pt x="666" y="0" on="0"/>
+        <pt x="334" y="0" on="0"/>
+      </contour>
+      <contour>
+        <pt x="130" y="553" on="0"/>
+        <pt x="130" y="247" on="0"/>
+        <pt x="347" y="30" on="0"/>
+        <pt x="653" y="30" on="0"/>
+        <pt x="870" y="247" on="0"/>
+        <pt x="870" y="553" on="0"/>
+        <pt x="653" y="770" on="0"/>
+        <pt x="347" y="770" on="0"/>
+      </contour>
+      <contour>
+        <pt x="623" y="482" on="0"/>
+        <pt x="623" y="554" on="0"/>
+        <pt x="646" y="606" on="0"/>
+        <pt x="680" y="606" on="0"/>
+        <pt x="703" y="554" on="0"/>
+        <pt x="703" y="482" on="0"/>
+        <pt x="680" y="430" on="0"/>
+        <pt x="646" y="430" on="0"/>
+      </contour>
+      <contour>
+        <pt x="297" y="482" on="0"/>
+        <pt x="297" y="554" on="0"/>
+        <pt x="320" y="606" on="0"/>
+        <pt x="354" y="606" on="0"/>
+        <pt x="377" y="554" on="0"/>
+        <pt x="377" y="482" on="0"/>
+        <pt x="354" y="430" on="0"/>
+        <pt x="320" y="430" on="0"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="smileface.84380d" xMin="238" yMin="181" xMax="761" yMax="606">
+      <contour>
+        <pt x="733" y="348" on="0"/>
+        <pt x="761" y="336" on="0"/>
+        <pt x="755" y="322" on="1"/>
+        <pt x="724" y="256" on="0"/>
+        <pt x="582" y="183" on="0"/>
+        <pt x="500" y="182" on="1"/>
+        <pt x="417" y="181" on="0"/>
+        <pt x="279" y="251" on="0"/>
+        <pt x="245" y="321" on="1"/>
+        <pt x="238" y="333" on="0"/>
+        <pt x="251" y="341" on="1"/>
+        <pt x="263" y="348" on="0"/>
+        <pt x="271" y="335" on="1"/>
+        <pt x="301" y="275" on="0"/>
+        <pt x="425" y="211" on="0"/>
+        <pt x="575" y="213" on="0"/>
+        <pt x="701" y="277" on="0"/>
+        <pt x="727" y="334" on="1"/>
+      </contour>
+      <contour>
+        <pt x="623" y="481" on="0"/>
+        <pt x="623" y="555" on="0"/>
+        <pt x="646" y="606" on="0"/>
+        <pt x="680" y="606" on="0"/>
+        <pt x="703" y="555" on="0"/>
+        <pt x="703" y="481" on="0"/>
+        <pt x="680" y="430" on="0"/>
+        <pt x="646" y="430" on="0"/>
+      </contour>
+      <contour>
+        <pt x="297" y="481" on="0"/>
+        <pt x="297" y="555" on="0"/>
+        <pt x="320" y="606" on="0"/>
+        <pt x="354" y="606" on="0"/>
+        <pt x="377" y="555" on="0"/>
+        <pt x="377" y="481" on="0"/>
+        <pt x="354" y="430" on="0"/>
+        <pt x="320" y="430" on="0"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="smileface.e59b25" xMin="100" yMin="0" xMax="900" yMax="800">
+      <contour>
+        <pt x="100" y="234" on="0"/>
+        <pt x="100" y="566" on="0"/>
+        <pt x="334" y="800" on="0"/>
+        <pt x="666" y="800" on="0"/>
+        <pt x="900" y="566" on="0"/>
+        <pt x="900" y="234" on="0"/>
+        <pt x="666" y="0" on="0"/>
+        <pt x="334" y="0" on="0"/>
+      </contour>
+      <contour>
+        <pt x="130" y="553" on="0"/>
+        <pt x="130" y="247" on="0"/>
+        <pt x="347" y="30" on="0"/>
+        <pt x="653" y="30" on="0"/>
+        <pt x="870" y="247" on="0"/>
+        <pt x="870" y="553" on="0"/>
+        <pt x="653" y="770" on="0"/>
+        <pt x="347" y="770" on="0"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="smileface.ffe08a" xMin="100" yMin="0" xMax="900" yMax="800">
+      <contour>
+        <pt x="100" y="234" on="0"/>
+        <pt x="100" y="566" on="0"/>
+        <pt x="334" y="800" on="0"/>
+        <pt x="666" y="800" on="0"/>
+        <pt x="900" y="566" on="0"/>
+        <pt x="900" y="234" on="0"/>
+        <pt x="666" y="0" on="0"/>
+        <pt x="334" y="0" on="0"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Created by Khaled Hosny with Sorts Mill Tools 2.1.0_alpha1 &lt;http://bitbucket.org/sortsmill/sortsmill-tools&gt;
+    </namerecord>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestCLR
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      FontForge : TestCLR : 1-12-2015
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestCLR
+    </namerecord>
+    <namerecord nameID="5" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Version 001.000
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestCLR-Regular
+    </namerecord>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Created by Khaled Hosny with Sorts Mill Tools 2.1.0_alpha1 &lt;http://bitbucket.org/sortsmill/sortsmill-tools&gt;
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      TestCLR
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      FontForge : TestCLR : 1-12-2015
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      TestCLR
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 001.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestCLR-Regular
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-125"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name="smileface"/>
+      <psName name="smileface.ffe08a"/>
+      <psName name="smileface.e59b25"/>
+      <psName name="smileface.84380d"/>
+    </extraNames>
+  </post>
+
+  <COLR>
+    <version value="0"/>
+    <ColorGlyph name="smileface">
+      <layer colorID="0" name="smileface.ffe08a"/>
+      <layer colorID="1" name="smileface.e59b25"/>
+      <layer colorID="2" name="smileface.84380d"/>
+    </ColorGlyph>
+  </COLR>
+
+  <CPAL>
+    <version value="0"/>
+    <numPaletteEntries value="3"/>
+    <palette index="0">
+      <color index="0" value="#FFE08AFF"/>
+      <color index="1" value="#E59B25FF"/>
+      <color index="2" value="#84380DFF"/>
+    </palette>
+  </CPAL>
+
+</ttFont>
diff --git a/Tests/subset/data/TestGVAR.ttx b/Tests/subset/data/TestGVAR.ttx
new file mode 100644
index 0000000..b14466d
--- /dev/null
+++ b/Tests/subset/data/TestGVAR.ttx
@@ -0,0 +1,655 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="space"/>
+    <GlyphID id="2" name="zero"/>
+    <GlyphID id="3" name="equal"/>
+    <GlyphID id="4" name="minus"/>
+    <GlyphID id="5" name="plus"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0xf1c8c54f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Mar 31 07:56:22 2016"/>
+    <modified value="Thu Mar 31 11:16:24 2016"/>
+    <xMin value="40"/>
+    <yMin value="-10"/>
+    <xMax value="620"/>
+    <yMax value="710"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="700"/>
+    <minLeftSideBearing value="40"/>
+    <minRightSideBearing value="40"/>
+    <xMaxExtent value="620"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="4"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="32"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="1"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="606"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="8722"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="700" lsb="80"/>
+    <mtx name="equal" width="660" lsb="80"/>
+    <mtx name="minus" width="660" lsb="80"/>
+    <mtx name="plus" width="660" lsb="80"/>
+    <mtx name="space" width="700" lsb="0"/>
+    <mtx name="zero" width="625" lsb="40"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x2b" name="plus"/><!-- PLUS SIGN -->
+      <map code="0x30" name="zero"/><!-- DIGIT ZERO -->
+      <map code="0x3d" name="equal"/><!-- EQUALS SIGN -->
+      <map code="0x2212" name="minus"/><!-- MINUS SIGN -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="80" yMin="0" xMax="620" yMax="700">
+      <contour>
+        <pt x="620" y="700" on="1"/>
+        <pt x="620" y="0" on="1"/>
+        <pt x="80" y="0" on="1"/>
+        <pt x="80" y="700" on="1"/>
+      </contour>
+      <contour>
+        <pt x="160" y="80" on="1"/>
+        <pt x="540" y="80" on="1"/>
+        <pt x="540" y="620" on="1"/>
+        <pt x="160" y="620" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="equal" xMin="80" yMin="230" xMax="580" yMax="490">
+      <contour>
+        <pt x="580" y="490" on="1"/>
+        <pt x="580" y="410" on="1"/>
+        <pt x="80" y="410" on="1"/>
+        <pt x="80" y="490" on="1"/>
+      </contour>
+      <contour>
+        <pt x="580" y="310" on="1"/>
+        <pt x="580" y="230" on="1"/>
+        <pt x="80" y="230" on="1"/>
+        <pt x="80" y="310" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="minus" xMin="80" yMin="310" xMax="580" yMax="390">
+      <contour>
+        <pt x="580" y="390" on="1"/>
+        <pt x="580" y="310" on="1"/>
+        <pt x="80" y="310" on="1"/>
+        <pt x="80" y="390" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="plus" xMin="80" yMin="100" xMax="580" yMax="600">
+      <contour>
+        <pt x="580" y="310" on="1"/>
+        <pt x="370" y="310" on="1"/>
+        <pt x="370" y="100" on="1"/>
+        <pt x="290" y="100" on="1"/>
+        <pt x="290" y="310" on="1"/>
+        <pt x="80" y="310" on="1"/>
+        <pt x="80" y="390" on="1"/>
+        <pt x="290" y="390" on="1"/>
+        <pt x="290" y="600" on="1"/>
+        <pt x="370" y="600" on="1"/>
+        <pt x="370" y="390" on="1"/>
+        <pt x="580" y="390" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+    <TTGlyph name="zero" xMin="40" yMin="-10" xMax="585" yMax="710">
+      <contour>
+        <pt x="237" y="-10" on="0"/>
+        <pt x="113" y="78" on="0"/>
+        <pt x="40" y="241" on="0"/>
+        <pt x="40" y="350" on="1"/>
+        <pt x="40" y="459" on="0"/>
+        <pt x="113" y="622" on="0"/>
+        <pt x="237" y="710" on="0"/>
+        <pt x="313" y="710" on="1"/>
+        <pt x="388" y="710" on="0"/>
+        <pt x="513" y="622" on="0"/>
+        <pt x="585" y="459" on="0"/>
+        <pt x="585" y="350" on="1"/>
+        <pt x="585" y="241" on="0"/>
+        <pt x="513" y="78" on="0"/>
+        <pt x="388" y="-10" on="0"/>
+        <pt x="313" y="-10" on="1"/>
+      </contour>
+      <contour>
+        <pt x="366" y="74" on="0"/>
+        <pt x="454" y="144" on="0"/>
+        <pt x="505" y="270" on="0"/>
+        <pt x="505" y="350" on="1"/>
+        <pt x="505" y="430" on="0"/>
+        <pt x="454" y="556" on="0"/>
+        <pt x="366" y="626" on="0"/>
+        <pt x="313" y="626" on="1"/>
+        <pt x="260" y="626" on="0"/>
+        <pt x="171" y="556" on="0"/>
+        <pt x="120" y="430" on="0"/>
+        <pt x="120" y="350" on="1"/>
+        <pt x="120" y="270" on="0"/>
+        <pt x="171" y="144" on="0"/>
+        <pt x="260" y="74" on="0"/>
+        <pt x="313" y="74" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestGVAR
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      1.000;UKWN;TestGVAR-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestGVAR
+    </namerecord>
+    <namerecord nameID="5" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestGVAR-Regular
+    </namerecord>
+    <namerecord nameID="9" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Sascha Brawer
+    </namerecord>
+    <namerecord nameID="257" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Weight
+    </namerecord>
+    <namerecord nameID="258" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Thin
+    </namerecord>
+    <namerecord nameID="259" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Light
+    </namerecord>
+    <namerecord nameID="260" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="261" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Bold
+    </namerecord>
+    <namerecord nameID="262" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Black
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      TestGVAR
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.000;UKWN;TestGVAR-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      TestGVAR-Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestGVAR-Regular
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Sascha Brawer
+    </namerecord>
+    <namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </namerecord>
+    <namerecord nameID="258" platformID="3" platEncID="1" langID="0x409">
+      Thin
+    </namerecord>
+    <namerecord nameID="259" platformID="3" platEncID="1" langID="0x409">
+      Light
+    </namerecord>
+    <namerecord nameID="260" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="261" platformID="3" platEncID="1" langID="0x409">
+      Bold
+    </namerecord>
+    <namerecord nameID="262" platformID="3" platEncID="1" langID="0x409">
+      Black
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+    </extraNames>
+  </post>
+
+  <gasp>
+    <gaspRange rangeMaxPPEM="7" rangeGaspBehavior="10"/>
+    <gaspRange rangeMaxPPEM="19" rangeGaspBehavior="7"/>
+    <gaspRange rangeMaxPPEM="65535" rangeGaspBehavior="15"/>
+  </gasp>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+  </GSUB>
+
+  <avar>
+    <segment axis="wght">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="0.3" to="0.5"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+  </avar>
+
+  <fvar>
+
+    <!-- Weight -->
+    <Axis>
+      <AxisTag>wght</AxisTag>
+      <MinValue>100.0</MinValue>
+      <DefaultValue>400.0</DefaultValue>
+      <MaxValue>900.0</MaxValue>
+      <AxisNameID>257</AxisNameID>
+    </Axis>
+
+    <!-- Thin -->
+    <NamedInstance subfamilyNameID="258">
+      <coord axis="wght" value="100.0"/>
+    </NamedInstance>
+
+    <!-- Light -->
+    <NamedInstance subfamilyNameID="259">
+      <coord axis="wght" value="300.0"/>
+    </NamedInstance>
+
+    <!-- Regular -->
+    <NamedInstance subfamilyNameID="260">
+      <coord axis="wght" value="400.0"/>
+    </NamedInstance>
+
+    <!-- Bold -->
+    <NamedInstance subfamilyNameID="261">
+      <coord axis="wght" value="700.0"/>
+    </NamedInstance>
+
+    <!-- Black -->
+    <NamedInstance subfamilyNameID="262">
+      <coord axis="wght" value="900.0"/>
+    </NamedInstance>
+  </fvar>
+
+  <gvar>
+    <version value="1"/>
+    <reserved value="0"/>
+    <glyphVariations glyph=".notdef">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="0" x="-20" y="0"/>
+        <delta pt="1" x="-20" y="0"/>
+        <delta pt="2" x="-20" y="0"/>
+        <delta pt="3" x="-20" y="0"/>
+        <delta pt="4" x="-80" y="-60"/>
+        <delta pt="5" x="40" y="-60"/>
+        <delta pt="6" x="40" y="60"/>
+        <delta pt="7" x="-80" y="60"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="-40" y="0"/>
+        <delta pt="10" x="0" y="0"/>
+        <delta pt="11" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="20" y="0"/>
+        <delta pt="1" x="20" y="0"/>
+        <delta pt="2" x="20" y="0"/>
+        <delta pt="3" x="20" y="0"/>
+        <delta pt="4" x="80" y="60"/>
+        <delta pt="5" x="-40" y="60"/>
+        <delta pt="6" x="-40" y="-60"/>
+        <delta pt="7" x="80" y="-60"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="40" y="0"/>
+        <delta pt="10" x="0" y="0"/>
+        <delta pt="11" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="space">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="-100" y="0"/>
+        <delta pt="2" x="0" y="0"/>
+        <delta pt="3" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="100" y="0"/>
+        <delta pt="2" x="0" y="0"/>
+        <delta pt="3" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="zero">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="0" x="-10" y="0"/>
+        <delta pt="1" x="-10" y="0"/>
+        <delta pt="2" x="-10" y="0"/>
+        <delta pt="3" x="-10" y="0"/>
+        <delta pt="4" x="-10" y="0"/>
+        <delta pt="5" x="-10" y="0"/>
+        <delta pt="6" x="-10" y="0"/>
+        <delta pt="7" x="-10" y="0"/>
+        <delta pt="8" x="-10" y="0"/>
+        <delta pt="9" x="-10" y="0"/>
+        <delta pt="10" x="-10" y="0"/>
+        <delta pt="11" x="-10" y="0"/>
+        <delta pt="12" x="-10" y="0"/>
+        <delta pt="13" x="-10" y="0"/>
+        <delta pt="14" x="-10" y="0"/>
+        <delta pt="15" x="-10" y="0"/>
+        <delta pt="16" x="7" y="-64"/>
+        <delta pt="17" x="34" y="-53"/>
+        <delta pt="18" x="50" y="-25"/>
+        <delta pt="19" x="50" y="0"/>
+        <delta pt="20" x="50" y="25"/>
+        <delta pt="21" x="34" y="53"/>
+        <delta pt="22" x="7" y="64"/>
+        <delta pt="23" x="-10" y="64"/>
+        <delta pt="24" x="-28" y="64"/>
+        <delta pt="25" x="-54" y="53"/>
+        <delta pt="26" x="-70" y="25"/>
+        <delta pt="27" x="-70" y="0"/>
+        <delta pt="28" x="-70" y="-25"/>
+        <delta pt="29" x="-54" y="-53"/>
+        <delta pt="30" x="-28" y="-64"/>
+        <delta pt="31" x="-10" y="-64"/>
+        <delta pt="32" x="0" y="0"/>
+        <delta pt="33" x="-20" y="0"/>
+        <delta pt="34" x="0" y="0"/>
+        <delta pt="35" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="10" y="0"/>
+        <delta pt="1" x="10" y="0"/>
+        <delta pt="2" x="10" y="0"/>
+        <delta pt="3" x="10" y="0"/>
+        <delta pt="4" x="10" y="0"/>
+        <delta pt="5" x="10" y="0"/>
+        <delta pt="6" x="10" y="0"/>
+        <delta pt="7" x="10" y="0"/>
+        <delta pt="8" x="10" y="0"/>
+        <delta pt="9" x="10" y="0"/>
+        <delta pt="10" x="10" y="0"/>
+        <delta pt="11" x="10" y="0"/>
+        <delta pt="12" x="10" y="0"/>
+        <delta pt="13" x="10" y="0"/>
+        <delta pt="14" x="10" y="0"/>
+        <delta pt="15" x="10" y="0"/>
+        <delta pt="16" x="-6" y="55"/>
+        <delta pt="17" x="-34" y="37"/>
+        <delta pt="18" x="-50" y="11"/>
+        <delta pt="19" x="-50" y="0"/>
+        <delta pt="20" x="-50" y="-11"/>
+        <delta pt="21" x="-34" y="-37"/>
+        <delta pt="22" x="-6" y="-55"/>
+        <delta pt="23" x="10" y="-55"/>
+        <delta pt="24" x="25" y="-55"/>
+        <delta pt="25" x="54" y="-37"/>
+        <delta pt="26" x="70" y="-11"/>
+        <delta pt="27" x="70" y="0"/>
+        <delta pt="28" x="70" y="11"/>
+        <delta pt="29" x="54" y="37"/>
+        <delta pt="30" x="25" y="55"/>
+        <delta pt="31" x="10" y="55"/>
+        <delta pt="32" x="0" y="0"/>
+        <delta pt="33" x="20" y="0"/>
+        <delta pt="34" x="0" y="0"/>
+        <delta pt="35" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="equal">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="0" x="-20" y="-60"/>
+        <delta pt="1" x="-20" y="0"/>
+        <delta pt="2" x="-20" y="0"/>
+        <delta pt="3" x="-20" y="-60"/>
+        <delta pt="4" x="-20" y="-30"/>
+        <delta pt="5" x="-20" y="30"/>
+        <delta pt="6" x="-20" y="30"/>
+        <delta pt="7" x="-20" y="-30"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="-40" y="0"/>
+        <delta pt="10" x="0" y="-60"/>
+        <delta pt="11" x="0" y="-30"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="20" y="10"/>
+        <delta pt="1" x="20" y="-30"/>
+        <delta pt="2" x="20" y="-30"/>
+        <delta pt="3" x="20" y="10"/>
+        <delta pt="4" x="20" y="10"/>
+        <delta pt="5" x="20" y="-30"/>
+        <delta pt="6" x="20" y="-30"/>
+        <delta pt="7" x="20" y="10"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="40" y="0"/>
+        <delta pt="10" x="0" y="10"/>
+        <delta pt="11" x="0" y="30"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="minus">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="0" x="-20" y="-30"/>
+        <delta pt="1" x="-20" y="30"/>
+        <delta pt="2" x="-20" y="30"/>
+        <delta pt="3" x="-20" y="-30"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="-40" y="0"/>
+        <delta pt="6" x="0" y="-30"/>
+        <delta pt="7" x="0" y="-30"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="20" y="30"/>
+        <delta pt="1" x="20" y="-30"/>
+        <delta pt="2" x="20" y="-30"/>
+        <delta pt="3" x="20" y="30"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="40" y="0"/>
+        <delta pt="6" x="0" y="30"/>
+        <delta pt="7" x="0" y="30"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="plus">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="0" x="-20" y="30"/>
+        <delta pt="1" x="-50" y="30"/>
+        <delta pt="2" x="-50" y="0"/>
+        <delta pt="3" x="10" y="0"/>
+        <delta pt="4" x="10" y="30"/>
+        <delta pt="5" x="-20" y="30"/>
+        <delta pt="6" x="-20" y="-30"/>
+        <delta pt="7" x="10" y="-30"/>
+        <delta pt="8" x="10" y="0"/>
+        <delta pt="9" x="-50" y="0"/>
+        <delta pt="10" x="-50" y="-30"/>
+        <delta pt="11" x="-20" y="-30"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="-40" y="0"/>
+        <delta pt="14" x="0" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="20" y="-30"/>
+        <delta pt="1" x="50" y="-30"/>
+        <delta pt="2" x="50" y="0"/>
+        <delta pt="3" x="-10" y="0"/>
+        <delta pt="4" x="-10" y="-30"/>
+        <delta pt="5" x="20" y="-30"/>
+        <delta pt="6" x="20" y="30"/>
+        <delta pt="7" x="-10" y="30"/>
+        <delta pt="8" x="-10" y="0"/>
+        <delta pt="9" x="50" y="0"/>
+        <delta pt="10" x="50" y="30"/>
+        <delta pt="11" x="20" y="30"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="40" y="0"/>
+        <delta pt="14" x="0" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+  </gvar>
+
+</ttFont>
diff --git a/Tests/subset/data/TestLCAR-0.ttx b/Tests/subset/data/TestLCAR-0.ttx
new file mode 100644
index 0000000..b9d6fc0
--- /dev/null
+++ b/Tests/subset/data/TestLCAR-0.ttx
@@ -0,0 +1,320 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="space"/>
+    <GlyphID id="2" name="zero"/>
+    <GlyphID id="3" name="one"/>
+    <GlyphID id="4" name="f_f_i"/>
+    <GlyphID id="5" name="f_i"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0xf1c8c54f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Mar 31 07:56:22 2016"/>
+    <modified value="Thu Mar 31 11:16:24 2016"/>
+    <xMin value="40"/>
+    <yMin value="-10"/>
+    <xMax value="620"/>
+    <yMax value="710"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="700"/>
+    <minLeftSideBearing value="40"/>
+    <minRightSideBearing value="40"/>
+    <xMaxExtent value="620"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="4"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="32"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="1"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="606"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="8722"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="700" lsb="80"/>
+    <mtx name="space" width="700" lsb="0"/>
+    <mtx name="zero" width="625" lsb="40"/>
+    <mtx name="one" width="660" lsb="80"/>
+    <mtx name="f_f_i" width="660" lsb="80"/>
+    <mtx name="f_i" width="660" lsb="80"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/>
+      <map code="0x30" name="zero"/>
+      <map code="0x31" name="one"/>
+      <map code="0xfb03" name="f_f_i"/>
+      <map code="0xfb01" name="f_i"/>
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="80" yMin="0" xMax="620" yMax="700">
+      <contour>
+        <pt x="620" y="700" on="1"/>
+        <pt x="620" y="0" on="1"/>
+        <pt x="80" y="0" on="1"/>
+        <pt x="80" y="700" on="1"/>
+      </contour>
+      <contour>
+        <pt x="160" y="80" on="1"/>
+        <pt x="540" y="80" on="1"/>
+        <pt x="540" y="620" on="1"/>
+        <pt x="160" y="620" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="one" xMin="80" yMin="230" xMax="580" yMax="490">
+      <contour>
+        <pt x="580" y="490" on="1"/>
+        <pt x="580" y="410" on="1"/>
+        <pt x="80" y="410" on="1"/>
+        <pt x="80" y="490" on="1"/>
+      </contour>
+      <contour>
+        <pt x="580" y="310" on="1"/>
+        <pt x="580" y="230" on="1"/>
+        <pt x="80" y="230" on="1"/>
+        <pt x="80" y="310" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="f_f_i" xMin="80" yMin="310" xMax="580" yMax="390">
+      <contour>
+        <pt x="580" y="390" on="1"/>
+        <pt x="580" y="310" on="1"/>
+        <pt x="80" y="310" on="1"/>
+        <pt x="80" y="390" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="f_i" xMin="80" yMin="100" xMax="580" yMax="600">
+      <contour>
+        <pt x="580" y="310" on="1"/>
+        <pt x="370" y="310" on="1"/>
+        <pt x="370" y="100" on="1"/>
+        <pt x="290" y="100" on="1"/>
+        <pt x="290" y="310" on="1"/>
+        <pt x="80" y="310" on="1"/>
+        <pt x="80" y="390" on="1"/>
+        <pt x="290" y="390" on="1"/>
+        <pt x="290" y="600" on="1"/>
+        <pt x="370" y="600" on="1"/>
+        <pt x="370" y="390" on="1"/>
+        <pt x="580" y="390" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+    <TTGlyph name="zero" xMin="40" yMin="-10" xMax="585" yMax="710">
+      <contour>
+        <pt x="237" y="-10" on="0"/>
+        <pt x="113" y="78" on="0"/>
+        <pt x="40" y="241" on="0"/>
+        <pt x="40" y="350" on="1"/>
+        <pt x="40" y="459" on="0"/>
+        <pt x="113" y="622" on="0"/>
+        <pt x="237" y="710" on="0"/>
+        <pt x="313" y="710" on="1"/>
+        <pt x="388" y="710" on="0"/>
+        <pt x="513" y="622" on="0"/>
+        <pt x="585" y="459" on="0"/>
+        <pt x="585" y="350" on="1"/>
+        <pt x="585" y="241" on="0"/>
+        <pt x="513" y="78" on="0"/>
+        <pt x="388" y="-10" on="0"/>
+        <pt x="313" y="-10" on="1"/>
+      </contour>
+      <contour>
+        <pt x="366" y="74" on="0"/>
+        <pt x="454" y="144" on="0"/>
+        <pt x="505" y="270" on="0"/>
+        <pt x="505" y="350" on="1"/>
+        <pt x="505" y="430" on="0"/>
+        <pt x="454" y="556" on="0"/>
+        <pt x="366" y="626" on="0"/>
+        <pt x="313" y="626" on="1"/>
+        <pt x="260" y="626" on="0"/>
+        <pt x="171" y="556" on="0"/>
+        <pt x="120" y="430" on="0"/>
+        <pt x="120" y="350" on="1"/>
+        <pt x="120" y="270" on="0"/>
+        <pt x="171" y="144" on="0"/>
+        <pt x="260" y="74" on="0"/>
+        <pt x="313" y="74" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestLCAR
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+  </name>
+
+  <gasp>
+    <gaspRange rangeMaxPPEM="7" rangeGaspBehavior="10"/>
+    <gaspRange rangeMaxPPEM="19" rangeGaspBehavior="7"/>
+    <gaspRange rangeMaxPPEM="65535" rangeGaspBehavior="15"/>
+  </gasp>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+    </extraNames>
+  </post>
+
+  <lcar>
+    <Version value="0x00010000"/>
+    <LigatureCarets Format="0">
+      <Carets>
+        <Lookup glyph="f_f_i">
+          <!-- DivsionPointCount=2 -->
+          <DivisionPoint index="0" value="239"/>
+          <DivisionPoint index="1" value="472"/>
+        </Lookup>
+        <Lookup glyph="f_i">
+          <!-- DivsionPointCount=1 -->
+          <DivisionPoint index="0" value="238"/>
+        </Lookup>
+      </Carets>
+    </LigatureCarets>
+  </lcar>
+
+</ttFont>
diff --git a/Tests/subset/data/TestLCAR-1.ttx b/Tests/subset/data/TestLCAR-1.ttx
new file mode 100644
index 0000000..6e8d85b
--- /dev/null
+++ b/Tests/subset/data/TestLCAR-1.ttx
@@ -0,0 +1,320 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="space"/>
+    <GlyphID id="2" name="zero"/>
+    <GlyphID id="3" name="one"/>
+    <GlyphID id="4" name="f_f_i"/>
+    <GlyphID id="5" name="f_i"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0xf1c8c54f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Mar 31 07:56:22 2016"/>
+    <modified value="Thu Mar 31 11:16:24 2016"/>
+    <xMin value="40"/>
+    <yMin value="-10"/>
+    <xMax value="620"/>
+    <yMax value="710"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="700"/>
+    <minLeftSideBearing value="40"/>
+    <minRightSideBearing value="40"/>
+    <xMaxExtent value="620"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="4"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="32"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="1"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="606"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="8722"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="700" lsb="80"/>
+    <mtx name="space" width="700" lsb="0"/>
+    <mtx name="zero" width="625" lsb="40"/>
+    <mtx name="one" width="660" lsb="80"/>
+    <mtx name="f_f_i" width="660" lsb="80"/>
+    <mtx name="f_i" width="660" lsb="80"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/>
+      <map code="0x30" name="zero"/>
+      <map code="0x31" name="one"/>
+      <map code="0xfb03" name="f_f_i"/>
+      <map code="0xfb01" name="f_i"/>
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="80" yMin="0" xMax="620" yMax="700">
+      <contour>
+        <pt x="620" y="700" on="1"/>
+        <pt x="620" y="0" on="1"/>
+        <pt x="80" y="0" on="1"/>
+        <pt x="80" y="700" on="1"/>
+      </contour>
+      <contour>
+        <pt x="160" y="80" on="1"/>
+        <pt x="540" y="80" on="1"/>
+        <pt x="540" y="620" on="1"/>
+        <pt x="160" y="620" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="one" xMin="80" yMin="230" xMax="580" yMax="490">
+      <contour>
+        <pt x="580" y="490" on="1"/>
+        <pt x="580" y="410" on="1"/>
+        <pt x="80" y="410" on="1"/>
+        <pt x="80" y="490" on="1"/>
+      </contour>
+      <contour>
+        <pt x="580" y="310" on="1"/>
+        <pt x="580" y="230" on="1"/>
+        <pt x="80" y="230" on="1"/>
+        <pt x="80" y="310" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="f_f_i" xMin="80" yMin="310" xMax="580" yMax="390">
+      <contour>
+        <pt x="580" y="390" on="1"/>
+        <pt x="580" y="310" on="1"/>
+        <pt x="80" y="310" on="1"/>
+        <pt x="80" y="390" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="f_i" xMin="80" yMin="100" xMax="580" yMax="600">
+      <contour>
+        <pt x="580" y="310" on="1"/>
+        <pt x="370" y="310" on="1"/>
+        <pt x="370" y="100" on="1"/>
+        <pt x="290" y="100" on="1"/>
+        <pt x="290" y="310" on="1"/>
+        <pt x="80" y="310" on="1"/>
+        <pt x="80" y="390" on="1"/>
+        <pt x="290" y="390" on="1"/>
+        <pt x="290" y="600" on="1"/>
+        <pt x="370" y="600" on="1"/>
+        <pt x="370" y="390" on="1"/>
+        <pt x="580" y="390" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+    <TTGlyph name="zero" xMin="40" yMin="-10" xMax="585" yMax="710">
+      <contour>
+        <pt x="237" y="-10" on="0"/>
+        <pt x="113" y="78" on="0"/>
+        <pt x="40" y="241" on="0"/>
+        <pt x="40" y="350" on="1"/>
+        <pt x="40" y="459" on="0"/>
+        <pt x="113" y="622" on="0"/>
+        <pt x="237" y="710" on="0"/>
+        <pt x="313" y="710" on="1"/>
+        <pt x="388" y="710" on="0"/>
+        <pt x="513" y="622" on="0"/>
+        <pt x="585" y="459" on="0"/>
+        <pt x="585" y="350" on="1"/>
+        <pt x="585" y="241" on="0"/>
+        <pt x="513" y="78" on="0"/>
+        <pt x="388" y="-10" on="0"/>
+        <pt x="313" y="-10" on="1"/>
+      </contour>
+      <contour>
+        <pt x="366" y="74" on="0"/>
+        <pt x="454" y="144" on="0"/>
+        <pt x="505" y="270" on="0"/>
+        <pt x="505" y="350" on="1"/>
+        <pt x="505" y="430" on="0"/>
+        <pt x="454" y="556" on="0"/>
+        <pt x="366" y="626" on="0"/>
+        <pt x="313" y="626" on="1"/>
+        <pt x="260" y="626" on="0"/>
+        <pt x="171" y="556" on="0"/>
+        <pt x="120" y="430" on="0"/>
+        <pt x="120" y="350" on="1"/>
+        <pt x="120" y="270" on="0"/>
+        <pt x="171" y="144" on="0"/>
+        <pt x="260" y="74" on="0"/>
+        <pt x="313" y="74" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestLCAR
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+  </name>
+
+  <gasp>
+    <gaspRange rangeMaxPPEM="7" rangeGaspBehavior="10"/>
+    <gaspRange rangeMaxPPEM="19" rangeGaspBehavior="7"/>
+    <gaspRange rangeMaxPPEM="65535" rangeGaspBehavior="15"/>
+  </gasp>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+    </extraNames>
+  </post>
+
+  <lcar>
+    <Version value="0x00010000"/>
+    <LigatureCarets Format="1">
+      <Carets>
+        <Lookup glyph="f_f_i">
+          <!-- DivsionPointCount=2 -->
+          <DivisionPoint index="0" value="2"/>
+          <DivisionPoint index="1" value="3"/>
+        </Lookup>
+        <Lookup glyph="f_i">
+          <!-- DivsionPointCount=1 -->
+          <DivisionPoint index="0" value="7"/>
+        </Lookup>
+      </Carets>
+    </LigatureCarets>
+  </lcar>
+
+</ttFont>
diff --git a/Tests/subset/data/TestMATH-Regular.ttx b/Tests/subset/data/TestMATH-Regular.ttx
new file mode 100644
index 0000000..63b8430
--- /dev/null
+++ b/Tests/subset/data/TestMATH-Regular.ttx
@@ -0,0 +1,7590 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="exclam"/>
+    <GlyphID id="2" name="quotedbl"/>
+    <GlyphID id="3" name="numbersign"/>
+    <GlyphID id="4" name="dollar"/>
+    <GlyphID id="5" name="percent"/>
+    <GlyphID id="6" name="ampersand"/>
+    <GlyphID id="7" name="quotesingle"/>
+    <GlyphID id="8" name="parenleft"/>
+    <GlyphID id="9" name="parenright"/>
+    <GlyphID id="10" name="asterisk"/>
+    <GlyphID id="11" name="plus"/>
+    <GlyphID id="12" name="comma"/>
+    <GlyphID id="13" name="hyphen"/>
+    <GlyphID id="14" name="period"/>
+    <GlyphID id="15" name="slash"/>
+    <GlyphID id="16" name="zero"/>
+    <GlyphID id="17" name="one"/>
+    <GlyphID id="18" name="two"/>
+    <GlyphID id="19" name="three"/>
+    <GlyphID id="20" name="four"/>
+    <GlyphID id="21" name="five"/>
+    <GlyphID id="22" name="six"/>
+    <GlyphID id="23" name="seven"/>
+    <GlyphID id="24" name="eight"/>
+    <GlyphID id="25" name="nine"/>
+    <GlyphID id="26" name="colon"/>
+    <GlyphID id="27" name="semicolon"/>
+    <GlyphID id="28" name="less"/>
+    <GlyphID id="29" name="equal"/>
+    <GlyphID id="30" name="greater"/>
+    <GlyphID id="31" name="question"/>
+    <GlyphID id="32" name="at"/>
+    <GlyphID id="33" name="A"/>
+    <GlyphID id="34" name="B"/>
+    <GlyphID id="35" name="C"/>
+    <GlyphID id="36" name="D"/>
+    <GlyphID id="37" name="E"/>
+    <GlyphID id="38" name="F"/>
+    <GlyphID id="39" name="G"/>
+    <GlyphID id="40" name="H"/>
+    <GlyphID id="41" name="I"/>
+    <GlyphID id="42" name="J"/>
+    <GlyphID id="43" name="K"/>
+    <GlyphID id="44" name="L"/>
+    <GlyphID id="45" name="M"/>
+    <GlyphID id="46" name="N"/>
+    <GlyphID id="47" name="O"/>
+    <GlyphID id="48" name="P"/>
+    <GlyphID id="49" name="Q"/>
+    <GlyphID id="50" name="R"/>
+    <GlyphID id="51" name="S"/>
+    <GlyphID id="52" name="T"/>
+    <GlyphID id="53" name="U"/>
+    <GlyphID id="54" name="V"/>
+    <GlyphID id="55" name="W"/>
+    <GlyphID id="56" name="X"/>
+    <GlyphID id="57" name="Y"/>
+    <GlyphID id="58" name="Z"/>
+    <GlyphID id="59" name="uni0300"/>
+    <GlyphID id="60" name="uni0301"/>
+    <GlyphID id="61" name="uni0302"/>
+    <GlyphID id="62" name="uni0303"/>
+    <GlyphID id="63" name="uni0304"/>
+    <GlyphID id="64" name="uni0305"/>
+    <GlyphID id="65" name="uni0306"/>
+    <GlyphID id="66" name="uni0307"/>
+    <GlyphID id="67" name="uni0308"/>
+    <GlyphID id="68" name="uni0309"/>
+    <GlyphID id="69" name="uni030A"/>
+    <GlyphID id="70" name="uni030B"/>
+    <GlyphID id="71" name="uni030C"/>
+    <GlyphID id="72" name="uni030D"/>
+    <GlyphID id="73" name="uni030E"/>
+    <GlyphID id="74" name="uni030F"/>
+    <GlyphID id="75" name="uni0310"/>
+    <GlyphID id="76" name="uni0311"/>
+    <GlyphID id="77" name="uni0312"/>
+    <GlyphID id="78" name="uni0313"/>
+    <GlyphID id="79" name="uni0314"/>
+    <GlyphID id="80" name="uni0315"/>
+    <GlyphID id="81" name="uni0316"/>
+    <GlyphID id="82" name="uni0317"/>
+    <GlyphID id="83" name="uni0318"/>
+    <GlyphID id="84" name="uni0319"/>
+    <GlyphID id="85" name="uni031A"/>
+    <GlyphID id="86" name="uni031B"/>
+    <GlyphID id="87" name="uni031C"/>
+    <GlyphID id="88" name="uni031D"/>
+    <GlyphID id="89" name="uni031E"/>
+    <GlyphID id="90" name="uni031F"/>
+    <GlyphID id="91" name="uni0320"/>
+    <GlyphID id="92" name="uni0321"/>
+    <GlyphID id="93" name="uni0322"/>
+    <GlyphID id="94" name="uni0323"/>
+    <GlyphID id="95" name="uni0324"/>
+    <GlyphID id="96" name="uni0325"/>
+    <GlyphID id="97" name="uni0326"/>
+    <GlyphID id="98" name="uni0327"/>
+    <GlyphID id="99" name="uni0328"/>
+    <GlyphID id="100" name="uni0329"/>
+    <GlyphID id="101" name="uni032A"/>
+    <GlyphID id="102" name="uni032B"/>
+    <GlyphID id="103" name="uni032C"/>
+    <GlyphID id="104" name="uni032D"/>
+    <GlyphID id="105" name="uni032E"/>
+    <GlyphID id="106" name="uni032F"/>
+    <GlyphID id="107" name="uni0330"/>
+    <GlyphID id="108" name="uni0331"/>
+    <GlyphID id="109" name="uni0332"/>
+    <GlyphID id="110" name="uni0333"/>
+    <GlyphID id="111" name="uni0334"/>
+    <GlyphID id="112" name="uni0335"/>
+    <GlyphID id="113" name="uni0336"/>
+    <GlyphID id="114" name="uni0337"/>
+    <GlyphID id="115" name="uni0338"/>
+    <GlyphID id="116" name="uni0339"/>
+    <GlyphID id="117" name="uni033A"/>
+    <GlyphID id="118" name="uni033B"/>
+    <GlyphID id="119" name="uni033C"/>
+    <GlyphID id="120" name="uni033D"/>
+    <GlyphID id="121" name="uni033E"/>
+    <GlyphID id="122" name="uni033F"/>
+    <GlyphID id="123" name="uni0346"/>
+    <GlyphID id="124" name="uni0347"/>
+    <GlyphID id="125" name="uni034C"/>
+    <GlyphID id="126" name="uni034D"/>
+    <GlyphID id="127" name="uni0359"/>
+    <GlyphID id="128" name="uni035C"/>
+    <GlyphID id="129" name="uni0360"/>
+    <GlyphID id="130" name="uni0361"/>
+    <GlyphID id="131" name="uni0362"/>
+    <GlyphID id="132" name="uni20EE"/>
+    <GlyphID id="133" name="uni20EF"/>
+    <GlyphID id="134" name="uni239B"/>
+    <GlyphID id="135" name="uni239C"/>
+    <GlyphID id="136" name="uni239D"/>
+    <GlyphID id="137" name="uni239E"/>
+    <GlyphID id="138" name="uni239F"/>
+    <GlyphID id="139" name="uni23A0"/>
+    <GlyphID id="140" name="u1D400"/>
+    <GlyphID id="141" name="u1D401"/>
+    <GlyphID id="142" name="u1D402"/>
+    <GlyphID id="143" name="u1D403"/>
+    <GlyphID id="144" name="u1D404"/>
+    <GlyphID id="145" name="u1D405"/>
+    <GlyphID id="146" name="u1D406"/>
+    <GlyphID id="147" name="u1D407"/>
+    <GlyphID id="148" name="u1D408"/>
+    <GlyphID id="149" name="u1D409"/>
+    <GlyphID id="150" name="u1D40A"/>
+    <GlyphID id="151" name="u1D40B"/>
+    <GlyphID id="152" name="u1D40C"/>
+    <GlyphID id="153" name="u1D40D"/>
+    <GlyphID id="154" name="u1D40E"/>
+    <GlyphID id="155" name="u1D40F"/>
+    <GlyphID id="156" name="u1D410"/>
+    <GlyphID id="157" name="u1D411"/>
+    <GlyphID id="158" name="u1D412"/>
+    <GlyphID id="159" name="u1D413"/>
+    <GlyphID id="160" name="u1D414"/>
+    <GlyphID id="161" name="u1D415"/>
+    <GlyphID id="162" name="u1D416"/>
+    <GlyphID id="163" name="u1D417"/>
+    <GlyphID id="164" name="u1D418"/>
+    <GlyphID id="165" name="u1D419"/>
+    <GlyphID id="166" name="u1D41A"/>
+    <GlyphID id="167" name="u1D41B"/>
+    <GlyphID id="168" name="u1D41C"/>
+    <GlyphID id="169" name="u1D41D"/>
+    <GlyphID id="170" name="u1D41E"/>
+    <GlyphID id="171" name="u1D41F"/>
+    <GlyphID id="172" name="u1D420"/>
+    <GlyphID id="173" name="u1D421"/>
+    <GlyphID id="174" name="u1D422"/>
+    <GlyphID id="175" name="u1D423"/>
+    <GlyphID id="176" name="u1D424"/>
+    <GlyphID id="177" name="u1D425"/>
+    <GlyphID id="178" name="u1D426"/>
+    <GlyphID id="179" name="u1D427"/>
+    <GlyphID id="180" name="u1D428"/>
+    <GlyphID id="181" name="u1D429"/>
+    <GlyphID id="182" name="u1D42A"/>
+    <GlyphID id="183" name="u1D42B"/>
+    <GlyphID id="184" name="u1D42C"/>
+    <GlyphID id="185" name="u1D42D"/>
+    <GlyphID id="186" name="u1D42E"/>
+    <GlyphID id="187" name="u1D42F"/>
+    <GlyphID id="188" name="u1D430"/>
+    <GlyphID id="189" name="u1D431"/>
+    <GlyphID id="190" name="u1D432"/>
+    <GlyphID id="191" name="u1D433"/>
+    <GlyphID id="192" name="u1D434"/>
+    <GlyphID id="193" name="u1D435"/>
+    <GlyphID id="194" name="u1D436"/>
+    <GlyphID id="195" name="u1D437"/>
+    <GlyphID id="196" name="u1D438"/>
+    <GlyphID id="197" name="u1D439"/>
+    <GlyphID id="198" name="u1D43A"/>
+    <GlyphID id="199" name="u1D43B"/>
+    <GlyphID id="200" name="u1D43C"/>
+    <GlyphID id="201" name="u1D43D"/>
+    <GlyphID id="202" name="u1D43E"/>
+    <GlyphID id="203" name="u1D43F"/>
+    <GlyphID id="204" name="u1D440"/>
+    <GlyphID id="205" name="u1D441"/>
+    <GlyphID id="206" name="u1D442"/>
+    <GlyphID id="207" name="u1D443"/>
+    <GlyphID id="208" name="u1D444"/>
+    <GlyphID id="209" name="u1D445"/>
+    <GlyphID id="210" name="u1D446"/>
+    <GlyphID id="211" name="u1D447"/>
+    <GlyphID id="212" name="u1D448"/>
+    <GlyphID id="213" name="u1D449"/>
+    <GlyphID id="214" name="u1D44A"/>
+    <GlyphID id="215" name="u1D44B"/>
+    <GlyphID id="216" name="u1D44C"/>
+    <GlyphID id="217" name="u1D44D"/>
+    <GlyphID id="218" name="u1D44E"/>
+    <GlyphID id="219" name="u1D44F"/>
+    <GlyphID id="220" name="u1D450"/>
+    <GlyphID id="221" name="u1D451"/>
+    <GlyphID id="222" name="u1D452"/>
+    <GlyphID id="223" name="u1D453"/>
+    <GlyphID id="224" name="u1D454"/>
+    <GlyphID id="225" name="u1D456"/>
+    <GlyphID id="226" name="u1D457"/>
+    <GlyphID id="227" name="u1D458"/>
+    <GlyphID id="228" name="u1D459"/>
+    <GlyphID id="229" name="u1D45A"/>
+    <GlyphID id="230" name="u1D45B"/>
+    <GlyphID id="231" name="u1D45C"/>
+    <GlyphID id="232" name="u1D45D"/>
+    <GlyphID id="233" name="u1D45E"/>
+    <GlyphID id="234" name="u1D45F"/>
+    <GlyphID id="235" name="u1D460"/>
+    <GlyphID id="236" name="u1D461"/>
+    <GlyphID id="237" name="u1D462"/>
+    <GlyphID id="238" name="u1D463"/>
+    <GlyphID id="239" name="u1D464"/>
+    <GlyphID id="240" name="u1D465"/>
+    <GlyphID id="241" name="u1D466"/>
+    <GlyphID id="242" name="u1D467"/>
+    <GlyphID id="243" name="parenleft.size1"/>
+    <GlyphID id="244" name="parenright.size1"/>
+    <GlyphID id="245" name="slash.size1"/>
+    <GlyphID id="246" name="uni0302.size1"/>
+    <GlyphID id="247" name="uni0303.size1"/>
+    <GlyphID id="248" name="uni0305.size1"/>
+    <GlyphID id="249" name="uni030C.size1"/>
+    <GlyphID id="250" name="uni0330.size1"/>
+    <GlyphID id="251" name="uni0332.size1"/>
+    <GlyphID id="252" name="uni0338.size1"/>
+    <GlyphID id="253" name="parenleft.size2"/>
+    <GlyphID id="254" name="parenright.size2"/>
+    <GlyphID id="255" name="slash.size2"/>
+    <GlyphID id="256" name="uni0302.size2"/>
+    <GlyphID id="257" name="uni0303.size2"/>
+    <GlyphID id="258" name="uni0305.size2"/>
+    <GlyphID id="259" name="uni030C.size2"/>
+    <GlyphID id="260" name="uni0330.size2"/>
+    <GlyphID id="261" name="uni0332.size2"/>
+    <GlyphID id="262" name="uni0338.size2"/>
+    <GlyphID id="263" name="parenleft.size3"/>
+    <GlyphID id="264" name="parenright.size3"/>
+    <GlyphID id="265" name="slash.size3"/>
+    <GlyphID id="266" name="uni0302.size3"/>
+    <GlyphID id="267" name="uni0303.size3"/>
+    <GlyphID id="268" name="uni0305.size3"/>
+    <GlyphID id="269" name="uni030C.size3"/>
+    <GlyphID id="270" name="uni0330.size3"/>
+    <GlyphID id="271" name="uni0332.size3"/>
+    <GlyphID id="272" name="uni0338.size3"/>
+    <GlyphID id="273" name="parenleft.size4"/>
+    <GlyphID id="274" name="parenright.size4"/>
+    <GlyphID id="275" name="slash.size4"/>
+    <GlyphID id="276" name="uni0302.size4"/>
+    <GlyphID id="277" name="uni0303.size4"/>
+    <GlyphID id="278" name="uni0305.size4"/>
+    <GlyphID id="279" name="uni030C.size4"/>
+    <GlyphID id="280" name="uni0330.size4"/>
+    <GlyphID id="281" name="uni0332.size4"/>
+    <GlyphID id="282" name="uni0338.size4"/>
+    <GlyphID id="283" name="uni0302.size5"/>
+    <GlyphID id="284" name="uni0303.size5"/>
+    <GlyphID id="285" name="uni0305.size5"/>
+    <GlyphID id="286" name="uni030C.size5"/>
+    <GlyphID id="287" name="uni0330.size5"/>
+    <GlyphID id="288" name="uni0332.size5"/>
+    <GlyphID id="289" name="uni0338.size5"/>
+    <GlyphID id="290" name="uni20EE.ex"/>
+    <GlyphID id="291" name="uni0338.size6"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.10799"/>
+    <checkSumAdjustment value="0x5881e67"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00001011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Sun Jan 10 17:35:12 2016"/>
+    <modified value="Sat Jan 23 18:52:32 2016"/>
+    <xMin value="-970"/>
+    <yMin value="-906"/>
+    <xMax value="3238"/>
+    <yMax value="2566"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="8"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="750"/>
+    <descent value="-250"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="3238"/>
+    <minLeftSideBearing value="-970"/>
+    <minRightSideBearing value="-490"/>
+    <xMaxExtent value="3238"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="292"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="292"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="666"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="500"/>
+    <ySubscriptYSize value="500"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="250"/>
+    <ySuperscriptXSize value="500"/>
+    <ySuperscriptYSize value="500"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="500"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="306"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="3"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="10100000 00000000 00100010 11111111"/>
+    <ulUnicodeRange2 value="00000010 00000011 11111101 11111111"/>
+    <ulUnicodeRange3 value="00001010 00000000 00000000 00100000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="STIX"/>
+    <fsSelection value="00000000 11000000"/>
+    <usFirstCharIndex value="0"/>
+    <usLastCharIndex value="65535"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="2598"/>
+    <usWinDescent value="918"/>
+    <ulCodePageRange1 value="01100000 00000000 00000000 10011111"/>
+    <ulCodePageRange2 value="11011111 11010111 00000000 00000000"/>
+    <sxHeight value="450"/>
+    <sCapHeight value="662"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="2"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      XITS Math
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x21" name="exclam"/><!-- EXCLAMATION MARK -->
+      <map code="0x22" name="quotedbl"/><!-- QUOTATION MARK -->
+      <map code="0x23" name="numbersign"/><!-- NUMBER SIGN -->
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x25" name="percent"/><!-- PERCENT SIGN -->
+      <map code="0x26" name="ampersand"/><!-- AMPERSAND -->
+      <map code="0x27" name="quotesingle"/><!-- APOSTROPHE -->
+      <map code="0x28" name="parenleft"/><!-- LEFT PARENTHESIS -->
+      <map code="0x29" name="parenright"/><!-- RIGHT PARENTHESIS -->
+      <map code="0x2a" name="asterisk"/><!-- ASTERISK -->
+      <map code="0x2b" name="plus"/><!-- PLUS SIGN -->
+      <map code="0x2c" name="comma"/><!-- COMMA -->
+      <map code="0x2d" name="hyphen"/><!-- HYPHEN-MINUS -->
+      <map code="0x2e" name="period"/><!-- FULL STOP -->
+      <map code="0x2f" name="slash"/><!-- SOLIDUS -->
+      <map code="0x30" name="zero"/><!-- DIGIT ZERO -->
+      <map code="0x31" name="one"/><!-- DIGIT ONE -->
+      <map code="0x32" name="two"/><!-- DIGIT TWO -->
+      <map code="0x33" name="three"/><!-- DIGIT THREE -->
+      <map code="0x34" name="four"/><!-- DIGIT FOUR -->
+      <map code="0x35" name="five"/><!-- DIGIT FIVE -->
+      <map code="0x36" name="six"/><!-- DIGIT SIX -->
+      <map code="0x37" name="seven"/><!-- DIGIT SEVEN -->
+      <map code="0x38" name="eight"/><!-- DIGIT EIGHT -->
+      <map code="0x39" name="nine"/><!-- DIGIT NINE -->
+      <map code="0x3a" name="colon"/><!-- COLON -->
+      <map code="0x3b" name="semicolon"/><!-- SEMICOLON -->
+      <map code="0x3c" name="less"/><!-- LESS-THAN SIGN -->
+      <map code="0x3d" name="equal"/><!-- EQUALS SIGN -->
+      <map code="0x3e" name="greater"/><!-- GREATER-THAN SIGN -->
+      <map code="0x3f" name="question"/><!-- QUESTION MARK -->
+      <map code="0x40" name="at"/><!-- COMMERCIAL AT -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x42" name="B"/><!-- LATIN CAPITAL LETTER B -->
+      <map code="0x43" name="C"/><!-- LATIN CAPITAL LETTER C -->
+      <map code="0x44" name="D"/><!-- LATIN CAPITAL LETTER D -->
+      <map code="0x45" name="E"/><!-- LATIN CAPITAL LETTER E -->
+      <map code="0x46" name="F"/><!-- LATIN CAPITAL LETTER F -->
+      <map code="0x47" name="G"/><!-- LATIN CAPITAL LETTER G -->
+      <map code="0x48" name="H"/><!-- LATIN CAPITAL LETTER H -->
+      <map code="0x49" name="I"/><!-- LATIN CAPITAL LETTER I -->
+      <map code="0x4a" name="J"/><!-- LATIN CAPITAL LETTER J -->
+      <map code="0x4b" name="K"/><!-- LATIN CAPITAL LETTER K -->
+      <map code="0x4c" name="L"/><!-- LATIN CAPITAL LETTER L -->
+      <map code="0x4d" name="M"/><!-- LATIN CAPITAL LETTER M -->
+      <map code="0x4e" name="N"/><!-- LATIN CAPITAL LETTER N -->
+      <map code="0x4f" name="O"/><!-- LATIN CAPITAL LETTER O -->
+      <map code="0x50" name="P"/><!-- LATIN CAPITAL LETTER P -->
+      <map code="0x51" name="Q"/><!-- LATIN CAPITAL LETTER Q -->
+      <map code="0x52" name="R"/><!-- LATIN CAPITAL LETTER R -->
+      <map code="0x53" name="S"/><!-- LATIN CAPITAL LETTER S -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0x55" name="U"/><!-- LATIN CAPITAL LETTER U -->
+      <map code="0x56" name="V"/><!-- LATIN CAPITAL LETTER V -->
+      <map code="0x57" name="W"/><!-- LATIN CAPITAL LETTER W -->
+      <map code="0x58" name="X"/><!-- LATIN CAPITAL LETTER X -->
+      <map code="0x59" name="Y"/><!-- LATIN CAPITAL LETTER Y -->
+      <map code="0x5a" name="Z"/><!-- LATIN CAPITAL LETTER Z -->
+      <map code="0x300" name="uni0300"/><!-- COMBINING GRAVE ACCENT -->
+      <map code="0x301" name="uni0301"/><!-- COMBINING ACUTE ACCENT -->
+      <map code="0x302" name="uni0302"/><!-- COMBINING CIRCUMFLEX ACCENT -->
+      <map code="0x303" name="uni0303"/><!-- COMBINING TILDE -->
+      <map code="0x304" name="uni0304"/><!-- COMBINING MACRON -->
+      <map code="0x305" name="uni0305"/><!-- COMBINING OVERLINE -->
+      <map code="0x306" name="uni0306"/><!-- COMBINING BREVE -->
+      <map code="0x307" name="uni0307"/><!-- COMBINING DOT ABOVE -->
+      <map code="0x308" name="uni0308"/><!-- COMBINING DIAERESIS -->
+      <map code="0x309" name="uni0309"/><!-- COMBINING HOOK ABOVE -->
+      <map code="0x30a" name="uni030A"/><!-- COMBINING RING ABOVE -->
+      <map code="0x30b" name="uni030B"/><!-- COMBINING DOUBLE ACUTE ACCENT -->
+      <map code="0x30c" name="uni030C"/><!-- COMBINING CARON -->
+      <map code="0x30d" name="uni030D"/><!-- COMBINING VERTICAL LINE ABOVE -->
+      <map code="0x30e" name="uni030E"/><!-- COMBINING DOUBLE VERTICAL LINE ABOVE -->
+      <map code="0x30f" name="uni030F"/><!-- COMBINING DOUBLE GRAVE ACCENT -->
+      <map code="0x310" name="uni0310"/><!-- COMBINING CANDRABINDU -->
+      <map code="0x311" name="uni0311"/><!-- COMBINING INVERTED BREVE -->
+      <map code="0x312" name="uni0312"/><!-- COMBINING TURNED COMMA ABOVE -->
+      <map code="0x313" name="uni0313"/><!-- COMBINING COMMA ABOVE -->
+      <map code="0x314" name="uni0314"/><!-- COMBINING REVERSED COMMA ABOVE -->
+      <map code="0x315" name="uni0315"/><!-- COMBINING COMMA ABOVE RIGHT -->
+      <map code="0x316" name="uni0316"/><!-- COMBINING GRAVE ACCENT BELOW -->
+      <map code="0x317" name="uni0317"/><!-- COMBINING ACUTE ACCENT BELOW -->
+      <map code="0x318" name="uni0318"/><!-- COMBINING LEFT TACK BELOW -->
+      <map code="0x319" name="uni0319"/><!-- COMBINING RIGHT TACK BELOW -->
+      <map code="0x31a" name="uni031A"/><!-- COMBINING LEFT ANGLE ABOVE -->
+      <map code="0x31b" name="uni031B"/><!-- COMBINING HORN -->
+      <map code="0x31c" name="uni031C"/><!-- COMBINING LEFT HALF RING BELOW -->
+      <map code="0x31d" name="uni031D"/><!-- COMBINING UP TACK BELOW -->
+      <map code="0x31e" name="uni031E"/><!-- COMBINING DOWN TACK BELOW -->
+      <map code="0x31f" name="uni031F"/><!-- COMBINING PLUS SIGN BELOW -->
+      <map code="0x320" name="uni0320"/><!-- COMBINING MINUS SIGN BELOW -->
+      <map code="0x321" name="uni0321"/><!-- COMBINING PALATALIZED HOOK BELOW -->
+      <map code="0x322" name="uni0322"/><!-- COMBINING RETROFLEX HOOK BELOW -->
+      <map code="0x323" name="uni0323"/><!-- COMBINING DOT BELOW -->
+      <map code="0x324" name="uni0324"/><!-- COMBINING DIAERESIS BELOW -->
+      <map code="0x325" name="uni0325"/><!-- COMBINING RING BELOW -->
+      <map code="0x326" name="uni0326"/><!-- COMBINING COMMA BELOW -->
+      <map code="0x327" name="uni0327"/><!-- COMBINING CEDILLA -->
+      <map code="0x328" name="uni0328"/><!-- COMBINING OGONEK -->
+      <map code="0x329" name="uni0329"/><!-- COMBINING VERTICAL LINE BELOW -->
+      <map code="0x32a" name="uni032A"/><!-- COMBINING BRIDGE BELOW -->
+      <map code="0x32b" name="uni032B"/><!-- COMBINING INVERTED DOUBLE ARCH BELOW -->
+      <map code="0x32c" name="uni032C"/><!-- COMBINING CARON BELOW -->
+      <map code="0x32d" name="uni032D"/><!-- COMBINING CIRCUMFLEX ACCENT BELOW -->
+      <map code="0x32e" name="uni032E"/><!-- COMBINING BREVE BELOW -->
+      <map code="0x32f" name="uni032F"/><!-- COMBINING INVERTED BREVE BELOW -->
+      <map code="0x330" name="uni0330"/><!-- COMBINING TILDE BELOW -->
+      <map code="0x331" name="uni0331"/><!-- COMBINING MACRON BELOW -->
+      <map code="0x332" name="uni0332"/><!-- COMBINING LOW LINE -->
+      <map code="0x333" name="uni0333"/><!-- COMBINING DOUBLE LOW LINE -->
+      <map code="0x334" name="uni0334"/><!-- COMBINING TILDE OVERLAY -->
+      <map code="0x335" name="uni0335"/><!-- COMBINING SHORT STROKE OVERLAY -->
+      <map code="0x336" name="uni0336"/><!-- COMBINING LONG STROKE OVERLAY -->
+      <map code="0x337" name="uni0337"/><!-- COMBINING SHORT SOLIDUS OVERLAY -->
+      <map code="0x338" name="uni0338"/><!-- COMBINING LONG SOLIDUS OVERLAY -->
+      <map code="0x339" name="uni0339"/><!-- COMBINING RIGHT HALF RING BELOW -->
+      <map code="0x33a" name="uni033A"/><!-- COMBINING INVERTED BRIDGE BELOW -->
+      <map code="0x33b" name="uni033B"/><!-- COMBINING SQUARE BELOW -->
+      <map code="0x33c" name="uni033C"/><!-- COMBINING SEAGULL BELOW -->
+      <map code="0x33d" name="uni033D"/><!-- COMBINING X ABOVE -->
+      <map code="0x33e" name="uni033E"/><!-- COMBINING VERTICAL TILDE -->
+      <map code="0x33f" name="uni033F"/><!-- COMBINING DOUBLE OVERLINE -->
+      <map code="0x346" name="uni0346"/><!-- COMBINING BRIDGE ABOVE -->
+      <map code="0x347" name="uni0347"/><!-- COMBINING EQUALS SIGN BELOW -->
+      <map code="0x34c" name="uni034C"/><!-- COMBINING ALMOST EQUAL TO ABOVE -->
+      <map code="0x34d" name="uni034D"/><!-- COMBINING LEFT RIGHT ARROW BELOW -->
+      <map code="0x359" name="uni0359"/><!-- COMBINING ASTERISK BELOW -->
+      <map code="0x35c" name="uni035C"/><!-- COMBINING DOUBLE BREVE BELOW -->
+      <map code="0x360" name="uni0360"/><!-- COMBINING DOUBLE TILDE -->
+      <map code="0x361" name="uni0361"/><!-- COMBINING DOUBLE INVERTED BREVE -->
+      <map code="0x362" name="uni0362"/><!-- COMBINING DOUBLE RIGHTWARDS ARROW BELOW -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="0" platEncID="4" format="12" reserved="0" length="124" language="0" nGroups="9">
+      <map code="0x21" name="exclam"/><!-- EXCLAMATION MARK -->
+      <map code="0x22" name="quotedbl"/><!-- QUOTATION MARK -->
+      <map code="0x23" name="numbersign"/><!-- NUMBER SIGN -->
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x25" name="percent"/><!-- PERCENT SIGN -->
+      <map code="0x26" name="ampersand"/><!-- AMPERSAND -->
+      <map code="0x27" name="quotesingle"/><!-- APOSTROPHE -->
+      <map code="0x28" name="parenleft"/><!-- LEFT PARENTHESIS -->
+      <map code="0x29" name="parenright"/><!-- RIGHT PARENTHESIS -->
+      <map code="0x2a" name="asterisk"/><!-- ASTERISK -->
+      <map code="0x2b" name="plus"/><!-- PLUS SIGN -->
+      <map code="0x2c" name="comma"/><!-- COMMA -->
+      <map code="0x2d" name="hyphen"/><!-- HYPHEN-MINUS -->
+      <map code="0x2e" name="period"/><!-- FULL STOP -->
+      <map code="0x2f" name="slash"/><!-- SOLIDUS -->
+      <map code="0x30" name="zero"/><!-- DIGIT ZERO -->
+      <map code="0x31" name="one"/><!-- DIGIT ONE -->
+      <map code="0x32" name="two"/><!-- DIGIT TWO -->
+      <map code="0x33" name="three"/><!-- DIGIT THREE -->
+      <map code="0x34" name="four"/><!-- DIGIT FOUR -->
+      <map code="0x35" name="five"/><!-- DIGIT FIVE -->
+      <map code="0x36" name="six"/><!-- DIGIT SIX -->
+      <map code="0x37" name="seven"/><!-- DIGIT SEVEN -->
+      <map code="0x38" name="eight"/><!-- DIGIT EIGHT -->
+      <map code="0x39" name="nine"/><!-- DIGIT NINE -->
+      <map code="0x3a" name="colon"/><!-- COLON -->
+      <map code="0x3b" name="semicolon"/><!-- SEMICOLON -->
+      <map code="0x3c" name="less"/><!-- LESS-THAN SIGN -->
+      <map code="0x3d" name="equal"/><!-- EQUALS SIGN -->
+      <map code="0x3e" name="greater"/><!-- GREATER-THAN SIGN -->
+      <map code="0x3f" name="question"/><!-- QUESTION MARK -->
+      <map code="0x40" name="at"/><!-- COMMERCIAL AT -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x42" name="B"/><!-- LATIN CAPITAL LETTER B -->
+      <map code="0x43" name="C"/><!-- LATIN CAPITAL LETTER C -->
+      <map code="0x44" name="D"/><!-- LATIN CAPITAL LETTER D -->
+      <map code="0x45" name="E"/><!-- LATIN CAPITAL LETTER E -->
+      <map code="0x46" name="F"/><!-- LATIN CAPITAL LETTER F -->
+      <map code="0x47" name="G"/><!-- LATIN CAPITAL LETTER G -->
+      <map code="0x48" name="H"/><!-- LATIN CAPITAL LETTER H -->
+      <map code="0x49" name="I"/><!-- LATIN CAPITAL LETTER I -->
+      <map code="0x4a" name="J"/><!-- LATIN CAPITAL LETTER J -->
+      <map code="0x4b" name="K"/><!-- LATIN CAPITAL LETTER K -->
+      <map code="0x4c" name="L"/><!-- LATIN CAPITAL LETTER L -->
+      <map code="0x4d" name="M"/><!-- LATIN CAPITAL LETTER M -->
+      <map code="0x4e" name="N"/><!-- LATIN CAPITAL LETTER N -->
+      <map code="0x4f" name="O"/><!-- LATIN CAPITAL LETTER O -->
+      <map code="0x50" name="P"/><!-- LATIN CAPITAL LETTER P -->
+      <map code="0x51" name="Q"/><!-- LATIN CAPITAL LETTER Q -->
+      <map code="0x52" name="R"/><!-- LATIN CAPITAL LETTER R -->
+      <map code="0x53" name="S"/><!-- LATIN CAPITAL LETTER S -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0x55" name="U"/><!-- LATIN CAPITAL LETTER U -->
+      <map code="0x56" name="V"/><!-- LATIN CAPITAL LETTER V -->
+      <map code="0x57" name="W"/><!-- LATIN CAPITAL LETTER W -->
+      <map code="0x58" name="X"/><!-- LATIN CAPITAL LETTER X -->
+      <map code="0x59" name="Y"/><!-- LATIN CAPITAL LETTER Y -->
+      <map code="0x5a" name="Z"/><!-- LATIN CAPITAL LETTER Z -->
+      <map code="0x300" name="uni0300"/><!-- COMBINING GRAVE ACCENT -->
+      <map code="0x301" name="uni0301"/><!-- COMBINING ACUTE ACCENT -->
+      <map code="0x302" name="uni0302"/><!-- COMBINING CIRCUMFLEX ACCENT -->
+      <map code="0x303" name="uni0303"/><!-- COMBINING TILDE -->
+      <map code="0x304" name="uni0304"/><!-- COMBINING MACRON -->
+      <map code="0x305" name="uni0305"/><!-- COMBINING OVERLINE -->
+      <map code="0x306" name="uni0306"/><!-- COMBINING BREVE -->
+      <map code="0x307" name="uni0307"/><!-- COMBINING DOT ABOVE -->
+      <map code="0x308" name="uni0308"/><!-- COMBINING DIAERESIS -->
+      <map code="0x309" name="uni0309"/><!-- COMBINING HOOK ABOVE -->
+      <map code="0x30a" name="uni030A"/><!-- COMBINING RING ABOVE -->
+      <map code="0x30b" name="uni030B"/><!-- COMBINING DOUBLE ACUTE ACCENT -->
+      <map code="0x30c" name="uni030C"/><!-- COMBINING CARON -->
+      <map code="0x30d" name="uni030D"/><!-- COMBINING VERTICAL LINE ABOVE -->
+      <map code="0x30e" name="uni030E"/><!-- COMBINING DOUBLE VERTICAL LINE ABOVE -->
+      <map code="0x30f" name="uni030F"/><!-- COMBINING DOUBLE GRAVE ACCENT -->
+      <map code="0x310" name="uni0310"/><!-- COMBINING CANDRABINDU -->
+      <map code="0x311" name="uni0311"/><!-- COMBINING INVERTED BREVE -->
+      <map code="0x312" name="uni0312"/><!-- COMBINING TURNED COMMA ABOVE -->
+      <map code="0x313" name="uni0313"/><!-- COMBINING COMMA ABOVE -->
+      <map code="0x314" name="uni0314"/><!-- COMBINING REVERSED COMMA ABOVE -->
+      <map code="0x315" name="uni0315"/><!-- COMBINING COMMA ABOVE RIGHT -->
+      <map code="0x316" name="uni0316"/><!-- COMBINING GRAVE ACCENT BELOW -->
+      <map code="0x317" name="uni0317"/><!-- COMBINING ACUTE ACCENT BELOW -->
+      <map code="0x318" name="uni0318"/><!-- COMBINING LEFT TACK BELOW -->
+      <map code="0x319" name="uni0319"/><!-- COMBINING RIGHT TACK BELOW -->
+      <map code="0x31a" name="uni031A"/><!-- COMBINING LEFT ANGLE ABOVE -->
+      <map code="0x31b" name="uni031B"/><!-- COMBINING HORN -->
+      <map code="0x31c" name="uni031C"/><!-- COMBINING LEFT HALF RING BELOW -->
+      <map code="0x31d" name="uni031D"/><!-- COMBINING UP TACK BELOW -->
+      <map code="0x31e" name="uni031E"/><!-- COMBINING DOWN TACK BELOW -->
+      <map code="0x31f" name="uni031F"/><!-- COMBINING PLUS SIGN BELOW -->
+      <map code="0x320" name="uni0320"/><!-- COMBINING MINUS SIGN BELOW -->
+      <map code="0x321" name="uni0321"/><!-- COMBINING PALATALIZED HOOK BELOW -->
+      <map code="0x322" name="uni0322"/><!-- COMBINING RETROFLEX HOOK BELOW -->
+      <map code="0x323" name="uni0323"/><!-- COMBINING DOT BELOW -->
+      <map code="0x324" name="uni0324"/><!-- COMBINING DIAERESIS BELOW -->
+      <map code="0x325" name="uni0325"/><!-- COMBINING RING BELOW -->
+      <map code="0x326" name="uni0326"/><!-- COMBINING COMMA BELOW -->
+      <map code="0x327" name="uni0327"/><!-- COMBINING CEDILLA -->
+      <map code="0x328" name="uni0328"/><!-- COMBINING OGONEK -->
+      <map code="0x329" name="uni0329"/><!-- COMBINING VERTICAL LINE BELOW -->
+      <map code="0x32a" name="uni032A"/><!-- COMBINING BRIDGE BELOW -->
+      <map code="0x32b" name="uni032B"/><!-- COMBINING INVERTED DOUBLE ARCH BELOW -->
+      <map code="0x32c" name="uni032C"/><!-- COMBINING CARON BELOW -->
+      <map code="0x32d" name="uni032D"/><!-- COMBINING CIRCUMFLEX ACCENT BELOW -->
+      <map code="0x32e" name="uni032E"/><!-- COMBINING BREVE BELOW -->
+      <map code="0x32f" name="uni032F"/><!-- COMBINING INVERTED BREVE BELOW -->
+      <map code="0x330" name="uni0330"/><!-- COMBINING TILDE BELOW -->
+      <map code="0x331" name="uni0331"/><!-- COMBINING MACRON BELOW -->
+      <map code="0x332" name="uni0332"/><!-- COMBINING LOW LINE -->
+      <map code="0x333" name="uni0333"/><!-- COMBINING DOUBLE LOW LINE -->
+      <map code="0x334" name="uni0334"/><!-- COMBINING TILDE OVERLAY -->
+      <map code="0x335" name="uni0335"/><!-- COMBINING SHORT STROKE OVERLAY -->
+      <map code="0x336" name="uni0336"/><!-- COMBINING LONG STROKE OVERLAY -->
+      <map code="0x337" name="uni0337"/><!-- COMBINING SHORT SOLIDUS OVERLAY -->
+      <map code="0x338" name="uni0338"/><!-- COMBINING LONG SOLIDUS OVERLAY -->
+      <map code="0x339" name="uni0339"/><!-- COMBINING RIGHT HALF RING BELOW -->
+      <map code="0x33a" name="uni033A"/><!-- COMBINING INVERTED BRIDGE BELOW -->
+      <map code="0x33b" name="uni033B"/><!-- COMBINING SQUARE BELOW -->
+      <map code="0x33c" name="uni033C"/><!-- COMBINING SEAGULL BELOW -->
+      <map code="0x33d" name="uni033D"/><!-- COMBINING X ABOVE -->
+      <map code="0x33e" name="uni033E"/><!-- COMBINING VERTICAL TILDE -->
+      <map code="0x33f" name="uni033F"/><!-- COMBINING DOUBLE OVERLINE -->
+      <map code="0x346" name="uni0346"/><!-- COMBINING BRIDGE ABOVE -->
+      <map code="0x347" name="uni0347"/><!-- COMBINING EQUALS SIGN BELOW -->
+      <map code="0x34c" name="uni034C"/><!-- COMBINING ALMOST EQUAL TO ABOVE -->
+      <map code="0x34d" name="uni034D"/><!-- COMBINING LEFT RIGHT ARROW BELOW -->
+      <map code="0x359" name="uni0359"/><!-- COMBINING ASTERISK BELOW -->
+      <map code="0x35c" name="uni035C"/><!-- COMBINING DOUBLE BREVE BELOW -->
+      <map code="0x360" name="uni0360"/><!-- COMBINING DOUBLE TILDE -->
+      <map code="0x361" name="uni0361"/><!-- COMBINING DOUBLE INVERTED BREVE -->
+      <map code="0x362" name="uni0362"/><!-- COMBINING DOUBLE RIGHTWARDS ARROW BELOW -->
+      <map code="0x1d400" name="u1D400"/><!-- MATHEMATICAL BOLD CAPITAL A -->
+      <map code="0x1d401" name="u1D401"/><!-- MATHEMATICAL BOLD CAPITAL B -->
+      <map code="0x1d402" name="u1D402"/><!-- MATHEMATICAL BOLD CAPITAL C -->
+      <map code="0x1d403" name="u1D403"/><!-- MATHEMATICAL BOLD CAPITAL D -->
+      <map code="0x1d404" name="u1D404"/><!-- MATHEMATICAL BOLD CAPITAL E -->
+      <map code="0x1d405" name="u1D405"/><!-- MATHEMATICAL BOLD CAPITAL F -->
+      <map code="0x1d406" name="u1D406"/><!-- MATHEMATICAL BOLD CAPITAL G -->
+      <map code="0x1d407" name="u1D407"/><!-- MATHEMATICAL BOLD CAPITAL H -->
+      <map code="0x1d408" name="u1D408"/><!-- MATHEMATICAL BOLD CAPITAL I -->
+      <map code="0x1d409" name="u1D409"/><!-- MATHEMATICAL BOLD CAPITAL J -->
+      <map code="0x1d40a" name="u1D40A"/><!-- MATHEMATICAL BOLD CAPITAL K -->
+      <map code="0x1d40b" name="u1D40B"/><!-- MATHEMATICAL BOLD CAPITAL L -->
+      <map code="0x1d40c" name="u1D40C"/><!-- MATHEMATICAL BOLD CAPITAL M -->
+      <map code="0x1d40d" name="u1D40D"/><!-- MATHEMATICAL BOLD CAPITAL N -->
+      <map code="0x1d40e" name="u1D40E"/><!-- MATHEMATICAL BOLD CAPITAL O -->
+      <map code="0x1d40f" name="u1D40F"/><!-- MATHEMATICAL BOLD CAPITAL P -->
+      <map code="0x1d410" name="u1D410"/><!-- MATHEMATICAL BOLD CAPITAL Q -->
+      <map code="0x1d411" name="u1D411"/><!-- MATHEMATICAL BOLD CAPITAL R -->
+      <map code="0x1d412" name="u1D412"/><!-- MATHEMATICAL BOLD CAPITAL S -->
+      <map code="0x1d413" name="u1D413"/><!-- MATHEMATICAL BOLD CAPITAL T -->
+      <map code="0x1d414" name="u1D414"/><!-- MATHEMATICAL BOLD CAPITAL U -->
+      <map code="0x1d415" name="u1D415"/><!-- MATHEMATICAL BOLD CAPITAL V -->
+      <map code="0x1d416" name="u1D416"/><!-- MATHEMATICAL BOLD CAPITAL W -->
+      <map code="0x1d417" name="u1D417"/><!-- MATHEMATICAL BOLD CAPITAL X -->
+      <map code="0x1d418" name="u1D418"/><!-- MATHEMATICAL BOLD CAPITAL Y -->
+      <map code="0x1d419" name="u1D419"/><!-- MATHEMATICAL BOLD CAPITAL Z -->
+      <map code="0x1d41a" name="u1D41A"/><!-- MATHEMATICAL BOLD SMALL A -->
+      <map code="0x1d41b" name="u1D41B"/><!-- MATHEMATICAL BOLD SMALL B -->
+      <map code="0x1d41c" name="u1D41C"/><!-- MATHEMATICAL BOLD SMALL C -->
+      <map code="0x1d41d" name="u1D41D"/><!-- MATHEMATICAL BOLD SMALL D -->
+      <map code="0x1d41e" name="u1D41E"/><!-- MATHEMATICAL BOLD SMALL E -->
+      <map code="0x1d41f" name="u1D41F"/><!-- MATHEMATICAL BOLD SMALL F -->
+      <map code="0x1d420" name="u1D420"/><!-- MATHEMATICAL BOLD SMALL G -->
+      <map code="0x1d421" name="u1D421"/><!-- MATHEMATICAL BOLD SMALL H -->
+      <map code="0x1d422" name="u1D422"/><!-- MATHEMATICAL BOLD SMALL I -->
+      <map code="0x1d423" name="u1D423"/><!-- MATHEMATICAL BOLD SMALL J -->
+      <map code="0x1d424" name="u1D424"/><!-- MATHEMATICAL BOLD SMALL K -->
+      <map code="0x1d425" name="u1D425"/><!-- MATHEMATICAL BOLD SMALL L -->
+      <map code="0x1d426" name="u1D426"/><!-- MATHEMATICAL BOLD SMALL M -->
+      <map code="0x1d427" name="u1D427"/><!-- MATHEMATICAL BOLD SMALL N -->
+      <map code="0x1d428" name="u1D428"/><!-- MATHEMATICAL BOLD SMALL O -->
+      <map code="0x1d429" name="u1D429"/><!-- MATHEMATICAL BOLD SMALL P -->
+      <map code="0x1d42a" name="u1D42A"/><!-- MATHEMATICAL BOLD SMALL Q -->
+      <map code="0x1d42b" name="u1D42B"/><!-- MATHEMATICAL BOLD SMALL R -->
+      <map code="0x1d42c" name="u1D42C"/><!-- MATHEMATICAL BOLD SMALL S -->
+      <map code="0x1d42d" name="u1D42D"/><!-- MATHEMATICAL BOLD SMALL T -->
+      <map code="0x1d42e" name="u1D42E"/><!-- MATHEMATICAL BOLD SMALL U -->
+      <map code="0x1d42f" name="u1D42F"/><!-- MATHEMATICAL BOLD SMALL V -->
+      <map code="0x1d430" name="u1D430"/><!-- MATHEMATICAL BOLD SMALL W -->
+      <map code="0x1d431" name="u1D431"/><!-- MATHEMATICAL BOLD SMALL X -->
+      <map code="0x1d432" name="u1D432"/><!-- MATHEMATICAL BOLD SMALL Y -->
+      <map code="0x1d433" name="u1D433"/><!-- MATHEMATICAL BOLD SMALL Z -->
+      <map code="0x1d434" name="u1D434"/><!-- MATHEMATICAL ITALIC CAPITAL A -->
+      <map code="0x1d435" name="u1D435"/><!-- MATHEMATICAL ITALIC CAPITAL B -->
+      <map code="0x1d436" name="u1D436"/><!-- MATHEMATICAL ITALIC CAPITAL C -->
+      <map code="0x1d437" name="u1D437"/><!-- MATHEMATICAL ITALIC CAPITAL D -->
+      <map code="0x1d438" name="u1D438"/><!-- MATHEMATICAL ITALIC CAPITAL E -->
+      <map code="0x1d439" name="u1D439"/><!-- MATHEMATICAL ITALIC CAPITAL F -->
+      <map code="0x1d43a" name="u1D43A"/><!-- MATHEMATICAL ITALIC CAPITAL G -->
+      <map code="0x1d43b" name="u1D43B"/><!-- MATHEMATICAL ITALIC CAPITAL H -->
+      <map code="0x1d43c" name="u1D43C"/><!-- MATHEMATICAL ITALIC CAPITAL I -->
+      <map code="0x1d43d" name="u1D43D"/><!-- MATHEMATICAL ITALIC CAPITAL J -->
+      <map code="0x1d43e" name="u1D43E"/><!-- MATHEMATICAL ITALIC CAPITAL K -->
+      <map code="0x1d43f" name="u1D43F"/><!-- MATHEMATICAL ITALIC CAPITAL L -->
+      <map code="0x1d440" name="u1D440"/><!-- MATHEMATICAL ITALIC CAPITAL M -->
+      <map code="0x1d441" name="u1D441"/><!-- MATHEMATICAL ITALIC CAPITAL N -->
+      <map code="0x1d442" name="u1D442"/><!-- MATHEMATICAL ITALIC CAPITAL O -->
+      <map code="0x1d443" name="u1D443"/><!-- MATHEMATICAL ITALIC CAPITAL P -->
+      <map code="0x1d444" name="u1D444"/><!-- MATHEMATICAL ITALIC CAPITAL Q -->
+      <map code="0x1d445" name="u1D445"/><!-- MATHEMATICAL ITALIC CAPITAL R -->
+      <map code="0x1d446" name="u1D446"/><!-- MATHEMATICAL ITALIC CAPITAL S -->
+      <map code="0x1d447" name="u1D447"/><!-- MATHEMATICAL ITALIC CAPITAL T -->
+      <map code="0x1d448" name="u1D448"/><!-- MATHEMATICAL ITALIC CAPITAL U -->
+      <map code="0x1d449" name="u1D449"/><!-- MATHEMATICAL ITALIC CAPITAL V -->
+      <map code="0x1d44a" name="u1D44A"/><!-- MATHEMATICAL ITALIC CAPITAL W -->
+      <map code="0x1d44b" name="u1D44B"/><!-- MATHEMATICAL ITALIC CAPITAL X -->
+      <map code="0x1d44c" name="u1D44C"/><!-- MATHEMATICAL ITALIC CAPITAL Y -->
+      <map code="0x1d44d" name="u1D44D"/><!-- MATHEMATICAL ITALIC CAPITAL Z -->
+      <map code="0x1d44e" name="u1D44E"/><!-- MATHEMATICAL ITALIC SMALL A -->
+      <map code="0x1d44f" name="u1D44F"/><!-- MATHEMATICAL ITALIC SMALL B -->
+      <map code="0x1d450" name="u1D450"/><!-- MATHEMATICAL ITALIC SMALL C -->
+      <map code="0x1d451" name="u1D451"/><!-- MATHEMATICAL ITALIC SMALL D -->
+      <map code="0x1d452" name="u1D452"/><!-- MATHEMATICAL ITALIC SMALL E -->
+      <map code="0x1d453" name="u1D453"/><!-- MATHEMATICAL ITALIC SMALL F -->
+      <map code="0x1d454" name="u1D454"/><!-- MATHEMATICAL ITALIC SMALL G -->
+      <map code="0x1d456" name="u1D456"/><!-- MATHEMATICAL ITALIC SMALL I -->
+      <map code="0x1d457" name="u1D457"/><!-- MATHEMATICAL ITALIC SMALL J -->
+      <map code="0x1d458" name="u1D458"/><!-- MATHEMATICAL ITALIC SMALL K -->
+      <map code="0x1d459" name="u1D459"/><!-- MATHEMATICAL ITALIC SMALL L -->
+      <map code="0x1d45a" name="u1D45A"/><!-- MATHEMATICAL ITALIC SMALL M -->
+      <map code="0x1d45b" name="u1D45B"/><!-- MATHEMATICAL ITALIC SMALL N -->
+      <map code="0x1d45c" name="u1D45C"/><!-- MATHEMATICAL ITALIC SMALL O -->
+      <map code="0x1d45d" name="u1D45D"/><!-- MATHEMATICAL ITALIC SMALL P -->
+      <map code="0x1d45e" name="u1D45E"/><!-- MATHEMATICAL ITALIC SMALL Q -->
+      <map code="0x1d45f" name="u1D45F"/><!-- MATHEMATICAL ITALIC SMALL R -->
+      <map code="0x1d460" name="u1D460"/><!-- MATHEMATICAL ITALIC SMALL S -->
+      <map code="0x1d461" name="u1D461"/><!-- MATHEMATICAL ITALIC SMALL T -->
+      <map code="0x1d462" name="u1D462"/><!-- MATHEMATICAL ITALIC SMALL U -->
+      <map code="0x1d463" name="u1D463"/><!-- MATHEMATICAL ITALIC SMALL V -->
+      <map code="0x1d464" name="u1D464"/><!-- MATHEMATICAL ITALIC SMALL W -->
+      <map code="0x1d465" name="u1D465"/><!-- MATHEMATICAL ITALIC SMALL X -->
+      <map code="0x1d466" name="u1D466"/><!-- MATHEMATICAL ITALIC SMALL Y -->
+      <map code="0x1d467" name="u1D467"/><!-- MATHEMATICAL ITALIC SMALL Z -->
+    </cmap_format_12>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x21" name="exclam"/><!-- EXCLAMATION MARK -->
+      <map code="0x22" name="quotedbl"/><!-- QUOTATION MARK -->
+      <map code="0x23" name="numbersign"/><!-- NUMBER SIGN -->
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x25" name="percent"/><!-- PERCENT SIGN -->
+      <map code="0x26" name="ampersand"/><!-- AMPERSAND -->
+      <map code="0x27" name="quotesingle"/><!-- APOSTROPHE -->
+      <map code="0x28" name="parenleft"/><!-- LEFT PARENTHESIS -->
+      <map code="0x29" name="parenright"/><!-- RIGHT PARENTHESIS -->
+      <map code="0x2a" name="asterisk"/><!-- ASTERISK -->
+      <map code="0x2b" name="plus"/><!-- PLUS SIGN -->
+      <map code="0x2c" name="comma"/><!-- COMMA -->
+      <map code="0x2d" name="hyphen"/><!-- HYPHEN-MINUS -->
+      <map code="0x2e" name="period"/><!-- FULL STOP -->
+      <map code="0x2f" name="slash"/><!-- SOLIDUS -->
+      <map code="0x30" name="zero"/><!-- DIGIT ZERO -->
+      <map code="0x31" name="one"/><!-- DIGIT ONE -->
+      <map code="0x32" name="two"/><!-- DIGIT TWO -->
+      <map code="0x33" name="three"/><!-- DIGIT THREE -->
+      <map code="0x34" name="four"/><!-- DIGIT FOUR -->
+      <map code="0x35" name="five"/><!-- DIGIT FIVE -->
+      <map code="0x36" name="six"/><!-- DIGIT SIX -->
+      <map code="0x37" name="seven"/><!-- DIGIT SEVEN -->
+      <map code="0x38" name="eight"/><!-- DIGIT EIGHT -->
+      <map code="0x39" name="nine"/><!-- DIGIT NINE -->
+      <map code="0x3a" name="colon"/><!-- COLON -->
+      <map code="0x3b" name="semicolon"/><!-- SEMICOLON -->
+      <map code="0x3c" name="less"/><!-- LESS-THAN SIGN -->
+      <map code="0x3d" name="equal"/><!-- EQUALS SIGN -->
+      <map code="0x3e" name="greater"/><!-- GREATER-THAN SIGN -->
+      <map code="0x3f" name="question"/><!-- QUESTION MARK -->
+      <map code="0x40" name="at"/><!-- COMMERCIAL AT -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x42" name="B"/><!-- LATIN CAPITAL LETTER B -->
+      <map code="0x43" name="C"/><!-- LATIN CAPITAL LETTER C -->
+      <map code="0x44" name="D"/><!-- LATIN CAPITAL LETTER D -->
+      <map code="0x45" name="E"/><!-- LATIN CAPITAL LETTER E -->
+      <map code="0x46" name="F"/><!-- LATIN CAPITAL LETTER F -->
+      <map code="0x47" name="G"/><!-- LATIN CAPITAL LETTER G -->
+      <map code="0x48" name="H"/><!-- LATIN CAPITAL LETTER H -->
+      <map code="0x49" name="I"/><!-- LATIN CAPITAL LETTER I -->
+      <map code="0x4a" name="J"/><!-- LATIN CAPITAL LETTER J -->
+      <map code="0x4b" name="K"/><!-- LATIN CAPITAL LETTER K -->
+      <map code="0x4c" name="L"/><!-- LATIN CAPITAL LETTER L -->
+      <map code="0x4d" name="M"/><!-- LATIN CAPITAL LETTER M -->
+      <map code="0x4e" name="N"/><!-- LATIN CAPITAL LETTER N -->
+      <map code="0x4f" name="O"/><!-- LATIN CAPITAL LETTER O -->
+      <map code="0x50" name="P"/><!-- LATIN CAPITAL LETTER P -->
+      <map code="0x51" name="Q"/><!-- LATIN CAPITAL LETTER Q -->
+      <map code="0x52" name="R"/><!-- LATIN CAPITAL LETTER R -->
+      <map code="0x53" name="S"/><!-- LATIN CAPITAL LETTER S -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0x55" name="U"/><!-- LATIN CAPITAL LETTER U -->
+      <map code="0x56" name="V"/><!-- LATIN CAPITAL LETTER V -->
+      <map code="0x57" name="W"/><!-- LATIN CAPITAL LETTER W -->
+      <map code="0x58" name="X"/><!-- LATIN CAPITAL LETTER X -->
+      <map code="0x59" name="Y"/><!-- LATIN CAPITAL LETTER Y -->
+      <map code="0x5a" name="Z"/><!-- LATIN CAPITAL LETTER Z -->
+      <map code="0x300" name="uni0300"/><!-- COMBINING GRAVE ACCENT -->
+      <map code="0x301" name="uni0301"/><!-- COMBINING ACUTE ACCENT -->
+      <map code="0x302" name="uni0302"/><!-- COMBINING CIRCUMFLEX ACCENT -->
+      <map code="0x303" name="uni0303"/><!-- COMBINING TILDE -->
+      <map code="0x304" name="uni0304"/><!-- COMBINING MACRON -->
+      <map code="0x305" name="uni0305"/><!-- COMBINING OVERLINE -->
+      <map code="0x306" name="uni0306"/><!-- COMBINING BREVE -->
+      <map code="0x307" name="uni0307"/><!-- COMBINING DOT ABOVE -->
+      <map code="0x308" name="uni0308"/><!-- COMBINING DIAERESIS -->
+      <map code="0x309" name="uni0309"/><!-- COMBINING HOOK ABOVE -->
+      <map code="0x30a" name="uni030A"/><!-- COMBINING RING ABOVE -->
+      <map code="0x30b" name="uni030B"/><!-- COMBINING DOUBLE ACUTE ACCENT -->
+      <map code="0x30c" name="uni030C"/><!-- COMBINING CARON -->
+      <map code="0x30d" name="uni030D"/><!-- COMBINING VERTICAL LINE ABOVE -->
+      <map code="0x30e" name="uni030E"/><!-- COMBINING DOUBLE VERTICAL LINE ABOVE -->
+      <map code="0x30f" name="uni030F"/><!-- COMBINING DOUBLE GRAVE ACCENT -->
+      <map code="0x310" name="uni0310"/><!-- COMBINING CANDRABINDU -->
+      <map code="0x311" name="uni0311"/><!-- COMBINING INVERTED BREVE -->
+      <map code="0x312" name="uni0312"/><!-- COMBINING TURNED COMMA ABOVE -->
+      <map code="0x313" name="uni0313"/><!-- COMBINING COMMA ABOVE -->
+      <map code="0x314" name="uni0314"/><!-- COMBINING REVERSED COMMA ABOVE -->
+      <map code="0x315" name="uni0315"/><!-- COMBINING COMMA ABOVE RIGHT -->
+      <map code="0x316" name="uni0316"/><!-- COMBINING GRAVE ACCENT BELOW -->
+      <map code="0x317" name="uni0317"/><!-- COMBINING ACUTE ACCENT BELOW -->
+      <map code="0x318" name="uni0318"/><!-- COMBINING LEFT TACK BELOW -->
+      <map code="0x319" name="uni0319"/><!-- COMBINING RIGHT TACK BELOW -->
+      <map code="0x31a" name="uni031A"/><!-- COMBINING LEFT ANGLE ABOVE -->
+      <map code="0x31b" name="uni031B"/><!-- COMBINING HORN -->
+      <map code="0x31c" name="uni031C"/><!-- COMBINING LEFT HALF RING BELOW -->
+      <map code="0x31d" name="uni031D"/><!-- COMBINING UP TACK BELOW -->
+      <map code="0x31e" name="uni031E"/><!-- COMBINING DOWN TACK BELOW -->
+      <map code="0x31f" name="uni031F"/><!-- COMBINING PLUS SIGN BELOW -->
+      <map code="0x320" name="uni0320"/><!-- COMBINING MINUS SIGN BELOW -->
+      <map code="0x321" name="uni0321"/><!-- COMBINING PALATALIZED HOOK BELOW -->
+      <map code="0x322" name="uni0322"/><!-- COMBINING RETROFLEX HOOK BELOW -->
+      <map code="0x323" name="uni0323"/><!-- COMBINING DOT BELOW -->
+      <map code="0x324" name="uni0324"/><!-- COMBINING DIAERESIS BELOW -->
+      <map code="0x325" name="uni0325"/><!-- COMBINING RING BELOW -->
+      <map code="0x326" name="uni0326"/><!-- COMBINING COMMA BELOW -->
+      <map code="0x327" name="uni0327"/><!-- COMBINING CEDILLA -->
+      <map code="0x328" name="uni0328"/><!-- COMBINING OGONEK -->
+      <map code="0x329" name="uni0329"/><!-- COMBINING VERTICAL LINE BELOW -->
+      <map code="0x32a" name="uni032A"/><!-- COMBINING BRIDGE BELOW -->
+      <map code="0x32b" name="uni032B"/><!-- COMBINING INVERTED DOUBLE ARCH BELOW -->
+      <map code="0x32c" name="uni032C"/><!-- COMBINING CARON BELOW -->
+      <map code="0x32d" name="uni032D"/><!-- COMBINING CIRCUMFLEX ACCENT BELOW -->
+      <map code="0x32e" name="uni032E"/><!-- COMBINING BREVE BELOW -->
+      <map code="0x32f" name="uni032F"/><!-- COMBINING INVERTED BREVE BELOW -->
+      <map code="0x330" name="uni0330"/><!-- COMBINING TILDE BELOW -->
+      <map code="0x331" name="uni0331"/><!-- COMBINING MACRON BELOW -->
+      <map code="0x332" name="uni0332"/><!-- COMBINING LOW LINE -->
+      <map code="0x333" name="uni0333"/><!-- COMBINING DOUBLE LOW LINE -->
+      <map code="0x334" name="uni0334"/><!-- COMBINING TILDE OVERLAY -->
+      <map code="0x335" name="uni0335"/><!-- COMBINING SHORT STROKE OVERLAY -->
+      <map code="0x336" name="uni0336"/><!-- COMBINING LONG STROKE OVERLAY -->
+      <map code="0x337" name="uni0337"/><!-- COMBINING SHORT SOLIDUS OVERLAY -->
+      <map code="0x338" name="uni0338"/><!-- COMBINING LONG SOLIDUS OVERLAY -->
+      <map code="0x339" name="uni0339"/><!-- COMBINING RIGHT HALF RING BELOW -->
+      <map code="0x33a" name="uni033A"/><!-- COMBINING INVERTED BRIDGE BELOW -->
+      <map code="0x33b" name="uni033B"/><!-- COMBINING SQUARE BELOW -->
+      <map code="0x33c" name="uni033C"/><!-- COMBINING SEAGULL BELOW -->
+      <map code="0x33d" name="uni033D"/><!-- COMBINING X ABOVE -->
+      <map code="0x33e" name="uni033E"/><!-- COMBINING VERTICAL TILDE -->
+      <map code="0x33f" name="uni033F"/><!-- COMBINING DOUBLE OVERLINE -->
+      <map code="0x346" name="uni0346"/><!-- COMBINING BRIDGE ABOVE -->
+      <map code="0x347" name="uni0347"/><!-- COMBINING EQUALS SIGN BELOW -->
+      <map code="0x34c" name="uni034C"/><!-- COMBINING ALMOST EQUAL TO ABOVE -->
+      <map code="0x34d" name="uni034D"/><!-- COMBINING LEFT RIGHT ARROW BELOW -->
+      <map code="0x359" name="uni0359"/><!-- COMBINING ASTERISK BELOW -->
+      <map code="0x35c" name="uni035C"/><!-- COMBINING DOUBLE BREVE BELOW -->
+      <map code="0x360" name="uni0360"/><!-- COMBINING DOUBLE TILDE -->
+      <map code="0x361" name="uni0361"/><!-- COMBINING DOUBLE INVERTED BREVE -->
+      <map code="0x362" name="uni0362"/><!-- COMBINING DOUBLE RIGHTWARDS ARROW BELOW -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="124" language="0" nGroups="9">
+      <map code="0x21" name="exclam"/><!-- EXCLAMATION MARK -->
+      <map code="0x22" name="quotedbl"/><!-- QUOTATION MARK -->
+      <map code="0x23" name="numbersign"/><!-- NUMBER SIGN -->
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x25" name="percent"/><!-- PERCENT SIGN -->
+      <map code="0x26" name="ampersand"/><!-- AMPERSAND -->
+      <map code="0x27" name="quotesingle"/><!-- APOSTROPHE -->
+      <map code="0x28" name="parenleft"/><!-- LEFT PARENTHESIS -->
+      <map code="0x29" name="parenright"/><!-- RIGHT PARENTHESIS -->
+      <map code="0x2a" name="asterisk"/><!-- ASTERISK -->
+      <map code="0x2b" name="plus"/><!-- PLUS SIGN -->
+      <map code="0x2c" name="comma"/><!-- COMMA -->
+      <map code="0x2d" name="hyphen"/><!-- HYPHEN-MINUS -->
+      <map code="0x2e" name="period"/><!-- FULL STOP -->
+      <map code="0x2f" name="slash"/><!-- SOLIDUS -->
+      <map code="0x30" name="zero"/><!-- DIGIT ZERO -->
+      <map code="0x31" name="one"/><!-- DIGIT ONE -->
+      <map code="0x32" name="two"/><!-- DIGIT TWO -->
+      <map code="0x33" name="three"/><!-- DIGIT THREE -->
+      <map code="0x34" name="four"/><!-- DIGIT FOUR -->
+      <map code="0x35" name="five"/><!-- DIGIT FIVE -->
+      <map code="0x36" name="six"/><!-- DIGIT SIX -->
+      <map code="0x37" name="seven"/><!-- DIGIT SEVEN -->
+      <map code="0x38" name="eight"/><!-- DIGIT EIGHT -->
+      <map code="0x39" name="nine"/><!-- DIGIT NINE -->
+      <map code="0x3a" name="colon"/><!-- COLON -->
+      <map code="0x3b" name="semicolon"/><!-- SEMICOLON -->
+      <map code="0x3c" name="less"/><!-- LESS-THAN SIGN -->
+      <map code="0x3d" name="equal"/><!-- EQUALS SIGN -->
+      <map code="0x3e" name="greater"/><!-- GREATER-THAN SIGN -->
+      <map code="0x3f" name="question"/><!-- QUESTION MARK -->
+      <map code="0x40" name="at"/><!-- COMMERCIAL AT -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x42" name="B"/><!-- LATIN CAPITAL LETTER B -->
+      <map code="0x43" name="C"/><!-- LATIN CAPITAL LETTER C -->
+      <map code="0x44" name="D"/><!-- LATIN CAPITAL LETTER D -->
+      <map code="0x45" name="E"/><!-- LATIN CAPITAL LETTER E -->
+      <map code="0x46" name="F"/><!-- LATIN CAPITAL LETTER F -->
+      <map code="0x47" name="G"/><!-- LATIN CAPITAL LETTER G -->
+      <map code="0x48" name="H"/><!-- LATIN CAPITAL LETTER H -->
+      <map code="0x49" name="I"/><!-- LATIN CAPITAL LETTER I -->
+      <map code="0x4a" name="J"/><!-- LATIN CAPITAL LETTER J -->
+      <map code="0x4b" name="K"/><!-- LATIN CAPITAL LETTER K -->
+      <map code="0x4c" name="L"/><!-- LATIN CAPITAL LETTER L -->
+      <map code="0x4d" name="M"/><!-- LATIN CAPITAL LETTER M -->
+      <map code="0x4e" name="N"/><!-- LATIN CAPITAL LETTER N -->
+      <map code="0x4f" name="O"/><!-- LATIN CAPITAL LETTER O -->
+      <map code="0x50" name="P"/><!-- LATIN CAPITAL LETTER P -->
+      <map code="0x51" name="Q"/><!-- LATIN CAPITAL LETTER Q -->
+      <map code="0x52" name="R"/><!-- LATIN CAPITAL LETTER R -->
+      <map code="0x53" name="S"/><!-- LATIN CAPITAL LETTER S -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0x55" name="U"/><!-- LATIN CAPITAL LETTER U -->
+      <map code="0x56" name="V"/><!-- LATIN CAPITAL LETTER V -->
+      <map code="0x57" name="W"/><!-- LATIN CAPITAL LETTER W -->
+      <map code="0x58" name="X"/><!-- LATIN CAPITAL LETTER X -->
+      <map code="0x59" name="Y"/><!-- LATIN CAPITAL LETTER Y -->
+      <map code="0x5a" name="Z"/><!-- LATIN CAPITAL LETTER Z -->
+      <map code="0x300" name="uni0300"/><!-- COMBINING GRAVE ACCENT -->
+      <map code="0x301" name="uni0301"/><!-- COMBINING ACUTE ACCENT -->
+      <map code="0x302" name="uni0302"/><!-- COMBINING CIRCUMFLEX ACCENT -->
+      <map code="0x303" name="uni0303"/><!-- COMBINING TILDE -->
+      <map code="0x304" name="uni0304"/><!-- COMBINING MACRON -->
+      <map code="0x305" name="uni0305"/><!-- COMBINING OVERLINE -->
+      <map code="0x306" name="uni0306"/><!-- COMBINING BREVE -->
+      <map code="0x307" name="uni0307"/><!-- COMBINING DOT ABOVE -->
+      <map code="0x308" name="uni0308"/><!-- COMBINING DIAERESIS -->
+      <map code="0x309" name="uni0309"/><!-- COMBINING HOOK ABOVE -->
+      <map code="0x30a" name="uni030A"/><!-- COMBINING RING ABOVE -->
+      <map code="0x30b" name="uni030B"/><!-- COMBINING DOUBLE ACUTE ACCENT -->
+      <map code="0x30c" name="uni030C"/><!-- COMBINING CARON -->
+      <map code="0x30d" name="uni030D"/><!-- COMBINING VERTICAL LINE ABOVE -->
+      <map code="0x30e" name="uni030E"/><!-- COMBINING DOUBLE VERTICAL LINE ABOVE -->
+      <map code="0x30f" name="uni030F"/><!-- COMBINING DOUBLE GRAVE ACCENT -->
+      <map code="0x310" name="uni0310"/><!-- COMBINING CANDRABINDU -->
+      <map code="0x311" name="uni0311"/><!-- COMBINING INVERTED BREVE -->
+      <map code="0x312" name="uni0312"/><!-- COMBINING TURNED COMMA ABOVE -->
+      <map code="0x313" name="uni0313"/><!-- COMBINING COMMA ABOVE -->
+      <map code="0x314" name="uni0314"/><!-- COMBINING REVERSED COMMA ABOVE -->
+      <map code="0x315" name="uni0315"/><!-- COMBINING COMMA ABOVE RIGHT -->
+      <map code="0x316" name="uni0316"/><!-- COMBINING GRAVE ACCENT BELOW -->
+      <map code="0x317" name="uni0317"/><!-- COMBINING ACUTE ACCENT BELOW -->
+      <map code="0x318" name="uni0318"/><!-- COMBINING LEFT TACK BELOW -->
+      <map code="0x319" name="uni0319"/><!-- COMBINING RIGHT TACK BELOW -->
+      <map code="0x31a" name="uni031A"/><!-- COMBINING LEFT ANGLE ABOVE -->
+      <map code="0x31b" name="uni031B"/><!-- COMBINING HORN -->
+      <map code="0x31c" name="uni031C"/><!-- COMBINING LEFT HALF RING BELOW -->
+      <map code="0x31d" name="uni031D"/><!-- COMBINING UP TACK BELOW -->
+      <map code="0x31e" name="uni031E"/><!-- COMBINING DOWN TACK BELOW -->
+      <map code="0x31f" name="uni031F"/><!-- COMBINING PLUS SIGN BELOW -->
+      <map code="0x320" name="uni0320"/><!-- COMBINING MINUS SIGN BELOW -->
+      <map code="0x321" name="uni0321"/><!-- COMBINING PALATALIZED HOOK BELOW -->
+      <map code="0x322" name="uni0322"/><!-- COMBINING RETROFLEX HOOK BELOW -->
+      <map code="0x323" name="uni0323"/><!-- COMBINING DOT BELOW -->
+      <map code="0x324" name="uni0324"/><!-- COMBINING DIAERESIS BELOW -->
+      <map code="0x325" name="uni0325"/><!-- COMBINING RING BELOW -->
+      <map code="0x326" name="uni0326"/><!-- COMBINING COMMA BELOW -->
+      <map code="0x327" name="uni0327"/><!-- COMBINING CEDILLA -->
+      <map code="0x328" name="uni0328"/><!-- COMBINING OGONEK -->
+      <map code="0x329" name="uni0329"/><!-- COMBINING VERTICAL LINE BELOW -->
+      <map code="0x32a" name="uni032A"/><!-- COMBINING BRIDGE BELOW -->
+      <map code="0x32b" name="uni032B"/><!-- COMBINING INVERTED DOUBLE ARCH BELOW -->
+      <map code="0x32c" name="uni032C"/><!-- COMBINING CARON BELOW -->
+      <map code="0x32d" name="uni032D"/><!-- COMBINING CIRCUMFLEX ACCENT BELOW -->
+      <map code="0x32e" name="uni032E"/><!-- COMBINING BREVE BELOW -->
+      <map code="0x32f" name="uni032F"/><!-- COMBINING INVERTED BREVE BELOW -->
+      <map code="0x330" name="uni0330"/><!-- COMBINING TILDE BELOW -->
+      <map code="0x331" name="uni0331"/><!-- COMBINING MACRON BELOW -->
+      <map code="0x332" name="uni0332"/><!-- COMBINING LOW LINE -->
+      <map code="0x333" name="uni0333"/><!-- COMBINING DOUBLE LOW LINE -->
+      <map code="0x334" name="uni0334"/><!-- COMBINING TILDE OVERLAY -->
+      <map code="0x335" name="uni0335"/><!-- COMBINING SHORT STROKE OVERLAY -->
+      <map code="0x336" name="uni0336"/><!-- COMBINING LONG STROKE OVERLAY -->
+      <map code="0x337" name="uni0337"/><!-- COMBINING SHORT SOLIDUS OVERLAY -->
+      <map code="0x338" name="uni0338"/><!-- COMBINING LONG SOLIDUS OVERLAY -->
+      <map code="0x339" name="uni0339"/><!-- COMBINING RIGHT HALF RING BELOW -->
+      <map code="0x33a" name="uni033A"/><!-- COMBINING INVERTED BRIDGE BELOW -->
+      <map code="0x33b" name="uni033B"/><!-- COMBINING SQUARE BELOW -->
+      <map code="0x33c" name="uni033C"/><!-- COMBINING SEAGULL BELOW -->
+      <map code="0x33d" name="uni033D"/><!-- COMBINING X ABOVE -->
+      <map code="0x33e" name="uni033E"/><!-- COMBINING VERTICAL TILDE -->
+      <map code="0x33f" name="uni033F"/><!-- COMBINING DOUBLE OVERLINE -->
+      <map code="0x346" name="uni0346"/><!-- COMBINING BRIDGE ABOVE -->
+      <map code="0x347" name="uni0347"/><!-- COMBINING EQUALS SIGN BELOW -->
+      <map code="0x34c" name="uni034C"/><!-- COMBINING ALMOST EQUAL TO ABOVE -->
+      <map code="0x34d" name="uni034D"/><!-- COMBINING LEFT RIGHT ARROW BELOW -->
+      <map code="0x359" name="uni0359"/><!-- COMBINING ASTERISK BELOW -->
+      <map code="0x35c" name="uni035C"/><!-- COMBINING DOUBLE BREVE BELOW -->
+      <map code="0x360" name="uni0360"/><!-- COMBINING DOUBLE TILDE -->
+      <map code="0x361" name="uni0361"/><!-- COMBINING DOUBLE INVERTED BREVE -->
+      <map code="0x362" name="uni0362"/><!-- COMBINING DOUBLE RIGHTWARDS ARROW BELOW -->
+      <map code="0x1d400" name="u1D400"/><!-- MATHEMATICAL BOLD CAPITAL A -->
+      <map code="0x1d401" name="u1D401"/><!-- MATHEMATICAL BOLD CAPITAL B -->
+      <map code="0x1d402" name="u1D402"/><!-- MATHEMATICAL BOLD CAPITAL C -->
+      <map code="0x1d403" name="u1D403"/><!-- MATHEMATICAL BOLD CAPITAL D -->
+      <map code="0x1d404" name="u1D404"/><!-- MATHEMATICAL BOLD CAPITAL E -->
+      <map code="0x1d405" name="u1D405"/><!-- MATHEMATICAL BOLD CAPITAL F -->
+      <map code="0x1d406" name="u1D406"/><!-- MATHEMATICAL BOLD CAPITAL G -->
+      <map code="0x1d407" name="u1D407"/><!-- MATHEMATICAL BOLD CAPITAL H -->
+      <map code="0x1d408" name="u1D408"/><!-- MATHEMATICAL BOLD CAPITAL I -->
+      <map code="0x1d409" name="u1D409"/><!-- MATHEMATICAL BOLD CAPITAL J -->
+      <map code="0x1d40a" name="u1D40A"/><!-- MATHEMATICAL BOLD CAPITAL K -->
+      <map code="0x1d40b" name="u1D40B"/><!-- MATHEMATICAL BOLD CAPITAL L -->
+      <map code="0x1d40c" name="u1D40C"/><!-- MATHEMATICAL BOLD CAPITAL M -->
+      <map code="0x1d40d" name="u1D40D"/><!-- MATHEMATICAL BOLD CAPITAL N -->
+      <map code="0x1d40e" name="u1D40E"/><!-- MATHEMATICAL BOLD CAPITAL O -->
+      <map code="0x1d40f" name="u1D40F"/><!-- MATHEMATICAL BOLD CAPITAL P -->
+      <map code="0x1d410" name="u1D410"/><!-- MATHEMATICAL BOLD CAPITAL Q -->
+      <map code="0x1d411" name="u1D411"/><!-- MATHEMATICAL BOLD CAPITAL R -->
+      <map code="0x1d412" name="u1D412"/><!-- MATHEMATICAL BOLD CAPITAL S -->
+      <map code="0x1d413" name="u1D413"/><!-- MATHEMATICAL BOLD CAPITAL T -->
+      <map code="0x1d414" name="u1D414"/><!-- MATHEMATICAL BOLD CAPITAL U -->
+      <map code="0x1d415" name="u1D415"/><!-- MATHEMATICAL BOLD CAPITAL V -->
+      <map code="0x1d416" name="u1D416"/><!-- MATHEMATICAL BOLD CAPITAL W -->
+      <map code="0x1d417" name="u1D417"/><!-- MATHEMATICAL BOLD CAPITAL X -->
+      <map code="0x1d418" name="u1D418"/><!-- MATHEMATICAL BOLD CAPITAL Y -->
+      <map code="0x1d419" name="u1D419"/><!-- MATHEMATICAL BOLD CAPITAL Z -->
+      <map code="0x1d41a" name="u1D41A"/><!-- MATHEMATICAL BOLD SMALL A -->
+      <map code="0x1d41b" name="u1D41B"/><!-- MATHEMATICAL BOLD SMALL B -->
+      <map code="0x1d41c" name="u1D41C"/><!-- MATHEMATICAL BOLD SMALL C -->
+      <map code="0x1d41d" name="u1D41D"/><!-- MATHEMATICAL BOLD SMALL D -->
+      <map code="0x1d41e" name="u1D41E"/><!-- MATHEMATICAL BOLD SMALL E -->
+      <map code="0x1d41f" name="u1D41F"/><!-- MATHEMATICAL BOLD SMALL F -->
+      <map code="0x1d420" name="u1D420"/><!-- MATHEMATICAL BOLD SMALL G -->
+      <map code="0x1d421" name="u1D421"/><!-- MATHEMATICAL BOLD SMALL H -->
+      <map code="0x1d422" name="u1D422"/><!-- MATHEMATICAL BOLD SMALL I -->
+      <map code="0x1d423" name="u1D423"/><!-- MATHEMATICAL BOLD SMALL J -->
+      <map code="0x1d424" name="u1D424"/><!-- MATHEMATICAL BOLD SMALL K -->
+      <map code="0x1d425" name="u1D425"/><!-- MATHEMATICAL BOLD SMALL L -->
+      <map code="0x1d426" name="u1D426"/><!-- MATHEMATICAL BOLD SMALL M -->
+      <map code="0x1d427" name="u1D427"/><!-- MATHEMATICAL BOLD SMALL N -->
+      <map code="0x1d428" name="u1D428"/><!-- MATHEMATICAL BOLD SMALL O -->
+      <map code="0x1d429" name="u1D429"/><!-- MATHEMATICAL BOLD SMALL P -->
+      <map code="0x1d42a" name="u1D42A"/><!-- MATHEMATICAL BOLD SMALL Q -->
+      <map code="0x1d42b" name="u1D42B"/><!-- MATHEMATICAL BOLD SMALL R -->
+      <map code="0x1d42c" name="u1D42C"/><!-- MATHEMATICAL BOLD SMALL S -->
+      <map code="0x1d42d" name="u1D42D"/><!-- MATHEMATICAL BOLD SMALL T -->
+      <map code="0x1d42e" name="u1D42E"/><!-- MATHEMATICAL BOLD SMALL U -->
+      <map code="0x1d42f" name="u1D42F"/><!-- MATHEMATICAL BOLD SMALL V -->
+      <map code="0x1d430" name="u1D430"/><!-- MATHEMATICAL BOLD SMALL W -->
+      <map code="0x1d431" name="u1D431"/><!-- MATHEMATICAL BOLD SMALL X -->
+      <map code="0x1d432" name="u1D432"/><!-- MATHEMATICAL BOLD SMALL Y -->
+      <map code="0x1d433" name="u1D433"/><!-- MATHEMATICAL BOLD SMALL Z -->
+      <map code="0x1d434" name="u1D434"/><!-- MATHEMATICAL ITALIC CAPITAL A -->
+      <map code="0x1d435" name="u1D435"/><!-- MATHEMATICAL ITALIC CAPITAL B -->
+      <map code="0x1d436" name="u1D436"/><!-- MATHEMATICAL ITALIC CAPITAL C -->
+      <map code="0x1d437" name="u1D437"/><!-- MATHEMATICAL ITALIC CAPITAL D -->
+      <map code="0x1d438" name="u1D438"/><!-- MATHEMATICAL ITALIC CAPITAL E -->
+      <map code="0x1d439" name="u1D439"/><!-- MATHEMATICAL ITALIC CAPITAL F -->
+      <map code="0x1d43a" name="u1D43A"/><!-- MATHEMATICAL ITALIC CAPITAL G -->
+      <map code="0x1d43b" name="u1D43B"/><!-- MATHEMATICAL ITALIC CAPITAL H -->
+      <map code="0x1d43c" name="u1D43C"/><!-- MATHEMATICAL ITALIC CAPITAL I -->
+      <map code="0x1d43d" name="u1D43D"/><!-- MATHEMATICAL ITALIC CAPITAL J -->
+      <map code="0x1d43e" name="u1D43E"/><!-- MATHEMATICAL ITALIC CAPITAL K -->
+      <map code="0x1d43f" name="u1D43F"/><!-- MATHEMATICAL ITALIC CAPITAL L -->
+      <map code="0x1d440" name="u1D440"/><!-- MATHEMATICAL ITALIC CAPITAL M -->
+      <map code="0x1d441" name="u1D441"/><!-- MATHEMATICAL ITALIC CAPITAL N -->
+      <map code="0x1d442" name="u1D442"/><!-- MATHEMATICAL ITALIC CAPITAL O -->
+      <map code="0x1d443" name="u1D443"/><!-- MATHEMATICAL ITALIC CAPITAL P -->
+      <map code="0x1d444" name="u1D444"/><!-- MATHEMATICAL ITALIC CAPITAL Q -->
+      <map code="0x1d445" name="u1D445"/><!-- MATHEMATICAL ITALIC CAPITAL R -->
+      <map code="0x1d446" name="u1D446"/><!-- MATHEMATICAL ITALIC CAPITAL S -->
+      <map code="0x1d447" name="u1D447"/><!-- MATHEMATICAL ITALIC CAPITAL T -->
+      <map code="0x1d448" name="u1D448"/><!-- MATHEMATICAL ITALIC CAPITAL U -->
+      <map code="0x1d449" name="u1D449"/><!-- MATHEMATICAL ITALIC CAPITAL V -->
+      <map code="0x1d44a" name="u1D44A"/><!-- MATHEMATICAL ITALIC CAPITAL W -->
+      <map code="0x1d44b" name="u1D44B"/><!-- MATHEMATICAL ITALIC CAPITAL X -->
+      <map code="0x1d44c" name="u1D44C"/><!-- MATHEMATICAL ITALIC CAPITAL Y -->
+      <map code="0x1d44d" name="u1D44D"/><!-- MATHEMATICAL ITALIC CAPITAL Z -->
+      <map code="0x1d44e" name="u1D44E"/><!-- MATHEMATICAL ITALIC SMALL A -->
+      <map code="0x1d44f" name="u1D44F"/><!-- MATHEMATICAL ITALIC SMALL B -->
+      <map code="0x1d450" name="u1D450"/><!-- MATHEMATICAL ITALIC SMALL C -->
+      <map code="0x1d451" name="u1D451"/><!-- MATHEMATICAL ITALIC SMALL D -->
+      <map code="0x1d452" name="u1D452"/><!-- MATHEMATICAL ITALIC SMALL E -->
+      <map code="0x1d453" name="u1D453"/><!-- MATHEMATICAL ITALIC SMALL F -->
+      <map code="0x1d454" name="u1D454"/><!-- MATHEMATICAL ITALIC SMALL G -->
+      <map code="0x1d456" name="u1D456"/><!-- MATHEMATICAL ITALIC SMALL I -->
+      <map code="0x1d457" name="u1D457"/><!-- MATHEMATICAL ITALIC SMALL J -->
+      <map code="0x1d458" name="u1D458"/><!-- MATHEMATICAL ITALIC SMALL K -->
+      <map code="0x1d459" name="u1D459"/><!-- MATHEMATICAL ITALIC SMALL L -->
+      <map code="0x1d45a" name="u1D45A"/><!-- MATHEMATICAL ITALIC SMALL M -->
+      <map code="0x1d45b" name="u1D45B"/><!-- MATHEMATICAL ITALIC SMALL N -->
+      <map code="0x1d45c" name="u1D45C"/><!-- MATHEMATICAL ITALIC SMALL O -->
+      <map code="0x1d45d" name="u1D45D"/><!-- MATHEMATICAL ITALIC SMALL P -->
+      <map code="0x1d45e" name="u1D45E"/><!-- MATHEMATICAL ITALIC SMALL Q -->
+      <map code="0x1d45f" name="u1D45F"/><!-- MATHEMATICAL ITALIC SMALL R -->
+      <map code="0x1d460" name="u1D460"/><!-- MATHEMATICAL ITALIC SMALL S -->
+      <map code="0x1d461" name="u1D461"/><!-- MATHEMATICAL ITALIC SMALL T -->
+      <map code="0x1d462" name="u1D462"/><!-- MATHEMATICAL ITALIC SMALL U -->
+      <map code="0x1d463" name="u1D463"/><!-- MATHEMATICAL ITALIC SMALL V -->
+      <map code="0x1d464" name="u1D464"/><!-- MATHEMATICAL ITALIC SMALL W -->
+      <map code="0x1d465" name="u1D465"/><!-- MATHEMATICAL ITALIC SMALL X -->
+      <map code="0x1d466" name="u1D466"/><!-- MATHEMATICAL ITALIC SMALL Y -->
+      <map code="0x1d467" name="u1D467"/><!-- MATHEMATICAL ITALIC SMALL Z -->
+    </cmap_format_12>
+  </cmap>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <CFF>
+    <CFFFont name="XITSMath">
+      <version value="1.108"/>
+      <Notice value="Copyright (c) 2001-2011 by the STI Pub Companies, consisting of the American Chemical Society, the American Institute of Physics, the American Mathematical Society, the American Physical Society, Elsevier, Inc., and The Institute of Electrical and Electronic Engineers, Inc.  Portions copyright (c) 1998-2003 by MicroPress, Inc.  Portions copyright (c) 1990 by Elsevier, Inc.  Portions copyright (c) 2009-2012 by Khaled Hosny.  All rights reserved. "/>
+      <FullName value="XITS Math"/>
+      <FamilyName value="XITS Math"/>
+      <Weight value="Regular"/>
+      <isFixedPitch value="0"/>
+      <ItalicAngle value="0"/>
+      <UnderlinePosition value="-50"/>
+      <UnderlineThickness value="50"/>
+      <PaintType value="0"/>
+      <CharstringType value="2"/>
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FontBBox value="-970 -906 3238 2566"/>
+      <StrokeWidth value="0"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <Encoding name="StandardEncoding"/>
+      <Private>
+        <BlueValues value="-14 0 450 460 662 676"/>
+        <BlueScale value="0.039625"/>
+        <BlueShift value="6"/>
+        <BlueFuzz value="1"/>
+        <StdHW value="66"/>
+        <StdVW value="66"/>
+        <StemSnapH value="23 28 31 34 38 43 50 54 63 66"/>
+        <StemSnapV value="39 43 48 52 56 59 66 73 79 83"/>
+        <ForceBold value="0"/>
+        <LanguageGroup value="0"/>
+        <ExpansionFactor value="0.06"/>
+        <initialRandomSeed value="0"/>
+        <defaultWidthX value="685"/>
+        <nominalWidthX value="601"/>
+        <Subrs>
+          <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+          <CharString index="0">
+            14 0 45 381 rlineto
+            1 11 1 15 0 12 rrcurveto
+            49 -19 32 -34 -38 -15 -33 -43 vhcurveto
+            0 -11 1 -12 2 -20 rrcurveto
+            return
+          </CharString>
+          <CharString index="1">
+            30 -24 25 -29 -30 -23 -24 -30 -30 22 -22 30 31 23 22 29 vhcurveto
+            return
+          </CharString>
+          <CharString index="2">
+            -106 -65 -101 -69 -32 -24 29 39 vhcurveto
+            0 68 34 88 44 47 12 14 21 9 16 0 rrcurveto
+            38 25 -35 -52 hvcurveto
+            return
+          </CharString>
+          <CharString index="3">
+            -44 hlineto
+            -92 -89 -13 -8 -62 0 -37 0 -24 10 -35 29 -27 22 -12 6 -21 0 rrcurveto
+            -102 -91 -100 -113 hvcurveto
+            return
+          </CharString>
+          <CharString index="4">
+            -78 51 -59 67 vhcurveto
+            33 0 26 11 27 26 48 45 32 75 0 70 0 16 -1 11 -3 17 32 -11 18 -3 26 0 53 0 31 14 37 43 rrcurveto
+            -394 -658 47 0 rlineto
+            return
+          </CharString>
+          <CharString index="5">
+            -115 -61 -101 -71 -34 -23 28 42 vhcurveto
+            0 55 17 59 31 49 20 33 11 10 41 20 17 -18 13 -9 14 -7 21 -10 4 -7 0 -29 rrcurveto
+            return
+          </CharString>
+          <CharString index="6">
+            49 28 19 6 44 7 39 6 13 11 0 26 0 23 -15 15 -22 0 -22 0 -31 -41 -24 -24 -19 -18 -11 -8 -28 -16 rrcurveto
+            24 vlineto
+            0 24 5 27 12 32 7 20 4 15 0 12 rrcurveto
+            20 -17 16 -20 -20 -16 -15 -19 vhcurveto
+            0 -13 4 -18 7 -21 10 -33 5 -31 0 -27 rrcurveto
+            -13 vlineto
+            -38 21 -19 16 -32 36 -22 24 -11 8 -16 0 -20 0 -13 -16 0 -21 0 -25 18 -11 49 -10 34 -7 25 -7 38 -23 -41 -25 -16 -6 -46 -9 rrcurveto
+            -45 -8 -18 -11 0 -26 0 -21 14 -15 23 0 16 0 10 6 17 19 29 33 2 2 10 10 10 9 7 5 35 22 rrcurveto
+            -9 vlineto
+            0 -43 -4 -24 -12 -32 -7 -19 -5 -16 0 -12 rrcurveto
+            -19 17 -17 19 20 20 19 20 vhcurveto
+            0 7 -4 12 -5 14 -13 35 -7 36 0 39 rrcurveto
+            0 9 8 -5 rlineto
+            20 -13 22 -12 27 -33 25 -31 16 -13 19 0 21 0 15 17 0 22 0 26 -16 10 -44 7 -37 6 -32 10 -27 18 rrcurveto
+            return
+          </CharString>
+          <CharString index="7">
+            66 -261 261 -66 -261 -261 -66 261 -261 66 261 vlineto
+            return
+          </CharString>
+          <CharString index="8">
+            30 -26 27 -29 -31 -25 -25 -31 -31 24 -24 31 30 26 25 29 vhcurveto
+            return
+          </CharString>
+          <CharString index="9">
+            30 -25 26 -30 -32 -24 -24 -32 -30 26 -25 30 30 25 25 30 vhcurveto
+            return
+          </CharString>
+          <CharString index="10">
+            30 -25 26 -30 -32 -24 -24 -32 -30 26 -25 30 vhcurveto
+            30 25 25 30 hvcurveto
+            return
+          </CharString>
+          <CharString index="11">
+            70 33 42 58 0 63 rrcurveto
+            54 -35 35 -45 vhcurveto
+            return
+          </CharString>
+          <CharString index="12">
+            9 0 13 4 6 0 8 0 6 -7 0 -7 0 -33 -28 -37 -55 -36 rrcurveto
+            return
+          </CharString>
+          <CharString index="13">
+            0 63 -432 215 432 215 0 65 -565 -275 0 -10 rlineto
+            return
+          </CharString>
+          <CharString index="14">
+            66 -589 -66 vlineto
+            return
+          </CharString>
+          <CharString index="15">
+            0 10 -565 275 0 -65 432 -215 -432 -215 0 -63 rlineto
+            return
+          </CharString>
+          <CharString index="16">
+            11 68 18 46 36 43 86 102 19 34 0 61 rrcurveto
+            97 -85 61 -97 -96 -68 -60 -83 -41 20 -25 28 25 16 18 25 vhcurveto
+            0 33 -38 4 0 37 rrcurveto
+            32 46 30 48 61 48 -49 -69 vhcurveto
+            0 -60 -25 -63 -26 -64 -35 -87 -8 -50 -1 -40 rrcurveto
+            return
+          </CharString>
+          <CharString index="17">
+            28 -24 25 -28 -31 -23 -23 -30 -28 20 -25 34 30 22 24 29 vhcurveto
+            return
+          </CharString>
+          <CharString index="18">
+            19 vlineto
+            -52 6 -14 21 -28 65 rrcurveto
+            -246 563 -20 0 -206 -488 rlineto
+            -59 -140 -9 -21 -58 -6 rrcurveto
+            -19 199 19 vlineto
+            -48 -22 10 31 hvcurveto
+            0 12 4 17 5 13 rrcurveto
+            46 114 262 0 41 -94 rlineto
+            12 -28 7 -27 0 -15 0 -9 -6 -11 -8 -4 -12 -7 -7 -2 -36 0 rrcurveto
+            -19 vlineto
+            return
+          </CharString>
+          <CharString index="19">
+            -231 0 115 275 rlineto
+            return
+          </CharString>
+          <CharString index="20">
+            -75 -71 -55 -30 -90 0 -69 0 -65 25 -44 47 -47 51 -25 78 0 110 0 173 89 122 151 0 65 0 52 -29 44 -47 25 -27 15 -29 12 -55 rrcurveto
+            23 0 -9 227 -20 0 rlineto
+            -6 -21 -17 -12 -19 0 -17 0 -31 12 -21 6 -40 11 -40 4 -39 0 -93 0 -86 -33 -64 -71 -57 -64 -33 -83 0 -100 0 -99 35 -91 61 -60 rrcurveto
+            58 -57 87 -32 91 0 118 0 97 44 58 83 rrcurveto
+            return
+          </CharString>
+          <CharString index="21">
+            -19 vlineto
+            76 -5 12 -9 0 -79 rrcurveto
+            -435 vlineto
+            0 -78 -10 -13 -78 -5 rrcurveto
+            -19 281 vlineto
+            101 0 89 24 62 40 89 57 47 94 0 119 0 103 -34 78 -65 56 -68 59 -103 32 -129 0 rrcurveto
+            return
+          </CharString>
+          <CharString index="22">
+            33 15 9 38 vhcurveto
+            100 0 63 -17 59 -52 63 -55 32 -69 0 -104 0 -113 -34 -79 -70 -47 -60 -40 -55 -12 -103 0 rrcurveto
+            -39 -9 12 35 hvcurveto
+            return
+          </CharString>
+          <CharString index="23">
+            -28 hlineto
+            -45 -106 -43 -26 -143 0 rrcurveto
+            -36 hlineto
+            -74 -27 5 42 hvcurveto
+            243 151 vlineto
+            86 0 15 -13 12 -84 rrcurveto
+            23 234 -23 hlineto
+            -12 -79 -15 -17 -86 0 rrcurveto
+            -151 220 hlineto
+            32 7 4 28 vhcurveto
+            131 hlineto
+            116 0 23 -15 15 -90 rrcurveto
+            25 0 -4 143 -530 0 0 -19 rlineto
+            75 -4 12 -15 0 -74 rrcurveto
+            -436 vlineto
+            0 -77 -10 -13 -77 -5 rrcurveto
+            -19 539 vlineto
+            return
+          </CharString>
+          <CharString index="24">
+            -256 -18 hlineto
+            79 -6 10 -8 0 -78 rrcurveto
+            -157 vlineto
+            -33 -56 -28 -77 -157 -106 112 186 vhcurveto
+            0 99 24 91 53 53 43 44 55 25 69 0 56 0 52 -19 38 -36 30 -29 16 -26 25 -61 rrcurveto
+            23 0 -8 211 -22 0 rlineto
+            -6 -19 -18 -14 -21 0 -10 0 -15 3 -19 7 -45 15 -34 8 -54 0 -97 0 -82 -31 -60 -55 -67 -61 -40 -92 0 -110 0 -98 27 -77 56 -59 rrcurveto
+            66 -69 102 -38 102 0 103 0 96 25 55 45 rrcurveto
+            200 vlineto
+            0 65 12 10 58 5 rrcurveto
+            return
+          </CharString>
+          <CharString index="25">
+            19 vlineto
+            -75 6 -14 15 0 71 rrcurveto
+            437 vlineto
+            0 77 15 12 74 6 rrcurveto
+            19 -280 -19 vlineto
+            76 -6 13 -10 0 -79 rrcurveto
+            -189 -303 189 vlineto
+            0 78 16 11 73 6 rrcurveto
+            19 -280 -19 vlineto
+            77 -6 12 -11 0 -78 rrcurveto
+            -426 vlineto
+            0 -86 -11 -12 -78 -5 rrcurveto
+            -19 279 vlineto
+            -1 19 rlineto
+            -74 4 -13 17 0 73 rrcurveto
+            202 303 -191 vlineto
+            0 -84 -10 -16 -78 -5 rrcurveto
+            -19 vlineto
+            return
+          </CharString>
+          <CharString index="26">
+            19 vlineto
+            -82 3 -16 15 0 75 rrcurveto
+            439 vlineto
+            0 76 14 12 84 4 rrcurveto
+            19 -297 -19 vlineto
+            85 -5 12 -8 0 -79 rrcurveto
+            -439 vlineto
+            0 -78 -13 -12 -84 -3 rrcurveto
+            -19 vlineto
+            return
+          </CharString>
+          <CharString index="27">
+            -287 -19 hlineto
+            81 -7 12 -5 0 -81 rrcurveto
+            -456 vlineto
+            -50 -6 -20 -31 vhcurveto
+            -20 0 -1 19 -10 25 -11 28 -15 14 -23 0 rrcurveto
+            -27 -22 -26 -26 -47 41 -25 60 106 61 62 137 hvcurveto
+            364 vlineto
+            0 77 11 11 81 6 rrcurveto
+            return
+          </CharString>
+          <CharString index="28">
+            19 vlineto
+            -46 0 -27 18 -83 91 rrcurveto
+            -234 256 186 178 rlineto
+            69 66 19 10 68 5 rrcurveto
+            19 -259 -19 vlineto
+            25 -1 rlineto
+            33 -1 8 -10 0 -20 0 -25 -30 -29 -48 -45 rrcurveto
+            -178 -164 0 200 rlineto
+            0 77 7 12 83 6 rrcurveto
+            19 -282 -19 vlineto
+            79 -5 11 -15 0 -74 rrcurveto
+            -424 vlineto
+            0 -87 -12 -15 -79 -4 rrcurveto
+            -19 282 19 vlineto
+            -73 6 -16 9 0 77 rrcurveto
+            0 185 26 21 100 -103 rlineto
+            79 -82 58 -61 0 -27 0 -14 -13 -9 -29 -1 rrcurveto
+            -27 -1 0 -19 rlineto
+            return
+          </CharString>
+          <CharString index="29">
+            -26 hlineto
+            -16 -35 -13 -22 -13 -17 -34 -43 -54 -18 -81 0 rrcurveto
+            -70 hlineto
+            -73 -17 7 38 hvcurveto
+            464 vlineto
+            0 77 12 13 82 5 rrcurveto
+            19 -283 -19 vlineto
+            78 -6 9 -14 0 -77 rrcurveto
+            -431 vlineto
+            0 -76 -13 -16 -74 -4 rrcurveto
+            -19 538 vlineto
+            return
+          </CharString>
+          <CharString index="30">
+            19 vlineto
+            -73 5 -15 17 0 71 rrcurveto
+            438 vlineto
+            0 75 15 17 72 1 rrcurveto
+            19 -199 vlineto
+            -221 -502 -231 502 -198 0 0 -19 rlineto
+            82 -5 13 -13 0 -76 rrcurveto
+            -398 vlineto
+            0 -112 -14 -14 -83 -6 rrcurveto
+            -19 234 19 vlineto
+            -77 4 -16 18 0 110 rrcurveto
+            0 398 252 -549 14 0 255 572 0 -449 rlineto
+            0 -83 -11 -16 -79 -5 rrcurveto
+            -19 vlineto
+            return
+          </CharString>
+          <CharString index="31">
+            -237 -19 hlineto
+            44 -5 18 -2 17 -19 15 -17 4 -30 0 -51 rrcurveto
+            0 -340 -386 483 -170 0 0 -19 rlineto
+            49 0 15 -12 33 -41 rrcurveto
+            -440 vlineto
+            0 -107 -15 -15 -82 -9 rrcurveto
+            -19 234 19 vlineto
+            -77 11 -16 17 0 103 rrcurveto
+            0 388 rlineto
+            return
+          </CharString>
+          <CharString index="32">
+            441 -549 18 0 0 518 rlineto
+            0 79 10 22 19 17 13 11 15 3 38 4 rrcurveto
+            return
+          </CharString>
+          <CharString index="33">
+            198 -131 147 -196 -195 -132 -139 -203 -208 133 -140 194 192 135 139 206 vhcurveto
+            return
+          </CharString>
+          <CharString index="34">
+            0 -120 -28 -94 -55 -53 -35 -33 -44 -15 -50 0 -46 0 -44 14 -34 31 -61 56 -29 90 0 119 0 95 26 97 43 49 39 45 50 22 54 0 rrcurveto
+            47 0 40 -17 36 -30 57 -48 34 -94 0 -114 rrcurveto
+            return
+          </CharString>
+          <CharString index="35">
+            -19 vlineto
+            74 -6 10 -12 0 -74 rrcurveto
+            -429 vlineto
+            0 -86 -6 -11 -78 -6 rrcurveto
+            -19 280 19 vlineto
+            -81 4 -13 13 0 76 rrcurveto
+            179 vlineto
+            27 -2 16 -1 26 0 107 0 49 9 58 52 37 33 20 45 0 53 0 50 -17 41 -33 28 -45 38 -72 25 -100 0 rrcurveto
+            return
+          </CharString>
+          <CharString index="36">
+            27 6 9 29 136 60 -40 -110 -90 -60 -57 -110 vhcurveto
+            -20 0 -18 1 -23 2 rrcurveto
+            return
+          </CharString>
+          <CharString index="37">
+            19 vlineto
+            -44 4 -21 6 -29 37 rrcurveto
+            -200 253 rlineto
+            125 19 56 58 0 87 0 44 -16 49 -33 27 -43 35 -71 24 -95 0 rrcurveto
+            -272 -19 hlineto
+            77 -6 8 -12 0 -74 rrcurveto
+            -427 vlineto
+            0 -88 -10 -11 -75 -6 rrcurveto
+            -19 276 19 vlineto
+            -77 5 -12 12 0 76 rrcurveto
+            0 194 56 2 238 -308 rlineto
+            return
+          </CharString>
+          <CharString index="38">
+            35 17 7 34 124 59 -44 -90 vhcurveto
+            0 -50 -21 -44 -36 -20 -46 -25 -35 -7 -96 -2 rrcurveto
+            return
+          </CharString>
+          <CharString index="39">
+            -22 213 -21 0 rlineto
+            return
+          </CharString>
+          <CharString index="40">
+            -4 -22 -11 -12 -17 0 -10 0 -16 4 -18 8 rrcurveto
+            return
+          </CharString>
+          <CharString index="41">
+            -38 16 -26 6 -41 0 -99 0 -75 -66 0 -109 0 -62 32 -62 90 -48 64 -34 69 -44 35 -38 19 -21 10 -23 0 -35 0 -66 -49 -46 -70 0 rrcurveto
+            -95 0 -65 56 -46 121 rrcurveto
+            -22 0 29 -212 22 0 rlineto
+            return
+          </CharString>
+          <CharString index="42">
+            21 13 12 15 vhcurveto
+            11 0 17 -3 19 -8 rrcurveto
+            return
+          </CharString>
+          <CharString index="43">
+            37 -16 39 -7 42 0 116 0 88 75 0 112 0 76 -49 58 -106 62 -105 61 -74 42 0 68 0 55 39 40 62 0 60 0 50 -33 35 -45 rrcurveto
+            18 -24 11 -26 12 -44 rrcurveto
+            return
+          </CharString>
+          <CharString index="44">
+            -7 170 -562 0 -7 -170 24 0 rlineto
+            20 110 27 18 106 0 rrcurveto
+            60 -497 hlineto
+            0 -88 -11 -11 -83 -5 rrcurveto
+            -19 292 19 vlineto
+            -82 4 -14 12 0 76 rrcurveto
+            509 59 vlineto
+            106 0 26 -18 22 -110 rrcurveto
+            return
+          </CharString>
+          <CharString index="45">
+            -232 -19 hlineto
+            81 -5 13 -22 0 -103 rrcurveto
+            -263 vlineto
+            0 -81 -4 -40 -33 -40 -32 -37 -56 -22 -64 0 -57 0 -42 17 -29 28 -40 39 -4 45 0 78 rrcurveto
+            312 vlineto
+            0 75 11 12 80 7 rrcurveto
+            19 -283 -19 vlineto
+            79 -6 11 -11 0 -67 rrcurveto
+            -310 vlineto
+            0 -106 26 -70 55 -41 41 -31 56 -15 73 0 92 0 68 25 42 48 45 51 9 54 0 97 rrcurveto
+            255 vlineto
+            0 95 10 24 84 8 rrcurveto
+            return
+          </CharString>
+          <CharString index="46">
+            -197 -19 hlineto
+            51 -1 19 -13 0 -27 0 -21 -6 -25 -11 -31 rrcurveto
+            -126 -339 -143 364 rlineto
+            -9 24 -8 23 0 11 0 21 27 13 50 1 rrcurveto
+            19 -265 -19 vlineto
+            57 -1 12 -7 33 -88 rrcurveto
+            28 -75 -104 -283 -144 376 rlineto
+            -7 19 -4 16 0 12 0 21 13 9 51 1 rrcurveto
+            19 -244 -19 vlineto
+            51 -1 20 -24 33 -95 59 -168 72 -186 61 -180 rrcurveto
+            15 0 154 423 rlineto
+            52 -133 57 -148 51 -142 rrcurveto
+            15 hlineto
+            85 261 10 29 109 294 19 52 11 8 53 10 rrcurveto
+            return
+          </CharString>
+          <CharString index="47">
+            19 vlineto
+            -53 6 -19 12 -39 56 rrcurveto
+            -192 274 143 177 rlineto
+            63 78 25 17 64 4 rrcurveto
+            19 -237 -19 vlineto
+            54 -2 14 -7 0 -23 0 -15 -10 -15 -30 -37 rrcurveto
+            -112 -140 -44 64 rlineto
+            -53 78 -30 38 0 29 0 23 18 5 28 1 rrcurveto
+            29 1 0 19 -301 0 0 -19 rlineto
+            66 -7 21 -12 96 -141 rrcurveto
+            106 -155 -156 -195 rlineto
+            -77 -97 -16 -12 -52 -5 rrcurveto
+            -19 232 19 vlineto
+            -58 4 -18 9 0 19 0 15 16 28 37 46 rrcurveto
+            119 149 95 -141 rlineto
+            34 -51 17 -31 0 -16 0 -18 -18 -9 -32 -2 rrcurveto
+            -26 -2 0 -19 rlineto
+            return
+          </CharString>
+          <CharString index="48">
+            -218 -19 hlineto
+            52 -1 15 -8 1 -22 0 -11 -4 -16 -10 -15 rrcurveto
+            -145 -222 -146 221 rlineto
+            -11 16 -7 18 0 13 0 16 15 9 32 1 rrcurveto
+            24 1 0 19 -279 0 0 -19 rlineto
+            48 -2 22 -20 92 -135 rrcurveto
+            131 -192 0 -171 rlineto
+            0 -89 -12 -9 -89 -6 rrcurveto
+            -19 306 19 vlineto
+            -84 4 -19 10 0 78 rrcurveto
+            0 192 148 228 rlineto
+            56 86 23 21 59 5 rrcurveto
+            return
+          </CharString>
+          <CharString index="49">
+            -23 hlineto
+            -18 -59 -9 -28 -33 -23 -25 -18 -36 -10 -56 0 rrcurveto
+            -252 0 432 609 0 15 -525 0 -21 -171 26 0 rlineto
+            10 55 6 17 25 26 24 24 39 11 71 0 rrcurveto
+            214 0 -437 -609 0 -15 563 0 rlineto
+            return
+          </CharString>
+          <CharString index="50">
+            50 -500 -50 vlineto
+            return
+          </CharString>
+          <CharString index="51">
+            -145 148 rlineto
+            -17 18 -11 5 -15 0 -22 0 -14 -11 0 -21 0 -16 11 -15 19 -12 rrcurveto
+            154 -96 rlineto
+            return
+          </CharString>
+          <CharString index="52">
+            27 -23 23 -27 -27 -22 -22 -27 -28 21 -22 27 28 23 22 27 vhcurveto
+            return
+          </CharString>
+          <CharString index="53">
+            54 -311 -54 vlineto
+            return
+          </CharString>
+          <CharString index="54">
+            -35 0 -41 -99 7 -5 rlineto
+            8 3 8 1 12 0 rrcurveto
+            38 15 -10 -26 -27 -21 -17 -34 hvcurveto
+            -21 0 -17 3 -27 9 rrcurveto
+            -14 -31 rlineto
+            29 -12 24 -4 31 0 rrcurveto
+            77 48 32 52 44 -32 25 -54 hvcurveto
+            -10 0 -6 -1 -10 -2 rrcurveto
+            return
+          </CharString>
+          <CharString index="55">
+            -125 167 -62 0 -124 -167 34 0 121 103 122 -103 rlineto
+            return
+          </CharString>
+          <CharString index="56">
+            -17 -37 -13 -13 -23 0 -15 0 -19 5 -19 10 rrcurveto
+            -24 12 rlineto
+            return
+          </CharString>
+          <CharString index="57">
+            -24 12 -24 6 -22 0 rrcurveto
+            return
+          </CharString>
+          <CharString index="58">
+            -50 0 -36 -36 -15 -65 rrcurveto
+            29 hlineto
+            return
+          </CharString>
+          <CharString index="59">
+            11 31 17 15 23 0 12 0 14 -4 15 -7 rrcurveto
+            23 -11 rlineto
+            return
+          </CharString>
+          <CharString index="60">
+            41 -20 15 -4 25 0 55 0 29 30 21 76 rrcurveto
+            return
+          </CharString>
+          <CharString index="61">
+            54 -45 46 -53 -56 -45 -44 -55 -56 43 -44 55 56 45 44 55 vhcurveto
+            return
+          </CharString>
+          <CharString index="62">
+            -37 -29 -29 -38 -35 -29 29 37 35 30 30 34 37 30 -29 -36 vhcurveto
+            return
+          </CharString>
+          <CharString index="63">
+            -29 -71 -28 -26 -54 0 -63 0 -36 34 -13 63 rrcurveto
+            -29 hlineto
+            -97 45 -60 94 vhcurveto
+            82 0 48 51 12 106 rrcurveto
+            return
+          </CharString>
+          <CharString index="64">
+            28 -19 22 -31 -30 -19 -23 -27 -27 22 -22 27 27 23 20 29 vhcurveto
+            return
+          </CharString>
+          <CharString index="65">
+            -35 0 -121 -103 -120 103 -35 0 123 -167 64 0 rlineto
+            return
+          </CharString>
+          <CharString index="66">
+            40 0 154 96 rlineto
+            21 13 8 14 0 18 0 19 -14 11 -20 0 -13 0 -10 -3 -20 -20 rrcurveto
+            return
+          </CharString>
+          <CharString index="67">
+            -30 -10 -82 -66 0 -71 rrcurveto
+            -83 58 -13 23 25 33 15 43 40 -36 10 -26 vhcurveto
+            -11 0 -7 -4 -6 0 -7 0 -7 5 0 9 0 19 16 45 66 42 rrcurveto
+            return
+          </CharString>
+          <CharString index="68">
+            30 10 82 66 0 71 rrcurveto
+            83 -58 13 -23 -25 -33 -15 -43 -40 36 -10 26 vhcurveto
+            11 0 7 4 6 0 7 0 7 -5 0 -9 0 -19 -16 -45 -66 -42 rrcurveto
+            return
+          </CharString>
+          <CharString index="69">
+            -66 42 -16 45 0 19 0 9 7 5 7 0 6 0 7 -4 11 0 rrcurveto
+            26 36 10 40 43 -33 15 -25 -23 -58 -13 -83 hvcurveto
+            0 -71 82 -66 30 -10 rrcurveto
+            return
+          </CharString>
+          <CharString index="70">
+            -37 30 vlineto
+            34 27 -26 -34 -34 -27 -27 -34 hvcurveto
+            -30 -37 30 hlineto
+            54 44 44 54 54 -44 43 -54 hvcurveto
+            return
+          </CharString>
+          <CharString index="71">
+            37 -30 vlineto
+            -34 -27 27 34 34 27 26 34 hvcurveto
+            30 37 -30 hlineto
+            -54 -44 -43 -54 -54 44 -44 54 hvcurveto
+            return
+          </CharString>
+          <CharString index="72">
+            40 -95 147 -40 -147 -95 -40 vlineto
+            return
+          </CharString>
+          <CharString index="73">
+            40 -230 -40 95 -147 40 147 vlineto
+            return
+          </CharString>
+          <CharString index="74">
+            -29 -27 -21 -9 -21 0 -22 0 -16 11 0 28 0 23 13 22 31 25 rrcurveto
+            -46 hlineto
+            -38 -27 -16 -28 0 -36 0 -47 31 -27 43 0 46 0 29 22 36 54 rrcurveto
+            return
+          </CharString>
+          <CharString index="75">
+            54 -45 45 -53 -56 -45 -44 -55 -56 43 -44 55 56 45 45 55 vhcurveto
+            return
+          </CharString>
+          <CharString index="76">
+            -17 -37 -13 -14 -23 0 -20 0 -14 6 -43 22 rrcurveto
+            return
+          </CharString>
+          <CharString index="77">
+            11 31 17 15 23 0 18 0 8 -4 38 -18 rrcurveto
+            return
+          </CharString>
+          <CharString index="78">
+            40 0 154 96 rlineto
+            19 12 11 15 0 16 0 21 -14 11 -22 0 -15 0 -11 -5 -17 -18 rrcurveto
+            return
+          </CharString>
+          <CharString index="79">
+            -146 148 rlineto
+            -20 20 -10 3 -13 0 -20 0 -14 -11 0 -19 0 -18 8 -14 21 -13 rrcurveto
+            154 -96 rlineto
+            return
+          </CharString>
+          <CharString index="80">
+            -12 106 -48 51 -82 0 rrcurveto
+            -94 -45 -60 -97 hvcurveto
+            29 hlineto
+            13 63 36 34 63 0 54 0 28 -26 29 -71 rrcurveto
+            return
+          </CharString>
+          <CharString index="81">
+            27 -23 22 -27 -27 -22 -22 -27 -28 21 -22 27 28 23 22 28 vhcurveto
+            return
+          </CharString>
+          <CharString index="82">
+            -133 106 -18 -8 rlineto
+            29 -29 21 -18 0 -9 rrcurveto
+            -13 -21 -2 -28 vhcurveto
+            -178 hlineto
+            -33 -16 3 12 hvcurveto
+            0 7 17 22 32 27 rrcurveto
+            -18 8 -132 -106 133 -106 18 8 rlineto
+            -29 29 -21 18 0 9 rrcurveto
+            10 16 5 34 vhcurveto
+            177 hlineto
+            33 16 -3 -12 hvcurveto
+            0 -7 -17 -22 -32 -27 rrcurveto
+            18 -8 rlineto
+            return
+          </CharString>
+          <CharString index="83">
+            14 -10 16 -15 vhcurveto
+            -4 0 -5 -2 -6 -3 -20 -12 -17 -35 -51 -28 0 49 21 44 0 22 rrcurveto
+            14 -14 13 -12 -14 -13 -13 -14 vhcurveto
+            0 -22 20 -44 0 -50 -39 21 -30 42 -21 13 -6 3 -5 2 -4 0 -15 0 -10 -16 0 -14 0 -9 5 -7 8 -5 20 -12 54 -6 36 -22 rrcurveto
+            -36 -21 -52 -6 -19 -11 -10 -5 -4 -13 0 -9 0 -11 9 -11 17 0 5 0 7 2 6 4 16 9 32 43 36 20 0 -48 -20 -43 0 -22 rrcurveto
+            -12 13 -15 14 12 14 15 12 vhcurveto
+            0 22 -21 43 0 48 54 -30 17 -48 28 0 18 0 9 13 0 13 0 8 -4 10 -11 5 -17 11 -44 2 -45 25 51 29 39 0 16 10 rrcurveto
+            12 6 5 7 0 9 rrcurveto
+            return
+          </CharString>
+          <CharString index="84">
+            54 -286 vlineto
+            -33 -16 3 12 hvcurveto
+            0 7 17 22 32 27 rrcurveto
+            -18 8 -132 -106 133 -106 18 8 rlineto
+            -29 29 -21 18 0 9 rrcurveto
+            13 21 2 28 vhcurveto
+            return
+          </CharString>
+          <CharString index="85">
+            25 vlineto
+            -41 0 -18 22 -51 121 rrcurveto
+            -222 522 -28 0 -221 -545 rlineto
+            -38 -94 -15 -18 -46 -8 rrcurveto
+            -25 202 25 vlineto
+            -59 4 -22 11 0 26 0 24 19 42 12 31 rrcurveto
+            13 34 225 0 rlineto
+            34 -79 12 -35 0 -22 0 -22 -13 -8 -33 -3 rrcurveto
+            -32 -3 0 -25 rlineto
+            return
+          </CharString>
+          <CharString index="86">
+            117 24 42 51 0 76 rrcurveto
+            103 -91 57 -161 vhcurveto
+            -317 -25 hlineto
+            68 -5 20 -17 0 -59 rrcurveto
+            -467 vlineto
+            0 -56 -15 -14 -73 -8 rrcurveto
+            -25 329 vlineto
+            178 96 81 107 hvcurveto
+            0 95 -83 67 -110 14 rrcurveto
+            return
+          </CharString>
+          <CharString index="87">
+            39 17 15 40 68 31 -49 -88 -98 -31 -38 -125 vhcurveto
+            return
+          </CharString>
+          <CharString index="88">
+            26 hlineto
+            106 51 -53 -102 -101 -42 -55 -78 -44 -19 18 48 hvcurveto
+            return
+          </CharString>
+          <CharString index="89">
+            -15 -36 -10 -10 -19 0 -10 0 -13 4 -23 10 rrcurveto
+            return
+          </CharString>
+          <CharString index="90">
+            -28 hlineto
+            -30 -65 -18 -30 -34 -28 -47 -39 -57 -15 -78 0 rrcurveto
+            -65 -18 13 43 hvcurveto
+            242 vlineto
+            109 0 43 -36 10 -119 rrcurveto
+            26 338 -26 hlineto
+            -12 -116 -44 -32 -106 -1 rrcurveto
+            225 vlineto
+            38 13 16 52 vhcurveto
+            163 0 51 -33 23 -134 rrcurveto
+            25 201 -577 -25 hlineto
+            69 -4 19 -16 0 -51 rrcurveto
+            -479 vlineto
+            0 -52 -14 -16 -74 -8 rrcurveto
+            -25 585 vlineto
+            return
+          </CharString>
+          <CharString index="91">
+            25 vlineto
+            -72 9 -16 11 0 53 rrcurveto
+            479 vlineto
+            0 54 20 15 68 5 rrcurveto
+            25 -339 -25 vlineto
+            70 -5 19 -13 0 -56 rrcurveto
+            -204 -241 204 vlineto
+            0 56 19 13 70 5 rrcurveto
+            25 -336 -25 vlineto
+            67 -6 18 -13 0 -55 rrcurveto
+            -479 vlineto
+            0 -53 -15 -11 -70 -9 rrcurveto
+            -25 336 25 vlineto
+            -73 8 -16 12 0 53 rrcurveto
+            228 241 -228 vlineto
+            0 -51 -16 -14 -73 -8 rrcurveto
+            -25 vlineto
+            return
+          </CharString>
+          <CharString index="92">
+            25 vlineto
+            -76 5 -19 11 0 65 rrcurveto
+            470 vlineto
+            0 56 21 16 74 3 rrcurveto
+            25 -350 -25 vlineto
+            72 -5 21 -14 0 -56 rrcurveto
+            -470 vlineto
+            0 -62 -18 -12 -75 -7 rrcurveto
+            -25 vlineto
+            return
+          </CharString>
+          <CharString index="93">
+            25 vlineto
+            -25 1 -12 5 -12 15 rrcurveto
+            -305 378 rlineto
+            186 198 37 17 99 12 rrcurveto
+            25 -289 -25 vlineto
+            35 -3 rlineto
+            36 -3 12 -8 0 -20 0 -16 -9 -11 -31 -31 rrcurveto
+            -212 -215 0 223 rlineto
+            0 61 14 18 74 5 rrcurveto
+            25 -337 -25 vlineto
+            69 -4 18 -12 0 -62 rrcurveto
+            -472 vlineto
+            0 -57 -13 -12 -74 -7 rrcurveto
+            -25 336 25 vlineto
+            -72 9 -15 12 0 49 rrcurveto
+            0 196 27 25 181 -225 rlineto
+            19 -24 6 -10 0 -11 0 -13 -12 -6 -65 -2 rrcurveto
+            -25 vlineto
+            return
+          </CharString>
+          <CharString index="94">
+            25 vlineto
+            -80 7 -8 21 0 73 rrcurveto
+            428 vlineto
+            0 75 15 17 73 5 rrcurveto
+            25 -252 vlineto
+            -201 -472 -199 472 -253 0 0 -25 rlineto
+            73 -6 16 -14 0 -54 rrcurveto
+            -469 vlineto
+            0 -65 -13 -10 -78 -8 rrcurveto
+            -25 234 25 vlineto
+            -82 6 -19 22 0 76 rrcurveto
+            0 465 252 -594 27 0 252 605 0 -496 rlineto
+            0 -59 -14 -18 -75 -7 rrcurveto
+            -25 vlineto
+            return
+          </CharString>
+          <CharString index="95">
+            -214 -25 hlineto
+            74 -7 18 -19 0 -80 rrcurveto
+            0 -299 -349 430 -211 0 0 -25 rlineto
+            20 0 23 -14 42 -58 rrcurveto
+            -470 vlineto
+            0 -61 -15 -14 -73 -9 rrcurveto
+            -25 226 25 vlineto
+            -77 9 -17 20 0 77 rrcurveto
+            0 396 rlineto
+            return
+          </CharString>
+          <CharString index="96">
+            447 -545 28 0 0 586 rlineto
+            0 63 13 10 65 10 rrcurveto
+            return
+          </CharString>
+          <CharString index="97">
+            207 -148 149 -201 -208 -151 -144 -213 -207 146 -146 207 207 148 146 208 vhcurveto
+            return
+          </CharString>
+          <CharString index="98">
+            -206 -61 -108 -115 -114 -64 105 211 210 63 118 110 117 64 -117 -213 vhcurveto
+            return
+          </CharString>
+          <CharString index="99">
+            -25 vlineto
+            70 -6 14 -14 0 -78 rrcurveto
+            -427 vlineto
+            0 -51 -6 -28 -33 -13 -11 -5 -15 -2 -19 -2 rrcurveto
+            -25 333 25 vlineto
+            -79 10 -8 9 0 67 rrcurveto
+            192 vlineto
+            132 1 34 3 43 16 83 31 46 61 0 80 rrcurveto
+            122 -102 59 -167 vhcurveto
+            return
+          </CharString>
+          <CharString index="100">
+            23 17 12 33 83 33 -46 -111 vhcurveto
+            0 -111 -41 -34 -125 -1 rrcurveto
+            return
+          </CharString>
+          <CharString index="101">
+            -2 201 -600 0 -3 -201 29 0 rlineto
+            17 119 52 46 124 4 rrcurveto
+            -527 vlineto
+            0 -74 -11 -11 -86 -7 rrcurveto
+            -25 357 25 vlineto
+            -87 6 -11 10 0 76 rrcurveto
+            527 vlineto
+            123 -4 52 -46 17 -119 rrcurveto
+            return
+          </CharString>
+          <CharString index="102">
+            25 vlineto
+            -34 1 -16 12 -47 73 rrcurveto
+            -182 283 80 111 rlineto
+            87 121 26 18 75 7 rrcurveto
+            25 -250 -25 vlineto
+            21 -2 rlineto
+            38 -4 13 -7 0 -24 0 -22 -11 -19 -47 -65 -20 -27 -18 -25 -18 -25 rrcurveto
+            -97 150 rlineto
+            -13 20 -2 5 0 12 0 21 12 8 36 2 rrcurveto
+            31 2 0 25 -346 0 0 -25 rlineto
+            36 -3 13 -10 31 -45 rrcurveto
+            198 -291 -175 -221 rlineto
+            -30 -37 -24 -13 -50 -6 rrcurveto
+            -25 250 25 vlineto
+            -61 6 -20 10 0 26 0 21 19 31 75 101 rrcurveto
+            38 51 101 -162 rlineto
+            12 -19 9 -23 0 -12 0 -17 -17 -9 -35 -2 rrcurveto
+            -28 -2 0 -25 rlineto
+            return
+          </CharString>
+          <CharString index="103">
+            24 vlineto
+            -35 5 -13 13 0 43 rrcurveto
+            376 -193 -24 vlineto
+            44 -9 10 -10 0 -45 rrcurveto
+            -285 vlineto
+            0 -46 -7 -7 -46 -11 rrcurveto
+            -24 vlineto
+            return
+          </CharString>
+          <CharString index="104">
+            -202 -24 hlineto
+            51 -4 12 -12 0 -43 rrcurveto
+            -474 vlineto
+            -53 -14 -23 -34 -19 -13 7 10 vhcurveto
+            0 5 3 7 6 10 10 15 4 12 0 10 rrcurveto
+            30 -26 24 -33 -37 -25 -25 -36 -59 58 -41 82 vhcurveto
+            69 0 53 27 28 49 19 32 8 38 0 59 rrcurveto
+            return
+          </CharString>
+          <CharString index="105">
+            142 -96 102 -126 -132 -97 -99 -146 -142 100 -100 125 129 97 102 141 vhcurveto
+            return
+          </CharString>
+          <CharString index="106">
+            -149 -20 -52 -58 -58 -21 56 145 171 18 53 61 59 19 -53 -171 vhcurveto
+            return
+          </CharString>
+          <CharString index="107">
+            16 -16 vlineto
+            -42 0 -25 21 -7 49 rrcurveto
+            -78 581 -18 0 -356 -577 rlineto
+            -36 -58 -37 -12 -35 -4 rrcurveto
+            -16 218 16 -29 vlineto
+            -26 0 -16 11 -1 18 0 7 3 11 7 8 rrcurveto
+            84 140 198 0 13 -113 rlineto
+            1 -7 1 -8 0 -6 rrcurveto
+            -40 -16 -21 -56 vhcurveto
+            -16 -16 hlineto
+            return
+          </CharString>
+          <CharString index="108">
+            -170 0 139 227 6 0 rlineto
+            return
+          </CharString>
+          <CharString index="109">
+            -5 -16 19 0 rlineto
+            59 19 -16 -26 hvcurveto
+            0 -6 -1 -10 -1 -5 rrcurveto
+            -123 -495 rlineto
+            -8 -34 -19 -29 -76 0 rrcurveto
+            -20 0 -4 -16 324 0 rlineto
+            207 82 94 99 hvcurveto
+            0 85 -67 42 -60 11 rrcurveto
+            -3 4 rlineto
+            20 5 29 8 25 13 48 24 43 45 0 78 rrcurveto
+            136 -152 9 -84 vhcurveto
+            return
+          </CharString>
+          <CharString index="110">
+            13 2 15 1 15 0 rrcurveto
+            54 65 -21 -77 -97 -56 -66 -112 hvcurveto
+            -15 0 -18 2 -20 4 rrcurveto
+            return
+          </CharString>
+          <CharString index="111">
+            15 4 20 0 16 0 rrcurveto
+            78 64 -35 -73 -130 -96 -42 -94 hvcurveto
+            -26 0 -20 2 -25 7 rrcurveto
+            return
+          </CharString>
+          <CharString index="112">
+            -534 0 -4 -16 19 0 rlineto
+            59 16 -11 -24 hvcurveto
+            0 -7 -1 -11 -4 -18 rrcurveto
+            -120 -488 rlineto
+            -8 -34 -23 -28 -73 0 rrcurveto
+            -19 0 -4 -16 571 0 66 198 -16 0 rlineto
+            -43 -113 -56 -47 -158 0 rrcurveto
+            -67 hlineto
+            -47 -16 11 16 hvcurveto
+            0 3 1 3 1 4 rrcurveto
+            66 258 90 0 rlineto
+            31 41 -4 -47 hvcurveto
+            0 -17 -5 -30 -1 -13 rrcurveto
+            17 0 58 242 -16 0 rlineto
+            -22 -82 -44 -13 -60 0 rrcurveto
+            -80 0 60 248 160 0 rlineto
+            51 0 55 -13 3 -74 0 -11 -1 -11 -1 -13 rrcurveto
+            16 hlineto
+            return
+          </CharString>
+          <CharString index="113">
+            -330 0 -4 -16 20 0 rlineto
+            57 19 -15 -24 hvcurveto
+            0 -7 -2 -7 -2 -10 rrcurveto
+            -53 -217 -278 0 52 217 rlineto
+            8 35 18 28 79 0 rrcurveto
+            18 0 4 16 -330 0 -4 -16 20 0 rlineto
+            62 16 -18 -21 hvcurveto
+            0 -7 -2 -11 -2 -6 rrcurveto
+            -123 -496 rlineto
+            -8 -34 -23 -28 -75 0 rrcurveto
+            -18 0 -4 -16 330 0 4 16 -20 0 rlineto
+            -59 -18 14 24 hvcurveto
+            0 8 2 10 2 6 rrcurveto
+            61 242 280 0 -61 -242 rlineto
+            -9 -37 -24 -25 -72 0 rrcurveto
+            -18 0 -4 -16 329 0 4 16 -19 0 rlineto
+            -59 -19 13 21 hvcurveto
+            0 10 2 9 2 9 rrcurveto
+            123 496 rlineto
+            9 35 21 28 76 0 rrcurveto
+            18 hlineto
+            return
+          </CharString>
+          <CharString index="114">
+            -328 0 -4 -16 18 0 rlineto
+            60 17 -16 -22 hvcurveto
+            0 -7 -2 -9 -2 -8 rrcurveto
+            -123 -497 rlineto
+            -8 -34 -21 -28 -76 0 rrcurveto
+            -19 0 -4 -16 328 0 4 16 -17 0 rlineto
+            -59 -19 12 21 hvcurveto
+            0 10 1 11 2 8 rrcurveto
+            123 497 rlineto
+            9 36 21 26 76 0 rrcurveto
+            19 hlineto
+            return
+          </CharString>
+          <CharString index="115">
+            -216 0 -4 -16 rlineto
+            42 0 8 -7 1 -14 0 -13 -8 -8 -10 -8 rrcurveto
+            -282 -221 -5 0 53 209 rlineto
+            9 35 13 27 75 0 rrcurveto
+            11 0 4 16 -312 0 -4 -16 18 0 rlineto
+            60 16 -16 -22 hvcurveto
+            0 -21 -3 -1 -7 -27 rrcurveto
+            -117 -472 rlineto
+            -7 -27 -17 -35 -60 0 rrcurveto
+            -18 0 -4 -16 304 0 4 16 -17 0 rlineto
+            -59 -19 16 24 hvcurveto
+            0 7 1 7 2 8 rrcurveto
+            62 243 4 0 156 -237 rlineto
+            8 -13 8 -6 0 -16 rrcurveto
+            -28 -19 -5 -28 vhcurveto
+            -11 0 -4 -16 287 0 4 16 rlineto
+            -65 0 -33 36 -29 43 rrcurveto
+            -187 278 271 211 rlineto
+            42 33 27 20 51 0 rrcurveto
+            return
+          </CharString>
+          <CharString index="116">
+            -215 0 -333 -493 -4 0 -89 493 -213 0 -4 -16 19 0 rlineto
+            62 15 -15 -20 hvcurveto
+            0 -9 -1 -12 -4 -17 rrcurveto
+            -119 -475 rlineto
+            -10 -40 -19 -33 -79 0 rrcurveto
+            -19 0 -4 -16 267 0 4 16 -20 0 rlineto
+            -59 -17 12 21 hvcurveto
+            0 12 2 15 3 13 rrcurveto
+            109 428 3 0 94 -517 25 0 345 517 6 0 -112 -439 rlineto
+            -8 -32 -18 -30 -79 0 rrcurveto
+            -17 0 -4 -16 330 0 4 16 -19 0 rlineto
+            -58 -19 15 19 hvcurveto
+            0 9 0 12 2 7 rrcurveto
+            127 498 rlineto
+            9 36 16 25 80 0 rrcurveto
+            17 hlineto
+            return
+          </CharString>
+          <CharString index="117">
+            -266 0 -4 -16 20 0 rlineto
+            59 17 -17 -20 hvcurveto
+            0 -10 -2 -7 -2 -9 rrcurveto
+            -97 -386 -5 0 -236 465 -200 0 -4 -16 19 0 rlineto
+            30 0 43 -7 19 -43 rrcurveto
+            -126 -509 rlineto
+            -8 -34 -23 -28 -75 0 rrcurveto
+            -18 0 -4 -16 266 0 4 16 -19 0 rlineto
+            -59 -17 15 20 hvcurveto
+            0 11 1 8 2 8 rrcurveto
+            108 448 6 0 274 -526 24 0 144 574 rlineto
+            9 37 23 26 75 0 rrcurveto
+            18 hlineto
+            return
+          </CharString>
+          <CharString index="118">
+            126 -82 123 -167 -255 -158 -244 -203 -137 104 -96 149 253 156 233 198 vhcurveto
+            return
+          </CharString>
+          <CharString index="119">
+            -141 -73 -275 -212 -78 -62 53 97 142 52 313 232 101 40 -82 -107 vhcurveto
+            return
+          </CharString>
+          <CharString index="120">
+            -4 -16 19 0 rlineto
+            61 17 -16 -21 hvcurveto
+            0 -8 -2 -9 -2 -8 rrcurveto
+            -125 -497 rlineto
+            -9 -34 -20 -28 -77 0 rrcurveto
+            -17 0 -4 -16 341 0 4 16 -32 0 rlineto
+            -59 -18 15 24 hvcurveto
+            0 8 1 7 2 8 rrcurveto
+            56 223 rlineto
+            4 -1 3 -1 5 0 24 -4 22 -1 28 0 rrcurveto
+            204 81 101 98 74 -62 86 -170 hvcurveto
+            return
+          </CharString>
+          <CharString index="121">
+            17 2 15 1 17 0 rrcurveto
+            95 30 -53 -60 -102 -80 -70 -102 hvcurveto
+            -20 0 -24 4 -16 4 rrcurveto
+            return
+          </CharString>
+          <CharString index="122">
+            -599 0 -46 -179 16 0 rlineto
+            18 81 70 61 83 0 rrcurveto
+            91 0 -133 -538 rlineto
+            -8 -34 -20 -28 -78 0 rrcurveto
+            -18 0 -4 -16 329 0 4 16 -21 0 rlineto
+            -60 -16 16 21 hvcurveto
+            0 9 3 7 2 9 rrcurveto
+            133 538 98 0 rlineto
+            41 58 -5 -66 hvcurveto
+            0 -27 -3 -27 -2 -17 rrcurveto
+            16 hlineto
+            return
+          </CharString>
+          <CharString index="123">
+            -222 0 -4 -16 9 0 rlineto
+            32 0 12 -8 1 -16 0 -11 -6 -12 -9 -11 rrcurveto
+            -164 -184 -60 179 rlineto
+            -4 11 -2 9 0 8 rrcurveto
+            31 22 4 30 vhcurveto
+            19 0 4 16 -283 0 -4 -16 9 0 rlineto
+            74 0 17 -52 11 -31 rrcurveto
+            85 -231 -208 -230 rlineto
+            -37 -40 -49 -30 -54 -7 rrcurveto
+            -4 -16 249 0 4 16 -22 0 rlineto
+            -38 -18 13 19 hvcurveto
+            0 9 3 9 9 10 rrcurveto
+            181 201 69 -194 rlineto
+            3 -10 1 -8 0 -8 rrcurveto
+            -37 -29 -4 -38 vhcurveto
+            -11 0 -4 -16 281 0 4 16 -14 0 rlineto
+            -50 0 -17 34 -12 34 rrcurveto
+            -96 266 187 213 rlineto
+            37 42 29 32 69 0 rrcurveto
+            4 hlineto
+            return
+          </CharString>
+          <CharString index="124">
+            -563 0 -48 -158 17 0 rlineto
+            38 115 89 6 96 0 rrcurveto
+            202 0 -573 -616 581 0 46 178 -16 0 rlineto
+            -35 -129 -88 -11 -94 0 rrcurveto
+            -218 hlineto
+            return
+          </CharString>
+          <CharString index="125">
+            25 -22 25 -26 -29 -19 -25 -25 -26 19 -20 29 26 22 20 26 vhcurveto
+            return
+          </CharString>
+          <CharString index="126">
+            108 -67 46 -80 -148 -103 -161 -128 -73 34 -90 101 153 110 159 139 vhcurveto
+            return
+          </CharString>
+          <CharString index="127">
+            -76 -71 -223 -106 -56 -3 74 33 136 87 149 82 53 14 -55 -38 vhcurveto
+            return
+          </CharString>
+          <CharString index="128">
+            -8 -61 -40 -32 -53 0 -22 0 -37 10 -50 22 -93 40 -64 21 -33 0 rrcurveto
+            -62 -73 -48 -104 hvcurveto
+            26 hlineto
+            10 60 41 31 50 0 21 0 36 -10 50 -21 108 -44 10 -16 69 0 rrcurveto
+            70 69 56 96 hvcurveto
+            return
+          </CharString>
+          <CharString index="129">
+            -80 0 -676 -1845 80 0 rlineto
+            return
+          </CharString>
+          <CharString index="130">
+            50 -1500 -50 vlineto
+            return
+          </CharString>
+          <CharString index="131">
+            -15 -61 -62 -32 -99 0 -42 0 -70 10 -94 22 -174 40 -120 21 -62 0 -116 0 -79 -40 -20 -112 rrcurveto
+            26 hlineto
+            19 60 61 31 94 0 39 0 68 -11 94 -20 203 -44 19 -16 129 0 131 0 77 46 19 106 rrcurveto
+            return
+          </CharString>
+          <CharString index="132">
+            50 -2000 -50 vlineto
+            return
+          </CharString>
+          <CharString index="133">
+            -21 -67 -85 -35 -142 0 -60 0 -142 12 -134 23 -251 43 -183 24 -90 0 -167 0 -127 -45 -32 -121 rrcurveto
+            26 hlineto
+            28 66 115 33 135 0 56 0 109 -11 134 -22 292 -48 70 -18 185 0 188 0 96 49 26 117 rrcurveto
+            return
+          </CharString>
+          <CharString index="134">
+            50 -2500 -50 vlineto
+            return
+          </CharString>
+          <CharString index="135">
+            -27 -66 -164 -31 -153 0 -64 0 -225 11 -143 22 -268 41 -280 23 -97 0 -179 0 -222 -44 -35 -119 rrcurveto
+            29 hlineto
+            33 65 216 32 144 0 60 0 170 -12 144 -22 311 -47 179 -16 199 0 200 0 172 48 29 115 rrcurveto
+            return
+          </CharString>
+          <CharString index="136">
+            50 -3000 -50 vlineto
+            return
+          </CharString>
+          <CharString index="137">
+            -27 -66 -279 -31 -153 0 -64 0 -337 11 -143 22 -268 41 -380 23 -97 0 -179 0 -337 -44 -35 -119 rrcurveto
+            29 hlineto
+            33 65 331 32 144 0 60 0 270 -12 144 -22 311 -47 291 -16 199 0 200 0 287 48 29 115 rrcurveto
+            return
+          </CharString>
+        </Subrs>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          -351 endchar
+        </CharString>
+        <CharString name="A">
+          121 0 20 196 41 397 20 hstem
+          707 hmoveto
+          -89 callsubr
+          -5 257 rmoveto
+          -88 callsubr
+          endchar
+        </CharString>
+        <CharString name="B">
+          66 0 37 588 37 hstem
+          422 349 rmoveto
+          52 13 20 8 24 21 25 22 16 41 0 41 rrcurveto
+          106 -89 61 -173 vhcurveto
+          -280 -19 hlineto
+          81 -5 15 -9 0 -79 rrcurveto
+          -437 vlineto
+          0 -76 -17 -16 -79 -2 rrcurveto
+          -19 337 vlineto
+          145 94 67 111 hvcurveto
+          0 44 -17 42 -32 29 -29 26 -27 18 -66 11 rrcurveto
+          -207 18 rmoveto
+          227 vlineto
+          23 7 9 17 vhcurveto
+          41 hlineto
+          119 58 -51 -84 -83 -49 -41 -101 hvcurveto
+          -92 -40 rmoveto
+          118 -4 38 4 60 -40 33 -22 14 -37 0 -48 0 -43 -13 -34 -25 -22 -44 -39 -35 -4 -84 0 rrcurveto
+          -46 -16 11 34 hvcurveto
+          endchar
+        </CharString>
+        <CharString name="C">
+          66 -14 44 606 40 hstem
+          614 131 rmoveto
+          -87 callsubr
+          endchar
+        </CharString>
+        <CharString name="D">
+          121 0 37 588 37 hstem
+          16 662 rmoveto
+          -86 callsubr
+          -80 -79 rmoveto
+          -85 callsubr
+          endchar
+        </CharString>
+        <CharString name="E">
+          10 0 37 290 41 256 38 hstem
+          597 169 rmoveto
+          -84 callsubr
+          endchar
+        </CharString>
+        <CharString name="F">
+          -45 0 20 307 41 256 38 hstem
+          546 519 rmoveto
+          -5 143 -529 0 0 -19 rlineto
+          77 -6 10 -13 0 -73 rrcurveto
+          -428 vlineto
+          0 -87 -12 -12 -76 -5 rrcurveto
+          -19 281 19 vlineto
+          -77 4 -14 13 0 76 rrcurveto
+          215 142 vlineto
+          86 0 19 -15 8 -82 rrcurveto
+          23 233 -23 hlineto
+          -9 -78 -20 -17 -84 0 rrcurveto
+          -142 217 hlineto
+          31 5 8 29 vhcurveto
+          132 hlineto
+          121 0 18 -16 15 -89 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="G">
+          121 -14 40 610 40 hstem
+          709 354 rmoveto
+          -83 callsubr
+          endchar
+        </CharString>
+        <CharString name="H">
+          121 0 20 295 44 283 20 hstem
+          703 hmoveto
+          -82 callsubr
+          endchar
+        </CharString>
+        <CharString name="I">
+          -268 0 20 622 20 hstem
+          315 hmoveto
+          -81 callsubr
+          endchar
+        </CharString>
+        <CharString name="J">
+          -228 -14 38 618 20 hstem
+          354 662 rmoveto
+          -80 callsubr
+          endchar
+        </CharString>
+        <CharString name="K">
+          121 0 20 622 20 hstem
+          723 hmoveto
+          -79 callsubr
+          endchar
+        </CharString>
+        <CharString name="L">
+          10 0 39 603 20 hstem
+          598 174 rmoveto
+          -78 callsubr
+          endchar
+        </CharString>
+        <CharString name="M">
+          288 0 20 622 20 hstem
+          109 44 vstem
+          864 hmoveto
+          -77 callsubr
+          endchar
+        </CharString>
+        <CharString name="N">
+          121 -11 20 -9 20 622 20 hstemhm
+          109 44 415 44 hintmask 01111000
+          707 662 rmoveto
+          -76 callsubr
+          hintmask 10111000
+          -75 callsubr
+          endchar
+        </CharString>
+        <CharString name="O">
+          121 -14 36 618 36 hstem
+          688 331 rmoveto
+          -74 callsubr
+          -114 6 rmoveto
+          -73 callsubr
+          endchar
+        </CharString>
+        <CharString name="P">
+          -44 0 20 268 40 297 37 hstem
+          16 662 rmoveto
+          -72 callsubr
+          -73 -73 rmoveto
+          -71 callsubr
+          endchar
+        </CharString>
+        <CharString name="Q">
+          121 640 36 hstem
+          701 -177 rmoveto
+          18 vlineto
+          -114 7 -88 41 -73 104 68 13 42 15 44 43 73 70 35 80 0 119 rrcurveto
+          207 -136 136 -191 -187 -140 -132 -212 vhcurveto
+          0 -90 28 -78 48 -64 38 -51 39 -23 78 -27 rrcurveto
+          41 -52 rlineto
+          68 -86 123 -38 156 0 rrcurveto
+          -79 515 rmoveto
+          0 -100 -24 -98 -40 -46 -37 -43 -50 -29 -62 0 -57 0 -65 30 -33 48 -38 53 -20 86 0 96 0 95 26 92 40 47 37 45 53 26 54 0 rrcurveto
+          47 0 42 -15 36 -30 57 -48 34 -94 0 -115 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="R">
+          66 0 20 605 37 hstem
+          660 hmoveto
+          -70 callsubr
+          -294 583 rmoveto
+          -69 callsubr
+          endchar
+        </CharString>
+        <CharString name="S">
+          -45 -14 36 -22 20 615 41 -34 20 hstemhm
+          71 86 hintmask 00101000
+          469 463 rmoveto
+          -68 callsubr
+          hintmask 00011000
+          -67 callsubr
+          hintmask 10101000
+          -66 callsubr
+          hintmask 01001000
+          -65 callsubr
+          hintmask 10101000
+          -64 callsubr
+          endchar
+        </CharString>
+        <CharString name="T">
+          10 0 20 600 42 hstem
+          593 492 rmoveto
+          -63 callsubr
+          endchar
+        </CharString>
+        <CharString name="U">
+          121 -14 44 612 20 hstem
+          567 44 vstem
+          705 662 rmoveto
+          -62 callsubr
+          endchar
+        </CharString>
+        <CharString name="V">
+          121 -11 20 633 20 hstem
+          697 662 rmoveto
+          -204 -19 hlineto
+          40 -4 32 2 0 -39 0 -17 -7 -25 -12 -30 rrcurveto
+          -147 -369 -152 342 rlineto
+          -30 67 -9 24 0 16 0 20 13 9 32 2 rrcurveto
+          28 2 0 19 -265 0 0 -19 rlineto
+          50 -1 15 -12 43 -97 rrcurveto
+          244 -544 15 0 222 563 rlineto
+          29 74 23 16 40 1 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="W">
+          343 -11 20 633 20 hstem
+          932 662 rmoveto
+          -61 callsubr
+          endchar
+        </CharString>
+        <CharString name="X">
+          121 0 20 622 20 hstem
+          704 hmoveto
+          -60 callsubr
+          endchar
+        </CharString>
+        <CharString name="Y">
+          121 0 20 622 20 hstem
+          703 662 rmoveto
+          -59 callsubr
+          endchar
+        </CharString>
+        <CharString name="Z">
+          11 0 38 586 38 hstem
+          598 176 rmoveto
+          -58 callsubr
+          endchar
+        </CharString>
+        <CharString name="ampersand">
+          177 -13 53 -53 71 586 32 hstemhm
+          42 92 68 79 157 53 hintmask 01111100
+          735 111 rmoveto
+          -32 -40 -21 -13 -39 0 -61 0 -44 31 -47 61 55 74 25 49 46 73 25 40 19 19 50 0 rrcurveto
+          21 -216 -21 vlineto
+          50 -5 15 -12 0 -30 0 -46 -27 -48 -65 -86 -56 63 -35 55 -41 88 117 50 38 46 0 72 rrcurveto
+          79 -58 45 -79 -85 -67 -60 -90 vhcurveto
+          0 -45 7 -36 28 -68 rrcurveto
+          -38 -22 rlineto
+          -102 -59 -55 -78 0 -81 rrcurveto
+          hintmask 10011100
+          -100 64 -50 107 vhcurveto
+          81 0 60 25 75 66 rrcurveto
+          hintmask 01111100
+          63 -65 49 -26 58 0 67 0 55 40 29 73 rrcurveto
+          -312 456 rmoveto
+          0 -59 -31 -42 -86 -39 -33 61 -7 33 0 51 rrcurveto
+          50 32 33 47 46 32 -39 -49 vhcurveto
+          hintmask 10011100
+          -34 -452 rmoveto
+          -60 -47 -43 -17 -43 0 -71 0 -53 49 0 85 0 69 31 44 87 56 rrcurveto
+          54 -113 40 -58 58 -68 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="asterisk">
+          -101 656 20 hstem
+          268 471 rmoveto
+          -101 callsubr
+          endchar
+        </CharString>
+        <CharString name="at">
+          320 -14 39 118 33 -32 43 277 44 137 31 hstemhm
+          116 84 121 71 377 40 hintmask 11011111
+          688 73 rmoveto
+          -72 -35 -62 -13 -67 0 -171 0 -116 123 0 174 0 74 23 78 39 56 51 73 74 42 95 0 rrcurveto
+          157 130 -123 -149 -101 -60 -96 -66 -29 -13 14 33 hvcurveto
+          0 7 1 5 1 5 rrcurveto
+          65 254 -69 0 -9 -38 -2 0 rlineto
+          -13 35 -24 17 -36 0 -52 0 -41 -24 -35 -46 -39 -51 -27 -66 0 -65 rrcurveto
+          hintmask 00100111
+          -69 39 -43 49 vhcurveto
+          47 0 47 27 31 45 rrcurveto
+          2 hlineto
+          hintmask 11001111
+          5 -44 40 -29 44 0 rrcurveto
+          99 85 111 129 165 -143 128 -185 hvcurveto
+          -86 0 -76 -23 -62 -46 -84 -63 -57 -103 0 -114 0 -197 158 -144 208 0 70 0 55 13 93 44 rrcurveto
+          hintmask 00110111
+          -128 361 rmoveto
+          0 -45 -18 -65 -26 -49 -20 -36 -29 -22 -27 0 -37 0 -23 33 0 54 0 44 14 43 22 33 31 46 38 24 29 0 rrcurveto
+          29 17 -21 -39 hvcurveto
+          endchar
+        </CharString>
+        <CharString name="colon">
+          -323 -11 111 248 111 hstem
+          81 111 vstem
+          192 403 rmoveto
+          -98 callsubr
+          -359 vmoveto
+          -98 callsubr
+          endchar
+        </CharString>
+        <CharString name="comma">
+          -351 -6 20 -16 20 hstemhm
+          156 39 hintmask 10100000
+          83 -141 rmoveto
+          69 33 43 63 0 60 rrcurveto
+          52 -35 35 -45 -36 -24 -23 -34 -34 21 -17 37 vhcurveto
+          hintmask 01100000
+          11 0 10 4 8 0 8 0 6 -6 0 -7 0 -33 -28 -37 -55 -37 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="dollar">
+          -101 0 20 hstem
+          52 74 104 34 114 80 vstem
+          264 637 rmoveto
+          80 -10 46 -44 20 -83 rrcurveto
+          15 111 hlineto
+          -31 31 -49 17 -81 5 rrcurveto
+          63 -34 -63 vlineto
+          -93 -6 -85 -45 0 -100 0 -84 40 -41 138 -78 rrcurveto
+          -282 vlineto
+          -88 0 -62 53 -21 100 rrcurveto
+          -15 -130 hlineto
+          55 -37 44 -14 87 0 rrcurveto
+          -87 34 87 vlineto
+          115 16 79 38 0 116 0 37 -7 32 -14 21 -27 41 -25 25 -121 65 rrcurveto
+          -34 16 rmoveto
+          -84 57 -20 24 0 44 0 51 33 40 71 14 rrcurveto
+          34 -344 rmoveto
+          91 -54 23 -31 0 -61 0 -71 -33 -31 -81 -17 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="eight">
+          -101 -14 28 634 28 hstemhm
+          56 76 -70 74 219 69 -55 76 hintmask 11000100
+          445 155 rmoveto
+          0 80 -32 54 -123 82 rrcurveto
+          hintmask 11011000
+          99 53 35 34 0 75 rrcurveto
+          78 -69 65 -99 -111 -83 -60 -96 vhcurveto
+          0 -65 21 -38 103 -85 rrcurveto
+          hintmask 11100100
+          -107 -75 -23 -39 0 -69 rrcurveto
+          -94 79 -69 113 vhcurveto
+          120 77 66 103 hvcurveto
+          hintmask 11011000
+          -90 378 rmoveto
+          0 -62 -26 -42 -68 -40 -88 52 -37 46 0 62 rrcurveto
+          62 43 37 67 vhcurveto
+          68 41 -46 -69 hvcurveto
+          hintmask 11100100
+          -84 -261 rmoveto
+          68 -46 30 -40 0 -62 rrcurveto
+          -65 -45 -45 -65 -76 -51 52 92 vhcurveto
+          0 65 21 41 59 48 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="equal">
+          120 66 134 66 hstem
+          637 320 rmoveto
+          -93 callsubr
+          589 -200 rmoveto
+          -93 callsubr
+          endchar
+        </CharString>
+        <CharString name="exclam">
+          -268 -9 106 559 20 hstem
+          130 106 vstem
+          175 176 rmoveto
+          -107 callsubr
+          103 -515 rmoveto
+          -106 callsubr
+          endchar
+        </CharString>
+        <CharString name="five">
+          -101 -14 37 560 79 hstem
+          356 70 vstem
+          438 681 rmoveto
+          -9 7 rlineto
+          -16 -21 -9 -5 -23 0 rrcurveto
+          -207 0 -109 -237 rlineto
+          0 -1 -3 -2 -6 3 -2 9 hvcurveto
+          96 0 70 -32 47 -38 45 -36 22 -50 0 -64 0 -86 -65 -83 -70 0 -20 0 -23 9 -28 23 -32 26 -19 5 -23 0 rrcurveto
+          -28 -17 -13 -25 -38 52 -24 75 hvcurveto
+          68 0 55 15 47 34 68 50 30 62 0 96 0 53 -9 38 -26 36 -57 79 -50 22 -143 27 rrcurveto
+          40 85 194 0 rlineto
+          16 0 8 6 3 7 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="four">
+          -101 0 20 147 64 425 20 hstem
+          292 78 vstem
+          473 167 rmoveto
+          64 -103 445 -44 vlineto
+          -314 -445 0 -64 280 0 0 -167 78 0 0 167 rlineto
+          -78 64 rmoveto
+          -240 0 240 343 rlineto
+          endchar
+        </CharString>
+        <CharString name="greater">
+          621 249 rmoveto
+          -92 callsubr
+          endchar
+        </CharString>
+        <CharString name="hyphen">
+          -268 194 63 hstem
+          285 194 rmoveto
+          63 -246 -63 vlineto
+          endchar
+        </CharString>
+        <CharString name="less">
+          621 -24 rmoveto
+          -94 callsubr
+          endchar
+        </CharString>
+        <CharString name="nine">
+          -101 237 43 368 28 hstem
+          30 92 vstem
+          59 -22 rmoveto
+          130 18 65 24 85 77 80 73 40 109 0 115 0 84 -24 72 -40 50 -39 48 -54 28 -64 0 rrcurveto
+          -119 -89 -101 -135 -122 72 -81 108 hvcurveto
+          59 0 48 15 43 42 -40 -164 -112 -105 -152 -27 rrcurveto
+          306 357 rmoveto
+          -53 -73 -22 -44 -75 -48 75 119 vhcurveto
+          0 54 15 59 20 27 17 22 26 12 30 0 rrcurveto
+          87 45 -86 -168 hvcurveto
+          endchar
+        </CharString>
+        <CharString name="numbersign">
+          -101 0 20 196 55 134 55 182 20 hstem
+          495 405 rmoveto
+          55 -96 vlineto
+          30 202 -58 0 -30 -202 -133 0 31 202 -58 0 -31 -202 -117 0 0 -55 109 0 -21 -134 -115 0 0 -55 106 0 -33 -216 rlineto
+          58 0 33 216 134 0 -31 -216 58 0 31 216 108 0 0 55 -99 0 20 134 rlineto
+          -58 hmoveto
+          -20 -134 -134 0 21 134 rlineto
+          endchar
+        </CharString>
+        <CharString name="one">
+          -101 0 20 636 20 hstem
+          213 86 vstem
+          394 hmoveto
+          15 vlineto
+          -75 -20 18 43 hvcurveto
+          0 597 -9 3 -179 -91 0 -14 27 10 rlineto
+          18 7 17 5 10 0 rrcurveto
+          21 9 -15 -34 hvcurveto
+          -449 vlineto
+          0 -55 -21 -21 -74 -4 rrcurveto
+          -15 vlineto
+          endchar
+        </CharString>
+        <CharString name="parenleft">
+          -268 656 20 hstem
+          48 86 vstem
+          304 -161 rmoveto
+          -140 117 -30 113 0 186 0 193 31 93 139 119 rrcurveto
+          -9 16 rlineto
+          -160 -95 -87 -144 0 -185 0 -170 86 -169 158 -90 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="parenleft.size1">
+          -133 139 81 vstem
+          382 -134 rmoveto
+          -90 110 -72 169 0 306 0 303 72 172 90 110 rrcurveto
+          30 vlineto
+          -142 -134 -101 -214 0 -267 0 -272 101 -209 142 -134 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="parenleft.size2">
+          -12 139 95 vstem
+          503 -243 rmoveto
+          -134 165 -135 265 0 456 0 458 135 294 134 138 rrcurveto
+          33 vlineto
+          -213 -171 -151 -352 0 -400 0 -409 151 -313 213 -200 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="parenleft.size3">
+          149 182 110 vstem
+          667 -346 rmoveto
+          -178 220 -197 349 0 613 0 606 197 396 178 184 rrcurveto
+          44 vlineto
+          -284 -228 -201 -464 0 -538 0 -541 201 -422 284 -267 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="parenleft.size4">
+          207 124 130 vstem
+          732 -453 rmoveto
+          -224 232 -254 504 0 746 0 777 254 473 224 231 rrcurveto
+          56 vlineto
+          -355 -286 -253 -578 0 -673 0 -675 253 -577 355 -286 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="parenright">
+          -268 656 20 hstem
+          199 86 vstem
+          29 660 rmoveto
+          145 -114 25 -115 0 -187 0 -194 -28 -94 -142 -117 rrcurveto
+          9 -16 rlineto
+          159 97 88 142 0 185 0 170 -91 167 -153 92 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="parenright.size1">
+          -133 248 81 vstem
+          86 1036 rmoveto
+          90 -110 72 -169 0 -306 0 -303 -72 -172 -90 -110 rrcurveto
+          -30 vlineto
+          142 134 101 214 0 267 0 272 -101 209 -142 134 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="parenright.size2">
+          7 383 95 vstem
+          114 1530 rmoveto
+          134 -165 135 -262 0 -460 0 -454 -135 -297 -134 -138 rrcurveto
+          -33 vlineto
+          213 171 151 351 0 400 0 409 -151 314 -213 200 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="parenright.size3">
+          149 458 110 vstem
+          83 2018 rmoveto
+          178 -217 197 -351 0 -614 0 -606 -197 -396 -178 -184 rrcurveto
+          -44 vlineto
+          284 228 201 468 0 534 0 545 -201 418 -284 267 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="parenright.size4">
+          207 554 130 vstem
+          76 2510 rmoveto
+          224 -232 254 -482 0 -766 0 -757 -254 -495 -224 -231 rrcurveto
+          -56 vlineto
+          355 286 253 585 0 667 0 681 -253 570 -355 286 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="percent">
+          146 -19 27 294 28 -4 27 230 26 47 20 hstemhm
+          61 71 189 24 56 72 190 23 hintmask 1101111110000000
+          686 213 rmoveto
+          74 -35 43 -61 -96 -93 -104 -107 -78 53 -60 68 vhcurveto
+          86 78 111 121 hvcurveto
+          -23 2 rmoveto
+          -105 callsubr
+          -42 491 rmoveto
+          -104 callsubr
+          hintmask 0011011110000000
+          -103 callsubr
+          130 587 rmoveto
+          -102 callsubr
+          endchar
+        </CharString>
+        <CharString name="period">
+          -351 -11 111 hstem
+          70 111 vstem
+          181 43 rmoveto
+          -99 callsubr
+          endchar
+        </CharString>
+        <CharString name="plus">
+          220 66 hstem
+          309 66 vstem
+          636 220 rmoveto
+          -100 callsubr
+          endchar
+        </CharString>
+        <CharString name="question">
+          -157 -8 106 349 20 179 30 hstem
+          68 51 65 106 32 92 vstem
+          244 164 rmoveto
+          -91 callsubr
+          63 -119 rmoveto
+          -90 callsubr
+          endchar
+        </CharString>
+        <CharString name="quotedbl">
+          -193 656 20 hstem
+          299 431 rmoveto
+          15 98 17 76 0 30 rrcurveto
+          23 -18 18 -24 -25 -18 -18 -25 vhcurveto
+          0 -17 14 -87 18 -98 rrcurveto
+          -148 hmoveto
+          20 96 12 78 0 30 rrcurveto
+          24 -18 17 -24 -25 -18 -17 -26 vhcurveto
+          0 -17 14 -87 18 -98 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="quotesingle">
+          -421 656 20 hstem
+          101 431 rmoveto
+          20 100 12 75 0 30 rrcurveto
+          23 -15 17 -27 -24 -19 -17 -25 vhcurveto
+          0 -28 14 -77 18 -98 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="semicolon">
+          -323 -6 20 -16 20 330 111 hstemhm
+          80 111 -11 39 hintmask 10110000
+          191 403 rmoveto
+          -97 callsubr
+          hintmask 10101000
+          -84 -544 rmoveto
+          -96 callsubr
+          hintmask 10110000
+          -33 -26 -23 -33 -33 21 -19 37 hvcurveto
+          hintmask 01001000
+          -95 callsubr
+          endchar
+        </CharString>
+        <CharString name="seven">
+          -101 -8 20 576 74 hstem
+          449 646 rmoveto
+          16 -369 vlineto
+          -60 -147 18 -9 rlineto
+          42 68 17 14 58 0 rrcurveto
+          215 0 -198 -596 65 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="six">
+          -101 -14 28 368 46 hstem
+          34 93 251 90 vstem
+          446 684 rmoveto
+          -138 -15 -79 -24 -86 -90 -71 -73 -38 -95 0 -108 0 -70 19 -71 28 -51 35 -64 63 -37 79 0 66 0 56 27 37 46 33 40 18 56 0 64 rrcurveto
+          129 -72 80 -117 vhcurveto
+          -44 0 -34 -7 -49 -38 27 151 112 108 157 26 rrcurveto
+          -70 -480 rmoveto
+          -102 -37 -72 -72 -94 -48 100 152 92 59 24 57 93 42 -66 -128 vhcurveto
+          endchar
+        </CharString>
+        <CharString name="slash">
+          -323 -14 20 650 20 hstem
+          287 676 rmoveto
+          -67 0 -229 -690 68 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="slash.size1">
+          -22 552 1066 rmoveto
+          -73 0 -454 -1230 73 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="slash.size2">
+          205 781 1566 rmoveto
+          22 callsubr
+          endchar
+        </CharString>
+        <CharString name="slash.size3">
+          500 1071 2066 rmoveto
+          -103 0 -938 -2460 103 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="slash.size4">
+          708 1293 2566 rmoveto
+          -104 0 -1173 -3075 104 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="three">
+          -101 -14 36 594 60 hstemhm
+          318 79 -38 72 hintmask 11100000
+          61 510 rmoveto
+          41 74 46 32 62 0 66 0 42 -34 0 -68 0 -61 -32 -45 -49 -28 -20 -12 -26 -11 -38 -13 rrcurveto
+          -14 vlineto
+          57 0 23 -3 22 -7 rrcurveto
+          hintmask 11010000
+          69 -20 35 -49 0 -76 0 -85 -56 -68 -74 0 -28 0 -21 5 -37 26 -28 20 -16 6 -17 0 rrcurveto
+          -23 -18 -15 -21 -36 39 -21 73 hvcurveto
+          89 0 95 29 48 64 29 38 17 49 0 53 0 52 -16 46 -28 31 -21 22 -18 12 -44 19 rrcurveto
+          hintmask 11100000
+          67 40 26 50 0 48 0 82 -63 55 -93 0 -104 0 -63 -67 -29 -95 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="two">
+          -101 0 76 526 74 hstem
+          337 86 vstem
+          474 137 rmoveto
+          -14 6 rlineto
+          -33 -56 -21 -11 -42 0 rrcurveto
+          -234 0 165 176 rlineto
+          89 94 39 75 0 79 0 99 -72 77 -113 0 -123 0 -64 -82 -21 -117 rrcurveto
+          21 -5 rlineto
+          40 98 35 32 72 0 85 0 54 -50 0 -91 0 -85 -36 -76 -94 -99 rrcurveto
+          -178 -189 0 -12 391 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="u1D400">
+          121 0 20 177 39 hstem
+          689 hmoveto
+          -22 callsubr
+          17 236 rmoveto
+          -195 0 94 243 rlineto
+          endchar
+        </CharString>
+        <CharString name="u1D401">
+          66 0 32 614 30 hstem
+          426 365 rmoveto
+          -21 callsubr
+          -162 228 rmoveto
+          -20 callsubr
+          -30 vmoveto
+          -19 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D402">
+          121 -19 48 613 20 -4 33 hstemhm
+          hintmask 10100000
+          657 152 rmoveto
+          -44 -46 -28 -25 -32 -18 -40 -22 -42 -12 -44 0 -59 0 -59 30 -29 46 -42 66 -12 73 0 101 0 200 79 113 105 0 67 0 54 -35 46 -55 rrcurveto
+          24 -28 24 -33 19 -54 rrcurveto
+          25 235 -27 hlineto
+          hintmask 01000000
+          -18 callsubr
+          hintmask 10100000
+          -58 24 -50 11 -48 0 rrcurveto
+          -204 -143 -156 -212 -195 135 -147 215 hvcurveto
+          115 0 80 31 93 115 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D403">
+          121 0 35 607 34 hstem
+          14 676 rmoveto
+          -25 vlineto
+          64 -6 19 -15 0 -46 rrcurveto
+          -486 vlineto
+          0 -48 -25 -23 -58 -2 rrcurveto
+          -25 309 vlineto
+          101 0 90 28 61 53 72 63 43 98 0 103 rrcurveto
+          206 -136 125 -228 vhcurveto
+          -67 -78 rmoveto
+          28 14 16 39 vhcurveto
+          75 0 50 -35 38 -66 29 -51 12 -74 0 -86 0 -88 -19 -90 -31 -45 -34 -48 -49 -24 -66 0 rrcurveto
+          -45 -13 20 45 hvcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D404">
+          66 0 31 611 34 hstem
+          641 208 rmoveto
+          -17 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D405">
+          10 0 20 622 34 hstem
+          583 474 rmoveto
+          202 -567 -25 vlineto
+          69 -4 19 -14 0 -58 rrcurveto
+          -472 vlineto
+          0 -56 -14 -14 -74 -8 rrcurveto
+          -25 360 25 vlineto
+          -92 4 -18 15 0 57 rrcurveto
+          228 vlineto
+          102 -2 41 -35 10 -118 rrcurveto
+          25 338 -25 hlineto
+          -14 -113 -39 -36 -100 0 rrcurveto
+          228 vlineto
+          37 19 14 51 vhcurveto
+          88 0 44 -12 31 -27 35 -30 11 -23 14 -76 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D406">
+          177 -19 33 628 20 -4 33 hstemhm
+          hintmask 10100000
+          755 287 rmoveto
+          -344 -25 hlineto
+          86 -5 16 -17 0 -52 rrcurveto
+          -89 vlineto
+          -51 -25 -34 -71 vhcurveto
+          -77 0 -43 29 -35 58 -33 55 -15 79 0 103 0 207 68 113 116 0 47 0 48 -18 43 -38 43 -38 18 -35 35 -76 rrcurveto
+          25 235 -27 hlineto
+          hintmask 01000000
+          -18 callsubr
+          hintmask 10100000
+          -58 24 -45 11 -52 0 rrcurveto
+          -200 -148 -161 -199 -214 154 -136 201 hvcurveto
+          104 0 108 25 65 38 rrcurveto
+          118 vlineto
+          0 63 11 29 75 8 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D407">
+          177 0 20 306 47 283 20 hstem
+          759 hmoveto
+          -16 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D408">
+          -212 0 20 636 20 hstem
+          370 hmoveto
+          -15 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D409">
+          -101 -96 33 719 20 hstem
+          478 676 rmoveto
+          -351 -25 hlineto
+          81 -3 20 -13 0 -58 rrcurveto
+          -550 vlineto
+          -62 -18 -28 -46 -28 -17 12 20 vhcurveto
+          0 26 27 10 0 30 rrcurveto
+          40 -34 36 -38 -37 -34 -37 -37 vhcurveto
+          0 -42 30 -43 37 -25 25 -17 43 -6 40 0 rrcurveto
+          139 73 71 151 hvcurveto
+          430 vlineto
+          0 72 13 18 75 5 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D40A">
+          177 0 20 636 20 hstem
+          769 hmoveto
+          -14 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D40B">
+          66 0 31 625 20 hstem
+          638 227 rmoveto
+          -29 hlineto
+          -27 -62 -26 -48 -34 -31 -41 -37 -46 -18 -83 0 rrcurveto
+          -65 -20 15 43 hvcurveto
+          464 vlineto
+          0 82 13 13 87 4 rrcurveto
+          24 -348 -25 vlineto
+          68 -4 18 -14 0 -57 rrcurveto
+          -478 vlineto
+          0 -53 -13 -11 -73 -9 rrcurveto
+          -25 577 vlineto
+          endchar
+        </CharString>
+        <CharString name="u1D40C">
+          343 0 20 636 20 hstem
+          105 42 vstem
+          921 hmoveto
+          -13 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D40D">
+          121 -18 20 -2 20 636 20 hstemhm
+          104 44 431 44 hintmask 01111000
+          701 676 rmoveto
+          -12 callsubr
+          hintmask 10111000
+          -11 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D40E">
+          177 -19 33 644 33 hstem
+          743 335 rmoveto
+          -10 callsubr
+          -177 -7 rmoveto
+          -9 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D40F">
+          10 0 20 621 35 hstem
+          16 676 rmoveto
+          -8 callsubr
+          -69 -70 rmoveto
+          -7 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D410">
+          177 -176 47 787 33 hstem
+          730 -117 rmoveto
+          -33 -10 -17 -2 -20 0 -47 0 -42 18 -34 34 -19 19 -10 15 -18 37 158 48 95 114 0 171 rrcurveto
+          220 -148 144 -206 -205 -149 -144 -217 vhcurveto
+          0 -155 85 -131 153 -46 21 -44 12 -19 23 -23 56 -56 84 -32 88 0 67 0 47 10 65 25 rrcurveto
+          -170 476 rmoveto
+          -205 -61 -116 -116 -116 -61 114 207 209 64 114 112 114 64 -111 -212 vhcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D411">
+          121 0 20 622 34 hstem
+          716 hmoveto
+          25 vlineto
+          -18 0 -17 9 -9 13 rrcurveto
+          -199 282 rlineto
+          58 16 26 16 28 25 29 27 16 46 0 45 rrcurveto
+          115 -101 57 -195 vhcurveto
+          -308 -25 hlineto
+          74 -5 14 -17 0 -78 rrcurveto
+          -431 vlineto
+          0 -79 -10 -7 -78 -9 rrcurveto
+          -25 338 25 vlineto
+          -78 10 -10 9 0 73 rrcurveto
+          196 28 vlineto
+          207 -313 rlineto
+          -235 597 rmoveto
+          33 14 12 39 93 37 -42 -106 vhcurveto
+          0 -122 -52 -26 -131 -1 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D412">
+          -45 -19 33 646 31 hstem
+          484 474 rmoveto
+          218 -30 vlineto
+          -7 -27 -7 -6 -18 0 -9 0 -12 2 -21 7 -46 17 -32 6 -39 0 -136 0 -83 -74 0 -127 0 -101 58 -55 113 -54 43 -21 45 -20 34 -24 rrcurveto
+          34 -24 23 -28 0 -36 0 -69 -49 -44 -77 0 -58 0 -48 24 -38 48 -29 37 -14 35 -17 71 rrcurveto
+          -29 -248 29 hlineto
+          6 26 8 8 16 0 8 0 11 -3 22 -7 49 -17 37 -7 44 0 148 0 100 87 0 126 0 76 -45 65 -64 35 -51 28 -48 23 -49 26 rrcurveto
+          -80 42 -23 23 0 49 0 57 41 42 69 0 45 0 42 -19 36 -37 34 -35 16 -30 20 -65 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D413">
+          66 0 20 636 20 hstem
+          636 475 rmoveto
+          -6 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D414">
+          121 -19 49 626 20 hstem
+          579 44 vstem
+          701 676 rmoveto
+          -219 -25 hlineto
+          74 -6 23 -20 0 -73 rrcurveto
+          -300 vlineto
+          -148 -59 -74 -117 -97 -49 63 139 vhcurveto
+          318 vlineto
+          0 82 17 13 78 6 rrcurveto
+          25 -336 -25 vlineto
+          70 -7 12 -8 0 -81 rrcurveto
+          -324 vlineto
+          0 -97 23 -54 57 -45 47 -37 64 -17 74 0 71 0 70 20 43 35 49 39 27 77 0 100 rrcurveto
+          314 vlineto
+          0 59 14 16 64 10 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D415">
+          121 -18 20 654 20 hstem
+          701 676 rmoveto
+          -213 -25 hlineto
+          71 -5 15 -7 0 -32 0 -16 -3 -11 -17 -44 rrcurveto
+          -127 -329 -138 334 rlineto
+          -20 48 -3 9 0 15 0 23 15 11 38 2 rrcurveto
+          33 2 0 25 -336 0 0 -25 rlineto
+          50 -7 10 -7 24 -56 rrcurveto
+          256 -599 27 0 228 587 rlineto
+          24 62 14 13 52 7 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D416">
+          399 -15 20 651 20 hstem
+          981 676 rmoveto
+          -182 -25 hlineto
+          54 -3 15 -10 0 -31 0 -13 -2 -15 -5 -14 rrcurveto
+          -112 -343 -108 336 rlineto
+          -11 34 -3 12 0 10 0 24 15 9 44 3 rrcurveto
+          13 1 0 25 -312 0 0 -25 rlineto
+          41 -2 22 -8 9 -25 rrcurveto
+          35 -96 -118 -308 -120 364 rlineto
+          -6 17 -1 7 0 9 0 29 12 9 52 4 rrcurveto
+          25 -294 -25 vlineto
+          42 -6 10 -9 17 -49 rrcurveto
+          212 -602 28 0 186 477 171 -477 27 0 200 602 rlineto
+          13 40 23 21 33 3 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D417">
+          121 0 20 636 20 hstem
+          699 hmoveto
+          -5 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D418">
+          121 0 20 636 20 hstem
+          699 676 rmoveto
+          -220 -25 hlineto
+          68 -5 16 -8 0 -29 0 -20 -15 -39 -26 -43 rrcurveto
+          -109 -178 -120 232 rlineto
+          -12 23 -12 27 0 9 0 23 14 3 40 3 rrcurveto
+          27 2 0 25 -335 0 0 -25 rlineto
+          34 -2 30 -23 19 -34 rrcurveto
+          180 -328 0 -136 rlineto
+          0 -75 -10 -21 -83 -7 rrcurveto
+          -25 347 25 vlineto
+          -79 7 -13 13 0 78 rrcurveto
+          0 178 191 311 rlineto
+          14 23 20 11 34 5 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D419">
+          66 0 35 621 20 hstem
+          634 242 rmoveto
+          -26 hlineto
+          -29 -85 -13 -42 -56 -40 -41 -29 -66 -11 -95 0 rrcurveto
+          -83 0 379 625 0 16 -523 0 -22 -207 28 0 rlineto
+          45 143 41 24 154 2 rrcurveto
+          83 1 -382 -623 0 -16 579 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="u1D41A">
+          -101 -14 20 435 32 hstem
+          473 64 rmoveto
+          -10 -10 rlineto
+          -3 -3 -3 -1 -5 0 rrcurveto
+          -14 -7 11 17 hvcurveto
+          261 vlineto
+          86 -74 48 -122 -113 -78 -46 -80 -42 24 -26 41 40 28 26 34 vhcurveto
+          0 14 -6 12 -13 16 -9 10 -2 7 0 6 rrcurveto
+          24 29 13 36 59 22 -29 -64 vhcurveto
+          -68 vlineto
+          -116 -33 -47 -18 -38 -25 -45 -30 -22 -38 0 -44 0 -74 46 -32 64 0 58 0 46 19 55 50 11 -51 22 -18 49 0 43 0 31 16 38 41 rrcurveto
+          -195 54 rmoveto
+          -22 -31 -24 -9 -24 0 -30 0 -22 23 0 44 0 58 42 41 80 22 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D41B">
+          -45 -14 32 401 54 183 20 hstem
+          211 676 rmoveto
+          -194 -24 hlineto
+          46 -9 9 -9 0 -40 rrcurveto
+          -607 12 vlineto
+          79 56 rlineto
+          46 -42 36 -15 50 0 rrcurveto
+          134 92 103 150 139 -77 95 -111 hvcurveto
+          -49 0 -35 -16 -38 -40 rrcurveto
+          -57 vmoveto
+          18 43 19 16 33 0 rrcurveto
+          62 31 -66 -132 -139 -30 -64 -64 -42 -27 31 48 hvcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D41C">
+          -157 -14 67 389 31 hstem
+          412 109 rmoveto
+          -37 -42 -29 -14 -41 0 rrcurveto
+          -87 -52 84 136 109 35 60 49 42 0 -23 -47 -48 22 -25 34 42 26 25 39 64 -67 46 -87 -133 -104 -102 -149 -143 89 -93 125 hvcurveto
+          80 0 56 30 55 75 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D41D">
+          -45 -14 56 -42 20 397 56 183 20 hstemhm
+          hintmask 01110000
+          534 20 rmoveto
+          23 vlineto
+          -46 3 -13 13 0 42 rrcurveto
+          575 -215 -24 vlineto
+          67 -5 9 -7 0 -46 rrcurveto
+          -183 vlineto
+          -43 46 -30 16 -46 0 rrcurveto
+          -110 -82 -107 -145 hvcurveto
+          hintmask 10000000
+          -137 76 -98 105 vhcurveto
+          53 0 33 16 47 50 rrcurveto
+          -65 vlineto
+          46 13 25 4 62 8 rrcurveto
+          hintmask 10100000
+          -136 100 rmoveto
+          0 -5 -9 -15 -10 -13 -20 -25 -23 -12 -23 0 rrcurveto
+          -53 -25 60 127 129 27 59 58 hvcurveto
+          33 0 30 -24 15 -38 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D41E">
+          -157 -14 72 187 37 160 31 hstem
+          403 126 rmoveto
+          -41 -49 -31 -19 -43 0 -98 0 -15 92 -6 95 rrcurveto
+          252 hlineto
+          -4 74 -18 63 -37 41 -29 32 -41 18 -58 0 rrcurveto
+          -125 -84 -100 -149 -137 83 -101 121 hvcurveto
+          85 0 53 31 60 95 rrcurveto
+          -129 170 rmoveto
+          -134 hlineto
+          120 24 40 46 vhcurveto
+          29 0 12 -17 14 -30 9 -19 0 -21 0 -52 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D41F">
+          -268 0 20 397 44 199 31 hstem
+          14 461 rmoveto
+          -44 57 -330 vlineto
+          0 -45 -11 -13 -46 -5 rrcurveto
+          -24 278 24 vlineto
+          -70 2 -12 16 0 65 rrcurveto
+          310 86 44 -86 120 vlineto
+          59 14 20 36 18 11 -7 -12 vhcurveto
+          0 -14 -22 -8 0 -35 rrcurveto
+          -31 26 -26 34 37 25 27 37 59 -58 41 -85 vhcurveto
+          -64 0 -44 -18 -27 -33 -33 -40 -7 -60 0 -79 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D420">
+          -101 -206 32 571 53 -8 30 hstemhm
+          28 88 288 79 hintmask 01011000
+          254 68 rmoveto
+          -80 -21 10 27 30 30 19 39 hvcurveto
+          91 0 3 0 33 14 61 25 31 43 0 63 0 40 -12 33 -28 25 rrcurveto
+          80 53 -129 hlineto
+          hintmask 10111000
+          -44 16 -28 6 -40 0 -119 0 -84 -62 0 -99 0 -76 53 -55 71 -20 -80 -23 -39 -35 0 -59 0 -37 18 -22 64 -23 -63 -9 -33 -25 0 -41 rrcurveto
+          -60 68 -32 126 167 94 60 94 74 -69 46 -108 vhcurveto
+          244 vmoveto
+          -94 -18 -36 -48 -48 -18 36 93 98 18 33 47 48 19 -37 -93 vhcurveto
+          9 -364 rmoveto
+          65 24 -22 -34 -38 -55 -28 -99 -88 -46 23 44 hvcurveto
+          0 21 7 11 28 23 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D421">
+          -45 0 20 386 67 183 20 hstem
+          534 hmoveto
+          24 vlineto
+          -35 -14 17 46 hvcurveto
+          250 vlineto
+          79 -48 57 -82 vhcurveto
+          -53 0 -45 -19 -49 -58 rrcurveto
+          280 -193 -24 vlineto
+          46 -9 8 -4 0 -48 rrcurveto
+          -502 vlineto
+          0 -56 -8 0 -45 -9 rrcurveto
+          -24 240 24 vlineto
+          -37 5 -11 15 0 41 rrcurveto
+          263 vlineto
+          0 4 7 10 10 10 22 22 24 12 24 0 rrcurveto
+          39 12 -26 -71 hvcurveto
+          -224 vlineto
+          0 -41 -12 -16 -34 -4 rrcurveto
+          -24 vlineto
+          endchar
+        </CharString>
+        <CharString name="u1D422">
+          -323 0 20 516 155 hstem
+          60 155 vstem
+          215 613 rmoveto
+          43 -34 35 -42 -45 -34 -34 -44 -44 33 -33 44 44 34 33 44 vhcurveto
+          41 -613 rmoveto
+          -4 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D423">
+          -268 -203 31 708 155 hstem
+          108 155 vstem
+          263 613 rmoveto
+          42 -35 36 -42 -44 -34 -34 -44 -44 32 -33 44 45 34 33 44 vhcurveto
+          -3 -152 rmoveto
+          -3 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D424">
+          -45 0 20 636 20 hstem
+          543 hmoveto
+          24 vlineto
+          -11 0 -5 3 -9 12 rrcurveto
+          -194 268 rlineto
+          100 105 26 18 63 8 rrcurveto
+          23 -214 -23 vlineto
+          24 -4 rlineto
+          26 -4 9 -6 0 -13 0 -9 -10 -18 -11 -11 rrcurveto
+          -128 -128 0 431 -187 0 0 -24 rlineto
+          34 -3 14 -17 0 -38 rrcurveto
+          -510 vlineto
+          0 -39 -15 -18 -33 -3 rrcurveto
+          -24 239 24 vlineto
+          -47 7 -5 6 0 47 rrcurveto
+          0 114 23 24 95 -134 rlineto
+          18 -25 6 -12 0 -8 0 -12 -14 -6 -28 -1 rrcurveto
+          -24 vlineto
+          endchar
+        </CharString>
+        <CharString name="u1D425">
+          -323 0 20 636 20 hstem
+          256 hmoveto
+          24 vlineto
+          -33 1 -17 16 0 46 rrcurveto
+          589 -191 -24 vlineto
+          35 -3 17 -25 0 -37 rrcurveto
+          -500 vlineto
+          0 -44 -17 -16 -34 -3 rrcurveto
+          -24 vlineto
+          endchar
+        </CharString>
+        <CharString name="u1D426">
+          232 0 20 386 67 hstem
+          814 hmoveto
+          24 vlineto
+          -39 2 -11 14 0 43 rrcurveto
+          248 vlineto
+          89 -53 53 -82 vhcurveto
+          -57 0 -38 -23 -52 -64 -30 63 -35 24 -63 0 -64 0 -42 -26 -38 -61 rrcurveto
+          -3 75 -192 -24 hlineto
+          44 -6 12 -11 0 -47 rrcurveto
+          -287 vlineto
+          0 -41 -11 -13 -44 -8 rrcurveto
+          -24 239 24 vlineto
+          -35 5 -10 16 0 38 rrcurveto
+          261 vlineto
+          24 61 38 23 39 15 -23 -60 vhcurveto
+          -240 vlineto
+          0 -41 -10 -15 -37 -3 rrcurveto
+          -24 232 24 vlineto
+          -35 4 -11 17 0 38 rrcurveto
+          265 vlineto
+          0 5 14 18 13 10 21 18 19 7 18 0 rrcurveto
+          38 15 -33 -62 hvcurveto
+          -228 vlineto
+          0 -42 -10 -14 -38 -3 rrcurveto
+          -24 vlineto
+          endchar
+        </CharString>
+        <CharString name="u1D427">
+          -45 0 20 386 67 hstem
+          539 hmoveto
+          24 vlineto
+          -39 3 -10 12 0 45 rrcurveto
+          247 vlineto
+          87 -53 55 -82 vhcurveto
+          -60 0 -48 -27 -34 -60 rrcurveto
+          -1 75 -191 -24 hlineto
+          44 -7 9 -11 0 -43 rrcurveto
+          -289 vlineto
+          0 -42 -8 -12 -45 -9 rrcurveto
+          -24 240 24 vlineto
+          -37 5 -11 18 0 37 rrcurveto
+          264 vlineto
+          0 4 7 10 10 10 22 22 24 12 24 0 rrcurveto
+          34 17 -29 -56 hvcurveto
+          -237 vlineto
+          0 -40 -12 -16 -34 -4 rrcurveto
+          -24 vlineto
+          endchar
+        </CharString>
+        <CharString name="u1D428">
+          -101 -14 31 425 31 hstem
+          476 229 rmoveto
+          -2 callsubr
+          -147 -11 rmoveto
+          -1 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D429">
+          -45 -13 55 374 57 hstem
+          212 461 rmoveto
+          -192 -24 hlineto
+          44 -7 11 -14 0 -44 rrcurveto
+          -490 vlineto
+          0 -42 -8 -13 -48 -8 rrcurveto
+          -24 272 24 vlineto
+          -61 3 -16 32 0 67 rrcurveto
+          126 vlineto
+          48 -47 25 -13 45 0 rrcurveto
+          120 72 107 155 147 -70 77 -111 hvcurveto
+          -59 0 -38 -21 -34 -60 rrcurveto
+          2 -46 rmoveto
+          0 6 7 14 11 13 20 24 25 13 23 0 rrcurveto
+          51 25 -59 -123 -138 -30 -54 -55 hvcurveto
+          -33 0 -29 27 -15 38 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D42A">
+          -45 -14 54 401 32 hstem
+          536 -205 rmoveto
+          24 vlineto
+          -46 9 -9 9 0 40 rrcurveto
+          595 -16 vlineto
+          -77 -55 rlineto
+          -51 43 -31 13 -50 0 rrcurveto
+          -135 -87 -114 -140 -145 82 -88 103 hvcurveto
+          48 0 40 11 35 45 rrcurveto
+          -132 vlineto
+          0 -66 -8 -16 -65 -9 rrcurveto
+          -24 vlineto
+          73 303 rmoveto
+          -38 -35 -20 -34 -54 -39 60 136 142 39 63 56 40 27 -32 -55 vhcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D42B">
+          -157 0 20 433 20 hstem
+          218 461 rmoveto
+          -190 -24 hlineto
+          43 -6 12 -11 0 -47 rrcurveto
+          -289 vlineto
+          0 -41 -10 -11 -44 -8 rrcurveto
+          -24 265 24 vlineto
+          -61 4 -11 14 0 64 rrcurveto
+          177 vlineto
+          59 26 45 35 vhcurveto
+          8 0 9 -5 11 -18 17 -28 17 -10 26 0 rrcurveto
+          37 26 28 40 46 -34 33 -47 hvcurveto
+          -50 0 -38 -26 -47 -68 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D42C">
+          -212 -14 34 411 20 -11 33 hstemhm
+          hintmask 01000000
+          340 326 rmoveto
+          145 -22 vlineto
+          -6 -15 -6 -5 -13 0 -6 0 -9 2 -16 5 rrcurveto
+          hintmask 10100000
+          -32 11 -23 4 -23 0 -91 0 -66 -62 0 -84 0 -66 41 -46 101 -43 69 -30 27 -25 0 -32 0 -39 -30 -26 -45 0 -70 0 -46 45 -21 87 rrcurveto
+          -28 -165 25 hlineto
+          11 21 6 7 9 0 5 0 8 -2 10 -4 29 -12 51 -11 28 0 91 0 63 62 0 90 0 71 -38 43 -100 42 -68 28 -28 26 0 34 rrcurveto
+          33 28 25 38 vhcurveto
+          27 0 26 -11 22 -21 21 -20 11 -19 15 -43 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D42D">
+          -268 -12 71 358 44 hstem
+          307 112 rmoveto
+          -20 -38 -16 -15 -21 0 rrcurveto
+          -28 -11 20 40 hvcurveto
+          298 95 44 -95 169 -25 vlineto
+          -58 -84 -37 -53 -72 -49 rrcurveto
+          -27 53 -322 vlineto
+          -67 43 -40 69 vhcurveto
+          67 0 40 31 41 82 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D42E">
+          -45 -14 65 -51 20 421 20 hstemhm
+          hintmask 01100000
+          538 20 rmoveto
+          23 vlineto
+          -44 2 -13 16 0 44 rrcurveto
+          356 -200 -24 vlineto
+          50 -4 11 -14 0 -45 rrcurveto
+          -276 vlineto
+          hintmask 10100000
+          -31 -34 -22 -13 -29 0 rrcurveto
+          -36 -20 20 56 hvcurveto
+          334 -188 -24 vlineto
+          41 -8 8 -11 0 -46 rrcurveto
+          -241 vlineto
+          -93 50 -52 83 vhcurveto
+          55 0 38 17 52 49 rrcurveto
+          -65 vlineto
+          43 15 24 4 65 7 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D42F">
+          -101 -14 20 435 20 hstem
+          485 461 rmoveto
+          -151 -24 hlineto
+          43 -2 12 -7 0 -24 0 -12 -3 -14 -8 -19 rrcurveto
+          -72 -182 -79 203 rlineto
+          -8 20 -1 4 0 7 0 15 10 6 25 3 rrcurveto
+          18 2 0 24 -250 0 0 -24 rlineto
+          22 -3 6 -3 6 -8 9 -12 37 -80 20 -49 rrcurveto
+          120 -296 26 0 160 396 rlineto
+          19 46 8 6 31 3 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D430">
+          121 -14 20 435 20 hstem
+          707 461 rmoveto
+          -135 -24 hlineto
+          37 -4 11 -8 0 -23 0 -13 -15 -37 -29 -78 rrcurveto
+          -35 -94 rlineto
+          -10 42 -4 15 -20 70 -20 68 -7 28 0 10 0 16 10 5 38 3 rrcurveto
+          24 -234 -24 vlineto
+          39 -4 1 -1 19 -66 2 -7 2 -6 2 -6 rrcurveto
+          -68 -171 -45 118 rlineto
+          -27 71 -12 31 0 13 0 17 10 7 28 4 rrcurveto
+          24 -222 -24 vlineto
+          26 -5 6 -10 25 -62 rrcurveto
+          148 -374 24 0 125 310 102 -310 23 0 155 401 rlineto
+          13 34 11 11 26 5 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D431">
+          -101 0 20 421 20 hstem
+          484 hmoveto
+          24 vlineto
+          -16 5 -7 4 -7 11 rrcurveto
+          -148 228 101 126 rlineto
+          19 23 20 11 31 5 rrcurveto
+          24 -168 -24 vlineto
+          20 -2 rlineto
+          23 -2 8 -5 0 -15 0 -15 -11 -14 -27 -35 rrcurveto
+          -36 -46 rlineto
+          -4 6 -5 6 -4 5 -33 43 -25 42 0 13 0 12 14 6 33 1 rrcurveto
+          24 -250 -24 vlineto
+          26 -4 6 -5 20 -30 rrcurveto
+          128 -197 rlineto
+          -15 -18 8 9 -15 -19 -15 -19 -14 -18 -14 -19 -56 -75 -20 -16 -37 -2 rrcurveto
+          -24 169 24 vlineto
+          -36 2 -14 7 0 15 0 16 25 40 39 47 6 7 5 7 5 6 13 -21 14 -21 15 -21 24 -35 9 -17 0 -12 0 -12 -13 -6 -32 -2 rrcurveto
+          -24 vlineto
+          endchar
+        </CharString>
+        <CharString name="u1D432">
+          -101 -205 57 589 20 hstem
+          482 461 rmoveto
+          -152 -24 hlineto
+          43 -2 11 -7 0 -24 0 -12 -2 -11 -9 -26 rrcurveto
+          -68 -188 -72 186 rlineto
+          -11 28 -9 26 0 4 0 15 11 8 25 2 rrcurveto
+          16 1 0 24 -249 0 0 -24 rlineto
+          22 -3 6 -3 6 -8 9 -12 38 -84 20 -50 rrcurveto
+          119 -290 -18 -53 rlineto
+          -17 -50 -25 -32 -24 0 -9 0 -8 8 0 9 0 0 0 3 1 3 1 5 1 5 0 4 rrcurveto
+          29 -26 23 -34 -38 -24 -29 -38 -47 40 -32 57 vhcurveto
+          34 0 29 11 21 22 21 23 21 39 35 94 rrcurveto
+          148 398 rlineto
+          16 43 13 8 31 4 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D433">
+          -157 0 20 421 20 hstem
+          420 160 rmoveto
+          -28 hlineto
+          -9 -32 -8 -16 -14 -21 -33 -46 -33 -13 -81 0 rrcurveto
+          -29 0 231 403 0 26 -371 0 -7 -142 26 0 rlineto
+          25 95 24 15 142 0 rrcurveto
+          -234 -404 0 -25 383 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="u1D434">
+          116 0 20 191 37 399 20 hstem
+          685 hmoveto
+          0 callsubr
+          66 248 rmoveto
+          1 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D435">
+          95 0 38 280 35 261 39 hstem
+          198 653 rmoveto
+          2 callsubr
+          -44 -42 rmoveto
+          3 callsubr
+          -9 -45 rmoveto
+          4 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D436">
+          70 -12 48 586 37 hstem
+          711 659 rmoveto
+          -18 hlineto
+          -15 -25 -16 -2 -11 0 -52 0 -52 27 -54 0 rrcurveto
+          -242 -201 -163 -230 -147 95 -131 193 hvcurveto
+          39 0 76 6 78 55 29 21 33 30 27 41 rrcurveto
+          -20 13 rlineto
+          -5 -7 -5 -6 -7 -7 -45 -49 -73 -49 -98 0 rrcurveto
+          -135 -60 102 112 171 118 201 201 121 31 -79 -65 hvcurveto
+          0 -25 0 -9 -1 -8 rrcurveto
+          16 hlineto
+          endchar
+        </CharString>
+        <CharString name="u1D437">
+          189 0 20 595 38 hstem
+          194 653 rmoveto
+          -5 -17 25 0 rlineto
+          59 18 -15 -22 hvcurveto
+          0 -7 -2 -9 -2 -8 rrcurveto
+          -122 -497 rlineto
+          -8 -34 -23 -28 -74 0 rrcurveto
+          -18 0 -4 -16 306 0 rlineto
+          258 163 167 211 172 -101 103 -188 hvcurveto
+          -71 -41 rmoveto
+          16 2 23 1 20 0 rrcurveto
+          147 33 -137 -115 -141 -79 -184 -192 -35 -60 2 41 hvcurveto
+          0 3 0 5 5 19 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D438">
+          113 0 38 295 36 248 36 hstem
+          734 653 rmoveto
+          5 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D439">
+          17 0 20 312 36 248 37 hstem
+          723 653 rmoveto
+          -520 0 -4 -15 19 0 rlineto
+          60 16 -18 -21 hvcurveto
+          0 -7 -2 -9 -2 -8 rrcurveto
+          -124 -497 rlineto
+          -9 -34 -21 -28 -76 0 rrcurveto
+          -18 0 -4 -16 342 0 4 16 -32 0 rlineto
+          -60 -19 16 25 hvcurveto
+          0 7 3 8 1 6 rrcurveto
+          62 254 97 0 rlineto
+          29 41 -3 -47 hvcurveto
+          0 -18 -2 -29 -3 -14 rrcurveto
+          16 0 60 242 -16 0 rlineto
+          -21 -85 -46 -10 -62 0 rrcurveto
+          -83 0 62 248 147 0 rlineto
+          47 0 60 -9 3 -78 0 -14 0 -14 -1 -8 rrcurveto
+          16 hlineto
+          endchar
+        </CharString>
+        <CharString name="u1D43A">
+          133 -12 37 606 37 hstem
+          734 329 rmoveto
+          -313 0 -4 -16 19 0 rlineto
+          61 14 -15 -22 hvcurveto
+          0 -7 -1 -10 -2 -8 rrcurveto
+          -57 -201 rlineto
+          -27 -17 -29 -8 -41 0 rrcurveto
+          -147 -34 97 82 179 89 248 229 113 47 -81 -73 hvcurveto
+          0 -11 -1 -11 -1 -10 rrcurveto
+          16 0 45 223 -16 0 rlineto
+          -3 -16 -18 -21 -22 0 -22 0 -19 15 -22 6 -22 7 -32 9 -49 0 rrcurveto
+          -253 -182 -216 -210 -179 135 -75 157 hvcurveto
+          63 0 75 0 79 47 rrcurveto
+          62 216 rlineto
+          10 36 18 26 85 0 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D43B">
+          272 0 20 300 37 hstem
+          923 653 rmoveto
+          6 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D43C">
+          -121 0 20 hstem
+          530 653 rmoveto
+          7 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D43D">
+          -61 -12 37 hstem
+          620 653 rmoveto
+          -329 0 -4 -16 20 0 rlineto
+          57 18 -15 -24 hvcurveto
+          0 -8 -80 -325 -37 -144 -7 -27 -18 -69 -49 0 -11 0 -13 6 0 15 0 27 27 0 0 32 rrcurveto
+          31 -33 20 -31 -50 -20 -34 -37 -60 57 -37 69 vhcurveto
+          25 0 45 3 39 25 40 27 33 43 19 75 rrcurveto
+          103 413 rlineto
+          9 36 25 27 74 0 rrcurveto
+          18 hlineto
+          endchar
+        </CharString>
+        <CharString name="u1D43E">
+          161 0 20 hstem
+          802 653 rmoveto
+          8 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D43F">
+          107 0 38 hstem
+          668 190 rmoveto
+          -16 hlineto
+          -5 -14 -5 -12 -7 -11 -46 -93 -51 -22 -102 0 rrcurveto
+          -122 hlineto
+          -12 -28 2 22 hvcurveto
+          0 2 1 14 4 15 rrcurveto
+          122 481 rlineto
+          9 37 21 26 75 0 rrcurveto
+          18 0 4 16 -327 0 -4 -16 18 0 rlineto
+          62 14 -18 -19 hvcurveto
+          0 -9 -3 -20 -4 -16 rrcurveto
+          -118 -477 rlineto
+          -8 -34 -24 -28 -75 0 rrcurveto
+          -17 0 -4 -16 569 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="u1D440">
+          404 0 20 hstem
+          1055 653 rmoveto
+          9 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D441">
+          250 0 20 hstem
+          901 653 rmoveto
+          10 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D442">
+          131 -11 38 605 37 hstem
+          712 420 rmoveto
+          11 callsubr
+          -116 23 rmoveto
+          12 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D443">
+          -7 0 20 274 37 285 37 hstem
+          201 653 rmoveto
+          13 callsubr
+          -61 -40 rmoveto
+          14 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D444">
+          180 -152 64 718 37 hstem
+          83 -115 rmoveto
+          11 -12 rlineto
+          27 13 23 5 8 0 30 0 54 -16 50 -11 37 -8 52 -8 53 0 118 0 70 46 59 73 rrcurveto
+          -13 11 rlineto
+          -50 -53 -47 -13 -39 0 -87 0 -93 34 -65 0 -13 0 -14 -1 -16 -3 rrcurveto
+          -4 5 64 43 rlineto
+          245 188 226 204 155 -93 92 -146 -254 -188 -241 -204 hvcurveto
+          0 -132 83 -81 114 -17 rrcurveto
+          365 475 rmoveto
+          -157 -94 -283 -217 -80 -51 52 97 138 90 316 222 65 65 -31 -132 vhcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D445">
+          139 0 20 293 36 267 37 hstem
+          725 16 rmoveto
+          -57 5 -26 42 -19 39 rrcurveto
+          -115 229 rlineto
+          107 21 71 67 0 83 rrcurveto
+          85 -56 66 -160 vhcurveto
+          -271 0 -4 -16 20 0 rlineto
+          60 17 -16 -21 hvcurveto
+          0 -8 -3 -9 -2 -8 rrcurveto
+          -124 -497 rlineto
+          -9 -34 -20 -28 -76 0 rrcurveto
+          -16 0 -4 -16 325 0 4 16 -19 0 rlineto
+          -58 -18 15 24 hvcurveto
+          0 8 1 7 2 8 rrcurveto
+          62 239 rlineto
+          14 -2 10 -2 14 0 8 0 11 1 10 1 rrcurveto
+          150 -315 169 0 rlineto
+          -313 613 rmoveto
+          11 2 9 1 14 0 rrcurveto
+          63 62 -17 -108 -84 -58 -58 -133 hvcurveto
+          -9 0 -10 1 -13 3 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D446">
+          49 -10 38 603 37 hstem
+          680 668 rmoveto
+          -16 hlineto
+          -3 -15 -14 -22 -20 0 -43 0 -57 37 -83 0 -158 0 -65 -94 0 -81 0 -49 45 -54 28 -28 36 -35 48 -36 30 -31 28 -28 18 -33 0 -35 rrcurveto
+          -65 -29 -71 -127 vhcurveto
+          -131 0 -37 88 -3 79 0 10 -1 11 0 10 rrcurveto
+          -16 0 -60 -236 16 0 rlineto
+          16 27 14 11 25 0 76 0 31 -38 94 0 157 0 87 92 0 99 0 87 -59 58 -59 49 -56 48 -61 42 0 69 rrcurveto
+          71 78 26 43 112 48 -76 -84 vhcurveto
+          0 -8 0 -8 -1 -8 rrcurveto
+          16 hlineto
+          endchar
+        </CharString>
+        <CharString name="u1D447">
+          -51 0 20 596 37 hstem
+          670 653 rmoveto
+          15 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D448">
+          104 -13 47 hstem
+          775 653 rmoveto
+          -263 0 -4 -16 19 0 rlineto
+          58 0 16 -14 1 -23 0 -11 -2 -8 -2 -7 rrcurveto
+          -86 -351 rlineto
+          -26 -108 -58 -81 -111 0 -97 0 -47 58 0 54 0 17 3 36 4 15 rrcurveto
+          90 360 rlineto
+          9 36 18 27 79 0 rrcurveto
+          17 0 4 16 -328 0 -4 -16 18 0 rlineto
+          54 21 -11 -20 hvcurveto
+          0 -2 0 -3 -1 -2 rrcurveto
+          -76 -330 rlineto
+          -2 -9 -12 -78 0 -8 0 -91 62 -96 160 0 190 0 56 125 26 106 rrcurveto
+          87 356 rlineto
+          9 36 23 27 75 0 rrcurveto
+          16 hlineto
+          endchar
+        </CharString>
+        <CharString name="u1D449">
+          -26 -16 20 hstem
+          760 653 rmoveto
+          -241 0 -4 -16 20 0 rlineto
+          38 14 -7 -15 hvcurveto
+          0 -21 -19 -29 -19 -24 rrcurveto
+          -280 -366 -4 1 0 391 rlineto
+          33 5 37 65 vhcurveto
+          19 0 4 16 -294 0 -4 -16 rlineto
+          61 38 -24 -58 hvcurveto
+          -571 24 vlineto
+          441 576 rlineto
+          26 34 41 39 65 4 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D44A">
+          315 -16 20 hstem
+          1101 653 rmoveto
+          -242 0 -4 -16 17 0 rlineto
+          41 13 -9 -16 hvcurveto
+          0 -19 -23 -31 -17 -22 rrcurveto
+          -278 -370 -6 0 0 395 rlineto
+          35 9 37 62 vhcurveto
+          18 0 4 16 -285 0 -4 -16 8 0 rlineto
+          63 19 -24 -31 hvcurveto
+          0 -61 -226 -332 -5 0 0 377 rlineto
+          71 37 0 39 vhcurveto
+          7 0 4 16 -288 0 -4 -16 10 0 rlineto
+          56 33 -29 -55 hvcurveto
+          -569 29 vlineto
+          297 442 11 0 0 -442 29 0 438 576 rlineto
+          25 33 43 41 66 3 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D44B">
+          189 0 20 hstem
+          810 653 rmoveto
+          16 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D44C">
+          -66 0 20 hstem
+          695 653 rmoveto
+          -237 0 -4 -16 rlineto
+          58 21 -9 -19 hvcurveto
+          0 -11 -4 -8 -12 -15 rrcurveto
+          -174 -212 -4 0 -75 207 rlineto
+          -2 7 -5 11 0 9 rrcurveto
+          25 8 15 50 vhcurveto
+          19 0 4 16 -299 0 -4 -16 rlineto
+          77 0 29 -31 17 -45 rrcurveto
+          89 -237 -61 -246 rlineto
+          -8 -34 -20 -28 -77 0 rrcurveto
+          -17 0 -4 -16 328 0 4 16 -20 0 rlineto
+          -58 -18 15 21 hvcurveto
+          0 11 1 6 2 9 rrcurveto
+          59 234 209 256 rlineto
+          31 38 38 27 55 4 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D44D">
+          171 0 38 578 37 hstem
+          802 653 rmoveto
+          17 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D44E">
+          -99 -10 61 361 29 hstem
+          40 87 vstem
+          472 428 rmoveto
+          -80 0 -9 -33 -1 0 rlineto
+          -2 22 -21 24 -43 0 rrcurveto
+          -144 -132 -200 -132 -64 26 -55 63 hvcurveto
+          39 0 62 13 72 92 rrcurveto
+          4 hlineto
+          -5 -16 -6 -21 0 -13 0 -32 9 -23 33 0 48 0 54 60 31 47 rrcurveto
+          -12 12 rlineto
+          -39 -52 -15 -2 -10 0 rrcurveto
+          -8 -6 6 11 9 1 2 0 hvcurveto
+          -14 258 rmoveto
+          0 -34 -7 -40 -8 -26 -28 -95 -70 -95 -69 0 -27 0 -31 13 0 53 0 35 10 44 18 45 34 84 60 87 68 0 38 0 12 -30 0 -41 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D44F">
+          -131 -11 29 368 55 207 20 hstem
+          367 83 vstem
+          214 382 rmoveto
+          73 286 -158 -24 3 -16 rlineto
+          8 3 12 1 10 0 13 0 15 -8 1 -13 0 -3 -1 -4 -1 -4 rrcurveto
+          -144 -563 rlineto
+          45 -30 41 -18 53 0 rrcurveto
+          157 109 180 121 82 -41 69 -70 hvcurveto
+          -49 0 -39 -31 -33 -32 rrcurveto
+          -39 -126 rmoveto
+          11 45 34 89 72 0 rrcurveto
+          33 38 -22 -70 -102 -66 -174 -122 -23 -32 12 7 hvcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D450">
+          -186 -11 55 368 29 hstem
+          40 87 210 63 vstem
+          363 111 rmoveto
+          -35 -34 -46 -33 -61 0 rrcurveto
+          -45 -49 22 87 80 57 179 119 24 10 -9 -12 hvcurveto
+          0 -22 -29 -12 0 -24 rrcurveto
+          -17 12 -21 26 38 16 32 38 49 -47 27 -50 -142 -121 -157 -140 -77 40 -78 97 vhcurveto
+          85 0 62 49 51 60 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D451">
+          -69 -12 61 362 30 207 20 hstem
+          40 87 vstem
+          527 668 rmoveto
+          -156 -24 4 -15 rlineto
+          7 1 11 2 9 0 26 0 7 -7 0 -13 0 -4 -1 -4 -1 -4 rrcurveto
+          -53 -209 -2 0 rlineto
+          -1 24 -23 26 -57 0 rrcurveto
+          -154 -103 -223 -103 -53 15 -74 73 hvcurveto
+          37 0 60 12 74 93 rrcurveto
+          5 hlineto
+          -8 -30 -2 -14 0 -20 0 -18 8 -23 32 0 47 0 53 60 30 45 rrcurveto
+          -11 12 rlineto
+          -14 -21 -28 -35 -20 0 -10 0 -5 3 0 6 0 3 1 6 1 3 rrcurveto
+          -15 270 rmoveto
+          0 -31 -5 -39 -11 -35 -28 -92 -66 -94 -70 0 rrcurveto
+          -28 -28 17 49 96 81 200 99 41 15 -24 -47 hvcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D452">
+          -156 -11 59 363 30 hstem
+          331 79 vstem
+          363 112 rmoveto
+          -36 -46 -48 -18 -42 0 rrcurveto
+          -61 -52 43 64 hvcurveto
+          28 vlineto
+          119 6 167 49 0 127 0 62 -60 14 -42 0 -127 0 -110 -125 -25 -111 -5 -23 -1 -14 0 -23 0 -72 39 -84 98 0 62 0 68 24 70 86 rrcurveto
+          -253 112 rmoveto
+          29 85 60 115 74 0 rrcurveto
+          25 19 -18 -25 -88 -94 -69 -104 hvcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D453">
+          -46 -187 29 557 38 202 29 hstem
+          40 64 444 67 vstem
+          248 437 rmoveto
+          -9 -38 77 0 -87 -342 rlineto
+          -15 -57 -30 -158 -65 0 -9 0 -6 3 0 6 0 12 16 1 0 19 rrcurveto
+          15 -10 18 -27 -29 -14 -25 -24 -30 24 -24 48 vhcurveto
+          88 0 54 95 33 83 15 37 8 38 7 27 rrcurveto
+          79 306 76 0 12 38 -80 0 19 70 rlineto
+          21 78 39 54 50 0 12 0 3 -3 0 -5 0 -8 -9 -15 0 -14 rrcurveto
+          -14 7 -11 28 20 21 12 28 35 -29 24 -50 vhcurveto
+          -76 0 -43 -43 -31 -47 -43 -64 5 -77 -63 0 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D454">
+          -109 -187 28 527 45 -2 30 hstemhm
+          20 63 21 80 -71 73 150 79 -66 70 hintmask 01001000
+          492 413 rmoveto
+          -126 hlineto
+          hintmask 10101000
+          -15 15 -31 13 -42 0 -106 0 -68 -79 0 -79 0 -47 27 -47 67 -15 rrcurveto
+          -4 vlineto
+          hintmask 10110101
+          -48 -37 -49 -38 hvcurveto
+          0 -17 7 -19 17 -11 -62 -20 -55 -30 0 -58 rrcurveto
+          -58 50 -57 134 149 66 80 71 vhcurveto
+          0 125 -233 -40 0 63 0 25 29 25 59 8 rrcurveto
+          hintmask 01001010
+          88 11 53 62 0 75 0 22 -4 13 -13 15 rrcurveto
+          81 hlineto
+          -143 -19 rmoveto
+          -56 -25 -102 -71 -44 -12 35 46 vhcurveto
+          hintmask 10101010
+          68 44 71 54 44 10 -25 -37 vhcurveto
+          hintmask 10110001
+          13 -418 rmoveto
+          -44 -36 -46 -91 -95 -44 50 47 vhcurveto
+          0 27 22 26 26 19 8 6 10 5 8 5 rrcurveto
+          60 -22 132 5 0 -78 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D456">
+          -290 -11 20 511 96 hstem
+          50 84 27 96 vstem
+          257 566 rmoveto
+          18 callsubr
+          -30 -125 rmoveto
+          -158 -22 3 -16 rlineto
+          7 2 12 2 9 0 23 0 8 -5 0 -17 0 -3 -1 -7 -1 -2 -79 -307 0 -19 0 -12 0 -27 12 -19 28 0 49 0 50 60 35 45 rrcurveto
+          -13 10 rlineto
+          -36 -39 -6 -12 -22 0 -7 0 -6 3 0 7 0 4 0 4 1 6 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D457">
+          -212 -187 31 676 96 hstem
+          -16 66 226 96 vstem
+          372 566 rmoveto
+          18 callsubr
+          -21 -125 rmoveto
+          -158 -28 3 -17 rlineto
+          7 3 12 2 8 0 rrcurveto
+          22 10 -10 -12 hvcurveto
+          -92 -363 rlineto
+          -24 -96 -33 -76 -46 0 -5 0 -5 6 0 4 0 16 16 3 0 15 rrcurveto
+          23 -16 15 -19 -35 -12 -30 -22 -28 20 -33 53 vhcurveto
+          108 0 56 115 22 88 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D458">
+          -59 -11 66 -55 20 628 20 hstemhm
+          hintmask 01100000
+          527 428 rmoveto
+          -194 0 -4 -12 12 0 rlineto
+          10 16 -7 -12 hvcurveto
+          0 -13 -10 -13 -11 -10 rrcurveto
+          -167 -149 113 456 -158 -25 3 -14 rlineto
+          8 2 10 0 9 0 22 0 10 -11 0 -11 0 -7 -1 -7 -2 -6 rrcurveto
+          -148 -589 76 0 45 166 56 49 63 -147 rlineto
+          hintmask 10000000
+          11 -26 21 -53 46 0 68 0 47 107 12 21 rrcurveto
+          -15 11 rlineto
+          -22 -30 -22 -43 -30 0 -26 0 -12 24 -15 35 rrcurveto
+          -64 152 106 92 rlineto
+          38 33 28 18 67 7 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D459">
+          -283 -10 63 595 20 hstem
+          278 668 rmoveto
+          -157 -23 3 -15 rlineto
+          8 1 12 2 7 0 23 0 8 -10 0 -12 0 -3 0 -8 -3 -13 rrcurveto
+          -128 -510 rlineto
+          -3 -10 -3 -14 0 -12 0 -25 9 -26 30 0 58 0 43 66 34 40 rrcurveto
+          -16 12 rlineto
+          -9 -16 -30 -39 -23 0 rrcurveto
+          -8 -4 6 7 7 1 4 0 hvcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D45A">
+          109 -8 20 -12 20 357 64 hstemhm
+          344 85 149 82 hintmask 01111000
+          667 107 rmoveto
+          -19 -37 -26 -16 -17 0 -9 0 -6 4 0 8 0 3 1 6 1 3 rrcurveto
+          61 231 rlineto
+          6 24 1 19 0 18 0 45 -18 26 -36 0 -20 0 -19 -7 -22 -15 -49 -33 -52 -62 -29 -52 rrcurveto
+          -5 0 10 36 rlineto
+          6 20 3 15 0 17 0 44 -18 37 -37 0 -38 0 -60 -26 -94 -143 rrcurveto
+          -6 0 44 169 -157 -24 2 -14 rlineto
+          8 1 11 1 8 0 rrcurveto
+          27 6 -13 -18 hvcurveto
+          -95 -374 78 0 35 135 rlineto
+          16 61 110 181 52 0 18 0 5 -18 0 -20 0 -15 -3 -16 -2 -9 rrcurveto
+          -77 -299 79 0 34 135 rlineto
+          15 61 110 181 54 0 18 0 6 -18 0 -20 0 -17 -2 -9 -4 -14 rrcurveto
+          -56 -221 rlineto
+          -5 -20 -1 -10 0 -7 rrcurveto
+          hintmask 10011000
+          -39 17 -10 22 vhcurveto
+          47 0 52 50 32 54 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D45B">
+          -104 -8 62 -54 20 359 62 hstemhm
+          364 84 hintmask 10110000
+          467 96 rmoveto
+          -13 11 rlineto
+          -14 -17 -24 -36 -24 0 -11 0 -3 8 -1 6 0 5 0 3 1 2 rrcurveto
+          59 231 rlineto
+          5 20 6 25 0 25 0 33 -12 29 -44 0 -60 0 -60 -66 -40 -50 -2 -3 -49 -70 -5 0 rrcurveto
+          -3 0 51 189 -159 -23 2 -15 rlineto
+          7 1 11 2 10 0 rrcurveto
+          27 3 -14 -18 hvcurveto
+          hintmask 01110000
+          -95 -374 77 0 33 124 rlineto
+          6 21 26 47 36 54 31 47 63 86 38 0 18 0 6 -19 0 -19 0 -14 -5 -20 -2 -8 rrcurveto
+          -57 -221 rlineto
+          -3 -11 -4 -16 0 -13 rrcurveto
+          hintmask 10010000
+          -23 9 -23 32 vhcurveto
+          52 0 48 64 28 34 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D45C">
+          -143 -11 30 392 30 hstem
+          40 81 236 81 vstem
+          438 287 rmoveto
+          19 callsubr
+          -81 31 rmoveto
+          20 callsubr
+          endchar
+        </CharString>
+        <CharString name="u1D45D">
+          -112 -12 29 368 56 hstem
+          390 84 vstem
+          253 370 rmoveto
+          -3 2 18 69 -158 -24 2 -16 rlineto
+          8 2 12 2 8 0 rrcurveto
+          30 1 -14 -19 hvcurveto
+          -117 -464 rlineto
+          -12 -46 -7 -30 -62 -3 rrcurveto
+          -3 -12 234 0 3 12 -16 0 rlineto
+          -50 -11 21 22 hvcurveto
+          0 11 2 11 3 13 rrcurveto
+          24 100 rlineto
+          19 -15 18 -4 24 0 rrcurveto
+          144 110 169 129 101 -33 54 -61 hvcurveto
+          -35 0 -39 -21 -39 -37 rrcurveto
+          -53 -145 rmoveto
+          20 86 52 61 53 0 rrcurveto
+          23 28 -20 -63 -112 -75 -173 -101 hvcurveto
+          -12 0 -20 5 -14 16 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D45E">
+          -143 -12 61 363 29 hstem
+          40 85 vstem
+          463 428 rmoveto
+          -81 0 -7 -39 -3 0 rlineto
+          -2 26 -22 26 -48 0 -28 0 -32 -13 -31 -21 -89 -58 -80 -123 0 -123 0 -57 25 -58 64 0 36 0 60 10 67 93 rrcurveto
+          6 0 -44 -184 rlineto
+          -9 -37 -16 -41 -68 0 rrcurveto
+          -19 0 -3 -12 250 0 3 12 -14 0 rlineto
+          -30 -19 18 30 hvcurveto
+          0 9 0 10 3 10 rrcurveto
+          26 432 rmoveto
+          0 -113 -60 -114 -63 -45 -18 -13 -23 -4 -17 0 -43 0 -9 36 0 27 0 88 59 133 64 52 20 16 20 11 21 0 36 0 13 -31 0 -43 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D45F">
+          -193 0 20 hstem
+          175 267 rmoveto
+          46 174 -157 -23 2 -15 rlineto
+          6 1 14 2 7 0 22 0 11 -6 0 -13 0 -5 -1 -7 -3 -11 rrcurveto
+          -92 -364 77 0 21 76 rlineto
+          5 17 87 256 61 0 18 0 -10 -38 39 0 rrcurveto
+          41 24 44 38 27 -15 21 -29 hvcurveto
+          -55 0 -48 -65 -34 -54 -11 -18 -12 -20 -9 -18 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D460">
+          -161 -11 20 -19 25 400 26 hstemhm
+          138 70 60 74 hintmask 01111000
+          390 441 rmoveto
+          -16 hlineto
+          -8 -10 -11 -10 -11 0 -29 0 -20 20 -43 0 -55 0 -59 -28 0 -79 0 -98 130 -50 0 -82 rrcurveto
+          -39 -14 -50 -59 -63 -41 67 63 vhcurveto
+          -16 0 rlineto
+          hintmask 10011000
+          -25 -156 17 0 rlineto
+          12 17 9 4 9 0 rrcurveto
+          hintmask 01111000
+          12 0 64 -20 34 0 87 0 48 63 0 62 0 95 -134 92 0 55 rrcurveto
+          41 26 17 25 56 36 -56 -64 vhcurveto
+          15 hlineto
+          endchar
+        </CharString>
+        <CharString name="u1D461">
+          -288 -9 66 343 38 hstem
+          283 438 rmoveto
+          -70 0 31 129 -13 0 rlineto
+          -41 -87 -61 -37 -75 -14 rrcurveto
+          -5 -29 77 0 -81 -320 rlineto
+          -3 -11 -2 -13 0 -13 0 -27 8 -25 31 0 59 0 50 76 24 31 rrcurveto
+          -13 11 rlineto
+          -37 -50 -23 -2 0 0 -13 0 -3 6 0 6 0 3 1 5 1 3 rrcurveto
+          78 320 70 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="u1D462">
+          -127 -9 66 hstem
+          30 81 vstem
+          444 428 rmoveto
+          -77 0 -21 -75 rlineto
+          -20 -73 -46 -89 -43 -57 -35 -46 -40 -31 -25 0 -18 0 -8 13 0 21 0 8 1 7 2 9 rrcurveto
+          84 326 -157 -26 3 -15 rlineto
+          7 2 12 1 7 0 22 0 11 -11 0 -12 0 -4 -2 -6 -4 -15 rrcurveto
+          -59 -231 rlineto
+          -5 -19 -3 -19 0 -21 0 -38 14 -36 47 0 23 0 26 11 23 17 40 30 90 121 7 5 rrcurveto
+          1 0 -26 -95 rlineto
+          -3 -12 -1 -16 0 -9 0 -33 11 -19 29 0 58 0 51 74 23 33 rrcurveto
+          -13 11 -5 -6 rlineto
+          -17 -20 -19 -26 -21 0 -10 0 -4 6 0 8 0 3 0 2 1 4 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D463">
+          -95 -9 39 408 20 hstem
+          72 82 285 40 vstem
+          247 454 rmoveto
+          -4 4 rlineto
+          -50 -22 -54 -7 -52 -13 rrcurveto
+          -15 vlineto
+          13 44 0 -20 hvcurveto
+          0 -8 -3 -9 -2 -8 rrcurveto
+          -58 -210 rlineto
+          -6 -20 -3 -21 0 -21 0 -73 52 -20 64 0 74 0 77 44 43 60 58 80 39 101 0 99 rrcurveto
+          32 -5 51 -42 -29 -25 -8 -34 vhcurveto
+          0 -44 61 9 0 -74 0 -25 -7 -25 -8 -24 -32 -90 -70 -113 -108 0 -40 0 -20 28 0 38 0 11 1 11 3 10 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D464">
+          174 -9 39 400 20 -12 20 -18 20 hstemhm
+          72 84 205 78 269 40 hintmask 11001110
+          531 450 rmoveto
+          -84 hlineto
+          -31 -110 -26 -78 -53 -81 -24 -37 -71 -113 -58 -1 -22 0 -6 21 0 26 0 28 8 32 3 13 rrcurveto
+          81 307 rlineto
+          hintmask 10011110
+          -4 3 rlineto
+          -50 -21 -52 -14 -53 -9 rrcurveto
+          -15 vlineto
+          16 38 2 -24 hvcurveto
+          0 -10 -2 -10 -3 -9 rrcurveto
+          -47 -177 rlineto
+          -8 -31 -11 -31 0 -33 0 -53 22 -34 56 0 112 0 82 136 40 89 -11 -40 -12 -43 0 -42 rrcurveto
+          -72 42 -28 69 171 105 238 146 vhcurveto
+          hintmask 10101110
+          32 -5 51 -42 vhcurveto
+          -24 0 -32 -6 2 -36 2 -43 59 8 0 -74 0 -25 -7 -25 -8 -24 -31 -88 -56 -115 -108 0 -40 0 -19 22 0 39 0 24 6 23 6 23 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D465">
+          -51 -9 64 320 66 hstem
+          305 288 rmoveto
+          -20 55 rlineto
+          -12 33 -19 41 -44 24 rrcurveto
+          -132 -33 3 -18 rlineto
+          10 4 15 2 13 0 55 0 26 -41 17 -48 rrcurveto
+          33 -92 -65 -101 rlineto
+          -32 -49 -26 -10 -12 0 -25 0 4 20 -26 0 rrcurveto
+          -25 -13 -21 -16 -22 16 -25 36 hvcurveto
+          56 0 29 43 32 49 rrcurveto
+          63 98 40 -113 rlineto
+          13 -36 19 -41 41 0 58 0 45 96 12 15 rrcurveto
+          -14 10 rlineto
+          -21 -43 -24 -14 -16 0 -45 0 -23 135 -30 62 rrcurveto
+          55 85 rlineto
+          14 22 11 16 30 0 17 0 5 -9 22 0 rrcurveto
+          26 13 23 19 21 -17 12 -32 hvcurveto
+          -50 0 -35 -44 -26 -40 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D466">
+          -105 -183 65 hstem
+          444 52 vstem
+          270 307 rmoveto
+          -9 74 -7 26 -23 33 rrcurveto
+          -150 -25 2 -16 rlineto
+          9 1 16 3 11 0 56 0 9 -60 5 -45 rrcurveto
+          38 -314 rlineto
+          -26 -40 -57 -62 -17 0 -16 0 2 31 -43 0 rrcurveto
+          -13 -27 -11 -30 -24 19 -31 37 hvcurveto
+          68 0 65 98 73 92 42 53 162 240 0 78 rrcurveto
+          39 -25 23 -32 -29 -18 -18 -22 vhcurveto
+          0 -38 52 -10 0 -21 0 -18 -8 -20 -20 -37 -22 -41 -35 -60 -61 -83 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D467">
+          -102 -14 22 -8 20 -6 80 276 80 hstemhm
+          382 66 hintmask 01011000
+          467 450 rmoveto
+          -314 0 -36 -137 18 -4 rlineto
+          23 55 24 6 57 0 rrcurveto
+          131 hlineto
+          -107 -125 -112 -122 -109 -123 rrcurveto
+          11 -10 rlineto
+          hintmask 00101000
+          37 16 45 8 40 0 rrcurveto
+          hintmask 01001000
+          33 0 34 -5 32 -9 rrcurveto
+          hintmask 10111000
+          21 -5 25 -9 22 0 rrcurveto
+          46 60 26 54 24 -15 18 -25 -19 -19 -19 -19 hvcurveto
+          0 -22 12 -3 0 -13 0 -16 -12 -8 -15 0 -45 0 -3 86 -92 0 -23 0 -25 -7 -19 -14 rrcurveto
+          -3 1 rlineto
+          107 120 107 120 108 120 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uni0300">
+          -601 658 20 hstem
+          -147 507 rmoveto
+          -56 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0301">
+          -601 658 20 hstem
+          -371 507 rmoveto
+          -29 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0302">
+          -601 654 20 hstem
+          -75 507 rmoveto
+          -52 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0302.size1">
+          -41 560 554 rmoveto
+          -256 213 -48 0 -256 -213 64 0 216 146 216 -146 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni0302.size2">
+          378 979 564 rmoveto
+          -465 213 -48 0 -466 -213 99 0 391 145 390 -145 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni0302.size3">
+          859 1460 564 rmoveto
+          -706 213 -48 0 -706 -213 153 0 577 145 577 -145 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni0302.size4">
+          1285 1886 599 rmoveto
+          -943 197 -943 -197 5 -26 937 161 939 -161 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni0302.size5">
+          1727 2328 603 rmoveto
+          -1164 213 -1164 -213 5 -31 1158 182 1160 -182 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni0303">
+          -601 532 55 -9 55 hstemhm
+          hintmask 10000000
+          -94 638 rmoveto
+          -31 callsubr
+          hintmask 01000000
+          -50 callsubr
+          hintmask 10000000
+          -49 callsubr
+          hintmask 01000000
+          -30 callsubr
+          hintmask 10000000
+          -47 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0303.size1">
+          -41 598 59 32 61 hstem
+          535 750 rmoveto
+          21 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0303.size2">
+          378 608 59 32 61 hstem
+          953 760 rmoveto
+          24 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0303.size3">
+          859 608 64 35 67 hstem
+          1434 774 rmoveto
+          26 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0303.size4">
+          1285 608 66 31 66 hstem
+          1857 771 rmoveto
+          28 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0303.size5">
+          1727 617 66 31 66 hstem
+          2299 780 rmoveto
+          30 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0304">
+          -601 547 54 hstem
+          -74 547 rmoveto
+          -54 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0305">
+          -601 770 50 hstem
+          20 770 rmoveto
+          -57 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0305.size1">
+          399 770 50 hstem
+          1000 770 rmoveto
+          50 -1000 -50 vlineto
+          endchar
+        </CharString>
+        <CharString name="uni0305.size2">
+          899 770 50 hstem
+          1500 770 rmoveto
+          23 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0305.size3">
+          1399 770 50 hstem
+          2000 770 rmoveto
+          25 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0305.size4">
+          1899 770 50 hstem
+          2500 770 rmoveto
+          27 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0305.size5">
+          2399 770 50 hstem
+          3000 770 rmoveto
+          29 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0306">
+          -601 507 60 77 20 hstem
+          -121 664 rmoveto
+          -44 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0307">
+          -601 523 99 hstem
+          -280 99 vstem
+          -181 572 rmoveto
+          -43 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0308">
+          -601 523 99 hstem
+          -379 99 100 99 vstem
+          -81 572 rmoveto
+          -55 callsubr
+          -199 hmoveto
+          -55 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0309">
+          -601 644 20 64 23 hstem
+          -173 55 vstem
+          -261 581 rmoveto
+          -89 33 vlineto
+          2 35 8 0 rlineto
+          54 46 50 66 53 -21 55 -83 hvcurveto
+          -23 0 -23 -7 -15 -11 -16 -11 -8 -19 0 -11 0 -14 11 -14 17 0 37 0 -19 64 43 0 rrcurveto
+          31 14 -40 -37 -40 -32 -30 -44 hvcurveto
+          endchar
+        </CharString>
+        <CharString name="uni030A">
+          -601 512 34 131 34 hstem
+          -329 34 131 34 vstem
+          -130 611 rmoveto
+          -46 callsubr
+          -34 1 rmoveto
+          -45 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni030B">
+          -601 658 20 hstem
+          -245 507 rmoveto
+          -41 callsubr
+          -302 -148 rmoveto
+          -41 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni030C">
+          -601 654 20 hstem
+          -74 674 rmoveto
+          -42 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni030C.size1">
+          -41 560 767 rmoveto
+          -64 0 -216 -146 -216 146 -64 0 256 -213 48 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni030C.size2">
+          378 979 777 rmoveto
+          -99 0 -390 -145 -391 145 -99 0 466 -213 48 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni030C.size3">
+          859 1460 777 rmoveto
+          -153 0 -577 -145 -577 145 -153 0 706 -213 48 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni030C.size4">
+          1285 1886 770 rmoveto
+          -5 26 -939 -161 -937 161 -5 -26 943 -197 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni030C.size5">
+          1727 2328 785 rmoveto
+          -5 31 -1160 -182 -1158 182 -5 -31 1164 -213 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni030D">
+          -601 -250 55 vstem
+          -195 500 rmoveto
+          200 -55 -200 vlineto
+          endchar
+        </CharString>
+        <CharString name="uni030E">
+          -601 -326 54 84 55 vstem
+          -133 500 rmoveto
+          200 -55 -200 vlineto
+          -84 hmoveto
+          200 -54 -200 vlineto
+          endchar
+        </CharString>
+        <CharString name="uni030F">
+          -601 658 20 hstem
+          -22 507 rmoveto
+          -28 callsubr
+          -116 hmoveto
+          -28 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0310">
+          -601 507 60 101 99 hstem
+          -280 99 vstem
+          -181 717 rmoveto
+          -43 callsubr
+          60 -53 rmoveto
+          -44 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0311">
+          -601 604 60 hstem
+          -92 507 rmoveto
+          -27 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0312">
+          -601 -299 39 vstem
+          -187 745 rmoveto
+          -40 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0313">
+          -601 -199 39 vstem
+          -272 502 rmoveto
+          -39 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0314">
+          -601 -299 39 vstem
+          -178 521 rmoveto
+          -38 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0315">
+          -601 15 39 vstem
+          -58 502 rmoveto
+          -39 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0316">
+          -601 -127 -224 rmoveto
+          -56 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0317">
+          -601 -371 -224 rmoveto
+          -29 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0318">
+          -601 -188 40 hstem
+          -250 40 vstem
+          -210 -283 rmoveto
+          230 -40 -95 -147 -40 147 -95 vlineto
+          endchar
+        </CharString>
+        <CharString name="uni0319">
+          -601 -188 40 hstem
+          -267 40 vstem
+          -80 -188 rmoveto
+          40 -147 95 -40 -230 40 95 vlineto
+          endchar
+        </CharString>
+        <CharString name="uni031A">
+          -601 680 55 hstem
+          -135 55 vstem
+          -80 531 rmoveto
+          204 -300 -55 245 -149 vlineto
+          endchar
+        </CharString>
+        <CharString name="uni031B">
+          -601 454 20 hstem
+          -36 345 rmoveto
+          14 2 23 4 17 10 24 14 9 21 0 22 rrcurveto
+          35 -25 21 -24 -27 -19 -22 -19 vhcurveto
+          0 -32 35 -10 0 -9 0 -3 -8 -8 -19 -4 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uni031C">
+          -601 -360 37 vstem
+          -232 -266 rmoveto
+          -36 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni031D">
+          -601 -240 40 hstem
+          -250 40 vstem
+          -115 -240 rmoveto
+          -35 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni031E">
+          -601 -93 40 hstem
+          -250 40 vstem
+          -115 -93 rmoveto
+          -34 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni031F">
+          -601 -168 44 hstem
+          -249 38 vstem
+          -134 -168 rmoveto
+          44 -77 71 -38 -71 -77 -44 77 -82 38 82 vlineto
+          endchar
+        </CharString>
+        <CharString name="uni0320">
+          -601 -168 44 hstem
+          -134 -168 rmoveto
+          44 -192 -44 vlineto
+          endchar
+        </CharString>
+        <CharString name="uni0321">
+          -601 -287 19 hstem
+          -54 55 vstem
+          1 75 rmoveto
+          -55 -252 hlineto
+          -44 -12 -47 -44 vhcurveto
+          -22 0 -18 15 -14 16 -14 16 -8 16 -21 0 rrcurveto
+          -16 -12 -12 -15 -32 58 -23 70 61 47 62 77 hvcurveto
+          endchar
+        </CharString>
+        <CharString name="uni0322">
+          -601 -287 19 hstem
+          -54 55 vstem
+          1 75 rmoveto
+          -55 -223 hlineto
+          -77 47 -62 61 70 58 23 32 15 -12 12 -16 vhcurveto
+          -21 0 -8 -16 -14 -16 -14 -16 -18 -15 -22 0 rrcurveto
+          -44 -12 47 44 hvcurveto
+          endchar
+        </CharString>
+        <CharString name="uni0323">
+          -601 -217 99 hstem
+          -280 99 vstem
+          -181 -168 rmoveto
+          -43 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0324">
+          -601 -218 99 hstem
+          -379 99 100 99 vstem
+          -81 -168 rmoveto
+          -26 callsubr
+          -199 hmoveto
+          -26 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0325">
+          -601 -268 34 131 34 hstem
+          -329 34 131 34 vstem
+          -130 -168 rmoveto
+          -32 callsubr
+          -34 hmoveto
+          -45 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0326">
+          -601 -199 39 vstem
+          -272 -353 rmoveto
+          -39 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0327">
+          -601 -215 35 180 20 hstem
+          -200 75 vstem
+          -212 hmoveto
+          -53 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0328">
+          -601 -165 56 109 20 hstem
+          -322 56 vstem
+          -157 -73 rmoveto
+          -33 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0329">
+          -601 -250 40 vstem
+          -210 -234 rmoveto
+          132 -40 -132 vlineto
+          endchar
+        </CharString>
+        <CharString name="uni032A">
+          -601 -153 55 hstem
+          -385 55 202 55 vstem
+          -73 -235 rmoveto
+          137 -312 -137 55 82 202 -82 vlineto
+          endchar
+        </CharString>
+        <CharString name="uni032B">
+          -601 -227 40 hstem
+          -380 40 90 40 95 40 vstem
+          -75 -110 rmoveto
+          -40 -35 hlineto
+          -28 -15 -14 -34 -26 -20 14 28 vhcurveto
+          35 -40 -36 vlineto
+          -28 -11 -13 -32 -27 -20 13 28 vhcurveto
+          36 -40 -35 vlineto
+          -50 35 -32 54 vhcurveto
+          32 0 21 16 8 18 7 -18 30 -16 31 0 rrcurveto
+          54 33 32 50 hvcurveto
+          endchar
+        </CharString>
+        <CharString name="uni032C">
+          -601 -74 -73 rmoveto
+          -42 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni032D">
+          -601 -74 -240 rmoveto
+          -52 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni032E">
+          -601 -225 60 hstem
+          -118 -68 rmoveto
+          -44 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni032F">
+          -601 -119 60 hstem
+          -89 -216 rmoveto
+          -27 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0330">
+          -601 -219 55 -9 55 hstemhm
+          hintmask 10000000
+          -94 -113 rmoveto
+          -31 callsubr
+          hintmask 01000000
+          -50 callsubr
+          hintmask 10000000
+          -49 callsubr
+          hintmask 01000000
+          -30 callsubr
+          hintmask 10000000
+          -47 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0330.size1">
+          -41 -269 59 32 61 hstem
+          535 -117 rmoveto
+          21 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0330.size2">
+          378 -269 59 32 61 hstem
+          953 -117 rmoveto
+          24 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0330.size3">
+          859 -283 64 35 67 hstem
+          1434 -117 rmoveto
+          26 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0330.size4">
+          1285 -280 66 31 66 hstem
+          1857 -117 rmoveto
+          28 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0330.size5">
+          1727 -280 66 31 66 hstem
+          2299 -117 rmoveto
+          30 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0331">
+          -601 -195 54 hstem
+          -74 -195 rmoveto
+          -54 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0332">
+          -601 -191 50 hstem
+          20 -191 rmoveto
+          -57 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0332.size1">
+          399 -177 50 hstem
+          1000 -177 rmoveto
+          50 -1000 -50 vlineto
+          endchar
+        </CharString>
+        <CharString name="uni0332.size2">
+          899 -177 50 hstem
+          1500 -177 rmoveto
+          23 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0332.size3">
+          1399 -177 50 hstem
+          2000 -177 rmoveto
+          25 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0332.size4">
+          1899 -177 50 hstem
+          2500 -177 rmoveto
+          27 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0332.size5">
+          2399 -177 50 hstem
+          3000 -177 rmoveto
+          29 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0333">
+          -601 -300 50 59 50 hstem
+          20 -191 rmoveto
+          -57 callsubr
+          500 -109 rmoveto
+          -57 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0334">
+          -601 214 55 -9 55 hstemhm
+          hintmask 10000000
+          -100 320 rmoveto
+          -31 callsubr
+          hintmask 01000000
+          -50 callsubr
+          hintmask 10000000
+          -49 callsubr
+          hintmask 01000000
+          -30 callsubr
+          hintmask 10000000
+          -47 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0335">
+          -601 230 44 hstem
+          -78 230 rmoveto
+          44 -306 -44 vlineto
+          endchar
+        </CharString>
+        <CharString name="uni0336">
+          -601 230 44 hstem
+          20 230 rmoveto
+          44 -500 -44 vlineto
+          endchar
+        </CharString>
+        <CharString name="uni0337">
+          -601 -41 580 rmoveto
+          -54 0 -285 -654 54 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni0338">
+          -601 642 20 hstem
+          31 662 rmoveto
+          -54 0 -357 -818 54 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni0338.size1">
+          -601 -554 -181 rmoveto
+          54 0 375 861 -54 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni0338.size2">
+          -601 -103 729 rmoveto
+          -54 0 -418 -958 54 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni0338.size3">
+          -601 -82 778 rmoveto
+          -54 0 -461 -1058 54 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni0338.size4">
+          -601 -141 830 rmoveto
+          -54 0 -323 -1157 54 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni0338.size5">
+          -601 -554 -429 rmoveto
+          54 0 369 1360 -54 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni0338.size6">
+          -601 -210 1565 rmoveto
+          -54 0 -497 -1846 54 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni0339">
+          -601 -189 37 vstem
+          -280 -71 rmoveto
+          -37 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni033A">
+          -601 -190 55 hstem
+          -385 55 202 55 vstem
+          -73 -190 rmoveto
+          137 -55 -82 -202 82 -55 -137 vlineto
+          endchar
+        </CharString>
+        <CharString name="uni033B">
+          -601 -227 40 94 40 hstem
+          -313 40 86 40 vstem
+          -147 -227 rmoveto
+          174 -166 -174 vlineto
+          126 40 rmoveto
+          -86 94 86 hlineto
+          endchar
+        </CharString>
+        <CharString name="uni033C">
+          -601 -120 55 hstem
+          -79 -91 rmoveto
+          -28 19 -13 7 -24 0 -29 0 -29 -13 -28 -40 -24 40 -29 13 -32 0 -19 0 -16 -6 -30 -20 rrcurveto
+          -51 vlineto
+          32 20 19 2 8 0 46 0 16 -32 20 -37 rrcurveto
+          20 hlineto
+          19 41 20 28 32 0 12 0 21 0 36 -22 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uni033D">
+          -601 -135 688 rmoveto
+          -27 27 -68 -67 -67 67 -29 -27 68 -68 -64 -65 26 -28 66 65 67 -67 26 30 -65 65 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni033E">
+          -601 202 55 -9 55 hintmask 01000000
+          -177 829 rmoveto
+          hintmask 10000000
+          -65 -15 -36 -36 0 -50 0 -22 6 -24 12 -24 rrcurveto
+          hintmask 01000000
+          22 -43 6 -14 0 -20 0 -23 -14 -13 -37 -17 rrcurveto
+          -29 vlineto
+          76 21 30 29 0 55 0 25 -4 15 -20 41 rrcurveto
+          hintmask 10000000
+          -18 38 -4 8 0 18 rrcurveto
+          hintmask 01000000
+          0 23 15 17 31 11 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uni033F">
+          -601 770 50 58 50 hstem
+          20 878 rmoveto
+          -57 callsubr
+          500 -108 rmoveto
+          -57 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0346">
+          -601 627 54 hstem
+          -350 55 173 54 vstem
+          -68 538 rmoveto
+          143 -282 -143 55 89 173 -89 vlineto
+          endchar
+        </CharString>
+        <CharString name="uni0347">
+          -600 -292 54 44 54 hstem
+          323 -194 rmoveto
+          54 -312 -54 vlineto
+          312 -98 rmoveto
+          54 -312 -54 vlineto
+          endchar
+        </CharString>
+        <CharString name="uni034C">
+          -601 532 56 -10 55 38 56 -10 55 hstemhm
+          hintmask 00100000
+          -85 777 rmoveto
+          -51 callsubr
+          hintmask 00010000
+          -50 callsubr
+          hintmask 00100000
+          -49 callsubr
+          hintmask 00010000
+          -48 callsubr
+          hintmask 10100000
+          -47 callsubr
+          -29 -139 rmoveto
+          -51 callsubr
+          hintmask 01000000
+          -50 callsubr
+          hintmask 10000000
+          -49 callsubr
+          hintmask 01000000
+          -48 callsubr
+          hintmask 10000000
+          -47 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni034D">
+          -123 -173 54 hstem
+          478 -146 rmoveto
+          -25 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0359">
+          -601 -87 -157 rmoveto
+          -24 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni035C">
+          -601 -233 60 hstem
+          266 -76 rmoveto
+          -18 -84 -218 -13 -69 0 -69 0 -218 13 -18 84 rrcurveto
+          -29 hlineto
+          -140 233 -17 101 101 233 17 140 vhcurveto
+          endchar
+        </CharString>
+        <CharString name="uni0360">
+          -601 517 55 6 55 hstem
+          336 618 rmoveto
+          -15 -50 -84 4 -40 0 -79 0 -106 21 -77 19 -51 12 -55 9 -52 0 -73 0 -84 -18 -15 -83 rrcurveto
+          29 hlineto
+          15 50 84 -4 40 0 51 0 54 -10 50 -12 85 -21 92 -18 88 0 73 0 84 18 15 83 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uni0361">
+          -601 604 60 hstem
+          295 507 rmoveto
+          140 -233 17 -101 -101 -233 -17 -140 vhcurveto
+          29 hlineto
+          18 84 218 13 69 0 69 0 218 -13 18 -84 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uni0362">
+          -601 -195 55 hstem
+          355 -160 rmoveto
+          -45 19 -36 40 -31 36 rrcurveto
+          -20 -19 27 -33 rlineto
+          4 -5 3 -3 0 -4 rrcurveto
+          -5 -8 -6 -4 vhcurveto
+          -640 -55 639 hlineto
+          9 3 -5 -4 hvcurveto
+          0 -4 -2 -5 -4 -5 rrcurveto
+          -27 -33 19 -19 rlineto
+          32 36 36 40 45 19 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uni20EE">
+          -165 -173 54 hstem
+          436 -173 rmoveto
+          -23 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni20EE.ex">
+          -301 -173 54 hstem
+          300 -173 rmoveto
+          54 -300 -54 vlineto
+          endchar
+        </CharString>
+        <CharString name="uni20EF">
+          -165 -173 54 hstem
+          436 -146 rmoveto
+          -132 106 -18 -8 rlineto
+          32 -27 17 -22 0 -7 rrcurveto
+          -12 -16 -3 -33 vhcurveto
+          -286 -54 286 hlineto
+          28 21 -2 -13 hvcurveto
+          0 -9 -21 -18 -29 -29 rrcurveto
+          18 -8 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni239B">
+          -151 50 124 vstem
+          400 1005 rmoveto
+          -261 -184 -89 -359 0 -303 rrcurveto
+          -159 124 239 vlineto
+          0 254 54 285 172 197 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uni239C">
+          -151 50 124 vstem
+          174 hmoveto
+          1010 -124 -1010 vlineto
+          endchar
+        </CharString>
+        <CharString name="uni239D">
+          -151 50 124 vstem
+          400 30 rmoveto
+          -172 197 -54 285 0 254 rrcurveto
+          239 -124 -159 vlineto
+          0 -303 89 -359 261 -184 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uni239E">
+          -151 276 124 vstem
+          400 hmoveto
+          159 vlineto
+          0 303 -89 359 -261 184 rrcurveto
+          -30 vlineto
+          172 -197 54 -285 0 -254 rrcurveto
+          -239 vlineto
+          endchar
+        </CharString>
+        <CharString name="uni239F">
+          -151 276 124 vstem
+          400 hmoveto
+          1010 -124 -1010 vlineto
+          endchar
+        </CharString>
+        <CharString name="uni23A0">
+          -151 276 124 vstem
+          400 1005 rmoveto
+          -124 -239 hlineto
+          0 -254 -54 -285 -172 -197 rrcurveto
+          -30 vlineto
+          261 184 89 359 0 303 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="zero">
+          -101 -14 26 638 26 hstem
+          476 330 rmoveto
+          205 -91 141 -131 -161 -69 -163 -177 -164 55 -186 171 163 63 172 172 vhcurveto
+          -96 -5 rmoveto
+          -198 -45 -115 -85 -86 -44 114 203 203 45 118 83 88 44 -117 -208 vhcurveto
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+  <MATH>
+    <Version value="0x00010000"/>
+    <MathConstants>
+      <ScriptPercentScaleDown value="75"/>
+      <ScriptScriptPercentScaleDown value="60"/>
+      <DelimitedSubFormulaMinHeight value="1500"/>
+      <DisplayOperatorMinHeight value="1450"/>
+      <MathLeading>
+        <Value value="150"/>
+      </MathLeading>
+      <AxisHeight>
+        <Value value="250"/>
+      </AxisHeight>
+      <AccentBaseHeight>
+        <Value value="450"/>
+      </AccentBaseHeight>
+      <FlattenedAccentBaseHeight>
+        <Value value="662"/>
+      </FlattenedAccentBaseHeight>
+      <SubscriptShiftDown>
+        <Value value="250"/>
+      </SubscriptShiftDown>
+      <SubscriptTopMax>
+        <Value value="400"/>
+      </SubscriptTopMax>
+      <SubscriptBaselineDropMin>
+        <Value value="50"/>
+      </SubscriptBaselineDropMin>
+      <SuperscriptShiftUp>
+        <Value value="400"/>
+      </SuperscriptShiftUp>
+      <SuperscriptShiftUpCramped>
+        <Value value="275"/>
+      </SuperscriptShiftUpCramped>
+      <SuperscriptBottomMin>
+        <Value value="125"/>
+      </SuperscriptBottomMin>
+      <SuperscriptBaselineDropMax>
+        <Value value="375"/>
+      </SuperscriptBaselineDropMax>
+      <SubSuperscriptGapMin>
+        <Value value="264"/>
+      </SubSuperscriptGapMin>
+      <SuperscriptBottomMaxWithSubscript>
+        <Value value="400"/>
+      </SuperscriptBottomMaxWithSubscript>
+      <SpaceAfterScript>
+        <Value value="41"/>
+      </SpaceAfterScript>
+      <UpperLimitGapMin>
+        <Value value="150"/>
+      </UpperLimitGapMin>
+      <UpperLimitBaselineRiseMin>
+        <Value value="300"/>
+      </UpperLimitBaselineRiseMin>
+      <LowerLimitGapMin>
+        <Value value="150"/>
+      </LowerLimitGapMin>
+      <LowerLimitBaselineDropMin>
+        <Value value="600"/>
+      </LowerLimitBaselineDropMin>
+      <StackTopShiftUp>
+        <Value value="480"/>
+      </StackTopShiftUp>
+      <StackTopDisplayStyleShiftUp>
+        <Value value="580"/>
+      </StackTopDisplayStyleShiftUp>
+      <StackBottomShiftDown>
+        <Value value="800"/>
+      </StackBottomShiftDown>
+      <StackBottomDisplayStyleShiftDown>
+        <Value value="900"/>
+      </StackBottomDisplayStyleShiftDown>
+      <StackGapMin>
+        <Value value="198"/>
+      </StackGapMin>
+      <StackDisplayStyleGapMin>
+        <Value value="462"/>
+      </StackDisplayStyleGapMin>
+      <StretchStackTopShiftUp>
+        <Value value="300"/>
+      </StretchStackTopShiftUp>
+      <StretchStackBottomShiftDown>
+        <Value value="600"/>
+      </StretchStackBottomShiftDown>
+      <StretchStackGapAboveMin>
+        <Value value="150"/>
+      </StretchStackGapAboveMin>
+      <StretchStackGapBelowMin>
+        <Value value="150"/>
+      </StretchStackGapBelowMin>
+      <FractionNumeratorShiftUp>
+        <Value value="480"/>
+      </FractionNumeratorShiftUp>
+      <FractionNumeratorDisplayStyleShiftUp>
+        <Value value="580"/>
+      </FractionNumeratorDisplayStyleShiftUp>
+      <FractionDenominatorShiftDown>
+        <Value value="480"/>
+      </FractionDenominatorShiftDown>
+      <FractionDenominatorDisplayStyleShiftDown>
+        <Value value="700"/>
+      </FractionDenominatorDisplayStyleShiftDown>
+      <FractionNumeratorGapMin>
+        <Value value="66"/>
+      </FractionNumeratorGapMin>
+      <FractionNumDisplayStyleGapMin>
+        <Value value="198"/>
+      </FractionNumDisplayStyleGapMin>
+      <FractionRuleThickness>
+        <Value value="66"/>
+      </FractionRuleThickness>
+      <FractionDenominatorGapMin>
+        <Value value="66"/>
+      </FractionDenominatorGapMin>
+      <FractionDenomDisplayStyleGapMin>
+        <Value value="198"/>
+      </FractionDenomDisplayStyleGapMin>
+      <SkewedFractionHorizontalGap>
+        <Value value="300"/>
+      </SkewedFractionHorizontalGap>
+      <SkewedFractionVerticalGap>
+        <Value value="66"/>
+      </SkewedFractionVerticalGap>
+      <OverbarVerticalGap>
+        <Value value="198"/>
+      </OverbarVerticalGap>
+      <OverbarRuleThickness>
+        <Value value="66"/>
+      </OverbarRuleThickness>
+      <OverbarExtraAscender>
+        <Value value="66"/>
+      </OverbarExtraAscender>
+      <UnderbarVerticalGap>
+        <Value value="198"/>
+      </UnderbarVerticalGap>
+      <UnderbarRuleThickness>
+        <Value value="66"/>
+      </UnderbarRuleThickness>
+      <UnderbarExtraDescender>
+        <Value value="66"/>
+      </UnderbarExtraDescender>
+      <RadicalVerticalGap>
+        <Value value="82"/>
+      </RadicalVerticalGap>
+      <RadicalDisplayStyleVerticalGap>
+        <Value value="186"/>
+      </RadicalDisplayStyleVerticalGap>
+      <RadicalRuleThickness>
+        <Value value="66"/>
+      </RadicalRuleThickness>
+      <RadicalExtraAscender>
+        <Value value="66"/>
+      </RadicalExtraAscender>
+      <RadicalKernBeforeDegree>
+        <Value value="277"/>
+      </RadicalKernBeforeDegree>
+      <RadicalKernAfterDegree>
+        <Value value="-555"/>
+      </RadicalKernAfterDegree>
+      <RadicalDegreeBottomRaisePercent value="70"/>
+    </MathConstants>
+    <MathGlyphInfo>
+      <MathItalicsCorrectionInfo>
+        <Coverage>
+          <Glyph value="u1D41F"/>
+          <Glyph value="u1D42B"/>
+          <Glyph value="u1D42D"/>
+          <Glyph value="u1D431"/>
+          <Glyph value="u1D432"/>
+          <Glyph value="u1D433"/>
+          <Glyph value="u1D435"/>
+          <Glyph value="u1D436"/>
+          <Glyph value="u1D437"/>
+          <Glyph value="u1D438"/>
+          <Glyph value="u1D439"/>
+          <Glyph value="u1D43A"/>
+          <Glyph value="u1D43B"/>
+          <Glyph value="u1D43C"/>
+          <Glyph value="u1D43D"/>
+          <Glyph value="u1D43E"/>
+          <Glyph value="u1D440"/>
+          <Glyph value="u1D441"/>
+          <Glyph value="u1D442"/>
+          <Glyph value="u1D443"/>
+          <Glyph value="u1D446"/>
+          <Glyph value="u1D447"/>
+          <Glyph value="u1D448"/>
+          <Glyph value="u1D449"/>
+          <Glyph value="u1D44A"/>
+          <Glyph value="u1D44B"/>
+          <Glyph value="u1D44C"/>
+          <Glyph value="u1D44D"/>
+          <Glyph value="u1D450"/>
+          <Glyph value="u1D451"/>
+          <Glyph value="u1D453"/>
+          <Glyph value="u1D454"/>
+          <Glyph value="u1D457"/>
+          <Glyph value="u1D459"/>
+          <Glyph value="u1D45E"/>
+        </Coverage>
+        <!-- ItalicsCorrectionCount=35 -->
+        <ItalicsCorrection index="0">
+          <Value value="100"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="1">
+          <Value value="60"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="2">
+          <Value value="60"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="3">
+          <Value value="20"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="4">
+          <Value value="20"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="5">
+          <Value value="20"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="6">
+          <Value value="40"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="7">
+          <Value value="80"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="8">
+          <Value value="15"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="9">
+          <Value value="60"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="10">
+          <Value value="145"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="11">
+          <Value value="20"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="12">
+          <Value value="90"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="13">
+          <Value value="90"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="14">
+          <Value value="120"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="15">
+          <Value value="80"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="16">
+          <Value value="90"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="17">
+          <Value value="90"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="18">
+          <Value value="20"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="19">
+          <Value value="150"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="20">
+          <Value value="70"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="21">
+          <Value value="160"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="22">
+          <Value value="110"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="23">
+          <Value value="225"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="24">
+          <Value value="225"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="25">
+          <Value value="60"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="26">
+          <Value value="200"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="27">
+          <Value value="70"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="28">
+          <Value value="50"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="29">
+          <Value value="60"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="30">
+          <Value value="120"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="31">
+          <Value value="40"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="32">
+          <Value value="60"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="33">
+          <Value value="20"/>
+        </ItalicsCorrection>
+        <ItalicsCorrection index="34">
+          <Value value="40"/>
+        </ItalicsCorrection>
+      </MathItalicsCorrectionInfo>
+      <MathTopAccentAttachment>
+        <TopAccentCoverage>
+          <Glyph value="A"/>
+          <Glyph value="B"/>
+          <Glyph value="C"/>
+          <Glyph value="D"/>
+          <Glyph value="E"/>
+          <Glyph value="F"/>
+          <Glyph value="G"/>
+          <Glyph value="H"/>
+          <Glyph value="I"/>
+          <Glyph value="J"/>
+          <Glyph value="K"/>
+          <Glyph value="L"/>
+          <Glyph value="M"/>
+          <Glyph value="N"/>
+          <Glyph value="O"/>
+          <Glyph value="P"/>
+          <Glyph value="Q"/>
+          <Glyph value="R"/>
+          <Glyph value="S"/>
+          <Glyph value="T"/>
+          <Glyph value="U"/>
+          <Glyph value="V"/>
+          <Glyph value="W"/>
+          <Glyph value="X"/>
+          <Glyph value="Y"/>
+          <Glyph value="Z"/>
+          <Glyph value="uni0300"/>
+          <Glyph value="uni0301"/>
+          <Glyph value="uni0302"/>
+          <Glyph value="uni0303"/>
+          <Glyph value="uni0304"/>
+          <Glyph value="uni0305"/>
+          <Glyph value="uni0306"/>
+          <Glyph value="uni0307"/>
+          <Glyph value="uni0308"/>
+          <Glyph value="uni0309"/>
+          <Glyph value="uni030A"/>
+          <Glyph value="uni030B"/>
+          <Glyph value="uni030C"/>
+          <Glyph value="uni030D"/>
+          <Glyph value="uni030E"/>
+          <Glyph value="uni030F"/>
+          <Glyph value="uni0310"/>
+          <Glyph value="uni0311"/>
+          <Glyph value="uni0312"/>
+          <Glyph value="uni0313"/>
+          <Glyph value="uni0314"/>
+          <Glyph value="uni0315"/>
+          <Glyph value="uni0316"/>
+          <Glyph value="uni0317"/>
+          <Glyph value="uni0318"/>
+          <Glyph value="uni0319"/>
+          <Glyph value="uni031A"/>
+          <Glyph value="uni031B"/>
+          <Glyph value="uni031C"/>
+          <Glyph value="uni031D"/>
+          <Glyph value="uni031E"/>
+          <Glyph value="uni031F"/>
+          <Glyph value="uni0320"/>
+          <Glyph value="uni0321"/>
+          <Glyph value="uni0322"/>
+          <Glyph value="uni0323"/>
+          <Glyph value="uni0324"/>
+          <Glyph value="uni0325"/>
+          <Glyph value="uni0326"/>
+          <Glyph value="uni0327"/>
+          <Glyph value="uni0328"/>
+          <Glyph value="uni0329"/>
+          <Glyph value="uni032A"/>
+          <Glyph value="uni032B"/>
+          <Glyph value="uni032C"/>
+          <Glyph value="uni032D"/>
+          <Glyph value="uni032E"/>
+          <Glyph value="uni032F"/>
+          <Glyph value="uni0330"/>
+          <Glyph value="uni0331"/>
+          <Glyph value="uni0332"/>
+          <Glyph value="uni0333"/>
+          <Glyph value="uni0334"/>
+          <Glyph value="uni0335"/>
+          <Glyph value="uni0336"/>
+          <Glyph value="uni0337"/>
+          <Glyph value="uni0338"/>
+          <Glyph value="uni0339"/>
+          <Glyph value="uni033A"/>
+          <Glyph value="uni033B"/>
+          <Glyph value="uni033C"/>
+          <Glyph value="uni033D"/>
+          <Glyph value="uni033E"/>
+          <Glyph value="uni033F"/>
+          <Glyph value="uni0346"/>
+          <Glyph value="uni034C"/>
+          <Glyph value="uni034D"/>
+          <Glyph value="uni0359"/>
+          <Glyph value="uni035C"/>
+          <Glyph value="uni0360"/>
+          <Glyph value="uni0361"/>
+          <Glyph value="uni0362"/>
+          <Glyph value="uni20EE"/>
+          <Glyph value="uni20EF"/>
+          <Glyph value="u1D400"/>
+          <Glyph value="u1D401"/>
+          <Glyph value="u1D402"/>
+          <Glyph value="u1D403"/>
+          <Glyph value="u1D404"/>
+          <Glyph value="u1D405"/>
+          <Glyph value="u1D406"/>
+          <Glyph value="u1D407"/>
+          <Glyph value="u1D408"/>
+          <Glyph value="u1D409"/>
+          <Glyph value="u1D40A"/>
+          <Glyph value="u1D40B"/>
+          <Glyph value="u1D40C"/>
+          <Glyph value="u1D40D"/>
+          <Glyph value="u1D40E"/>
+          <Glyph value="u1D40F"/>
+          <Glyph value="u1D410"/>
+          <Glyph value="u1D411"/>
+          <Glyph value="u1D412"/>
+          <Glyph value="u1D413"/>
+          <Glyph value="u1D414"/>
+          <Glyph value="u1D415"/>
+          <Glyph value="u1D416"/>
+          <Glyph value="u1D417"/>
+          <Glyph value="u1D418"/>
+          <Glyph value="u1D419"/>
+          <Glyph value="u1D41A"/>
+          <Glyph value="u1D41B"/>
+          <Glyph value="u1D41C"/>
+          <Glyph value="u1D41D"/>
+          <Glyph value="u1D41E"/>
+          <Glyph value="u1D41F"/>
+          <Glyph value="u1D420"/>
+          <Glyph value="u1D421"/>
+          <Glyph value="u1D422"/>
+          <Glyph value="u1D423"/>
+          <Glyph value="u1D424"/>
+          <Glyph value="u1D425"/>
+          <Glyph value="u1D426"/>
+          <Glyph value="u1D427"/>
+          <Glyph value="u1D428"/>
+          <Glyph value="u1D429"/>
+          <Glyph value="u1D42A"/>
+          <Glyph value="u1D42B"/>
+          <Glyph value="u1D42C"/>
+          <Glyph value="u1D42D"/>
+          <Glyph value="u1D42E"/>
+          <Glyph value="u1D42F"/>
+          <Glyph value="u1D430"/>
+          <Glyph value="u1D431"/>
+          <Glyph value="u1D432"/>
+          <Glyph value="u1D433"/>
+          <Glyph value="u1D434"/>
+          <Glyph value="u1D435"/>
+          <Glyph value="u1D436"/>
+          <Glyph value="u1D437"/>
+          <Glyph value="u1D438"/>
+          <Glyph value="u1D439"/>
+          <Glyph value="u1D43A"/>
+          <Glyph value="u1D43B"/>
+          <Glyph value="u1D43C"/>
+          <Glyph value="u1D43D"/>
+          <Glyph value="u1D43E"/>
+          <Glyph value="u1D43F"/>
+          <Glyph value="u1D440"/>
+          <Glyph value="u1D441"/>
+          <Glyph value="u1D442"/>
+          <Glyph value="u1D443"/>
+          <Glyph value="u1D444"/>
+          <Glyph value="u1D445"/>
+          <Glyph value="u1D446"/>
+          <Glyph value="u1D447"/>
+          <Glyph value="u1D448"/>
+          <Glyph value="u1D449"/>
+          <Glyph value="u1D44A"/>
+          <Glyph value="u1D44B"/>
+          <Glyph value="u1D44C"/>
+          <Glyph value="u1D44D"/>
+          <Glyph value="u1D44E"/>
+          <Glyph value="u1D44F"/>
+          <Glyph value="u1D450"/>
+          <Glyph value="u1D451"/>
+          <Glyph value="u1D452"/>
+          <Glyph value="u1D453"/>
+          <Glyph value="u1D454"/>
+          <Glyph value="u1D456"/>
+          <Glyph value="u1D457"/>
+          <Glyph value="u1D458"/>
+          <Glyph value="u1D459"/>
+          <Glyph value="u1D45A"/>
+          <Glyph value="u1D45B"/>
+          <Glyph value="u1D45C"/>
+          <Glyph value="u1D45D"/>
+          <Glyph value="u1D45E"/>
+          <Glyph value="u1D45F"/>
+          <Glyph value="u1D460"/>
+          <Glyph value="u1D461"/>
+          <Glyph value="u1D462"/>
+          <Glyph value="u1D463"/>
+          <Glyph value="u1D464"/>
+          <Glyph value="u1D465"/>
+          <Glyph value="u1D466"/>
+          <Glyph value="u1D467"/>
+          <Glyph value="uni0302.size1"/>
+          <Glyph value="uni0303.size1"/>
+          <Glyph value="uni0305.size1"/>
+          <Glyph value="uni030C.size1"/>
+          <Glyph value="uni0330.size1"/>
+          <Glyph value="uni0332.size1"/>
+          <Glyph value="uni0338.size1"/>
+          <Glyph value="uni0302.size2"/>
+          <Glyph value="uni0303.size2"/>
+          <Glyph value="uni0305.size2"/>
+          <Glyph value="uni030C.size2"/>
+          <Glyph value="uni0330.size2"/>
+          <Glyph value="uni0332.size2"/>
+          <Glyph value="uni0338.size2"/>
+          <Glyph value="uni0302.size3"/>
+          <Glyph value="uni0303.size3"/>
+          <Glyph value="uni0305.size3"/>
+          <Glyph value="uni030C.size3"/>
+          <Glyph value="uni0330.size3"/>
+          <Glyph value="uni0332.size3"/>
+          <Glyph value="uni0338.size3"/>
+          <Glyph value="uni0302.size4"/>
+          <Glyph value="uni0303.size4"/>
+          <Glyph value="uni0305.size4"/>
+          <Glyph value="uni030C.size4"/>
+          <Glyph value="uni0330.size4"/>
+          <Glyph value="uni0332.size4"/>
+          <Glyph value="uni0338.size4"/>
+          <Glyph value="uni0302.size5"/>
+          <Glyph value="uni0303.size5"/>
+          <Glyph value="uni0305.size5"/>
+          <Glyph value="uni030C.size5"/>
+          <Glyph value="uni0330.size5"/>
+          <Glyph value="uni0332.size5"/>
+          <Glyph value="uni0338.size5"/>
+          <Glyph value="uni0338.size6"/>
+        </TopAccentCoverage>
+        <!-- TopAccentAttachmentCount=239 -->
+        <TopAccentAttachment index="0">
+          <Value value="361"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="1">
+          <Value value="305"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="2">
+          <Value value="360"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="3">
+          <Value value="350"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="4">
+          <Value value="304"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="5">
+          <Value value="294"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="6">
+          <Value value="376"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="7">
+          <Value value="360"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="8">
+          <Value value="166"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="9">
+          <Value value="214"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="10">
+          <Value value="368"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="11">
+          <Value value="151"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="12">
+          <Value value="438"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="13">
+          <Value value="360"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="14">
+          <Value value="361"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="15">
+          <Value value="279"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="16">
+          <Value value="358"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="17">
+          <Value value="272"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="18">
+          <Value value="267"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="19">
+          <Value value="305"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="20">
+          <Value value="390"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="21">
+          <Value value="388"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="22">
+          <Value value="456"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="23">
+          <Value value="397"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="24">
+          <Value value="396"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="25">
+          <Value value="304"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="26">
+          <Value value="-259"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="27">
+          <Value value="-259"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="28">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="29">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="30">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="31">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="32">
+          <Value value="-232"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="33">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="34">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="35">
+          <Value value="-212"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="36">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="37">
+          <Value value="-212"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="38">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="39">
+          <Value value="-222"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="40">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="41">
+          <Value value="-212"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="42">
+          <Value value="-232"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="43">
+          <Value value="-232"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="44">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="45">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="46">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="47">
+          <Value value="-16"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="48">
+          <Value value="-239"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="49">
+          <Value value="-259"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="50">
+          <Value value="-304"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="51">
+          <Value value="-174"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="52">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="53">
+          <Value value="4"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="54">
+          <Value value="-296"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="55">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="56">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="57">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="58">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="59">
+          <Value value="-117"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="60">
+          <Value value="64"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="61">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="62">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="63">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="64">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="65">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="66">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="67">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="68">
+          <Value value="-229"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="69">
+          <Value value="-228"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="70">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="71">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="72">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="73">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="74">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="75">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="76">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="77">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="78">
+          <Value value="-236"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="79">
+          <Value value="-231"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="80">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="81">
+          <Value value="-210"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="82">
+          <Value value="-174"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="83">
+          <Value value="-216"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="84">
+          <Value value="-229"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="85">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="86">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="87">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="88">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="89">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="90">
+          <Value value="-209"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="91">
+          <Value value="-221"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="92">
+          <Value value="239"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="93">
+          <Value value="-222"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="94">
+          <Value value="-39"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="95">
+          <Value value="-15"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="96">
+          <Value value="-39"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="97">
+          <Value value="-20"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="98">
+          <Value value="218"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="99">
+          <Value value="218"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="100">
+          <Value value="345"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="101">
+          <Value value="318"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="102">
+          <Value value="368"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="103">
+          <Value value="352"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="104">
+          <Value value="328"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="105">
+          <Value value="300"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="106">
+          <Value value="396"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="107">
+          <Value value="390"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="108">
+          <Value value="195"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="109">
+          <Value value="310"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="110">
+          <Value value="410"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="111">
+          <Value value="192"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="112">
+          <Value value="468"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="113">
+          <Value value="358"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="114">
+          <Value value="389"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="115">
+          <Value value="308"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="116">
+          <Value value="389"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="117">
+          <Value value="323"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="118">
+          <Value value="274"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="119">
+          <Value value="334"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="120">
+          <Value value="420"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="121">
+          <Value value="424"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="122">
+          <Value value="540"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="123">
+          <Value value="402"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="124">
+          <Value value="415"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="125">
+          <Value value="331"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="126">
+          <Value value="256"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="127">
+          <Value value="139"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="128">
+          <Value value="261"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="129">
+          <Value value="400"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="130">
+          <Value value="235"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="131">
+          <Value value="246"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="132">
+          <Value value="240"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="133">
+          <Value value="130"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="134">
+          <Value value="136"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="135">
+          <Value value="189"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="136">
+          <Value value="140"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="137">
+          <Value value="136"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="138">
+          <Value value="414"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="139">
+          <Value value="280"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="140">
+          <Value value="250"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="141">
+          <Value value="272"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="142">
+          <Value value="285"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="143">
+          <Value value="231"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="144">
+          <Value value="193"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="145">
+          <Value value="176"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="146">
+          <Value value="249"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="147">
+          <Value value="288"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="148">
+          <Value value="407"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="149">
+          <Value value="262"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="150">
+          <Value value="265"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="151">
+          <Value value="220"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="152">
+          <Value value="510"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="153">
+          <Value value="476"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="154">
+          <Value value="492"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="155">
+          <Value value="494"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="156">
+          <Value value="490"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="157">
+          <Value value="490"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="158">
+          <Value value="488"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="159">
+          <Value value="556"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="160">
+          <Value value="388"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="161">
+          <Value value="478"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="162">
+          <Value value="536"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="163">
+          <Value value="387"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="164">
+          <Value value="624"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="165">
+          <Value value="536"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="166">
+          <Value value="475"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="167">
+          <Value value="477"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="168">
+          <Value value="490"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="169">
+          <Value value="474"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="170">
+          <Value value="469"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="171">
+          <Value value="382"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="172">
+          <Value value="440"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="173">
+          <Value value="438"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="174">
+          <Value value="574"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="175">
+          <Value value="530"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="176">
+          <Value value="391"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="177">
+          <Value value="521"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="178">
+          <Value value="324"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="179">
+          <Value value="240"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="180">
+          <Value value="304"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="181">
+          <Value value="470"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="182">
+          <Value value="299"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="183">
+          <Value value="528"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="184">
+          <Value value="324"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="185">
+          <Value value="208"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="186">
+          <Value value="322"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="187">
+          <Value value="234"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="188">
+          <Value value="222"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="189">
+          <Value value="399"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="190">
+          <Value value="306"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="191">
+          <Value value="293"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="192">
+          <Value value="320"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="193">
+          <Value value="340"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="194">
+          <Value value="244"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="195">
+          <Value value="268"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="196">
+          <Value value="214"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="197">
+          <Value value="277"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="198">
+          <Value value="319"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="199">
+          <Value value="481"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="200">
+          <Value value="304"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="201">
+          <Value value="319"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="202">
+          <Value value="300"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="203">
+          <Value value="280"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="204">
+          <Value value="280"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="205">
+          <Value value="500"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="206">
+          <Value value="280"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="207">
+          <Value value="280"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="208">
+          <Value value="500"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="209">
+          <Value value="-339"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="210">
+          <Value value="489"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="211">
+          <Value value="489"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="212">
+          <Value value="750"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="213">
+          <Value value="489"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="214">
+          <Value value="489"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="215">
+          <Value value="750"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="216">
+          <Value value="-339"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="217">
+          <Value value="730"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="218">
+          <Value value="730"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="219">
+          <Value value="1000"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="220">
+          <Value value="730"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="221">
+          <Value value="730"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="222">
+          <Value value="1000"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="223">
+          <Value value="-340"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="224">
+          <Value value="943"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="225">
+          <Value value="943"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="226">
+          <Value value="1250"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="227">
+          <Value value="943"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="228">
+          <Value value="943"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="229">
+          <Value value="1250"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="230">
+          <Value value="-330"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="231">
+          <Value value="1164"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="232">
+          <Value value="1164"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="233">
+          <Value value="1500"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="234">
+          <Value value="1164"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="235">
+          <Value value="1164"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="236">
+          <Value value="1500"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="237">
+          <Value value="-343"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="238">
+          <Value value="-486"/>
+        </TopAccentAttachment>
+      </MathTopAccentAttachment>
+      <ExtendedShapeCoverage>
+        <Glyph value="parenleft.size1"/>
+        <Glyph value="parenright.size1"/>
+        <Glyph value="slash.size1"/>
+        <Glyph value="uni0338.size1"/>
+        <Glyph value="parenleft.size2"/>
+        <Glyph value="parenright.size2"/>
+        <Glyph value="slash.size2"/>
+        <Glyph value="uni0338.size2"/>
+        <Glyph value="parenleft.size3"/>
+        <Glyph value="parenright.size3"/>
+        <Glyph value="slash.size3"/>
+        <Glyph value="parenleft.size4"/>
+        <Glyph value="parenright.size4"/>
+        <Glyph value="slash.size4"/>
+      </ExtendedShapeCoverage>
+      <MathKernInfo>
+        <MathKernCoverage>
+          <Glyph value="A"/>
+          <Glyph value="F"/>
+          <Glyph value="L"/>
+          <Glyph value="P"/>
+          <Glyph value="T"/>
+          <Glyph value="V"/>
+          <Glyph value="W"/>
+          <Glyph value="Y"/>
+          <Glyph value="u1D400"/>
+          <Glyph value="u1D405"/>
+          <Glyph value="u1D40B"/>
+          <Glyph value="u1D40F"/>
+          <Glyph value="u1D413"/>
+          <Glyph value="u1D418"/>
+        </MathKernCoverage>
+        <!-- MathKernCount=14 -->
+        <MathKernInfoRecords index="0">
+          <TopRightMathKern>
+            <!-- HeightCount=1 -->
+            <CorrectionHeight index="0">
+              <Value value="275"/>
+            </CorrectionHeight>
+            <KernValue index="0">
+              <Value value="-100"/>
+            </KernValue>
+            <KernValue index="1">
+              <Value value="-150"/>
+            </KernValue>
+          </TopRightMathKern>
+          <BottomRightMathKern>
+            <!-- HeightCount=0 -->
+            <KernValue index="0">
+              <Value value="50"/>
+            </KernValue>
+          </BottomRightMathKern>
+        </MathKernInfoRecords>
+        <MathKernInfoRecords index="1">
+          <TopRightMathKern>
+            <!-- HeightCount=0 -->
+            <KernValue index="0">
+              <Value value="50"/>
+            </KernValue>
+          </TopRightMathKern>
+          <BottomRightMathKern>
+            <!-- HeightCount=0 -->
+            <KernValue index="0">
+              <Value value="-150"/>
+            </KernValue>
+          </BottomRightMathKern>
+        </MathKernInfoRecords>
+        <MathKernInfoRecords index="2">
+          <TopRightMathKern>
+            <!-- HeightCount=0 -->
+            <KernValue index="0">
+              <Value value="-150"/>
+            </KernValue>
+          </TopRightMathKern>
+          <BottomRightMathKern>
+            <!-- HeightCount=0 -->
+            <KernValue index="0">
+              <Value value="50"/>
+            </KernValue>
+          </BottomRightMathKern>
+        </MathKernInfoRecords>
+        <MathKernInfoRecords index="3">
+          <TopRightMathKern>
+            <!-- HeightCount=0 -->
+            <KernValue index="0">
+              <Value value="50"/>
+            </KernValue>
+          </TopRightMathKern>
+          <BottomRightMathKern>
+            <!-- HeightCount=0 -->
+            <KernValue index="0">
+              <Value value="-150"/>
+            </KernValue>
+          </BottomRightMathKern>
+        </MathKernInfoRecords>
+        <MathKernInfoRecords index="4">
+          <TopRightMathKern>
+            <!-- HeightCount=0 -->
+            <KernValue index="0">
+              <Value value="50"/>
+            </KernValue>
+          </TopRightMathKern>
+          <BottomRightMathKern>
+            <!-- HeightCount=0 -->
+            <KernValue index="0">
+              <Value value="-100"/>
+            </KernValue>
+          </BottomRightMathKern>
+        </MathKernInfoRecords>
+        <MathKernInfoRecords index="5">
+          <BottomRightMathKern>
+            <!-- HeightCount=0 -->
+            <KernValue index="0">
+              <Value value="-150"/>
+            </KernValue>
+          </BottomRightMathKern>
+        </MathKernInfoRecords>
+        <MathKernInfoRecords index="6">
+          <TopRightMathKern>
+            <!-- HeightCount=0 -->
+            <KernValue index="0">
+              <Value value="50"/>
+            </KernValue>
+          </TopRightMathKern>
+          <BottomRightMathKern>
+            <!-- HeightCount=0 -->
+            <KernValue index="0">
+              <Value value="-150"/>
+            </KernValue>
+          </BottomRightMathKern>
+        </MathKernInfoRecords>
+        <MathKernInfoRecords index="7">
+          <BottomRightMathKern>
+            <!-- HeightCount=0 -->
+            <KernValue index="0">
+              <Value value="-150"/>
+            </KernValue>
+          </BottomRightMathKern>
+        </MathKernInfoRecords>
+        <MathKernInfoRecords index="8">
+          <TopRightMathKern>
+            <!-- HeightCount=1 -->
+            <CorrectionHeight index="0">
+              <Value value="275"/>
+            </CorrectionHeight>
+            <KernValue index="0">
+              <Value value="-100"/>
+            </KernValue>
+            <KernValue index="1">
+              <Value value="-150"/>
+            </KernValue>
+          </TopRightMathKern>
+          <BottomRightMathKern>
+            <!-- HeightCount=0 -->
+            <KernValue index="0">
+              <Value value="30"/>
+            </KernValue>
+          </BottomRightMathKern>
+        </MathKernInfoRecords>
+        <MathKernInfoRecords index="9">
+          <TopRightMathKern>
+            <!-- HeightCount=0 -->
+            <KernValue index="0">
+              <Value value="50"/>
+            </KernValue>
+          </TopRightMathKern>
+          <BottomRightMathKern>
+            <!-- HeightCount=0 -->
+            <KernValue index="0">
+              <Value value="-150"/>
+            </KernValue>
+          </BottomRightMathKern>
+        </MathKernInfoRecords>
+        <MathKernInfoRecords index="10">
+          <TopRightMathKern>
+            <!-- HeightCount=0 -->
+            <KernValue index="0">
+              <Value value="-150"/>
+            </KernValue>
+          </TopRightMathKern>
+        </MathKernInfoRecords>
+        <MathKernInfoRecords index="11">
+          <TopRightMathKern>
+            <!-- HeightCount=0 -->
+            <KernValue index="0">
+              <Value value="50"/>
+            </KernValue>
+          </TopRightMathKern>
+          <BottomRightMathKern>
+            <!-- HeightCount=0 -->
+            <KernValue index="0">
+              <Value value="-150"/>
+            </KernValue>
+          </BottomRightMathKern>
+        </MathKernInfoRecords>
+        <MathKernInfoRecords index="12">
+          <TopRightMathKern>
+            <!-- HeightCount=0 -->
+            <KernValue index="0">
+              <Value value="30"/>
+            </KernValue>
+          </TopRightMathKern>
+          <BottomRightMathKern>
+            <!-- HeightCount=0 -->
+            <KernValue index="0">
+              <Value value="-120"/>
+            </KernValue>
+          </BottomRightMathKern>
+        </MathKernInfoRecords>
+        <MathKernInfoRecords index="13">
+          <TopRightMathKern>
+            <!-- HeightCount=0 -->
+            <KernValue index="0">
+              <Value value="50"/>
+            </KernValue>
+          </TopRightMathKern>
+          <BottomRightMathKern>
+            <!-- HeightCount=0 -->
+            <KernValue index="0">
+              <Value value="-100"/>
+            </KernValue>
+          </BottomRightMathKern>
+        </MathKernInfoRecords>
+      </MathKernInfo>
+    </MathGlyphInfo>
+    <MathVariants>
+      <MinConnectorOverlap value="50"/>
+      <VertGlyphCoverage>
+        <Glyph value="parenleft"/>
+        <Glyph value="parenright"/>
+        <Glyph value="slash"/>
+        <Glyph value="uni0338"/>
+      </VertGlyphCoverage>
+      <HorizGlyphCoverage>
+        <Glyph value="uni0302"/>
+        <Glyph value="uni0303"/>
+        <Glyph value="uni0305"/>
+        <Glyph value="uni030C"/>
+        <Glyph value="uni0330"/>
+        <Glyph value="uni0332"/>
+        <Glyph value="uni034D"/>
+        <Glyph value="uni20EE"/>
+        <Glyph value="uni20EF"/>
+      </HorizGlyphCoverage>
+      <!-- VertGlyphCount=4 -->
+      <!-- HorizGlyphCount=9 -->
+      <VertGlyphConstruction index="0">
+        <GlyphAssembly>
+          <ItalicsCorrection>
+            <Value value="0"/>
+          </ItalicsCorrection>
+          <!-- PartCount=3 -->
+          <PartRecords index="0">
+            <glyph value="uni239D"/>
+            <StartConnectorLength value="0"/>
+            <EndConnectorLength value="150"/>
+            <FullAdvance value="1005"/>
+            <PartFlags value="0"/>
+          </PartRecords>
+          <PartRecords index="1">
+            <glyph value="uni239C"/>
+            <StartConnectorLength value="150"/>
+            <EndConnectorLength value="150"/>
+            <FullAdvance value="1010"/>
+            <PartFlags value="1"/>
+          </PartRecords>
+          <PartRecords index="2">
+            <glyph value="uni239B"/>
+            <StartConnectorLength value="150"/>
+            <EndConnectorLength value="0"/>
+            <FullAdvance value="1005"/>
+            <PartFlags value="0"/>
+          </PartRecords>
+        </GlyphAssembly>
+        <!-- VariantCount=5 -->
+        <MathGlyphVariantRecord index="0">
+          <VariantGlyph value="parenleft"/>
+          <AdvanceMeasurement value="854"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="1">
+          <VariantGlyph value="parenleft.size1"/>
+          <AdvanceMeasurement value="1231"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="2">
+          <VariantGlyph value="parenleft.size2"/>
+          <AdvanceMeasurement value="1846"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="3">
+          <VariantGlyph value="parenleft.size3"/>
+          <AdvanceMeasurement value="2461"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="4">
+          <VariantGlyph value="parenleft.size4"/>
+          <AdvanceMeasurement value="3076"/>
+        </MathGlyphVariantRecord>
+      </VertGlyphConstruction>
+      <VertGlyphConstruction index="1">
+        <GlyphAssembly>
+          <ItalicsCorrection>
+            <Value value="0"/>
+          </ItalicsCorrection>
+          <!-- PartCount=3 -->
+          <PartRecords index="0">
+            <glyph value="uni23A0"/>
+            <StartConnectorLength value="0"/>
+            <EndConnectorLength value="150"/>
+            <FullAdvance value="1005"/>
+            <PartFlags value="0"/>
+          </PartRecords>
+          <PartRecords index="1">
+            <glyph value="uni239F"/>
+            <StartConnectorLength value="150"/>
+            <EndConnectorLength value="150"/>
+            <FullAdvance value="1010"/>
+            <PartFlags value="1"/>
+          </PartRecords>
+          <PartRecords index="2">
+            <glyph value="uni239E"/>
+            <StartConnectorLength value="150"/>
+            <EndConnectorLength value="0"/>
+            <FullAdvance value="1005"/>
+            <PartFlags value="0"/>
+          </PartRecords>
+        </GlyphAssembly>
+        <!-- VariantCount=5 -->
+        <MathGlyphVariantRecord index="0">
+          <VariantGlyph value="parenright"/>
+          <AdvanceMeasurement value="854"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="1">
+          <VariantGlyph value="parenright.size1"/>
+          <AdvanceMeasurement value="1231"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="2">
+          <VariantGlyph value="parenright.size2"/>
+          <AdvanceMeasurement value="1846"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="3">
+          <VariantGlyph value="parenright.size3"/>
+          <AdvanceMeasurement value="2461"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="4">
+          <VariantGlyph value="parenright.size4"/>
+          <AdvanceMeasurement value="3076"/>
+        </MathGlyphVariantRecord>
+      </VertGlyphConstruction>
+      <VertGlyphConstruction index="2">
+        <!-- VariantCount=5 -->
+        <MathGlyphVariantRecord index="0">
+          <VariantGlyph value="slash"/>
+          <AdvanceMeasurement value="691"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="1">
+          <VariantGlyph value="slash.size1"/>
+          <AdvanceMeasurement value="1231"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="2">
+          <VariantGlyph value="slash.size2"/>
+          <AdvanceMeasurement value="1846"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="3">
+          <VariantGlyph value="slash.size3"/>
+          <AdvanceMeasurement value="2461"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="4">
+          <VariantGlyph value="slash.size4"/>
+          <AdvanceMeasurement value="3076"/>
+        </MathGlyphVariantRecord>
+      </VertGlyphConstruction>
+      <VertGlyphConstruction index="3">
+        <!-- VariantCount=7 -->
+        <MathGlyphVariantRecord index="0">
+          <VariantGlyph value="uni0338"/>
+          <AdvanceMeasurement value="819"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="1">
+          <VariantGlyph value="uni0338.size1"/>
+          <AdvanceMeasurement value="862"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="2">
+          <VariantGlyph value="uni0338.size2"/>
+          <AdvanceMeasurement value="959"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="3">
+          <VariantGlyph value="uni0338.size3"/>
+          <AdvanceMeasurement value="1059"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="4">
+          <VariantGlyph value="uni0338.size4"/>
+          <AdvanceMeasurement value="1158"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="5">
+          <VariantGlyph value="uni0338.size5"/>
+          <AdvanceMeasurement value="1361"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="6">
+          <VariantGlyph value="uni0338.size6"/>
+          <AdvanceMeasurement value="1847"/>
+        </MathGlyphVariantRecord>
+      </VertGlyphConstruction>
+      <HorizGlyphConstruction index="0">
+        <!-- VariantCount=6 -->
+        <MathGlyphVariantRecord index="0">
+          <VariantGlyph value="uni0302"/>
+          <AdvanceMeasurement value="312"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="1">
+          <VariantGlyph value="uni0302.size1"/>
+          <AdvanceMeasurement value="561"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="2">
+          <VariantGlyph value="uni0302.size2"/>
+          <AdvanceMeasurement value="980"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="3">
+          <VariantGlyph value="uni0302.size3"/>
+          <AdvanceMeasurement value="1461"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="4">
+          <VariantGlyph value="uni0302.size4"/>
+          <AdvanceMeasurement value="1887"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="5">
+          <VariantGlyph value="uni0302.size5"/>
+          <AdvanceMeasurement value="2329"/>
+        </MathGlyphVariantRecord>
+      </HorizGlyphConstruction>
+      <HorizGlyphConstruction index="1">
+        <!-- VariantCount=6 -->
+        <MathGlyphVariantRecord index="0">
+          <VariantGlyph value="uni0303"/>
+          <AdvanceMeasurement value="331"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="1">
+          <VariantGlyph value="uni0303.size1"/>
+          <AdvanceMeasurement value="561"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="2">
+          <VariantGlyph value="uni0303.size2"/>
+          <AdvanceMeasurement value="980"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="3">
+          <VariantGlyph value="uni0303.size3"/>
+          <AdvanceMeasurement value="1461"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="4">
+          <VariantGlyph value="uni0303.size4"/>
+          <AdvanceMeasurement value="1887"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="5">
+          <VariantGlyph value="uni0303.size5"/>
+          <AdvanceMeasurement value="2329"/>
+        </MathGlyphVariantRecord>
+      </HorizGlyphConstruction>
+      <HorizGlyphConstruction index="2">
+        <GlyphAssembly>
+          <ItalicsCorrection>
+            <Value value="0"/>
+          </ItalicsCorrection>
+          <!-- PartCount=2 -->
+          <PartRecords index="0">
+            <glyph value="uni0305.size1"/>
+            <StartConnectorLength value="0"/>
+            <EndConnectorLength value="1000"/>
+            <FullAdvance value="1000"/>
+            <PartFlags value="0"/>
+          </PartRecords>
+          <PartRecords index="1">
+            <glyph value="uni0305.size1"/>
+            <StartConnectorLength value="1000"/>
+            <EndConnectorLength value="0"/>
+            <FullAdvance value="1000"/>
+            <PartFlags value="1"/>
+          </PartRecords>
+        </GlyphAssembly>
+        <!-- VariantCount=6 -->
+        <MathGlyphVariantRecord index="0">
+          <VariantGlyph value="uni0305"/>
+          <AdvanceMeasurement value="501"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="1">
+          <VariantGlyph value="uni0305.size1"/>
+          <AdvanceMeasurement value="1001"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="2">
+          <VariantGlyph value="uni0305.size2"/>
+          <AdvanceMeasurement value="1501"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="3">
+          <VariantGlyph value="uni0305.size3"/>
+          <AdvanceMeasurement value="2001"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="4">
+          <VariantGlyph value="uni0305.size4"/>
+          <AdvanceMeasurement value="2501"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="5">
+          <VariantGlyph value="uni0305.size5"/>
+          <AdvanceMeasurement value="3001"/>
+        </MathGlyphVariantRecord>
+      </HorizGlyphConstruction>
+      <HorizGlyphConstruction index="3">
+        <!-- VariantCount=6 -->
+        <MathGlyphVariantRecord index="0">
+          <VariantGlyph value="uni030C"/>
+          <AdvanceMeasurement value="312"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="1">
+          <VariantGlyph value="uni030C.size1"/>
+          <AdvanceMeasurement value="561"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="2">
+          <VariantGlyph value="uni030C.size2"/>
+          <AdvanceMeasurement value="980"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="3">
+          <VariantGlyph value="uni030C.size3"/>
+          <AdvanceMeasurement value="1461"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="4">
+          <VariantGlyph value="uni030C.size4"/>
+          <AdvanceMeasurement value="1887"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="5">
+          <VariantGlyph value="uni030C.size5"/>
+          <AdvanceMeasurement value="2329"/>
+        </MathGlyphVariantRecord>
+      </HorizGlyphConstruction>
+      <HorizGlyphConstruction index="4">
+        <!-- VariantCount=6 -->
+        <MathGlyphVariantRecord index="0">
+          <VariantGlyph value="uni0330"/>
+          <AdvanceMeasurement value="331"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="1">
+          <VariantGlyph value="uni0330.size1"/>
+          <AdvanceMeasurement value="561"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="2">
+          <VariantGlyph value="uni0330.size2"/>
+          <AdvanceMeasurement value="980"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="3">
+          <VariantGlyph value="uni0330.size3"/>
+          <AdvanceMeasurement value="1461"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="4">
+          <VariantGlyph value="uni0330.size4"/>
+          <AdvanceMeasurement value="1887"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="5">
+          <VariantGlyph value="uni0330.size5"/>
+          <AdvanceMeasurement value="2329"/>
+        </MathGlyphVariantRecord>
+      </HorizGlyphConstruction>
+      <HorizGlyphConstruction index="5">
+        <GlyphAssembly>
+          <ItalicsCorrection>
+            <Value value="0"/>
+          </ItalicsCorrection>
+          <!-- PartCount=2 -->
+          <PartRecords index="0">
+            <glyph value="uni0332.size1"/>
+            <StartConnectorLength value="0"/>
+            <EndConnectorLength value="1000"/>
+            <FullAdvance value="1000"/>
+            <PartFlags value="0"/>
+          </PartRecords>
+          <PartRecords index="1">
+            <glyph value="uni0332.size1"/>
+            <StartConnectorLength value="1000"/>
+            <EndConnectorLength value="0"/>
+            <FullAdvance value="1000"/>
+            <PartFlags value="1"/>
+          </PartRecords>
+        </GlyphAssembly>
+        <!-- VariantCount=6 -->
+        <MathGlyphVariantRecord index="0">
+          <VariantGlyph value="uni0332"/>
+          <AdvanceMeasurement value="501"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="1">
+          <VariantGlyph value="uni0332.size1"/>
+          <AdvanceMeasurement value="1001"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="2">
+          <VariantGlyph value="uni0332.size2"/>
+          <AdvanceMeasurement value="1501"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="3">
+          <VariantGlyph value="uni0332.size3"/>
+          <AdvanceMeasurement value="2001"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="4">
+          <VariantGlyph value="uni0332.size4"/>
+          <AdvanceMeasurement value="2501"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="5">
+          <VariantGlyph value="uni0332.size5"/>
+          <AdvanceMeasurement value="3001"/>
+        </MathGlyphVariantRecord>
+      </HorizGlyphConstruction>
+      <HorizGlyphConstruction index="6">
+        <GlyphAssembly>
+          <ItalicsCorrection>
+            <Value value="0"/>
+          </ItalicsCorrection>
+          <!-- PartCount=3 -->
+          <PartRecords index="0">
+            <glyph value="uni20EE"/>
+            <StartConnectorLength value="0"/>
+            <EndConnectorLength value="350"/>
+            <FullAdvance value="436"/>
+            <PartFlags value="0"/>
+          </PartRecords>
+          <PartRecords index="1">
+            <glyph value="uni20EE.ex"/>
+            <StartConnectorLength value="300"/>
+            <EndConnectorLength value="300"/>
+            <FullAdvance value="300"/>
+            <PartFlags value="1"/>
+          </PartRecords>
+          <PartRecords index="2">
+            <glyph value="uni20EF"/>
+            <StartConnectorLength value="350"/>
+            <EndConnectorLength value="0"/>
+            <FullAdvance value="436"/>
+            <PartFlags value="0"/>
+          </PartRecords>
+        </GlyphAssembly>
+        <!-- VariantCount=1 -->
+        <MathGlyphVariantRecord index="0">
+          <VariantGlyph value="uni034D"/>
+          <AdvanceMeasurement value="479"/>
+        </MathGlyphVariantRecord>
+      </HorizGlyphConstruction>
+      <HorizGlyphConstruction index="7">
+        <GlyphAssembly>
+          <ItalicsCorrection>
+            <Value value="0"/>
+          </ItalicsCorrection>
+          <!-- PartCount=2 -->
+          <PartRecords index="0">
+            <glyph value="glyph00955"/>
+            <StartConnectorLength value="0"/>
+            <EndConnectorLength value="350"/>
+            <FullAdvance value="436"/>
+            <PartFlags value="0"/>
+          </PartRecords>
+          <PartRecords index="1">
+            <glyph value="glyph03844"/>
+            <StartConnectorLength value="300"/>
+            <EndConnectorLength value="0"/>
+            <FullAdvance value="300"/>
+            <PartFlags value="1"/>
+          </PartRecords>
+        </GlyphAssembly>
+        <!-- VariantCount=1 -->
+        <MathGlyphVariantRecord index="0">
+          <VariantGlyph value="glyph00955"/>
+          <AdvanceMeasurement value="437"/>
+        </MathGlyphVariantRecord>
+      </HorizGlyphConstruction>
+      <HorizGlyphConstruction index="8">
+        <GlyphAssembly>
+          <ItalicsCorrection>
+            <Value value="0"/>
+          </ItalicsCorrection>
+          <!-- PartCount=2 -->
+          <PartRecords index="0">
+            <glyph value="glyph03844"/>
+            <StartConnectorLength value="0"/>
+            <EndConnectorLength value="300"/>
+            <FullAdvance value="300"/>
+            <PartFlags value="1"/>
+          </PartRecords>
+          <PartRecords index="1">
+            <glyph value="glyph00956"/>
+            <StartConnectorLength value="350"/>
+            <EndConnectorLength value="0"/>
+            <FullAdvance value="436"/>
+            <PartFlags value="0"/>
+          </PartRecords>
+        </GlyphAssembly>
+        <!-- VariantCount=1 -->
+        <MathGlyphVariantRecord index="0">
+          <VariantGlyph value="glyph00956"/>
+          <AdvanceMeasurement value="437"/>
+        </MathGlyphVariantRecord>
+      </HorizGlyphConstruction>
+    </MathVariants>
+  </MATH>
+
+  <hmtx>
+    <mtx name=".notdef" width="250" lsb="0"/>
+    <mtx name="A" width="722" lsb="15"/>
+    <mtx name="B" width="667" lsb="17"/>
+    <mtx name="C" width="667" lsb="28"/>
+    <mtx name="D" width="722" lsb="16"/>
+    <mtx name="E" width="611" lsb="12"/>
+    <mtx name="F" width="556" lsb="11"/>
+    <mtx name="G" width="722" lsb="32"/>
+    <mtx name="H" width="722" lsb="18"/>
+    <mtx name="I" width="333" lsb="18"/>
+    <mtx name="J" width="373" lsb="-6"/>
+    <mtx name="K" width="722" lsb="33"/>
+    <mtx name="L" width="611" lsb="12"/>
+    <mtx name="M" width="889" lsb="12"/>
+    <mtx name="N" width="722" lsb="12"/>
+    <mtx name="O" width="722" lsb="34"/>
+    <mtx name="P" width="557" lsb="16"/>
+    <mtx name="Q" width="722" lsb="34"/>
+    <mtx name="R" width="667" lsb="17"/>
+    <mtx name="S" width="556" lsb="43"/>
+    <mtx name="T" width="611" lsb="17"/>
+    <mtx name="U" width="722" lsb="14"/>
+    <mtx name="V" width="722" lsb="16"/>
+    <mtx name="W" width="944" lsb="5"/>
+    <mtx name="X" width="722" lsb="10"/>
+    <mtx name="Y" width="722" lsb="22"/>
+    <mtx name="Z" width="612" lsb="10"/>
+    <mtx name="ampersand" width="778" lsb="42"/>
+    <mtx name="asterisk" width="500" lsb="68"/>
+    <mtx name="at" width="921" lsb="116"/>
+    <mtx name="colon" width="278" lsb="81"/>
+    <mtx name="comma" width="250" lsb="55"/>
+    <mtx name="dollar" width="500" lsb="44"/>
+    <mtx name="eight" width="500" lsb="56"/>
+    <mtx name="equal" width="685" lsb="48"/>
+    <mtx name="exclam" width="333" lsb="130"/>
+    <mtx name="five" width="500" lsb="31"/>
+    <mtx name="four" width="500" lsb="12"/>
+    <mtx name="greater" width="685" lsb="56"/>
+    <mtx name="hyphen" width="333" lsb="39"/>
+    <mtx name="less" width="685" lsb="56"/>
+    <mtx name="nine" width="500" lsb="30"/>
+    <mtx name="numbersign" width="500" lsb="6"/>
+    <mtx name="one" width="500" lsb="111"/>
+    <mtx name="parenleft" width="333" lsb="48"/>
+    <mtx name="parenleft.size1" width="468" lsb="139"/>
+    <mtx name="parenleft.size2" width="589" lsb="139"/>
+    <mtx name="parenleft.size3" width="750" lsb="182"/>
+    <mtx name="parenleft.size4" width="808" lsb="124"/>
+    <mtx name="parenright" width="333" lsb="29"/>
+    <mtx name="parenright.size1" width="468" lsb="86"/>
+    <mtx name="parenright.size2" width="608" lsb="114"/>
+    <mtx name="parenright.size3" width="750" lsb="83"/>
+    <mtx name="parenright.size4" width="808" lsb="76"/>
+    <mtx name="percent" width="747" lsb="61"/>
+    <mtx name="period" width="250" lsb="70"/>
+    <mtx name="plus" width="685" lsb="48"/>
+    <mtx name="question" width="444" lsb="68"/>
+    <mtx name="quotedbl" width="408" lsb="77"/>
+    <mtx name="quotesingle" width="180" lsb="48"/>
+    <mtx name="semicolon" width="278" lsb="80"/>
+    <mtx name="seven" width="500" lsb="20"/>
+    <mtx name="six" width="500" lsb="34"/>
+    <mtx name="slash" width="278" lsb="-9"/>
+    <mtx name="slash.size1" width="579" lsb="25"/>
+    <mtx name="slash.size2" width="806" lsb="25"/>
+    <mtx name="slash.size3" width="1101" lsb="30"/>
+    <mtx name="slash.size4" width="1309" lsb="16"/>
+    <mtx name="three" width="500" lsb="41"/>
+    <mtx name="two" width="500" lsb="29"/>
+    <mtx name="u1D400" width="722" lsb="9"/>
+    <mtx name="u1D401" width="667" lsb="16"/>
+    <mtx name="u1D402" width="722" lsb="49"/>
+    <mtx name="u1D403" width="722" lsb="14"/>
+    <mtx name="u1D404" width="667" lsb="16"/>
+    <mtx name="u1D405" width="611" lsb="16"/>
+    <mtx name="u1D406" width="778" lsb="37"/>
+    <mtx name="u1D407" width="778" lsb="21"/>
+    <mtx name="u1D408" width="389" lsb="20"/>
+    <mtx name="u1D409" width="500" lsb="3"/>
+    <mtx name="u1D40A" width="778" lsb="30"/>
+    <mtx name="u1D40B" width="667" lsb="19"/>
+    <mtx name="u1D40C" width="944" lsb="14"/>
+    <mtx name="u1D40D" width="722" lsb="16"/>
+    <mtx name="u1D40E" width="778" lsb="35"/>
+    <mtx name="u1D40F" width="611" lsb="16"/>
+    <mtx name="u1D410" width="778" lsb="35"/>
+    <mtx name="u1D411" width="722" lsb="26"/>
+    <mtx name="u1D412" width="556" lsb="35"/>
+    <mtx name="u1D413" width="667" lsb="31"/>
+    <mtx name="u1D414" width="722" lsb="16"/>
+    <mtx name="u1D415" width="722" lsb="16"/>
+    <mtx name="u1D416" width="1000" lsb="19"/>
+    <mtx name="u1D417" width="722" lsb="16"/>
+    <mtx name="u1D418" width="722" lsb="15"/>
+    <mtx name="u1D419" width="667" lsb="28"/>
+    <mtx name="u1D41A" width="500" lsb="25"/>
+    <mtx name="u1D41B" width="556" lsb="17"/>
+    <mtx name="u1D41C" width="444" lsb="25"/>
+    <mtx name="u1D41D" width="556" lsb="25"/>
+    <mtx name="u1D41E" width="444" lsb="25"/>
+    <mtx name="u1D41F" width="333" lsb="14"/>
+    <mtx name="u1D420" width="500" lsb="28"/>
+    <mtx name="u1D421" width="556" lsb="15"/>
+    <mtx name="u1D422" width="278" lsb="15"/>
+    <mtx name="u1D423" width="333" lsb="-57"/>
+    <mtx name="u1D424" width="556" lsb="22"/>
+    <mtx name="u1D425" width="278" lsb="15"/>
+    <mtx name="u1D426" width="833" lsb="15"/>
+    <mtx name="u1D427" width="556" lsb="21"/>
+    <mtx name="u1D428" width="500" lsb="25"/>
+    <mtx name="u1D429" width="556" lsb="19"/>
+    <mtx name="u1D42A" width="556" lsb="34"/>
+    <mtx name="u1D42B" width="444" lsb="28"/>
+    <mtx name="u1D42C" width="389" lsb="25"/>
+    <mtx name="u1D42D" width="333" lsb="19"/>
+    <mtx name="u1D42E" width="556" lsb="16"/>
+    <mtx name="u1D42F" width="500" lsb="21"/>
+    <mtx name="u1D430" width="722" lsb="23"/>
+    <mtx name="u1D431" width="500" lsb="12"/>
+    <mtx name="u1D432" width="500" lsb="16"/>
+    <mtx name="u1D433" width="444" lsb="21"/>
+    <mtx name="u1D434" width="717" lsb="35"/>
+    <mtx name="u1D435" width="696" lsb="38"/>
+    <mtx name="u1D436" width="671" lsb="50"/>
+    <mtx name="u1D437" width="790" lsb="38"/>
+    <mtx name="u1D438" width="714" lsb="38"/>
+    <mtx name="u1D439" width="618" lsb="38"/>
+    <mtx name="u1D43A" width="734" lsb="50"/>
+    <mtx name="u1D43B" width="873" lsb="38"/>
+    <mtx name="u1D43C" width="480" lsb="38"/>
+    <mtx name="u1D43D" width="540" lsb="60"/>
+    <mtx name="u1D43E" width="762" lsb="38"/>
+    <mtx name="u1D43F" width="708" lsb="38"/>
+    <mtx name="u1D440" width="1005" lsb="38"/>
+    <mtx name="u1D441" width="851" lsb="38"/>
+    <mtx name="u1D442" width="732" lsb="50"/>
+    <mtx name="u1D443" width="594" lsb="38"/>
+    <mtx name="u1D444" width="781" lsb="50"/>
+    <mtx name="u1D445" width="740" lsb="38"/>
+    <mtx name="u1D446" width="650" lsb="50"/>
+    <mtx name="u1D447" width="550" lsb="25"/>
+    <mtx name="u1D448" width="705" lsb="65"/>
+    <mtx name="u1D449" width="575" lsb="60"/>
+    <mtx name="u1D44A" width="916" lsb="60"/>
+    <mtx name="u1D44B" width="790" lsb="25"/>
+    <mtx name="u1D44C" width="535" lsb="35"/>
+    <mtx name="u1D44D" width="772" lsb="60"/>
+    <mtx name="u1D44E" width="502" lsb="40"/>
+    <mtx name="u1D44F" width="470" lsb="45"/>
+    <mtx name="u1D450" width="415" lsb="40"/>
+    <mtx name="u1D451" width="532" lsb="40"/>
+    <mtx name="u1D452" width="445" lsb="40"/>
+    <mtx name="u1D453" width="555" lsb="40"/>
+    <mtx name="u1D454" width="492" lsb="20"/>
+    <mtx name="u1D456" width="311" lsb="50"/>
+    <mtx name="u1D457" width="389" lsb="-16"/>
+    <mtx name="u1D458" width="542" lsb="45"/>
+    <mtx name="u1D459" width="318" lsb="45"/>
+    <mtx name="u1D45A" width="710" lsb="30"/>
+    <mtx name="u1D45B" width="497" lsb="30"/>
+    <mtx name="u1D45C" width="458" lsb="40"/>
+    <mtx name="u1D45D" width="489" lsb="-30"/>
+    <mtx name="u1D45E" width="458" lsb="40"/>
+    <mtx name="u1D45F" width="408" lsb="30"/>
+    <mtx name="u1D460" width="440" lsb="50"/>
+    <mtx name="u1D461" width="313" lsb="40"/>
+    <mtx name="u1D462" width="474" lsb="30"/>
+    <mtx name="u1D463" width="506" lsb="72"/>
+    <mtx name="u1D464" width="775" lsb="72"/>
+    <mtx name="u1D465" width="550" lsb="30"/>
+    <mtx name="u1D466" width="496" lsb="30"/>
+    <mtx name="u1D467" width="499" lsb="42"/>
+    <mtx name="uni0300" width="0" lsb="-371"/>
+    <mtx name="uni0301" width="0" lsb="-371"/>
+    <mtx name="uni0302" width="0" lsb="-386"/>
+    <mtx name="uni0302.size1" width="560" lsb="0"/>
+    <mtx name="uni0302.size2" width="979" lsb="0"/>
+    <mtx name="uni0302.size3" width="1460" lsb="0"/>
+    <mtx name="uni0302.size4" width="1886" lsb="0"/>
+    <mtx name="uni0302.size5" width="2328" lsb="0"/>
+    <mtx name="uni0303" width="0" lsb="-395"/>
+    <mtx name="uni0303.size1" width="560" lsb="0"/>
+    <mtx name="uni0303.size2" width="979" lsb="0"/>
+    <mtx name="uni0303.size3" width="1460" lsb="0"/>
+    <mtx name="uni0303.size4" width="1886" lsb="0"/>
+    <mtx name="uni0303.size5" width="2328" lsb="0"/>
+    <mtx name="uni0304" width="0" lsb="-385"/>
+    <mtx name="uni0305" width="0" lsb="-480"/>
+    <mtx name="uni0305.size1" width="1000" lsb="0"/>
+    <mtx name="uni0305.size2" width="1500" lsb="0"/>
+    <mtx name="uni0305.size3" width="2000" lsb="0"/>
+    <mtx name="uni0305.size4" width="2500" lsb="0"/>
+    <mtx name="uni0305.size5" width="3000" lsb="0"/>
+    <mtx name="uni0306" width="0" lsb="-373"/>
+    <mtx name="uni0307" width="0" lsb="-280"/>
+    <mtx name="uni0308" width="0" lsb="-379"/>
+    <mtx name="uni0309" width="0" lsb="-307"/>
+    <mtx name="uni030A" width="0" lsb="-329"/>
+    <mtx name="uni030B" width="0" lsb="-401"/>
+    <mtx name="uni030C" width="0" lsb="-385"/>
+    <mtx name="uni030C.size1" width="560" lsb="0"/>
+    <mtx name="uni030C.size2" width="979" lsb="0"/>
+    <mtx name="uni030C.size3" width="1460" lsb="0"/>
+    <mtx name="uni030C.size4" width="1886" lsb="0"/>
+    <mtx name="uni030C.size5" width="2328" lsb="0"/>
+    <mtx name="uni030D" width="0" lsb="-250"/>
+    <mtx name="uni030E" width="0" lsb="-326"/>
+    <mtx name="uni030F" width="0" lsb="-401"/>
+    <mtx name="uni0310" width="0" lsb="-373"/>
+    <mtx name="uni0311" width="0" lsb="-373"/>
+    <mtx name="uni0312" width="0" lsb="-299"/>
+    <mtx name="uni0313" width="0" lsb="-299"/>
+    <mtx name="uni0314" width="0" lsb="-299"/>
+    <mtx name="uni0315" width="0" lsb="-85"/>
+    <mtx name="uni0316" width="0" lsb="-351"/>
+    <mtx name="uni0317" width="0" lsb="-371"/>
+    <mtx name="uni0318" width="0" lsb="-397"/>
+    <mtx name="uni0319" width="0" lsb="-267"/>
+    <mtx name="uni031A" width="0" lsb="-380"/>
+    <mtx name="uni031B" width="0" lsb="-44"/>
+    <mtx name="uni031C" width="0" lsb="-360"/>
+    <mtx name="uni031D" width="0" lsb="-345"/>
+    <mtx name="uni031E" width="0" lsb="-345"/>
+    <mtx name="uni031F" width="0" lsb="-326"/>
+    <mtx name="uni0320" width="0" lsb="-326"/>
+    <mtx name="uni0321" width="0" lsb="-235"/>
+    <mtx name="uni0322" width="0" lsb="-54"/>
+    <mtx name="uni0323" width="0" lsb="-280"/>
+    <mtx name="uni0324" width="0" lsb="-379"/>
+    <mtx name="uni0325" width="0" lsb="-329"/>
+    <mtx name="uni0326" width="0" lsb="-299"/>
+    <mtx name="uni0327" width="0" lsb="-334"/>
+    <mtx name="uni0328" width="0" lsb="-322"/>
+    <mtx name="uni0329" width="0" lsb="-250"/>
+    <mtx name="uni032A" width="0" lsb="-385"/>
+    <mtx name="uni032B" width="0" lsb="-380"/>
+    <mtx name="uni032C" width="0" lsb="-385"/>
+    <mtx name="uni032D" width="0" lsb="-385"/>
+    <mtx name="uni032E" width="0" lsb="-370"/>
+    <mtx name="uni032F" width="0" lsb="-370"/>
+    <mtx name="uni0330" width="0" lsb="-395"/>
+    <mtx name="uni0330.size1" width="560" lsb="0"/>
+    <mtx name="uni0330.size2" width="979" lsb="0"/>
+    <mtx name="uni0330.size3" width="1460" lsb="0"/>
+    <mtx name="uni0330.size4" width="1886" lsb="0"/>
+    <mtx name="uni0330.size5" width="2328" lsb="0"/>
+    <mtx name="uni0331" width="0" lsb="-385"/>
+    <mtx name="uni0332" width="0" lsb="-480"/>
+    <mtx name="uni0332.size1" width="1000" lsb="0"/>
+    <mtx name="uni0332.size2" width="1500" lsb="0"/>
+    <mtx name="uni0332.size3" width="2000" lsb="0"/>
+    <mtx name="uni0332.size4" width="2500" lsb="0"/>
+    <mtx name="uni0332.size5" width="3000" lsb="0"/>
+    <mtx name="uni0333" width="0" lsb="-480"/>
+    <mtx name="uni0334" width="0" lsb="-401"/>
+    <mtx name="uni0335" width="0" lsb="-384"/>
+    <mtx name="uni0336" width="0" lsb="-480"/>
+    <mtx name="uni0337" width="0" lsb="-380"/>
+    <mtx name="uni0338" width="0" lsb="-380"/>
+    <mtx name="uni0338.size1" width="0" lsb="-554"/>
+    <mtx name="uni0338.size2" width="0" lsb="-575"/>
+    <mtx name="uni0338.size3" width="0" lsb="-597"/>
+    <mtx name="uni0338.size4" width="0" lsb="-518"/>
+    <mtx name="uni0338.size5" width="0" lsb="-554"/>
+    <mtx name="uni0338.size6" width="0" lsb="-761"/>
+    <mtx name="uni0339" width="0" lsb="-280"/>
+    <mtx name="uni033A" width="0" lsb="-385"/>
+    <mtx name="uni033B" width="0" lsb="-313"/>
+    <mtx name="uni033C" width="0" lsb="-380"/>
+    <mtx name="uni033D" width="0" lsb="-326"/>
+    <mtx name="uni033E" width="0" lsb="-283"/>
+    <mtx name="uni033F" width="0" lsb="-480"/>
+    <mtx name="uni0346" width="0" lsb="-350"/>
+    <mtx name="uni0347" width="1" lsb="11"/>
+    <mtx name="uni034C" width="0" lsb="-386"/>
+    <mtx name="uni034D" width="478" lsb="0"/>
+    <mtx name="uni0359" width="0" lsb="-357"/>
+    <mtx name="uni035C" width="0" lsb="-373"/>
+    <mtx name="uni0360" width="0" lsb="-395"/>
+    <mtx name="uni0361" width="0" lsb="-373"/>
+    <mtx name="uni0362" width="0" lsb="-395"/>
+    <mtx name="uni20EE" width="436" lsb="0"/>
+    <mtx name="uni20EE.ex" width="300" lsb="0"/>
+    <mtx name="uni20EF" width="436" lsb="0"/>
+    <mtx name="uni239B" width="450" lsb="50"/>
+    <mtx name="uni239C" width="450" lsb="50"/>
+    <mtx name="uni239D" width="450" lsb="50"/>
+    <mtx name="uni239E" width="450" lsb="50"/>
+    <mtx name="uni239F" width="450" lsb="276"/>
+    <mtx name="uni23A0" width="450" lsb="50"/>
+    <mtx name="zero" width="500" lsb="24"/>
+  </hmtx>
+
+</ttFont>
diff --git a/Tests/subset/data/TestOPBD-0.ttx b/Tests/subset/data/TestOPBD-0.ttx
new file mode 100644
index 0000000..8d7af05
--- /dev/null
+++ b/Tests/subset/data/TestOPBD-0.ttx
@@ -0,0 +1,299 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="space"/>
+    <GlyphID id="2" name="zero"/>
+    <GlyphID id="3" name="one"/>
+    <GlyphID id="4" name="two"/>
+    <GlyphID id="5" name="A"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0xf1c8c54f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Mar 31 07:56:22 2016"/>
+    <modified value="Thu Mar 31 11:16:24 2016"/>
+    <xMin value="40"/>
+    <yMin value="-10"/>
+    <xMax value="620"/>
+    <yMax value="710"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="700"/>
+    <minLeftSideBearing value="40"/>
+    <minRightSideBearing value="40"/>
+    <xMaxExtent value="620"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="4"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="32"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="1"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="606"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="8722"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="700" lsb="80"/>
+    <mtx name="space" width="700" lsb="0"/>
+    <mtx name="zero" width="625" lsb="40"/>
+    <mtx name="one" width="660" lsb="80"/>
+    <mtx name="two" width="660" lsb="80"/>
+    <mtx name="A" width="660" lsb="80"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/>
+      <map code="0x30" name="zero"/>
+      <map code="0x31" name="one"/>
+      <map code="0x32" name="two"/>
+      <map code="0x41" name="A"/>
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="80" yMin="0" xMax="620" yMax="700">
+      <contour>
+        <pt x="620" y="700" on="1"/>
+        <pt x="620" y="0" on="1"/>
+        <pt x="80" y="0" on="1"/>
+        <pt x="80" y="700" on="1"/>
+      </contour>
+      <contour>
+        <pt x="160" y="80" on="1"/>
+        <pt x="540" y="80" on="1"/>
+        <pt x="540" y="620" on="1"/>
+        <pt x="160" y="620" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="one" xMin="80" yMin="230" xMax="580" yMax="490">
+      <contour>
+        <pt x="580" y="490" on="1"/>
+        <pt x="580" y="410" on="1"/>
+        <pt x="80" y="410" on="1"/>
+        <pt x="80" y="490" on="1"/>
+      </contour>
+      <contour>
+        <pt x="580" y="310" on="1"/>
+        <pt x="580" y="230" on="1"/>
+        <pt x="80" y="230" on="1"/>
+        <pt x="80" y="310" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="two" xMin="80" yMin="310" xMax="580" yMax="390">
+      <contour>
+        <pt x="580" y="390" on="1"/>
+        <pt x="580" y="310" on="1"/>
+        <pt x="80" y="310" on="1"/>
+        <pt x="80" y="390" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="80" yMin="100" xMax="580" yMax="600">
+      <contour>
+        <pt x="580" y="310" on="1"/>
+        <pt x="370" y="310" on="1"/>
+        <pt x="370" y="100" on="1"/>
+        <pt x="290" y="100" on="1"/>
+        <pt x="290" y="310" on="1"/>
+        <pt x="80" y="310" on="1"/>
+        <pt x="80" y="390" on="1"/>
+        <pt x="290" y="390" on="1"/>
+        <pt x="290" y="600" on="1"/>
+        <pt x="370" y="600" on="1"/>
+        <pt x="370" y="390" on="1"/>
+        <pt x="580" y="390" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+    <TTGlyph name="zero" xMin="40" yMin="-10" xMax="585" yMax="710">
+      <contour>
+        <pt x="237" y="-10" on="0"/>
+        <pt x="113" y="78" on="0"/>
+        <pt x="40" y="241" on="0"/>
+        <pt x="40" y="350" on="1"/>
+        <pt x="40" y="459" on="0"/>
+        <pt x="113" y="622" on="0"/>
+        <pt x="237" y="710" on="0"/>
+        <pt x="313" y="710" on="1"/>
+        <pt x="388" y="710" on="0"/>
+        <pt x="513" y="622" on="0"/>
+        <pt x="585" y="459" on="0"/>
+        <pt x="585" y="350" on="1"/>
+        <pt x="585" y="241" on="0"/>
+        <pt x="513" y="78" on="0"/>
+        <pt x="388" y="-10" on="0"/>
+        <pt x="313" y="-10" on="1"/>
+      </contour>
+      <contour>
+        <pt x="366" y="74" on="0"/>
+        <pt x="454" y="144" on="0"/>
+        <pt x="505" y="270" on="0"/>
+        <pt x="505" y="350" on="1"/>
+        <pt x="505" y="430" on="0"/>
+        <pt x="454" y="556" on="0"/>
+        <pt x="366" y="626" on="0"/>
+        <pt x="313" y="626" on="1"/>
+        <pt x="260" y="626" on="0"/>
+        <pt x="171" y="556" on="0"/>
+        <pt x="120" y="430" on="0"/>
+        <pt x="120" y="350" on="1"/>
+        <pt x="120" y="270" on="0"/>
+        <pt x="171" y="144" on="0"/>
+        <pt x="260" y="74" on="0"/>
+        <pt x="313" y="74" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestOPBD
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+  </name>
+
+  <gasp>
+    <gaspRange rangeMaxPPEM="7" rangeGaspBehavior="10"/>
+    <gaspRange rangeMaxPPEM="19" rangeGaspBehavior="7"/>
+    <gaspRange rangeMaxPPEM="65535" rangeGaspBehavior="15"/>
+  </gasp>
+
+  <opbd>
+    <Version value="0x00010000"/>
+    <OpticalBounds Format="0">
+      <OpticalBoundsDeltas>
+        <Lookup glyph="A">
+          <Left value="-99"/>
+          <Top value="0"/>
+          <Right value="55"/>
+          <Bottom value="0"/>
+        </Lookup>
+        <Lookup glyph="zero">
+          <Left value="-1"/>
+          <Top value="2"/>
+          <Right value="200"/>
+          <Bottom value="77"/>
+        </Lookup>
+      </OpticalBoundsDeltas>
+    </OpticalBounds>
+  </opbd>
+
+</ttFont>
diff --git a/Tests/subset/data/TestOPBD-1.ttx b/Tests/subset/data/TestOPBD-1.ttx
new file mode 100644
index 0000000..0552181
--- /dev/null
+++ b/Tests/subset/data/TestOPBD-1.ttx
@@ -0,0 +1,299 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="space"/>
+    <GlyphID id="2" name="zero"/>
+    <GlyphID id="3" name="one"/>
+    <GlyphID id="4" name="two"/>
+    <GlyphID id="5" name="A"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0xf1c8c54f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Mar 31 07:56:22 2016"/>
+    <modified value="Thu Mar 31 11:16:24 2016"/>
+    <xMin value="40"/>
+    <yMin value="-10"/>
+    <xMax value="620"/>
+    <yMax value="710"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="700"/>
+    <minLeftSideBearing value="40"/>
+    <minRightSideBearing value="40"/>
+    <xMaxExtent value="620"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="4"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="32"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="1"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="606"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="8722"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="700" lsb="80"/>
+    <mtx name="space" width="700" lsb="0"/>
+    <mtx name="zero" width="625" lsb="40"/>
+    <mtx name="one" width="660" lsb="80"/>
+    <mtx name="two" width="660" lsb="80"/>
+    <mtx name="A" width="660" lsb="80"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/>
+      <map code="0x30" name="zero"/>
+      <map code="0x31" name="one"/>
+      <map code="0x32" name="two"/>
+      <map code="0x41" name="A"/>
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="80" yMin="0" xMax="620" yMax="700">
+      <contour>
+        <pt x="620" y="700" on="1"/>
+        <pt x="620" y="0" on="1"/>
+        <pt x="80" y="0" on="1"/>
+        <pt x="80" y="700" on="1"/>
+      </contour>
+      <contour>
+        <pt x="160" y="80" on="1"/>
+        <pt x="540" y="80" on="1"/>
+        <pt x="540" y="620" on="1"/>
+        <pt x="160" y="620" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="one" xMin="80" yMin="230" xMax="580" yMax="490">
+      <contour>
+        <pt x="580" y="490" on="1"/>
+        <pt x="580" y="410" on="1"/>
+        <pt x="80" y="410" on="1"/>
+        <pt x="80" y="490" on="1"/>
+      </contour>
+      <contour>
+        <pt x="580" y="310" on="1"/>
+        <pt x="580" y="230" on="1"/>
+        <pt x="80" y="230" on="1"/>
+        <pt x="80" y="310" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="two" xMin="80" yMin="310" xMax="580" yMax="390">
+      <contour>
+        <pt x="580" y="390" on="1"/>
+        <pt x="580" y="310" on="1"/>
+        <pt x="80" y="310" on="1"/>
+        <pt x="80" y="390" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="80" yMin="100" xMax="580" yMax="600">
+      <contour>
+        <pt x="580" y="310" on="1"/>
+        <pt x="370" y="310" on="1"/>
+        <pt x="370" y="100" on="1"/>
+        <pt x="290" y="100" on="1"/>
+        <pt x="290" y="310" on="1"/>
+        <pt x="80" y="310" on="1"/>
+        <pt x="80" y="390" on="1"/>
+        <pt x="290" y="390" on="1"/>
+        <pt x="290" y="600" on="1"/>
+        <pt x="370" y="600" on="1"/>
+        <pt x="370" y="390" on="1"/>
+        <pt x="580" y="390" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+    <TTGlyph name="zero" xMin="40" yMin="-10" xMax="585" yMax="710">
+      <contour>
+        <pt x="237" y="-10" on="0"/>
+        <pt x="113" y="78" on="0"/>
+        <pt x="40" y="241" on="0"/>
+        <pt x="40" y="350" on="1"/>
+        <pt x="40" y="459" on="0"/>
+        <pt x="113" y="622" on="0"/>
+        <pt x="237" y="710" on="0"/>
+        <pt x="313" y="710" on="1"/>
+        <pt x="388" y="710" on="0"/>
+        <pt x="513" y="622" on="0"/>
+        <pt x="585" y="459" on="0"/>
+        <pt x="585" y="350" on="1"/>
+        <pt x="585" y="241" on="0"/>
+        <pt x="513" y="78" on="0"/>
+        <pt x="388" y="-10" on="0"/>
+        <pt x="313" y="-10" on="1"/>
+      </contour>
+      <contour>
+        <pt x="366" y="74" on="0"/>
+        <pt x="454" y="144" on="0"/>
+        <pt x="505" y="270" on="0"/>
+        <pt x="505" y="350" on="1"/>
+        <pt x="505" y="430" on="0"/>
+        <pt x="454" y="556" on="0"/>
+        <pt x="366" y="626" on="0"/>
+        <pt x="313" y="626" on="1"/>
+        <pt x="260" y="626" on="0"/>
+        <pt x="171" y="556" on="0"/>
+        <pt x="120" y="430" on="0"/>
+        <pt x="120" y="350" on="1"/>
+        <pt x="120" y="270" on="0"/>
+        <pt x="171" y="144" on="0"/>
+        <pt x="260" y="74" on="0"/>
+        <pt x="313" y="74" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestOPBD
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+  </name>
+
+  <gasp>
+    <gaspRange rangeMaxPPEM="7" rangeGaspBehavior="10"/>
+    <gaspRange rangeMaxPPEM="19" rangeGaspBehavior="7"/>
+    <gaspRange rangeMaxPPEM="65535" rangeGaspBehavior="15"/>
+  </gasp>
+
+  <opbd>
+    <Version value="0x00010000"/>
+    <OpticalBounds Format="1">
+      <OpticalBoundsPoints>
+        <Lookup glyph="A">
+          <Left value="3"/>
+          <Top value="4"/>
+          <Right value="5"/>
+          <Bottom value="6"/>
+        </Lookup>
+        <Lookup glyph="zero">
+          <Left value="9"/>
+          <Top value="8"/>
+          <Right value="7"/>
+          <Bottom value="6"/>
+        </Lookup>
+      </OpticalBoundsPoints>
+    </OpticalBounds>
+  </opbd>
+
+</ttFont>
diff --git a/Tests/subset/data/TestOTF-Regular.ttx b/Tests/subset/data/TestOTF-Regular.ttx
new file mode 100644
index 0000000..a650301
--- /dev/null
+++ b/Tests/subset/data/TestOTF-Regular.ttx
@@ -0,0 +1,262 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="2.5">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="A"/>
+    <GlyphID id="2" name="B"/>
+    <GlyphID id="3" name="C"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0xc6122d26"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri Nov 13 01:59:53 2015"/>
+    <modified value="Fri Nov 13 01:59:53 2015"/>
+    <xMin value="10"/>
+    <yMin value="0"/>
+    <xMax value="486"/>
+    <yMax value="660"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="660"/>
+    <descent value="-340"/>
+    <lineGap value="200"/>
+    <advanceWidthMax value="500"/>
+    <minLeftSideBearing value="10"/>
+    <minRightSideBearing value="10"/>
+    <xMaxExtent value="486"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="4"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="4"/>
+  </maxp>
+
+  <OS_2>
+    <version value="3"/>
+    <xAvgCharWidth value="474"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000100"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="220"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <fsFirstCharIndex value="65"/>
+    <fsLastCharIndex value="67"/>
+    <sTypoAscender value="660"/>
+    <sTypoDescender value="-340"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="0"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="0"/>
+    <sCapHeight value="0"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContex value="0"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0">
+      Test OTF
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="1" platEncID="0" langID="0x0">
+      1.000;UKWN;TestOTF-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0">
+      Test OTF
+    </namerecord>
+    <namerecord nameID="5" platformID="1" platEncID="0" langID="0x0">
+      Version 1.000;PS 1.0;hotconv 1.0.88;makeotf.lib2.5.647800
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0">
+      TestOTF-Regular
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test OTF
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.000;UKWN;TestOTF-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test OTF
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000;PS 1.0;hotconv 1.0.88;makeotf.lib2.5.647800
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestOTF-Regular
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x42" name="B"/><!-- LATIN CAPITAL LETTER B -->
+      <map code="0x43" name="C"/><!-- LATIN CAPITAL LETTER C -->
+    </cmap_format_4>
+    <cmap_format_6 platformID="1" platEncID="0" language="0">
+      <map code="0x41" name="A"/>
+      <map code="0x42" name="B"/>
+      <map code="0x43" name="C"/>
+    </cmap_format_6>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x42" name="B"/><!-- LATIN CAPITAL LETTER B -->
+      <map code="0x43" name="C"/><!-- LATIN CAPITAL LETTER C -->
+    </cmap_format_4>
+  </cmap>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <CFF>
+    <CFFFont name="TestOTF-Regular">
+      <version value="1.0"/>
+      <FamilyName value="Test OTF"/>
+      <isFixedPitch value="0"/>
+      <ItalicAngle value="0"/>
+      <UnderlineThickness value="50"/>
+      <PaintType value="0"/>
+      <CharstringType value="2"/>
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FontBBox value="0 0 486 660"/>
+      <StrokeWidth value="0"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <Encoding name="StandardEncoding"/>
+      <Private>
+        <BlueValues value="-10 0 500 510"/>
+        <BlueScale value="0.039625"/>
+        <BlueShift value="7"/>
+        <BlueFuzz value="1"/>
+        <StdHW value="20"/>
+        <StdVW value="20"/>
+        <StemSnapH value="20"/>
+        <StemSnapV value="20"/>
+        <ForceBold value="0"/>
+        <LanguageGroup value="0"/>
+        <ExpansionFactor value="0.06"/>
+        <initialRandomSeed value="0"/>
+        <defaultWidthX value="500"/>
+        <nominalWidthX value="300"/>
+        <Subrs>
+          <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+          <CharString index="0">
+            132 304 rmoveto
+            233 263 -233 hlineto
+            endchar
+          </CharString>
+        </Subrs>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          196 10 hmoveto
+          476 660 -476 hlineto
+          108 -602 rmoveto
+          74 132 54 103 rlineto
+          4 hlineto
+          52 -103 73 -132 rlineto
+          -129 329 rmoveto
+          -50 94 -66 119 rlineto
+          235 hlineto
+          -66 -119 -49 -94 rlineto
+          -175 -277 rmoveto
+          462 vlineto
+          127 -232 rlineto
+          217 -230 rmoveto
+          -126 230 126 232 rlineto
+          endchar
+        </CharString>
+        <CharString name="A">
+          -107 callsubr
+        </CharString>
+        <CharString name="B">
+          100 304 263 hstem
+          132 233 vstem
+          -107 callsubr
+        </CharString>
+        <CharString name="C">
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+  <hmtx>
+    <mtx name=".notdef" width="496" lsb="10"/>
+    <mtx name="A" width="500" lsb="132"/>
+    <mtx name="B" width="400" lsb="132"/>
+    <mtx name="C" width="500" lsb="0"/>
+  </hmtx>
+
+</ttFont>
diff --git a/Tests/subset/data/TestPROP.ttx b/Tests/subset/data/TestPROP.ttx
new file mode 100644
index 0000000..c783fce
--- /dev/null
+++ b/Tests/subset/data/TestPROP.ttx
@@ -0,0 +1,322 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="space"/>
+    <GlyphID id="2" name="zero"/>
+    <GlyphID id="3" name="one"/>
+    <GlyphID id="4" name="two"/>
+    <GlyphID id="5" name="A"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0xf1c8c54f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Mar 31 07:56:22 2016"/>
+    <modified value="Thu Mar 31 11:16:24 2016"/>
+    <xMin value="40"/>
+    <yMin value="-10"/>
+    <xMax value="620"/>
+    <yMax value="710"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="700"/>
+    <minLeftSideBearing value="40"/>
+    <minRightSideBearing value="40"/>
+    <xMaxExtent value="620"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="4"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="32"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="1"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="606"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="8722"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="700" lsb="80"/>
+    <mtx name="space" width="700" lsb="0"/>
+    <mtx name="zero" width="625" lsb="40"/>
+    <mtx name="one" width="660" lsb="80"/>
+    <mtx name="two" width="660" lsb="80"/>
+    <mtx name="A" width="660" lsb="80"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/>
+      <map code="0x30" name="zero"/>
+      <map code="0x31" name="one"/>
+      <map code="0x32" name="two"/>
+      <map code="0x41" name="A"/>
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="80" yMin="0" xMax="620" yMax="700">
+      <contour>
+        <pt x="620" y="700" on="1"/>
+        <pt x="620" y="0" on="1"/>
+        <pt x="80" y="0" on="1"/>
+        <pt x="80" y="700" on="1"/>
+      </contour>
+      <contour>
+        <pt x="160" y="80" on="1"/>
+        <pt x="540" y="80" on="1"/>
+        <pt x="540" y="620" on="1"/>
+        <pt x="160" y="620" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="one" xMin="80" yMin="230" xMax="580" yMax="490">
+      <contour>
+        <pt x="580" y="490" on="1"/>
+        <pt x="580" y="410" on="1"/>
+        <pt x="80" y="410" on="1"/>
+        <pt x="80" y="490" on="1"/>
+      </contour>
+      <contour>
+        <pt x="580" y="310" on="1"/>
+        <pt x="580" y="230" on="1"/>
+        <pt x="80" y="230" on="1"/>
+        <pt x="80" y="310" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="two" xMin="80" yMin="310" xMax="580" yMax="390">
+      <contour>
+        <pt x="580" y="390" on="1"/>
+        <pt x="580" y="310" on="1"/>
+        <pt x="80" y="310" on="1"/>
+        <pt x="80" y="390" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="80" yMin="100" xMax="580" yMax="600">
+      <contour>
+        <pt x="580" y="310" on="1"/>
+        <pt x="370" y="310" on="1"/>
+        <pt x="370" y="100" on="1"/>
+        <pt x="290" y="100" on="1"/>
+        <pt x="290" y="310" on="1"/>
+        <pt x="80" y="310" on="1"/>
+        <pt x="80" y="390" on="1"/>
+        <pt x="290" y="390" on="1"/>
+        <pt x="290" y="600" on="1"/>
+        <pt x="370" y="600" on="1"/>
+        <pt x="370" y="390" on="1"/>
+        <pt x="580" y="390" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+    <TTGlyph name="zero" xMin="40" yMin="-10" xMax="585" yMax="710">
+      <contour>
+        <pt x="237" y="-10" on="0"/>
+        <pt x="113" y="78" on="0"/>
+        <pt x="40" y="241" on="0"/>
+        <pt x="40" y="350" on="1"/>
+        <pt x="40" y="459" on="0"/>
+        <pt x="113" y="622" on="0"/>
+        <pt x="237" y="710" on="0"/>
+        <pt x="313" y="710" on="1"/>
+        <pt x="388" y="710" on="0"/>
+        <pt x="513" y="622" on="0"/>
+        <pt x="585" y="459" on="0"/>
+        <pt x="585" y="350" on="1"/>
+        <pt x="585" y="241" on="0"/>
+        <pt x="513" y="78" on="0"/>
+        <pt x="388" y="-10" on="0"/>
+        <pt x="313" y="-10" on="1"/>
+      </contour>
+      <contour>
+        <pt x="366" y="74" on="0"/>
+        <pt x="454" y="144" on="0"/>
+        <pt x="505" y="270" on="0"/>
+        <pt x="505" y="350" on="1"/>
+        <pt x="505" y="430" on="0"/>
+        <pt x="454" y="556" on="0"/>
+        <pt x="366" y="626" on="0"/>
+        <pt x="313" y="626" on="1"/>
+        <pt x="260" y="626" on="0"/>
+        <pt x="171" y="556" on="0"/>
+        <pt x="120" y="430" on="0"/>
+        <pt x="120" y="350" on="1"/>
+        <pt x="120" y="270" on="0"/>
+        <pt x="171" y="144" on="0"/>
+        <pt x="260" y="74" on="0"/>
+        <pt x="313" y="74" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestPROP
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestPROP
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestPROP-Regular
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+    </extraNames>
+  </post>
+
+  <prop>
+    <Version value="3.0"/>
+    <GlyphProperties Format="1">
+      <DefaultProperties value="0"/>
+      <Properties>
+	<Lookup glyph="space" value="10"/>
+	<Lookup glyph="zero" value="3"/>
+        <Lookup glyph="one" value="3"/>
+	<Lookup glyph="two" value="3"/>
+      </Properties>
+    </GlyphProperties>
+  </prop>
+
+  <gasp>
+    <gaspRange rangeMaxPPEM="7" rangeGaspBehavior="10"/>
+    <gaspRange rangeMaxPPEM="19" rangeGaspBehavior="7"/>
+    <gaspRange rangeMaxPPEM="65535" rangeGaspBehavior="15"/>
+  </gasp>
+
+</ttFont>
diff --git a/Tests/subset/data/TestTTF-Regular.ttx b/Tests/subset/data/TestTTF-Regular.ttx
new file mode 100644
index 0000000..b6859e5
--- /dev/null
+++ b/Tests/subset/data/TestTTF-Regular.ttx
@@ -0,0 +1,705 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="2.5">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="A"/>
+    <GlyphID id="2" name="B"/>
+    <GlyphID id="3" name="C"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x840fbf5c"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00001011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri Nov 13 03:46:42 2015"/>
+    <modified value="Fri Nov 13 11:59:06 2015"/>
+    <xMin value="10"/>
+    <yMin value="0"/>
+    <xMax value="486"/>
+    <yMax value="660"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="9"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="660"/>
+    <descent value="-340"/>
+    <lineGap value="200"/>
+    <advanceWidthMax value="500"/>
+    <minLeftSideBearing value="10"/>
+    <minRightSideBearing value="10"/>
+    <xMaxExtent value="486"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="4"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="4"/>
+    <maxPoints value="22"/>
+    <maxContours value="5"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="10"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="512"/>
+    <maxSizeOfInstructions value="371"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <version value="3"/>
+    <xAvgCharWidth value="474"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000100"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="220"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <fsFirstCharIndex value="0"/>
+    <fsLastCharIndex value="67"/>
+    <sTypoAscender value="660"/>
+    <sTypoDescender value="-340"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="0"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="0"/>
+    <sCapHeight value="0"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContex value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="496" lsb="10"/>
+    <mtx name="A" width="500" lsb="132"/>
+    <mtx name="B" width="400" lsb="132"/>
+    <mtx name="C" width="500" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x42" name="B"/><!-- LATIN CAPITAL LETTER B -->
+      <map code="0x43" name="C"/><!-- LATIN CAPITAL LETTER C -->
+    </cmap_format_4>
+    <cmap_format_6 platformID="1" platEncID="0" language="0">
+      <map code="0x41" name="A"/>
+      <map code="0x42" name="B"/>
+      <map code="0x43" name="C"/>
+    </cmap_format_6>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x42" name="B"/><!-- LATIN CAPITAL LETTER B -->
+      <map code="0x43" name="C"/><!-- LATIN CAPITAL LETTER C -->
+    </cmap_format_4>
+  </cmap>
+
+  <fpgm>
+    <assembly>
+      PUSH[ ]	/* 1 value pushed */
+      0
+      FDEF[ ]	/* FunctionDefinition */
+      MPPEM[ ]	/* MeasurePixelPerEm */
+      PUSH[ ]	/* 1 value pushed */
+      9
+      LT[ ]	/* LessThan */
+      IF[ ]	/* If */
+      PUSH[ ]	/* 2 values pushed */
+      1 1
+      INSTCTRL[ ]	/* SetInstrExecControl */
+      EIF[ ]	/* EndIf */
+      PUSH[ ]	/* 1 value pushed */
+      511
+      SCANCTRL[ ]	/* ScanConversionControl */
+      PUSH[ ]	/* 1 value pushed */
+      68
+      SCVTCI[ ]	/* SetCVTCutIn */
+      PUSH[ ]	/* 2 values pushed */
+      9 3
+      SDS[ ]	/* SetDeltaShiftInGState */
+      SDB[ ]	/* SetDeltaBaseInGState */
+      ENDF[ ]	/* EndFunctionDefinition */
+      PUSH[ ]	/* 1 value pushed */
+      1
+      FDEF[ ]	/* FunctionDefinition */
+      DUP[ ]	/* DuplicateTopStack */
+      DUP[ ]	/* DuplicateTopStack */
+      RCVT[ ]	/* ReadCVT */
+      ROUND[01]	/* Round */
+      WCVTP[ ]	/* WriteCVTInPixels */
+      PUSH[ ]	/* 1 value pushed */
+      1
+      ADD[ ]	/* Add */
+      ENDF[ ]	/* EndFunctionDefinition */
+      PUSH[ ]	/* 1 value pushed */
+      2
+      FDEF[ ]	/* FunctionDefinition */
+      PUSH[ ]	/* 1 value pushed */
+      1
+      LOOPCALL[ ]	/* LoopAndCallFunction */
+      POP[ ]	/* PopTopStack */
+      ENDF[ ]	/* EndFunctionDefinition */
+      PUSH[ ]	/* 1 value pushed */
+      3
+      FDEF[ ]	/* FunctionDefinition */
+      DUP[ ]	/* DuplicateTopStack */
+      GC[0]	/* GetCoordOnPVector */
+      PUSH[ ]	/* 1 value pushed */
+      3
+      CINDEX[ ]	/* CopyXToTopStack */
+      GC[0]	/* GetCoordOnPVector */
+      GT[ ]	/* GreaterThan */
+      IF[ ]	/* If */
+      SWAP[ ]	/* SwapTopStack */
+      EIF[ ]	/* EndIf */
+      DUP[ ]	/* DuplicateTopStack */
+      ROLL[ ]	/* RollTopThreeStack */
+      DUP[ ]	/* DuplicateTopStack */
+      ROLL[ ]	/* RollTopThreeStack */
+      MD[0]	/* MeasureDistance */
+      ABS[ ]	/* Absolute */
+      ROLL[ ]	/* RollTopThreeStack */
+      DUP[ ]	/* DuplicateTopStack */
+      GC[0]	/* GetCoordOnPVector */
+      DUP[ ]	/* DuplicateTopStack */
+      ROUND[00]	/* Round */
+      SUB[ ]	/* Subtract */
+      ABS[ ]	/* Absolute */
+      PUSH[ ]	/* 1 value pushed */
+      4
+      CINDEX[ ]	/* CopyXToTopStack */
+      GC[0]	/* GetCoordOnPVector */
+      DUP[ ]	/* DuplicateTopStack */
+      ROUND[00]	/* Round */
+      SUB[ ]	/* Subtract */
+      ABS[ ]	/* Absolute */
+      GT[ ]	/* GreaterThan */
+      IF[ ]	/* If */
+      SWAP[ ]	/* SwapTopStack */
+      NEG[ ]	/* Negate */
+      ROLL[ ]	/* RollTopThreeStack */
+      EIF[ ]	/* EndIf */
+      MDAP[1]	/* MoveDirectAbsPt */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSH[ ]	/* 1 value pushed */
+      0
+      GTEQ[ ]	/* GreaterThanOrEqual */
+      IF[ ]	/* If */
+      ROUND[01]	/* Round */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSH[ ]	/* 1 value pushed */
+      0
+      EQ[ ]	/* Equal */
+      IF[ ]	/* If */
+      POP[ ]	/* PopTopStack */
+      PUSH[ ]	/* 1 value pushed */
+      64
+      EIF[ ]	/* EndIf */
+      ELSE[ ]	/* Else */
+      ROUND[01]	/* Round */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSH[ ]	/* 1 value pushed */
+      0
+      EQ[ ]	/* Equal */
+      IF[ ]	/* If */
+      POP[ ]	/* PopTopStack */
+      PUSH[ ]	/* 1 value pushed */
+      64
+      NEG[ ]	/* Negate */
+      EIF[ ]	/* EndIf */
+      EIF[ ]	/* EndIf */
+      MSIRP[0]	/* MoveStackIndirRelPt */
+      ENDF[ ]	/* EndFunctionDefinition */
+      PUSH[ ]	/* 1 value pushed */
+      4
+      FDEF[ ]	/* FunctionDefinition */
+      DUP[ ]	/* DuplicateTopStack */
+      GC[0]	/* GetCoordOnPVector */
+      PUSH[ ]	/* 1 value pushed */
+      4
+      CINDEX[ ]	/* CopyXToTopStack */
+      GC[0]	/* GetCoordOnPVector */
+      GT[ ]	/* GreaterThan */
+      IF[ ]	/* If */
+      SWAP[ ]	/* SwapTopStack */
+      ROLL[ ]	/* RollTopThreeStack */
+      EIF[ ]	/* EndIf */
+      DUP[ ]	/* DuplicateTopStack */
+      GC[0]	/* GetCoordOnPVector */
+      DUP[ ]	/* DuplicateTopStack */
+      ROUND[10]	/* Round */
+      SUB[ ]	/* Subtract */
+      ABS[ ]	/* Absolute */
+      PUSH[ ]	/* 1 value pushed */
+      4
+      CINDEX[ ]	/* CopyXToTopStack */
+      GC[0]	/* GetCoordOnPVector */
+      DUP[ ]	/* DuplicateTopStack */
+      ROUND[10]	/* Round */
+      SUB[ ]	/* Subtract */
+      ABS[ ]	/* Absolute */
+      GT[ ]	/* GreaterThan */
+      IF[ ]	/* If */
+      SWAP[ ]	/* SwapTopStack */
+      ROLL[ ]	/* RollTopThreeStack */
+      EIF[ ]	/* EndIf */
+      MDAP[1]	/* MoveDirectAbsPt */
+      MIRP[11101]	/* MoveIndirectRelPt */
+      ENDF[ ]	/* EndFunctionDefinition */
+      PUSH[ ]	/* 1 value pushed */
+      5
+      FDEF[ ]	/* FunctionDefinition */
+      MPPEM[ ]	/* MeasurePixelPerEm */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSH[ ]	/* 1 value pushed */
+      3
+      MINDEX[ ]	/* MoveXToTopStack */
+      LT[ ]	/* LessThan */
+      IF[ ]	/* If */
+      LTEQ[ ]	/* LessThenOrEqual */
+      IF[ ]	/* If */
+      PUSH[ ]	/* 1 value pushed */
+      128
+      WCVTP[ ]	/* WriteCVTInPixels */
+      ELSE[ ]	/* Else */
+      PUSH[ ]	/* 1 value pushed */
+      64
+      WCVTP[ ]	/* WriteCVTInPixels */
+      EIF[ ]	/* EndIf */
+      ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      POP[ ]	/* PopTopStack */
+      DUP[ ]	/* DuplicateTopStack */
+      RCVT[ ]	/* ReadCVT */
+      PUSH[ ]	/* 1 value pushed */
+      192
+      LT[ ]	/* LessThan */
+      IF[ ]	/* If */
+      PUSH[ ]	/* 1 value pushed */
+      192
+      WCVTP[ ]	/* WriteCVTInPixels */
+      ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      EIF[ ]	/* EndIf */
+      EIF[ ]	/* EndIf */
+      ENDF[ ]	/* EndFunctionDefinition */
+      PUSH[ ]	/* 1 value pushed */
+      6
+      FDEF[ ]	/* FunctionDefinition */
+      DUP[ ]	/* DuplicateTopStack */
+      DUP[ ]	/* DuplicateTopStack */
+      RCVT[ ]	/* ReadCVT */
+      ROUND[01]	/* Round */
+      WCVTP[ ]	/* WriteCVTInPixels */
+      PUSH[ ]	/* 1 value pushed */
+      1
+      ADD[ ]	/* Add */
+      DUP[ ]	/* DuplicateTopStack */
+      DUP[ ]	/* DuplicateTopStack */
+      RCVT[ ]	/* ReadCVT */
+      RDTG[ ]	/* RoundDownToGrid */
+      ROUND[01]	/* Round */
+      RTG[ ]	/* RoundToGrid */
+      WCVTP[ ]	/* WriteCVTInPixels */
+      PUSH[ ]	/* 1 value pushed */
+      1
+      ADD[ ]	/* Add */
+      ENDF[ ]	/* EndFunctionDefinition */
+      PUSH[ ]	/* 1 value pushed */
+      7
+      FDEF[ ]	/* FunctionDefinition */
+      PUSH[ ]	/* 1 value pushed */
+      6
+      LOOPCALL[ ]	/* LoopAndCallFunction */
+      ENDF[ ]	/* EndFunctionDefinition */
+      PUSH[ ]	/* 1 value pushed */
+      8
+      FDEF[ ]	/* FunctionDefinition */
+      MPPEM[ ]	/* MeasurePixelPerEm */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSH[ ]	/* 1 value pushed */
+      3
+      MINDEX[ ]	/* MoveXToTopStack */
+      GTEQ[ ]	/* GreaterThanOrEqual */
+      IF[ ]	/* If */
+      PUSH[ ]	/* 1 value pushed */
+      64
+      ELSE[ ]	/* Else */
+      PUSH[ ]	/* 1 value pushed */
+      0
+      EIF[ ]	/* EndIf */
+      ROLL[ ]	/* RollTopThreeStack */
+      ROLL[ ]	/* RollTopThreeStack */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSH[ ]	/* 1 value pushed */
+      3
+      MINDEX[ ]	/* MoveXToTopStack */
+      GTEQ[ ]	/* GreaterThanOrEqual */
+      IF[ ]	/* If */
+      SWAP[ ]	/* SwapTopStack */
+      POP[ ]	/* PopTopStack */
+      PUSH[ ]	/* 1 value pushed */
+      128
+      ROLL[ ]	/* RollTopThreeStack */
+      ROLL[ ]	/* RollTopThreeStack */
+      ELSE[ ]	/* Else */
+      ROLL[ ]	/* RollTopThreeStack */
+      SWAP[ ]	/* SwapTopStack */
+      EIF[ ]	/* EndIf */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSH[ ]	/* 1 value pushed */
+      3
+      MINDEX[ ]	/* MoveXToTopStack */
+      GTEQ[ ]	/* GreaterThanOrEqual */
+      IF[ ]	/* If */
+      SWAP[ ]	/* SwapTopStack */
+      POP[ ]	/* PopTopStack */
+      PUSH[ ]	/* 1 value pushed */
+      192
+      ROLL[ ]	/* RollTopThreeStack */
+      ROLL[ ]	/* RollTopThreeStack */
+      ELSE[ ]	/* Else */
+      ROLL[ ]	/* RollTopThreeStack */
+      SWAP[ ]	/* SwapTopStack */
+      EIF[ ]	/* EndIf */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSH[ ]	/* 1 value pushed */
+      3
+      MINDEX[ ]	/* MoveXToTopStack */
+      GTEQ[ ]	/* GreaterThanOrEqual */
+      IF[ ]	/* If */
+      SWAP[ ]	/* SwapTopStack */
+      POP[ ]	/* PopTopStack */
+      PUSH[ ]	/* 1 value pushed */
+      256
+      ROLL[ ]	/* RollTopThreeStack */
+      ROLL[ ]	/* RollTopThreeStack */
+      ELSE[ ]	/* Else */
+      ROLL[ ]	/* RollTopThreeStack */
+      SWAP[ ]	/* SwapTopStack */
+      EIF[ ]	/* EndIf */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSH[ ]	/* 1 value pushed */
+      3
+      MINDEX[ ]	/* MoveXToTopStack */
+      GTEQ[ ]	/* GreaterThanOrEqual */
+      IF[ ]	/* If */
+      SWAP[ ]	/* SwapTopStack */
+      POP[ ]	/* PopTopStack */
+      PUSH[ ]	/* 1 value pushed */
+      320
+      ROLL[ ]	/* RollTopThreeStack */
+      ROLL[ ]	/* RollTopThreeStack */
+      ELSE[ ]	/* Else */
+      ROLL[ ]	/* RollTopThreeStack */
+      SWAP[ ]	/* SwapTopStack */
+      EIF[ ]	/* EndIf */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSH[ ]	/* 1 value pushed */
+      3
+      MINDEX[ ]	/* MoveXToTopStack */
+      GTEQ[ ]	/* GreaterThanOrEqual */
+      IF[ ]	/* If */
+      PUSH[ ]	/* 1 value pushed */
+      3
+      CINDEX[ ]	/* CopyXToTopStack */
+      RCVT[ ]	/* ReadCVT */
+      PUSH[ ]	/* 1 value pushed */
+      384
+      LT[ ]	/* LessThan */
+      IF[ ]	/* If */
+      SWAP[ ]	/* SwapTopStack */
+      POP[ ]	/* PopTopStack */
+      PUSH[ ]	/* 1 value pushed */
+      384
+      SWAP[ ]	/* SwapTopStack */
+      POP[ ]	/* PopTopStack */
+      ELSE[ ]	/* Else */
+      PUSH[ ]	/* 1 value pushed */
+      3
+      CINDEX[ ]	/* CopyXToTopStack */
+      RCVT[ ]	/* ReadCVT */
+      SWAP[ ]	/* SwapTopStack */
+      POP[ ]	/* PopTopStack */
+      SWAP[ ]	/* SwapTopStack */
+      POP[ ]	/* PopTopStack */
+      EIF[ ]	/* EndIf */
+      ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      EIF[ ]	/* EndIf */
+      WCVTP[ ]	/* WriteCVTInPixels */
+      ENDF[ ]	/* EndFunctionDefinition */
+      PUSH[ ]	/* 1 value pushed */
+      9
+      FDEF[ ]	/* FunctionDefinition */
+      MPPEM[ ]	/* MeasurePixelPerEm */
+      GTEQ[ ]	/* GreaterThanOrEqual */
+      IF[ ]	/* If */
+      RCVT[ ]	/* ReadCVT */
+      WCVTP[ ]	/* WriteCVTInPixels */
+      ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      POP[ ]	/* PopTopStack */
+      EIF[ ]	/* EndIf */
+      ENDF[ ]	/* EndFunctionDefinition */
+    </assembly>
+  </fpgm>
+
+  <prep>
+    <assembly>
+      PUSH[ ]	/* 1 value pushed */
+      0
+      CALL[ ]	/* CallFunction */
+      SVTCA[0]	/* SetFPVectorToAxis */
+      PUSH[ ]	/* 3 values pushed */
+      1 1 2
+      CALL[ ]	/* CallFunction */
+      SVTCA[1]	/* SetFPVectorToAxis */
+      PUSH[ ]	/* 3 values pushed */
+      2 1 2
+      CALL[ ]	/* CallFunction */
+      SVTCA[1]	/* SetFPVectorToAxis */
+      PUSH[ ]	/* 8 values pushed */
+      2 275 225 175 125 75 0 8
+      CALL[ ]	/* CallFunction */
+      SVTCA[0]	/* SetFPVectorToAxis */
+      PUSH[ ]	/* 8 values pushed */
+      1 275 225 175 125 75 0 8
+      CALL[ ]	/* CallFunction */
+      SVTCA[0]	/* SetFPVectorToAxis */
+      PUSH[ ]	/* 3 values pushed */
+      3 2 7
+      CALL[ ]	/* CallFunction */
+      PUSH[ ]	/* 1 value pushed */
+      0
+      DUP[ ]	/* DuplicateTopStack */
+      RCVT[ ]	/* ReadCVT */
+      RDTG[ ]	/* RoundDownToGrid */
+      ROUND[01]	/* Round */
+      RTG[ ]	/* RoundToGrid */
+      WCVTP[ ]	/* WriteCVTInPixels */
+      MPPEM[ ]	/* MeasurePixelPerEm */
+      PUSH[ ]	/* 1 value pushed */
+      96
+      GT[ ]	/* GreaterThan */
+      IF[ ]	/* If */
+      PUSH[ ]	/* 1 value pushed */
+      1
+      ELSE[ ]	/* Else */
+      PUSH[ ]	/* 1 value pushed */
+      0
+      EIF[ ]	/* EndIf */
+      PUSH[ ]	/* 1 value pushed */
+      1
+      INSTCTRL[ ]	/* SetInstrExecControl */
+    </assembly>
+  </prep>
+
+  <cvt>
+    <cv index="0" value="20"/>
+    <cv index="1" value="20"/>
+    <cv index="2" value="20"/>
+    <cv index="3" value="0"/>
+    <cv index="4" value="10"/>
+    <cv index="5" value="500"/>
+    <cv index="6" value="10"/>
+  </cvt>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="10" yMin="0" xMax="486" yMax="660">
+      <contour>
+        <pt x="10" y="660" on="1"/>
+        <pt x="486" y="660" on="1"/>
+        <pt x="486" y="0" on="1"/>
+        <pt x="10" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="375" y="58" on="1"/>
+        <pt x="302" y="190" on="1"/>
+        <pt x="250" y="293" on="1"/>
+        <pt x="246" y="293" on="1"/>
+        <pt x="192" y="190" on="1"/>
+        <pt x="118" y="58" on="1"/>
+      </contour>
+      <contour>
+        <pt x="250" y="387" on="1"/>
+        <pt x="299" y="481" on="1"/>
+        <pt x="365" y="600" on="1"/>
+        <pt x="130" y="600" on="1"/>
+        <pt x="196" y="481" on="1"/>
+        <pt x="246" y="387" on="1"/>
+      </contour>
+      <contour>
+        <pt x="202" y="340" on="1"/>
+        <pt x="75" y="572" on="1"/>
+        <pt x="75" y="110" on="1"/>
+      </contour>
+      <contour>
+        <pt x="419" y="572" on="1"/>
+        <pt x="293" y="340" on="1"/>
+        <pt x="419" y="110" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="132" yMin="304" xMax="365" yMax="567">
+      <contour>
+        <pt x="132" y="567" on="1"/>
+        <pt x="365" y="567" on="1"/>
+        <pt x="365" y="304" on="1"/>
+        <pt x="132" y="304" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="B" xMin="132" yMin="304" xMax="365" yMax="567">
+      <contour>
+        <pt x="132" y="567" on="1"/>
+        <pt x="365" y="567" on="1"/>
+        <pt x="365" y="304" on="1"/>
+        <pt x="132" y="304" on="1"/>
+      </contour>
+      <instructions><assembly>
+          SVTCA[0]	/* SetFPVectorToAxis */
+          PUSH[ ]	/* 3 values pushed */
+          1 2 3
+          CALL[ ]	/* CallFunction */
+          IUP[0]	/* InterpolateUntPts */
+          IUP[1]	/* InterpolateUntPts */
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="C"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0">
+      TestTTF
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="1" platEncID="0" langID="0x0">
+      1.000;UKWN;TestTTF-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0">
+      TestTTF
+    </namerecord>
+    <namerecord nameID="5" platformID="1" platEncID="0" langID="0x0">
+      Version 1.000;PS 1.000;hotconv 1.0.88;makeotf.lib2.5.647800 DEVELOPMENT
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0">
+      TestTTF-Regular
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      TestTTF
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.000;UKWN;TestTTF-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      TestTTF
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000;PS 1.000;hotconv 1.0.88;makeotf.lib2.5.647800 DEVELOPMENT
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestTTF-Regular
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+    </extraNames>
+  </post>
+
+  <gasp>
+    <gaspRange rangeMaxPPEM="65535" rangeGaspBehavior="2"/>
+  </gasp>
+
+</ttFont>
diff --git a/Tests/subset/data/TestTTF-Regular_non_BMP_char.ttx b/Tests/subset/data/TestTTF-Regular_non_BMP_char.ttx
new file mode 100644
index 0000000..c5890b4
--- /dev/null
+++ b/Tests/subset/data/TestTTF-Regular_non_BMP_char.ttx
@@ -0,0 +1,722 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.2">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="A"/>
+    <GlyphID id="2" name="B"/>
+    <GlyphID id="3" name="C"/>
+    <GlyphID id="4" name="u1F6D2"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0xfe2f7de1"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00001011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri Nov 13 03:46:42 2015"/>
+    <modified value="Tue Nov 29 15:14:07 2016"/>
+    <xMin value="10"/>
+    <yMin value="0"/>
+    <xMax value="486"/>
+    <yMax value="660"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="9"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="660"/>
+    <descent value="-340"/>
+    <lineGap value="200"/>
+    <advanceWidthMax value="1000"/>
+    <minLeftSideBearing value="10"/>
+    <minRightSideBearing value="10"/>
+    <xMaxExtent value="486"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="5"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="5"/>
+    <maxPoints value="22"/>
+    <maxContours value="5"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="10"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="512"/>
+    <maxSizeOfInstructions value="371"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="474"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000100"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="220"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="65"/>
+    <usLastCharIndex value="65535"/>
+    <sTypoAscender value="660"/>
+    <sTypoDescender value="-340"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="0"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="0"/>
+    <sCapHeight value="0"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="496" lsb="10"/>
+    <mtx name="A" width="500" lsb="132"/>
+    <mtx name="B" width="400" lsb="132"/>
+    <mtx name="C" width="500" lsb="0"/>
+    <mtx name="u1F6D2" width="1000" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x42" name="B"/><!-- LATIN CAPITAL LETTER B -->
+      <map code="0x43" name="C"/><!-- LATIN CAPITAL LETTER C -->
+    </cmap_format_4>
+    <cmap_format_6 platformID="1" platEncID="0" language="0">
+      <map code="0x41" name="A"/>
+      <map code="0x42" name="B"/>
+      <map code="0x43" name="C"/>
+    </cmap_format_6>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x42" name="B"/><!-- LATIN CAPITAL LETTER B -->
+      <map code="0x43" name="C"/><!-- LATIN CAPITAL LETTER C -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="40" language="0" nGroups="2">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x42" name="B"/><!-- LATIN CAPITAL LETTER B -->
+      <map code="0x43" name="C"/><!-- LATIN CAPITAL LETTER C -->
+      <map code="0x1f6d2" name="u1F6D2"/><!-- ???? -->
+    </cmap_format_12>
+  </cmap>
+
+  <fpgm>
+    <assembly>
+      PUSHB[ ]	/* 1 value pushed */
+      0
+      FDEF[ ]	/* FunctionDefinition */
+      MPPEM[ ]	/* MeasurePixelPerEm */
+      PUSHB[ ]	/* 1 value pushed */
+      9
+      LT[ ]	/* LessThan */
+      IF[ ]	/* If */
+      PUSHB[ ]	/* 2 values pushed */
+      1 1
+      INSTCTRL[ ]	/* SetInstrExecControl */
+      EIF[ ]	/* EndIf */
+      PUSHW[ ]	/* 1 value pushed */
+      511
+      SCANCTRL[ ]	/* ScanConversionControl */
+      PUSHB[ ]	/* 1 value pushed */
+      68
+      SCVTCI[ ]	/* SetCVTCutIn */
+      PUSHB[ ]	/* 2 values pushed */
+      9 3
+      SDS[ ]	/* SetDeltaShiftInGState */
+      SDB[ ]	/* SetDeltaBaseInGState */
+      ENDF[ ]	/* EndFunctionDefinition */
+      PUSHB[ ]	/* 1 value pushed */
+      1
+      FDEF[ ]	/* FunctionDefinition */
+      DUP[ ]	/* DuplicateTopStack */
+      DUP[ ]	/* DuplicateTopStack */
+      RCVT[ ]	/* ReadCVT */
+      ROUND[01]	/* Round */
+      WCVTP[ ]	/* WriteCVTInPixels */
+      PUSHB[ ]	/* 1 value pushed */
+      1
+      ADD[ ]	/* Add */
+      ENDF[ ]	/* EndFunctionDefinition */
+      PUSHB[ ]	/* 1 value pushed */
+      2
+      FDEF[ ]	/* FunctionDefinition */
+      PUSHB[ ]	/* 1 value pushed */
+      1
+      LOOPCALL[ ]	/* LoopAndCallFunction */
+      POP[ ]	/* PopTopStack */
+      ENDF[ ]	/* EndFunctionDefinition */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      FDEF[ ]	/* FunctionDefinition */
+      DUP[ ]	/* DuplicateTopStack */
+      GC[0]	/* GetCoordOnPVector */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      CINDEX[ ]	/* CopyXToTopStack */
+      GC[0]	/* GetCoordOnPVector */
+      GT[ ]	/* GreaterThan */
+      IF[ ]	/* If */
+      SWAP[ ]	/* SwapTopStack */
+      EIF[ ]	/* EndIf */
+      DUP[ ]	/* DuplicateTopStack */
+      ROLL[ ]	/* RollTopThreeStack */
+      DUP[ ]	/* DuplicateTopStack */
+      ROLL[ ]	/* RollTopThreeStack */
+      MD[0]	/* MeasureDistance */
+      ABS[ ]	/* Absolute */
+      ROLL[ ]	/* RollTopThreeStack */
+      DUP[ ]	/* DuplicateTopStack */
+      GC[0]	/* GetCoordOnPVector */
+      DUP[ ]	/* DuplicateTopStack */
+      ROUND[00]	/* Round */
+      SUB[ ]	/* Subtract */
+      ABS[ ]	/* Absolute */
+      PUSHB[ ]	/* 1 value pushed */
+      4
+      CINDEX[ ]	/* CopyXToTopStack */
+      GC[0]	/* GetCoordOnPVector */
+      DUP[ ]	/* DuplicateTopStack */
+      ROUND[00]	/* Round */
+      SUB[ ]	/* Subtract */
+      ABS[ ]	/* Absolute */
+      GT[ ]	/* GreaterThan */
+      IF[ ]	/* If */
+      SWAP[ ]	/* SwapTopStack */
+      NEG[ ]	/* Negate */
+      ROLL[ ]	/* RollTopThreeStack */
+      EIF[ ]	/* EndIf */
+      MDAP[1]	/* MoveDirectAbsPt */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      0
+      GTEQ[ ]	/* GreaterThanOrEqual */
+      IF[ ]	/* If */
+      ROUND[01]	/* Round */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      0
+      EQ[ ]	/* Equal */
+      IF[ ]	/* If */
+      POP[ ]	/* PopTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      64
+      EIF[ ]	/* EndIf */
+      ELSE[ ]	/* Else */
+      ROUND[01]	/* Round */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      0
+      EQ[ ]	/* Equal */
+      IF[ ]	/* If */
+      POP[ ]	/* PopTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      64
+      NEG[ ]	/* Negate */
+      EIF[ ]	/* EndIf */
+      EIF[ ]	/* EndIf */
+      MSIRP[0]	/* MoveStackIndirRelPt */
+      ENDF[ ]	/* EndFunctionDefinition */
+      PUSHB[ ]	/* 1 value pushed */
+      4
+      FDEF[ ]	/* FunctionDefinition */
+      DUP[ ]	/* DuplicateTopStack */
+      GC[0]	/* GetCoordOnPVector */
+      PUSHB[ ]	/* 1 value pushed */
+      4
+      CINDEX[ ]	/* CopyXToTopStack */
+      GC[0]	/* GetCoordOnPVector */
+      GT[ ]	/* GreaterThan */
+      IF[ ]	/* If */
+      SWAP[ ]	/* SwapTopStack */
+      ROLL[ ]	/* RollTopThreeStack */
+      EIF[ ]	/* EndIf */
+      DUP[ ]	/* DuplicateTopStack */
+      GC[0]	/* GetCoordOnPVector */
+      DUP[ ]	/* DuplicateTopStack */
+      ROUND[10]	/* Round */
+      SUB[ ]	/* Subtract */
+      ABS[ ]	/* Absolute */
+      PUSHB[ ]	/* 1 value pushed */
+      4
+      CINDEX[ ]	/* CopyXToTopStack */
+      GC[0]	/* GetCoordOnPVector */
+      DUP[ ]	/* DuplicateTopStack */
+      ROUND[10]	/* Round */
+      SUB[ ]	/* Subtract */
+      ABS[ ]	/* Absolute */
+      GT[ ]	/* GreaterThan */
+      IF[ ]	/* If */
+      SWAP[ ]	/* SwapTopStack */
+      ROLL[ ]	/* RollTopThreeStack */
+      EIF[ ]	/* EndIf */
+      MDAP[1]	/* MoveDirectAbsPt */
+      MIRP[11101]	/* MoveIndirectRelPt */
+      ENDF[ ]	/* EndFunctionDefinition */
+      PUSHB[ ]	/* 1 value pushed */
+      5
+      FDEF[ ]	/* FunctionDefinition */
+      MPPEM[ ]	/* MeasurePixelPerEm */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      MINDEX[ ]	/* MoveXToTopStack */
+      LT[ ]	/* LessThan */
+      IF[ ]	/* If */
+      LTEQ[ ]	/* LessThenOrEqual */
+      IF[ ]	/* If */
+      PUSHB[ ]	/* 1 value pushed */
+      128
+      WCVTP[ ]	/* WriteCVTInPixels */
+      ELSE[ ]	/* Else */
+      PUSHB[ ]	/* 1 value pushed */
+      64
+      WCVTP[ ]	/* WriteCVTInPixels */
+      EIF[ ]	/* EndIf */
+      ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      POP[ ]	/* PopTopStack */
+      DUP[ ]	/* DuplicateTopStack */
+      RCVT[ ]	/* ReadCVT */
+      PUSHB[ ]	/* 1 value pushed */
+      192
+      LT[ ]	/* LessThan */
+      IF[ ]	/* If */
+      PUSHB[ ]	/* 1 value pushed */
+      192
+      WCVTP[ ]	/* WriteCVTInPixels */
+      ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      EIF[ ]	/* EndIf */
+      EIF[ ]	/* EndIf */
+      ENDF[ ]	/* EndFunctionDefinition */
+      PUSHB[ ]	/* 1 value pushed */
+      6
+      FDEF[ ]	/* FunctionDefinition */
+      DUP[ ]	/* DuplicateTopStack */
+      DUP[ ]	/* DuplicateTopStack */
+      RCVT[ ]	/* ReadCVT */
+      ROUND[01]	/* Round */
+      WCVTP[ ]	/* WriteCVTInPixels */
+      PUSHB[ ]	/* 1 value pushed */
+      1
+      ADD[ ]	/* Add */
+      DUP[ ]	/* DuplicateTopStack */
+      DUP[ ]	/* DuplicateTopStack */
+      RCVT[ ]	/* ReadCVT */
+      RDTG[ ]	/* RoundDownToGrid */
+      ROUND[01]	/* Round */
+      RTG[ ]	/* RoundToGrid */
+      WCVTP[ ]	/* WriteCVTInPixels */
+      PUSHB[ ]	/* 1 value pushed */
+      1
+      ADD[ ]	/* Add */
+      ENDF[ ]	/* EndFunctionDefinition */
+      PUSHB[ ]	/* 1 value pushed */
+      7
+      FDEF[ ]	/* FunctionDefinition */
+      PUSHB[ ]	/* 1 value pushed */
+      6
+      LOOPCALL[ ]	/* LoopAndCallFunction */
+      ENDF[ ]	/* EndFunctionDefinition */
+      PUSHB[ ]	/* 1 value pushed */
+      8
+      FDEF[ ]	/* FunctionDefinition */
+      MPPEM[ ]	/* MeasurePixelPerEm */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      MINDEX[ ]	/* MoveXToTopStack */
+      GTEQ[ ]	/* GreaterThanOrEqual */
+      IF[ ]	/* If */
+      PUSHB[ ]	/* 1 value pushed */
+      64
+      ELSE[ ]	/* Else */
+      PUSHB[ ]	/* 1 value pushed */
+      0
+      EIF[ ]	/* EndIf */
+      ROLL[ ]	/* RollTopThreeStack */
+      ROLL[ ]	/* RollTopThreeStack */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      MINDEX[ ]	/* MoveXToTopStack */
+      GTEQ[ ]	/* GreaterThanOrEqual */
+      IF[ ]	/* If */
+      SWAP[ ]	/* SwapTopStack */
+      POP[ ]	/* PopTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      128
+      ROLL[ ]	/* RollTopThreeStack */
+      ROLL[ ]	/* RollTopThreeStack */
+      ELSE[ ]	/* Else */
+      ROLL[ ]	/* RollTopThreeStack */
+      SWAP[ ]	/* SwapTopStack */
+      EIF[ ]	/* EndIf */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      MINDEX[ ]	/* MoveXToTopStack */
+      GTEQ[ ]	/* GreaterThanOrEqual */
+      IF[ ]	/* If */
+      SWAP[ ]	/* SwapTopStack */
+      POP[ ]	/* PopTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      192
+      ROLL[ ]	/* RollTopThreeStack */
+      ROLL[ ]	/* RollTopThreeStack */
+      ELSE[ ]	/* Else */
+      ROLL[ ]	/* RollTopThreeStack */
+      SWAP[ ]	/* SwapTopStack */
+      EIF[ ]	/* EndIf */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      MINDEX[ ]	/* MoveXToTopStack */
+      GTEQ[ ]	/* GreaterThanOrEqual */
+      IF[ ]	/* If */
+      SWAP[ ]	/* SwapTopStack */
+      POP[ ]	/* PopTopStack */
+      PUSHW[ ]	/* 1 value pushed */
+      256
+      ROLL[ ]	/* RollTopThreeStack */
+      ROLL[ ]	/* RollTopThreeStack */
+      ELSE[ ]	/* Else */
+      ROLL[ ]	/* RollTopThreeStack */
+      SWAP[ ]	/* SwapTopStack */
+      EIF[ ]	/* EndIf */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      MINDEX[ ]	/* MoveXToTopStack */
+      GTEQ[ ]	/* GreaterThanOrEqual */
+      IF[ ]	/* If */
+      SWAP[ ]	/* SwapTopStack */
+      POP[ ]	/* PopTopStack */
+      PUSHW[ ]	/* 1 value pushed */
+      320
+      ROLL[ ]	/* RollTopThreeStack */
+      ROLL[ ]	/* RollTopThreeStack */
+      ELSE[ ]	/* Else */
+      ROLL[ ]	/* RollTopThreeStack */
+      SWAP[ ]	/* SwapTopStack */
+      EIF[ ]	/* EndIf */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      MINDEX[ ]	/* MoveXToTopStack */
+      GTEQ[ ]	/* GreaterThanOrEqual */
+      IF[ ]	/* If */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      CINDEX[ ]	/* CopyXToTopStack */
+      RCVT[ ]	/* ReadCVT */
+      PUSHW[ ]	/* 1 value pushed */
+      384
+      LT[ ]	/* LessThan */
+      IF[ ]	/* If */
+      SWAP[ ]	/* SwapTopStack */
+      POP[ ]	/* PopTopStack */
+      PUSHW[ ]	/* 1 value pushed */
+      384
+      SWAP[ ]	/* SwapTopStack */
+      POP[ ]	/* PopTopStack */
+      ELSE[ ]	/* Else */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      CINDEX[ ]	/* CopyXToTopStack */
+      RCVT[ ]	/* ReadCVT */
+      SWAP[ ]	/* SwapTopStack */
+      POP[ ]	/* PopTopStack */
+      SWAP[ ]	/* SwapTopStack */
+      POP[ ]	/* PopTopStack */
+      EIF[ ]	/* EndIf */
+      ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      EIF[ ]	/* EndIf */
+      WCVTP[ ]	/* WriteCVTInPixels */
+      ENDF[ ]	/* EndFunctionDefinition */
+      PUSHB[ ]	/* 1 value pushed */
+      9
+      FDEF[ ]	/* FunctionDefinition */
+      MPPEM[ ]	/* MeasurePixelPerEm */
+      GTEQ[ ]	/* GreaterThanOrEqual */
+      IF[ ]	/* If */
+      RCVT[ ]	/* ReadCVT */
+      WCVTP[ ]	/* WriteCVTInPixels */
+      ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      POP[ ]	/* PopTopStack */
+      EIF[ ]	/* EndIf */
+      ENDF[ ]	/* EndFunctionDefinition */
+    </assembly>
+  </fpgm>
+
+  <prep>
+    <assembly>
+      PUSHB[ ]	/* 1 value pushed */
+      0
+      CALL[ ]	/* CallFunction */
+      SVTCA[0]	/* SetFPVectorToAxis */
+      PUSHB[ ]	/* 3 values pushed */
+      1 1 2
+      CALL[ ]	/* CallFunction */
+      SVTCA[1]	/* SetFPVectorToAxis */
+      PUSHB[ ]	/* 3 values pushed */
+      2 1 2
+      CALL[ ]	/* CallFunction */
+      SVTCA[1]	/* SetFPVectorToAxis */
+      PUSHW[ ]	/* 2 values pushed */
+      2 275
+      PUSHB[ ]	/* 6 values pushed */
+      225 175 125 75 0 8
+      CALL[ ]	/* CallFunction */
+      SVTCA[0]	/* SetFPVectorToAxis */
+      PUSHW[ ]	/* 2 values pushed */
+      1 275
+      PUSHB[ ]	/* 6 values pushed */
+      225 175 125 75 0 8
+      CALL[ ]	/* CallFunction */
+      SVTCA[0]	/* SetFPVectorToAxis */
+      PUSHB[ ]	/* 3 values pushed */
+      3 2 7
+      CALL[ ]	/* CallFunction */
+      PUSHB[ ]	/* 1 value pushed */
+      0
+      DUP[ ]	/* DuplicateTopStack */
+      RCVT[ ]	/* ReadCVT */
+      RDTG[ ]	/* RoundDownToGrid */
+      ROUND[01]	/* Round */
+      RTG[ ]	/* RoundToGrid */
+      WCVTP[ ]	/* WriteCVTInPixels */
+      MPPEM[ ]	/* MeasurePixelPerEm */
+      PUSHB[ ]	/* 1 value pushed */
+      96
+      GT[ ]	/* GreaterThan */
+      IF[ ]	/* If */
+      PUSHB[ ]	/* 1 value pushed */
+      1
+      ELSE[ ]	/* Else */
+      PUSHB[ ]	/* 1 value pushed */
+      0
+      EIF[ ]	/* EndIf */
+      PUSHB[ ]	/* 1 value pushed */
+      1
+      INSTCTRL[ ]	/* SetInstrExecControl */
+    </assembly>
+  </prep>
+
+  <cvt>
+    <cv index="0" value="20"/>
+    <cv index="1" value="20"/>
+    <cv index="2" value="20"/>
+    <cv index="3" value="0"/>
+    <cv index="4" value="10"/>
+    <cv index="5" value="500"/>
+    <cv index="6" value="10"/>
+  </cvt>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="10" yMin="0" xMax="486" yMax="660">
+      <contour>
+        <pt x="10" y="660" on="1"/>
+        <pt x="486" y="660" on="1"/>
+        <pt x="486" y="0" on="1"/>
+        <pt x="10" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="375" y="58" on="1"/>
+        <pt x="302" y="190" on="1"/>
+        <pt x="250" y="293" on="1"/>
+        <pt x="246" y="293" on="1"/>
+        <pt x="192" y="190" on="1"/>
+        <pt x="118" y="58" on="1"/>
+      </contour>
+      <contour>
+        <pt x="250" y="387" on="1"/>
+        <pt x="299" y="481" on="1"/>
+        <pt x="365" y="600" on="1"/>
+        <pt x="130" y="600" on="1"/>
+        <pt x="196" y="481" on="1"/>
+        <pt x="246" y="387" on="1"/>
+      </contour>
+      <contour>
+        <pt x="202" y="340" on="1"/>
+        <pt x="75" y="572" on="1"/>
+        <pt x="75" y="110" on="1"/>
+      </contour>
+      <contour>
+        <pt x="419" y="572" on="1"/>
+        <pt x="293" y="340" on="1"/>
+        <pt x="419" y="110" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="132" yMin="304" xMax="365" yMax="567">
+      <contour>
+        <pt x="132" y="567" on="1"/>
+        <pt x="365" y="567" on="1"/>
+        <pt x="365" y="304" on="1"/>
+        <pt x="132" y="304" on="1"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="B" xMin="132" yMin="304" xMax="365" yMax="567">
+      <contour>
+        <pt x="132" y="567" on="1"/>
+        <pt x="365" y="567" on="1"/>
+        <pt x="365" y="304" on="1"/>
+        <pt x="132" y="304" on="1"/>
+      </contour>
+      <instructions><assembly>
+          SVTCA[0]	/* SetFPVectorToAxis */
+          PUSHB[ ]	/* 3 values pushed */
+          1 2 3
+          CALL[ ]	/* CallFunction */
+          IUP[0]	/* InterpolateUntPts */
+          IUP[1]	/* InterpolateUntPts */
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="C"/><!-- contains no outline data -->
+
+    <TTGlyph name="u1F6D2"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestTTF
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      1.000;UKWN;TestTTF-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestTTF
+    </namerecord>
+    <namerecord nameID="5" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Version 1.000;PS 1.000;hotconv 1.0.88;makeotf.lib2.5.647800 DEVELOPMENT
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestTTF-Regular
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      TestTTF
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.000;UKWN;TestTTF-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      TestTTF
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000;PS 1.000;hotconv 1.0.88;makeotf.lib2.5.647800 DEVELOPMENT
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestTTF-Regular
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name="u1F6D2"/>
+    </extraNames>
+  </post>
+
+  <gasp>
+    <gaspRange rangeMaxPPEM="65535" rangeGaspBehavior="2"/>
+  </gasp>
+
+</ttFont>
diff --git a/Tests/subset/data/expect_ankr.ttx b/Tests/subset/data/expect_ankr.ttx
new file mode 100644
index 0000000..faad147
--- /dev/null
+++ b/Tests/subset/data/expect_ankr.ttx
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <ankr>
+    <AnchorPoints Format="0">
+      <Flags value="0"/>
+      <Anchors>
+        <Lookup glyph="one">
+          <!-- AnchorPointCount=1 -->
+          <AnchorPoint index="0">
+            <XCoordinate value="565"/>
+            <YCoordinate value="1118"/>
+          </AnchorPoint>
+        </Lookup>
+      </Anchors>
+    </AnchorPoints>
+  </ankr>
+
+</ttFont>
diff --git a/Tests/subset/data/expect_bsln_0.ttx b/Tests/subset/data/expect_bsln_0.ttx
new file mode 100644
index 0000000..89f68d4
--- /dev/null
+++ b/Tests/subset/data/expect_bsln_0.ttx
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <bsln>
+    <Version value="0x00010000"/>
+    <Baseline Format="0">
+      <DefaultBaseline value="0"/>
+      <Delta index="0" value="0"/>
+      <Delta index="1" value="465"/>
+      <Delta index="2" value="0"/>
+      <Delta index="3" value="1345"/>
+      <Delta index="4" value="507"/>
+      <Delta index="5" value="0"/>
+      <Delta index="6" value="0"/>
+      <Delta index="7" value="0"/>
+      <Delta index="8" value="0"/>
+      <Delta index="9" value="0"/>
+      <Delta index="10" value="0"/>
+      <Delta index="11" value="0"/>
+      <Delta index="12" value="0"/>
+      <Delta index="13" value="0"/>
+      <Delta index="14" value="0"/>
+      <Delta index="15" value="0"/>
+      <Delta index="16" value="0"/>
+      <Delta index="17" value="0"/>
+      <Delta index="18" value="0"/>
+      <Delta index="19" value="0"/>
+      <Delta index="20" value="0"/>
+      <Delta index="21" value="0"/>
+      <Delta index="22" value="0"/>
+      <Delta index="23" value="0"/>
+      <Delta index="24" value="0"/>
+      <Delta index="25" value="0"/>
+      <Delta index="26" value="0"/>
+      <Delta index="27" value="0"/>
+      <Delta index="28" value="0"/>
+      <Delta index="29" value="0"/>
+      <Delta index="30" value="0"/>
+      <Delta index="31" value="0"/>
+    </Baseline>
+  </bsln>
+
+</ttFont>
diff --git a/Tests/subset/data/expect_bsln_1.ttx b/Tests/subset/data/expect_bsln_1.ttx
new file mode 100644
index 0000000..71e1df4
--- /dev/null
+++ b/Tests/subset/data/expect_bsln_1.ttx
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <bsln>
+    <Version value="0x00010000"/>
+    <Baseline Format="1">
+      <DefaultBaseline value="0"/>
+      <Delta index="0" value="0"/>
+      <Delta index="1" value="465"/>
+      <Delta index="2" value="0"/>
+      <Delta index="3" value="1345"/>
+      <Delta index="4" value="507"/>
+      <Delta index="5" value="0"/>
+      <Delta index="6" value="0"/>
+      <Delta index="7" value="0"/>
+      <Delta index="8" value="0"/>
+      <Delta index="9" value="0"/>
+      <Delta index="10" value="0"/>
+      <Delta index="11" value="0"/>
+      <Delta index="12" value="0"/>
+      <Delta index="13" value="0"/>
+      <Delta index="14" value="0"/>
+      <Delta index="15" value="0"/>
+      <Delta index="16" value="0"/>
+      <Delta index="17" value="0"/>
+      <Delta index="18" value="0"/>
+      <Delta index="19" value="0"/>
+      <Delta index="20" value="0"/>
+      <Delta index="21" value="0"/>
+      <Delta index="22" value="0"/>
+      <Delta index="23" value="0"/>
+      <Delta index="24" value="0"/>
+      <Delta index="25" value="0"/>
+      <Delta index="26" value="0"/>
+      <Delta index="27" value="0"/>
+      <Delta index="28" value="0"/>
+      <Delta index="29" value="0"/>
+      <Delta index="30" value="0"/>
+      <Delta index="31" value="0"/>
+      <BaselineValues>
+        <Lookup glyph="uni2EA2" value="1"/>
+      </BaselineValues>
+    </Baseline>
+  </bsln>
+
+</ttFont>
diff --git a/Tests/subset/data/expect_bsln_2.ttx b/Tests/subset/data/expect_bsln_2.ttx
new file mode 100644
index 0000000..6d2da18
--- /dev/null
+++ b/Tests/subset/data/expect_bsln_2.ttx
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <bsln>
+    <Version value="0x00010000"/>
+    <Baseline Format="2">
+      <DefaultBaseline value="0"/>
+      <StandardGlyph value="glyph00002"/>
+      <ControlPoint index="0" value="7"/>
+      <ControlPoint index="1" value="8"/>
+      <ControlPoint index="2" value="9"/>
+      <ControlPoint index="3" value="10"/>
+      <ControlPoint index="4" value="11"/>
+      <ControlPoint index="5" value="65535"/>
+      <ControlPoint index="6" value="65535"/>
+      <ControlPoint index="7" value="65535"/>
+      <ControlPoint index="8" value="65535"/>
+      <ControlPoint index="9" value="65535"/>
+      <ControlPoint index="10" value="65535"/>
+      <ControlPoint index="11" value="65535"/>
+      <ControlPoint index="12" value="65535"/>
+      <ControlPoint index="13" value="65535"/>
+      <ControlPoint index="14" value="65535"/>
+      <ControlPoint index="15" value="65535"/>
+      <ControlPoint index="16" value="65535"/>
+      <ControlPoint index="17" value="65535"/>
+      <ControlPoint index="18" value="65535"/>
+      <ControlPoint index="19" value="65535"/>
+      <ControlPoint index="20" value="65535"/>
+      <ControlPoint index="21" value="65535"/>
+      <ControlPoint index="22" value="65535"/>
+      <ControlPoint index="23" value="65535"/>
+      <ControlPoint index="24" value="65535"/>
+      <ControlPoint index="25" value="65535"/>
+      <ControlPoint index="26" value="65535"/>
+      <ControlPoint index="27" value="65535"/>
+      <ControlPoint index="28" value="65535"/>
+      <ControlPoint index="29" value="65535"/>
+      <ControlPoint index="30" value="65535"/>
+      <ControlPoint index="31" value="65535"/>
+    </Baseline>
+  </bsln>
+
+</ttFont>
diff --git a/Tests/subset/data/expect_bsln_3.ttx b/Tests/subset/data/expect_bsln_3.ttx
new file mode 100644
index 0000000..04f3f9a
--- /dev/null
+++ b/Tests/subset/data/expect_bsln_3.ttx
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <bsln>
+    <Version value="0x00010000"/>
+    <Baseline Format="3">
+      <DefaultBaseline value="0"/>
+      <StandardGlyph value="glyph00003"/>
+      <ControlPoint index="0" value="7"/>
+      <ControlPoint index="1" value="8"/>
+      <ControlPoint index="2" value="9"/>
+      <ControlPoint index="3" value="10"/>
+      <ControlPoint index="4" value="11"/>
+      <ControlPoint index="5" value="65535"/>
+      <ControlPoint index="6" value="65535"/>
+      <ControlPoint index="7" value="65535"/>
+      <ControlPoint index="8" value="65535"/>
+      <ControlPoint index="9" value="65535"/>
+      <ControlPoint index="10" value="65535"/>
+      <ControlPoint index="11" value="65535"/>
+      <ControlPoint index="12" value="65535"/>
+      <ControlPoint index="13" value="65535"/>
+      <ControlPoint index="14" value="65535"/>
+      <ControlPoint index="15" value="65535"/>
+      <ControlPoint index="16" value="65535"/>
+      <ControlPoint index="17" value="65535"/>
+      <ControlPoint index="18" value="65535"/>
+      <ControlPoint index="19" value="65535"/>
+      <ControlPoint index="20" value="65535"/>
+      <ControlPoint index="21" value="65535"/>
+      <ControlPoint index="22" value="65535"/>
+      <ControlPoint index="23" value="65535"/>
+      <ControlPoint index="24" value="65535"/>
+      <ControlPoint index="25" value="65535"/>
+      <ControlPoint index="26" value="65535"/>
+      <ControlPoint index="27" value="65535"/>
+      <ControlPoint index="28" value="65535"/>
+      <ControlPoint index="29" value="65535"/>
+      <ControlPoint index="30" value="65535"/>
+      <ControlPoint index="31" value="65535"/>
+      <BaselineValues>
+        <Lookup glyph="uni2EA2" value="1"/>
+      </BaselineValues>
+    </Baseline>
+  </bsln>
+
+</ttFont>
diff --git a/Tests/subset/data/expect_desubroutinize_CFF.ttx b/Tests/subset/data/expect_desubroutinize_CFF.ttx
new file mode 100644
index 0000000..81b5369
--- /dev/null
+++ b/Tests/subset/data/expect_desubroutinize_CFF.ttx
@@ -0,0 +1,238 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <CFF>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="Lobster1.4">
+      <version value="001.001"/>
+      <Notice value="Copyright (c) 2010 by Pablo Impallari. www.impallari.com. All rights reserved."/>
+      <Copyright value="Copyright (c) 2010 by Pablo Impallari. All rights reserved."/>
+      <FullName value="Lobster 1.4"/>
+      <FamilyName value="Lobster 1.4"/>
+      <Weight value="Regular"/>
+      <isFixedPitch value="0"/>
+      <ItalicAngle value="0"/>
+      <UnderlinePosition value="-100"/>
+      <UnderlineThickness value="50"/>
+      <PaintType value="0"/>
+      <CharstringType value="2"/>
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FontBBox value="-209 -250 1186 1000"/>
+      <StrokeWidth value="0"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <Encoding name="StandardEncoding"/>
+      <Private>
+        <BlueValues value="0 0"/>
+        <BlueScale value="0.039625"/>
+        <BlueShift value="7"/>
+        <BlueFuzz value="1"/>
+        <ForceBold value="0"/>
+        <LanguageGroup value="0"/>
+        <ExpansionFactor value="0.06"/>
+        <initialRandomSeed value="0"/>
+        <defaultWidthX value="267"/>
+        <nominalWidthX value="448"/>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          -63 endchar
+        </CharString>
+        <CharString name="A">
+          220 -93 -21 114 -20 297 181 -59 59 292 -20 hstemhm
+          9 118 -43 120 hintmask 11101100
+          535 hmoveto
+          157 736 rlineto
+          10 -24 -32 4 -23 hhcurveto
+          -117 -130 -135 -160 -101 hvcurveto
+          2 -21 -17 1 -14 hhcurveto
+          -118 -86 -55 -68 -39 28 -19 34 31 25 15 24 14 -8 17 -5 hvcurveto
+          hintmask 11011010
+          13 34 42 14 62 4 rrcurveto
+          -87 -153 -60 -164 -90 vvcurveto
+          -104 80 -2 54 vhcurveto
+          -6 9 -8 15 32 vvcurveto
+          104 55 190 75 163 vhcurveto
+          44 -4 39 -9 51 -23 -77 -363 rcurveline
+          86 407 rmoveto
+          -39 16 -43 11 -40 8 56 112 64 93 60 32 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="A.salt">
+          142 -92 -21 113 -20 386 52 333 -20 hstem
+          8 120 vstem
+          459 hmoveto
+          157 736 rlineto
+          12 -30 -26 3 -24 hhcurveto
+          -238 -290 -563 -189 -106 65 -2 69 -4 hvcurveto
+          -1 9 -13 -4 51 vvcurveto
+          97 42 172 64 154 vhcurveto
+          158 hlineto
+          -77 -366 rlineto
+          -59 418 rmoveto
+          58 126 72 106 73 32 -56 -264 rcurveline
+          endchar
+        </CharString>
+        <CharString name="B">
+          187 -17 96 -79 -20 406 48 270 46 hstemhm
+          6 93 362 139 -119 101 -101 119 hintmask 01111100
+          230 636 rmoveto
+          -136 -636 rlineto
+          144 hlineto
+          82 383 rlineto
+          2 18 20 1 8 hhcurveto
+          73 22 -57 -70 hvcurveto
+          hintmask 10111001
+          -76 -26 -104 -73 -23 -19 10 26 -25 vhcurveto
+          -9 -23 -4 -19 -16 vvcurveto
+          -61 56 -13 43 167 52 192 96 75 -33 69 -85 17 vhcurveto
+          hintmask 10111010
+          65 37 35 63 59 vvcurveto
+          82 -66 77 -147 -189 -174 -127 -138 -67 41 -25 66 vhcurveto
+          -1 9 -13 8 51 vvcurveto
+          165 133 78 117 95 37 -51 -57 -75 -64 -87 -80 vhcurveto
+          -6 hlineto
+          47 222 rlineto
+          endchar
+        </CharString>
+        <CharString name="B.salt">
+          185 -28 92 -64 -20 413 41 270 46 hstemhm
+          6 93 350 149 -119 105 -105 119 hintmask 01111100
+          230 636 rmoveto
+          -136 -636 rlineto
+          144 hlineto
+          6 30 rlineto
+          hintmask 10111001
+          -41 39 41 -17 39 hhcurveto
+          125 110 175 136 72 -32 62 -82 15 hvcurveto
+          hintmask 10111010
+          64 38 36 61 58 vvcurveto
+          83 -74 78 -144 -183 -177 -126 -139 -67 41 -25 66 vhcurveto
+          -1 9 -13 8 51 vvcurveto
+          152 116 91 138 101 25 -49 -53 -81 -59 -87 -83 vhcurveto
+          -6 hlineto
+          47 222 rlineto
+          -59 -592 rmoveto
+          -20 -21 8 21 -20 hvcurveto
+          62 290 rlineto
+          2 18 20 1 7 hhcurveto
+          hintmask 10111100
+          63 21 -49 -57 -96 -58 -120 -72 hvcurveto
+          endchar
+        </CharString>
+        <CharString name="I">
+          -73 21 -21 750 -20 hstem
+          6 93 vstem
+          397 748 rmoveto
+          1 -13 -13 1 -14 hhcurveto
+          -167 -184 -127 -133 -72 38 -25 69 hvcurveto
+          -1 9 -13 8 51 vvcurveto
+          107 53 75 87 36 vhcurveto
+          -145 -679 rlineto
+          144 hlineto
+          endchar
+        </CharString>
+        <CharString name="IJ">
+          215 -207 50 157 -20 770 -20 hstemhm
+          6 93 13 84 -84 205 hintmask 11111000
+          397 748 rmoveto
+          1 -13 -13 1 -14 hhcurveto
+          -167 -184 -127 -133 -72 38 -25 69 hvcurveto
+          -1 9 -13 8 51 vvcurveto
+          107 53 75 87 36 vhcurveto
+          -145 -679 rlineto
+          34 hlineto
+          -11 -20 -5 -23 -27 vvcurveto
+          -79 48 -58 113 155 66 109 138 29 vhcurveto
+          150 710 -150 -33 -164 -751 rlineto
+          -100 -22 -30 -23 -40 hhcurveto
+          -44 -27 29 39 40 29 33 36 16 17 -7 -16 16 hvcurveto
+          hintmask 11110100
+          4 11 3 11 11 vvcurveto
+          34 -26 24 -41 6 vhcurveto
+          endchar
+        </CharString>
+        <CharString name="J">
+          88 -207 50 144 81 682 -20 hstemhm
+          17 84 -84 220 -50 93 hintmask 11110100
+          538 750 rmoveto
+          -167 -184 -127 -133 -72 38 -25 69 hvcurveto
+          -1 9 -13 8 51 vvcurveto
+          107 54 76 87 36 vhcurveto
+          -157 -714 rlineto
+          -103 -23 -27 -20 -45 hhcurveto
+          -29 -39 18 52 37 24 37 46 20 15 -5 -21 25 hvcurveto
+          hintmask 11101000
+          4 15 2 14 11 vvcurveto
+          64 -58 3 -40 -79 -43 -66 -68 -83 53 -58 95 164 67 94 153 32 vhcurveto
+          150 710 rlineto
+          endchar
+        </CharString>
+        <CharString name="one">
+          -131 21 -21 624 46 78 -20 hstem
+          324 748 rmoveto
+          -72 -121 -78 -6 -55 hhcurveto
+          -12 -46 rlineto
+          95 hlineto
+          -132 -624 rlineto
+          144 hlineto
+          endchar
+        </CharString>
+        <CharString name="three">
+          66 -5 65 197 51 204 237 -54 54 hstemhm
+          6 111 -12 110 117 155 -117 117 hintmask 11101001
+          205 257 rmoveto
+          38 -8 -33 13 -37 hhcurveto
+          -80 -41 -60 -83 -154 141 -16 58 171 111 136 121 71 -38 65 -88 29 hvcurveto
+          92 46 45 74 66 vvcurveto
+          78 -63 68 -123 vhcurveto
+          hintmask 11100110
+          -116 -91 -61 -91 -54 32 -31 40 24 27 11 23 25 hvcurveto
+          -28 8 -10 36 27 vvcurveto
+          hintmask 11011001
+          47 31 31 48 51 25 -36 -46 -70 -58 -94 -113 -31 vhcurveto
+          hintmask 11101010
+          93 -33 40 -80 -76 vvcurveto
+          -87 -53 -82 -86 -37 -39 13 76 40 10 62 78 6 vhcurveto
+          endchar
+        </CharString>
+        <CharString name="two">
+          44 -11 125 -89 89 -89 107 380 237 -54 54 hstemhm
+          66 110 142 119 -119 144 hintmask 00110101
+          111 132 rmoveto
+          -5 hlineto
+          83 135 273 98 223 vvcurveto
+          97 -53 64 -137 -151 -55 -79 -68 -58 31 -32 41 24 26 11 23 26 vhcurveto
+          -28 8 -10 37 23 vvcurveto
+          hintmask 01001110
+          50 14 31 67 29 32 -33 -49 vhcurveto
+          -266 -329 -98 -219 vvcurveto
+          -11 0 -11 2 -11 vhcurveto
+          7 20 36 21 23 hhcurveto
+          hintmask 10010110
+          102 37 -36 109 hhcurveto
+          99 20 52 98 14 0 14 -1 16 hvcurveto
+          -44 -47 -17 -25 -70 hhcurveto
+          hintmask 00110110
+          -75 -57 18 -59 hhcurveto
+          endchar
+        </CharString>
+        <CharString name="zero">
+          98 -9 84 623 52 hstem
+          30 158 236 131 vstem
+          377 750 rmoveto
+          -215 -132 -223 -273 -166 35 -97 172 205 113 299 199 168 -53 93 -125 hvcurveto
+          -189 -425 rmoveto
+          225 17 105 148 60 hhcurveto
+          47 7 -63 -82 -232 -68 -246 -114 -48 -11 77 74 37 3 35 2 27 hvcurveto
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+</ttFont>
diff --git a/Tests/subset/data/expect_keep_colr.ttx b/Tests/subset/data/expect_keep_colr.ttx
new file mode 100644
index 0000000..d6736ef
--- /dev/null
+++ b/Tests/subset/data/expect_keep_colr.ttx
@@ -0,0 +1,195 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="smileface"/>
+    <GlyphID id="2" name="glyph00002"/>
+    <GlyphID id="3" name="glyph00003"/>
+    <GlyphID id="4" name="glyph00004"/>
+  </GlyphOrder>
+
+  <hmtx>
+    <mtx name=".notdef" width="364" lsb="33"/>
+    <mtx name="glyph00002" width="1000" lsb="100"/>
+    <mtx name="glyph00003" width="1000" lsb="100"/>
+    <mtx name="glyph00004" width="1000" lsb="238"/>
+    <mtx name="smileface" width="1000" lsb="100"/>
+  </hmtx>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef"/><!-- contains no outline data -->
+
+    <TTGlyph name="glyph00002" xMin="100" yMin="0" xMax="900" yMax="800">
+      <contour>
+        <pt x="100" y="234" on="0"/>
+        <pt x="100" y="566" on="0"/>
+        <pt x="334" y="800" on="0"/>
+        <pt x="666" y="800" on="0"/>
+        <pt x="900" y="566" on="0"/>
+        <pt x="900" y="234" on="0"/>
+        <pt x="666" y="0" on="0"/>
+        <pt x="334" y="0" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="glyph00003" xMin="100" yMin="0" xMax="900" yMax="800">
+      <contour>
+        <pt x="100" y="234" on="0"/>
+        <pt x="100" y="566" on="0"/>
+        <pt x="334" y="800" on="0"/>
+        <pt x="666" y="800" on="0"/>
+        <pt x="900" y="566" on="0"/>
+        <pt x="900" y="234" on="0"/>
+        <pt x="666" y="0" on="0"/>
+        <pt x="334" y="0" on="0"/>
+      </contour>
+      <contour>
+        <pt x="130" y="553" on="0"/>
+        <pt x="130" y="247" on="0"/>
+        <pt x="347" y="30" on="0"/>
+        <pt x="653" y="30" on="0"/>
+        <pt x="870" y="247" on="0"/>
+        <pt x="870" y="553" on="0"/>
+        <pt x="653" y="770" on="0"/>
+        <pt x="347" y="770" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="glyph00004" xMin="238" yMin="181" xMax="761" yMax="606">
+      <contour>
+        <pt x="733" y="348" on="0"/>
+        <pt x="761" y="336" on="0"/>
+        <pt x="755" y="322" on="1"/>
+        <pt x="724" y="256" on="0"/>
+        <pt x="582" y="183" on="0"/>
+        <pt x="500" y="182" on="1"/>
+        <pt x="417" y="181" on="0"/>
+        <pt x="279" y="251" on="0"/>
+        <pt x="245" y="321" on="1"/>
+        <pt x="238" y="333" on="0"/>
+        <pt x="251" y="341" on="1"/>
+        <pt x="263" y="348" on="0"/>
+        <pt x="271" y="335" on="1"/>
+        <pt x="301" y="275" on="0"/>
+        <pt x="425" y="211" on="0"/>
+        <pt x="575" y="213" on="0"/>
+        <pt x="701" y="277" on="0"/>
+        <pt x="727" y="334" on="1"/>
+      </contour>
+      <contour>
+        <pt x="623" y="481" on="0"/>
+        <pt x="623" y="555" on="0"/>
+        <pt x="646" y="606" on="0"/>
+        <pt x="680" y="606" on="0"/>
+        <pt x="703" y="555" on="0"/>
+        <pt x="703" y="481" on="0"/>
+        <pt x="680" y="430" on="0"/>
+        <pt x="646" y="430" on="0"/>
+      </contour>
+      <contour>
+        <pt x="297" y="481" on="0"/>
+        <pt x="297" y="555" on="0"/>
+        <pt x="320" y="606" on="0"/>
+        <pt x="354" y="606" on="0"/>
+        <pt x="377" y="555" on="0"/>
+        <pt x="377" y="481" on="0"/>
+        <pt x="354" y="430" on="0"/>
+        <pt x="320" y="430" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="smileface" xMin="100" yMin="0" xMax="900" yMax="800">
+      <contour>
+        <pt x="733" y="348" on="0"/>
+        <pt x="761" y="336" on="0"/>
+        <pt x="755" y="322" on="1"/>
+        <pt x="724" y="256" on="0"/>
+        <pt x="582" y="183" on="0"/>
+        <pt x="500" y="182" on="1"/>
+        <pt x="417" y="181" on="0"/>
+        <pt x="279" y="251" on="0"/>
+        <pt x="245" y="321" on="1"/>
+        <pt x="238" y="333" on="0"/>
+        <pt x="251" y="341" on="1"/>
+        <pt x="263" y="348" on="0"/>
+        <pt x="271" y="335" on="1"/>
+        <pt x="301" y="275" on="0"/>
+        <pt x="425" y="211" on="0"/>
+        <pt x="575" y="213" on="0"/>
+        <pt x="701" y="277" on="0"/>
+        <pt x="727" y="334" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="234" on="0"/>
+        <pt x="100" y="566" on="0"/>
+        <pt x="334" y="800" on="0"/>
+        <pt x="666" y="800" on="0"/>
+        <pt x="900" y="566" on="0"/>
+        <pt x="900" y="234" on="0"/>
+        <pt x="666" y="0" on="0"/>
+        <pt x="334" y="0" on="0"/>
+      </contour>
+      <contour>
+        <pt x="130" y="553" on="0"/>
+        <pt x="130" y="247" on="0"/>
+        <pt x="347" y="30" on="0"/>
+        <pt x="653" y="30" on="0"/>
+        <pt x="870" y="247" on="0"/>
+        <pt x="870" y="553" on="0"/>
+        <pt x="653" y="770" on="0"/>
+        <pt x="347" y="770" on="0"/>
+      </contour>
+      <contour>
+        <pt x="623" y="482" on="0"/>
+        <pt x="623" y="554" on="0"/>
+        <pt x="646" y="606" on="0"/>
+        <pt x="680" y="606" on="0"/>
+        <pt x="703" y="554" on="0"/>
+        <pt x="703" y="482" on="0"/>
+        <pt x="680" y="430" on="0"/>
+        <pt x="646" y="430" on="0"/>
+      </contour>
+      <contour>
+        <pt x="297" y="482" on="0"/>
+        <pt x="297" y="554" on="0"/>
+        <pt x="320" y="606" on="0"/>
+        <pt x="354" y="606" on="0"/>
+        <pt x="377" y="554" on="0"/>
+        <pt x="377" y="482" on="0"/>
+        <pt x="354" y="430" on="0"/>
+        <pt x="320" y="430" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <COLR>
+    <version value="0"/>
+    <ColorGlyph name="smileface">
+      <layer colorID="0" name="glyph00002"/>
+      <layer colorID="1" name="glyph00003"/>
+      <layer colorID="2" name="glyph00004"/>
+    </ColorGlyph>
+  </COLR>
+
+  <CPAL>
+    <version value="0"/>
+    <numPaletteEntries value="3"/>
+    <palette index="0">
+      <color index="0" value="#FFE08AFF"/>
+      <color index="1" value="#E59B25FF"/>
+      <color index="2" value="#84380DFF"/>
+    </palette>
+  </CPAL>
+
+</ttFont>
diff --git a/Tests/subset/data/expect_keep_gvar.ttx b/Tests/subset/data/expect_keep_gvar.ttx
new file mode 100644
index 0000000..43e4a34
--- /dev/null
+++ b/Tests/subset/data/expect_keep_gvar.ttx
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="minus"/>
+    <GlyphID id="2" name="plus"/>
+  </GlyphOrder>
+
+  <avar>
+    <segment axis="wght">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="0.3" to="0.5"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+  </avar>
+
+  <fvar>
+
+    <!-- Weight -->
+    <Axis>
+      <AxisTag>wght</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>100.0</MinValue>
+      <DefaultValue>400.0</DefaultValue>
+      <MaxValue>900.0</MaxValue>
+      <AxisNameID>257</AxisNameID>
+    </Axis>
+
+    <!-- Thin -->
+    <NamedInstance flags="0x0" subfamilyNameID="258">
+      <coord axis="wght" value="100.0"/>
+    </NamedInstance>
+
+    <!-- Light -->
+    <NamedInstance flags="0x0" subfamilyNameID="259">
+      <coord axis="wght" value="300.0"/>
+    </NamedInstance>
+
+    <!-- Regular -->
+    <NamedInstance flags="0x0" subfamilyNameID="260">
+      <coord axis="wght" value="400.0"/>
+    </NamedInstance>
+
+    <!-- Bold -->
+    <NamedInstance flags="0x0" subfamilyNameID="261">
+      <coord axis="wght" value="700.0"/>
+    </NamedInstance>
+
+    <!-- Black -->
+    <NamedInstance flags="0x0" subfamilyNameID="262">
+      <coord axis="wght" value="900.0"/>
+    </NamedInstance>
+  </fvar>
+
+  <gvar>
+    <version value="1"/>
+    <reserved value="0"/>
+    <glyphVariations glyph="minus">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="0" x="-20" y="-30"/>
+        <delta pt="1" x="-20" y="30"/>
+        <delta pt="2" x="-20" y="30"/>
+        <delta pt="3" x="-20" y="-30"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="-40" y="0"/>
+        <delta pt="6" x="0" y="-30"/>
+        <delta pt="7" x="0" y="-30"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="20" y="30"/>
+        <delta pt="1" x="20" y="-30"/>
+        <delta pt="2" x="20" y="-30"/>
+        <delta pt="3" x="20" y="30"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="40" y="0"/>
+        <delta pt="6" x="0" y="30"/>
+        <delta pt="7" x="0" y="30"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="plus">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="0" x="-20" y="30"/>
+        <delta pt="1" x="-50" y="30"/>
+        <delta pt="2" x="-50" y="0"/>
+        <delta pt="3" x="10" y="0"/>
+        <delta pt="4" x="10" y="30"/>
+        <delta pt="5" x="-20" y="30"/>
+        <delta pt="6" x="-20" y="-30"/>
+        <delta pt="7" x="10" y="-30"/>
+        <delta pt="8" x="10" y="0"/>
+        <delta pt="9" x="-50" y="0"/>
+        <delta pt="10" x="-50" y="-30"/>
+        <delta pt="11" x="-20" y="-30"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="-40" y="0"/>
+        <delta pt="14" x="0" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="20" y="-30"/>
+        <delta pt="1" x="50" y="-30"/>
+        <delta pt="2" x="50" y="0"/>
+        <delta pt="3" x="-10" y="0"/>
+        <delta pt="4" x="-10" y="-30"/>
+        <delta pt="5" x="20" y="-30"/>
+        <delta pt="6" x="20" y="30"/>
+        <delta pt="7" x="-10" y="30"/>
+        <delta pt="8" x="-10" y="0"/>
+        <delta pt="9" x="50" y="0"/>
+        <delta pt="10" x="50" y="30"/>
+        <delta pt="11" x="20" y="30"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="40" y="0"/>
+        <delta pt="14" x="0" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+  </gvar>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      TestGVAR
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.000;UKWN;TestGVAR-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      TestGVAR-Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestGVAR-Regular
+    </namerecord>
+    <namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </namerecord>
+    <namerecord nameID="258" platformID="3" platEncID="1" langID="0x409">
+      Thin
+    </namerecord>
+    <namerecord nameID="259" platformID="3" platEncID="1" langID="0x409">
+      Light
+    </namerecord>
+    <namerecord nameID="260" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="261" platformID="3" platEncID="1" langID="0x409">
+      Bold
+    </namerecord>
+    <namerecord nameID="262" platformID="3" platEncID="1" langID="0x409">
+      Black
+    </namerecord>
+  </name>
+
+</ttFont>
diff --git a/Tests/subset/data/expect_keep_gvar_notdef_outline.ttx b/Tests/subset/data/expect_keep_gvar_notdef_outline.ttx
new file mode 100644
index 0000000..d4cc79f
--- /dev/null
+++ b/Tests/subset/data/expect_keep_gvar_notdef_outline.ttx
@@ -0,0 +1,213 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="zero"/>
+  </GlyphOrder>
+
+  <avar>
+    <segment axis="wght">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="0.3" to="0.5"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+  </avar>
+
+  <fvar>
+
+    <!-- Weight -->
+    <Axis>
+      <AxisTag>wght</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>100.0</MinValue>
+      <DefaultValue>400.0</DefaultValue>
+      <MaxValue>900.0</MaxValue>
+      <AxisNameID>257</AxisNameID>
+    </Axis>
+
+    <!-- Thin -->
+    <NamedInstance flags="0x0" subfamilyNameID="258">
+      <coord axis="wght" value="100.0"/>
+    </NamedInstance>
+
+    <!-- Light -->
+    <NamedInstance flags="0x0" subfamilyNameID="259">
+      <coord axis="wght" value="300.0"/>
+    </NamedInstance>
+
+    <!-- Regular -->
+    <NamedInstance flags="0x0" subfamilyNameID="260">
+      <coord axis="wght" value="400.0"/>
+    </NamedInstance>
+
+    <!-- Bold -->
+    <NamedInstance flags="0x0" subfamilyNameID="261">
+      <coord axis="wght" value="700.0"/>
+    </NamedInstance>
+
+    <!-- Black -->
+    <NamedInstance flags="0x0" subfamilyNameID="262">
+      <coord axis="wght" value="900.0"/>
+    </NamedInstance>
+  </fvar>
+
+  <gvar>
+    <version value="1"/>
+    <reserved value="0"/>
+    <glyphVariations glyph=".notdef">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="0" x="-20" y="0"/>
+        <delta pt="1" x="-20" y="0"/>
+        <delta pt="2" x="-20" y="0"/>
+        <delta pt="3" x="-20" y="0"/>
+        <delta pt="4" x="-80" y="-60"/>
+        <delta pt="5" x="40" y="-60"/>
+        <delta pt="6" x="40" y="60"/>
+        <delta pt="7" x="-80" y="60"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="-40" y="0"/>
+        <delta pt="10" x="0" y="0"/>
+        <delta pt="11" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="20" y="0"/>
+        <delta pt="1" x="20" y="0"/>
+        <delta pt="2" x="20" y="0"/>
+        <delta pt="3" x="20" y="0"/>
+        <delta pt="4" x="80" y="60"/>
+        <delta pt="5" x="-40" y="60"/>
+        <delta pt="6" x="-40" y="-60"/>
+        <delta pt="7" x="80" y="-60"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="40" y="0"/>
+        <delta pt="10" x="0" y="0"/>
+        <delta pt="11" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="zero">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="0" x="-10" y="0"/>
+        <delta pt="1" x="-10" y="0"/>
+        <delta pt="2" x="-10" y="0"/>
+        <delta pt="3" x="-10" y="0"/>
+        <delta pt="4" x="-10" y="0"/>
+        <delta pt="5" x="-10" y="0"/>
+        <delta pt="6" x="-10" y="0"/>
+        <delta pt="7" x="-10" y="0"/>
+        <delta pt="8" x="-10" y="0"/>
+        <delta pt="9" x="-10" y="0"/>
+        <delta pt="10" x="-10" y="0"/>
+        <delta pt="11" x="-10" y="0"/>
+        <delta pt="12" x="-10" y="0"/>
+        <delta pt="13" x="-10" y="0"/>
+        <delta pt="14" x="-10" y="0"/>
+        <delta pt="15" x="-10" y="0"/>
+        <delta pt="16" x="7" y="-64"/>
+        <delta pt="17" x="34" y="-53"/>
+        <delta pt="18" x="50" y="-25"/>
+        <delta pt="19" x="50" y="0"/>
+        <delta pt="20" x="50" y="25"/>
+        <delta pt="21" x="34" y="53"/>
+        <delta pt="22" x="7" y="64"/>
+        <delta pt="23" x="-10" y="64"/>
+        <delta pt="24" x="-28" y="64"/>
+        <delta pt="25" x="-54" y="53"/>
+        <delta pt="26" x="-70" y="25"/>
+        <delta pt="27" x="-70" y="0"/>
+        <delta pt="28" x="-70" y="-25"/>
+        <delta pt="29" x="-54" y="-53"/>
+        <delta pt="30" x="-28" y="-64"/>
+        <delta pt="31" x="-10" y="-64"/>
+        <delta pt="32" x="0" y="0"/>
+        <delta pt="33" x="-20" y="0"/>
+        <delta pt="34" x="0" y="0"/>
+        <delta pt="35" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="10" y="0"/>
+        <delta pt="1" x="10" y="0"/>
+        <delta pt="2" x="10" y="0"/>
+        <delta pt="3" x="10" y="0"/>
+        <delta pt="4" x="10" y="0"/>
+        <delta pt="5" x="10" y="0"/>
+        <delta pt="6" x="10" y="0"/>
+        <delta pt="7" x="10" y="0"/>
+        <delta pt="8" x="10" y="0"/>
+        <delta pt="9" x="10" y="0"/>
+        <delta pt="10" x="10" y="0"/>
+        <delta pt="11" x="10" y="0"/>
+        <delta pt="12" x="10" y="0"/>
+        <delta pt="13" x="10" y="0"/>
+        <delta pt="14" x="10" y="0"/>
+        <delta pt="15" x="10" y="0"/>
+        <delta pt="16" x="-6" y="55"/>
+        <delta pt="17" x="-34" y="37"/>
+        <delta pt="18" x="-50" y="11"/>
+        <delta pt="19" x="-50" y="0"/>
+        <delta pt="20" x="-50" y="-11"/>
+        <delta pt="21" x="-34" y="-37"/>
+        <delta pt="22" x="-6" y="-55"/>
+        <delta pt="23" x="10" y="-55"/>
+        <delta pt="24" x="25" y="-55"/>
+        <delta pt="25" x="54" y="-37"/>
+        <delta pt="26" x="70" y="-11"/>
+        <delta pt="27" x="70" y="0"/>
+        <delta pt="28" x="70" y="11"/>
+        <delta pt="29" x="54" y="37"/>
+        <delta pt="30" x="25" y="55"/>
+        <delta pt="31" x="10" y="55"/>
+        <delta pt="32" x="0" y="0"/>
+        <delta pt="33" x="20" y="0"/>
+        <delta pt="34" x="0" y="0"/>
+        <delta pt="35" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+  </gvar>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      TestGVAR
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.000;UKWN;TestGVAR-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      TestGVAR-Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestGVAR-Regular
+    </namerecord>
+    <namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </namerecord>
+    <namerecord nameID="258" platformID="3" platEncID="1" langID="0x409">
+      Thin
+    </namerecord>
+    <namerecord nameID="259" platformID="3" platEncID="1" langID="0x409">
+      Light
+    </namerecord>
+    <namerecord nameID="260" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="261" platformID="3" platEncID="1" langID="0x409">
+      Bold
+    </namerecord>
+    <namerecord nameID="262" platformID="3" platEncID="1" langID="0x409">
+      Black
+    </namerecord>
+  </name>
+
+</ttFont>
diff --git a/Tests/subset/data/expect_keep_math.ttx b/Tests/subset/data/expect_keep_math.ttx
new file mode 100644
index 0000000..c734dd9
--- /dev/null
+++ b/Tests/subset/data/expect_keep_math.ttx
@@ -0,0 +1,634 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="parenleft"/>
+    <GlyphID id="2" name="A"/>
+    <GlyphID id="3" name="uni0302"/>
+    <GlyphID id="4" name="uni239B"/>
+    <GlyphID id="5" name="uni239C"/>
+    <GlyphID id="6" name="uni239D"/>
+    <GlyphID id="7" name="u1D400"/>
+    <GlyphID id="8" name="u1D435"/>
+    <GlyphID id="9" name="parenleft.size1"/>
+    <GlyphID id="10" name="uni0302.size1"/>
+    <GlyphID id="11" name="parenleft.size2"/>
+    <GlyphID id="12" name="uni0302.size2"/>
+    <GlyphID id="13" name="parenleft.size3"/>
+    <GlyphID id="14" name="uni0302.size3"/>
+    <GlyphID id="15" name="parenleft.size4"/>
+    <GlyphID id="16" name="uni0302.size4"/>
+    <GlyphID id="17" name="uni0302.size5"/>
+  </GlyphOrder>
+
+  <CFF>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="XITSMath">
+      <version value="1.108"/>
+      <Notice value="Copyright (c) 2001-2011 by the STI Pub Companies, consisting of the American Chemical Society, the American Institute of Physics, the American Mathematical Society, the American Physical Society, Elsevier, Inc., and The Institute of Electrical and Electronic Engineers, Inc.  Portions copyright (c) 1998-2003 by MicroPress, Inc.  Portions copyright (c) 1990 by Elsevier, Inc.  Portions copyright (c) 2009-2012 by Khaled Hosny.  All rights reserved. "/>
+      <FullName value="XITS Math"/>
+      <FamilyName value="XITS Math"/>
+      <Weight value="Regular"/>
+      <isFixedPitch value="0"/>
+      <ItalicAngle value="0"/>
+      <UnderlinePosition value="-50"/>
+      <UnderlineThickness value="50"/>
+      <PaintType value="0"/>
+      <CharstringType value="2"/>
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FontBBox value="-970 -906 3238 2566"/>
+      <StrokeWidth value="0"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <Encoding name="StandardEncoding"/>
+      <Private>
+        <BlueValues value="-14 0 450 460 662 676"/>
+        <BlueScale value="0.039625"/>
+        <BlueShift value="6"/>
+        <BlueFuzz value="1"/>
+        <StdHW value="66"/>
+        <StdVW value="66"/>
+        <StemSnapH value="23 28 31 34 38 43 50 54 63 66"/>
+        <StemSnapV value="39 43 48 52 56 59 66 73 79 83"/>
+        <ForceBold value="0"/>
+        <LanguageGroup value="0"/>
+        <ExpansionFactor value="0.06"/>
+        <initialRandomSeed value="0"/>
+        <defaultWidthX value="685"/>
+        <nominalWidthX value="601"/>
+        <Subrs>
+          <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+          <CharString index="0">
+            19 vlineto
+            -52 6 -14 21 -28 65 rrcurveto
+            -246 563 -20 0 -206 -488 rlineto
+            -59 -140 -9 -21 -58 -6 rrcurveto
+            -19 199 19 vlineto
+            -48 -22 10 31 hvcurveto
+            0 12 4 17 5 13 rrcurveto
+            46 114 262 0 41 -94 rlineto
+            12 -28 7 -27 0 -15 0 -9 -6 -11 -8 -4 -12 -7 -7 -2 -36 0 rrcurveto
+            -19 vlineto
+            return
+          </CharString>
+          <CharString index="1">
+            -231 0 115 275 rlineto
+            return
+          </CharString>
+          <CharString index="2">
+            -125 167 -62 0 -124 -167 34 0 121 103 122 -103 rlineto
+            return
+          </CharString>
+          <CharString index="3">
+            25 vlineto
+            -41 0 -18 22 -51 121 rrcurveto
+            -222 522 -28 0 -221 -545 rlineto
+            -38 -94 -15 -18 -46 -8 rrcurveto
+            -25 202 25 vlineto
+            -59 4 -22 11 0 26 0 24 19 42 12 31 rrcurveto
+            13 34 225 0 rlineto
+            34 -79 12 -35 0 -22 0 -22 -13 -8 -33 -3 rrcurveto
+            -32 -3 0 -25 rlineto
+            return
+          </CharString>
+          <CharString index="4">
+            -5 -16 19 0 rlineto
+            59 19 -16 -26 hvcurveto
+            0 -6 -1 -10 -1 -5 rrcurveto
+            -123 -495 rlineto
+            -8 -34 -19 -29 -76 0 rrcurveto
+            -20 0 -4 -16 324 0 rlineto
+            207 82 94 99 hvcurveto
+            0 85 -67 42 -60 11 rrcurveto
+            -3 4 rlineto
+            20 5 29 8 25 13 48 24 43 45 0 78 rrcurveto
+            136 -152 9 -84 vhcurveto
+            return
+          </CharString>
+          <CharString index="5">
+            13 2 15 1 15 0 rrcurveto
+            54 65 -21 -77 -97 -56 -66 -112 hvcurveto
+            -15 0 -18 2 -20 4 rrcurveto
+            return
+          </CharString>
+          <CharString index="6">
+            15 4 20 0 16 0 rrcurveto
+            78 64 -35 -73 -130 -96 -42 -94 hvcurveto
+            -26 0 -20 2 -25 7 rrcurveto
+            return
+          </CharString>
+        </Subrs>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          -351 endchar
+        </CharString>
+        <CharString name="A">
+          121 0 20 196 41 397 20 hstem
+          707 hmoveto
+          -107 callsubr
+          -5 257 rmoveto
+          -106 callsubr
+          endchar
+        </CharString>
+        <CharString name="parenleft">
+          -268 656 20 hstem
+          48 86 vstem
+          304 -161 rmoveto
+          -140 117 -30 113 0 186 0 193 31 93 139 119 rrcurveto
+          -9 16 rlineto
+          -160 -95 -87 -144 0 -185 0 -170 86 -169 158 -90 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="parenleft.size1">
+          -133 139 81 vstem
+          382 -134 rmoveto
+          -90 110 -72 169 0 306 0 303 72 172 90 110 rrcurveto
+          30 vlineto
+          -142 -134 -101 -214 0 -267 0 -272 101 -209 142 -134 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="parenleft.size2">
+          -12 139 95 vstem
+          503 -243 rmoveto
+          -134 165 -135 265 0 456 0 458 135 294 134 138 rrcurveto
+          33 vlineto
+          -213 -171 -151 -352 0 -400 0 -409 151 -313 213 -200 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="parenleft.size3">
+          149 182 110 vstem
+          667 -346 rmoveto
+          -178 220 -197 349 0 613 0 606 197 396 178 184 rrcurveto
+          44 vlineto
+          -284 -228 -201 -464 0 -538 0 -541 201 -422 284 -267 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="parenleft.size4">
+          207 124 130 vstem
+          732 -453 rmoveto
+          -224 232 -254 504 0 746 0 777 254 473 224 231 rrcurveto
+          56 vlineto
+          -355 -286 -253 -578 0 -673 0 -675 253 -577 355 -286 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="u1D400">
+          121 0 20 177 39 hstem
+          689 hmoveto
+          -104 callsubr
+          17 236 rmoveto
+          -195 0 94 243 rlineto
+          endchar
+        </CharString>
+        <CharString name="u1D435">
+          95 0 38 280 35 261 39 hstem
+          198 653 rmoveto
+          -103 callsubr
+          -44 -42 rmoveto
+          -102 callsubr
+          -9 -45 rmoveto
+          -101 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0302">
+          -601 654 20 hstem
+          -75 507 rmoveto
+          -105 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0302.size1">
+          -41 560 554 rmoveto
+          -256 213 -48 0 -256 -213 64 0 216 146 216 -146 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni0302.size2">
+          378 979 564 rmoveto
+          -465 213 -48 0 -466 -213 99 0 391 145 390 -145 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni0302.size3">
+          859 1460 564 rmoveto
+          -706 213 -48 0 -706 -213 153 0 577 145 577 -145 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni0302.size4">
+          1285 1886 599 rmoveto
+          -943 197 -943 -197 5 -26 937 161 939 -161 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni0302.size5">
+          1727 2328 603 rmoveto
+          -1164 213 -1164 -213 5 -31 1158 182 1160 -182 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni239B">
+          -151 50 124 vstem
+          400 1005 rmoveto
+          -261 -184 -89 -359 0 -303 rrcurveto
+          -159 124 239 vlineto
+          0 254 54 285 172 197 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uni239C">
+          -151 50 124 vstem
+          174 hmoveto
+          1010 -124 -1010 vlineto
+          endchar
+        </CharString>
+        <CharString name="uni239D">
+          -151 50 124 vstem
+          400 30 rmoveto
+          -172 197 -54 285 0 254 rrcurveto
+          239 -124 -159 vlineto
+          0 -303 89 -359 261 -184 rrcurveto
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+  <MATH>
+    <Version value="0x00010000"/>
+    <MathConstants>
+      <ScriptPercentScaleDown value="75"/>
+      <ScriptScriptPercentScaleDown value="60"/>
+      <DelimitedSubFormulaMinHeight value="1500"/>
+      <DisplayOperatorMinHeight value="1450"/>
+      <MathLeading>
+        <Value value="150"/>
+      </MathLeading>
+      <AxisHeight>
+        <Value value="250"/>
+      </AxisHeight>
+      <AccentBaseHeight>
+        <Value value="450"/>
+      </AccentBaseHeight>
+      <FlattenedAccentBaseHeight>
+        <Value value="662"/>
+      </FlattenedAccentBaseHeight>
+      <SubscriptShiftDown>
+        <Value value="250"/>
+      </SubscriptShiftDown>
+      <SubscriptTopMax>
+        <Value value="400"/>
+      </SubscriptTopMax>
+      <SubscriptBaselineDropMin>
+        <Value value="50"/>
+      </SubscriptBaselineDropMin>
+      <SuperscriptShiftUp>
+        <Value value="400"/>
+      </SuperscriptShiftUp>
+      <SuperscriptShiftUpCramped>
+        <Value value="275"/>
+      </SuperscriptShiftUpCramped>
+      <SuperscriptBottomMin>
+        <Value value="125"/>
+      </SuperscriptBottomMin>
+      <SuperscriptBaselineDropMax>
+        <Value value="375"/>
+      </SuperscriptBaselineDropMax>
+      <SubSuperscriptGapMin>
+        <Value value="264"/>
+      </SubSuperscriptGapMin>
+      <SuperscriptBottomMaxWithSubscript>
+        <Value value="400"/>
+      </SuperscriptBottomMaxWithSubscript>
+      <SpaceAfterScript>
+        <Value value="41"/>
+      </SpaceAfterScript>
+      <UpperLimitGapMin>
+        <Value value="150"/>
+      </UpperLimitGapMin>
+      <UpperLimitBaselineRiseMin>
+        <Value value="300"/>
+      </UpperLimitBaselineRiseMin>
+      <LowerLimitGapMin>
+        <Value value="150"/>
+      </LowerLimitGapMin>
+      <LowerLimitBaselineDropMin>
+        <Value value="600"/>
+      </LowerLimitBaselineDropMin>
+      <StackTopShiftUp>
+        <Value value="480"/>
+      </StackTopShiftUp>
+      <StackTopDisplayStyleShiftUp>
+        <Value value="580"/>
+      </StackTopDisplayStyleShiftUp>
+      <StackBottomShiftDown>
+        <Value value="800"/>
+      </StackBottomShiftDown>
+      <StackBottomDisplayStyleShiftDown>
+        <Value value="900"/>
+      </StackBottomDisplayStyleShiftDown>
+      <StackGapMin>
+        <Value value="198"/>
+      </StackGapMin>
+      <StackDisplayStyleGapMin>
+        <Value value="462"/>
+      </StackDisplayStyleGapMin>
+      <StretchStackTopShiftUp>
+        <Value value="300"/>
+      </StretchStackTopShiftUp>
+      <StretchStackBottomShiftDown>
+        <Value value="600"/>
+      </StretchStackBottomShiftDown>
+      <StretchStackGapAboveMin>
+        <Value value="150"/>
+      </StretchStackGapAboveMin>
+      <StretchStackGapBelowMin>
+        <Value value="150"/>
+      </StretchStackGapBelowMin>
+      <FractionNumeratorShiftUp>
+        <Value value="480"/>
+      </FractionNumeratorShiftUp>
+      <FractionNumeratorDisplayStyleShiftUp>
+        <Value value="580"/>
+      </FractionNumeratorDisplayStyleShiftUp>
+      <FractionDenominatorShiftDown>
+        <Value value="480"/>
+      </FractionDenominatorShiftDown>
+      <FractionDenominatorDisplayStyleShiftDown>
+        <Value value="700"/>
+      </FractionDenominatorDisplayStyleShiftDown>
+      <FractionNumeratorGapMin>
+        <Value value="66"/>
+      </FractionNumeratorGapMin>
+      <FractionNumDisplayStyleGapMin>
+        <Value value="198"/>
+      </FractionNumDisplayStyleGapMin>
+      <FractionRuleThickness>
+        <Value value="66"/>
+      </FractionRuleThickness>
+      <FractionDenominatorGapMin>
+        <Value value="66"/>
+      </FractionDenominatorGapMin>
+      <FractionDenomDisplayStyleGapMin>
+        <Value value="198"/>
+      </FractionDenomDisplayStyleGapMin>
+      <SkewedFractionHorizontalGap>
+        <Value value="300"/>
+      </SkewedFractionHorizontalGap>
+      <SkewedFractionVerticalGap>
+        <Value value="66"/>
+      </SkewedFractionVerticalGap>
+      <OverbarVerticalGap>
+        <Value value="198"/>
+      </OverbarVerticalGap>
+      <OverbarRuleThickness>
+        <Value value="66"/>
+      </OverbarRuleThickness>
+      <OverbarExtraAscender>
+        <Value value="66"/>
+      </OverbarExtraAscender>
+      <UnderbarVerticalGap>
+        <Value value="198"/>
+      </UnderbarVerticalGap>
+      <UnderbarRuleThickness>
+        <Value value="66"/>
+      </UnderbarRuleThickness>
+      <UnderbarExtraDescender>
+        <Value value="66"/>
+      </UnderbarExtraDescender>
+      <RadicalVerticalGap>
+        <Value value="82"/>
+      </RadicalVerticalGap>
+      <RadicalDisplayStyleVerticalGap>
+        <Value value="186"/>
+      </RadicalDisplayStyleVerticalGap>
+      <RadicalRuleThickness>
+        <Value value="66"/>
+      </RadicalRuleThickness>
+      <RadicalExtraAscender>
+        <Value value="66"/>
+      </RadicalExtraAscender>
+      <RadicalKernBeforeDegree>
+        <Value value="277"/>
+      </RadicalKernBeforeDegree>
+      <RadicalKernAfterDegree>
+        <Value value="-555"/>
+      </RadicalKernAfterDegree>
+      <RadicalDegreeBottomRaisePercent value="70"/>
+    </MathConstants>
+    <MathGlyphInfo>
+      <MathItalicsCorrectionInfo>
+        <Coverage Format="1">
+          <Glyph value="u1D435"/>
+        </Coverage>
+        <!-- ItalicsCorrectionCount=1 -->
+        <ItalicsCorrection index="0">
+          <Value value="40"/>
+        </ItalicsCorrection>
+      </MathItalicsCorrectionInfo>
+      <MathTopAccentAttachment>
+        <TopAccentCoverage Format="1">
+          <Glyph value="A"/>
+          <Glyph value="uni0302"/>
+          <Glyph value="u1D400"/>
+          <Glyph value="u1D435"/>
+          <Glyph value="uni0302.size1"/>
+          <Glyph value="uni0302.size2"/>
+          <Glyph value="uni0302.size3"/>
+          <Glyph value="uni0302.size4"/>
+          <Glyph value="uni0302.size5"/>
+        </TopAccentCoverage>
+        <!-- TopAccentAttachmentCount=9 -->
+        <TopAccentAttachment index="0">
+          <Value value="361"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="1">
+          <Value value="-230"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="2">
+          <Value value="345"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="3">
+          <Value value="476"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="4">
+          <Value value="280"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="5">
+          <Value value="489"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="6">
+          <Value value="730"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="7">
+          <Value value="943"/>
+        </TopAccentAttachment>
+        <TopAccentAttachment index="8">
+          <Value value="1164"/>
+        </TopAccentAttachment>
+      </MathTopAccentAttachment>
+      <ExtendedShapeCoverage Format="1">
+        <Glyph value="parenleft.size1"/>
+        <Glyph value="parenleft.size2"/>
+        <Glyph value="parenleft.size3"/>
+        <Glyph value="parenleft.size4"/>
+      </ExtendedShapeCoverage>
+      <MathKernInfo>
+        <MathKernCoverage Format="1">
+          <Glyph value="A"/>
+          <Glyph value="u1D400"/>
+        </MathKernCoverage>
+        <!-- MathKernCount=2 -->
+        <MathKernInfoRecords index="0">
+          <TopRightMathKern>
+            <!-- HeightCount=1 -->
+            <CorrectionHeight index="0">
+              <Value value="275"/>
+            </CorrectionHeight>
+            <KernValue index="0">
+              <Value value="-100"/>
+            </KernValue>
+            <KernValue index="1">
+              <Value value="-150"/>
+            </KernValue>
+          </TopRightMathKern>
+          <BottomRightMathKern>
+            <!-- HeightCount=0 -->
+            <KernValue index="0">
+              <Value value="50"/>
+            </KernValue>
+          </BottomRightMathKern>
+        </MathKernInfoRecords>
+        <MathKernInfoRecords index="1">
+          <TopRightMathKern>
+            <!-- HeightCount=1 -->
+            <CorrectionHeight index="0">
+              <Value value="275"/>
+            </CorrectionHeight>
+            <KernValue index="0">
+              <Value value="-100"/>
+            </KernValue>
+            <KernValue index="1">
+              <Value value="-150"/>
+            </KernValue>
+          </TopRightMathKern>
+          <BottomRightMathKern>
+            <!-- HeightCount=0 -->
+            <KernValue index="0">
+              <Value value="30"/>
+            </KernValue>
+          </BottomRightMathKern>
+        </MathKernInfoRecords>
+      </MathKernInfo>
+    </MathGlyphInfo>
+    <MathVariants>
+      <MinConnectorOverlap value="50"/>
+      <VertGlyphCoverage Format="1">
+        <Glyph value="parenleft"/>
+      </VertGlyphCoverage>
+      <HorizGlyphCoverage Format="1">
+        <Glyph value="uni0302"/>
+      </HorizGlyphCoverage>
+      <!-- VertGlyphCount=1 -->
+      <!-- HorizGlyphCount=1 -->
+      <VertGlyphConstruction index="0">
+        <GlyphAssembly>
+          <ItalicsCorrection>
+            <Value value="0"/>
+          </ItalicsCorrection>
+          <!-- PartCount=3 -->
+          <PartRecords index="0">
+            <glyph value="uni239D"/>
+            <StartConnectorLength value="0"/>
+            <EndConnectorLength value="150"/>
+            <FullAdvance value="1005"/>
+            <PartFlags value="0"/>
+          </PartRecords>
+          <PartRecords index="1">
+            <glyph value="uni239C"/>
+            <StartConnectorLength value="150"/>
+            <EndConnectorLength value="150"/>
+            <FullAdvance value="1010"/>
+            <PartFlags value="1"/>
+          </PartRecords>
+          <PartRecords index="2">
+            <glyph value="uni239B"/>
+            <StartConnectorLength value="150"/>
+            <EndConnectorLength value="0"/>
+            <FullAdvance value="1005"/>
+            <PartFlags value="0"/>
+          </PartRecords>
+        </GlyphAssembly>
+        <!-- VariantCount=5 -->
+        <MathGlyphVariantRecord index="0">
+          <VariantGlyph value="parenleft"/>
+          <AdvanceMeasurement value="854"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="1">
+          <VariantGlyph value="parenleft.size1"/>
+          <AdvanceMeasurement value="1231"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="2">
+          <VariantGlyph value="parenleft.size2"/>
+          <AdvanceMeasurement value="1846"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="3">
+          <VariantGlyph value="parenleft.size3"/>
+          <AdvanceMeasurement value="2461"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="4">
+          <VariantGlyph value="parenleft.size4"/>
+          <AdvanceMeasurement value="3076"/>
+        </MathGlyphVariantRecord>
+      </VertGlyphConstruction>
+      <HorizGlyphConstruction index="0">
+        <!-- VariantCount=6 -->
+        <MathGlyphVariantRecord index="0">
+          <VariantGlyph value="uni0302"/>
+          <AdvanceMeasurement value="312"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="1">
+          <VariantGlyph value="uni0302.size1"/>
+          <AdvanceMeasurement value="561"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="2">
+          <VariantGlyph value="uni0302.size2"/>
+          <AdvanceMeasurement value="980"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="3">
+          <VariantGlyph value="uni0302.size3"/>
+          <AdvanceMeasurement value="1461"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="4">
+          <VariantGlyph value="uni0302.size4"/>
+          <AdvanceMeasurement value="1887"/>
+        </MathGlyphVariantRecord>
+        <MathGlyphVariantRecord index="5">
+          <VariantGlyph value="uni0302.size5"/>
+          <AdvanceMeasurement value="2329"/>
+        </MathGlyphVariantRecord>
+      </HorizGlyphConstruction>
+    </MathVariants>
+  </MATH>
+
+  <hmtx>
+    <mtx name=".notdef" width="250" lsb="0"/>
+    <mtx name="A" width="722" lsb="15"/>
+    <mtx name="parenleft" width="333" lsb="48"/>
+    <mtx name="parenleft.size1" width="468" lsb="139"/>
+    <mtx name="parenleft.size2" width="589" lsb="139"/>
+    <mtx name="parenleft.size3" width="750" lsb="182"/>
+    <mtx name="parenleft.size4" width="808" lsb="124"/>
+    <mtx name="u1D400" width="722" lsb="9"/>
+    <mtx name="u1D435" width="696" lsb="38"/>
+    <mtx name="uni0302" width="0" lsb="-386"/>
+    <mtx name="uni0302.size1" width="560" lsb="0"/>
+    <mtx name="uni0302.size2" width="979" lsb="0"/>
+    <mtx name="uni0302.size3" width="1460" lsb="0"/>
+    <mtx name="uni0302.size4" width="1886" lsb="0"/>
+    <mtx name="uni0302.size5" width="2328" lsb="0"/>
+    <mtx name="uni239B" width="450" lsb="50"/>
+    <mtx name="uni239C" width="450" lsb="50"/>
+    <mtx name="uni239D" width="450" lsb="50"/>
+  </hmtx>
+
+</ttFont>
diff --git a/Tests/subset/data/expect_lcar_0.ttx b/Tests/subset/data/expect_lcar_0.ttx
new file mode 100644
index 0000000..feb866d
--- /dev/null
+++ b/Tests/subset/data/expect_lcar_0.ttx
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <lcar>
+    <Version value="0x00010000"/>
+    <LigatureCarets Format="0">
+      <Carets>
+        <Lookup glyph="uniFB01">
+          <!-- DivsionPointCount=1 -->
+          <DivisionPoint index="0" value="238"/>
+        </Lookup>
+      </Carets>
+    </LigatureCarets>
+  </lcar>
+  
+</ttFont>
diff --git a/Tests/subset/data/expect_lcar_1.ttx b/Tests/subset/data/expect_lcar_1.ttx
new file mode 100644
index 0000000..26b5008
--- /dev/null
+++ b/Tests/subset/data/expect_lcar_1.ttx
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <lcar>
+    <Version value="0x00010000"/>
+    <LigatureCarets Format="1">
+      <Carets>
+        <Lookup glyph="uniFB01">
+          <!-- DivsionPointCount=1 -->
+          <DivisionPoint index="0" value="7"/>
+        </Lookup>
+      </Carets>
+    </LigatureCarets>
+  </lcar>
+  
+</ttFont>
diff --git a/Tests/subset/data/expect_no_hinting_CFF.ttx b/Tests/subset/data/expect_no_hinting_CFF.ttx
new file mode 100644
index 0000000..c2d1872
--- /dev/null
+++ b/Tests/subset/data/expect_no_hinting_CFF.ttx
@@ -0,0 +1,212 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <CFF>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="Lobster1.4">
+      <version value="001.001"/>
+      <Notice value="Copyright (c) 2010 by Pablo Impallari. www.impallari.com. All rights reserved."/>
+      <Copyright value="Copyright (c) 2010 by Pablo Impallari. All rights reserved."/>
+      <FullName value="Lobster 1.4"/>
+      <FamilyName value="Lobster 1.4"/>
+      <Weight value="Regular"/>
+      <isFixedPitch value="0"/>
+      <ItalicAngle value="0"/>
+      <UnderlinePosition value="-100"/>
+      <UnderlineThickness value="50"/>
+      <PaintType value="0"/>
+      <CharstringType value="2"/>
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FontBBox value="-209 -250 1186 1000"/>
+      <StrokeWidth value="0"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <Encoding name="StandardEncoding"/>
+      <Private>
+        <BlueScale value="0.039625"/>
+        <BlueShift value="7"/>
+        <BlueFuzz value="1"/>
+        <ForceBold value="0"/>
+        <LanguageGroup value="0"/>
+        <ExpansionFactor value="0.06"/>
+        <initialRandomSeed value="0"/>
+        <defaultWidthX value="267"/>
+        <nominalWidthX value="448"/>
+        <Subrs>
+          <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+          <CharString index="0">
+            397 748 rmoveto
+            1 -13 -13 1 -14 hhcurveto
+            -106 callsubr
+            53 75 87 36 vhcurveto
+            -145 -679 rlineto
+            return
+          </CharString>
+          <CharString index="1">
+            -167 -184 -127 -133 -72 38 -25 69 hvcurveto
+            -1 9 -13 8 51 vvcurveto
+            107 return
+          </CharString>
+          <CharString index="2">
+            230 636 rmoveto
+            -136 -636 rlineto
+            144 hlineto
+            return
+          </CharString>
+          <CharString index="3">
+            -67 41 -25 66 vhcurveto
+            -1 9 -13 8 51 vvcurveto
+            return
+          </CharString>
+        </Subrs>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          -63 endchar
+        </CharString>
+        <CharString name="A">
+          220 535 hmoveto
+          157 736 rlineto
+          10 -24 -32 4 -23 hhcurveto
+          -117 -130 -135 -160 -101 hvcurveto
+          2 -21 -17 1 -14 hhcurveto
+          -118 -86 -55 -68 -39 28 -19 34 31 25 15 24 14 -8 17 -5 hvcurveto
+          13 34 42 14 62 4 rrcurveto
+          -87 -153 -60 -164 -90 vvcurveto
+          -104 80 -2 54 vhcurveto
+          -6 9 -8 15 32 vvcurveto
+          104 55 190 75 163 vhcurveto
+          44 -4 39 -9 51 -23 -77 -363 rcurveline
+          86 407 rmoveto
+          -39 16 -43 11 -40 8 56 112 64 93 60 32 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="A.salt">
+          142 459 hmoveto
+          157 736 rlineto
+          12 -30 -26 3 -24 hhcurveto
+          -238 -290 -563 -189 -106 65 -2 69 -4 hvcurveto
+          -1 9 -13 -4 51 vvcurveto
+          97 42 172 64 154 vhcurveto
+          158 hlineto
+          -77 -366 rlineto
+          -59 418 rmoveto
+          58 126 72 106 73 32 -56 -264 rcurveline
+          endchar
+        </CharString>
+        <CharString name="B">
+          187 -105 callsubr
+          82 383 rlineto
+          2 18 20 1 8 hhcurveto
+          73 22 -57 -70 hvcurveto
+          -76 -26 -104 -73 -23 -19 10 26 -25 vhcurveto
+          -9 -23 -4 -19 -16 vvcurveto
+          -61 56 -13 43 167 52 192 96 75 -33 69 -85 17 vhcurveto
+          65 37 35 63 59 vvcurveto
+          82 -66 77 -147 -189 -174 -127 -138 -104 callsubr
+          165 133 78 117 95 37 -51 -57 -75 -64 -87 -80 vhcurveto
+          -6 hlineto
+          47 222 rlineto
+          endchar
+        </CharString>
+        <CharString name="B.salt">
+          185 -105 callsubr
+          6 30 rlineto
+          -41 39 41 -17 39 hhcurveto
+          125 110 175 136 72 -32 62 -82 15 hvcurveto
+          64 38 36 61 58 vvcurveto
+          83 -74 78 -144 -183 -177 -126 -139 -104 callsubr
+          152 116 91 138 101 25 -49 -53 -81 -59 -87 -83 vhcurveto
+          -6 hlineto
+          47 222 rlineto
+          -59 -592 rmoveto
+          -20 -21 8 21 -20 hvcurveto
+          62 290 rlineto
+          2 18 20 1 7 hhcurveto
+          63 21 -49 -57 -96 -58 -120 -72 hvcurveto
+          endchar
+        </CharString>
+        <CharString name="I">
+          -73 -107 callsubr
+          144 hlineto
+          endchar
+        </CharString>
+        <CharString name="IJ">
+          215 -107 callsubr
+          34 hlineto
+          -11 -20 -5 -23 -27 vvcurveto
+          -79 48 -58 113 155 66 109 138 29 vhcurveto
+          150 710 -150 -33 -164 -751 rlineto
+          -100 -22 -30 -23 -40 hhcurveto
+          -44 -27 29 39 40 29 33 36 16 17 -7 -16 16 hvcurveto
+          4 11 3 11 11 vvcurveto
+          34 -26 24 -41 6 vhcurveto
+          endchar
+        </CharString>
+        <CharString name="J">
+          88 538 750 rmoveto
+          -106 callsubr
+          54 76 87 36 vhcurveto
+          -157 -714 rlineto
+          -103 -23 -27 -20 -45 hhcurveto
+          -29 -39 18 52 37 24 37 46 20 15 -5 -21 25 hvcurveto
+          4 15 2 14 11 vvcurveto
+          64 -58 3 -40 -79 -43 -66 -68 -83 53 -58 95 164 67 94 153 32 vhcurveto
+          150 710 rlineto
+          endchar
+        </CharString>
+        <CharString name="one">
+          -131 324 748 rmoveto
+          -72 -121 -78 -6 -55 hhcurveto
+          -12 -46 rlineto
+          95 hlineto
+          -132 -624 rlineto
+          144 hlineto
+          endchar
+        </CharString>
+        <CharString name="three">
+          66 205 257 rmoveto
+          38 -8 -33 13 -37 hhcurveto
+          -80 -41 -60 -83 -154 141 -16 58 171 111 136 121 71 -38 65 -88 29 hvcurveto
+          92 46 45 74 66 vvcurveto
+          78 -63 68 -123 vhcurveto
+          -116 -91 -61 -91 -54 32 -31 40 24 27 11 23 25 hvcurveto
+          -28 8 -10 36 27 vvcurveto
+          47 31 31 48 51 25 -36 -46 -70 -58 -94 -113 -31 vhcurveto
+          93 -33 40 -80 -76 vvcurveto
+          -87 -53 -82 -86 -37 -39 13 76 40 10 62 78 6 vhcurveto
+          endchar
+        </CharString>
+        <CharString name="two">
+          44 111 132 rmoveto
+          -5 hlineto
+          83 135 273 98 223 vvcurveto
+          97 -53 64 -137 -151 -55 -79 -68 -58 31 -32 41 24 26 11 23 26 vhcurveto
+          -28 8 -10 37 23 vvcurveto
+          50 14 31 67 29 32 -33 -49 vhcurveto
+          -266 -329 -98 -219 vvcurveto
+          -11 0 -11 2 -11 vhcurveto
+          7 20 36 21 23 hhcurveto
+          102 37 -36 109 hhcurveto
+          99 20 52 98 14 0 14 -1 16 hvcurveto
+          -44 -47 -17 -25 -70 hhcurveto
+          -75 -57 18 -59 hhcurveto
+          endchar
+        </CharString>
+        <CharString name="zero">
+          98 377 750 rmoveto
+          -215 -132 -223 -273 -166 35 -97 172 205 113 299 199 168 -53 93 -125 hvcurveto
+          -189 -425 rmoveto
+          225 17 105 148 60 hhcurveto
+          47 7 -63 -82 -232 -68 -246 -114 -48 -11 77 74 37 3 35 2 27 hvcurveto
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+</ttFont>
diff --git a/Tests/subset/data/expect_no_hinting_TTF.ttx b/Tests/subset/data/expect_no_hinting_TTF.ttx
new file mode 100644
index 0000000..4c2e55b
--- /dev/null
+++ b/Tests/subset/data/expect_no_hinting_TTF.ttx
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="10" yMin="0" xMax="486" yMax="660">
+      <contour>
+        <pt x="10" y="660" on="1"/>
+        <pt x="486" y="660" on="1"/>
+        <pt x="486" y="0" on="1"/>
+        <pt x="10" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="375" y="58" on="1"/>
+        <pt x="302" y="190" on="1"/>
+        <pt x="250" y="293" on="1"/>
+        <pt x="246" y="293" on="1"/>
+        <pt x="192" y="190" on="1"/>
+        <pt x="118" y="58" on="1"/>
+      </contour>
+      <contour>
+        <pt x="250" y="387" on="1"/>
+        <pt x="299" y="481" on="1"/>
+        <pt x="365" y="600" on="1"/>
+        <pt x="130" y="600" on="1"/>
+        <pt x="196" y="481" on="1"/>
+        <pt x="246" y="387" on="1"/>
+      </contour>
+      <contour>
+        <pt x="202" y="340" on="1"/>
+        <pt x="75" y="572" on="1"/>
+        <pt x="75" y="110" on="1"/>
+      </contour>
+      <contour>
+        <pt x="419" y="572" on="1"/>
+        <pt x="293" y="340" on="1"/>
+        <pt x="419" y="110" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="132" yMin="304" xMax="365" yMax="567">
+      <contour>
+        <pt x="132" y="567" on="1"/>
+        <pt x="365" y="567" on="1"/>
+        <pt x="365" y="304" on="1"/>
+        <pt x="132" y="304" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="B" xMin="132" yMin="304" xMax="365" yMax="567">
+      <contour>
+        <pt x="132" y="567" on="1"/>
+        <pt x="365" y="567" on="1"/>
+        <pt x="365" y="304" on="1"/>
+        <pt x="132" y="304" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="C"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="4"/>
+    <maxPoints value="22"/>
+    <maxContours value="5"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+</ttFont>
diff --git a/Tests/subset/data/expect_no_hinting_desubroutinize_CFF.ttx b/Tests/subset/data/expect_no_hinting_desubroutinize_CFF.ttx
new file mode 100644
index 0000000..7f44c90
--- /dev/null
+++ b/Tests/subset/data/expect_no_hinting_desubroutinize_CFF.ttx
@@ -0,0 +1,202 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <CFF>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="Lobster1.4">
+      <version value="001.001"/>
+      <Notice value="Copyright (c) 2010 by Pablo Impallari. www.impallari.com. All rights reserved."/>
+      <Copyright value="Copyright (c) 2010 by Pablo Impallari. All rights reserved."/>
+      <FullName value="Lobster 1.4"/>
+      <FamilyName value="Lobster 1.4"/>
+      <Weight value="Regular"/>
+      <isFixedPitch value="0"/>
+      <ItalicAngle value="0"/>
+      <UnderlinePosition value="-100"/>
+      <UnderlineThickness value="50"/>
+      <PaintType value="0"/>
+      <CharstringType value="2"/>
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FontBBox value="-209 -250 1186 1000"/>
+      <StrokeWidth value="0"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <Encoding name="StandardEncoding"/>
+      <Private>
+        <BlueScale value="0.039625"/>
+        <BlueShift value="7"/>
+        <BlueFuzz value="1"/>
+        <ForceBold value="0"/>
+        <LanguageGroup value="0"/>
+        <ExpansionFactor value="0.06"/>
+        <initialRandomSeed value="0"/>
+        <defaultWidthX value="267"/>
+        <nominalWidthX value="448"/>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          -63 endchar
+        </CharString>
+        <CharString name="A">
+          220 535 hmoveto
+          157 736 rlineto
+          10 -24 -32 4 -23 hhcurveto
+          -117 -130 -135 -160 -101 hvcurveto
+          2 -21 -17 1 -14 hhcurveto
+          -118 -86 -55 -68 -39 28 -19 34 31 25 15 24 14 -8 17 -5 hvcurveto
+          13 34 42 14 62 4 rrcurveto
+          -87 -153 -60 -164 -90 vvcurveto
+          -104 80 -2 54 vhcurveto
+          -6 9 -8 15 32 vvcurveto
+          104 55 190 75 163 vhcurveto
+          44 -4 39 -9 51 -23 -77 -363 rcurveline
+          86 407 rmoveto
+          -39 16 -43 11 -40 8 56 112 64 93 60 32 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="A.salt">
+          142 459 hmoveto
+          157 736 rlineto
+          12 -30 -26 3 -24 hhcurveto
+          -238 -290 -563 -189 -106 65 -2 69 -4 hvcurveto
+          -1 9 -13 -4 51 vvcurveto
+          97 42 172 64 154 vhcurveto
+          158 hlineto
+          -77 -366 rlineto
+          -59 418 rmoveto
+          58 126 72 106 73 32 -56 -264 rcurveline
+          endchar
+        </CharString>
+        <CharString name="B">
+          187 230 636 rmoveto
+          -136 -636 rlineto
+          144 hlineto
+          82 383 rlineto
+          2 18 20 1 8 hhcurveto
+          73 22 -57 -70 hvcurveto
+          -76 -26 -104 -73 -23 -19 10 26 -25 vhcurveto
+          -9 -23 -4 -19 -16 vvcurveto
+          -61 56 -13 43 167 52 192 96 75 -33 69 -85 17 vhcurveto
+          65 37 35 63 59 vvcurveto
+          82 -66 77 -147 -189 -174 -127 -138 -67 41 -25 66 vhcurveto
+          -1 9 -13 8 51 vvcurveto
+          165 133 78 117 95 37 -51 -57 -75 -64 -87 -80 vhcurveto
+          -6 hlineto
+          47 222 rlineto
+          endchar
+        </CharString>
+        <CharString name="B.salt">
+          185 230 636 rmoveto
+          -136 -636 rlineto
+          144 hlineto
+          6 30 rlineto
+          -41 39 41 -17 39 hhcurveto
+          125 110 175 136 72 -32 62 -82 15 hvcurveto
+          64 38 36 61 58 vvcurveto
+          83 -74 78 -144 -183 -177 -126 -139 -67 41 -25 66 vhcurveto
+          -1 9 -13 8 51 vvcurveto
+          152 116 91 138 101 25 -49 -53 -81 -59 -87 -83 vhcurveto
+          -6 hlineto
+          47 222 rlineto
+          -59 -592 rmoveto
+          -20 -21 8 21 -20 hvcurveto
+          62 290 rlineto
+          2 18 20 1 7 hhcurveto
+          63 21 -49 -57 -96 -58 -120 -72 hvcurveto
+          endchar
+        </CharString>
+        <CharString name="I">
+          -73 397 748 rmoveto
+          1 -13 -13 1 -14 hhcurveto
+          -167 -184 -127 -133 -72 38 -25 69 hvcurveto
+          -1 9 -13 8 51 vvcurveto
+          107 53 75 87 36 vhcurveto
+          -145 -679 rlineto
+          144 hlineto
+          endchar
+        </CharString>
+        <CharString name="IJ">
+          215 397 748 rmoveto
+          1 -13 -13 1 -14 hhcurveto
+          -167 -184 -127 -133 -72 38 -25 69 hvcurveto
+          -1 9 -13 8 51 vvcurveto
+          107 53 75 87 36 vhcurveto
+          -145 -679 rlineto
+          34 hlineto
+          -11 -20 -5 -23 -27 vvcurveto
+          -79 48 -58 113 155 66 109 138 29 vhcurveto
+          150 710 -150 -33 -164 -751 rlineto
+          -100 -22 -30 -23 -40 hhcurveto
+          -44 -27 29 39 40 29 33 36 16 17 -7 -16 16 hvcurveto
+          4 11 3 11 11 vvcurveto
+          34 -26 24 -41 6 vhcurveto
+          endchar
+        </CharString>
+        <CharString name="J">
+          88 538 750 rmoveto
+          -167 -184 -127 -133 -72 38 -25 69 hvcurveto
+          -1 9 -13 8 51 vvcurveto
+          107 54 76 87 36 vhcurveto
+          -157 -714 rlineto
+          -103 -23 -27 -20 -45 hhcurveto
+          -29 -39 18 52 37 24 37 46 20 15 -5 -21 25 hvcurveto
+          4 15 2 14 11 vvcurveto
+          64 -58 3 -40 -79 -43 -66 -68 -83 53 -58 95 164 67 94 153 32 vhcurveto
+          150 710 rlineto
+          endchar
+        </CharString>
+        <CharString name="one">
+          -131 324 748 rmoveto
+          -72 -121 -78 -6 -55 hhcurveto
+          -12 -46 rlineto
+          95 hlineto
+          -132 -624 rlineto
+          144 hlineto
+          endchar
+        </CharString>
+        <CharString name="three">
+          66 205 257 rmoveto
+          38 -8 -33 13 -37 hhcurveto
+          -80 -41 -60 -83 -154 141 -16 58 171 111 136 121 71 -38 65 -88 29 hvcurveto
+          92 46 45 74 66 vvcurveto
+          78 -63 68 -123 vhcurveto
+          -116 -91 -61 -91 -54 32 -31 40 24 27 11 23 25 hvcurveto
+          -28 8 -10 36 27 vvcurveto
+          47 31 31 48 51 25 -36 -46 -70 -58 -94 -113 -31 vhcurveto
+          93 -33 40 -80 -76 vvcurveto
+          -87 -53 -82 -86 -37 -39 13 76 40 10 62 78 6 vhcurveto
+          endchar
+        </CharString>
+        <CharString name="two">
+          44 111 132 rmoveto
+          -5 hlineto
+          83 135 273 98 223 vvcurveto
+          97 -53 64 -137 -151 -55 -79 -68 -58 31 -32 41 24 26 11 23 26 vhcurveto
+          -28 8 -10 37 23 vvcurveto
+          50 14 31 67 29 32 -33 -49 vhcurveto
+          -266 -329 -98 -219 vvcurveto
+          -11 0 -11 2 -11 vhcurveto
+          7 20 36 21 23 hhcurveto
+          102 37 -36 109 hhcurveto
+          99 20 52 98 14 0 14 -1 16 hvcurveto
+          -44 -47 -17 -25 -70 hhcurveto
+          -75 -57 18 -59 hhcurveto
+          endchar
+        </CharString>
+        <CharString name="zero">
+          98 377 750 rmoveto
+          -215 -132 -223 -273 -166 35 -97 172 205 113 299 199 168 -53 93 -125 hvcurveto
+          -189 -425 rmoveto
+          225 17 105 148 60 hhcurveto
+          47 7 -63 -82 -232 -68 -246 -114 -48 -11 77 74 37 3 35 2 27 hvcurveto
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+</ttFont>
diff --git a/Tests/subset/data/expect_no_notdef_outline_cid.ttx b/Tests/subset/data/expect_no_notdef_outline_cid.ttx
new file mode 100644
index 0000000..5167c2c
--- /dev/null
+++ b/Tests/subset/data/expect_no_notdef_outline_cid.ttx
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.0">
+
+  <CFF>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="TestCID-Regular">
+      <ROS Registry="Adobe" Order="Identity" Supplement="0"/>
+      <FullName value="Test CID Regular"/>
+      <FamilyName value="Test CID"/>
+      <Weight value="Regular"/>
+      <isFixedPitch value="0"/>
+      <ItalicAngle value="0"/>
+      <UnderlinePosition value="-150"/>
+      <UnderlineThickness value="50"/>
+      <PaintType value="0"/>
+      <CharstringType value="2"/>
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FontBBox value="-1000 -1000 1000 1000"/>
+      <StrokeWidth value="0"/>
+      <CIDFontVersion value="1.0"/>
+      <CIDFontRevision value="0"/>
+      <CIDFontType value="0"/>
+      <CIDCount value="4"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <FDSelect format="3"/>
+      <FDArray>
+        <FontDict index="0">
+          <FontName value="TestCID-Regular-One"/>
+          <FontMatrix value="0.001 0 0 0.001 0 0"/>
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="1"/>
+            <StdHW value="40"/>
+            <StdVW value="40"/>
+            <StemSnapH value="40 120"/>
+            <StemSnapV value="40 120"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="107"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef" fdSelectIndex="0">
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+</ttFont>
diff --git a/Tests/subset/data/expect_no_notdef_outline_otf.ttx b/Tests/subset/data/expect_no_notdef_outline_otf.ttx
new file mode 100644
index 0000000..22273c7
--- /dev/null
+++ b/Tests/subset/data/expect_no_notdef_outline_otf.ttx
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.0">
+
+  <CFF>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="TestOTF-Regular">
+      <version value="1.0"/>
+      <FamilyName value="Test OTF"/>
+      <isFixedPitch value="0"/>
+      <ItalicAngle value="0"/>
+      <UnderlinePosition value="-100"/>
+      <UnderlineThickness value="50"/>
+      <PaintType value="0"/>
+      <CharstringType value="2"/>
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FontBBox value="0 0 486 660"/>
+      <StrokeWidth value="0"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <Encoding name="StandardEncoding"/>
+      <Private>
+        <BlueValues value="-10 0 500 510"/>
+        <BlueScale value="0.039625"/>
+        <BlueShift value="7"/>
+        <BlueFuzz value="1"/>
+        <StdHW value="20"/>
+        <StdVW value="20"/>
+        <StemSnapH value="20"/>
+        <StemSnapV value="20"/>
+        <ForceBold value="0"/>
+        <LanguageGroup value="0"/>
+        <ExpansionFactor value="0.06"/>
+        <initialRandomSeed value="0"/>
+        <defaultWidthX value="500"/>
+        <nominalWidthX value="300"/>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          196 endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+</ttFont>
diff --git a/Tests/subset/data/expect_no_notdef_outline_ttf.ttx b/Tests/subset/data/expect_no_notdef_outline_ttf.ttx
new file mode 100644
index 0000000..4096adb
--- /dev/null
+++ b/Tests/subset/data/expect_no_notdef_outline_ttf.ttx
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="2.5">
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <hmtx>
+    <mtx name=".notdef" width="496" lsb="10"/>
+  </hmtx>
+
+</ttFont>
diff --git a/Tests/subset/data/expect_notdef_width_cid.ttx b/Tests/subset/data/expect_notdef_width_cid.ttx
new file mode 100644
index 0000000..ccd0f65
--- /dev/null
+++ b/Tests/subset/data/expect_notdef_width_cid.ttx
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.7">
+
+  <CFF>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="NotdefWidthCID-Regular">
+      <ROS Registry="Adobe" Order="Identity" Supplement="0"/>
+      <FullName value="Notdef Width CID Regular"/>
+      <FamilyName value="Notdef Width CID"/>
+      <Weight value="Regular"/>
+      <isFixedPitch value="0"/>
+      <ItalicAngle value="0"/>
+      <UnderlinePosition value="-150"/>
+      <UnderlineThickness value="50"/>
+      <PaintType value="0"/>
+      <CharstringType value="2"/>
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FontBBox value="-1000 -1000 1000 1000"/>
+      <StrokeWidth value="0"/>
+      <CIDFontVersion value="1.0"/>
+      <CIDFontRevision value="0"/>
+      <CIDFontType value="0"/>
+      <CIDCount value="4"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <FDSelect format="3"/>
+      <FDArray>
+        <FontDict index="0">
+          <FontName value="NotdefWidthCID-Regular-Space"/>
+          <FontMatrix value="0.001 0 0 0.001 0 0"/>
+          <Private>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="0"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="602"/>
+            <nominalWidthX value="630"/>
+          </Private>
+        </FontDict>
+        <FontDict index="1">
+          <FontName value="NotdefWidthCID-Regular-Notdef"/>
+          <FontMatrix value="0.001 0 0 0.001 0 0"/>
+          <Private>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="0"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="107"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef" fdSelectIndex="1">
+          endchar
+        </CharString>
+        <CharString name="cid00001" fdSelectIndex="0">
+          -407 endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+</ttFont>
diff --git a/Tests/subset/data/expect_opbd_0.ttx b/Tests/subset/data/expect_opbd_0.ttx
new file mode 100644
index 0000000..55842a0
--- /dev/null
+++ b/Tests/subset/data/expect_opbd_0.ttx
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <opbd>
+    <Version value="0x00010000"/>
+    <OpticalBounds Format="0">
+      <OpticalBoundsDeltas>
+        <Lookup glyph="A">
+          <Left value="-99"/>
+          <Top value="0"/>
+          <Right value="55"/>
+          <Bottom value="0"/>
+        </Lookup>
+      </OpticalBoundsDeltas>
+    </OpticalBounds>
+  </opbd>
+  
+</ttFont>
diff --git a/Tests/subset/data/expect_opbd_1.ttx b/Tests/subset/data/expect_opbd_1.ttx
new file mode 100644
index 0000000..080abd9
--- /dev/null
+++ b/Tests/subset/data/expect_opbd_1.ttx
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <opbd>
+    <Version value="0x00010000"/>
+    <OpticalBounds Format="1">
+      <OpticalBoundsPoints>
+        <Lookup glyph="A">
+          <Left value="3"/>
+          <Top value="4"/>
+          <Right value="5"/>
+          <Bottom value="6"/>
+        </Lookup>
+      </OpticalBoundsPoints>
+    </OpticalBounds>
+  </opbd>
+  
+</ttFont>
diff --git a/Tests/subset/data/expect_prop_0.ttx b/Tests/subset/data/expect_prop_0.ttx
new file mode 100644
index 0000000..f8ca150
--- /dev/null
+++ b/Tests/subset/data/expect_prop_0.ttx
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <prop>
+    <Version value="3.0"/>
+    <GlyphProperties Format="0">
+      <DefaultProperties value="3"/>
+    </GlyphProperties>
+  </prop>
+  
+</ttFont>
diff --git a/Tests/subset/data/expect_prop_1.ttx b/Tests/subset/data/expect_prop_1.ttx
new file mode 100644
index 0000000..f7f2d23
--- /dev/null
+++ b/Tests/subset/data/expect_prop_1.ttx
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <prop>
+    <Version value="3.0"/>
+    <GlyphProperties Format="1">
+      <DefaultProperties value="3"/>
+      <Properties>
+        <Lookup glyph=".notdef" value="0"/>
+      </Properties>
+    </GlyphProperties>
+  </prop>
+  
+</ttFont>
diff --git a/Tests/subset/data/google_color.ttx b/Tests/subset/data/google_color.ttx
new file mode 100644
index 0000000..2eca0ac
--- /dev/null
+++ b/Tests/subset/data/google_color.ttx
@@ -0,0 +1,507 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="x"/>
+    <GlyphID id="2" name="y"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x75dc550a"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00001011"/>
+    <unitsPerEm value="2048"/>
+    <created value="Wed May 22 20:07:39 2013"/>
+    <modified value="Wed Dec  9 20:01:21 2015"/>
+    <xMin value="0"/>
+    <yMin value="-512"/>
+    <xMax value="2048"/>
+    <yMax value="1536"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="8"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1536"/>
+    <descent value="-512"/>
+    <lineGap value="256"/>
+    <advanceWidthMax value="2048"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="0"/>
+    <xMaxExtent value="1491"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="96"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="96"/>
+    <maxPoints value="8"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="2"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="1"/>
+    <maxFunctionDefs value="1"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="64"/>
+    <maxSizeOfInstructions value="46"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="2048"/>
+    <usWeightClass value="500"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="1331"/>
+    <ySubscriptYSize value="1433"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="286"/>
+    <ySuperscriptXSize value="1331"/>
+    <ySuperscriptYSize value="1433"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="983"/>
+    <yStrikeoutSize value="102"/>
+    <yStrikeoutPosition value="530"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="6"/>
+      <bProportion value="9"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="GOOG"/>
+    <fsSelection value="00000000 11000000"/>
+    <usFirstCharIndex value="0"/>
+    <usLastCharIndex value="126"/>
+    <sTypoAscender value="1536"/>
+    <sTypoDescender value="-512"/>
+    <sTypoLineGap value="184"/>
+    <usWinAscent value="1536"/>
+    <usWinDescent value="512"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="0"/>
+    <sCapHeight value="1322"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="2048" lsb="0"/>
+    <mtx name="x" width="1600" lsb="0"/>
+    <mtx name="y" width="1584" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x78" name="x"/>
+      <map code="0x79" name="y"/>
+    </cmap_format_4>
+    <cmap_format_0 platformID="1" platEncID="0" language="0">
+      <map code="0x0" name=".notdef"/>
+      <map code="0x1" name=".notdef"/>
+      <map code="0x2" name=".notdef"/>
+      <map code="0x3" name=".notdef"/>
+      <map code="0x4" name=".notdef"/>
+      <map code="0x5" name=".notdef"/>
+      <map code="0x6" name=".notdef"/>
+      <map code="0x7" name=".notdef"/>
+      <map code="0x8" name=".notdef"/>
+      <map code="0x9" name=".notdef"/>
+      <map code="0xa" name=".notdef"/>
+      <map code="0xb" name=".notdef"/>
+      <map code="0xc" name=".notdef"/>
+      <map code="0xd" name=".notdef"/>
+      <map code="0xe" name=".notdef"/>
+      <map code="0xf" name=".notdef"/>
+      <map code="0x10" name=".notdef"/>
+      <map code="0x11" name=".notdef"/>
+      <map code="0x12" name=".notdef"/>
+      <map code="0x13" name=".notdef"/>
+      <map code="0x14" name=".notdef"/>
+      <map code="0x15" name=".notdef"/>
+      <map code="0x16" name=".notdef"/>
+      <map code="0x17" name=".notdef"/>
+      <map code="0x18" name=".notdef"/>
+      <map code="0x19" name=".notdef"/>
+      <map code="0x1a" name=".notdef"/>
+      <map code="0x1b" name=".notdef"/>
+      <map code="0x1c" name=".notdef"/>
+      <map code="0x1d" name=".notdef"/>
+      <map code="0x1e" name=".notdef"/>
+      <map code="0x1f" name=".notdef"/>
+      <map code="0x20" name=".notdef"/>
+      <map code="0x21" name=".notdef"/>
+      <map code="0x22" name=".notdef"/>
+      <map code="0x23" name=".notdef"/>
+      <map code="0x24" name=".notdef"/>
+      <map code="0x25" name=".notdef"/>
+      <map code="0x26" name=".notdef"/>
+      <map code="0x27" name=".notdef"/>
+      <map code="0x28" name=".notdef"/>
+      <map code="0x29" name=".notdef"/>
+      <map code="0x2a" name=".notdef"/>
+      <map code="0x2b" name=".notdef"/>
+      <map code="0x2c" name=".notdef"/>
+      <map code="0x2d" name=".notdef"/>
+      <map code="0x2e" name=".notdef"/>
+      <map code="0x2f" name=".notdef"/>
+      <map code="0x30" name=".notdef"/>
+      <map code="0x31" name=".notdef"/>
+      <map code="0x32" name=".notdef"/>
+      <map code="0x33" name=".notdef"/>
+      <map code="0x34" name=".notdef"/>
+      <map code="0x35" name=".notdef"/>
+      <map code="0x36" name=".notdef"/>
+      <map code="0x37" name=".notdef"/>
+      <map code="0x38" name=".notdef"/>
+      <map code="0x39" name=".notdef"/>
+      <map code="0x3a" name=".notdef"/>
+      <map code="0x3b" name=".notdef"/>
+      <map code="0x3c" name=".notdef"/>
+      <map code="0x3d" name=".notdef"/>
+      <map code="0x3e" name=".notdef"/>
+      <map code="0x3f" name=".notdef"/>
+      <map code="0x40" name=".notdef"/>
+      <map code="0x41" name=".notdef"/>
+      <map code="0x42" name=".notdef"/>
+      <map code="0x43" name=".notdef"/>
+      <map code="0x44" name=".notdef"/>
+      <map code="0x45" name=".notdef"/>
+      <map code="0x46" name=".notdef"/>
+      <map code="0x47" name=".notdef"/>
+      <map code="0x48" name=".notdef"/>
+      <map code="0x49" name=".notdef"/>
+      <map code="0x4a" name=".notdef"/>
+      <map code="0x4b" name=".notdef"/>
+      <map code="0x4c" name=".notdef"/>
+      <map code="0x4d" name=".notdef"/>
+      <map code="0x4e" name=".notdef"/>
+      <map code="0x4f" name=".notdef"/>
+      <map code="0x50" name=".notdef"/>
+      <map code="0x51" name=".notdef"/>
+      <map code="0x52" name=".notdef"/>
+      <map code="0x53" name=".notdef"/>
+      <map code="0x54" name=".notdef"/>
+      <map code="0x55" name=".notdef"/>
+      <map code="0x56" name=".notdef"/>
+      <map code="0x57" name=".notdef"/>
+      <map code="0x58" name=".notdef"/>
+      <map code="0x59" name=".notdef"/>
+      <map code="0x5a" name=".notdef"/>
+      <map code="0x5b" name=".notdef"/>
+      <map code="0x5c" name=".notdef"/>
+      <map code="0x5d" name=".notdef"/>
+      <map code="0x5e" name=".notdef"/>
+      <map code="0x5f" name=".notdef"/>
+      <map code="0x60" name=".notdef"/>
+      <map code="0x61" name=".notdef"/>
+      <map code="0x62" name=".notdef"/>
+      <map code="0x63" name=".notdef"/>
+      <map code="0x64" name=".notdef"/>
+      <map code="0x65" name=".notdef"/>
+      <map code="0x66" name=".notdef"/>
+      <map code="0x67" name=".notdef"/>
+      <map code="0x68" name=".notdef"/>
+      <map code="0x69" name=".notdef"/>
+      <map code="0x6a" name=".notdef"/>
+      <map code="0x6b" name=".notdef"/>
+      <map code="0x6c" name=".notdef"/>
+      <map code="0x6d" name=".notdef"/>
+      <map code="0x6e" name=".notdef"/>
+      <map code="0x6f" name=".notdef"/>
+      <map code="0x70" name=".notdef"/>
+      <map code="0x71" name=".notdef"/>
+      <map code="0x72" name=".notdef"/>
+      <map code="0x73" name=".notdef"/>
+      <map code="0x74" name=".notdef"/>
+      <map code="0x75" name=".notdef"/>
+      <map code="0x76" name=".notdef"/>
+      <map code="0x77" name=".notdef"/>
+      <map code="0x78" name=".notdef"/>
+      <map code="0x79" name=".notdef"/>
+      <map code="0x7a" name=".notdef"/>
+      <map code="0x7b" name=".notdef"/>
+      <map code="0x7c" name=".notdef"/>
+      <map code="0x7d" name=".notdef"/>
+      <map code="0x7e" name=".notdef"/>
+      <map code="0x7f" name=".notdef"/>
+      <map code="0x80" name=".notdef"/>
+      <map code="0x81" name=".notdef"/>
+      <map code="0x82" name=".notdef"/>
+      <map code="0x83" name=".notdef"/>
+      <map code="0x84" name=".notdef"/>
+      <map code="0x85" name=".notdef"/>
+      <map code="0x86" name=".notdef"/>
+      <map code="0x87" name=".notdef"/>
+      <map code="0x88" name=".notdef"/>
+      <map code="0x89" name=".notdef"/>
+      <map code="0x8a" name=".notdef"/>
+      <map code="0x8b" name=".notdef"/>
+      <map code="0x8c" name=".notdef"/>
+      <map code="0x8d" name=".notdef"/>
+      <map code="0x8e" name=".notdef"/>
+      <map code="0x8f" name=".notdef"/>
+      <map code="0x90" name=".notdef"/>
+      <map code="0x91" name=".notdef"/>
+      <map code="0x92" name=".notdef"/>
+      <map code="0x93" name=".notdef"/>
+      <map code="0x94" name=".notdef"/>
+      <map code="0x95" name=".notdef"/>
+      <map code="0x96" name=".notdef"/>
+      <map code="0x97" name=".notdef"/>
+      <map code="0x98" name=".notdef"/>
+      <map code="0x99" name=".notdef"/>
+      <map code="0x9a" name=".notdef"/>
+      <map code="0x9b" name=".notdef"/>
+      <map code="0x9c" name=".notdef"/>
+      <map code="0x9d" name=".notdef"/>
+      <map code="0x9e" name=".notdef"/>
+      <map code="0x9f" name=".notdef"/>
+      <map code="0xa0" name=".notdef"/>
+      <map code="0xa1" name=".notdef"/>
+      <map code="0xa2" name=".notdef"/>
+      <map code="0xa3" name=".notdef"/>
+      <map code="0xa4" name=".notdef"/>
+      <map code="0xa5" name=".notdef"/>
+      <map code="0xa6" name=".notdef"/>
+      <map code="0xa7" name=".notdef"/>
+      <map code="0xa8" name=".notdef"/>
+      <map code="0xa9" name=".notdef"/>
+      <map code="0xaa" name=".notdef"/>
+      <map code="0xab" name=".notdef"/>
+      <map code="0xac" name=".notdef"/>
+      <map code="0xad" name=".notdef"/>
+      <map code="0xae" name=".notdef"/>
+      <map code="0xaf" name=".notdef"/>
+      <map code="0xb0" name=".notdef"/>
+      <map code="0xb1" name=".notdef"/>
+      <map code="0xb2" name=".notdef"/>
+      <map code="0xb3" name=".notdef"/>
+      <map code="0xb4" name=".notdef"/>
+      <map code="0xb5" name=".notdef"/>
+      <map code="0xb6" name=".notdef"/>
+      <map code="0xb7" name=".notdef"/>
+      <map code="0xb8" name=".notdef"/>
+      <map code="0xb9" name=".notdef"/>
+      <map code="0xba" name=".notdef"/>
+      <map code="0xbb" name=".notdef"/>
+      <map code="0xbc" name=".notdef"/>
+      <map code="0xbd" name=".notdef"/>
+      <map code="0xbe" name=".notdef"/>
+      <map code="0xbf" name=".notdef"/>
+      <map code="0xc0" name=".notdef"/>
+      <map code="0xc1" name=".notdef"/>
+      <map code="0xc2" name=".notdef"/>
+      <map code="0xc3" name=".notdef"/>
+      <map code="0xc4" name=".notdef"/>
+      <map code="0xc5" name=".notdef"/>
+      <map code="0xc6" name=".notdef"/>
+      <map code="0xc7" name=".notdef"/>
+      <map code="0xc8" name=".notdef"/>
+      <map code="0xc9" name=".notdef"/>
+      <map code="0xca" name=".notdef"/>
+      <map code="0xcb" name=".notdef"/>
+      <map code="0xcc" name=".notdef"/>
+      <map code="0xcd" name=".notdef"/>
+      <map code="0xce" name=".notdef"/>
+      <map code="0xcf" name=".notdef"/>
+      <map code="0xd0" name=".notdef"/>
+      <map code="0xd1" name=".notdef"/>
+      <map code="0xd2" name=".notdef"/>
+      <map code="0xd3" name=".notdef"/>
+      <map code="0xd4" name=".notdef"/>
+      <map code="0xd5" name=".notdef"/>
+      <map code="0xd6" name=".notdef"/>
+      <map code="0xd7" name=".notdef"/>
+      <map code="0xd8" name=".notdef"/>
+      <map code="0xd9" name=".notdef"/>
+      <map code="0xda" name=".notdef"/>
+      <map code="0xdb" name=".notdef"/>
+      <map code="0xdc" name=".notdef"/>
+      <map code="0xdd" name=".notdef"/>
+      <map code="0xde" name=".notdef"/>
+      <map code="0xdf" name=".notdef"/>
+      <map code="0xe0" name=".notdef"/>
+      <map code="0xe1" name=".notdef"/>
+      <map code="0xe2" name=".notdef"/>
+      <map code="0xe3" name=".notdef"/>
+      <map code="0xe4" name=".notdef"/>
+      <map code="0xe5" name=".notdef"/>
+      <map code="0xe6" name=".notdef"/>
+      <map code="0xe7" name=".notdef"/>
+      <map code="0xe8" name=".notdef"/>
+      <map code="0xe9" name=".notdef"/>
+      <map code="0xea" name=".notdef"/>
+      <map code="0xeb" name=".notdef"/>
+      <map code="0xec" name=".notdef"/>
+      <map code="0xed" name=".notdef"/>
+      <map code="0xee" name=".notdef"/>
+      <map code="0xef" name=".notdef"/>
+      <map code="0xf0" name=".notdef"/>
+      <map code="0xf1" name=".notdef"/>
+      <map code="0xf2" name=".notdef"/>
+      <map code="0xf3" name=".notdef"/>
+      <map code="0xf4" name=".notdef"/>
+      <map code="0xf5" name=".notdef"/>
+      <map code="0xf6" name=".notdef"/>
+      <map code="0xf7" name=".notdef"/>
+      <map code="0xf8" name=".notdef"/>
+      <map code="0xf9" name=".notdef"/>
+      <map code="0xfa" name=".notdef"/>
+      <map code="0xfb" name=".notdef"/>
+      <map code="0xfc" name=".notdef"/>
+      <map code="0xfd" name=".notdef"/>
+      <map code="0xfe" name=".notdef"/>
+      <map code="0xff" name=".notdef"/>
+    </cmap_format_0>
+  </cmap>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Meh
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      ZOMG
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-1244"/>
+    <underlineThickness value="131"/>
+    <isFixedPitch value="1"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+    </extraNames>
+  </post>
+
+  <CBDT>
+    <header version="2.0"/>
+    <strikedata index="0">
+      <cbdt_bitmap_format_17 name="x">
+        <SmallGlyphMetrics>
+          <height value="128"/>
+          <width value="100"/>
+          <BearingX value="0"/>
+          <BearingY value="96"/>
+          <Advance value="100"/>
+        </SmallGlyphMetrics>
+        <rawimagedata>
+          DEAD
+        </rawimagedata>
+      </cbdt_bitmap_format_17>
+      <cbdt_bitmap_format_17 name="y">
+        <SmallGlyphMetrics>
+          <height value="128"/>
+          <width value="99"/>
+          <BearingX value="0"/>
+          <BearingY value="96"/>
+          <Advance value="99"/>
+        </SmallGlyphMetrics>
+        <rawimagedata>
+          F00D
+        </rawimagedata>
+      </cbdt_bitmap_format_17>
+    </strikedata>
+  </CBDT>
+
+  <CBLC>
+    <header version="2.0"/>
+    <strike index="0">
+      <bitmapSizeTable>
+        <sbitLineMetrics direction="hori">
+          <ascender value="97"/>
+          <descender value="-32"/>
+          <widthMax value="100"/>
+          <caretSlopeNumerator value="0"/>
+          <caretSlopeDenominator value="0"/>
+          <caretOffset value="0"/>
+          <minOriginSB value="0"/>
+          <minAdvanceSB value="0"/>
+          <maxBeforeBL value="0"/>
+          <minAfterBL value="0"/>
+          <pad1 value="0"/>
+          <pad2 value="0"/>
+        </sbitLineMetrics>
+        <sbitLineMetrics direction="vert">
+          <ascender value="97"/>
+          <descender value="-32"/>
+          <widthMax value="100"/>
+          <caretSlopeNumerator value="0"/>
+          <caretSlopeDenominator value="0"/>
+          <caretOffset value="0"/>
+          <minOriginSB value="0"/>
+          <minAdvanceSB value="0"/>
+          <maxBeforeBL value="0"/>
+          <minAfterBL value="0"/>
+          <pad1 value="0"/>
+          <pad2 value="0"/>
+        </sbitLineMetrics>
+        <colorRef value="0"/>
+        <startGlyphIndex value="81"/>
+        <endGlyphIndex value="82"/>
+        <ppemX value="129"/>
+        <ppemY value="129"/>
+        <bitDepth value="32"/>
+        <flags value="1"/>
+      </bitmapSizeTable>
+      <!-- GlyphIds are written but not read. The firstGlyphIndex and
+           lastGlyphIndex values will be recalculated by the compiler. -->
+      <eblc_index_sub_table_1 imageFormat="17" firstGlyphIndex="1" lastGlyphIndex="2">
+        <glyphLoc id="1" name="x"/>
+        <glyphLoc id="2" name="y"/>
+      </eblc_index_sub_table_1>
+    </strike>
+  </CBLC>
+
+</ttFont>
diff --git a/Tests/subset/subset_test.py b/Tests/subset/subset_test.py
new file mode 100644
index 0000000..e9b3cba
--- /dev/null
+++ b/Tests/subset/subset_test.py
@@ -0,0 +1,482 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools import subset
+from fontTools.ttLib import TTFont, newTable
+from fontTools.misc.loggingTools import CapturingLogHandler
+import difflib
+import logging
+import os
+import shutil
+import sys
+import tempfile
+import unittest
+
+
+class SubsetTest(unittest.TestCase):
+    def __init__(self, methodName):
+        unittest.TestCase.__init__(self, methodName)
+        # Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
+        # and fires deprecation warnings if a program uses the old name.
+        if not hasattr(self, "assertRaisesRegex"):
+            self.assertRaisesRegex = self.assertRaisesRegexp
+
+    def setUp(self):
+        self.tempdir = None
+        self.num_tempfiles = 0
+
+    def tearDown(self):
+        if self.tempdir:
+            shutil.rmtree(self.tempdir)
+
+    @staticmethod
+    def getpath(testfile):
+        path, _ = os.path.split(__file__)
+        return os.path.join(path, "data", testfile)
+
+    def temp_path(self, suffix):
+        if not self.tempdir:
+            self.tempdir = tempfile.mkdtemp()
+        self.num_tempfiles += 1
+        return os.path.join(self.tempdir,
+                            "tmp%d%s" % (self.num_tempfiles, suffix))
+
+    def read_ttx(self, path):
+        lines = []
+        with open(path, "r", encoding="utf-8") as ttx:
+            for line in ttx.readlines():
+                # Elide ttFont attributes because ttLibVersion may change,
+                # and use os-native line separators so we can run difflib.
+                if line.startswith("<ttFont "):
+                    lines.append("<ttFont>" + os.linesep)
+                else:
+                    lines.append(line.rstrip() + os.linesep)
+        return lines
+
+    def expect_ttx(self, font, expected_ttx, tables):
+        path = self.temp_path(suffix=".ttx")
+        font.saveXML(path, tables=tables)
+        actual = self.read_ttx(path)
+        expected = self.read_ttx(expected_ttx)
+        if actual != expected:
+            for line in difflib.unified_diff(
+                    expected, actual, fromfile=expected_ttx, tofile=path):
+                sys.stdout.write(line)
+            self.fail("TTX output is different from expected")
+
+    def compile_font(self, path, suffix):
+        savepath = self.temp_path(suffix=suffix)
+        font = TTFont(recalcBBoxes=False, recalcTimestamp=False)
+        font.importXML(path)
+        font.save(savepath, reorderTables=None)
+        return font, savepath
+
+# -----
+# Tests
+# -----
+
+    def test_no_notdef_outline_otf(self):
+        _, fontpath = self.compile_font(self.getpath("TestOTF-Regular.ttx"), ".otf")
+        subsetpath = self.temp_path(".otf")
+        subset.main([fontpath, "--no-notdef-outline", "--gids=0", "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath("expect_no_notdef_outline_otf.ttx"), ["CFF "])
+
+    def test_no_notdef_outline_cid(self):
+        _, fontpath = self.compile_font(self.getpath("TestCID-Regular.ttx"), ".otf")
+        subsetpath = self.temp_path(".otf")
+        subset.main([fontpath, "--no-notdef-outline", "--gids=0", "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath("expect_no_notdef_outline_cid.ttx"), ["CFF "])
+
+    def test_no_notdef_outline_ttf(self):
+        _, fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--no-notdef-outline", "--gids=0", "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath("expect_no_notdef_outline_ttf.ttx"), ["glyf", "hmtx"])
+
+    def test_subset_ankr(self):
+        _, fontpath = self.compile_font(self.getpath("TestANKR.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--glyphs=one", "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath("expect_ankr.ttx"), ["ankr"])
+
+    def test_subset_ankr_remove(self):
+        _, fontpath = self.compile_font(self.getpath("TestANKR.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--glyphs=two", "--output-file=%s" % subsetpath])
+        self.assertNotIn("ankr", TTFont(subsetpath))
+
+    def test_subset_bsln_format_0(self):
+        _, fontpath = self.compile_font(self.getpath("TestBSLN-0.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--glyphs=one", "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath("expect_bsln_0.ttx"), ["bsln"])
+
+    def test_subset_bsln_format_0_from_format_1(self):
+        # TestBSLN-1 defines the ideographic baseline to be the font's default,
+        # and specifies that glyphs {.notdef, zero, one, two} use the roman
+        # baseline instead of the default ideographic baseline. As we request
+        # a subsetted font with {zero, one} and the implicit .notdef, all
+        # glyphs in the resulting font use the Roman baseline. In this case,
+        # we expect a format 0 'bsln' table because it is the most compact.
+        _, fontpath = self.compile_font(self.getpath("TestBSLN-1.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--unicodes=U+0030-0031",
+                     "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath("expect_bsln_0.ttx"), ["bsln"])
+
+    def test_subset_bsln_format_1(self):
+        # TestBSLN-1 defines the ideographic baseline to be the font's default,
+        # and specifies that glyphs {.notdef, zero, one, two} use the roman
+        # baseline instead of the default ideographic baseline. We request
+        # a subset where the majority of glyphs use the roman baseline,
+        # but one single glyph (uni2EA2) is ideographic. In the resulting
+        # subsetted font, we expect a format 1 'bsln' table whose default
+        # is Roman, but with an override that uses the ideographic baseline
+        # for uni2EA2.
+        _, fontpath = self.compile_font(self.getpath("TestBSLN-1.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--unicodes=U+0030-0031,U+2EA2",
+                     "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath("expect_bsln_1.ttx"), ["bsln"])
+
+    def test_subset_bsln_format_2(self):
+        # The 'bsln' table in TestBSLN-2 refers to control points in glyph 'P'
+        # for defining its baselines. Therefore, the subsetted font should
+        # include this glyph even though it is not requested explicitly.
+        _, fontpath = self.compile_font(self.getpath("TestBSLN-2.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--glyphs=one", "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath("expect_bsln_2.ttx"), ["bsln"])
+
+    def test_subset_bsln_format_2_from_format_3(self):
+        # TestBSLN-3 defines the ideographic baseline to be the font's default,
+        # and specifies that glyphs {.notdef, zero, one, two, P} use the roman
+        # baseline instead of the default ideographic baseline. As we request
+        # a subsetted font with zero and the implicit .notdef and P for
+        # baseline measurement, all glyphs in the resulting font use the Roman
+        # baseline. In this case, we expect a format 2 'bsln' table because it
+        # is the most compact encoding.
+        _, fontpath = self.compile_font(self.getpath("TestBSLN-3.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--unicodes=U+0030",
+                     "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath("expect_bsln_2.ttx"), ["bsln"])
+
+    def test_subset_bsln_format_3(self):
+        # TestBSLN-3 defines the ideographic baseline to be the font's default,
+        # and specifies that glyphs {.notdef, zero, one, two} use the roman
+        # baseline instead of the default ideographic baseline. We request
+        # a subset where the majority of glyphs use the roman baseline,
+        # but one single glyph (uni2EA2) is ideographic. In the resulting
+        # subsetted font, we expect a format 1 'bsln' table whose default
+        # is Roman, but with an override that uses the ideographic baseline
+        # for uni2EA2.
+        _, fontpath = self.compile_font(self.getpath("TestBSLN-3.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--unicodes=U+0030-0031,U+2EA2",
+                     "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath("expect_bsln_3.ttx"), ["bsln"])
+
+    def test_subset_clr(self):
+        _, fontpath = self.compile_font(self.getpath("TestCLR-Regular.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--glyphs=smileface", "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath("expect_keep_colr.ttx"), ["GlyphOrder", "hmtx", "glyf", "COLR", "CPAL"])
+
+    def test_subset_gvar(self):
+        _, fontpath = self.compile_font(self.getpath("TestGVAR.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--unicodes=U+002B,U+2212", "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath("expect_keep_gvar.ttx"), ["GlyphOrder", "avar", "fvar", "gvar", "name"])
+
+    def test_subset_gvar_notdef_outline(self):
+        _, fontpath = self.compile_font(self.getpath("TestGVAR.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--unicodes=U+0030", "--notdef_outline", "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath("expect_keep_gvar_notdef_outline.ttx"), ["GlyphOrder", "avar", "fvar", "gvar", "name"])
+
+    def test_subset_lcar_remove(self):
+        _, fontpath = self.compile_font(self.getpath("TestLCAR-0.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--glyphs=one", "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.assertNotIn("lcar", subsetfont)
+
+    def test_subset_lcar_format_0(self):
+        _, fontpath = self.compile_font(self.getpath("TestLCAR-0.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--unicodes=U+FB01",
+                     "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath("expect_lcar_0.ttx"), ["lcar"])
+
+    def test_subset_lcar_format_1(self):
+        _, fontpath = self.compile_font(self.getpath("TestLCAR-1.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--unicodes=U+FB01",
+                     "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath("expect_lcar_1.ttx"), ["lcar"])
+
+    def test_subset_math(self):
+        _, fontpath = self.compile_font(self.getpath("TestMATH-Regular.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--unicodes=U+0041,U+0028,U+0302,U+1D400,U+1D435", "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath("expect_keep_math.ttx"), ["GlyphOrder", "CFF ", "MATH", "hmtx"])
+
+    def test_subset_opbd_remove(self):
+        # In the test font, only the glyphs 'A' and 'zero' have an entry in
+        # the Optical Bounds table. When subsetting, we do not request any
+        # of those glyphs. Therefore, the produced subsetted font should
+        # not contain an 'opbd' table.
+        _, fontpath = self.compile_font(self.getpath("TestOPBD-0.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--glyphs=one", "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.assertNotIn("opbd", subsetfont)
+
+    def test_subset_opbd_format_0(self):
+        _, fontpath = self.compile_font(self.getpath("TestOPBD-0.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--glyphs=A", "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath("expect_opbd_0.ttx"), ["opbd"])
+
+    def test_subset_opbd_format_1(self):
+        _, fontpath = self.compile_font(self.getpath("TestOPBD-1.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--glyphs=A", "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath("expect_opbd_1.ttx"), ["opbd"])
+
+    def test_subset_prop_remove_default_zero(self):
+        # If all glyphs have an AAT glyph property with value 0,
+        # the "prop" table should be removed from the subsetted font.
+        _, fontpath = self.compile_font(self.getpath("TestPROP.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--unicodes=U+0041",
+                     "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.assertNotIn("prop", subsetfont)
+
+    def test_subset_prop_0(self):
+        # If all glyphs share the same AAT glyph properties, the "prop" table
+        # in the subsetted font should use format 0.
+        #
+        # Unless the shared value is zero, in which case the subsetted font
+        # should have no "prop" table at all. But that case has already been
+        # tested above in test_subset_prop_remove_default_zero().
+        _, fontpath = self.compile_font(self.getpath("TestPROP.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--unicodes=U+0030-0032", "--no-notdef-glyph",
+                     "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath("expect_prop_0.ttx"), ["prop"])
+
+    def test_subset_prop_1(self):
+        # If not all glyphs share the same AAT glyph properties, the subsetted
+        # font should contain a "prop" table in format 1. To save space, the
+        # DefaultProperties should be set to the most frequent value.
+        _, fontpath = self.compile_font(self.getpath("TestPROP.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--unicodes=U+0030-0032", "--notdef-outline",
+                     "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath("expect_prop_1.ttx"), ["prop"])
+
+    def test_options(self):
+        # https://github.com/behdad/fonttools/issues/413
+        opt1 = subset.Options()
+        self.assertTrue('Xyz-' not in opt1.layout_features)
+        opt2 = subset.Options()
+        opt2.layout_features.append('Xyz-')
+        self.assertTrue('Xyz-' in opt2.layout_features)
+        self.assertTrue('Xyz-' not in opt1.layout_features)
+
+    def test_google_color(self):
+        _, fontpath = self.compile_font(self.getpath("google_color.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--gids=0,1", "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.assertTrue("CBDT" in subsetfont)
+        self.assertTrue("CBLC" in subsetfont)
+        self.assertTrue("x" in subsetfont['CBDT'].strikeData[0])
+        self.assertFalse("y" in subsetfont['CBDT'].strikeData[0])
+
+    def test_google_color_all(self):
+        _, fontpath = self.compile_font(self.getpath("google_color.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--unicodes=*", "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.assertTrue("x" in subsetfont['CBDT'].strikeData[0])
+        self.assertTrue("y" in subsetfont['CBDT'].strikeData[0])
+
+    def test_timing_publishes_parts(self):
+        _, fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf")
+
+        options = subset.Options()
+        options.timing = True
+        subsetter = subset.Subsetter(options)
+        subsetter.populate(text='ABC')
+        font = TTFont(fontpath)
+        with CapturingLogHandler('fontTools.subset.timer', logging.DEBUG) as captor:
+            captor.logger.propagate = False
+            subsetter.subset(font)
+            logs = captor.records
+        captor.logger.propagate = True
+
+        self.assertTrue(len(logs) > 5)
+        self.assertEqual(len(logs), len([l for l in logs if 'msg' in l.args and 'time' in l.args]))
+        # Look for a few things we know should happen
+        self.assertTrue(filter(lambda l: l.args['msg'] == "load 'cmap'", logs))
+        self.assertTrue(filter(lambda l: l.args['msg'] == "subset 'cmap'", logs))
+        self.assertTrue(filter(lambda l: l.args['msg'] == "subset 'glyf'", logs))
+
+    def test_passthrough_tables(self):
+        _, fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf")
+        font = TTFont(fontpath)
+        unknown_tag = 'ZZZZ'
+        unknown_table = newTable(unknown_tag)
+        unknown_table.data = b'\0'*10
+        font[unknown_tag] = unknown_table
+        font.save(fontpath)
+
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+
+        # tables we can't subset are dropped by default
+        self.assertFalse(unknown_tag in subsetfont)
+
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--passthrough-tables", "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+
+        # unknown tables are kept if --passthrough-tables option is passed
+        self.assertTrue(unknown_tag in subsetfont)
+
+    def test_non_BMP_text_arg_input(self):
+        _, fontpath = self.compile_font(
+            self.getpath("TestTTF-Regular_non_BMP_char.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        text = tostr(u"A\U0001F6D2", encoding='utf-8')
+
+        subset.main([fontpath, "--text=%s" % text, "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+
+        self.assertEqual(subsetfont['maxp'].numGlyphs, 3)
+        self.assertEqual(subsetfont.getGlyphOrder(), ['.notdef', 'A', 'u1F6D2'])
+
+    def test_non_BMP_text_file_input(self):
+        _, fontpath = self.compile_font(
+            self.getpath("TestTTF-Regular_non_BMP_char.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        text = tobytes(u"A\U0001F6D2", encoding='utf-8')
+        with tempfile.NamedTemporaryFile(delete=False) as tmp:
+            tmp.write(text)
+
+        try:
+            subset.main([fontpath, "--text-file=%s" % tmp.name,
+                         "--output-file=%s" % subsetpath])
+            subsetfont = TTFont(subsetpath)
+        finally:
+            os.remove(tmp.name)
+
+        self.assertEqual(subsetfont['maxp'].numGlyphs, 3)
+        self.assertEqual(subsetfont.getGlyphOrder(), ['.notdef', 'A', 'u1F6D2'])
+
+    def test_no_hinting_CFF(self):
+        ttxpath = self.getpath("Lobster.subset.ttx")
+        _, fontpath = self.compile_font(ttxpath, ".otf")
+        subsetpath = self.temp_path(".otf")
+        subset.main([fontpath, "--no-hinting", "--notdef-outline",
+                     "--output-file=%s" % subsetpath, "*"])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath(
+            "expect_no_hinting_CFF.ttx"), ["CFF "])
+
+    def test_desubroutinize_CFF(self):
+        ttxpath = self.getpath("Lobster.subset.ttx")
+        _, fontpath = self.compile_font(ttxpath, ".otf")
+        subsetpath = self.temp_path(".otf")
+        subset.main([fontpath, "--desubroutinize", "--notdef-outline",
+                     "--output-file=%s" % subsetpath, "*"])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath(
+            "expect_desubroutinize_CFF.ttx"), ["CFF "])
+
+    def test_no_hinting_desubroutinize_CFF(self):
+        ttxpath = self.getpath("Lobster.subset.ttx")
+        _, fontpath = self.compile_font(ttxpath, ".otf")
+        subsetpath = self.temp_path(".otf")
+        subset.main([fontpath, "--no-hinting", "--desubroutinize", "--notdef-outline",
+                     "--output-file=%s" % subsetpath, "*"])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath(
+            "expect_no_hinting_desubroutinize_CFF.ttx"), ["CFF "])
+
+    def test_no_hinting_TTF(self):
+        _, fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--no-hinting", "--notdef-outline",
+                     "--output-file=%s" % subsetpath, "*"])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath(
+            "expect_no_hinting_TTF.ttx"), ["glyf", "maxp"])
+        for tag in subset.Options().hinting_tables:
+            self.assertTrue(tag not in subsetfont)
+
+    def test_notdef_width_cid(self):
+        # https://github.com/fonttools/fonttools/pull/845
+        _, fontpath = self.compile_font(self.getpath("NotdefWidthCID-Regular.ttx"), ".otf")
+        subsetpath = self.temp_path(".otf")
+        subset.main([fontpath, "--no-notdef-outline", "--gids=0,1", "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath("expect_notdef_width_cid.ttx"), ["CFF "])
+
+    def test_recalc_timestamp_ttf(self):
+        ttxpath = self.getpath("TestTTF-Regular.ttx")
+        font = TTFont()
+        font.importXML(ttxpath)
+        modified = font['head'].modified
+        _, fontpath = self.compile_font(ttxpath, ".ttf")
+        subsetpath = self.temp_path(".ttf")
+
+        # by default, the subsetter does not recalculate the modified timestamp
+        subset.main([fontpath, "--output-file=%s" % subsetpath, "*"])
+        self.assertEqual(modified, TTFont(subsetpath)['head'].modified)
+
+        subset.main([fontpath, "--recalc-timestamp", "--output-file=%s" % subsetpath, "*"])
+        self.assertLess(modified, TTFont(subsetpath)['head'].modified)
+
+    def test_recalc_timestamp_otf(self):
+        ttxpath = self.getpath("TestOTF-Regular.ttx")
+        font = TTFont()
+        font.importXML(ttxpath)
+        modified = font['head'].modified
+        _, fontpath = self.compile_font(ttxpath, ".otf")
+        subsetpath = self.temp_path(".otf")
+
+        # by default, the subsetter does not recalculate the modified timestamp
+        subset.main([fontpath, "--output-file=%s" % subsetpath, "*"])
+        self.assertEqual(modified, TTFont(subsetpath)['head'].modified)
+
+        subset.main([fontpath, "--recalc-timestamp", "--output-file=%s" % subsetpath, "*"])
+        self.assertLess(modified, TTFont(subsetpath)['head'].modified)
+
+
+if __name__ == "__main__":
+    sys.exit(unittest.main())
diff --git a/Tests/svgLib/path/__init__.py b/Tests/svgLib/path/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/svgLib/path/__init__.py
diff --git a/Tests/svgLib/path/parser_test.py b/Tests/svgLib/path/parser_test.py
new file mode 100644
index 0000000..7865870
--- /dev/null
+++ b/Tests/svgLib/path/parser_test.py
@@ -0,0 +1,297 @@
+from __future__ import print_function, absolute_import, division
+
+from fontTools.misc.py23 import *
+from fontTools.pens.recordingPen import RecordingPen
+from fontTools.svgLib import parse_path
+
+import pytest
+
+
+@pytest.mark.parametrize(
+    "pathdef, expected",
+    [
+
+        # Examples from the SVG spec
+
+        (
+            "M 100 100 L 300 100 L 200 300 z",
+            [
+                ("moveTo", ((100.0, 100.0),)),
+                ("lineTo", ((300.0, 100.0),)),
+                ("lineTo", ((200.0, 300.0),)),
+                ("lineTo", ((100.0, 100.0),)),
+                ("closePath", ()),
+            ]
+        ),
+        # for Z command behavior when there is multiple subpaths
+        (
+            "M 0 0 L 50 20 M 100 100 L 300 100 L 200 300 z",
+            [
+                ("moveTo", ((0.0, 0.0),)),
+                ("lineTo", ((50.0, 20.0),)),
+                ("endPath", ()),
+                ("moveTo", ((100.0, 100.0),)),
+                ("lineTo", ((300.0, 100.0),)),
+                ("lineTo", ((200.0, 300.0),)),
+                ("lineTo", ((100.0, 100.0),)),
+                ("closePath", ()),
+            ]
+        ),
+        (
+            "M100,200 C100,100 250,100 250,200 S400,300 400,200",
+            [
+                ("moveTo", ((100.0, 200.0),)),
+                ("curveTo", ((100.0, 100.0),
+                             (250.0, 100.0),
+                             (250.0, 200.0))),
+                ("curveTo", ((250.0, 300.0),
+                             (400.0, 300.0),
+                             (400.0, 200.0))),
+                ("endPath", ()),
+            ]
+        ),
+        (
+            "M100,200 C100,100 400,100 400,200",
+            [
+                ("moveTo", ((100.0, 200.0),)),
+                ("curveTo", ((100.0, 100.0),
+                             (400.0, 100.0),
+                             (400.0, 200.0))),
+                ("endPath", ()),
+            ]
+        ),
+        (
+            "M100,500 C25,400 475,400 400,500",
+            [
+                ("moveTo", ((100.0, 500.0),)),
+                ("curveTo", ((25.0, 400.0),
+                             (475.0, 400.0),
+                             (400.0, 500.0))),
+                ("endPath", ()),
+            ]
+        ),
+        (
+            "M100,800 C175,700 325,700 400,800",
+            [
+                ("moveTo", ((100.0, 800.0),)),
+                ("curveTo", ((175.0, 700.0),
+                             (325.0, 700.0),
+                             (400.0, 800.0))),
+                ("endPath", ()),
+            ]
+        ),
+        (
+            "M600,200 C675,100 975,100 900,200",
+            [
+                ("moveTo", ((600.0, 200.0),)),
+                ("curveTo", ((675.0, 100.0),
+                             (975.0, 100.0),
+                             (900.0, 200.0))),
+                ("endPath", ()),
+            ]
+        ),
+        (
+            "M600,500 C600,350 900,650 900,500",
+            [
+                ("moveTo", ((600.0, 500.0),)),
+                ("curveTo", ((600.0, 350.0),
+                             (900.0, 650.0),
+                             (900.0, 500.0))),
+                ("endPath", ()),
+            ]
+        ),
+        (
+            "M600,800 C625,700 725,700 750,800 S875,900 900,800",
+            [
+                ("moveTo", ((600.0, 800.0),)),
+                ("curveTo", ((625.0, 700.0),
+                             (725.0, 700.0),
+                             (750.0, 800.0))),
+                ("curveTo", ((775.0, 900.0),
+                             (875.0, 900.0),
+                             (900.0, 800.0))),
+                ("endPath", ()),
+            ]
+        ),
+        (
+            "M200,300 Q400,50 600,300 T1000,300",
+            [
+                ("moveTo", ((200.0, 300.0),)),
+                ("qCurveTo", ((400.0, 50.0),
+                              (600.0, 300.0))),
+                ("qCurveTo", ((800.0, 550.0),
+                              (1000.0, 300.0))),
+                ("endPath", ()),
+            ]
+        ),
+        # End examples from SVG spec
+
+        # Relative moveto
+        (
+            "M 0 0 L 50 20 m 50 80 L 300 100 L 200 300 z",
+            [
+                ("moveTo", ((0.0, 0.0),)),
+                ("lineTo", ((50.0, 20.0),)),
+                ("endPath", ()),
+                ("moveTo", ((100.0, 100.0),)),
+                ("lineTo", ((300.0, 100.0),)),
+                ("lineTo", ((200.0, 300.0),)),
+                ("lineTo", ((100.0, 100.0),)),
+                ("closePath", ()),
+            ]
+        ),
+        # Initial smooth and relative curveTo
+        (
+            "M100,200 s 150,-100 150,0",
+            [
+                ("moveTo", ((100.0, 200.0),)),
+                ("curveTo", ((100.0, 200.0),
+                             (250.0, 100.0),
+                             (250.0, 200.0))),
+                ("endPath", ()),
+            ]
+        ),
+        # Initial smooth and relative qCurveTo
+        (
+            "M100,200 t 150,0",
+            [
+                ("moveTo", ((100.0, 200.0),)),
+                ("qCurveTo", ((100.0, 200.0),
+                              (250.0, 200.0))),
+                ("endPath", ()),
+            ]
+        ),
+        # relative l command
+        (
+            "M 100 100 L 300 100 l -100 200 z",
+            [
+                ("moveTo", ((100.0, 100.0),)),
+                ("lineTo", ((300.0, 100.0),)),
+                ("lineTo", ((200.0, 300.0),)),
+                ("lineTo", ((100.0, 100.0),)),
+                ("closePath", ()),
+            ]
+        ),
+        # relative q command
+        (
+            "M200,300 q200,-250 400,0",
+            [
+                ("moveTo", ((200.0, 300.0),)),
+                ("qCurveTo", ((400.0, 50.0),
+                              (600.0, 300.0))),
+                ("endPath", ()),
+            ]
+        ),
+        # absolute H command
+        (
+            "M 100 100 H 300 L 200 300 z",
+            [
+                ("moveTo", ((100.0, 100.0),)),
+                ("lineTo", ((300.0, 100.0),)),
+                ("lineTo", ((200.0, 300.0),)),
+                ("lineTo", ((100.0, 100.0),)),
+                ("closePath", ()),
+            ]
+        ),
+        # relative h command
+        (
+            "M 100 100 h 200 L 200 300 z",
+            [
+                ("moveTo", ((100.0, 100.0),)),
+                ("lineTo", ((300.0, 100.0),)),
+                ("lineTo", ((200.0, 300.0),)),
+                ("lineTo", ((100.0, 100.0),)),
+                ("closePath", ()),
+            ]
+        ),
+        # absolute V command
+        (
+            "M 100 100 V 300 L 200 300 z",
+            [
+                ("moveTo", ((100.0, 100.0),)),
+                ("lineTo", ((100.0, 300.0),)),
+                ("lineTo", ((200.0, 300.0),)),
+                ("lineTo", ((100.0, 100.0),)),
+                ("closePath", ()),
+            ]
+        ),
+        # relative v command
+        (
+            "M 100 100 v 200 L 200 300 z",
+            [
+                ("moveTo", ((100.0, 100.0),)),
+                ("lineTo", ((100.0, 300.0),)),
+                ("lineTo", ((200.0, 300.0),)),
+                ("lineTo", ((100.0, 100.0),)),
+                ("closePath", ()),
+            ]
+        ),
+    ]
+)
+def test_parse_path(pathdef, expected):
+    pen = RecordingPen()
+    parse_path(pathdef, pen)
+
+    assert pen.value == expected
+
+
+@pytest.mark.parametrize(
+    "pathdef1, pathdef2",
+    [
+        # don't need spaces between numbers and commands
+        (
+            "M 100 100 L 200 200",
+            "M100 100L200 200",
+        ),
+        # repeated implicit command
+        (
+            "M 100 200 L 200 100 L -100 -200",
+            "M 100 200 L 200 100 -100 -200"
+        ),
+        # don't need spaces before a minus-sign
+        (
+            "M100,200c10-5,20-10,30-20",
+            "M 100 200 c 10 -5 20 -10 30 -20"
+        ),
+        # closed paths have an implicit lineTo if they don't
+        # end on the same point as the initial moveTo
+        (
+            "M 100 100 L 300 100 L 200 300 z",
+            "M 100 100 L 300 100 L 200 300 L 100 100 z"
+        )
+    ]
+)
+def test_equivalent_paths(pathdef1, pathdef2):
+    pen1 = RecordingPen()
+    parse_path(pathdef1, pen1)
+
+    pen2 = RecordingPen()
+    parse_path(pathdef2, pen2)
+
+    assert pen1.value == pen2.value
+
+
+def test_exponents():
+    # It can be e or E, the plus is optional, and a minimum of +/-3.4e38 must be supported.
+    pen = RecordingPen()
+    parse_path("M-3.4e38 3.4E+38L-3.4E-38,3.4e-38", pen)
+    expected = [
+        ("moveTo", ((-3.4e+38, 3.4e+38),)),
+        ("lineTo", ((-3.4e-38, 3.4e-38),)),
+        ("endPath", ()),
+    ]
+
+    assert pen.value == expected
+
+
+def test_invalid_implicit_command():
+    with pytest.raises(ValueError) as exc_info:
+        parse_path("M 100 100 L 200 200 Z 100 200", RecordingPen())
+    assert exc_info.match("Unallowed implicit command")
+
+
+def test_arc_not_implemented():
+    pathdef = "M300,200 h-150 a150,150 0 1,0 150,-150 z"
+    with pytest.raises(NotImplementedError) as exc_info:
+        parse_path(pathdef, RecordingPen())
+    assert exc_info.match("arcs are not supported")
diff --git a/Tests/svgLib/path/path_test.py b/Tests/svgLib/path/path_test.py
new file mode 100644
index 0000000..09b9447
--- /dev/null
+++ b/Tests/svgLib/path/path_test.py
@@ -0,0 +1,80 @@
+from __future__ import print_function, absolute_import, division
+
+from fontTools.misc.py23 import *
+from fontTools.pens.recordingPen import RecordingPen
+from fontTools.svgLib import SVGPath
+
+import os
+from tempfile import NamedTemporaryFile
+
+
+SVG_DATA = """\
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+ "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
+ width="1000.0" height="1000.0">
+<path d="M 100 100 L 300 100 L 200 300 z"/>
+<path d="M100,200 C100,100 250,100 250,200 S400,300 400,200"/>
+</svg>
+"""
+
+EXPECTED_PEN_COMMANDS = [
+    ("moveTo", ((100.0, 100.0),)),
+    ("lineTo", ((300.0, 100.0),)),
+    ("lineTo", ((200.0, 300.0),)),
+    ("lineTo", ((100.0, 100.0),)),
+    ("closePath", ()),
+    ("moveTo", ((100.0, 200.0),)),
+    ("curveTo", ((100.0, 100.0),
+                 (250.0, 100.0),
+                 (250.0, 200.0))),
+    ("curveTo", ((250.0, 300.0),
+                 (400.0, 300.0),
+                 (400.0, 200.0))),
+    ("endPath", ())
+]
+
+
+class SVGPathTest(object):
+
+    def test_from_svg_file(self):
+        pen = RecordingPen()
+        with NamedTemporaryFile(delete=False) as tmp:
+            tmp.write(tobytes(SVG_DATA))
+        try:
+            svg = SVGPath(tmp.name)
+            svg.draw(pen)
+        finally:
+            os.remove(tmp.name)
+
+        assert pen.value == EXPECTED_PEN_COMMANDS
+
+    def test_fromstring(self):
+        pen = RecordingPen()
+        svg = SVGPath.fromstring(SVG_DATA)
+        svg.draw(pen)
+
+        assert pen.value == EXPECTED_PEN_COMMANDS
+
+    def test_transform(self):
+        pen = RecordingPen()
+        svg = SVGPath.fromstring(SVG_DATA,
+                                 transform=(1.0, 0, 0, -1.0, 0, 1000))
+        svg.draw(pen)
+
+        assert pen.value == [
+            ("moveTo", ((100.0, 900.0),)),
+            ("lineTo", ((300.0, 900.0),)),
+            ("lineTo", ((200.0, 700.0),)),
+            ("lineTo", ((100.0, 900.0),)),
+            ("closePath", ()),
+            ("moveTo", ((100.0, 800.0),)),
+            ("curveTo", ((100.0, 900.0),
+                         (250.0, 900.0),
+                         (250.0, 800.0))),
+            ("curveTo", ((250.0, 700.0),
+                         (400.0, 700.0),
+                         (400.0, 800.0))),
+            ("endPath", ())
+        ]
diff --git a/Tests/t1Lib/data/TestT1-Regular.lwfn b/Tests/t1Lib/data/TestT1-Regular.lwfn
new file mode 100644
index 0000000..c3c09dd
--- /dev/null
+++ b/Tests/t1Lib/data/TestT1-Regular.lwfn
Binary files differ
diff --git a/Tests/t1Lib/data/TestT1-Regular.pfa b/Tests/t1Lib/data/TestT1-Regular.pfa
new file mode 100644
index 0000000..a3c9d3e
--- /dev/null
+++ b/Tests/t1Lib/data/TestT1-Regular.pfa
@@ -0,0 +1,60 @@
+%!FontType1-1.1: TestT1-Regular 1.0

+%%BeginResource: font TestT1-Regular

+12 dict dup begin

+/FontType 1 def

+/FontName /TestT1-Regular def

+/FontInfo 14 dict dup begin

+/version (1.0) def

+/Notice (Test T1 is not a trademark of FontTools.) def

+/Copyright (Copyright c 2015 by FontTools. No rights reserved.) def

+/FullName (Test T1) def

+/FamilyName (Test T1) def

+/Weight (Regular) def

+/ItalicAngle 0.000000 def

+/isFixedPitch false def

+/UnderlinePosition -75.000000 def

+/UnderlineThickness 50.000000 def

+/FSType 0 def

+end def

+/PaintType 0 def

+/FontMatrix [0.001 0 0 0.001 0 0] def

+/Encoding 256 array

+0 1 255 {1 index exch /.notdef put} for

+def

+/FontBBox {50.000000 0.000000 668.000000 750.000000} def

+end

+currentfile eexec bab431ea06bb0a1031e1aa11919e714ac1ac5197cb08b39a4d7e746fca0af12d89ac0ebd1bc11ab1

+b3887b922efcec739534242d2fd22e7c30e3edce24b93798627e1ac3387816a8c4b84d76047dada8

+28b2ad27c5603046fecbc2a97adc5c37a68912324d2d435f2ee0ccc38df10ba1271a1c9af8897a6d

+6e425cd7d18fd6bd64c2adadb74365bc101a850841669886291e158cbfa7f204b3fe0ba49ffe0c80

+4f6795d32eb770c5fcd38a3879c06a4bb87b2d3ab100d8c2b5f89e9be99248575575025c66381446

+e4d9183674880aef57fb2032a1e00431133b16f6d758de7c3d0c48a0fada1d40034742a69fb3a6f9

+450d2251e659158a04697cbfa70907346d27d37ef683284385c44a1b5089bd29b4629b6483122dc8

+cbce7327bdc33dd30e6fcdb346c0ddaf433a5ac740423aa35639b2386673832f5ae8cc380e9703ba

+d3369533bfa85af9f56a090c9d97f5fc26ed102c07b647137e83632be51a65a532bd26430b59a31c

+3cb037ded351c1d4e944733feb30a3e6f81c1a7b74ac4e0eadbe705412d47991c246e8820876bbc6

+1f6a3e264ae6b2ad4b864b0d7abee289308bea26eb15d00d2b9103861386e0a5f1802ba06f916810

+62110d2b1c3641806f78eea365614f440b580185e84bac6f87bee36108d95174c786600cf0e9dc4a

+5545d1a84cfe8392115c0b7027c17fd460481d21f684af32204085690946327bfded992852645149

+8d44150d2495bd2efe0db6a450c6e28d0a52ca234e252129d5095596b0d8de096682d2eb00bc8320

+f257fd653b05a22eab7a193ccc315a6ee274a03ff1fdf443b310157a02656ca4b06c581dca8ced72

+c6ddcab26eb856ad1093452c587438b7f8408c1311e19254955914612c09828fd4d4fc2b8b0406ea

+2ee38348a8bdab88a77b8033366b2e469834c01b7bd73207b7c67756937c7a9232947fde2e0ea327

+7b7d610e601b91389ccbcdd813c87db5333c0c723e48d3ef69285f246327978ce68ae9081076a227

+1a962a2a10e2b1147ec40b0f6553a00c8b329118569d16fe04a4fa195caf1b04c52c9a562b72e0cd

+e411d747af796b9d2fb086ed927efb0e5fc9f50aa18aaf4949cba0de0805210620a19eec4319dfef

+a74d9d13d16f8ad793323a231347e6b40022a1100c1e064b8679c1da63a26dfb217a6037096ad796

+320da5a9d0526eed51d7d64d3223e285c1a8c70780c59ecc9dd9bc90a0f84ffa038834918cebe247

+f6e8fa4ca0654019196388f2df008e63bc32c8e5e686dbb69193b7749638c22b389fb1f090fbb007

+fdb8a6ee4e4b29e123fe1652fe72239bd2c8

+0000000000000000000000000000000000000000000000000000000000000000

+0000000000000000000000000000000000000000000000000000000000000000

+0000000000000000000000000000000000000000000000000000000000000000

+0000000000000000000000000000000000000000000000000000000000000000

+0000000000000000000000000000000000000000000000000000000000000000

+0000000000000000000000000000000000000000000000000000000000000000

+0000000000000000000000000000000000000000000000000000000000000000

+0000000000000000000000000000000000000000000000000000000000000000

+cleartomark

+%%EndResource

+%%EOF

diff --git a/Tests/t1Lib/data/TestT1-Regular.pfb b/Tests/t1Lib/data/TestT1-Regular.pfb
new file mode 100644
index 0000000..d4173f1
--- /dev/null
+++ b/Tests/t1Lib/data/TestT1-Regular.pfb
Binary files differ
diff --git a/Tests/t1Lib/t1Lib_test.py b/Tests/t1Lib/t1Lib_test.py
new file mode 100644
index 0000000..f5e934d
--- /dev/null
+++ b/Tests/t1Lib/t1Lib_test.py
@@ -0,0 +1,100 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+import unittest
+import os
+from fontTools import t1Lib
+from fontTools.pens.basePen import NullPen
+import random
+
+
+CWD = os.path.abspath(os.path.dirname(__file__))
+DATADIR = os.path.join(CWD, 'data')
+# I used `tx` to convert PFA to LWFN (stored in the data fork)
+LWFN = os.path.join(DATADIR, 'TestT1-Regular.lwfn')
+PFA = os.path.join(DATADIR, 'TestT1-Regular.pfa')
+PFB = os.path.join(DATADIR, 'TestT1-Regular.pfb')
+
+
+class FindEncryptedChunksTest(unittest.TestCase):
+
+	def test_findEncryptedChunks(self):
+		with open(PFA, "rb") as f:
+			data = f.read()
+		chunks = t1Lib.findEncryptedChunks(data)
+		self.assertEqual(len(chunks), 3)
+		self.assertFalse(chunks[0][0])
+		# the second chunk is encrypted
+		self.assertTrue(chunks[1][0])
+		self.assertFalse(chunks[2][0])
+
+
+class DecryptType1Test(unittest.TestCase):
+
+	def test_decryptType1(self):
+		with open(PFA, "rb") as f:
+			data = f.read()
+		decrypted = t1Lib.decryptType1(data)
+		self.assertNotEqual(decrypted, data)
+
+
+class ReadWriteTest(unittest.TestCase):
+
+	def test_read_pfa_write_pfb(self):
+		font = t1Lib.T1Font(PFA)
+		data = self.write(font, 'PFB')
+		self.assertEqual(font.getData(), data)
+
+	def test_read_pfb_write_pfa(self):
+		font = t1Lib.T1Font(PFB)
+		# 'OTHER' == 'PFA'
+		data = self.write(font, 'OTHER', dohex=True)
+		self.assertEqual(font.getData(), data)
+
+	@staticmethod
+	def write(font, outtype, dohex=False):
+		temp = os.path.join(DATADIR, 'temp.' + outtype.lower())
+		try:
+			font.saveAs(temp, outtype, dohex=dohex)
+			newfont = t1Lib.T1Font(temp)
+			data = newfont.getData()
+		finally:
+			if os.path.exists(temp):
+				os.remove(temp)
+		return data
+
+
+class T1FontTest(unittest.TestCase):
+
+	def test_parse_lwfn(self):
+		# the extended attrs are lost on git so we can't auto-detect 'LWFN'
+		font = t1Lib.T1Font(LWFN, kind="LWFN")
+		font.parse()
+		self.assertEqual(font['FontName'], 'TestT1-Regular')
+		self.assertTrue('Subrs' in font['Private'])
+
+	def test_parse_pfa(self):
+		font = t1Lib.T1Font(PFA)
+		font.parse()
+		self.assertEqual(font['FontName'], 'TestT1-Regular')
+		self.assertTrue('Subrs' in font['Private'])
+
+	def test_parse_pfb(self):
+		font = t1Lib.T1Font(PFB)
+		font.parse()
+		self.assertEqual(font['FontName'], 'TestT1-Regular')
+		self.assertTrue('Subrs' in font['Private'])
+
+	def test_getGlyphSet(self):
+		font = t1Lib.T1Font(PFA)
+		glyphs = font.getGlyphSet()
+		i = random.randrange(len(glyphs))
+		aglyph = list(glyphs.values())[i]
+		self.assertTrue(hasattr(aglyph, 'draw'))
+		self.assertFalse(hasattr(aglyph, 'width'))
+		aglyph.draw(NullPen())
+		self.assertTrue(hasattr(aglyph, 'width'))
+
+
+if __name__ == '__main__':
+	import sys
+	sys.exit(unittest.main())
diff --git a/Tests/ttLib/data/TestOTF-Regular.otx b/Tests/ttLib/data/TestOTF-Regular.otx
new file mode 100644
index 0000000..573fe24
--- /dev/null
+++ b/Tests/ttLib/data/TestOTF-Regular.otx
@@ -0,0 +1,519 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="2.5">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name=".null"/>
+    <GlyphID id="2" name="CR"/>
+    <GlyphID id="3" name="space"/>
+    <GlyphID id="4" name="period"/>
+    <GlyphID id="5" name="ellipsis"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x34034793"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Jun  4 14:29:11 2015"/>
+    <modified value="Sat Aug  1 10:07:17 2015"/>
+    <xMin value="50"/>
+    <yMin value="0"/>
+    <xMax value="668"/>
+    <yMax value="750"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="9"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="1.0"/>
+    <ascent value="900"/>
+    <descent value="-300"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="723"/>
+    <minLeftSideBearing value="50"/>
+    <minRightSideBearing value="50"/>
+    <xMaxExtent value="668"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="6"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="6"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="392"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="700"/>
+    <ySubscriptYSize value="650"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="140"/>
+    <ySuperscriptXSize value="700"/>
+    <ySuperscriptYSize value="650"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="477"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="250"/>
+    <sFamilyClass value="2050"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="6"/>
+      <bProportion value="4"/>
+      <bContrast value="4"/>
+      <bStrokeVariation value="2"/>
+      <bArmStyle value="7"/>
+      <bLetterForm value="8"/>
+      <bMidline value="1"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="10000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="NONE"/>
+    <fsSelection value="00000000 11000000"/>
+    <usFirstCharIndex value="0"/>
+    <usLastCharIndex value="8230"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="900"/>
+    <usWinDescent value="300"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="0" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Copyright (c) 2015 by FontTools. No rights reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Test OTF
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      FontTools: Test OTF: 2015
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Test OTF
+    </namerecord>
+    <namerecord nameID="5" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestOTF-Regular
+    </namerecord>
+    <namerecord nameID="7" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Test OTF is not a trademark of FontTools.
+    </namerecord>
+    <namerecord nameID="8" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      FontTools
+    </namerecord>
+    <namerecord nameID="9" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      FontTools
+    </namerecord>
+    <namerecord nameID="11" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      https://github.com/behdad/fonttools
+    </namerecord>
+    <namerecord nameID="12" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      https://github.com/behdad/fonttools
+    </namerecord>
+    <namerecord nameID="14" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+    </namerecord>
+    <namerecord nameID="18" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Test TTF
+    </namerecord>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (c) 2015 by FontTools. No rights reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test OTF
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      FontTools: Test OTF: 2015
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test OTF
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestOTF-Regular
+    </namerecord>
+    <namerecord nameID="7" platformID="3" platEncID="1" langID="0x409">
+      Test OTF is not a trademark of FontTools.
+    </namerecord>
+    <namerecord nameID="8" platformID="3" platEncID="1" langID="0x409">
+      FontTools
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      FontTools
+    </namerecord>
+    <namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
+      https://github.com/behdad/fonttools
+    </namerecord>
+    <namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
+      https://github.com/behdad/fonttools
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x0" name=".null"/><!-- ???? -->
+      <map code="0xd" name="CR"/><!-- ???? -->
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x2e" name="period"/><!-- FULL STOP -->
+      <map code="0x2026" name="ellipsis"/><!-- HORIZONTAL ELLIPSIS -->
+    </cmap_format_4>
+    <cmap_format_6 platformID="1" platEncID="0" language="0">
+      <map code="0x0" name=".null"/>
+      <map code="0x1" name=".notdef"/>
+      <map code="0x2" name=".notdef"/>
+      <map code="0x3" name=".notdef"/>
+      <map code="0x4" name=".notdef"/>
+      <map code="0x5" name=".notdef"/>
+      <map code="0x6" name=".notdef"/>
+      <map code="0x7" name=".notdef"/>
+      <map code="0x8" name=".notdef"/>
+      <map code="0x9" name=".notdef"/>
+      <map code="0xa" name=".notdef"/>
+      <map code="0xb" name=".notdef"/>
+      <map code="0xc" name=".notdef"/>
+      <map code="0xd" name="CR"/>
+      <map code="0xe" name=".notdef"/>
+      <map code="0xf" name=".notdef"/>
+      <map code="0x10" name=".notdef"/>
+      <map code="0x11" name=".notdef"/>
+      <map code="0x12" name=".notdef"/>
+      <map code="0x13" name=".notdef"/>
+      <map code="0x14" name=".notdef"/>
+      <map code="0x15" name=".notdef"/>
+      <map code="0x16" name=".notdef"/>
+      <map code="0x17" name=".notdef"/>
+      <map code="0x18" name=".notdef"/>
+      <map code="0x19" name=".notdef"/>
+      <map code="0x1a" name=".notdef"/>
+      <map code="0x1b" name=".notdef"/>
+      <map code="0x1c" name=".notdef"/>
+      <map code="0x1d" name=".notdef"/>
+      <map code="0x1e" name=".notdef"/>
+      <map code="0x1f" name=".notdef"/>
+      <map code="0x20" name="space"/>
+      <map code="0x21" name=".notdef"/>
+      <map code="0x22" name=".notdef"/>
+      <map code="0x23" name=".notdef"/>
+      <map code="0x24" name=".notdef"/>
+      <map code="0x25" name=".notdef"/>
+      <map code="0x26" name=".notdef"/>
+      <map code="0x27" name=".notdef"/>
+      <map code="0x28" name=".notdef"/>
+      <map code="0x29" name=".notdef"/>
+      <map code="0x2a" name=".notdef"/>
+      <map code="0x2b" name=".notdef"/>
+      <map code="0x2c" name=".notdef"/>
+      <map code="0x2d" name=".notdef"/>
+      <map code="0x2e" name="period"/>
+      <map code="0x2f" name=".notdef"/>
+      <map code="0x30" name=".notdef"/>
+      <map code="0x31" name=".notdef"/>
+      <map code="0x32" name=".notdef"/>
+      <map code="0x33" name=".notdef"/>
+      <map code="0x34" name=".notdef"/>
+      <map code="0x35" name=".notdef"/>
+      <map code="0x36" name=".notdef"/>
+      <map code="0x37" name=".notdef"/>
+      <map code="0x38" name=".notdef"/>
+      <map code="0x39" name=".notdef"/>
+      <map code="0x3a" name=".notdef"/>
+      <map code="0x3b" name=".notdef"/>
+      <map code="0x3c" name=".notdef"/>
+      <map code="0x3d" name=".notdef"/>
+      <map code="0x3e" name=".notdef"/>
+      <map code="0x3f" name=".notdef"/>
+      <map code="0x40" name=".notdef"/>
+      <map code="0x41" name=".notdef"/>
+      <map code="0x42" name=".notdef"/>
+      <map code="0x43" name=".notdef"/>
+      <map code="0x44" name=".notdef"/>
+      <map code="0x45" name=".notdef"/>
+      <map code="0x46" name=".notdef"/>
+      <map code="0x47" name=".notdef"/>
+      <map code="0x48" name=".notdef"/>
+      <map code="0x49" name=".notdef"/>
+      <map code="0x4a" name=".notdef"/>
+      <map code="0x4b" name=".notdef"/>
+      <map code="0x4c" name=".notdef"/>
+      <map code="0x4d" name=".notdef"/>
+      <map code="0x4e" name=".notdef"/>
+      <map code="0x4f" name=".notdef"/>
+      <map code="0x50" name=".notdef"/>
+      <map code="0x51" name=".notdef"/>
+      <map code="0x52" name=".notdef"/>
+      <map code="0x53" name=".notdef"/>
+      <map code="0x54" name=".notdef"/>
+      <map code="0x55" name=".notdef"/>
+      <map code="0x56" name=".notdef"/>
+      <map code="0x57" name=".notdef"/>
+      <map code="0x58" name=".notdef"/>
+      <map code="0x59" name=".notdef"/>
+      <map code="0x5a" name=".notdef"/>
+      <map code="0x5b" name=".notdef"/>
+      <map code="0x5c" name=".notdef"/>
+      <map code="0x5d" name=".notdef"/>
+      <map code="0x5e" name=".notdef"/>
+      <map code="0x5f" name=".notdef"/>
+      <map code="0x60" name=".notdef"/>
+      <map code="0x61" name=".notdef"/>
+      <map code="0x62" name=".notdef"/>
+      <map code="0x63" name=".notdef"/>
+      <map code="0x64" name=".notdef"/>
+      <map code="0x65" name=".notdef"/>
+      <map code="0x66" name=".notdef"/>
+      <map code="0x67" name=".notdef"/>
+      <map code="0x68" name=".notdef"/>
+      <map code="0x69" name=".notdef"/>
+      <map code="0x6a" name=".notdef"/>
+      <map code="0x6b" name=".notdef"/>
+      <map code="0x6c" name=".notdef"/>
+      <map code="0x6d" name=".notdef"/>
+      <map code="0x6e" name=".notdef"/>
+      <map code="0x6f" name=".notdef"/>
+      <map code="0x70" name=".notdef"/>
+      <map code="0x71" name=".notdef"/>
+      <map code="0x72" name=".notdef"/>
+      <map code="0x73" name=".notdef"/>
+      <map code="0x74" name=".notdef"/>
+      <map code="0x75" name=".notdef"/>
+      <map code="0x76" name=".notdef"/>
+      <map code="0x77" name=".notdef"/>
+      <map code="0x78" name=".notdef"/>
+      <map code="0x79" name=".notdef"/>
+      <map code="0x7a" name=".notdef"/>
+      <map code="0x7b" name=".notdef"/>
+      <map code="0x7c" name=".notdef"/>
+      <map code="0x7d" name=".notdef"/>
+      <map code="0x7e" name=".notdef"/>
+      <map code="0x7f" name=".notdef"/>
+      <map code="0x80" name=".notdef"/>
+      <map code="0x81" name=".notdef"/>
+      <map code="0x82" name=".notdef"/>
+      <map code="0x83" name=".notdef"/>
+      <map code="0x84" name=".notdef"/>
+      <map code="0x85" name=".notdef"/>
+      <map code="0x86" name=".notdef"/>
+      <map code="0x87" name=".notdef"/>
+      <map code="0x88" name=".notdef"/>
+      <map code="0x89" name=".notdef"/>
+      <map code="0x8a" name=".notdef"/>
+      <map code="0x8b" name=".notdef"/>
+      <map code="0x8c" name=".notdef"/>
+      <map code="0x8d" name=".notdef"/>
+      <map code="0x8e" name=".notdef"/>
+      <map code="0x8f" name=".notdef"/>
+      <map code="0x90" name=".notdef"/>
+      <map code="0x91" name=".notdef"/>
+      <map code="0x92" name=".notdef"/>
+      <map code="0x93" name=".notdef"/>
+      <map code="0x94" name=".notdef"/>
+      <map code="0x95" name=".notdef"/>
+      <map code="0x96" name=".notdef"/>
+      <map code="0x97" name=".notdef"/>
+      <map code="0x98" name=".notdef"/>
+      <map code="0x99" name=".notdef"/>
+      <map code="0x9a" name=".notdef"/>
+      <map code="0x9b" name=".notdef"/>
+      <map code="0x9c" name=".notdef"/>
+      <map code="0x9d" name=".notdef"/>
+      <map code="0x9e" name=".notdef"/>
+      <map code="0x9f" name=".notdef"/>
+      <map code="0xa0" name=".notdef"/>
+      <map code="0xa1" name=".notdef"/>
+      <map code="0xa2" name=".notdef"/>
+      <map code="0xa3" name=".notdef"/>
+      <map code="0xa4" name=".notdef"/>
+      <map code="0xa5" name=".notdef"/>
+      <map code="0xa6" name=".notdef"/>
+      <map code="0xa7" name=".notdef"/>
+      <map code="0xa8" name=".notdef"/>
+      <map code="0xa9" name=".notdef"/>
+      <map code="0xaa" name=".notdef"/>
+      <map code="0xab" name=".notdef"/>
+      <map code="0xac" name=".notdef"/>
+      <map code="0xad" name=".notdef"/>
+      <map code="0xae" name=".notdef"/>
+      <map code="0xaf" name=".notdef"/>
+      <map code="0xb0" name=".notdef"/>
+      <map code="0xb1" name=".notdef"/>
+      <map code="0xb2" name=".notdef"/>
+      <map code="0xb3" name=".notdef"/>
+      <map code="0xb4" name=".notdef"/>
+      <map code="0xb5" name=".notdef"/>
+      <map code="0xb6" name=".notdef"/>
+      <map code="0xb7" name=".notdef"/>
+      <map code="0xb8" name=".notdef"/>
+      <map code="0xb9" name=".notdef"/>
+      <map code="0xba" name=".notdef"/>
+      <map code="0xbb" name=".notdef"/>
+      <map code="0xbc" name=".notdef"/>
+      <map code="0xbd" name=".notdef"/>
+      <map code="0xbe" name=".notdef"/>
+      <map code="0xbf" name=".notdef"/>
+      <map code="0xc0" name=".notdef"/>
+      <map code="0xc1" name=".notdef"/>
+      <map code="0xc2" name=".notdef"/>
+      <map code="0xc3" name=".notdef"/>
+      <map code="0xc4" name=".notdef"/>
+      <map code="0xc5" name=".notdef"/>
+      <map code="0xc6" name=".notdef"/>
+      <map code="0xc7" name=".notdef"/>
+      <map code="0xc8" name=".notdef"/>
+      <map code="0xc9" name="ellipsis"/>
+    </cmap_format_6>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x0" name=".null"/><!-- ???? -->
+      <map code="0xd" name="CR"/><!-- ???? -->
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x2e" name="period"/><!-- FULL STOP -->
+      <map code="0x2026" name="ellipsis"/><!-- HORIZONTAL ELLIPSIS -->
+    </cmap_format_4>
+  </cmap>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <CFF>
+    <CFFFont name="TestOTF-Regular">
+      <version value="001.001"/>
+      <Notice value="Copyright \(c\) 2015 by FontTools. No rights reserved."/>
+      <FullName value="Test OTF"/>
+      <FamilyName value="Test OTF"/>
+      <Weight value="Regular"/>
+      <isFixedPitch value="0"/>
+      <ItalicAngle value="0"/>
+      <UnderlineThickness value="50"/>
+      <PaintType value="0"/>
+      <CharstringType value="2"/>
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FontBBox value="50 0 668 750"/>
+      <StrokeWidth value="0"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <Encoding name="StandardEncoding"/>
+      <Private>
+        <BlueScale value="0.039625"/>
+        <BlueShift value="7"/>
+        <BlueFuzz value="1"/>
+        <ForceBold value="0"/>
+        <LanguageGroup value="0"/>
+        <ExpansionFactor value="0.06"/>
+        <initialRandomSeed value="0"/>
+        <defaultWidthX value="0"/>
+        <nominalWidthX value="0"/>
+        <Subrs>
+          <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+          <CharString index="0">
+            131 122 -131 hlineto
+            return
+          </CharString>
+        </Subrs>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          500 450 hmoveto
+          750 -400 -750 vlineto
+          50 50 rmoveto
+          650 300 -650 vlineto
+          endchar
+        </CharString>
+        <CharString name=".null">
+          0 endchar
+        </CharString>
+        <CharString name="CR">
+          250 endchar
+        </CharString>
+        <CharString name="ellipsis">
+          723 55 hmoveto
+          -107 callsubr
+          241 -122 rmoveto
+          -107 callsubr
+          241 -122 rmoveto
+          -107 callsubr
+          endchar
+        </CharString>
+        <CharString name="period">
+          241 55 hmoveto
+          -107 callsubr
+          endchar
+        </CharString>
+        <CharString name="space">
+          250 endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name=".null" width="0" lsb="0"/>
+    <mtx name="CR" width="250" lsb="0"/>
+    <mtx name="ellipsis" width="723" lsb="55"/>
+    <mtx name="period" width="241" lsb="55"/>
+    <mtx name="space" width="250" lsb="0"/>
+  </hmtx>
+
+  <DSIG>
+    <!-- note that the Digital Signature will be invalid after recompilation! -->
+    <tableHeader flag="0x0" numSigs="0" version="1"/>
+  </DSIG>
+
+</ttFont>
diff --git a/Tests/ttLib/data/TestTTF-Regular.ttx b/Tests/ttLib/data/TestTTF-Regular.ttx
new file mode 100644
index 0000000..8ffcefe
--- /dev/null
+++ b/Tests/ttLib/data/TestTTF-Regular.ttx
@@ -0,0 +1,553 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="2.5">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name=".null"/>
+    <GlyphID id="2" name="CR"/>
+    <GlyphID id="3" name="space"/>
+    <GlyphID id="4" name="period"/>
+    <GlyphID id="5" name="ellipsis"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x2ee689e2"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Jun  4 14:29:11 2015"/>
+    <modified value="Mon Aug  3 13:04:43 2015"/>
+    <xMin value="50"/>
+    <yMin value="0"/>
+    <xMax value="668"/>
+    <yMax value="750"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="9"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="900"/>
+    <descent value="-300"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="723"/>
+    <minLeftSideBearing value="50"/>
+    <minRightSideBearing value="50"/>
+    <xMaxExtent value="668"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="6"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="8"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="12"/>
+    <maxCompositeContours value="3"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="3"/>
+    <maxComponentDepth value="1"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="392"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="700"/>
+    <ySubscriptYSize value="650"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="140"/>
+    <ySuperscriptXSize value="700"/>
+    <ySuperscriptYSize value="650"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="477"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="250"/>
+    <sFamilyClass value="2050"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="6"/>
+      <bProportion value="4"/>
+      <bContrast value="4"/>
+      <bStrokeVariation value="2"/>
+      <bArmStyle value="7"/>
+      <bLetterForm value="8"/>
+      <bMidline value="1"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="10000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="NONE"/>
+    <fsSelection value="00000000 11000000"/>
+    <usFirstCharIndex value="0"/>
+    <usLastCharIndex value="8230"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="900"/>
+    <usWinDescent value="300"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name=".null" width="0" lsb="0"/>
+    <mtx name="CR" width="250" lsb="0"/>
+    <mtx name="ellipsis" width="723" lsb="55"/>
+    <mtx name="period" width="241" lsb="55"/>
+    <mtx name="space" width="250" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x0" name=".null"/><!-- ???? -->
+      <map code="0xd" name="CR"/><!-- ???? -->
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x2e" name="period"/><!-- FULL STOP -->
+      <map code="0x2026" name="ellipsis"/><!-- HORIZONTAL ELLIPSIS -->
+    </cmap_format_4>
+    <cmap_format_6 platformID="1" platEncID="0" language="0">
+      <map code="0x0" name=".null"/>
+      <map code="0x1" name=".notdef"/>
+      <map code="0x2" name=".notdef"/>
+      <map code="0x3" name=".notdef"/>
+      <map code="0x4" name=".notdef"/>
+      <map code="0x5" name=".notdef"/>
+      <map code="0x6" name=".notdef"/>
+      <map code="0x7" name=".notdef"/>
+      <map code="0x8" name=".notdef"/>
+      <map code="0x9" name=".notdef"/>
+      <map code="0xa" name=".notdef"/>
+      <map code="0xb" name=".notdef"/>
+      <map code="0xc" name=".notdef"/>
+      <map code="0xd" name="CR"/>
+      <map code="0xe" name=".notdef"/>
+      <map code="0xf" name=".notdef"/>
+      <map code="0x10" name=".notdef"/>
+      <map code="0x11" name=".notdef"/>
+      <map code="0x12" name=".notdef"/>
+      <map code="0x13" name=".notdef"/>
+      <map code="0x14" name=".notdef"/>
+      <map code="0x15" name=".notdef"/>
+      <map code="0x16" name=".notdef"/>
+      <map code="0x17" name=".notdef"/>
+      <map code="0x18" name=".notdef"/>
+      <map code="0x19" name=".notdef"/>
+      <map code="0x1a" name=".notdef"/>
+      <map code="0x1b" name=".notdef"/>
+      <map code="0x1c" name=".notdef"/>
+      <map code="0x1d" name=".notdef"/>
+      <map code="0x1e" name=".notdef"/>
+      <map code="0x1f" name=".notdef"/>
+      <map code="0x20" name="space"/>
+      <map code="0x21" name=".notdef"/>
+      <map code="0x22" name=".notdef"/>
+      <map code="0x23" name=".notdef"/>
+      <map code="0x24" name=".notdef"/>
+      <map code="0x25" name=".notdef"/>
+      <map code="0x26" name=".notdef"/>
+      <map code="0x27" name=".notdef"/>
+      <map code="0x28" name=".notdef"/>
+      <map code="0x29" name=".notdef"/>
+      <map code="0x2a" name=".notdef"/>
+      <map code="0x2b" name=".notdef"/>
+      <map code="0x2c" name=".notdef"/>
+      <map code="0x2d" name=".notdef"/>
+      <map code="0x2e" name="period"/>
+      <map code="0x2f" name=".notdef"/>
+      <map code="0x30" name=".notdef"/>
+      <map code="0x31" name=".notdef"/>
+      <map code="0x32" name=".notdef"/>
+      <map code="0x33" name=".notdef"/>
+      <map code="0x34" name=".notdef"/>
+      <map code="0x35" name=".notdef"/>
+      <map code="0x36" name=".notdef"/>
+      <map code="0x37" name=".notdef"/>
+      <map code="0x38" name=".notdef"/>
+      <map code="0x39" name=".notdef"/>
+      <map code="0x3a" name=".notdef"/>
+      <map code="0x3b" name=".notdef"/>
+      <map code="0x3c" name=".notdef"/>
+      <map code="0x3d" name=".notdef"/>
+      <map code="0x3e" name=".notdef"/>
+      <map code="0x3f" name=".notdef"/>
+      <map code="0x40" name=".notdef"/>
+      <map code="0x41" name=".notdef"/>
+      <map code="0x42" name=".notdef"/>
+      <map code="0x43" name=".notdef"/>
+      <map code="0x44" name=".notdef"/>
+      <map code="0x45" name=".notdef"/>
+      <map code="0x46" name=".notdef"/>
+      <map code="0x47" name=".notdef"/>
+      <map code="0x48" name=".notdef"/>
+      <map code="0x49" name=".notdef"/>
+      <map code="0x4a" name=".notdef"/>
+      <map code="0x4b" name=".notdef"/>
+      <map code="0x4c" name=".notdef"/>
+      <map code="0x4d" name=".notdef"/>
+      <map code="0x4e" name=".notdef"/>
+      <map code="0x4f" name=".notdef"/>
+      <map code="0x50" name=".notdef"/>
+      <map code="0x51" name=".notdef"/>
+      <map code="0x52" name=".notdef"/>
+      <map code="0x53" name=".notdef"/>
+      <map code="0x54" name=".notdef"/>
+      <map code="0x55" name=".notdef"/>
+      <map code="0x56" name=".notdef"/>
+      <map code="0x57" name=".notdef"/>
+      <map code="0x58" name=".notdef"/>
+      <map code="0x59" name=".notdef"/>
+      <map code="0x5a" name=".notdef"/>
+      <map code="0x5b" name=".notdef"/>
+      <map code="0x5c" name=".notdef"/>
+      <map code="0x5d" name=".notdef"/>
+      <map code="0x5e" name=".notdef"/>
+      <map code="0x5f" name=".notdef"/>
+      <map code="0x60" name=".notdef"/>
+      <map code="0x61" name=".notdef"/>
+      <map code="0x62" name=".notdef"/>
+      <map code="0x63" name=".notdef"/>
+      <map code="0x64" name=".notdef"/>
+      <map code="0x65" name=".notdef"/>
+      <map code="0x66" name=".notdef"/>
+      <map code="0x67" name=".notdef"/>
+      <map code="0x68" name=".notdef"/>
+      <map code="0x69" name=".notdef"/>
+      <map code="0x6a" name=".notdef"/>
+      <map code="0x6b" name=".notdef"/>
+      <map code="0x6c" name=".notdef"/>
+      <map code="0x6d" name=".notdef"/>
+      <map code="0x6e" name=".notdef"/>
+      <map code="0x6f" name=".notdef"/>
+      <map code="0x70" name=".notdef"/>
+      <map code="0x71" name=".notdef"/>
+      <map code="0x72" name=".notdef"/>
+      <map code="0x73" name=".notdef"/>
+      <map code="0x74" name=".notdef"/>
+      <map code="0x75" name=".notdef"/>
+      <map code="0x76" name=".notdef"/>
+      <map code="0x77" name=".notdef"/>
+      <map code="0x78" name=".notdef"/>
+      <map code="0x79" name=".notdef"/>
+      <map code="0x7a" name=".notdef"/>
+      <map code="0x7b" name=".notdef"/>
+      <map code="0x7c" name=".notdef"/>
+      <map code="0x7d" name=".notdef"/>
+      <map code="0x7e" name=".notdef"/>
+      <map code="0x7f" name=".notdef"/>
+      <map code="0x80" name=".notdef"/>
+      <map code="0x81" name=".notdef"/>
+      <map code="0x82" name=".notdef"/>
+      <map code="0x83" name=".notdef"/>
+      <map code="0x84" name=".notdef"/>
+      <map code="0x85" name=".notdef"/>
+      <map code="0x86" name=".notdef"/>
+      <map code="0x87" name=".notdef"/>
+      <map code="0x88" name=".notdef"/>
+      <map code="0x89" name=".notdef"/>
+      <map code="0x8a" name=".notdef"/>
+      <map code="0x8b" name=".notdef"/>
+      <map code="0x8c" name=".notdef"/>
+      <map code="0x8d" name=".notdef"/>
+      <map code="0x8e" name=".notdef"/>
+      <map code="0x8f" name=".notdef"/>
+      <map code="0x90" name=".notdef"/>
+      <map code="0x91" name=".notdef"/>
+      <map code="0x92" name=".notdef"/>
+      <map code="0x93" name=".notdef"/>
+      <map code="0x94" name=".notdef"/>
+      <map code="0x95" name=".notdef"/>
+      <map code="0x96" name=".notdef"/>
+      <map code="0x97" name=".notdef"/>
+      <map code="0x98" name=".notdef"/>
+      <map code="0x99" name=".notdef"/>
+      <map code="0x9a" name=".notdef"/>
+      <map code="0x9b" name=".notdef"/>
+      <map code="0x9c" name=".notdef"/>
+      <map code="0x9d" name=".notdef"/>
+      <map code="0x9e" name=".notdef"/>
+      <map code="0x9f" name=".notdef"/>
+      <map code="0xa0" name=".notdef"/>
+      <map code="0xa1" name=".notdef"/>
+      <map code="0xa2" name=".notdef"/>
+      <map code="0xa3" name=".notdef"/>
+      <map code="0xa4" name=".notdef"/>
+      <map code="0xa5" name=".notdef"/>
+      <map code="0xa6" name=".notdef"/>
+      <map code="0xa7" name=".notdef"/>
+      <map code="0xa8" name=".notdef"/>
+      <map code="0xa9" name=".notdef"/>
+      <map code="0xaa" name=".notdef"/>
+      <map code="0xab" name=".notdef"/>
+      <map code="0xac" name=".notdef"/>
+      <map code="0xad" name=".notdef"/>
+      <map code="0xae" name=".notdef"/>
+      <map code="0xaf" name=".notdef"/>
+      <map code="0xb0" name=".notdef"/>
+      <map code="0xb1" name=".notdef"/>
+      <map code="0xb2" name=".notdef"/>
+      <map code="0xb3" name=".notdef"/>
+      <map code="0xb4" name=".notdef"/>
+      <map code="0xb5" name=".notdef"/>
+      <map code="0xb6" name=".notdef"/>
+      <map code="0xb7" name=".notdef"/>
+      <map code="0xb8" name=".notdef"/>
+      <map code="0xb9" name=".notdef"/>
+      <map code="0xba" name=".notdef"/>
+      <map code="0xbb" name=".notdef"/>
+      <map code="0xbc" name=".notdef"/>
+      <map code="0xbd" name=".notdef"/>
+      <map code="0xbe" name=".notdef"/>
+      <map code="0xbf" name=".notdef"/>
+      <map code="0xc0" name=".notdef"/>
+      <map code="0xc1" name=".notdef"/>
+      <map code="0xc2" name=".notdef"/>
+      <map code="0xc3" name=".notdef"/>
+      <map code="0xc4" name=".notdef"/>
+      <map code="0xc5" name=".notdef"/>
+      <map code="0xc6" name=".notdef"/>
+      <map code="0xc7" name=".notdef"/>
+      <map code="0xc8" name=".notdef"/>
+      <map code="0xc9" name="ellipsis"/>
+    </cmap_format_6>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x0" name=".null"/><!-- ???? -->
+      <map code="0xd" name="CR"/><!-- ???? -->
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x2e" name="period"/><!-- FULL STOP -->
+      <map code="0x2026" name="ellipsis"/><!-- HORIZONTAL ELLIPSIS -->
+    </cmap_format_4>
+  </cmap>
+
+  <fpgm>
+    <assembly>
+      SVTCA[0]  /* SetFPVectorToAxis */
+    </assembly>
+  </fpgm>
+
+  <prep>
+    <assembly>
+      SVTCA[0]  /* SetFPVectorToAxis */
+    </assembly>
+  </prep>
+
+  <cvt>
+    <cv index="0" value="0"/>
+  </cvt>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="50" yMin="0" xMax="450" yMax="750">
+      <contour>
+        <pt x="50" y="0" on="1"/>
+        <pt x="50" y="750" on="1"/>
+        <pt x="450" y="750" on="1"/>
+        <pt x="450" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="400" y="50" on="1"/>
+        <pt x="400" y="700" on="1"/>
+        <pt x="100" y="700" on="1"/>
+        <pt x="100" y="50" on="1"/>
+      </contour>
+      <instructions><assembly>
+          SVTCA[0]  /* SetFPVectorToAxis */
+          SVTCA[1]  /* SetFPVectorToAxis */
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name=".null"/><!-- contains no outline data -->
+
+    <TTGlyph name="CR"/><!-- contains no outline data -->
+
+    <TTGlyph name="ellipsis" xMin="55" yMin="0" xMax="668" yMax="122">
+      <component glyphName="period" x="0" y="0" flags="0x4"/>
+      <component glyphName="period" x="241" y="0" flags="0x4"/>
+      <component glyphName="period" x="482" y="0" flags="0x4"/>
+      <instructions><assembly>
+          SVTCA[0]  /* SetFPVectorToAxis */
+          SVTCA[1]  /* SetFPVectorToAxis */
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="period" xMin="55" yMin="0" xMax="186" yMax="122">
+      <contour>
+        <pt x="55" y="122" on="1"/>
+        <pt x="186" y="122" on="1"/>
+        <pt x="186" y="0" on="1"/>
+        <pt x="55" y="0" on="1"/>
+      </contour>
+      <instructions><assembly>
+          SVTCA[0]  /* SetFPVectorToAxis */
+          SVTCA[1]  /* SetFPVectorToAxis */
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Copyright (c) 2015 by FontTools. No rights reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Test TTF
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      FontTools: Test TTF: 2015
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Test TTF
+    </namerecord>
+    <namerecord nameID="5" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestTTF-Regular
+    </namerecord>
+    <namerecord nameID="7" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Test TTF is not a trademark of FontTools.
+    </namerecord>
+    <namerecord nameID="8" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      FontTools
+    </namerecord>
+    <namerecord nameID="9" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      FontTools
+    </namerecord>
+    <namerecord nameID="11" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      https://github.com/behdad/fonttools
+    </namerecord>
+    <namerecord nameID="12" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      https://github.com/behdad/fonttools
+    </namerecord>
+    <namerecord nameID="14" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+    </namerecord>
+    <namerecord nameID="18" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Test TTF
+    </namerecord>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (c) 2015 by FontTools. No rights reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test TTF
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      FontTools: Test TTF: 2015
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test TTF
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestTTF-Regular
+    </namerecord>
+    <namerecord nameID="7" platformID="3" platEncID="1" langID="0x409">
+      Test TTF is not a trademark of FontTools.
+    </namerecord>
+    <namerecord nameID="8" platformID="3" platEncID="1" langID="0x409">
+      FontTools
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      FontTools
+    </namerecord>
+    <namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
+      https://github.com/behdad/fonttools
+    </namerecord>
+    <namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
+      https://github.com/behdad/fonttools
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name=".null"/>
+      <psName name="CR"/>
+    </extraNames>
+  </post>
+
+  <gasp>
+    <gaspRange rangeMaxPPEM="8" rangeGaspBehavior="10"/>
+    <gaspRange rangeMaxPPEM="65535" rangeGaspBehavior="15"/>
+  </gasp>
+
+  <DSIG>
+    <!-- note that the Digital Signature will be invalid after recompilation! -->
+    <tableHeader flag="0x0" numSigs="0" version="1"/>
+  </DSIG>
+
+</ttFont>
diff --git a/Tests/ttLib/data/TestTTFComplex-Regular.ttx b/Tests/ttLib/data/TestTTFComplex-Regular.ttx
new file mode 100644
index 0000000..00e89cd
--- /dev/null
+++ b/Tests/ttLib/data/TestTTFComplex-Regular.ttx
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="2.5">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name=".null"/>
+    <GlyphID id="2" name="CR"/>
+    <GlyphID id="3" name="space"/>
+    <GlyphID id="4" name="period"/>
+    <GlyphID id="5" name="ellipsis"/>
+  </GlyphOrder>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="8"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="12"/>
+    <maxCompositeContours value="3"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="3"/>
+    <maxComponentDepth value="1"/>
+  </maxp>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name=".null" width="0" lsb="0"/>
+    <mtx name="CR" width="250" lsb="0"/>
+    <mtx name="ellipsis" width="723" lsb="55"/>
+    <mtx name="period" width="241" lsb="55"/>
+    <mtx name="space" width="250" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x0" name=".null"/><!-- ???? -->
+      <map code="0xd" name="CR"/><!-- ???? -->
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x2e" name="period"/><!-- FULL STOP -->
+      <map code="0x2026" name="ellipsis"/><!-- HORIZONTAL ELLIPSIS -->
+    </cmap_format_4>
+  </cmap>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="50" yMin="0" xMax="450" yMax="750">
+      <contour>
+        <pt x="250" y="0" on="1"/>
+        <pt x="50" y="0" on="0"/>
+        <pt x="50" y="375" on="1"/>
+        <pt x="50" y="750" on="0"/>
+        <pt x="250" y="750" on="1"/>
+        <pt x="450" y="750" on="0"/>
+        <pt x="450" y="375" on="1"/>
+        <pt x="450" y="0" on="0"/>
+      </contour>
+      <contour>
+        <pt x="250" y="50" on="1"/>
+        <pt x="300" y="50" on="0"/>
+        <pt x="400" y="325" on="0"/>
+        <pt x="400" y="375" on="1"/>
+        <pt x="400" y="425" on="0"/>
+        <pt x="300" y="700" on="0"/>
+        <pt x="250" y="700" on="1"/>
+        <pt x="200" y="700" on="0"/>
+        <pt x="100" y="425" on="0"/>
+        <pt x="100" y="375" on="1"/>
+        <pt x="100" y="325" on="0"/>
+        <pt x="200" y="50" on="0"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name=".null"/><!-- contains no outline data -->
+
+    <TTGlyph name="CR"/><!-- contains no outline data -->
+
+    <TTGlyph name="ellipsis" xMin="55" yMin="0" xMax="668" yMax="122">
+      <component glyphName="period" x="0" y="0" scale="1.5" flags="0x4"/>
+      <component glyphName="period" x="241" y="0" scale="1.5" flags="0x4"/>
+      <component glyphName="period" x="482" y="0" scale="1.5" flags="0x4"/>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="period" xMin="55" yMin="0" xMax="186" yMax="122">
+      <contour>
+        <pt x="55" y="122" on="0"/>
+        <pt x="186" y="122" on="0"/>
+        <pt x="186" y="0" on="0"/>
+        <pt x="55" y="0" on="0"/>
+      </contour>
+      <instructions><assembly>
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+  </glyf>
+
+</ttFont>
diff --git a/Tests/ttLib/data/test_woff2_metadata.xml b/Tests/ttLib/data/test_woff2_metadata.xml
new file mode 100644
index 0000000..9ab26a4
--- /dev/null
+++ b/Tests/ttLib/data/test_woff2_metadata.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<metadata version="1.0">
+	<uniqueid id="org.w3.webfonts.wofftest" />
+	<vendor name="Test Vendor" url="http://w3c.org/Fonts" />
+	<credits>
+		<credit name="Credit 1" role="Role 1" url="http://w3c.org/Fonts" />
+		<credit name="Credit 2" role="Role 2" url="http://w3c.org/Fonts" />
+	</credits>
+	<description url="http://w3c.org/Fonts">
+		<text>
+			Description without language.
+		</text>
+		<text lang="en">
+			Description with "en" language.
+		</text>
+		<text lang="fr">
+			Description with "fr" language.
+		</text>
+	</description>
+	<license url="http://w3c.org/Fonts" id="License ID">
+		<text>
+			License without language.
+		</text>
+		<text lang="en">
+			License with "en" language.
+		</text>
+		<text lang="fr">
+			License with "fr" language.
+		</text>
+	</license>
+	<copyright>
+		<text>
+			Copyright without language.
+		</text>
+		<text lang="en">
+			Copyright with "en" language.
+		</text>
+		<text lang="fr">
+			Copyright with "fr" language.
+		</text>
+	</copyright>
+	<trademark>
+		<text>
+			Trademark without language.
+		</text>
+		<text lang="en">
+			Trademark with "en" language.
+		</text>
+		<text lang="fr">
+			Trademark with "fr" language.
+		</text>
+	</trademark>
+	<licensee name="Licensee Name" />
+	<extension id="Extension 1">
+		<name>Extension 1 - Name Without Language</name>
+		<name lang="en">Extension 1 - Name With "en" Language</name>
+		<name lang="fr">Extension 1 - Name With "fr" Language</name>
+		<item id="Extension 1 - Item 1 ID">
+			<name>Extension 1 - Item 1 - Name Without Language</name>
+			<name lang="en">Extension 1 - Item 1 - Name With "en" Language</name>
+			<name lang="fr">Extension 1 - Item 1 - Name With "fr" Language</name>
+			<value>Extension 1 - Item 1 - Value Without Language</value>
+			<value lang="en">Extension 1 - Item 1 - Value With "en" Language</value>
+			<value lang="fr">Extension 1 - Item 1 - Value With "fr" Language</value>
+		</item>
+		<item id="Extension 1 - Item 2 ID">
+			<name>Extension 1 - Item 2 - Name Without Language</name>
+			<name lang="en">Extension 1 - Item 2 - Name With "en" Language</name>
+			<name lang="fr">Extension 1 - Item 2 - Name With "fr" Language</name>
+			<value>Extension 1 - Item 2 - Value Without Language</value>
+			<value lang="en">Extension 1 - Item 2 - Value With "en" Language</value>
+			<value lang="fr">Extension 1 - Item 2 - Value With "fr" Language</value>
+		</item>
+	</extension>
+	<extension id="Extension 2">
+		<name>Extension 2 - Name Without Language</name>
+		<name lang="en">Extension 2 - Name With "en" Language</name>
+		<name lang="fr">Extension 2 - Name With "fr" Language</name>
+		<item id="Extension 2 - Item 1 ID">
+			<name>Extension 2 - Item 1 - Name Without Language</name>
+			<name lang="en">Extension 2 - Item 1 - Name With "en" Language</name>
+			<name lang="fr">Extension 2 - Item 1 - Name With "fr" Language</name>
+			<value>Extension 2 - Item 1 - Value Without Language</value>
+			<value lang="en">Extension 2 - Item 1 - Value With "en" Language</value>
+			<value lang="fr">Extension 2 - Item 1 - Value With "fr" Language</value>
+		</item>
+		<item id="Extension 2 - Item 2 ID">
+			<name>Extension 2 - Item 2 - Name Without Language</name>
+			<name lang="en">Extension 2 - Item 2 - Name With "en" Language</name>
+			<name lang="fr">Extension 2 - Item 2 - Name With "fr" Language</name>
+			<value>Extension 2 - Item 2 - Value Without Language</value>
+			<value lang="en">Extension 2 - Item 2 - Value With "en" Language</value>
+			<value lang="fr">Extension 2 - Item 2 - Value With "fr" Language</value>
+		</item>
+		<item id="Extension 2 - Item 3 ID">
+			<name>Extension 2 - Item 3 - Name Without Language</name>
+			<name lang="en">Extension 2 - Item 3 - Name With "en" Language</name>
+			<name lang="fr">Extension 2 - Item 3 - Name With "fr" Language</name>
+			<value>Extension 2 - Item 3 - Value Without Language</value>
+			<value lang="en">Extension 2 - Item 3 - Value With "en" Language</value>
+		</item>
+	</extension>
+</metadata>
diff --git a/Tests/ttLib/sfnt_test.py b/Tests/ttLib/sfnt_test.py
new file mode 100644
index 0000000..2119351
--- /dev/null
+++ b/Tests/ttLib/sfnt_test.py
@@ -0,0 +1,8 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.ttLib.sfnt import calcChecksum
+
+
+def test_calcChecksum():
+    assert calcChecksum(b"abcd") == 1633837924
+    assert calcChecksum(b"abcdxyz") == 3655064932
diff --git a/Tests/ttLib/tables/C_F_F__2_test.py b/Tests/ttLib/tables/C_F_F__2_test.py
new file mode 100644
index 0000000..191f60b
--- /dev/null
+++ b/Tests/ttLib/tables/C_F_F__2_test.py
@@ -0,0 +1,60 @@
+"""cff2Lib_test.py -- unit test for Adobe CFF fonts."""
+
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.ttLib import TTFont, newTable
+import re
+import os
+import unittest
+
+
+CURR_DIR = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
+DATA_DIR = os.path.join(CURR_DIR, 'data')
+
+CFF_TTX = os.path.join(DATA_DIR, "C_F_F__2.ttx")
+CFF_BIN = os.path.join(DATA_DIR, "C_F_F__2.bin")
+
+
+def strip_VariableItems(string):
+    # ttlib changes with the fontTools version
+    string = re.sub(' ttLibVersion=".*"', '', string)
+    # head table checksum and mod date changes with each save.
+    string = re.sub('<checkSumAdjustment value="[^"]+"/>', '', string)
+    string = re.sub('<modified value="[^"]+"/>', '', string)
+    return string
+
+class CFFTableTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        with open(CFF_BIN, 'rb') as f:
+            font = TTFont(file=CFF_BIN)
+            cffTable = font['CFF2']
+            cls.cff2Data = cffTable.compile(font)
+        with open(CFF_TTX, 'r') as f:
+            cff2XML = f.read()
+            cff2XML = strip_VariableItems(cff2XML)
+            cls.cff2XML = cff2XML.splitlines()
+
+    def test_toXML(self):
+        font = TTFont(file=CFF_BIN)
+        cffTable = font['CFF2']
+        cffData = cffTable.compile(font)
+        out = UnicodeIO()
+        font.saveXML(out)
+        cff2XML = out.getvalue()
+        cff2XML = strip_VariableItems(cff2XML)
+        cff2XML = cff2XML.splitlines()
+        self.assertEqual(cff2XML, self.cff2XML)
+
+    def test_fromXML(self):
+        font = TTFont(sfntVersion='OTTO')
+        font.importXML(CFF_TTX)
+        cffTable = font['CFF2']
+        cff2Data = cffTable.compile(font)
+        self.assertEqual(cff2Data, self.cff2Data)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/Tests/ttLib/tables/C_F_F_test.py b/Tests/ttLib/tables/C_F_F_test.py
new file mode 100644
index 0000000..63f767c
--- /dev/null
+++ b/Tests/ttLib/tables/C_F_F_test.py
@@ -0,0 +1,51 @@
+"""cffLib_test.py -- unit test for Adobe CFF fonts."""
+
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.ttLib import TTFont, newTable
+import re
+import os
+import unittest
+
+
+CURR_DIR = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
+DATA_DIR = os.path.join(CURR_DIR, 'data')
+
+CFF_TTX = os.path.join(DATA_DIR, "C_F_F_.ttx")
+CFF_BIN = os.path.join(DATA_DIR, "C_F_F_.bin")
+
+
+def strip_ttLibVersion(string):
+    return re.sub(' ttLibVersion=".*"', '', string)
+
+
+class CFFTableTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        with open(CFF_BIN, 'rb') as f:
+            cls.cffData = f.read()
+        with open(CFF_TTX, 'r') as f:
+            cls.cffXML = strip_ttLibVersion(f.read()).splitlines()
+
+    def test_toXML(self):
+        font = TTFont(sfntVersion='OTTO')
+        cffTable = font['CFF '] = newTable('CFF ')
+        cffTable.decompile(self.cffData, font)
+        out = UnicodeIO()
+        font.saveXML(out)
+        cffXML = strip_ttLibVersion(out.getvalue()).splitlines()
+        self.assertEqual(cffXML, self.cffXML)
+
+    def test_fromXML(self):
+        font = TTFont(sfntVersion='OTTO')
+        font.importXML(CFF_TTX)
+        cffTable = font['CFF ']
+        cffData = cffTable.compile(font)
+        self.assertEqual(cffData, self.cffData)
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/C_P_A_L_test.py b/Tests/ttLib/tables/C_P_A_L_test.py
new file mode 100644
index 0000000..bacd4a8
--- /dev/null
+++ b/Tests/ttLib/tables/C_P_A_L_test.py
@@ -0,0 +1,227 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.testTools import getXML, parseXML
+from fontTools.misc.textTools import deHexStr
+from fontTools.ttLib import getTableModule, newTable
+import unittest
+
+
+CPAL_DATA_V0 = deHexStr(
+    '0000 0002 '          # version=0, numPaletteEntries=2
+    '0002 0004 '          # numPalettes=2, numColorRecords=4
+    '00000010 '           # offsetToFirstColorRecord=16
+    '0000 0002 '          # colorRecordIndex=[0, 2]
+    '000000FF FFCC66FF '  # colorRecord #0, #1 (blue/green/red/alpha)
+    '000000FF 000080FF')  # colorRecord #2, #3
+
+
+CPAL_DATA_V0_SHARING_COLORS = deHexStr(
+    '0000 0003 '                   # version=0, numPaletteEntries=3
+    '0004 0006 '                   # numPalettes=4, numColorRecords=6
+    '00000014 '                    # offsetToFirstColorRecord=20
+    '0000 0000 0003 0000 '         # colorRecordIndex=[0, 0, 3, 0]
+    '443322FF 77889911 55555555 '  # colorRecord #0, #1, #2 (BGRA)
+    '443322FF 77889911 FFFFFFFF')  # colorRecord #3, #4, #5
+
+
+CPAL_DATA_V1_NOLABELS_NOTYPES = deHexStr(
+    '0001 0003 '                   # version=1, numPaletteEntries=3
+    '0002 0006 '                   # numPalettes=2, numColorRecords=6
+    '0000001C  '                   # offsetToFirstColorRecord=28
+    '0000 0003 '                   # colorRecordIndex=[0, 3]
+    '00000000  '                   # offsetToPaletteTypeArray=0
+    '00000000  '                   # offsetToPaletteLabelArray=0
+    '00000000  '                   # offsetToPaletteEntryLabelArray=0
+    'CAFECAFE 00112233 44556677 '  # colorRecord #0, #1, #2 (BGRA)
+    '31415927 42424242 00331337')  # colorRecord #3, #4, #5
+
+
+CPAL_DATA_V1 = deHexStr(
+    '0001 0003 '                   # version=1, numPaletteEntries=3
+    '0002 0006 '                   # numPalettes=2, numColorRecords=6
+    '0000001C  '                   # offsetToFirstColorRecord=28
+    '0000 0003 '                   # colorRecordIndex=[0, 3]
+    '00000034  '                   # offsetToPaletteTypeArray=52
+    '0000003C  '                   # offsetToPaletteLabelArray=60
+    '00000040  '                   # offsetToPaletteEntryLabelArray=64
+    'CAFECAFE 00112233 44556677 '  # colorRecord #0, #1, #2 (BGRA)
+    '31415927 42424242 00331337 '  # colorRecord #3, #4, #5
+    '00000001 00000002 '           # paletteType=[1, 2]
+    '0102 0103 '                   # paletteLabel=[258, 259]
+    '0201 0202 0203')              # paletteEntryLabel=[513, 514, 515]
+
+
+class FakeNameTable(object):
+    def __init__(self, names):
+        self.names = names
+
+    def getDebugName(self, nameID):
+        return self.names.get(nameID)
+
+
+class CPALTest(unittest.TestCase):
+    def test_decompile_v0(self):
+        cpal = newTable('CPAL')
+        cpal.decompile(CPAL_DATA_V0, ttFont=None)
+        self.assertEqual(cpal.version, 0)
+        self.assertEqual(cpal.numPaletteEntries, 2)
+        self.assertEqual(repr(cpal.palettes),
+                         '[[#000000FF, #66CCFFFF], [#000000FF, #800000FF]]')
+        self.assertEqual(cpal.paletteLabels, [0, 0])
+        self.assertEqual(cpal.paletteTypes, [0, 0])
+        self.assertEqual(cpal.paletteEntryLabels, [0, 0])
+
+    def test_decompile_v0_sharingColors(self):
+        cpal = newTable('CPAL')
+        cpal.decompile(CPAL_DATA_V0_SHARING_COLORS, ttFont=None)
+        self.assertEqual(cpal.version, 0)
+        self.assertEqual(cpal.numPaletteEntries, 3)
+        self.assertEqual([repr(p) for p in cpal.palettes], [
+            '[#223344FF, #99887711, #55555555]',
+            '[#223344FF, #99887711, #55555555]',
+            '[#223344FF, #99887711, #FFFFFFFF]',
+            '[#223344FF, #99887711, #55555555]'])
+        self.assertEqual(cpal.paletteLabels, [0, 0, 0, 0])
+        self.assertEqual(cpal.paletteTypes, [0, 0, 0, 0])
+        self.assertEqual(cpal.paletteEntryLabels, [0, 0, 0])
+
+    def test_decompile_v1_noLabelsNoTypes(self):
+        cpal = newTable('CPAL')
+        cpal.decompile(CPAL_DATA_V1_NOLABELS_NOTYPES, ttFont=None)
+        self.assertEqual(cpal.version, 1)
+        self.assertEqual(cpal.numPaletteEntries, 3)
+        self.assertEqual([repr(p) for p in cpal.palettes], [
+            '[#CAFECAFE, #22110033, #66554477]',  # RGBA
+            '[#59413127, #42424242, #13330037]'])
+        self.assertEqual(cpal.paletteLabels, [0, 0])
+        self.assertEqual(cpal.paletteTypes, [0, 0])
+        self.assertEqual(cpal.paletteEntryLabels, [0, 0, 0])
+
+    def test_decompile_v1(self):
+        cpal = newTable('CPAL')
+        cpal.decompile(CPAL_DATA_V1, ttFont=None)
+        self.assertEqual(cpal.version, 1)
+        self.assertEqual(cpal.numPaletteEntries, 3)
+        self.assertEqual([repr(p) for p in cpal.palettes], [
+            '[#CAFECAFE, #22110033, #66554477]',  # RGBA
+            '[#59413127, #42424242, #13330037]'])
+        self.assertEqual(cpal.paletteTypes, [1, 2])
+        self.assertEqual(cpal.paletteLabels, [258, 259])
+        self.assertEqual(cpal.paletteEntryLabels, [513, 514, 515])
+
+    def test_compile_v0(self):
+        cpal = newTable('CPAL')
+        cpal.decompile(CPAL_DATA_V0, ttFont=None)
+        self.assertEqual(cpal.compile(ttFont=None), CPAL_DATA_V0)
+
+    def test_compile_v0_sharingColors(self):
+        cpal = newTable('CPAL')
+        cpal.version = 0
+        Color = getTableModule('CPAL').Color
+        palette1 = [Color(red=0x22, green=0x33, blue=0x44, alpha=0xff),
+                    Color(red=0x99, green=0x88, blue=0x77, alpha=0x11),
+                    Color(red=0x55, green=0x55, blue=0x55, alpha=0x55)]
+        palette2 = [Color(red=0x22, green=0x33, blue=0x44, alpha=0xff),
+                    Color(red=0x99, green=0x88, blue=0x77, alpha=0x11),
+                    Color(red=0xFF, green=0xFF, blue=0xFF, alpha=0xFF)]
+        cpal.numPaletteEntries = len(palette1)
+        cpal.palettes = [palette1, palette1, palette2, palette1]
+        self.assertEqual(cpal.compile(ttFont=None),
+                         CPAL_DATA_V0_SHARING_COLORS)
+
+    def test_compile_v1(self):
+        cpal = newTable('CPAL')
+        cpal.decompile(CPAL_DATA_V1, ttFont=None)
+        self.assertEqual(cpal.compile(ttFont=None), CPAL_DATA_V1)
+
+    def test_compile_v1_noLabelsNoTypes(self):
+        cpal = newTable('CPAL')
+        cpal.decompile(CPAL_DATA_V1_NOLABELS_NOTYPES, ttFont=None)
+        self.assertEqual(cpal.compile(ttFont=None),
+                         CPAL_DATA_V1_NOLABELS_NOTYPES)
+
+    def test_toXML_v0(self):
+        cpal = newTable('CPAL')
+        cpal.decompile(CPAL_DATA_V0, ttFont=None)
+        self.assertEqual(getXML(cpal.toXML),
+                         ['<version value="0"/>',
+                          '<numPaletteEntries value="2"/>',
+                          '<palette index="0">',
+                          '  <color index="0" value="#000000FF"/>',
+                          '  <color index="1" value="#66CCFFFF"/>',
+                          '</palette>',
+                          '<palette index="1">',
+                          '  <color index="0" value="#000000FF"/>',
+                          '  <color index="1" value="#800000FF"/>',
+                          '</palette>'])
+
+    def test_toXML_v1(self):
+        name = FakeNameTable({258: "Spring theme", 259: "Winter theme",
+                              513: "darks", 515: "lights"})
+        cpal = newTable('CPAL')
+        ttFont = {"name": name, "CPAL": cpal}
+        cpal.decompile(CPAL_DATA_V1, ttFont)
+        self.assertEqual(getXML(cpal.toXML, ttFont),
+                         ['<version value="1"/>',
+                          '<numPaletteEntries value="3"/>',
+                          '<palette index="0" label="258" type="1">',
+                          '  <!-- Spring theme -->',
+                          '  <color index="0" value="#CAFECAFE"/>',
+                          '  <color index="1" value="#22110033"/>',
+                          '  <color index="2" value="#66554477"/>',
+                          '</palette>',
+                          '<palette index="1" label="259" type="2">',
+                          '  <!-- Winter theme -->',
+                          '  <color index="0" value="#59413127"/>',
+                          '  <color index="1" value="#42424242"/>',
+                          '  <color index="2" value="#13330037"/>',
+                          '</palette>',
+                          '<paletteEntryLabels>',
+                          '  <label index="0" value="513"/><!-- darks -->',
+                          '  <label index="1" value="514"/>',
+                          '  <label index="2" value="515"/><!-- lights -->',
+                          '</paletteEntryLabels>'])
+
+    def test_fromXML_v0(self):
+        cpal = newTable('CPAL')
+        for name, attrs, content in parseXML(
+                '<version value="0"/>'
+                '<numPaletteEntries value="2"/>'
+                '<palette index="0">'
+                '  <color index="0" value="#12345678"/>'
+                '  <color index="1" value="#FEDCBA98"/>'
+                '</palette>'):
+            cpal.fromXML(name, attrs, content, ttFont=None)
+        self.assertEqual(cpal.version, 0)
+        self.assertEqual(cpal.numPaletteEntries, 2)
+        self.assertEqual(repr(cpal.palettes), '[[#12345678, #FEDCBA98]]')
+        self.assertEqual(cpal.paletteLabels, [0])
+        self.assertEqual(cpal.paletteTypes, [0])
+        self.assertEqual(cpal.paletteEntryLabels, [0, 0])
+
+    def test_fromXML_v1(self):
+        cpal = newTable('CPAL')
+        for name, attrs, content in parseXML(
+                '<version value="1"/>'
+                '<numPaletteEntries value="3"/>'
+                '<palette index="0" label="259" type="2">'
+                '  <color index="0" value="#12345678"/>'
+                '  <color index="1" value="#FEDCBA98"/>'
+                '  <color index="2" value="#CAFECAFE"/>'
+                '</palette>'
+                '<paletteEntryLabels>'
+                '  <label index="1" value="262"/>'
+                '</paletteEntryLabels>'):
+            cpal.fromXML(name, attrs, content, ttFont=None)
+        self.assertEqual(cpal.version, 1)
+        self.assertEqual(cpal.numPaletteEntries, 3)
+        self.assertEqual(repr(cpal.palettes),
+                         '[[#12345678, #FEDCBA98, #CAFECAFE]]')
+        self.assertEqual(cpal.paletteLabels, [259])
+        self.assertEqual(cpal.paletteTypes, [2])
+        self.assertEqual(cpal.paletteEntryLabels, [0, 262, 0])
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/M_V_A_R_test.py b/Tests/ttLib/tables/M_V_A_R_test.py
new file mode 100644
index 0000000..05a92e8
--- /dev/null
+++ b/Tests/ttLib/tables/M_V_A_R_test.py
@@ -0,0 +1,139 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.testTools import FakeFont, getXML, parseXML
+from fontTools.misc.textTools import deHexStr, hexStr
+from fontTools.ttLib.tables._f_v_a_r import Axis
+from fontTools.ttLib import newTable, TTFont
+import unittest
+
+
+MVAR_DATA = deHexStr(
+    '0001 0000 '  # 0:   version=1.0
+    '0000 0008 '  # 4:   reserved=0, valueRecordSize=8
+    '0007 '       # 8:   valueRecordCount=7
+    '0044 '       # 10:  offsetToItemVariationStore=68
+    '6861 7363 '  # 12:  ValueRecord.valueTag="hasc"
+    '0000 '       # 16:  ValueRecord.deltaSetOuterIndex
+    '0003 '       # 18:  ValueRecord.deltaSetInnerIndex
+    '6863 6C61 '  # 20:  ValueRecord.valueTag="hcla"
+    '0000 '       # 24:  ValueRecord.deltaSetOuterIndex
+    '0003 '       # 26:  ValueRecord.deltaSetInnerIndex
+    '6863 6C64 '  # 28:  ValueRecord.valueTag="hcld"
+    '0000 '       # 32:  ValueRecord.deltaSetOuterIndex
+    '0003 '       # 34:  ValueRecord.deltaSetInnerIndex
+    '6864 7363 '  # 36:  ValueRecord.valueTag="hdsc"
+    '0000 '       # 40:  ValueRecord.deltaSetOuterIndex
+    '0000 '       # 42:  ValueRecord.deltaSetInnerIndex
+    '686C 6770 '  # 44:  ValueRecord.valueTag="hlgp"
+    '0000 '       # 48:  ValueRecord.deltaSetOuterIndex
+    '0002 '       # 50:  ValueRecord.deltaSetInnerIndex
+    '7362 796F '  # 52:  ValueRecord.valueTag="sbyo"
+    '0000 '       # 56:  ValueRecord.deltaSetOuterIndex
+    '0001 '       # 58:  ValueRecord.deltaSetInnerIndex
+    '7370 796F '  # 60:  ValueRecord.valueTag="spyo"
+    '0000 '       # 64:  ValueRecord.deltaSetOuterIndex
+    '0002 '       # 66:  ValueRecord.deltaSetInnerIndex
+    '0001 '       # 68:  VarStore.format=1
+    '0000 000C '  # 70:  VarStore.offsetToVariationRegionList=12
+    '0001 '       # 74:  VarStore.itemVariationDataCount=1
+    '0000 0016 '  # 76:  VarStore.itemVariationDataOffsets[0]=22
+    '0001 '       # 80:  VarRegionList.axisCount=1
+    '0001 '       # 82:  VarRegionList.regionCount=1
+    '0000 '       # 84:  variationRegions[0].regionAxes[0].startCoord=0.0
+    '4000 '       # 86:  variationRegions[0].regionAxes[0].peakCoord=1.0
+    '4000 '       # 88:  variationRegions[0].regionAxes[0].endCoord=1.0
+    '0004 '       # 90:  VarData.ItemCount=4
+    '0001 '       # 92:  VarData.NumShorts=1
+    '0001 '       # 94:  VarData.VarRegionCount=1
+    '0000 '       # 96:  VarData.VarRegionIndex[0]=0
+    'FF38 '       # 98:  VarData.deltaSets[0]=-200
+    'FFCE '       # 100: VarData.deltaSets[0]=-50
+    '0064 '       # 102: VarData.deltaSets[0]=100
+    '00C8 '       # 104: VarData.deltaSets[0]=200
+)
+
+MVAR_XML = [
+    '<Version value="0x00010000"/>',
+    '<Reserved value="0"/>',
+    '<ValueRecordSize value="8"/>',
+    '<!-- ValueRecordCount=7 -->',
+    '<VarStore Format="1">',
+    '  <Format value="1"/>',
+    '  <VarRegionList>',
+    '    <!-- RegionAxisCount=1 -->',
+    '    <!-- RegionCount=1 -->',
+    '    <Region index="0">',
+    '      <VarRegionAxis index="0">',
+    '        <StartCoord value="0.0"/>',
+    '        <PeakCoord value="1.0"/>',
+    '        <EndCoord value="1.0"/>',
+    '      </VarRegionAxis>',
+    '    </Region>',
+    '  </VarRegionList>',
+    '  <!-- VarDataCount=1 -->',
+    '  <VarData index="0">',
+    '    <!-- ItemCount=4 -->',
+    '    <NumShorts value="1"/>',
+    '    <!-- VarRegionCount=1 -->',
+    '    <VarRegionIndex index="0" value="0"/>',
+    '    <Item index="0" value="[-200]"/>',
+    '    <Item index="1" value="[-50]"/>',
+    '    <Item index="2" value="[100]"/>',
+    '    <Item index="3" value="[200]"/>',
+    '  </VarData>',
+    '</VarStore>',
+    '<ValueRecord index="0">',
+    '  <ValueTag value="hasc"/>',
+    '  <VarIdx value="3"/>',
+    '</ValueRecord>',
+    '<ValueRecord index="1">',
+    '  <ValueTag value="hcla"/>',
+    '  <VarIdx value="3"/>',
+    '</ValueRecord>',
+    '<ValueRecord index="2">',
+    '  <ValueTag value="hcld"/>',
+    '  <VarIdx value="3"/>',
+    '</ValueRecord>',
+    '<ValueRecord index="3">',
+    '  <ValueTag value="hdsc"/>',
+    '  <VarIdx value="0"/>',
+    '</ValueRecord>',
+    '<ValueRecord index="4">',
+    '  <ValueTag value="hlgp"/>',
+    '  <VarIdx value="2"/>',
+    '</ValueRecord>',
+    '<ValueRecord index="5">',
+    '  <ValueTag value="sbyo"/>',
+    '  <VarIdx value="1"/>',
+    '</ValueRecord>',
+    '<ValueRecord index="6">',
+    '  <ValueTag value="spyo"/>',
+    '  <VarIdx value="2"/>',
+    '</ValueRecord>',
+]
+
+
+class MVARTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.maxDiff = None
+
+    def test_decompile_toXML(self):
+        mvar = newTable('MVAR')
+        font = TTFont()
+        mvar.decompile(MVAR_DATA, font)
+        self.assertEqual(getXML(mvar.toXML), MVAR_XML)
+
+    def test_compile_fromXML(self):
+        mvar = newTable('MVAR')
+        font = TTFont()
+        for name, attrs, content in parseXML(MVAR_XML):
+            mvar.fromXML(name, attrs, content, font=font)
+        data = MVAR_DATA
+        self.assertEqual(hexStr(mvar.compile(font)), hexStr(data))
+
+
+if __name__ == '__main__':
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/O_S_2f_2_test.py b/Tests/ttLib/tables/O_S_2f_2_test.py
new file mode 100644
index 0000000..116e82e
--- /dev/null
+++ b/Tests/ttLib/tables/O_S_2f_2_test.py
@@ -0,0 +1,62 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.ttLib import TTFont, newTable, getTableModule
+from fontTools.ttLib.tables.O_S_2f_2 import *
+import unittest
+
+
+class OS2TableTest(unittest.TestCase):
+
+	def test_getUnicodeRanges(self):
+		table = table_O_S_2f_2()
+		table.ulUnicodeRange1 = 0xFFFFFFFF
+		table.ulUnicodeRange2 = 0xFFFFFFFF
+		table.ulUnicodeRange3 = 0xFFFFFFFF
+		table.ulUnicodeRange4 = 0xFFFFFFFF
+		bits = table.getUnicodeRanges()
+		for i in range(127):
+			self.assertIn(i, bits)
+
+	def test_setUnicodeRanges(self):
+		table = table_O_S_2f_2()
+		table.ulUnicodeRange1 = 0
+		table.ulUnicodeRange2 = 0
+		table.ulUnicodeRange3 = 0
+		table.ulUnicodeRange4 = 0
+		bits = set(range(123))
+		table.setUnicodeRanges(bits)
+		self.assertEqual(table.getUnicodeRanges(), bits)
+		with self.assertRaises(ValueError):
+			table.setUnicodeRanges([-1, 127, 255])
+
+	def test_recalcUnicodeRanges(self):
+		font = TTFont()
+		font['OS/2'] = os2 = newTable('OS/2')
+		font['cmap'] = cmap = newTable('cmap')
+		st = getTableModule('cmap').CmapSubtable.newSubtable(4)
+		st.platformID, st.platEncID, st.language = 3, 1, 0
+		st.cmap = {0x0041:'A', 0x03B1: 'alpha', 0x0410: 'Acyr'}
+		cmap.tables = []
+		cmap.tables.append(st)
+		os2.setUnicodeRanges({0, 1, 9})
+		# 'pruneOnly' will clear any bits for which there's no intersection:
+		# bit 1 ('Latin 1 Supplement'), in this case. However, it won't set
+		# bit 7 ('Greek and Coptic') despite the "alpha" character is present.
+		self.assertEqual(os2.recalcUnicodeRanges(font, pruneOnly=True), {0, 9})
+		# try again with pruneOnly=False: bit 7 is now set.
+		self.assertEqual(os2.recalcUnicodeRanges(font), {0, 7, 9})
+		# add a non-BMP char from 'Mahjong Tiles' block (bit 122)
+		st.cmap[0x1F000] = 'eastwindtile'
+		# the bit 122 and the special bit 57 ('Non Plane 0') are also enabled
+		self.assertEqual(os2.recalcUnicodeRanges(font), {0, 7, 9, 57, 122})
+
+	def test_intersectUnicodeRanges(self):
+		self.assertEqual(intersectUnicodeRanges([0x0410]), {9})
+		self.assertEqual(intersectUnicodeRanges([0x0410, 0x1F000]), {9, 57, 122})
+		self.assertEqual(
+			intersectUnicodeRanges([0x0410, 0x1F000], inverse=True),
+			(set(range(123)) - {9, 57, 122}))
+
+
+if __name__ == "__main__":
+	import sys
+	sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/S_T_A_T_test.py b/Tests/ttLib/tables/S_T_A_T_test.py
new file mode 100644
index 0000000..e851f98
--- /dev/null
+++ b/Tests/ttLib/tables/S_T_A_T_test.py
@@ -0,0 +1,264 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.testTools import FakeFont, getXML, parseXML
+from fontTools.misc.textTools import deHexStr
+from fontTools.ttLib import newTable
+import unittest
+
+
+STAT_DATA = deHexStr(
+    '0001 0000 '           #   0: Version=1.0
+    '0008 0002 '           #   4: DesignAxisSize=8, DesignAxisCount=2
+    '0000 0012 '           #   8: OffsetToDesignAxes=18
+    '0003 0000 0022 '      #  12: AxisValueCount=3, OffsetToAxisValueOffsets=34
+    '7767 6874 '           #  18: DesignAxis[0].AxisTag='wght'
+    '012D 0002 '           #  22: DesignAxis[0].NameID=301, .AxisOrdering=2
+    '5445 5354 '           #  26: DesignAxis[1].AxisTag='TEST'
+    '012E 0001 '           #  30: DesignAxis[1].NameID=302, .AxisOrdering=1
+    '0006 0012 0026 '      #  34: AxisValueOffsets = [6, 18, 38] (+34)
+    '0001 0000 0000  '     #  40: AxisValue[0].Format=1, .AxisIndex=0, .Flags=0
+    '0191 0190 0000 '      #  46: AxisValue[0].ValueNameID=401, .Value=400.0
+    '0002 0001 0000 '      #  52: AxisValue[1].Format=2, .AxisIndex=1, .Flags=0
+    '0192 '                #  58: AxisValue[1].ValueNameID=402
+    '0002 0000 '           #  60: AxisValue[1].NominalValue=2.0
+    '0001 0000 '           #  64: AxisValue[1].RangeMinValue=1.0
+    '0003 0000 '           #  68: AxisValue[1].RangeMaxValue=3.0
+    '0003 0000 0000 '      #  72: AxisValue[2].Format=3, .AxisIndex=0, .Flags=0
+    '0002 '                #  78: AxisValue[2].ValueNameID=2 'Regular'
+    '0190 0000 02BC 0000 ' #  80: AxisValue[2].Value=400.0, .LinkedValue=700.0
+)                          #  88: <end>
+assert(len(STAT_DATA) == 88)
+
+
+STAT_XML = [
+    '<Version value="0x00010000"/>',
+    '<DesignAxisRecordSize value="8"/>',
+    '<!-- DesignAxisCount=2 -->',
+    '<DesignAxisRecord>',
+    '  <Axis index="0">',
+    '    <AxisTag value="wght"/>',
+    '    <AxisNameID value="301"/>',
+    '    <AxisOrdering value="2"/>',
+    '  </Axis>',
+    '  <Axis index="1">',
+    '    <AxisTag value="TEST"/>',
+    '    <AxisNameID value="302"/>',
+    '    <AxisOrdering value="1"/>',
+    '  </Axis>',
+    '</DesignAxisRecord>',
+    '<!-- AxisValueCount=3 -->',
+    '<AxisValueArray>',
+    '  <AxisValue index="0" Format="1">',
+    '    <AxisIndex value="0"/>',
+    '    <Flags value="0"/>',
+    '    <ValueNameID value="401"/>',
+    '    <Value value="400.0"/>',
+    '  </AxisValue>',
+    '  <AxisValue index="1" Format="2">',
+    '    <AxisIndex value="1"/>',
+    '    <Flags value="0"/>',
+    '    <ValueNameID value="402"/>',
+    '    <NominalValue value="2.0"/>',
+    '    <RangeMinValue value="1.0"/>',
+    '    <RangeMaxValue value="3.0"/>',
+    '  </AxisValue>',
+    '  <AxisValue index="2" Format="3">',
+    '    <AxisIndex value="0"/>',
+    '    <Flags value="0"/>',
+    '    <ValueNameID value="2"/>',
+    '    <Value value="400.0"/>',
+    '    <LinkedValue value="700.0"/>',
+    '  </AxisValue>',
+    '</AxisValueArray>',
+]
+
+
+# Contains junk data for making sure we get our offset decoding right.
+STAT_DATA_WITH_AXIS_JUNK = deHexStr(
+    '0001 0000 '           #   0: Version=1.0
+    '000A 0002 '           #   4: DesignAxisSize=10, DesignAxisCount=2
+    '0000 0012 '           #   8: OffsetToDesignAxes=18
+    '0000 0000 0000 '      #  12: AxisValueCount=3, OffsetToAxisValueOffsets=34
+    '7767 6874 '           #  18: DesignAxis[0].AxisTag='wght'
+    '012D 0002 '           #  22: DesignAxis[0].NameID=301, .AxisOrdering=2
+    'DEAD '                #  26: <junk>
+    '5445 5354 '           #  28: DesignAxis[1].AxisTag='TEST'
+    '012E 0001 '           #  32: DesignAxis[1].NameID=302, .AxisOrdering=1
+    'BEEF '                #  36: <junk>
+)                          #  38: <end>
+
+assert(len(STAT_DATA_WITH_AXIS_JUNK) == 38)
+
+
+STAT_XML_WITH_AXIS_JUNK = [
+    '<Version value="0x00010000"/>',
+    '<DesignAxisRecordSize value="10"/>',
+    '<!-- DesignAxisCount=2 -->',
+    '<DesignAxisRecord>',
+    '  <Axis index="0">',
+    '    <AxisTag value="wght"/>',
+    '    <AxisNameID value="301"/>',
+    '    <AxisOrdering value="2"/>',
+    '    <MoreBytes index="0" value="222"/>',  # 0xDE
+    '    <MoreBytes index="1" value="173"/>',  # 0xAD
+    '  </Axis>',
+    '  <Axis index="1">',
+    '    <AxisTag value="TEST"/>',
+    '    <AxisNameID value="302"/>',
+    '    <AxisOrdering value="1"/>',
+    '    <MoreBytes index="0" value="190"/>',  # 0xBE
+    '    <MoreBytes index="1" value="239"/>',  # 0xEF
+    '  </Axis>',
+    '</DesignAxisRecord>',
+    '<!-- AxisValueCount=0 -->',
+]
+
+
+STAT_DATA_AXIS_VALUE_FORMAT3 = deHexStr(
+    '0001 0000 '  #  0: Version=1.0
+    '0008 0001 '  #  4: DesignAxisSize=8, DesignAxisCount=1
+    '0000 0012 '  #  8: OffsetToDesignAxes=18
+    '0001 '       # 12: AxisValueCount=1
+    '0000 001A '  # 14: OffsetToAxisValueOffsets=26
+    '7767 6874 '  # 18: DesignAxis[0].AxisTag='wght'
+    '0102  '      # 22: DesignAxis[0].AxisNameID=258 'Weight'
+    '0000 '       # 24: DesignAxis[0].AxisOrdering=0
+    '0002 '       # 26: AxisValueOffsets=[2] (+26)
+    '0003 '       # 28: AxisValue[0].Format=3
+    '0000 0002 '  # 30: AxisValue[0].AxisIndex=0, .Flags=0x2
+    '0002 '       # 34: AxisValue[0].ValueNameID=2 'Regular'
+    '0190 0000 '  # 36: AxisValue[0].Value=400.0
+    '02BC 0000 '  # 40: AxisValue[0].LinkedValue=700.0
+)                 # 44: <end>
+assert(len(STAT_DATA_AXIS_VALUE_FORMAT3) == 44)
+
+
+STAT_XML_AXIS_VALUE_FORMAT3 = [
+    '<Version value="0x00010000"/>',
+    '<DesignAxisRecordSize value="8"/>',
+    '<!-- DesignAxisCount=1 -->',
+    '<DesignAxisRecord>',
+    '  <Axis index="0">',
+    '    <AxisTag value="wght"/>',
+    '    <AxisNameID value="258"/>',
+    '    <AxisOrdering value="0"/>',
+    '  </Axis>',
+    '</DesignAxisRecord>',
+    '<!-- AxisValueCount=1 -->',
+    '<AxisValueArray>',
+    '  <AxisValue index="0" Format="3">',
+    '    <AxisIndex value="0"/>',
+    '    <Flags value="2"/>',
+    '    <ValueNameID value="2"/>',
+    '    <Value value="400.0"/>',
+    '    <LinkedValue value="700.0"/>',
+    '  </AxisValue>',
+    '</AxisValueArray>',
+]
+
+
+STAT_DATA_VERSION_1_1 = deHexStr(
+    '0001 0001 '  #  0: Version=1.1
+    '0008 0001 '  #  4: DesignAxisSize=8, DesignAxisCount=1
+    '0000 0014 '  #  8: OffsetToDesignAxes=20
+    '0001 '       # 12: AxisValueCount=1
+    '0000 001C '  # 14: OffsetToAxisValueOffsets=28
+    '0101 '       # 18: ElidedFallbackNameID: 257
+    '7767 6874 '  # 20: DesignAxis[0].AxisTag='wght'
+    '0102  '      # 24: DesignAxis[0].AxisNameID=258 'Weight'
+    '0000 '       # 26: DesignAxis[0].AxisOrdering=0
+    '0002 '       # 28: AxisValueOffsets=[2] (+28)
+    '0003 '       # 30: AxisValue[0].Format=3
+    '0000 0002 '  # 32: AxisValue[0].AxisIndex=0, .Flags=0x2
+    '0002 '       # 36: AxisValue[0].ValueNameID=2 'Regular'
+    '0190 0000 '  # 38: AxisValue[0].Value=400.0
+    '02BC 0000 '  # 42: AxisValue[0].LinkedValue=700.0
+)                 # 46: <end>
+assert(len(STAT_DATA_VERSION_1_1) == 46)
+
+
+STAT_XML_VERSION_1_1 = [
+    '<Version value="0x00010001"/>',
+    '<DesignAxisRecordSize value="8"/>',
+    '<!-- DesignAxisCount=1 -->',
+    '<DesignAxisRecord>',
+    '  <Axis index="0">',
+    '    <AxisTag value="wght"/>',
+    '    <AxisNameID value="258"/>',
+    '    <AxisOrdering value="0"/>',
+    '  </Axis>',
+    '</DesignAxisRecord>',
+    '<!-- AxisValueCount=1 -->',
+    '<AxisValueArray>',
+    '  <AxisValue index="0" Format="3">',
+    '    <AxisIndex value="0"/>',
+    '    <Flags value="2"/>',
+    '    <ValueNameID value="2"/>',
+    '    <Value value="400.0"/>',
+    '    <LinkedValue value="700.0"/>',
+    '  </AxisValue>',
+    '</AxisValueArray>',
+    '<ElidedFallbackNameID value="257"/>',
+]
+
+
+class STATTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.maxDiff = None
+
+    def test_decompile_toXML(self):
+        table = newTable('STAT')
+        table.decompile(STAT_DATA, font=FakeFont(['.notdef']))
+        self.assertEqual(getXML(table.toXML), STAT_XML)
+
+    def test_decompile_toXML_withAxisJunk(self):
+        table = newTable('STAT')
+        table.decompile(STAT_DATA_WITH_AXIS_JUNK, font=FakeFont(['.notdef']))
+        self.assertEqual(getXML(table.toXML), STAT_XML_WITH_AXIS_JUNK)
+
+    def test_decompile_toXML_format3(self):
+        table = newTable('STAT')
+        table.decompile(STAT_DATA_AXIS_VALUE_FORMAT3,
+                        font=FakeFont(['.notdef']))
+        self.assertEqual(getXML(table.toXML), STAT_XML_AXIS_VALUE_FORMAT3)
+
+    def test_decompile_toXML_version_1_1(self):
+        table = newTable('STAT')
+        table.decompile(STAT_DATA_VERSION_1_1,
+                        font=FakeFont(['.notdef']))
+        self.assertEqual(getXML(table.toXML), STAT_XML_VERSION_1_1)
+
+    def test_compile_fromXML(self):
+        table = newTable('STAT')
+        font = FakeFont(['.notdef'])
+        for name, attrs, content in parseXML(STAT_XML):
+            table.fromXML(name, attrs, content, font=font)
+        self.assertEqual(table.compile(font), STAT_DATA)
+
+    def test_compile_fromXML_withAxisJunk(self):
+        table = newTable('STAT')
+        font = FakeFont(['.notdef'])
+        for name, attrs, content in parseXML(STAT_XML_WITH_AXIS_JUNK):
+            table.fromXML(name, attrs, content, font=font)
+        self.assertEqual(table.compile(font), STAT_DATA_WITH_AXIS_JUNK)
+
+    def test_compile_fromXML_format3(self):
+        table = newTable('STAT')
+        font = FakeFont(['.notdef'])
+        for name, attrs, content in parseXML(STAT_XML_AXIS_VALUE_FORMAT3):
+            table.fromXML(name, attrs, content, font=font)
+        self.assertEqual(table.compile(font), STAT_DATA_AXIS_VALUE_FORMAT3)
+
+    def test_compile_fromXML_version_1_1(self):
+        table = newTable('STAT')
+        font = FakeFont(['.notdef'])
+        for name, attrs, content in parseXML(STAT_XML_VERSION_1_1):
+            table.fromXML(name, attrs, content, font=font)
+        self.assertEqual(table.compile(font), STAT_DATA_VERSION_1_1)
+
+
+if __name__ == '__main__':
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/T_S_I__0_test.py b/Tests/ttLib/tables/T_S_I__0_test.py
new file mode 100644
index 0000000..1de6b34
--- /dev/null
+++ b/Tests/ttLib/tables/T_S_I__0_test.py
@@ -0,0 +1,106 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import SimpleNamespace
+from fontTools.misc.textTools import deHexStr
+from fontTools.misc.testTools import getXML
+from fontTools.ttLib.tables.T_S_I__0 import table_T_S_I__0
+import pytest
+
+
+# (gid, length, offset) for glyph programs
+TSI0_INDICES = [
+    (0, 1, 0),
+    (1, 5, 1),
+    (2, 0, 1),
+    (3, 0, 1),
+    (4, 8, 6)]
+
+# (type, length, offset) for 'extra' programs
+TSI0_EXTRA_INDICES = [
+    (0xFFFA, 2, 14),          # ppgm
+    (0xFFFB, 4, 16),          # cvt
+    (0xFFFC, 6, 20),          # reserved
+    (0xFFFD, 10, 26)]         # fpgm
+
+# compiled TSI0 table from data above
+TSI0_DATA = deHexStr(
+    "0000 0001 00000000"
+    "0001 0005 00000001"
+    "0002 0000 00000001"
+    "0003 0000 00000001"
+    "0004 0008 00000006"
+    "FFFE 0000 ABFC1F34"      # 'magic' separates glyph from extra programs
+    "FFFA 0002 0000000E"
+    "FFFB 0004 00000010"
+    "FFFC 0006 00000014"
+    "FFFD 000A 0000001A")
+
+# empty font has no glyph programs but 4 extra programs are always present
+EMPTY_TSI0_EXTRA_INDICES = [
+    (0xFFFA, 0, 0),
+    (0xFFFB, 0, 0),
+    (0xFFFC, 0, 0),
+    (0xFFFD, 0, 0)]
+
+EMPTY_TSI0_DATA = deHexStr(
+    "FFFE 0000 ABFC1F34"
+    "FFFA 0000 00000000"
+    "FFFB 0000 00000000"
+    "FFFC 0000 00000000"
+    "FFFD 0000 00000000")
+
+
+@pytest.fixture
+def table():
+    return table_T_S_I__0()
+
+
+@pytest.mark.parametrize(
+    "numGlyphs, data, expected_indices, expected_extra_indices",
+    [
+        (5, TSI0_DATA, TSI0_INDICES, TSI0_EXTRA_INDICES),
+        (0, EMPTY_TSI0_DATA, [], EMPTY_TSI0_EXTRA_INDICES)
+    ],
+    ids=["simple", "empty"]
+)
+def test_decompile(table, numGlyphs, data, expected_indices,
+                   expected_extra_indices):
+    font = {'maxp': SimpleNamespace(numGlyphs=numGlyphs)}
+
+    table.decompile(data, font)
+
+    assert len(table.indices) == numGlyphs
+    assert table.indices == expected_indices
+    assert len(table.extra_indices) == 4
+    assert table.extra_indices == expected_extra_indices
+
+
+@pytest.mark.parametrize(
+    "numGlyphs, indices, extra_indices, expected_data",
+    [
+        (5, TSI0_INDICES, TSI0_EXTRA_INDICES, TSI0_DATA),
+        (0, [], EMPTY_TSI0_EXTRA_INDICES, EMPTY_TSI0_DATA)
+    ],
+    ids=["simple", "empty"]
+)
+def test_compile(table, numGlyphs, indices, extra_indices, expected_data):
+    assert table.compile(ttFont=None) == b""
+
+    table.set(indices, extra_indices)
+    data = table.compile(ttFont=None)
+    assert data == expected_data
+
+
+def test_set(table):
+    table.set(TSI0_INDICES, TSI0_EXTRA_INDICES)
+    assert table.indices == TSI0_INDICES
+    assert table.extra_indices == TSI0_EXTRA_INDICES
+
+
+def test_toXML(table):
+    assert getXML(table.toXML, ttFont=None) == [
+        '<!-- This table will be calculated by the compiler -->']
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(pytest.main(sys.argv))
diff --git a/Tests/ttLib/tables/T_S_I__1_test.py b/Tests/ttLib/tables/T_S_I__1_test.py
new file mode 100644
index 0000000..e529425
--- /dev/null
+++ b/Tests/ttLib/tables/T_S_I__1_test.py
@@ -0,0 +1,184 @@
+from __future__ import (
+    print_function, division, absolute_import, unicode_literals
+)
+from fontTools.misc.py23 import unichr, tobytes
+from fontTools.misc.loggingTools import CapturingLogHandler
+from fontTools.ttLib import TTFont, TTLibError
+from fontTools.ttLib.tables.T_S_I__0 import table_T_S_I__0
+from fontTools.ttLib.tables.T_S_I__1 import table_T_S_I__1
+import pytest
+
+
+TSI1_DATA = b"""abcdefghijklmnopqrstuvxywz0123456789"""
+TSI1_UTF8_DATA = b"""abcd\xc3\xa9ghijklmnopqrstuvxywz0123456789"""
+
+
+@pytest.fixture
+def indextable():
+    table = table_T_S_I__0()
+    table.set(
+        [(0, 1, 0),         # gid 0, length=1, offset=0, text='a'
+         (1, 5, 1),         # gid 1, length=5, offset=1, text='bcdef'
+         (2, 0, 1),         # gid 2, length=0, offset=1, text=''
+         (3, 0, 1),         # gid 3, length=0, offset=1, text=''
+         (4, 8, 6)],        # gid 4, length=8, offset=6, text='ghijklmn'
+        [(0xFFFA, 2, 14),   # 'ppgm', length=2, offset=14, text='op'
+         (0xFFFB, 4, 16),   # 'cvt', length=4, offset=16, text='qrst'
+         (0xFFFC, 6, 20),   # 'reserved', length=6, offset=20, text='uvxywz'
+         (0xFFFD, 10, 26)]  # 'fpgm', length=10, offset=26, text='0123456789'
+    )
+    return table
+
+
+@pytest.fixture
+def font(indextable):
+    font = TTFont()
+    # ['a', 'b', 'c', ...]
+    ch = 0x61
+    n = len(indextable.indices)
+    font.glyphOrder = [unichr(i) for i in range(ch, ch+n)]
+    font['TSI0'] = indextable
+    return font
+
+
+@pytest.fixture
+def empty_font():
+    font = TTFont()
+    font.glyphOrder = []
+    indextable = table_T_S_I__0()
+    indextable.set([], [(0xFFFA, 0, 0),
+                        (0xFFFB, 0, 0),
+                        (0xFFFC, 0, 0),
+                        (0xFFFD, 0, 0)])
+    font['TSI0'] = indextable
+    return font
+
+
+def test_decompile(font):
+    table = table_T_S_I__1()
+    table.decompile(TSI1_DATA, font)
+
+    assert table.glyphPrograms == {
+        'a': 'a',
+        'b': 'bcdef',
+        # 'c': '',  # zero-length entries are skipped
+        # 'd': '',
+        'e': 'ghijklmn'}
+    assert table.extraPrograms == {
+        'ppgm': 'op',
+        'cvt': 'qrst',
+        'reserved': 'uvxywz',
+        'fpgm': '0123456789'}
+
+
+def test_decompile_utf8(font):
+    table = table_T_S_I__1()
+    table.decompile(TSI1_UTF8_DATA, font)
+
+    assert table.glyphPrograms == {
+        'a': 'a',
+        'b': 'bcd\u00e9',
+        # 'c': '',  # zero-length entries are skipped
+        # 'd': '',
+        'e': 'ghijklmn'}
+    assert table.extraPrograms == {
+        'ppgm': 'op',
+        'cvt': 'qrst',
+        'reserved': 'uvxywz',
+        'fpgm': '0123456789'}
+
+
+def test_decompile_empty(empty_font):
+    table = table_T_S_I__1()
+    table.decompile(b"", empty_font)
+
+    assert table.glyphPrograms == {}
+    assert table.extraPrograms == {}
+
+
+def test_decompile_invalid_length(empty_font):
+    empty_font.glyphOrder = ['a']
+    empty_font['TSI0'].indices = [(0, 0x8000+1, 0)]
+
+    table = table_T_S_I__1()
+    with pytest.raises(TTLibError) as excinfo:
+        table.decompile(b'', empty_font)
+    assert excinfo.match("textLength .* must not be > 32768")
+
+
+def test_decompile_offset_past_end(empty_font):
+    empty_font.glyphOrder = ['foo', 'bar']
+    content = 'baz'
+    data = tobytes(content)
+    empty_font['TSI0'].indices = [(0, len(data), 0), (1, 1, len(data)+1)]
+
+    table = table_T_S_I__1()
+    with CapturingLogHandler(table.log, "WARNING") as captor:
+        table.decompile(data, empty_font)
+
+    # the 'bar' program is skipped because its offset > len(data)
+    assert table.glyphPrograms == {'foo': 'baz'}
+    assert any("textOffset > totalLength" in r.msg for r in captor.records)
+
+
+def test_decompile_magic_length_last_extra(empty_font):
+    indextable = empty_font['TSI0']
+    indextable.extra_indices[-1] = (0xFFFD, 0x8000, 0)
+    content = "0" * (0x8000 + 1)
+    data = tobytes(content)
+
+    table = table_T_S_I__1()
+    table.decompile(data, empty_font)
+
+    assert table.extraPrograms['fpgm'] == content
+
+
+def test_decompile_magic_length_last_glyph(empty_font):
+    empty_font.glyphOrder = ['foo', 'bar']
+    indextable = empty_font['TSI0']
+    indextable.indices = [
+        (0, 3, 0),
+        (1, 0x8000, 3)]           # the actual length of 'bar' program is
+    indextable.extra_indices = [  # the difference between the first extra's
+        (0xFFFA, 0, 0x8004),      # offset and 'bar' offset: 0x8004 - 3
+        (0xFFFB, 0, 0x8004),
+        (0xFFFC, 0, 0x8004),
+        (0xFFFD, 0, 0x8004)]
+    foo_content = "0" * 3
+    bar_content = "1" * (0x8000 + 1)
+    data = tobytes(foo_content + bar_content)
+
+    table = table_T_S_I__1()
+    table.decompile(data, empty_font)
+
+    assert table.glyphPrograms['foo'] == foo_content
+    assert table.glyphPrograms['bar'] == bar_content
+
+
+def test_decompile_magic_length_non_last(empty_font):
+    indextable = empty_font['TSI0']
+    indextable.extra_indices = [
+        (0xFFFA, 3, 0),
+        (0xFFFB, 0x8000, 3),  # the actual length of 'cvt' program is:
+        (0xFFFC, 0, 0x8004),  # nextTextOffset - textOffset: 0x8004 - 3
+        (0xFFFD, 0, 0x8004)]
+    ppgm_content = "0" * 3
+    cvt_content = "1" * (0x8000 + 1)
+    data = tobytes(ppgm_content + cvt_content)
+
+    table = table_T_S_I__1()
+    table.decompile(data, empty_font)
+
+    assert table.extraPrograms['ppgm'] == ppgm_content
+    assert table.extraPrograms['cvt'] == cvt_content
+
+    table = table_T_S_I__1()
+    with CapturingLogHandler(table.log, "WARNING") as captor:
+        table.decompile(data[:-1], empty_font)  # last entry is truncated
+    captor.assertRegex("nextTextOffset > totalLength")
+    assert table.extraPrograms['cvt'] == cvt_content[:-1]
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(pytest.main(sys.argv))
diff --git a/Tests/ttLib/tables/TupleVariation_test.py b/Tests/ttLib/tables/TupleVariation_test.py
new file mode 100644
index 0000000..6986d5b
--- /dev/null
+++ b/Tests/ttLib/tables/TupleVariation_test.py
@@ -0,0 +1,687 @@
+from __future__ import \
+	print_function, division, absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.misc.loggingTools import CapturingLogHandler
+from fontTools.misc.testTools import parseXML
+from fontTools.misc.textTools import deHexStr, hexStr
+from fontTools.misc.xmlWriter import XMLWriter
+from fontTools.ttLib.tables.TupleVariation import \
+	log, TupleVariation, compileSharedTuples, decompileSharedTuples, \
+	compileTupleVariationStore, decompileTupleVariationStore, inferRegion_
+import random
+import unittest
+
+
+def hexencode(s):
+	h = hexStr(s).upper()
+	return ' '.join([h[i:i+2] for i in range(0, len(h), 2)])
+
+
+AXES = {
+	"wdth": (0.3, 0.4, 0.5),
+	"wght": (0.0, 1.0, 1.0),
+	"opsz": (-0.7, -0.7, 0.0)
+}
+
+
+# Shared tuples in the 'gvar' table of the Skia font, as printed
+# in Apple's TrueType specification.
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6gvar.html
+SKIA_GVAR_SHARED_TUPLES_DATA = deHexStr(
+	"40 00 00 00 C0 00 00 00 00 00 40 00 00 00 C0 00 "
+	"C0 00 C0 00 40 00 C0 00 40 00 40 00 C0 00 40 00")
+
+SKIA_GVAR_SHARED_TUPLES = [
+	{"wght": 1.0, "wdth": 0.0},
+	{"wght": -1.0, "wdth": 0.0},
+	{"wght": 0.0, "wdth": 1.0},
+	{"wght": 0.0, "wdth": -1.0},
+	{"wght": -1.0, "wdth": -1.0},
+	{"wght": 1.0, "wdth": -1.0},
+	{"wght": 1.0, "wdth": 1.0},
+	{"wght": -1.0, "wdth": 1.0}
+]
+
+
+# Tuple Variation Store of uppercase I in the Skia font, as printed in Apple's
+# TrueType spec. The actual Skia font uses a different table for uppercase I
+# than what is printed in Apple's spec, but we still want to make sure that
+# we can parse the data as it appears in the specification.
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6gvar.html
+SKIA_GVAR_I_DATA = deHexStr(
+	"00 08 00 24 00 33 20 00 00 15 20 01 00 1B 20 02 "
+	"00 24 20 03 00 15 20 04 00 26 20 07 00 0D 20 06 "
+	"00 1A 20 05 00 40 01 01 01 81 80 43 FF 7E FF 7E "
+	"FF 7E FF 7E 00 81 45 01 01 01 03 01 04 01 04 01 "
+	"04 01 02 80 40 00 82 81 81 04 3A 5A 3E 43 20 81 "
+	"04 0E 40 15 45 7C 83 00 0D 9E F3 F2 F0 F0 F0 F0 "
+	"F3 9E A0 A1 A1 A1 9F 80 00 91 81 91 00 0D 0A 0A "
+	"09 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0B 80 00 15 81 "
+	"81 00 C4 89 00 C4 83 00 0D 80 99 98 96 96 96 96 "
+	"99 80 82 83 83 83 81 80 40 FF 18 81 81 04 E6 F9 "
+	"10 21 02 81 04 E8 E5 EB 4D DA 83 00 0D CE D3 D4 "
+	"D3 D3 D3 D5 D2 CE CC CD CD CD CD 80 00 A1 81 91 "
+	"00 0D 07 03 04 02 02 02 03 03 07 07 08 08 08 07 "
+	"80 00 09 81 81 00 28 40 00 A4 02 24 24 66 81 04 "
+	"08 FA FA FA 28 83 00 82 02 FF FF FF 83 02 01 01 "
+	"01 84 91 00 80 06 07 08 08 08 08 0A 07 80 03 FE "
+	"FF FF FF 81 00 08 81 82 02 EE EE EE 8B 6D 00")
+
+
+class TupleVariationTest(unittest.TestCase):
+	def test_equal(self):
+		var1 = TupleVariation({"wght":(0.0, 1.0, 1.0)}, [(0,0), (9,8), (7,6)])
+		var2 = TupleVariation({"wght":(0.0, 1.0, 1.0)}, [(0,0), (9,8), (7,6)])
+		self.assertEqual(var1, var2)
+
+	def test_equal_differentAxes(self):
+		var1 = TupleVariation({"wght":(0.0, 1.0, 1.0)}, [(0,0), (9,8), (7,6)])
+		var2 = TupleVariation({"wght":(0.7, 0.8, 0.9)}, [(0,0), (9,8), (7,6)])
+		self.assertNotEqual(var1, var2)
+
+	def test_equal_differentCoordinates(self):
+		var1 = TupleVariation({"wght":(0.0, 1.0, 1.0)}, [(0,0), (9,8), (7,6)])
+		var2 = TupleVariation({"wght":(0.0, 1.0, 1.0)}, [(0,0), (9,8)])
+		self.assertNotEqual(var1, var2)
+
+	def test_hasImpact_someDeltasNotZero(self):
+		axes = {"wght":(0.0, 1.0, 1.0)}
+		var = TupleVariation(axes, [(0,0), (9,8), (7,6)])
+		self.assertTrue(var.hasImpact())
+
+	def test_hasImpact_allDeltasZero(self):
+		axes = {"wght":(0.0, 1.0, 1.0)}
+		var = TupleVariation(axes, [(0,0), (0,0), (0,0)])
+		self.assertTrue(var.hasImpact())
+
+	def test_hasImpact_allDeltasNone(self):
+		axes = {"wght":(0.0, 1.0, 1.0)}
+		var = TupleVariation(axes, [None, None, None])
+		self.assertFalse(var.hasImpact())
+
+	def test_toXML_badDeltaFormat(self):
+		writer = XMLWriter(BytesIO())
+		g = TupleVariation(AXES, ["String"])
+		with CapturingLogHandler(log, "ERROR") as captor:
+			g.toXML(writer, ["wdth"])
+		self.assertIn("bad delta format", [r.msg for r in captor.records])
+		self.assertEqual([
+			'<tuple>',
+			  '<coord axis="wdth" max="0.5" min="0.3" value="0.4"/>',
+			  '<!-- bad delta #0 -->',
+			'</tuple>',
+		], TupleVariationTest.xml_lines(writer))
+
+	def test_toXML_constants(self):
+		writer = XMLWriter(BytesIO())
+		g = TupleVariation(AXES, [42, None, 23, 0, -17, None])
+		g.toXML(writer, ["wdth", "wght", "opsz"])
+		self.assertEqual([
+			'<tuple>',
+			  '<coord axis="wdth" max="0.5" min="0.3" value="0.4"/>',
+			  '<coord axis="wght" value="1.0"/>',
+			  '<coord axis="opsz" value="-0.7"/>',
+			  '<delta cvt="0" value="42"/>',
+			  '<delta cvt="2" value="23"/>',
+			  '<delta cvt="3" value="0"/>',
+			  '<delta cvt="4" value="-17"/>',
+			'</tuple>'
+		], TupleVariationTest.xml_lines(writer))
+
+	def test_toXML_points(self):
+		writer = XMLWriter(BytesIO())
+		g = TupleVariation(AXES, [(9,8), None, (7,6), (0,0), (-1,-2), None])
+		g.toXML(writer, ["wdth", "wght", "opsz"])
+		self.assertEqual([
+			'<tuple>',
+			  '<coord axis="wdth" max="0.5" min="0.3" value="0.4"/>',
+			  '<coord axis="wght" value="1.0"/>',
+			  '<coord axis="opsz" value="-0.7"/>',
+			  '<delta pt="0" x="9" y="8"/>',
+			  '<delta pt="2" x="7" y="6"/>',
+			  '<delta pt="3" x="0" y="0"/>',
+			  '<delta pt="4" x="-1" y="-2"/>',
+			'</tuple>'
+		], TupleVariationTest.xml_lines(writer))
+
+	def test_toXML_allDeltasNone(self):
+		writer = XMLWriter(BytesIO())
+		axes = {"wght":(0.0, 1.0, 1.0)}
+		g = TupleVariation(axes, [None] * 5)
+		g.toXML(writer, ["wght", "wdth"])
+		self.assertEqual([
+			'<tuple>',
+			  '<coord axis="wght" value="1.0"/>',
+			  '<!-- no deltas -->',
+			'</tuple>'
+		], TupleVariationTest.xml_lines(writer))
+
+	def test_fromXML_badDeltaFormat(self):
+		g = TupleVariation({}, [])
+		with CapturingLogHandler(log, "WARNING") as captor:
+			for name, attrs, content in parseXML('<delta a="1" b="2"/>'):
+				g.fromXML(name, attrs, content)
+		self.assertIn("bad delta format: a, b",
+		              [r.msg for r in captor.records])
+
+	def test_fromXML_constants(self):
+		g = TupleVariation({}, [None] * 4)
+		for name, attrs, content in parseXML(
+				'<coord axis="wdth" min="0.3" value="0.4" max="0.5"/>'
+				'<coord axis="wght" value="1.0"/>'
+				'<coord axis="opsz" value="-0.7"/>'
+				'<delta cvt="1" value="42"/>'
+				'<delta cvt="2" value="-23"/>'):
+			g.fromXML(name, attrs, content)
+		self.assertEqual(AXES, g.axes)
+		self.assertEqual([None, 42, -23, None], g.coordinates)
+
+	def test_fromXML_points(self):
+		g = TupleVariation({}, [None] * 4)
+		for name, attrs, content in parseXML(
+				'<coord axis="wdth" min="0.3" value="0.4" max="0.5"/>'
+				'<coord axis="wght" value="1.0"/>'
+				'<coord axis="opsz" value="-0.7"/>'
+				'<delta pt="1" x="33" y="44"/>'
+				'<delta pt="2" x="-2" y="170"/>'):
+			g.fromXML(name, attrs, content)
+		self.assertEqual(AXES, g.axes)
+		self.assertEqual([None, (33, 44), (-2, 170), None], g.coordinates)
+
+	def test_compile_sharedPeaks_nonIntermediate_sharedPoints(self):
+		var = TupleVariation(
+			{"wght": (0.0, 0.5, 0.5), "wdth": (0.0, 0.8, 0.8)},
+			[(7,4), (8,5), (9,6)])
+		axisTags = ["wght", "wdth"]
+		sharedPeakIndices = { var.compileCoord(axisTags): 0x77 }
+		tup, deltas, _ = var.compile(axisTags, sharedPeakIndices,
+		                          sharedPoints={0,1,2})
+		# len(deltas)=8; flags=None; tupleIndex=0x77
+		# embeddedPeaks=[]; intermediateCoord=[]
+		self.assertEqual("00 08 00 77", hexencode(tup))
+		self.assertEqual("02 07 08 09 "     # deltaX: [7, 8, 9]
+						 "02 04 05 06",     # deltaY: [4, 5, 6]
+						 hexencode(deltas))
+
+	def test_compile_sharedPeaks_intermediate_sharedPoints(self):
+		var = TupleVariation(
+			{"wght": (0.3, 0.5, 0.7), "wdth": (0.1, 0.8, 0.9)},
+			[(7,4), (8,5), (9,6)])
+		axisTags = ["wght", "wdth"]
+		sharedPeakIndices = { var.compileCoord(axisTags): 0x77 }
+		tup, deltas, _ = var.compile(axisTags, sharedPeakIndices,
+		                          sharedPoints={0,1,2})
+		# len(deltas)=8; flags=INTERMEDIATE_REGION; tupleIndex=0x77
+		# embeddedPeak=[]; intermediateCoord=[(0.3, 0.1), (0.7, 0.9)]
+		self.assertEqual("00 08 40 77 13 33 06 66 2C CD 39 9A", hexencode(tup))
+		self.assertEqual("02 07 08 09 "     # deltaX: [7, 8, 9]
+						 "02 04 05 06",     # deltaY: [4, 5, 6]
+						 hexencode(deltas))
+
+	def test_compile_sharedPeaks_nonIntermediate_privatePoints(self):
+		var = TupleVariation(
+			{"wght": (0.0, 0.5, 0.5), "wdth": (0.0, 0.8, 0.8)},
+			[(7,4), (8,5), (9,6)])
+		axisTags = ["wght", "wdth"]
+		sharedPeakIndices = { var.compileCoord(axisTags): 0x77 }
+		tup, deltas, _ = var.compile(axisTags, sharedPeakIndices,
+		                          sharedPoints=None)
+		# len(deltas)=9; flags=PRIVATE_POINT_NUMBERS; tupleIndex=0x77
+		# embeddedPeak=[]; intermediateCoord=[]
+		self.assertEqual("00 09 20 77", hexencode(tup))
+		self.assertEqual("00 "              # all points in glyph
+						 "02 07 08 09 "     # deltaX: [7, 8, 9]
+						 "02 04 05 06",     # deltaY: [4, 5, 6]
+						 hexencode(deltas))
+
+	def test_compile_sharedPeaks_intermediate_privatePoints(self):
+		var = TupleVariation(
+			{"wght": (0.0, 0.5, 1.0), "wdth": (0.0, 0.8, 1.0)},
+			[(7,4), (8,5), (9,6)])
+		axisTags = ["wght", "wdth"]
+		sharedPeakIndices = { var.compileCoord(axisTags): 0x77 }
+		tuple, deltas, _ = var.compile(axisTags,
+		                            sharedPeakIndices, sharedPoints=None)
+		# len(deltas)=9; flags=PRIVATE_POINT_NUMBERS; tupleIndex=0x77
+		# embeddedPeak=[]; intermediateCoord=[(0.0, 0.0), (1.0, 1.0)]
+		self.assertEqual("00 09 60 77 00 00 00 00 40 00 40 00",
+		                 hexencode(tuple))
+		self.assertEqual("00 "              # all points in glyph
+						 "02 07 08 09 "     # deltaX: [7, 8, 9]
+						 "02 04 05 06",     # deltaY: [4, 5, 6]
+						 hexencode(deltas))
+
+	def test_compile_embeddedPeak_nonIntermediate_sharedPoints(self):
+		var = TupleVariation(
+			{"wght": (0.0, 0.5, 0.5), "wdth": (0.0, 0.8, 0.8)},
+			[(7,4), (8,5), (9,6)])
+		tup, deltas, _ = var.compile(axisTags=["wght", "wdth"],
+		                          sharedCoordIndices={}, sharedPoints={0, 1, 2})
+		# len(deltas)=8; flags=EMBEDDED_PEAK_TUPLE
+		# embeddedPeak=[(0.5, 0.8)]; intermediateCoord=[]
+		self.assertEqual("00 08 80 00 20 00 33 33", hexencode(tup))
+		self.assertEqual("02 07 08 09 "     # deltaX: [7, 8, 9]
+						 "02 04 05 06",     # deltaY: [4, 5, 6]
+						 hexencode(deltas))
+
+	def test_compile_embeddedPeak_nonIntermediate_sharedConstants(self):
+		var = TupleVariation(
+			{"wght": (0.0, 0.5, 0.5), "wdth": (0.0, 0.8, 0.8)},
+			[3, 1, 4])
+		tup, deltas, _ = var.compile(axisTags=["wght", "wdth"],
+		                          sharedCoordIndices={}, sharedPoints={0, 1, 2})
+		# len(deltas)=4; flags=EMBEDDED_PEAK_TUPLE
+		# embeddedPeak=[(0.5, 0.8)]; intermediateCoord=[]
+		self.assertEqual("00 04 80 00 20 00 33 33", hexencode(tup))
+		self.assertEqual("02 03 01 04",     # delta: [3, 1, 4]
+						 hexencode(deltas))
+
+	def test_compile_embeddedPeak_intermediate_sharedPoints(self):
+		var = TupleVariation(
+			{"wght": (0.0, 0.5, 1.0), "wdth": (0.0, 0.8, 0.8)},
+			[(7,4), (8,5), (9,6)])
+		tup, deltas, _ = var.compile(axisTags=["wght", "wdth"],
+		                          sharedCoordIndices={},
+		                          sharedPoints={0, 1, 2})
+		# len(deltas)=8; flags=EMBEDDED_PEAK_TUPLE
+		# embeddedPeak=[(0.5, 0.8)]; intermediateCoord=[(0.0, 0.0), (1.0, 0.8)]
+		self.assertEqual("00 08 C0 00 20 00 33 33 00 00 00 00 40 00 33 33",
+		                hexencode(tup))
+		self.assertEqual("02 07 08 09 "  # deltaX: [7, 8, 9]
+						 "02 04 05 06",  # deltaY: [4, 5, 6]
+						 hexencode(deltas))
+
+	def test_compile_embeddedPeak_nonIntermediate_privatePoints(self):
+		var = TupleVariation(
+			{"wght": (0.0, 0.5, 0.5), "wdth": (0.0, 0.8, 0.8)},
+			[(7,4), (8,5), (9,6)])
+		tup, deltas, _ = var.compile(
+			axisTags=["wght", "wdth"], sharedCoordIndices={}, sharedPoints=None)
+		# len(deltas)=9; flags=PRIVATE_POINT_NUMBERS|EMBEDDED_PEAK_TUPLE
+		# embeddedPeak=[(0.5, 0.8)]; intermediateCoord=[]
+		self.assertEqual("00 09 A0 00 20 00 33 33", hexencode(tup))
+		self.assertEqual("00 "           # all points in glyph
+		                 "02 07 08 09 "  # deltaX: [7, 8, 9]
+		                 "02 04 05 06",  # deltaY: [4, 5, 6]
+		                 hexencode(deltas))
+
+	def test_compile_embeddedPeak_nonIntermediate_privateConstants(self):
+		var = TupleVariation(
+			{"wght": (0.0, 0.5, 0.5), "wdth": (0.0, 0.8, 0.8)},
+			[7, 8, 9])
+		tup, deltas, _ = var.compile(
+			axisTags=["wght", "wdth"], sharedCoordIndices={}, sharedPoints=None)
+		# len(deltas)=5; flags=PRIVATE_POINT_NUMBERS|EMBEDDED_PEAK_TUPLE
+		# embeddedPeak=[(0.5, 0.8)]; intermediateCoord=[]
+		self.assertEqual("00 05 A0 00 20 00 33 33", hexencode(tup))
+		self.assertEqual("00 "           # all points in glyph
+		                 "02 07 08 09",  # delta: [7, 8, 9]
+		                 hexencode(deltas))
+
+	def test_compile_embeddedPeak_intermediate_privatePoints(self):
+		var = TupleVariation(
+			{"wght": (0.4, 0.5, 0.6), "wdth": (0.7, 0.8, 0.9)},
+			[(7,4), (8,5), (9,6)])
+		tup, deltas, _ = var.compile(
+			axisTags = ["wght", "wdth"],
+			sharedCoordIndices={}, sharedPoints=None)
+		# len(deltas)=9;
+		# flags=PRIVATE_POINT_NUMBERS|INTERMEDIATE_REGION|EMBEDDED_PEAK_TUPLE
+		# embeddedPeak=(0.5, 0.8); intermediateCoord=[(0.4, 0.7), (0.6, 0.9)]
+		self.assertEqual("00 09 E0 00 20 00 33 33 19 9A 2C CD 26 66 39 9A",
+		                 hexencode(tup))
+		self.assertEqual("00 "              # all points in glyph
+		                 "02 07 08 09 "     # deltaX: [7, 8, 9]
+		                 "02 04 05 06",     # deltaY: [4, 5, 6]
+		                 hexencode(deltas))
+
+	def test_compile_embeddedPeak_intermediate_privateConstants(self):
+		var = TupleVariation(
+			{"wght": (0.4, 0.5, 0.6), "wdth": (0.7, 0.8, 0.9)},
+			[7, 8, 9])
+		tup, deltas, _ = var.compile(
+			axisTags = ["wght", "wdth"],
+			sharedCoordIndices={}, sharedPoints=None)
+		# len(deltas)=5;
+		# flags=PRIVATE_POINT_NUMBERS|INTERMEDIATE_REGION|EMBEDDED_PEAK_TUPLE
+		# embeddedPeak=(0.5, 0.8); intermediateCoord=[(0.4, 0.7), (0.6, 0.9)]
+		self.assertEqual("00 05 E0 00 20 00 33 33 19 9A 2C CD 26 66 39 9A",
+		                 hexencode(tup))
+		self.assertEqual("00 "             # all points in glyph
+		                 "02 07 08 09",    # delta: [7, 8, 9]
+		                 hexencode(deltas))
+
+	def test_compileCoord(self):
+		var = TupleVariation({"wght": (-1.0, -1.0, -1.0), "wdth": (0.4, 0.5, 0.6)}, [None] * 4)
+		self.assertEqual("C0 00 20 00", hexencode(var.compileCoord(["wght", "wdth"])))
+		self.assertEqual("20 00 C0 00", hexencode(var.compileCoord(["wdth", "wght"])))
+		self.assertEqual("C0 00", hexencode(var.compileCoord(["wght"])))
+
+	def test_compileIntermediateCoord(self):
+		var = TupleVariation({"wght": (-1.0, -1.0, 0.0), "wdth": (0.4, 0.5, 0.6)}, [None] * 4)
+		self.assertEqual("C0 00 19 9A 00 00 26 66", hexencode(var.compileIntermediateCoord(["wght", "wdth"])))
+		self.assertEqual("19 9A C0 00 26 66 00 00", hexencode(var.compileIntermediateCoord(["wdth", "wght"])))
+		self.assertEqual(None, var.compileIntermediateCoord(["wght"]))
+		self.assertEqual("19 9A 26 66", hexencode(var.compileIntermediateCoord(["wdth"])))
+
+	def test_decompileCoord(self):
+		decompileCoord = TupleVariation.decompileCoord_
+		data = deHexStr("DE AD C0 00 20 00 DE AD")
+		self.assertEqual(({"wght": -1.0, "wdth": 0.5}, 6), decompileCoord(["wght", "wdth"], data, 2))
+
+	def test_decompileCoord_roundTrip(self):
+		# Make sure we are not affected by https://github.com/behdad/fonttools/issues/286
+		data = deHexStr("7F B9 80 35")
+		values, _ = TupleVariation.decompileCoord_(["wght", "wdth"], data, 0)
+		axisValues = {axis:(val, val, val) for axis, val in  values.items()}
+		var = TupleVariation(axisValues, [None] * 4)
+		self.assertEqual("7F B9 80 35", hexencode(var.compileCoord(["wght", "wdth"])))
+
+	def test_compilePoints(self):
+		compilePoints = lambda p: TupleVariation.compilePoints(set(p), numPointsInGlyph=999)
+		self.assertEqual("00", hexencode(compilePoints(range(999))))  # all points in glyph
+		self.assertEqual("01 00 07", hexencode(compilePoints([7])))
+		self.assertEqual("01 80 FF FF", hexencode(compilePoints([65535])))
+		self.assertEqual("02 01 09 06", hexencode(compilePoints([9, 15])))
+		self.assertEqual("06 05 07 01 F7 02 01 F2", hexencode(compilePoints([7, 8, 255, 257, 258, 500])))
+		self.assertEqual("03 01 07 01 80 01 EC", hexencode(compilePoints([7, 8, 500])))
+		self.assertEqual("04 01 07 01 81 BE E7 0C 0F", hexencode(compilePoints([7, 8, 0xBEEF, 0xCAFE])))
+		self.maxDiff = None
+		self.assertEqual("81 2C" +  # 300 points (0x12c) in total
+				 " 7F 00" + (127 * " 01") +  # first run, contains 128 points: [0 .. 127]
+				 " 7F" + (128 * " 01") +  # second run, contains 128 points: [128 .. 255]
+				 " 2B" + (44 * " 01"),  # third run, contains 44 points: [256 .. 299]
+				 hexencode(compilePoints(range(300))))
+		self.assertEqual("81 8F" +  # 399 points (0x18f) in total
+				 " 7F 00" + (127 * " 01") +  # first run, contains 128 points: [0 .. 127]
+				 " 7F" + (128 * " 01") +  # second run, contains 128 points: [128 .. 255]
+				 " 7F" + (128 * " 01") +  # third run, contains 128 points: [256 .. 383]
+				 " 0E" + (15 * " 01"),  # fourth run, contains 15 points: [384 .. 398]
+				 hexencode(compilePoints(range(399))))
+
+	def test_decompilePoints(self):
+		numPointsInGlyph = 65536
+		allPoints = list(range(numPointsInGlyph))
+		def decompilePoints(data, offset):
+			points, offset = TupleVariation.decompilePoints_(numPointsInGlyph, deHexStr(data), offset, "gvar")
+			# Conversion to list needed for Python 3.
+			return (list(points), offset)
+		# all points in glyph
+		self.assertEqual((allPoints, 1), decompilePoints("00", 0))
+		# all points in glyph (in overly verbose encoding, not explicitly prohibited by spec)
+		self.assertEqual((allPoints, 2), decompilePoints("80 00", 0))
+		# 2 points; first run: [9, 9+6]
+		self.assertEqual(([9, 15], 4), decompilePoints("02 01 09 06", 0))
+		# 2 points; first run: [0xBEEF, 0xCAFE]. (0x0C0F = 0xCAFE - 0xBEEF)
+		self.assertEqual(([0xBEEF, 0xCAFE], 6), decompilePoints("02 81 BE EF 0C 0F", 0))
+		# 1 point; first run: [7]
+		self.assertEqual(([7], 3), decompilePoints("01 00 07", 0))
+		# 1 point; first run: [7] in overly verbose encoding
+		self.assertEqual(([7], 4), decompilePoints("01 80 00 07", 0))
+		# 1 point; first run: [65535]; requires words to be treated as unsigned numbers
+		self.assertEqual(([65535], 4), decompilePoints("01 80 FF FF", 0))
+		# 4 points; first run: [7, 8]; second run: [255, 257]. 257 is stored in delta-encoded bytes (0xFF + 2).
+		self.assertEqual(([7, 8, 263, 265], 7), decompilePoints("04 01 07 01 01 FF 02", 0))
+		# combination of all encodings, preceded and followed by 4 bytes of unused data
+		data = "DE AD DE AD 04 01 07 01 81 BE E7 0C 0F DE AD DE AD"
+		self.assertEqual(([7, 8, 0xBEEF, 0xCAFE], 13), decompilePoints(data, 4))
+		self.assertSetEqual(set(range(300)), set(decompilePoints(
+		    "81 2C" +  # 300 points (0x12c) in total
+		    " 7F 00" + (127 * " 01") +  # first run, contains 128 points: [0 .. 127]
+		    " 7F" + (128 * " 01") +  # second run, contains 128 points: [128 .. 255]
+		    " AB" + (44 * " 00 01"),  # third run, contains 44 points: [256 .. 299]
+		    0)[0]))
+		self.assertSetEqual(set(range(399)), set(decompilePoints(
+		    "81 8F" +  # 399 points (0x18f) in total
+		    " 7F 00" + (127 * " 01") +  # first run, contains 128 points: [0 .. 127]
+		    " 7F" + (128 * " 01") +  # second run, contains 128 points: [128 .. 255]
+		    " FF" + (128 * " 00 01") + # third run, contains 128 points: [256 .. 383]
+		    " 8E" + (15 * " 00 01"),  # fourth run, contains 15 points: [384 .. 398]
+		    0)[0]))
+
+	def test_decompilePoints_shouldAcceptBadPointNumbers(self):
+		decompilePoints = TupleVariation.decompilePoints_
+		# 2 points; first run: [3, 9].
+		numPointsInGlyph = 8
+		with CapturingLogHandler(log, "WARNING") as captor:
+			decompilePoints(numPointsInGlyph,
+			                deHexStr("02 01 03 06"), 0, "cvar")
+		self.assertIn("point 9 out of range in 'cvar' table",
+		              [r.msg for r in captor.records])
+
+	def test_decompilePoints_roundTrip(self):
+		numPointsInGlyph = 500  # greater than 255, so we also exercise code path for 16-bit encoding
+		compile = lambda points: TupleVariation.compilePoints(points, numPointsInGlyph)
+		decompile = lambda data: set(TupleVariation.decompilePoints_(numPointsInGlyph, data, 0, "gvar")[0])
+		for i in range(50):
+			points = set(random.sample(range(numPointsInGlyph), 30))
+			self.assertSetEqual(points, decompile(compile(points)),
+					    "failed round-trip decompile/compilePoints; points=%s" % points)
+		allPoints = set(range(numPointsInGlyph))
+		self.assertSetEqual(allPoints, decompile(compile(allPoints)))
+
+	def test_compileDeltas_points(self):
+		var = TupleVariation({}, [(0,0), (1, 0), (2, 0), None, (4, 0), (5, 0)])
+		points = {1, 2, 3, 4}
+		# deltaX for points: [1, 2, 4]; deltaY for points: [0, 0, 0]
+		self.assertEqual("02 01 02 04 82", hexencode(var.compileDeltas(points)))
+
+	def test_compileDeltas_constants(self):
+		var = TupleVariation({}, [0, 1, 2, None, 4, 5])
+		cvts = {1, 2, 3, 4}
+		# delta for cvts: [1, 2, 4]
+		self.assertEqual("02 01 02 04", hexencode(var.compileDeltas(cvts)))
+
+	def test_compileDeltaValues(self):
+		compileDeltaValues = lambda values: hexencode(TupleVariation.compileDeltaValues_(values))
+		# zeroes
+		self.assertEqual("80", compileDeltaValues([0]))
+		self.assertEqual("BF", compileDeltaValues([0] * 64))
+		self.assertEqual("BF 80", compileDeltaValues([0] * 65))
+		self.assertEqual("BF A3", compileDeltaValues([0] * 100))
+		self.assertEqual("BF BF BF BF", compileDeltaValues([0] * 256))
+		# bytes
+		self.assertEqual("00 01", compileDeltaValues([1]))
+		self.assertEqual("06 01 02 03 7F 80 FF FE", compileDeltaValues([1, 2, 3, 127, -128, -1, -2]))
+		self.assertEqual("3F" + (64 * " 7F"), compileDeltaValues([127] * 64))
+		self.assertEqual("3F" + (64 * " 7F") + " 00 7F", compileDeltaValues([127] * 65))
+		# words
+		self.assertEqual("40 66 66", compileDeltaValues([0x6666]))
+		self.assertEqual("43 66 66 7F FF FF FF 80 00", compileDeltaValues([0x6666, 32767, -1, -32768]))
+		self.assertEqual("7F" + (64 * " 11 22"), compileDeltaValues([0x1122] * 64))
+		self.assertEqual("7F" + (64 * " 11 22") + " 40 11 22", compileDeltaValues([0x1122] * 65))
+		# bytes, zeroes, bytes: a single zero is more compact when encoded as part of the bytes run
+		self.assertEqual("04 7F 7F 00 7F 7F", compileDeltaValues([127, 127, 0, 127, 127]))
+		self.assertEqual("01 7F 7F 81 01 7F 7F", compileDeltaValues([127, 127, 0, 0, 127, 127]))
+		self.assertEqual("01 7F 7F 82 01 7F 7F", compileDeltaValues([127, 127, 0, 0, 0, 127, 127]))
+		self.assertEqual("01 7F 7F 83 01 7F 7F", compileDeltaValues([127, 127, 0, 0, 0, 0, 127, 127]))
+		# bytes, zeroes
+		self.assertEqual("01 01 00", compileDeltaValues([1, 0]))
+		self.assertEqual("00 01 81", compileDeltaValues([1, 0, 0]))
+		# words, bytes, words: a single byte is more compact when encoded as part of the words run
+		self.assertEqual("42 66 66 00 02 77 77", compileDeltaValues([0x6666, 2, 0x7777]))
+		self.assertEqual("40 66 66 01 02 02 40 77 77", compileDeltaValues([0x6666, 2, 2, 0x7777]))
+		# words, zeroes, words
+		self.assertEqual("40 66 66 80 40 77 77", compileDeltaValues([0x6666, 0, 0x7777]))
+		self.assertEqual("40 66 66 81 40 77 77", compileDeltaValues([0x6666, 0, 0, 0x7777]))
+		self.assertEqual("40 66 66 82 40 77 77", compileDeltaValues([0x6666, 0, 0, 0, 0x7777]))
+		# words, zeroes, bytes
+		self.assertEqual("40 66 66 80 02 01 02 03", compileDeltaValues([0x6666, 0, 1, 2, 3]))
+		self.assertEqual("40 66 66 81 02 01 02 03", compileDeltaValues([0x6666, 0, 0, 1, 2, 3]))
+		self.assertEqual("40 66 66 82 02 01 02 03", compileDeltaValues([0x6666, 0, 0, 0, 1, 2, 3]))
+		# words, zeroes
+		self.assertEqual("40 66 66 80", compileDeltaValues([0x6666, 0]))
+		self.assertEqual("40 66 66 81", compileDeltaValues([0x6666, 0, 0]))
+		# bytes or words from floats
+		self.assertEqual("00 01", compileDeltaValues([1.1]))
+		self.assertEqual("00 02", compileDeltaValues([1.9]))
+		self.assertEqual("40 66 66", compileDeltaValues([0x6666 + 0.1]))
+		self.assertEqual("40 66 66", compileDeltaValues([0x6665 + 0.9]))
+
+	def test_decompileDeltas(self):
+		decompileDeltas = TupleVariation.decompileDeltas_
+		# 83 = zero values (0x80), count = 4 (1 + 0x83 & 0x3F)
+		self.assertEqual(([0, 0, 0, 0], 1), decompileDeltas(4, deHexStr("83"), 0))
+		# 41 01 02 FF FF = signed 16-bit values (0x40), count = 2 (1 + 0x41 & 0x3F)
+		self.assertEqual(([258, -1], 5), decompileDeltas(2, deHexStr("41 01 02 FF FF"), 0))
+		# 01 81 07 = signed 8-bit values, count = 2 (1 + 0x01 & 0x3F)
+		self.assertEqual(([-127, 7], 3), decompileDeltas(2, deHexStr("01 81 07"), 0))
+		# combination of all three encodings, preceded and followed by 4 bytes of unused data
+		data = deHexStr("DE AD BE EF 83 40 01 02 01 81 80 DE AD BE EF")
+		self.assertEqual(([0, 0, 0, 0, 258, -127, -128], 11), decompileDeltas(7, data, 4))
+
+	def test_decompileDeltas_roundTrip(self):
+		numDeltas = 30
+		compile = TupleVariation.compileDeltaValues_
+		decompile = lambda data: TupleVariation.decompileDeltas_(numDeltas, data, 0)[0]
+		for i in range(50):
+			deltas = random.sample(range(-128, 127), 10)
+			deltas.extend(random.sample(range(-32768, 32767), 10))
+			deltas.extend([0] * 10)
+			random.shuffle(deltas)
+			self.assertListEqual(deltas, decompile(compile(deltas)))
+
+	def test_compileSharedTuples(self):
+		# Below, the peak coordinate {"wght": 1.0, "wdth": 0.7} appears
+		# three times; {"wght": 1.0, "wdth": 0.8} appears twice.
+		# Because the start and end of variation ranges is not encoded
+		# into the shared pool, they should get ignored.
+		deltas = [None] * 4
+		variations = [
+			TupleVariation({
+				"wght": (1.0, 1.0, 1.0),
+				"wdth": (0.5, 0.7, 1.0)
+			}, deltas),
+			TupleVariation({
+				"wght": (1.0, 1.0, 1.0),
+				"wdth": (0.2, 0.7, 1.0)
+			}, deltas),
+			TupleVariation({
+				"wght": (1.0, 1.0, 1.0),
+				"wdth": (0.2, 0.8, 1.0)
+			}, deltas),
+			TupleVariation({
+				"wght": (1.0, 1.0, 1.0),
+				"wdth": (0.3, 0.7, 1.0)
+			}, deltas),
+			TupleVariation({
+				"wght": (1.0, 1.0, 1.0),
+				"wdth": (0.3, 0.8, 1.0)
+			}, deltas),
+			TupleVariation({
+				"wght": (1.0, 1.0, 1.0),
+				"wdth": (0.3, 0.9, 1.0)
+            }, deltas)
+		]
+		result = compileSharedTuples(["wght", "wdth"], variations)
+		self.assertEqual([hexencode(c) for c in result],
+		                 ["40 00 2C CD", "40 00 33 33"])
+
+	def test_decompileSharedTuples_Skia(self):
+		sharedTuples = decompileSharedTuples(
+			axisTags=["wght", "wdth"], sharedTupleCount=8,
+			data=SKIA_GVAR_SHARED_TUPLES_DATA, offset=0)
+		self.assertEqual(sharedTuples, SKIA_GVAR_SHARED_TUPLES)
+
+	def test_decompileSharedTuples_empty(self):
+		self.assertEqual(decompileSharedTuples(["wght"], 0, b"", 0), [])
+
+	def test_compileTupleVariationStore_allVariationsRedundant(self):
+		axes = {"wght": (0.3, 0.4, 0.5), "opsz": (0.7, 0.8, 0.9)}
+		variations = [
+			TupleVariation(axes, [None] * 4),
+			TupleVariation(axes, [None] * 4),
+			TupleVariation(axes, [None] * 4)
+		]
+		self.assertEqual(
+			compileTupleVariationStore(variations, pointCount=8,
+			                           axisTags=["wght", "opsz"],
+			                           sharedTupleIndices={}),
+            (0, b"", b""))
+
+	def test_compileTupleVariationStore_noVariations(self):
+		self.assertEqual(
+			compileTupleVariationStore(variations=[], pointCount=8,
+			                           axisTags=["wght", "opsz"],
+			                           sharedTupleIndices={}),
+            (0, b"", b""))
+
+	def test_compileTupleVariationStore_roundTrip_cvar(self):
+		deltas = [1, 2, 3, 4]
+		variations = [
+			TupleVariation({"wght": (0.5, 1.0, 1.0), "wdth": (1.0, 1.0, 1.0)},
+			               deltas),
+			TupleVariation({"wght": (1.0, 1.0, 1.0), "wdth": (1.0, 1.0, 1.0)},
+			               deltas)
+		]
+		tupleVariationCount, tuples, data = compileTupleVariationStore(
+			variations, pointCount=4, axisTags=["wght", "wdth"],
+			sharedTupleIndices={})
+		self.assertEqual(
+			decompileTupleVariationStore("cvar", ["wght", "wdth"],
+			                             tupleVariationCount, pointCount=4,
+			                             sharedTuples={}, data=(tuples + data),
+			                             pos=0, dataPos=len(tuples)),
+            variations)
+
+	def test_compileTupleVariationStore_roundTrip_gvar(self):
+		deltas = [(1,1), (2,2), (3,3), (4,4)]
+		variations = [
+			TupleVariation({"wght": (0.5, 1.0, 1.0), "wdth": (1.0, 1.0, 1.0)},
+			               deltas),
+			TupleVariation({"wght": (1.0, 1.0, 1.0), "wdth": (1.0, 1.0, 1.0)},
+			               deltas)
+		]
+		tupleVariationCount, tuples, data = compileTupleVariationStore(
+			variations, pointCount=4, axisTags=["wght", "wdth"],
+			sharedTupleIndices={})
+		self.assertEqual(
+			decompileTupleVariationStore("gvar", ["wght", "wdth"],
+			                             tupleVariationCount, pointCount=4,
+			                             sharedTuples={}, data=(tuples + data),
+			                             pos=0, dataPos=len(tuples)),
+            variations)
+
+	def test_decompileTupleVariationStore_Skia_I(self):
+		tvar = decompileTupleVariationStore(
+			tableTag="gvar", axisTags=["wght", "wdth"],
+			tupleVariationCount=8, pointCount=18,
+			sharedTuples=SKIA_GVAR_SHARED_TUPLES,
+			data=SKIA_GVAR_I_DATA, pos=4, dataPos=36)
+		self.assertEqual(len(tvar), 8)
+		self.assertEqual(tvar[0].axes, {"wght": (0.0, 1.0, 1.0)})
+		self.assertEqual(
+			" ".join(["%d,%d" % c for c in tvar[0].coordinates]),
+			"257,0 -127,0 -128,58 -130,90 -130,62 -130,67 -130,32 -127,0 "
+			"257,0 259,14 260,64 260,21 260,69 258,124 0,0 130,0 0,0 0,0")
+
+	def test_decompileTupleVariationStore_empty(self):
+		self.assertEqual(
+			decompileTupleVariationStore(tableTag="gvar", axisTags=[],
+			                             tupleVariationCount=0, pointCount=5,
+			                             sharedTuples=[],
+			                             data=b"", pos=4, dataPos=4),
+			[])
+
+	def test_getTupleSize(self):
+		getTupleSize = TupleVariation.getTupleSize_
+		numAxes = 3
+		self.assertEqual(4 + numAxes * 2, getTupleSize(0x8042, numAxes))
+		self.assertEqual(4 + numAxes * 4, getTupleSize(0x4077, numAxes))
+		self.assertEqual(4, getTupleSize(0x2077, numAxes))
+		self.assertEqual(4, getTupleSize(11, numAxes))
+
+	def test_inferRegion(self):
+		start, end = inferRegion_({"wght": -0.3, "wdth": 0.7})
+		self.assertEqual(start, {"wght": -0.3, "wdth": 0.0})
+		self.assertEqual(end, {"wght": 0.0, "wdth": 0.7})
+
+	@staticmethod
+	def xml_lines(writer):
+		content = writer.file.getvalue().decode("utf-8")
+		return [line.strip() for line in content.splitlines()][1:]
+
+
+if __name__ == "__main__":
+	import sys
+	sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/_a_n_k_r_test.py b/Tests/ttLib/tables/_a_n_k_r_test.py
new file mode 100644
index 0000000..0327e46
--- /dev/null
+++ b/Tests/ttLib/tables/_a_n_k_r_test.py
@@ -0,0 +1,167 @@
+# coding: utf-8
+from __future__ import print_function, division, absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.misc.testTools import FakeFont, getXML, parseXML
+from fontTools.misc.textTools import deHexStr, hexStr
+from fontTools.ttLib import newTable
+import unittest
+
+
+# This is the anchor points table of the first font file in
+# “/Library/Fonts/Devanagari Sangam MN.ttc” on macOS 10.12.6.
+# For testing, we’ve changed the GlyphIDs to smaller values.
+# Also, in the AATLookup, we’ve changed GlyphDataOffset value
+# for the end-of-table marker from 0xFFFF to 0 since that is
+# what our encoder emits. (The value for end-of-table markers
+# does not actually matter).
+ANKR_FORMAT_0_DATA = deHexStr(
+    '0000 0000 '       #  0: Format=0, Flags=0
+    '0000 000C '       #  4: LookupTableOffset=12
+    '0000 0024 '       #  8: GlyphDataTableOffset=36
+    '0006 0004 0002 '  # 12: LookupFormat=6, UnitSize=4, NUnits=2
+    '0008 0001 0000 '  # 18: SearchRange=8, EntrySelector=1, RangeShift=0
+    '0001 0000 '       # 24: Glyph=A, Offset=0 (+GlyphDataTableOffset=36)
+    '0003 0008 '       # 28: Glyph=C, Offset=8 (+GlyphDataTableOffset=44)
+    'FFFF 0000 '       # 32: Glyph=<end>, Offset=<n/a>
+    '0000 0001 '       # 36: GlyphData[A].NumPoints=1
+    '0235 045E '       # 40: GlyphData[A].Points[0].X=565, .Y=1118
+    '0000 0001 '       # 44: GlyphData[C].NumPoints=1
+    'FED2 045E '       # 48: GlyphData[C].Points[0].X=-302, .Y=1118
+)                      # 52: <end>
+assert len(ANKR_FORMAT_0_DATA) == 52
+
+
+ANKR_FORMAT_0_XML = [
+    '<AnchorPoints Format="0">',
+    '  <Flags value="0"/>',
+    '  <Anchors>',
+    '    <Lookup glyph="A">',
+    '      <!-- AnchorPointCount=1 -->',
+    '      <AnchorPoint index="0">',
+    '        <XCoordinate value="565"/>',
+    '        <YCoordinate value="1118"/>',
+    '      </AnchorPoint>',
+    '    </Lookup>',
+    '    <Lookup glyph="C">',
+    '      <!-- AnchorPointCount=1 -->',
+    '      <AnchorPoint index="0">',
+    '        <XCoordinate value="-302"/>',
+    '        <YCoordinate value="1118"/>',
+    '      </AnchorPoint>',
+    '    </Lookup>',
+    '  </Anchors>',
+    '</AnchorPoints>',
+]
+
+
+# Same data as ANKR_FORMAT_0_DATA, but with chunks of unused data
+# whose presence should not stop us from decompiling the table.
+ANKR_FORMAT_0_STRAY_DATA = deHexStr(
+    '0000 0000 '       #  0: Format=0, Flags=0
+    '0000 0018 '       #  4: LookupTableOffset=24
+    '0000 0034 '       #  8: GlyphDataTableOffset=52
+    'DEAD BEEF CAFE '  # 12: <stray data>
+    'DEAD BEEF CAFE '  # 18: <stray data>
+    '0006 0004 0002 '  # 24: LookupFormat=6, UnitSize=4, NUnits=2
+    '0008 0001 0000 '  # 30: SearchRange=8, EntrySelector=1, RangeShift=0
+    '0001 0000 '       # 36: Glyph=A, Offset=0 (+GlyphDataTableOffset=52)
+    '0003 0008 '       # 40: Glyph=C, Offset=8 (+GlyphDataTableOffset=60)
+    'FFFF 0000 '       # 44: Glyph=<end>, Offset=<n/a>
+    'BEEF F00D '       # 48: <stray data>
+    '0000 0001 '       # 52: GlyphData[A].NumPoints=1
+    '0235 045E '       # 56: GlyphData[A].Points[0].X=565, .Y=1118
+    '0000 0001 '       # 60: GlyphData[C].NumPoints=1
+    'FED2 045E '       # 64: GlyphData[C].Points[0].X=-302, .Y=1118
+)                      # 68: <end>
+assert len(ANKR_FORMAT_0_STRAY_DATA) == 68
+
+
+# Constructed test case where glyphs A and D share the same anchor data.
+ANKR_FORMAT_0_SHARING_DATA = deHexStr(
+    '0000 0000 '       #  0: Format=0, Flags=0
+    '0000 000C '       #  4: LookupTableOffset=12
+    '0000 0028 '       #  8: GlyphDataTableOffset=40
+    '0006 0004 0003 '  # 12: LookupFormat=6, UnitSize=4, NUnits=3
+    '0008 0001 0004 '  # 18: SearchRange=8, EntrySelector=1, RangeShift=4
+    '0001 0000 '       # 24: Glyph=A, Offset=0 (+GlyphDataTableOffset=36)
+    '0003 0008 '       # 28: Glyph=C, Offset=8 (+GlyphDataTableOffset=44)
+    '0004 0000 '       # 32: Glyph=D, Offset=0 (+GlyphDataTableOffset=36)
+    'FFFF 0000 '       # 36: Glyph=<end>, Offset=<n/a>
+    '0000 0001 '       # 40: GlyphData[A].NumPoints=1
+    '0235 045E '       # 44: GlyphData[A].Points[0].X=565, .Y=1118
+    '0000 0002 '       # 48: GlyphData[C].NumPoints=2
+    '000B 000C '       # 52: GlyphData[C].Points[0].X=11, .Y=12
+    '001B 001C '       # 56: GlyphData[C].Points[1].X=27, .Y=28
+)                      # 60: <end>
+assert len(ANKR_FORMAT_0_SHARING_DATA) == 60
+
+
+ANKR_FORMAT_0_SHARING_XML = [
+    '<AnchorPoints Format="0">',
+    '  <Flags value="0"/>',
+    '  <Anchors>',
+    '    <Lookup glyph="A">',
+    '      <!-- AnchorPointCount=1 -->',
+    '      <AnchorPoint index="0">',
+    '        <XCoordinate value="565"/>',
+    '        <YCoordinate value="1118"/>',
+    '      </AnchorPoint>',
+    '    </Lookup>',
+    '    <Lookup glyph="C">',
+    '      <!-- AnchorPointCount=2 -->',
+    '      <AnchorPoint index="0">',
+    '        <XCoordinate value="11"/>',
+    '        <YCoordinate value="12"/>',
+    '      </AnchorPoint>',
+    '      <AnchorPoint index="1">',
+    '        <XCoordinate value="27"/>',
+    '        <YCoordinate value="28"/>',
+    '      </AnchorPoint>',
+    '    </Lookup>',
+    '    <Lookup glyph="D">',
+    '      <!-- AnchorPointCount=1 -->',
+    '      <AnchorPoint index="0">',
+    '        <XCoordinate value="565"/>',
+    '        <YCoordinate value="1118"/>',
+    '      </AnchorPoint>',
+    '    </Lookup>',
+    '  </Anchors>',
+    '</AnchorPoints>',
+]
+
+
+class ANKRTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.maxDiff = None
+        cls.font = FakeFont(['.notdef', 'A', 'B', 'C', 'D'])
+
+    def decompileToXML(self, data, xml):
+        table = newTable('ankr')
+        table.decompile(data, self.font)
+        self.assertEqual(getXML(table.toXML), xml)
+
+    def compileFromXML(self, xml, data):
+        table = newTable('ankr')
+        for name, attrs, content in parseXML(xml):
+            table.fromXML(name, attrs, content, font=self.font)
+        self.assertEqual(hexStr(table.compile(self.font)), hexStr(data))
+
+    def roundtrip(self, data, xml):
+        self.decompileToXML(data, xml)
+        self.compileFromXML(xml, data)
+
+    def testFormat0(self):
+        self.roundtrip(ANKR_FORMAT_0_DATA, ANKR_FORMAT_0_XML)
+
+    def testFormat0_stray(self):
+        self.decompileToXML(ANKR_FORMAT_0_STRAY_DATA, ANKR_FORMAT_0_XML)
+
+    def testFormat0_sharing(self):
+        self.roundtrip(ANKR_FORMAT_0_SHARING_DATA, ANKR_FORMAT_0_SHARING_XML)
+
+
+if __name__ == '__main__':
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/_a_v_a_r_test.py b/Tests/ttLib/tables/_a_v_a_r_test.py
new file mode 100644
index 0000000..7b92118
--- /dev/null
+++ b/Tests/ttLib/tables/_a_v_a_r_test.py
@@ -0,0 +1,85 @@
+from __future__ import print_function, division, absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.misc.testTools import parseXML
+from fontTools.misc.textTools import deHexStr
+from fontTools.misc.xmlWriter import XMLWriter
+from fontTools.ttLib import TTLibError
+from fontTools.ttLib.tables._a_v_a_r import table__a_v_a_r
+from fontTools.ttLib.tables._f_v_a_r import table__f_v_a_r, Axis
+import collections
+import logging
+import unittest
+
+
+TEST_DATA = deHexStr(
+    "00 01 00 00 00 00 00 02 "
+    "00 04 C0 00 C0 00 00 00 00 00 13 33 33 33 40 00 40 00 "
+    "00 03 C0 00 C0 00 00 00 00 00 40 00 40 00")
+
+
+class AxisVariationTableTest(unittest.TestCase):
+    def test_compile(self):
+        avar = table__a_v_a_r()
+        avar.segments["wdth"] = {-1.0: -1.0, 0.0: 0.0, 0.3: 0.8, 1.0: 1.0}
+        avar.segments["wght"] = {-1.0: -1.0, 0.0: 0.0, 1.0: 1.0}
+        self.assertEqual(TEST_DATA, avar.compile(self.makeFont(["wdth", "wght"])))
+
+    def test_decompile(self):
+        avar = table__a_v_a_r()
+        avar.decompile(TEST_DATA, self.makeFont(["wdth", "wght"]))
+        self.assertEqual({
+            "wdth": {-1.0: -1.0, 0.0: 0.0, 0.3: 0.8, 1.0: 1.0},
+            "wght": {-1.0: -1.0, 0.0: 0.0, 1.0: 1.0}
+        }, avar.segments)
+
+    def test_decompile_unsupportedVersion(self):
+        avar = table__a_v_a_r()
+        font = self.makeFont(["wdth", "wght"])
+        self.assertRaises(TTLibError, avar.decompile, deHexStr("02 01 03 06 00 00 00 00"), font)
+
+    def test_toXML(self):
+        avar = table__a_v_a_r()
+        avar.segments["opsz"] = {-1.0: -1.0, 0.0: 0.0, 0.3: 0.8, 1.0: 1.0}
+        writer = XMLWriter(BytesIO())
+        avar.toXML(writer, self.makeFont(["opsz"]))
+        self.assertEqual([
+            '<segment axis="opsz">',
+                '<mapping from="-1.0" to="-1.0"/>',
+                '<mapping from="0.0" to="0.0"/>',
+                '<mapping from="0.3" to="0.8"/>',
+                '<mapping from="1.0" to="1.0"/>',
+            '</segment>'
+        ], self.xml_lines(writer))
+
+    def test_fromXML(self):
+        avar = table__a_v_a_r()
+        for name, attrs, content in parseXML(
+                '<segment axis="wdth">'
+                '    <mapping from="-1.0" to="-1.0"/>'
+                '    <mapping from="0.0" to="0.0"/>'
+                '    <mapping from="0.7" to="0.2"/>'
+                '    <mapping from="1.0" to="1.0"/>'
+                '</segment>'):
+            avar.fromXML(name, attrs, content, ttFont=None)
+        self.assertEqual({"wdth": {-1: -1, 0: 0, 0.7: 0.2, 1.0: 1.0}},
+                         avar.segments)
+
+    @staticmethod
+    def makeFont(axisTags):
+        """['opsz', 'wdth'] --> ttFont"""
+        fvar = table__f_v_a_r()
+        for tag in axisTags:
+            axis = Axis()
+            axis.axisTag = tag
+            fvar.axes.append(axis)
+        return {"fvar": fvar}
+
+    @staticmethod
+    def xml_lines(writer):
+        content = writer.file.getvalue().decode("utf-8")
+        return [line.strip() for line in content.splitlines()][1:]
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/_b_s_l_n_test.py b/Tests/ttLib/tables/_b_s_l_n_test.py
new file mode 100644
index 0000000..97ffa6a
--- /dev/null
+++ b/Tests/ttLib/tables/_b_s_l_n_test.py
@@ -0,0 +1,311 @@
+from __future__ import print_function, division, absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.misc.testTools import FakeFont, getXML, parseXML
+from fontTools.misc.textTools import deHexStr, hexStr
+from fontTools.ttLib import newTable
+import unittest
+
+
+# Apple's spec of the baseline table gives no example for 'bsln' format 0,
+# but the Apple Chancery font contains the following data.
+BSLN_FORMAT_0_DATA = deHexStr(
+    '0001 0000 0000 '       #  0: Version=1.0, Format=0
+    '0000 '                 #  6: DefaultBaseline=0 (Roman baseline)
+    '0000 01D1 0000 0541 '  #  8: Delta[0..3]=0, 465, 0, 1345
+    '01FB 0000 0000 0000 '  # 16: Delta[4..7]=507, 0, 0, 0
+    '0000 0000 0000 0000 '  # 24: Delta[8..11]=0, 0, 0, 0
+    '0000 0000 0000 0000 '  # 32: Delta[12..15]=0, 0, 0, 0
+    '0000 0000 0000 0000 '  # 40: Delta[16..19]=0, 0, 0, 0
+    '0000 0000 0000 0000 '  # 48: Delta[20..23]=0, 0, 0, 0
+    '0000 0000 0000 0000 '  # 56: Delta[24..27]=0, 0, 0, 0
+    '0000 0000 0000 0000 '  # 64: Delta[28..31]=0, 0, 0, 0
+)                           # 72: <end>
+assert len(BSLN_FORMAT_0_DATA) == 72
+
+
+BSLN_FORMAT_0_XML = [
+    '<Version value="0x00010000"/>',
+    '<Baseline Format="0">',
+    '  <DefaultBaseline value="0"/>',
+    '  <Delta index="0" value="0"/>',
+    '  <Delta index="1" value="465"/>',
+    '  <Delta index="2" value="0"/>',
+    '  <Delta index="3" value="1345"/>',
+    '  <Delta index="4" value="507"/>',
+    '  <Delta index="5" value="0"/>',
+    '  <Delta index="6" value="0"/>',
+    '  <Delta index="7" value="0"/>',
+    '  <Delta index="8" value="0"/>',
+    '  <Delta index="9" value="0"/>',
+    '  <Delta index="10" value="0"/>',
+    '  <Delta index="11" value="0"/>',
+    '  <Delta index="12" value="0"/>',
+    '  <Delta index="13" value="0"/>',
+    '  <Delta index="14" value="0"/>',
+    '  <Delta index="15" value="0"/>',
+    '  <Delta index="16" value="0"/>',
+    '  <Delta index="17" value="0"/>',
+    '  <Delta index="18" value="0"/>',
+    '  <Delta index="19" value="0"/>',
+    '  <Delta index="20" value="0"/>',
+    '  <Delta index="21" value="0"/>',
+    '  <Delta index="22" value="0"/>',
+    '  <Delta index="23" value="0"/>',
+    '  <Delta index="24" value="0"/>',
+    '  <Delta index="25" value="0"/>',
+    '  <Delta index="26" value="0"/>',
+    '  <Delta index="27" value="0"/>',
+    '  <Delta index="28" value="0"/>',
+    '  <Delta index="29" value="0"/>',
+    '  <Delta index="30" value="0"/>',
+    '  <Delta index="31" value="0"/>',
+    '</Baseline>',
+]
+
+
+# Example: Format 1 Baseline Table
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6bsln.html
+# The example in the AAT specification uses the value 270 for Seg[0].LastGlyph,
+# whereas we use the value 10 for testng to shorten the XML dump.
+BSLN_FORMAT_1_DATA = deHexStr(
+    '0001 0000 0001 '       #  0: Version=1.0, Format=1
+    '0001 '                 #  6: DefaultBaseline=1 (Ideographic baseline)
+    '0000 0357 0000 05F0 '  #  8: Delta[0..3]=0, 855, 0, 1520
+    '0000 0000 0000 0000 '  # 16: Delta[4..7]=0, 0, 0, 0
+    '0000 0000 0000 0000 '  # 24: Delta[8..11]=0, 0, 0, 0
+    '0000 0000 0000 0000 '  # 32: Delta[12..15]=0, 0, 0, 0
+    '0000 0000 0000 0000 '  # 40: Delta[16..19]=0, 0, 0, 0
+    '0000 0000 0000 0000 '  # 48: Delta[20..23]=0, 0, 0, 0
+    '0000 0000 0000 0000 '  # 56: Delta[24..27]=0, 0, 0, 0
+    '0000 0000 0000 0000 '  # 64: Delta[28..31]=0, 0, 0, 0
+    '0002 0006 0001 '       # 72: LookupFormat=2, UnitSize=6, NUnits=1
+    '0006 0000 0000 '       # 78: SearchRange=6, EntrySelector=0, RangeShift=0
+    '000A 0002 0000 '       # 84: Seg[0].LastGlyph=10 FirstGl=2 Value=0/Roman
+    'FFFF FFFF 0000 '       # 90: Seg[1]=<end>
+)                           # 96: <end>
+assert len(BSLN_FORMAT_1_DATA) == 96
+
+
+BSLN_FORMAT_1_XML = [
+    '<Version value="0x00010000"/>',
+    '<Baseline Format="1">',
+    '  <DefaultBaseline value="1"/>',
+    '  <Delta index="0" value="0"/>',
+    '  <Delta index="1" value="855"/>',
+    '  <Delta index="2" value="0"/>',
+    '  <Delta index="3" value="1520"/>',
+    '  <Delta index="4" value="0"/>',
+    '  <Delta index="5" value="0"/>',
+    '  <Delta index="6" value="0"/>',
+    '  <Delta index="7" value="0"/>',
+    '  <Delta index="8" value="0"/>',
+    '  <Delta index="9" value="0"/>',
+    '  <Delta index="10" value="0"/>',
+    '  <Delta index="11" value="0"/>',
+    '  <Delta index="12" value="0"/>',
+    '  <Delta index="13" value="0"/>',
+    '  <Delta index="14" value="0"/>',
+    '  <Delta index="15" value="0"/>',
+    '  <Delta index="16" value="0"/>',
+    '  <Delta index="17" value="0"/>',
+    '  <Delta index="18" value="0"/>',
+    '  <Delta index="19" value="0"/>',
+    '  <Delta index="20" value="0"/>',
+    '  <Delta index="21" value="0"/>',
+    '  <Delta index="22" value="0"/>',
+    '  <Delta index="23" value="0"/>',
+    '  <Delta index="24" value="0"/>',
+    '  <Delta index="25" value="0"/>',
+    '  <Delta index="26" value="0"/>',
+    '  <Delta index="27" value="0"/>',
+    '  <Delta index="28" value="0"/>',
+    '  <Delta index="29" value="0"/>',
+    '  <Delta index="30" value="0"/>',
+    '  <Delta index="31" value="0"/>',
+    '  <BaselineValues>',
+    '    <Lookup glyph="B" value="0"/>',
+    '    <Lookup glyph="C" value="0"/>',
+    '    <Lookup glyph="D" value="0"/>',
+    '    <Lookup glyph="E" value="0"/>',
+    '    <Lookup glyph="F" value="0"/>',
+    '    <Lookup glyph="G" value="0"/>',
+    '    <Lookup glyph="H" value="0"/>',
+    '    <Lookup glyph="I" value="0"/>',
+    '    <Lookup glyph="J" value="0"/>',
+    '  </BaselineValues>',
+    '</Baseline>',
+]
+
+
+BSLN_FORMAT_2_DATA = deHexStr(
+    '0001 0000 0002 '       #  0: Version=1.0, Format=2
+    '0004 '                 #  6: DefaultBaseline=4 (Math)
+    '0016 '                 #  8: StandardGlyph=22
+    '0050 0051 FFFF 0052 '  # 10: ControlPoint[0..3]=80, 81, <none>, 82
+    'FFFF FFFF FFFF FFFF '  # 18: ControlPoint[4..7]=<none>
+    'FFFF FFFF FFFF FFFF '  # 26: ControlPoint[8..11]=<none>
+    'FFFF FFFF FFFF FFFF '  # 34: ControlPoint[12..15]=<none>
+    'FFFF FFFF FFFF FFFF '  # 42: ControlPoint[16..19]=<none>
+    'FFFF FFFF FFFF FFFF '  # 50: ControlPoint[20..23]=<none>
+    'FFFF FFFF FFFF FFFF '  # 58: ControlPoint[24..27]=<none>
+    'FFFF FFFF FFFF FFFF '  # 66: ControlPoint[28..31]=<none>
+)                           # 74: <end>
+assert len(BSLN_FORMAT_2_DATA) == 74
+
+
+BSLN_FORMAT_2_XML = [
+    '<Version value="0x00010000"/>',
+    '<Baseline Format="2">',
+    '  <DefaultBaseline value="4"/>',
+    '  <StandardGlyph value="V"/>',
+    '  <ControlPoint index="0" value="80"/>',
+    '  <ControlPoint index="1" value="81"/>',
+    '  <ControlPoint index="2" value="65535"/>',
+    '  <ControlPoint index="3" value="82"/>',
+    '  <ControlPoint index="4" value="65535"/>',
+    '  <ControlPoint index="5" value="65535"/>',
+    '  <ControlPoint index="6" value="65535"/>',
+    '  <ControlPoint index="7" value="65535"/>',
+    '  <ControlPoint index="8" value="65535"/>',
+    '  <ControlPoint index="9" value="65535"/>',
+    '  <ControlPoint index="10" value="65535"/>',
+    '  <ControlPoint index="11" value="65535"/>',
+    '  <ControlPoint index="12" value="65535"/>',
+    '  <ControlPoint index="13" value="65535"/>',
+    '  <ControlPoint index="14" value="65535"/>',
+    '  <ControlPoint index="15" value="65535"/>',
+    '  <ControlPoint index="16" value="65535"/>',
+    '  <ControlPoint index="17" value="65535"/>',
+    '  <ControlPoint index="18" value="65535"/>',
+    '  <ControlPoint index="19" value="65535"/>',
+    '  <ControlPoint index="20" value="65535"/>',
+    '  <ControlPoint index="21" value="65535"/>',
+    '  <ControlPoint index="22" value="65535"/>',
+    '  <ControlPoint index="23" value="65535"/>',
+    '  <ControlPoint index="24" value="65535"/>',
+    '  <ControlPoint index="25" value="65535"/>',
+    '  <ControlPoint index="26" value="65535"/>',
+    '  <ControlPoint index="27" value="65535"/>',
+    '  <ControlPoint index="28" value="65535"/>',
+    '  <ControlPoint index="29" value="65535"/>',
+    '  <ControlPoint index="30" value="65535"/>',
+    '  <ControlPoint index="31" value="65535"/>',
+    '</Baseline>',
+]
+
+
+# Example: Format 3 Baseline Table
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6bsln.html
+# The example in the AAT specification uses the value 270 for Seg[0].LastGlyph,
+# whereas we use the value 10 for testng to shorten the XML dump.
+BSLN_FORMAT_3_DATA = deHexStr(
+    '0001 0000 0003 '       #  0: Version=1.0, Format=3
+    '0001 '                 #  6: DefaultBaseline=1 (Ideographic)
+    '0016 '                 #  8: StandardGlyph=22
+    '0050 0051 FFFF 0052 '  # 10: ControlPoint[0..3]=80, 81, <none>, 82
+    'FFFF FFFF FFFF FFFF '  # 18: ControlPoint[4..7]=<none>
+    'FFFF FFFF FFFF FFFF '  # 26: ControlPoint[8..11]=<none>
+    'FFFF FFFF FFFF FFFF '  # 34: ControlPoint[12..15]=<none>
+    'FFFF FFFF FFFF FFFF '  # 42: ControlPoint[16..19]=<none>
+    'FFFF FFFF FFFF FFFF '  # 50: ControlPoint[20..23]=<none>
+    'FFFF FFFF FFFF FFFF '  # 58: ControlPoint[24..27]=<none>
+    'FFFF FFFF FFFF FFFF '  # 66: ControlPoint[28..31]=<none>
+    '0002 0006 0001 '       # 74: LookupFormat=2, UnitSize=6, NUnits=1
+    '0006 0000 0000 '       # 80: SearchRange=6, EntrySelector=0, RangeShift=0
+    '000A 0002 0000 '       # 86: Seg[0].LastGlyph=10 FirstGl=2 Value=0/Roman
+    'FFFF FFFF 0000 '       # 92: Seg[1]=<end>
+)                           # 98: <end>
+assert len(BSLN_FORMAT_3_DATA) == 98
+
+
+BSLN_FORMAT_3_XML = [
+    '<Version value="0x00010000"/>',
+    '<Baseline Format="3">',
+    '  <DefaultBaseline value="1"/>',
+    '  <StandardGlyph value="V"/>',
+    '  <ControlPoint index="0" value="80"/>',
+    '  <ControlPoint index="1" value="81"/>',
+    '  <ControlPoint index="2" value="65535"/>',
+    '  <ControlPoint index="3" value="82"/>',
+    '  <ControlPoint index="4" value="65535"/>',
+    '  <ControlPoint index="5" value="65535"/>',
+    '  <ControlPoint index="6" value="65535"/>',
+    '  <ControlPoint index="7" value="65535"/>',
+    '  <ControlPoint index="8" value="65535"/>',
+    '  <ControlPoint index="9" value="65535"/>',
+    '  <ControlPoint index="10" value="65535"/>',
+    '  <ControlPoint index="11" value="65535"/>',
+    '  <ControlPoint index="12" value="65535"/>',
+    '  <ControlPoint index="13" value="65535"/>',
+    '  <ControlPoint index="14" value="65535"/>',
+    '  <ControlPoint index="15" value="65535"/>',
+    '  <ControlPoint index="16" value="65535"/>',
+    '  <ControlPoint index="17" value="65535"/>',
+    '  <ControlPoint index="18" value="65535"/>',
+    '  <ControlPoint index="19" value="65535"/>',
+    '  <ControlPoint index="20" value="65535"/>',
+    '  <ControlPoint index="21" value="65535"/>',
+    '  <ControlPoint index="22" value="65535"/>',
+    '  <ControlPoint index="23" value="65535"/>',
+    '  <ControlPoint index="24" value="65535"/>',
+    '  <ControlPoint index="25" value="65535"/>',
+    '  <ControlPoint index="26" value="65535"/>',
+    '  <ControlPoint index="27" value="65535"/>',
+    '  <ControlPoint index="28" value="65535"/>',
+    '  <ControlPoint index="29" value="65535"/>',
+    '  <ControlPoint index="30" value="65535"/>',
+    '  <ControlPoint index="31" value="65535"/>',
+    '  <BaselineValues>',
+    '    <Lookup glyph="B" value="0"/>',
+    '    <Lookup glyph="C" value="0"/>',
+    '    <Lookup glyph="D" value="0"/>',
+    '    <Lookup glyph="E" value="0"/>',
+    '    <Lookup glyph="F" value="0"/>',
+    '    <Lookup glyph="G" value="0"/>',
+    '    <Lookup glyph="H" value="0"/>',
+    '    <Lookup glyph="I" value="0"/>',
+    '    <Lookup glyph="J" value="0"/>',
+    '  </BaselineValues>',
+    '</Baseline>',
+]
+
+
+class BSLNTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.maxDiff = None
+        cls.font = FakeFont(
+            ['.notdef'] + [g for g in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'])
+
+    def decompileToXML(self, data, xml):
+        table = newTable('bsln')
+        table.decompile(data, self.font)
+        self.assertEqual(getXML(table.toXML), xml)
+
+    def compileFromXML(self, xml, data):
+        table = newTable('bsln')
+        for name, attrs, content in parseXML(xml):
+            table.fromXML(name, attrs, content, font=self.font)
+        self.assertEqual(hexStr(table.compile(self.font)), hexStr(data))
+
+    def testFormat0(self):
+        self.decompileToXML(BSLN_FORMAT_0_DATA, BSLN_FORMAT_0_XML)
+        self.compileFromXML(BSLN_FORMAT_0_XML, BSLN_FORMAT_0_DATA)
+
+    def testFormat1(self):
+        self.decompileToXML(BSLN_FORMAT_1_DATA, BSLN_FORMAT_1_XML)
+        self.compileFromXML(BSLN_FORMAT_1_XML, BSLN_FORMAT_1_DATA)
+
+    def testFormat2(self):
+        self.decompileToXML(BSLN_FORMAT_2_DATA, BSLN_FORMAT_2_XML)
+        self.compileFromXML(BSLN_FORMAT_2_XML, BSLN_FORMAT_2_DATA)
+
+    def testFormat3(self):
+        self.decompileToXML(BSLN_FORMAT_3_DATA, BSLN_FORMAT_3_XML)
+        self.compileFromXML(BSLN_FORMAT_3_XML, BSLN_FORMAT_3_DATA)
+
+
+if __name__ == '__main__':
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/_c_i_d_g_test.py b/Tests/ttLib/tables/_c_i_d_g_test.py
new file mode 100644
index 0000000..c6e8d3c
--- /dev/null
+++ b/Tests/ttLib/tables/_c_i_d_g_test.py
@@ -0,0 +1,70 @@
+# coding: utf-8
+from __future__ import print_function, division, absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.misc.testTools import FakeFont, getXML, parseXML
+from fontTools.misc.textTools import deHexStr, hexStr
+from fontTools.ttLib import newTable
+import unittest
+
+
+# On macOS X 10.12.6, the first font in /System/Library/Fonts/PingFang.ttc
+# has a ‘cidg’ table with a similar structure as this test data, just larger.
+CIDG_DATA = deHexStr(
+    "0000 0000 "          #   0: Format=0, Flags=0
+    "0000 0098 "          #   4: StructLength=152
+    "0000 "               #   8: Registry=0
+    "41 64 6F 62 65 "     #  10: RegistryName="Adobe"
+    + ("00" * 59) +       #  15: <padding>
+    "0002 "               #  74: Order=2
+    "43 4E 53 31 "        #  76: Order="CNS1"
+    + ("00" * 60) +       #  80: <padding>
+    "0000 "               # 140: SupplementVersion=0
+    "0004 "               # 142: Count
+    "0000 "               # 144: GlyphID[0]=.notdef
+    "FFFF "               # 146: CIDs[1]=<None>
+    "0003 "               # 148: CIDs[2]=C
+    "0001 "               # 150: CIDs[3]=A
+)                         # 152: <end>
+assert len(CIDG_DATA) == 152, len(CIDG_DATA)
+
+
+CIDG_XML = [
+   '<CIDGlyphMapping Format="0">',
+   '  <DataFormat value="0"/>',
+   '  <!-- StructLength=152 -->',
+   '  <Registry value="0"/>',
+   '  <RegistryName value="Adobe"/>',
+   '  <Order value="2"/>',
+   '  <OrderName value="CNS1"/>',
+   '  <SupplementVersion value="0"/>',
+   '  <Mapping>',
+   '    <CID cid="0" glyph=".notdef"/>',
+   '    <CID cid="2" glyph="C"/>',
+   '    <CID cid="3" glyph="A"/>',
+   '  </Mapping>',
+   '</CIDGlyphMapping>',
+]
+
+
+class GCIDTest(unittest.TestCase):
+    @classmethod
+    def setUpClass(cls):
+        cls.maxDiff = None
+        cls.font = FakeFont(['.notdef', 'A', 'B', 'C', 'D'])
+
+    def testDecompileToXML(self):
+        table = newTable('cidg')
+        table.decompile(CIDG_DATA, self.font)
+        self.assertEqual(getXML(table.toXML, self.font), CIDG_XML)
+
+    def testCompileFromXML(self):
+        table = newTable('cidg')
+        for name, attrs, content in parseXML(CIDG_XML):
+            table.fromXML(name, attrs, content, font=self.font)
+        self.assertEqual(hexStr(table.compile(self.font)),
+                         hexStr(CIDG_DATA))
+
+
+if __name__ == '__main__':
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/_c_m_a_p_test.py b/Tests/ttLib/tables/_c_m_a_p_test.py
new file mode 100644
index 0000000..6647345
--- /dev/null
+++ b/Tests/ttLib/tables/_c_m_a_p_test.py
@@ -0,0 +1,88 @@
+from __future__ import print_function, division, absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+from fontTools import ttLib
+import unittest
+from fontTools.ttLib.tables._c_m_a_p import CmapSubtable, table__c_m_a_p
+
+class CmapSubtableTest(unittest.TestCase):
+
+	def makeSubtable(self, cmapFormat, platformID, platEncID, langID):
+		subtable = CmapSubtable.newSubtable(cmapFormat)
+		subtable.platformID, subtable.platEncID, subtable.language = (platformID, platEncID, langID)
+		return subtable
+
+	def test_toUnicode_utf16be(self):
+		subtable = self.makeSubtable(4, 0, 2, 7)
+		self.assertEqual("utf_16_be", subtable.getEncoding())
+		self.assertEqual(True, subtable.isUnicode())
+
+	def test_toUnicode_macroman(self):
+		subtable = self.makeSubtable(4, 1, 0, 7)  # MacRoman
+		self.assertEqual("mac_roman", subtable.getEncoding())
+		self.assertEqual(False, subtable.isUnicode())
+
+	def test_toUnicode_macromanian(self):
+		subtable = self.makeSubtable(4, 1, 0, 37)  # Mac Romanian
+		self.assertNotEqual(None, subtable.getEncoding())
+		self.assertEqual(False, subtable.isUnicode())
+
+	def test_extended_mac_encodings(self):
+		subtable = self.makeSubtable(4, 1, 1, 0) # Mac Japanese
+		self.assertNotEqual(None, subtable.getEncoding())
+		self.assertEqual(False, subtable.isUnicode())
+
+	def test_extended_unknown(self):
+		subtable = self.makeSubtable(4, 10, 11, 12)
+		self.assertEqual(subtable.getEncoding(), None)
+		self.assertEqual(subtable.getEncoding("ascii"), "ascii")
+		self.assertEqual(subtable.getEncoding(default="xyz"), "xyz")
+
+	def test_decompile_4(self):
+		subtable = CmapSubtable.newSubtable(4)
+		font = ttLib.TTFont()
+		font.setGlyphOrder([])
+		subtable.decompile(b'\0' * 3 + b'\x10' + b'\0' * 12, font)
+
+	def test_decompile_12(self):
+		subtable = CmapSubtable.newSubtable(12)
+		font = ttLib.TTFont()
+		font.setGlyphOrder([])
+		subtable.decompile(b'\0' * 7 + b'\x10' + b'\0' * 8, font)
+
+	def test_buildReversed(self):
+		c4 = self.makeSubtable(4, 3, 1, 0)
+		c4.cmap = {0x0041:'A', 0x0391:'A'}
+		c12 = self.makeSubtable(12, 3, 10, 0)
+		c12.cmap = {0x10314: 'u10314'}
+		cmap = table__c_m_a_p()
+		cmap.tables = [c4, c12]
+		self.assertEqual(cmap.buildReversed(), {'A':{0x0041, 0x0391}, 'u10314':{0x10314}})
+
+	def test_getBestCmap(self):
+		c4 = self.makeSubtable(4, 3, 1, 0)
+		c4.cmap = {0x0041:'A', 0x0391:'A'}
+		c12 = self.makeSubtable(12, 3, 10, 0)
+		c12.cmap = {0x10314: 'u10314'}
+		cmap = table__c_m_a_p()
+		cmap.tables = [c4, c12]
+		self.assertEqual(cmap.getBestCmap(), {0x10314: 'u10314'})
+		self.assertEqual(cmap.getBestCmap(cmapPreferences=[(3, 1)]), {0x0041:'A', 0x0391:'A'})
+		self.assertEqual(cmap.getBestCmap(cmapPreferences=[(0, 4)]), None)
+
+	def test_font_getBestCmap(self):
+		c4 = self.makeSubtable(4, 3, 1, 0)
+		c4.cmap = {0x0041:'A', 0x0391:'A'}
+		c12 = self.makeSubtable(12, 3, 10, 0)
+		c12.cmap = {0x10314: 'u10314'}
+		cmap = table__c_m_a_p()
+		cmap.tables = [c4, c12]
+		font = ttLib.TTFont()
+		font["cmap"] = cmap
+		self.assertEqual(font.getBestCmap(), {0x10314: 'u10314'})
+		self.assertEqual(font.getBestCmap(cmapPreferences=[(3, 1)]), {0x0041:'A', 0x0391:'A'})
+		self.assertEqual(font.getBestCmap(cmapPreferences=[(0, 4)]), None)
+
+
+if __name__ == "__main__":
+	import sys
+	sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/_c_v_a_r_test.py b/Tests/ttLib/tables/_c_v_a_r_test.py
new file mode 100644
index 0000000..0fd5531
--- /dev/null
+++ b/Tests/ttLib/tables/_c_v_a_r_test.py
@@ -0,0 +1,111 @@
+from __future__ import \
+    print_function, division, absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.misc.testTools import getXML, parseXML
+from fontTools.misc.textTools import deHexStr, hexStr
+from fontTools.ttLib import TTLibError, getTableModule, newTable
+from fontTools.ttLib.tables.TupleVariation import TupleVariation
+
+import unittest
+
+
+CVAR_DATA = deHexStr(
+    "0001 0000 "      #  0: majorVersion=1 minorVersion=0
+    "8002 0018 "      #  4: tupleVariationCount=2|TUPLES_SHARE_POINT_NUMBERS offsetToData=24
+    "0004 "           #  8: tvHeader[0].variationDataSize=4
+    "8000 "           # 10: tvHeader[0].tupleIndex=EMBEDDED_PEAK
+    "4000 0000 "      # 12: tvHeader[0].peakTuple=[1.0, 0.0]
+    "0004 "           # 16: tvHeader[1].variationDataSize=4
+    "8000 "           # 18: tvHeader[1].tupleIndex=EMBEDDED_PEAK
+    "C000 3333 "      # 20: tvHeader[1].peakTuple=[-1.0, 0.8]
+    "03 02 02 01 01"  # 24: shared_pointCount=03, run_count=2 cvt=[2, 3, 4]
+    "02 03 01 04 "    # 25: deltas=[3, 1, 4]
+    "02 09 07 08")    # 29: deltas=[9, 7, 8]
+
+CVAR_PRIVATE_POINT_DATA = deHexStr(
+    "0001 0000 "                    #  0: majorVersion=1 minorVersion=0
+    "0002 0018 "                    #  4: tupleVariationCount=2 offsetToData=24
+    "0009 "                         #  8: tvHeader[0].variationDataSize=9
+    "A000 "                         # 10: tvHeader[0].tupleIndex=EMBEDDED_PEAK|PRIVATE_POINT_NUMBERS
+    "4000 0000 "                    # 12: tvHeader[0].peakTuple=[1.0, 0.0]
+    "0009 "                         # 16: tvHeader[1].variationDataSize=9
+    "A000 "                         # 18: tvHeader[1].tupleIndex=EMBEDDED_PEAK|PRIVATE_POINT_NUMBERS
+    "C000 3333 "                    # 20: tvHeader[1].peakTuple=[-1.0, 0.8]
+    "03 02 02 01 01 02 03 01 04 "   # 24: pointCount=3 run_count=2 cvt=2 1 1 run_count=2 deltas=[3, 1, 4]
+    "03 02 02 01 01 02 09 07 08 ")  # 33: pointCount=3 run_count=2 cvt=2 1 1 run_count=2 deltas=[9, 7, 8]
+
+CVAR_XML = [
+    '<version major="1" minor="0"/>',
+    '<tuple>',
+    '  <coord axis="wght" value="1.0"/>',
+    '  <delta cvt="2" value="3"/>',
+    '  <delta cvt="3" value="1"/>',
+    '  <delta cvt="4" value="4"/>',
+    '</tuple>',
+    '<tuple>',
+    '  <coord axis="wght" value="-1.0"/>',
+    '  <coord axis="wdth" value="0.8"/>',
+    '  <delta cvt="2" value="9"/>',
+    '  <delta cvt="3" value="7"/>',
+    '  <delta cvt="4" value="8"/>',
+    '</tuple>',
+]
+
+CVAR_VARIATIONS = [
+    TupleVariation({"wght": (0.0, 1.0, 1.0)}, [None, None, 3, 1, 4]),
+    TupleVariation({"wght": (-1, -1.0, 0.0), "wdth": (0.0, 0.8, 0.8)},
+                   [None, None, 9, 7, 8]),
+]
+
+
+class CVARTableTest(unittest.TestCase):
+    def makeFont(self):
+        cvt, cvar, fvar = newTable("cvt "), newTable("cvar"), newTable("fvar")
+        font = {"cvt ": cvt, "cvar": cvar, "fvar": fvar}
+        cvt.values = [0, 0, 0, 1000, -2000]
+        Axis = getTableModule("fvar").Axis
+        fvar.axes = [Axis(), Axis()]
+        fvar.axes[0].axisTag, fvar.axes[1].axisTag = "wght", "wdth"
+        return font, cvar
+
+    def test_compile(self):
+        font, cvar = self.makeFont()
+        cvar.variations = CVAR_VARIATIONS
+        self.assertEqual(hexStr(cvar.compile(font)), hexStr(CVAR_PRIVATE_POINT_DATA))
+
+    def test_compile_shared_points(self):
+        font, cvar = self.makeFont()
+        cvar.variations = CVAR_VARIATIONS
+        self.assertEqual(hexStr(cvar.compile(font, useSharedPoints=True)), hexStr(CVAR_DATA))
+
+    def test_decompile(self):
+        font, cvar = self.makeFont()
+        cvar.decompile(CVAR_PRIVATE_POINT_DATA, font)
+        self.assertEqual(cvar.majorVersion, 1)
+        self.assertEqual(cvar.minorVersion, 0)
+        self.assertEqual(cvar.variations, CVAR_VARIATIONS)
+
+    def test_decompile_shared_points(self):
+        font, cvar = self.makeFont()
+        cvar.decompile(CVAR_DATA, font)
+        self.assertEqual(cvar.majorVersion, 1)
+        self.assertEqual(cvar.minorVersion, 0)
+        self.assertEqual(cvar.variations, CVAR_VARIATIONS)
+
+    def test_fromXML(self):
+        font, cvar = self.makeFont()
+        for name, attrs, content in parseXML(CVAR_XML):
+            cvar.fromXML(name, attrs, content, ttFont=font)
+        self.assertEqual(cvar.majorVersion, 1)
+        self.assertEqual(cvar.minorVersion, 0)
+        self.assertEqual(cvar.variations, CVAR_VARIATIONS)
+
+    def test_toXML(self):
+        font, cvar = self.makeFont()
+        cvar.variations = CVAR_VARIATIONS
+        self.assertEqual(getXML(cvar.toXML, font), CVAR_XML)
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/_f_p_g_m_test.py b/Tests/ttLib/tables/_f_p_g_m_test.py
new file mode 100644
index 0000000..71fec48
--- /dev/null
+++ b/Tests/ttLib/tables/_f_p_g_m_test.py
@@ -0,0 +1,20 @@
+from __future__ import print_function, division, absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.ttLib.tables._f_p_g_m import table__f_p_g_m
+from fontTools.ttLib.tables import ttProgram
+
+
+def test__bool__():
+    fpgm = table__f_p_g_m()
+    assert not bool(fpgm)
+
+    p = ttProgram.Program()
+    fpgm.program = p
+    assert not bool(fpgm)
+
+    bc = bytearray([0])
+    p.fromBytecode(bc)
+    assert bool(fpgm)
+
+    p.bytecode.pop()
+    assert not bool(fpgm)
diff --git a/Tests/ttLib/tables/_f_v_a_r_test.py b/Tests/ttLib/tables/_f_v_a_r_test.py
new file mode 100644
index 0000000..4a8e767
--- /dev/null
+++ b/Tests/ttLib/tables/_f_v_a_r_test.py
@@ -0,0 +1,264 @@
+from __future__ import print_function, division, absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.misc.testTools import parseXML
+from fontTools.misc.textTools import deHexStr
+from fontTools.misc.xmlWriter import XMLWriter
+from fontTools.ttLib import TTLibError
+from fontTools.ttLib.tables._f_v_a_r import table__f_v_a_r, Axis, NamedInstance
+from fontTools.ttLib.tables._n_a_m_e import table__n_a_m_e, NameRecord
+import unittest
+
+
+
+FVAR_DATA = deHexStr(
+    "00 01 00 00 00 10 00 02 00 02 00 14 00 02 00 0C "
+    "77 67 68 74 00 64 00 00 01 90 00 00 03 84 00 00 00 00 01 01 "
+    "77 64 74 68 00 32 00 00 00 64 00 00 00 c8 00 00 00 00 01 02 "
+    "01 03 00 00 01 2c 00 00 00 64 00 00 "
+    "01 04 00 00 01 2c 00 00 00 4b 00 00")
+
+FVAR_AXIS_DATA = deHexStr(
+    "6F 70 73 7a ff ff 80 00 00 01 4c cd 00 01 80 00 00 00 01 59")
+
+FVAR_INSTANCE_DATA_WITHOUT_PSNAME = deHexStr(
+    "01 59 00 00 00 00 b3 33 00 00 80 00")
+
+FVAR_INSTANCE_DATA_WITH_PSNAME = (
+    FVAR_INSTANCE_DATA_WITHOUT_PSNAME + deHexStr("02 34"))
+
+
+def xml_lines(writer):
+    content = writer.file.getvalue().decode("utf-8")
+    return [line.strip() for line in content.splitlines()][1:]
+
+
+def AddName(font, name):
+    nameTable = font.get("name")
+    if nameTable is None:
+        nameTable = font["name"] = table__n_a_m_e()
+        nameTable.names = []
+    namerec = NameRecord()
+    namerec.nameID = 1 + max([n.nameID for n in nameTable.names] + [256])
+    namerec.string = name.encode('mac_roman')
+    namerec.platformID, namerec.platEncID, namerec.langID = (1, 0, 0)
+    nameTable.names.append(namerec)
+    return namerec
+
+
+def MakeFont():
+    axes = [("wght", "Weight", 100, 400, 900), ("wdth", "Width", 50, 100, 200)]
+    instances = [("Light", 300, 100), ("Light Condensed", 300, 75)]
+    fvarTable = table__f_v_a_r()
+    font = {"fvar": fvarTable}
+    for tag, name, minValue, defaultValue, maxValue in axes:
+        axis = Axis()
+        axis.axisTag = tag
+        axis.defaultValue = defaultValue
+        axis.minValue, axis.maxValue = minValue, maxValue
+        axis.axisNameID = AddName(font, name).nameID
+        fvarTable.axes.append(axis)
+    for name, weight, width in instances:
+        inst = NamedInstance()
+        inst.subfamilyNameID = AddName(font, name).nameID
+        inst.coordinates = {"wght": weight, "wdth": width}
+        fvarTable.instances.append(inst)
+    return font
+
+
+class FontVariationTableTest(unittest.TestCase):
+    def test_compile(self):
+        font = MakeFont()
+        h = font["fvar"].compile(font)
+        self.assertEqual(FVAR_DATA, font["fvar"].compile(font))
+
+    def test_decompile(self):
+        fvar = table__f_v_a_r()
+        fvar.decompile(FVAR_DATA, ttFont={"fvar": fvar})
+        self.assertEqual(["wght", "wdth"], [a.axisTag for a in fvar.axes])
+        self.assertEqual([259, 260], [i.subfamilyNameID for i in fvar.instances])
+
+    def test_toXML(self):
+        font = MakeFont()
+        writer = XMLWriter(BytesIO())
+        font["fvar"].toXML(writer, font)
+        xml = writer.file.getvalue().decode("utf-8")
+        self.assertEqual(2, xml.count("<Axis>"))
+        self.assertTrue("<AxisTag>wght</AxisTag>" in xml)
+        self.assertTrue("<AxisTag>wdth</AxisTag>" in xml)
+        self.assertEqual(2, xml.count("<NamedInstance "))
+        self.assertTrue("<!-- Light -->" in xml)
+        self.assertTrue("<!-- Light Condensed -->" in xml)
+
+    def test_fromXML(self):
+        fvar = table__f_v_a_r()
+        for name, attrs, content in parseXML(
+                '<Axis>'
+                '    <AxisTag>opsz</AxisTag>'
+                '</Axis>'
+                '<Axis>'
+                '    <AxisTag>slnt</AxisTag>'
+                '    <Flags>0x123</Flags>'
+                '</Axis>'
+                '<NamedInstance subfamilyNameID="765"/>'
+                '<NamedInstance subfamilyNameID="234"/>'):
+            fvar.fromXML(name, attrs, content, ttFont=None)
+        self.assertEqual(["opsz", "slnt"], [a.axisTag for a in fvar.axes])
+        self.assertEqual([0, 0x123], [a.flags for a in fvar.axes])
+        self.assertEqual([765, 234], [i.subfamilyNameID for i in fvar.instances])
+
+
+class AxisTest(unittest.TestCase):
+    def test_compile(self):
+        axis = Axis()
+        axis.axisTag, axis.axisNameID = ('opsz', 345)
+        axis.minValue, axis.defaultValue, axis.maxValue = (-0.5, 1.3, 1.5)
+        self.assertEqual(FVAR_AXIS_DATA, axis.compile())
+
+    def test_decompile(self):
+        axis = Axis()
+        axis.decompile(FVAR_AXIS_DATA)
+        self.assertEqual("opsz", axis.axisTag)
+        self.assertEqual(345, axis.axisNameID)
+        self.assertEqual(-0.5, axis.minValue)
+        self.assertEqual(1.3, axis.defaultValue)
+        self.assertEqual(1.5, axis.maxValue)
+
+    def test_toXML(self):
+        font = MakeFont()
+        axis = Axis()
+        axis.decompile(FVAR_AXIS_DATA)
+        AddName(font, "Optical Size").nameID = 256
+        axis.axisNameID = 256
+        axis.flags = 0xABC
+        writer = XMLWriter(BytesIO())
+        axis.toXML(writer, font)
+        self.assertEqual([
+            '',
+            '<!-- Optical Size -->',
+            '<Axis>',
+                '<AxisTag>opsz</AxisTag>',
+                '<Flags>0xABC</Flags>',
+                '<MinValue>-0.5</MinValue>',
+                '<DefaultValue>1.3</DefaultValue>',
+                '<MaxValue>1.5</MaxValue>',
+                '<AxisNameID>256</AxisNameID>',
+            '</Axis>'
+        ], xml_lines(writer))
+
+    def test_fromXML(self):
+        axis = Axis()
+        for name, attrs, content in parseXML(
+                '<Axis>'
+                '    <AxisTag>wght</AxisTag>'
+                '    <Flags>0x123ABC</Flags>'
+                '    <MinValue>100</MinValue>'
+                '    <DefaultValue>400</DefaultValue>'
+                '    <MaxValue>900</MaxValue>'
+                '    <AxisNameID>256</AxisNameID>'
+                '</Axis>'):
+            axis.fromXML(name, attrs, content, ttFont=None)
+        self.assertEqual("wght", axis.axisTag)
+        self.assertEqual(0x123ABC, axis.flags)
+        self.assertEqual(100, axis.minValue)
+        self.assertEqual(400, axis.defaultValue)
+        self.assertEqual(900, axis.maxValue)
+        self.assertEqual(256, axis.axisNameID)
+
+
+class NamedInstanceTest(unittest.TestCase):
+    def test_compile_withPostScriptName(self):
+        inst = NamedInstance()
+        inst.subfamilyNameID = 345
+        inst.postscriptNameID = 564
+        inst.coordinates = {"wght": 0.7, "wdth": 0.5}
+        self.assertEqual(FVAR_INSTANCE_DATA_WITH_PSNAME,
+                         inst.compile(["wght", "wdth"], True))
+
+    def test_compile_withoutPostScriptName(self):
+        inst = NamedInstance()
+        inst.subfamilyNameID = 345
+        inst.postscriptNameID = 564
+        inst.coordinates = {"wght": 0.7, "wdth": 0.5}
+        self.assertEqual(FVAR_INSTANCE_DATA_WITHOUT_PSNAME,
+                         inst.compile(["wght", "wdth"], False))
+
+    def test_decompile_withPostScriptName(self):
+        inst = NamedInstance()
+        inst.decompile(FVAR_INSTANCE_DATA_WITH_PSNAME, ["wght", "wdth"])
+        self.assertEqual(564, inst.postscriptNameID)
+        self.assertEqual(345, inst.subfamilyNameID)
+        self.assertEqual({"wght": 0.7, "wdth": 0.5}, inst.coordinates)
+
+    def test_decompile_withoutPostScriptName(self):
+        inst = NamedInstance()
+        inst.decompile(FVAR_INSTANCE_DATA_WITHOUT_PSNAME, ["wght", "wdth"])
+        self.assertEqual(0xFFFF, inst.postscriptNameID)
+        self.assertEqual(345, inst.subfamilyNameID)
+        self.assertEqual({"wght": 0.7, "wdth": 0.5}, inst.coordinates)
+
+    def test_toXML_withPostScriptName(self):
+        font = MakeFont()
+        inst = NamedInstance()
+        inst.flags = 0xE9
+        inst.subfamilyNameID = AddName(font, "Light Condensed").nameID
+        inst.postscriptNameID = AddName(font, "Test-LightCondensed").nameID
+        inst.coordinates = {"wght": 0.7, "wdth": 0.5}
+        writer = XMLWriter(BytesIO())
+        inst.toXML(writer, font)
+        self.assertEqual([
+            '',
+            '<!-- Light Condensed -->',
+            '<!-- PostScript: Test-LightCondensed -->',
+            '<NamedInstance flags="0xE9" postscriptNameID="%s" subfamilyNameID="%s">' % (
+                inst.postscriptNameID, inst.subfamilyNameID),
+              '<coord axis="wght" value="0.7"/>',
+              '<coord axis="wdth" value="0.5"/>',
+            '</NamedInstance>'
+        ], xml_lines(writer))
+
+    def test_toXML_withoutPostScriptName(self):
+        font = MakeFont()
+        inst = NamedInstance()
+        inst.flags = 0xABC
+        inst.subfamilyNameID = AddName(font, "Light Condensed").nameID
+        inst.coordinates = {"wght": 0.7, "wdth": 0.5}
+        writer = XMLWriter(BytesIO())
+        inst.toXML(writer, font)
+        self.assertEqual([
+            '',
+            '<!-- Light Condensed -->',
+            '<NamedInstance flags="0xABC" subfamilyNameID="%s">' %
+                inst.subfamilyNameID,
+              '<coord axis="wght" value="0.7"/>',
+              '<coord axis="wdth" value="0.5"/>',
+            '</NamedInstance>'
+        ], xml_lines(writer))
+
+    def test_fromXML_withPostScriptName(self):
+        inst = NamedInstance()
+        for name, attrs, content in parseXML(
+                '<NamedInstance flags="0x0" postscriptNameID="257" subfamilyNameID="345">'
+                '    <coord axis="wght" value="0.7"/>'
+                '    <coord axis="wdth" value="0.5"/>'
+                '</NamedInstance>'):
+            inst.fromXML(name, attrs, content, ttFont=MakeFont())
+        self.assertEqual(257, inst.postscriptNameID)
+        self.assertEqual(345, inst.subfamilyNameID)
+        self.assertEqual({"wght": 0.7, "wdth": 0.5}, inst.coordinates)
+
+    def test_fromXML_withoutPostScriptName(self):
+        inst = NamedInstance()
+        for name, attrs, content in parseXML(
+                '<NamedInstance flags="0x123ABC" subfamilyNameID="345">'
+                '    <coord axis="wght" value="0.7"/>'
+                '    <coord axis="wdth" value="0.5"/>'
+                '</NamedInstance>'):
+            inst.fromXML(name, attrs, content, ttFont=MakeFont())
+        self.assertEqual(0x123ABC, inst.flags)
+        self.assertEqual(345, inst.subfamilyNameID)
+        self.assertEqual({"wght": 0.7, "wdth": 0.5}, inst.coordinates)
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/_g_c_i_d_test.py b/Tests/ttLib/tables/_g_c_i_d_test.py
new file mode 100644
index 0000000..1ec92fb
--- /dev/null
+++ b/Tests/ttLib/tables/_g_c_i_d_test.py
@@ -0,0 +1,70 @@
+# coding: utf-8
+from __future__ import print_function, division, absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.misc.testTools import FakeFont, getXML, parseXML
+from fontTools.misc.textTools import deHexStr, hexStr
+from fontTools.ttLib import newTable
+import unittest
+
+
+# On macOS X 10.12.3, the font /Library/Fonts/AppleGothic.ttf has a ‘gcid’
+# table with a similar structure as this test data, just more CIDs.
+GCID_DATA = deHexStr(
+    "0000 0000 "          #   0: Format=0, Flags=0
+    "0000 0098 "          #   4: Size=152
+    "0000 "               #   8: Registry=0
+    "41 64 6F 62 65 "     #  10: RegistryName="Adobe"
+    + ("00" * 59) +       #  15: <padding>
+    "0003 "               #  74: Order=3
+    "4B 6F 72 65 61 31 "  #  76: Order="Korea1"
+    + ("00" * 58) +       #  82: <padding>
+    "0001 "               # 140: SupplementVersion
+    "0004 "               # 142: Count
+    "1234 "               # 144: CIDs[0/.notdef]=4660
+    "FFFF "               # 146: CIDs[1/A]=None
+    "0007 "               # 148: CIDs[2/B]=7
+    "DEF0 "               # 150: CIDs[3/C]=57072
+)                         # 152: <end>
+assert len(GCID_DATA) == 152, len(GCID_DATA)
+
+
+GCID_XML = [
+   '<GlyphCIDMapping Format="0">',
+   '  <DataFormat value="0"/>',
+   '  <!-- StructLength=152 -->',
+   '  <Registry value="0"/>',
+   '  <RegistryName value="Adobe"/>',
+   '  <Order value="3"/>',
+   '  <OrderName value="Korea1"/>',
+   '  <SupplementVersion value="1"/>',
+   '  <Mapping>',
+   '    <CID glyph=".notdef" value="4660"/>',
+   '    <CID glyph="B" value="7"/>',
+   '    <CID glyph="C" value="57072"/>',
+   '  </Mapping>',
+   '</GlyphCIDMapping>',
+]
+
+
+class GCIDTest(unittest.TestCase):
+    @classmethod
+    def setUpClass(cls):
+        cls.maxDiff = None
+        cls.font = FakeFont(['.notdef', 'A', 'B', 'C', 'D'])
+
+    def testDecompileToXML(self):
+        table = newTable('gcid')
+        table.decompile(GCID_DATA, self.font)
+        self.assertEqual(getXML(table.toXML, self.font), GCID_XML)
+
+    def testCompileFromXML(self):
+        table = newTable('gcid')
+        for name, attrs, content in parseXML(GCID_XML):
+            table.fromXML(name, attrs, content, font=self.font)
+        self.assertEqual(hexStr(table.compile(self.font)),
+                         hexStr(GCID_DATA))
+
+
+if __name__ == '__main__':
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/_g_l_y_f_test.py b/Tests/ttLib/tables/_g_l_y_f_test.py
new file mode 100644
index 0000000..205af84
--- /dev/null
+++ b/Tests/ttLib/tables/_g_l_y_f_test.py
@@ -0,0 +1,160 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.fixedTools import otRound
+from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates
+import sys
+import array
+import pytest
+
+
+class GlyphCoordinatesTest(object):
+
+    def test_translate(self):
+        g = GlyphCoordinates([(1,2)])
+        g.translate((.5,0))
+        assert g == GlyphCoordinates([(1.5,2.0)])
+
+    def test_scale(self):
+        g = GlyphCoordinates([(1,2)])
+        g.scale((.5,0))
+        assert g == GlyphCoordinates([(0.5,0.0)])
+
+    def test_transform(self):
+        g = GlyphCoordinates([(1,2)])
+        g.transform(((.5,0),(.2,.5)))
+        assert g[0] == GlyphCoordinates([(0.9,1.0)])[0]
+
+    def test__eq__(self):
+        g = GlyphCoordinates([(1,2)])
+        g2 = GlyphCoordinates([(1.0,2)])
+        g3 = GlyphCoordinates([(1.5,2)])
+        assert g == g2
+        assert not g == g3
+        assert not g2 == g3
+        assert not g == object()
+
+    def test__ne__(self):
+        g = GlyphCoordinates([(1,2)])
+        g2 = GlyphCoordinates([(1.0,2)])
+        g3 = GlyphCoordinates([(1.5,2)])
+        assert not (g != g2)
+        assert g != g3
+        assert g2 != g3
+        assert g != object()
+
+    def test__pos__(self):
+        g = GlyphCoordinates([(1,2)])
+        g2 = +g
+        assert g == g2
+
+    def test__neg__(self):
+        g = GlyphCoordinates([(1,2)])
+        g2 = -g
+        assert g2 == GlyphCoordinates([(-1, -2)])
+
+    @pytest.mark.skipif(sys.version_info[0] < 3,
+                        reason="__round___ requires Python 3")
+    def test__round__(self):
+        g = GlyphCoordinates([(-1.5,2)])
+        g2 = round(g)
+        assert g2 == GlyphCoordinates([(-1,2)])
+
+    def test__add__(self):
+        g1 = GlyphCoordinates([(1,2)])
+        g2 = GlyphCoordinates([(3,4)])
+        g3 = GlyphCoordinates([(4,6)])
+        assert g1 + g2 == g3
+        assert g1 + (1, 1) == GlyphCoordinates([(2,3)])
+        with pytest.raises(TypeError) as excinfo:
+            assert g1 + object()
+        assert 'unsupported operand' in str(excinfo.value)
+
+    def test__sub__(self):
+        g1 = GlyphCoordinates([(1,2)])
+        g2 = GlyphCoordinates([(3,4)])
+        g3 = GlyphCoordinates([(-2,-2)])
+        assert g1 - g2 == g3
+        assert g1 - (1, 1) == GlyphCoordinates([(0,1)])
+        with pytest.raises(TypeError) as excinfo:
+            assert g1 - object()
+        assert 'unsupported operand' in str(excinfo.value)
+
+    def test__rsub__(self):
+        g = GlyphCoordinates([(1,2)])
+        # other + (-self)
+        assert (1, 1) - g == GlyphCoordinates([(0,-1)])
+
+    def test__mul__(self):
+        g = GlyphCoordinates([(1,2)])
+        assert g * 3 == GlyphCoordinates([(3,6)])
+        assert g * (3,2) == GlyphCoordinates([(3,4)])
+        assert g * (1,1) == g
+        with pytest.raises(TypeError) as excinfo:
+            assert g * object()
+        assert 'unsupported operand' in str(excinfo.value)
+
+    def test__truediv__(self):
+        g = GlyphCoordinates([(1,2)])
+        assert g / 2 == GlyphCoordinates([(.5,1)])
+        assert g / (1, 2) == GlyphCoordinates([(1,1)])
+        assert g / (1, 1) == g
+        with pytest.raises(TypeError) as excinfo:
+            assert g / object()
+        assert 'unsupported operand' in str(excinfo.value)
+
+    def test__iadd__(self):
+        g = GlyphCoordinates([(1,2)])
+        g += (.5,0)
+        assert g == GlyphCoordinates([(1.5, 2.0)])
+        g2 = GlyphCoordinates([(3,4)])
+        g += g2
+        assert g == GlyphCoordinates([(4.5, 6.0)])
+
+    def test__isub__(self):
+        g = GlyphCoordinates([(1,2)])
+        g -= (.5, 0)
+        assert g == GlyphCoordinates([(0.5, 2.0)])
+        g2 = GlyphCoordinates([(3,4)])
+        g -= g2
+        assert g == GlyphCoordinates([(-2.5, -2.0)])
+
+    def __test__imul__(self):
+        g = GlyphCoordinates([(1,2)])
+        g *= (2,.5)
+        g *= 2
+        assert g == GlyphCoordinates([(4.0, 2.0)])
+        g = GlyphCoordinates([(1,2)])
+        g *= 2
+        assert g == GlyphCoordinates([(2, 4)])
+
+    def test__itruediv__(self):
+        g = GlyphCoordinates([(1,3)])
+        g /= (.5,1.5)
+        g /= 2
+        assert g == GlyphCoordinates([(1.0, 1.0)])
+
+    def test__bool__(self):
+        g = GlyphCoordinates([])
+        assert bool(g) == False
+        g = GlyphCoordinates([(0,0), (0.,0)])
+        assert bool(g) == True
+        g = GlyphCoordinates([(0,0), (1,0)])
+        assert bool(g) == True
+        g = GlyphCoordinates([(0,.5), (0,0)])
+        assert bool(g) == True
+
+    def test_double_precision_float(self):
+        # https://github.com/fonttools/fonttools/issues/963
+        afloat = 242.50000000000003
+        g = GlyphCoordinates([(afloat, 0)])
+        g.toInt()
+        # this would return 242 if the internal array.array typecode is 'f',
+        # since the Python float is truncated to a C float.
+        # when using typecode 'd' it should return the correct value 243
+        assert g[0][0] == otRound(afloat)
+
+    def test__checkFloat_overflow(self):
+        g = GlyphCoordinates([(1, 1)], typecode="h")
+        g.append((0x8000, 0))
+        assert g.array.typecode == "d"
+        assert g.array == array.array("d", [1.0, 1.0, 32768.0, 0.0])
diff --git a/Tests/ttLib/tables/_g_v_a_r_test.py b/Tests/ttLib/tables/_g_v_a_r_test.py
new file mode 100644
index 0000000..64adbb6
--- /dev/null
+++ b/Tests/ttLib/tables/_g_v_a_r_test.py
@@ -0,0 +1,211 @@
+from __future__ import print_function, division, absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.misc.testTools import FakeFont, getXML, parseXML
+from fontTools.misc.textTools import deHexStr, hexStr
+from fontTools.ttLib import TTLibError, getTableClass, getTableModule, newTable
+import unittest
+from fontTools.ttLib.tables.TupleVariation import TupleVariation
+
+
+gvarClass = getTableClass("gvar")
+
+
+GVAR_DATA = deHexStr(
+    "0001 0000 "      #   0: majorVersion=1 minorVersion=0
+    "0002 0000 "      #   4: axisCount=2 sharedTupleCount=0
+    "0000001C "       #   8: offsetToSharedTuples=28
+    "0003 0000 "      #  12: glyphCount=3 flags=0
+    "0000001C "       #  16: offsetToGlyphVariationData=28
+    "0000 0000 000C 002F " #  20: offsets=[0,0,12,47], times 2: [0,0,24,94],
+    #                 #           +offsetToGlyphVariationData: [28,28,52,122]
+    #
+    # 28: Glyph variation data for glyph #0, ".notdef"
+    # ------------------------------------------------
+    # (no variation data for this glyph)
+    #
+    # 28: Glyph variation data for glyph #1, "space"
+    # ----------------------------------------------
+    "8001 000C "      #  28: tupleVariationCount=1|TUPLES_SHARE_POINT_NUMBERS, offsetToData=12(+28=40)
+    "000A "           #  32: tvHeader[0].variationDataSize=10
+    "8000 "           #  34: tvHeader[0].tupleIndex=EMBEDDED_PEAK
+    "0000 2CCD "      #  36: tvHeader[0].peakTuple={wght:0.0, wdth:0.7}
+    "00 "             #  40: all points
+    "03 01 02 03 04 " #  41: deltaX=[1, 2, 3, 4]
+    "03 0b 16 21 2C " #  46: deltaY=[11, 22, 33, 44]
+    "00 "             #  51: padding
+    #
+    # 52: Glyph variation data for glyph #2, "I"
+    # ------------------------------------------
+    "8002 001c "      #  52: tupleVariationCount=2|TUPLES_SHARE_POINT_NUMBERS, offsetToData=28(+52=80)
+    "0012 "           #  56: tvHeader[0].variationDataSize=18
+    "C000 "           #  58: tvHeader[0].tupleIndex=EMBEDDED_PEAK|INTERMEDIATE_REGION
+    "2000 0000 "      #  60: tvHeader[0].peakTuple={wght:0.5, wdth:0.0}
+    "0000 0000 "      #  64: tvHeader[0].intermediateStart={wght:0.0, wdth:0.0}
+    "4000 0000 "      #  68: tvHeader[0].intermediateEnd={wght:1.0, wdth:0.0}
+    "0016 "           #  72: tvHeader[1].variationDataSize=22
+    "A000 "           #  74: tvHeader[1].tupleIndex=EMBEDDED_PEAK|PRIVATE_POINTS
+    "C000 3333 "      #  76: tvHeader[1].peakTuple={wght:-1.0, wdth:0.8}
+    "00 "             #  80: all points
+    "07 03 01 04 01 " #  81: deltaX.len=7, deltaX=[3, 1, 4, 1,
+    "05 09 02 06 "    #  86:                       5, 9, 2, 6]
+    "07 03 01 04 01 " #  90: deltaY.len=7, deltaY=[3, 1, 4, 1,
+    "05 09 02 06 "    #  95:                       5, 9, 2, 6]
+    "06 "             #  99: 6 points
+    "05 00 01 03 01 " # 100: runLen=5(+1=6); delta-encoded run=[0, 1, 4, 5,
+    "01 01 "          # 105:                                    6, 7]
+    "05 f8 07 fc 03 fe 01 "  # 107: deltaX.len=5, deltaX=[-8,7,-4,3,-2,1]
+    "05 a8 4d 2c 21 ea 0b "  # 114: deltaY.len=5, deltaY=[-88,77,44,33,-22,11]
+    "00"              # 121: padding
+)                     # 122: <end>
+assert len(GVAR_DATA) == 122
+
+
+GVAR_VARIATIONS = {
+    ".notdef": [
+    ],
+    "space": [
+        TupleVariation(
+            {"wdth": (0.0, 0.7, 0.7)},
+            [(1, 11), (2, 22), (3, 33), (4, 44)]),
+    ],
+    "I": [
+        TupleVariation(
+            {"wght": (0.0, 0.5, 1.0)},
+            [(3,3), (1,1), (4,4), (1,1), (5,5), (9,9), (2,2), (6,6)]),
+        TupleVariation(
+            {"wght": (-1.0, -1.0, 0.0), "wdth": (0.0, 0.8, 0.8)},
+            [(-8,-88), (7,77), None, None, (-4,44), (3,33), (-2,-22), (1,11)]),
+    ],
+}
+
+
+GVAR_XML = [
+    '<version value="1"/>',
+    '<reserved value="0"/>',
+    '<glyphVariations glyph="space">',
+    '  <tuple>',
+    '    <coord axis="wdth" value="0.7"/>',
+    '    <delta pt="0" x="1" y="11"/>',
+    '    <delta pt="1" x="2" y="22"/>',
+    '    <delta pt="2" x="3" y="33"/>',
+    '    <delta pt="3" x="4" y="44"/>',
+    '  </tuple>',
+    '</glyphVariations>',
+    '<glyphVariations glyph="I">',
+    '  <tuple>',
+    '    <coord axis="wght" max="1.0" min="0.0" value="0.5"/>',
+    '    <delta pt="0" x="3" y="3"/>',
+    '    <delta pt="1" x="1" y="1"/>',
+    '    <delta pt="2" x="4" y="4"/>',
+    '    <delta pt="3" x="1" y="1"/>',
+    '    <delta pt="4" x="5" y="5"/>',
+    '    <delta pt="5" x="9" y="9"/>',
+    '    <delta pt="6" x="2" y="2"/>',
+    '    <delta pt="7" x="6" y="6"/>',
+    '  </tuple>',
+    '  <tuple>',
+    '    <coord axis="wght" value="-1.0"/>',
+    '    <coord axis="wdth" value="0.8"/>',
+    '    <delta pt="0" x="-8" y="-88"/>',
+    '    <delta pt="1" x="7" y="77"/>',
+    '    <delta pt="4" x="-4" y="44"/>',
+    '    <delta pt="5" x="3" y="33"/>',
+    '    <delta pt="6" x="-2" y="-22"/>',
+    '    <delta pt="7" x="1" y="11"/>',
+    '  </tuple>',
+    '</glyphVariations>',
+]
+
+
+GVAR_DATA_EMPTY_VARIATIONS = deHexStr(
+    "0001 0000 "           #  0: majorVersion=1 minorVersion=0
+    "0002 0000 "           #  4: axisCount=2 sharedTupleCount=0
+    "0000001c "            #  8: offsetToSharedTuples=28
+    "0003 0000 "           # 12: glyphCount=3 flags=0
+    "0000001c "            # 16: offsetToGlyphVariationData=28
+    "0000 0000 0000 0000"  # 20: offsets=[0, 0, 0, 0]
+)                          # 28: <end>
+
+
+def hexencode(s):
+	h = hexStr(s).upper()
+	return ' '.join([h[i:i+2] for i in range(0, len(h), 2)])
+
+
+class GVARTableTest(unittest.TestCase):
+	def makeFont(self, variations):
+		glyphs=[".notdef", "space", "I"]
+		Axis = getTableModule("fvar").Axis
+		Glyph = getTableModule("glyf").Glyph
+		glyf, fvar, gvar = newTable("glyf"), newTable("fvar"), newTable("gvar")
+		font = FakeFont(glyphs)
+		font.tables = {"glyf": glyf, "gvar": gvar, "fvar": fvar}
+		glyf.glyphs = {glyph: Glyph() for glyph in glyphs}
+		glyf.glyphs["I"].coordinates = [(10, 10), (10, 20), (20, 20), (20, 10)]
+		fvar.axes = [Axis(), Axis()]
+		fvar.axes[0].axisTag, fvar.axes[1].axisTag = "wght", "wdth"
+		gvar.variations = variations
+		return font, gvar
+
+	def test_compile(self):
+		font, gvar = self.makeFont(GVAR_VARIATIONS)
+		self.assertEqual(hexStr(gvar.compile(font)), hexStr(GVAR_DATA))
+
+	def test_compile_noVariations(self):
+		font, gvar = self.makeFont({})
+		self.assertEqual(hexStr(gvar.compile(font)),
+		                 hexStr(GVAR_DATA_EMPTY_VARIATIONS))
+
+	def test_compile_emptyVariations(self):
+		font, gvar = self.makeFont({".notdef": [], "space": [], "I": []})
+		self.assertEqual(hexStr(gvar.compile(font)),
+		                 hexStr(GVAR_DATA_EMPTY_VARIATIONS))
+
+	def test_decompile(self):
+		font, gvar = self.makeFont({})
+		gvar.decompile(GVAR_DATA, font)
+		self.assertEqual(gvar.variations, GVAR_VARIATIONS)
+
+	def test_decompile_noVariations(self):
+		font, gvar = self.makeFont({})
+		gvar.decompile(GVAR_DATA_EMPTY_VARIATIONS, font)
+		self.assertEqual(gvar.variations,
+		                 {".notdef": [], "space": [], "I": []})
+
+	def test_fromXML(self):
+		font, gvar = self.makeFont({})
+		for name, attrs, content in parseXML(GVAR_XML):
+			gvar.fromXML(name, attrs, content, ttFont=font)
+		self.assertEqual(gvar.variations,
+                         {g:v for g,v in GVAR_VARIATIONS.items() if v})
+
+	def test_toXML(self):
+		font, gvar = self.makeFont(GVAR_VARIATIONS)
+		self.assertEqual(getXML(gvar.toXML, font), GVAR_XML)
+
+	def test_compileOffsets_shortFormat(self):
+		self.assertEqual((deHexStr("00 00 00 02 FF C0"), 0),
+		                 gvarClass.compileOffsets_([0, 4, 0x1ff80]))
+
+	def test_compileOffsets_longFormat(self):
+		self.assertEqual((deHexStr("00 00 00 00 00 00 00 04 CA FE BE EF"), 1),
+		                 gvarClass.compileOffsets_([0, 4, 0xCAFEBEEF]))
+
+	def test_decompileOffsets_shortFormat(self):
+		decompileOffsets = gvarClass.decompileOffsets_
+		data = deHexStr("00 11 22 33 44 55 66 77 88 99 aa bb")
+		self.assertEqual(
+			[2*0x0011, 2*0x2233, 2*0x4455, 2*0x6677, 2*0x8899, 2*0xaabb],
+			list(decompileOffsets(data, tableFormat=0, glyphCount=5)))
+
+	def test_decompileOffsets_longFormat(self):
+		decompileOffsets = gvarClass.decompileOffsets_
+		data = deHexStr("00 11 22 33 44 55 66 77 88 99 aa bb")
+		self.assertEqual(
+			[0x00112233, 0x44556677, 0x8899aabb],
+			list(decompileOffsets(data, tableFormat=1, glyphCount=2)))
+
+
+if __name__ == "__main__":
+	import sys
+	sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/_h_h_e_a_test.py b/Tests/ttLib/tables/_h_h_e_a_test.py
new file mode 100644
index 0000000..64849e9
--- /dev/null
+++ b/Tests/ttLib/tables/_h_h_e_a_test.py
@@ -0,0 +1,198 @@
+from __future__ import absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.misc.loggingTools import CapturingLogHandler
+from fontTools.misc.testTools import parseXML, getXML
+from fontTools.misc.textTools import deHexStr
+from fontTools.ttLib import TTFont, newTable
+from fontTools.misc.fixedTools import log
+import os
+import unittest
+
+
+CURR_DIR = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
+DATA_DIR = os.path.join(CURR_DIR, 'data')
+
+HHEA_DATA = deHexStr(
+    '0001 0000 '  # 1.0   version
+    '02EE '       # 750   ascent
+    'FF06 '       # -250  descent
+    '00C8 '       # 200   lineGap
+    '03E8 '       # 1000  advanceWidthMax
+    'FFE7 '       # -25   minLeftSideBearing
+    'FFEC '       # -20   minRightSideBearing
+    '03D1 '       # 977   xMaxExtent
+    '0000 '       # 0     caretSlopeRise
+    '0001 '       # 1     caretSlopeRun
+    '0010 '       # 16    caretOffset
+    '0000 '       # 0     reserved0
+    '0000 '       # 0     reserved1
+    '0000 '       # 0     reserved2
+    '0000 '       # 0     reserved3
+    '0000 '       # 0     metricDataFormat
+    '002A '       # 42    numberOfHMetrics
+)
+
+HHEA_AS_DICT = {
+    'tableTag': 'hhea',
+    'tableVersion': 0x00010000,
+    'ascent': 750,
+    'descent': -250,
+    'lineGap': 200,
+    'advanceWidthMax': 1000,
+    'minLeftSideBearing': -25,
+    'minRightSideBearing': -20,
+    'xMaxExtent': 977,
+    'caretSlopeRise': 0,
+    'caretSlopeRun': 1,
+    'caretOffset': 16,
+    'reserved0': 0,
+    'reserved1': 0,
+    'reserved2': 0,
+    'reserved3': 0,
+    'metricDataFormat': 0,
+    'numberOfHMetrics': 42,
+}
+
+HHEA_XML = [
+    '<tableVersion value="0x00010000"/>',
+    '<ascent value="750"/>',
+    '<descent value="-250"/>',
+    '<lineGap value="200"/>',
+    '<advanceWidthMax value="1000"/>',
+    '<minLeftSideBearing value="-25"/>',
+    '<minRightSideBearing value="-20"/>',
+    '<xMaxExtent value="977"/>',
+    '<caretSlopeRise value="0"/>',
+    '<caretSlopeRun value="1"/>',
+    '<caretOffset value="16"/>',
+    '<reserved0 value="0"/>',
+    '<reserved1 value="0"/>',
+    '<reserved2 value="0"/>',
+    '<reserved3 value="0"/>',
+    '<metricDataFormat value="0"/>',
+    '<numberOfHMetrics value="42"/>',
+]
+
+HHEA_XML_VERSION_AS_FLOAT = [
+    '<tableVersion value="1.0"/>',
+] + HHEA_XML[1:]
+
+
+class HheaCompileOrToXMLTest(unittest.TestCase):
+
+    def setUp(self):
+        hhea = newTable('hhea')
+        hhea.tableVersion = 0x00010000
+        hhea.ascent = 750
+        hhea.descent = -250
+        hhea.lineGap = 200
+        hhea.advanceWidthMax = 1000
+        hhea.minLeftSideBearing = -25
+        hhea.minRightSideBearing = -20
+        hhea.xMaxExtent = 977
+        hhea.caretSlopeRise = 0
+        hhea.caretSlopeRun = 1
+        hhea.caretOffset = 16
+        hhea.metricDataFormat = 0
+        hhea.numberOfHMetrics = 42
+        hhea.reserved0 = hhea.reserved1 = hhea.reserved2 = hhea.reserved3 = 0
+        self.font = TTFont(sfntVersion='OTTO')
+        self.font['hhea'] = hhea
+
+    def test_compile(self):
+        hhea = self.font['hhea']
+        hhea.tableVersion = 0x00010000
+        self.assertEqual(HHEA_DATA, hhea.compile(self.font))
+
+    def test_compile_version_10_as_float(self):
+        hhea = self.font['hhea']
+        hhea.tableVersion = 1.0
+        with CapturingLogHandler(log, "WARNING") as captor:
+            self.assertEqual(HHEA_DATA, hhea.compile(self.font))
+        self.assertTrue(
+            len([r for r in captor.records
+                 if "Table version value is a float" in r.msg]) == 1)
+
+    def test_toXML(self):
+        hhea = self.font['hhea']
+        self.font['hhea'].tableVersion = 0x00010000
+        self.assertEqual(getXML(hhea.toXML), HHEA_XML)
+
+    def test_toXML_version_as_float(self):
+        hhea = self.font['hhea']
+        hhea.tableVersion = 1.0
+        with CapturingLogHandler(log, "WARNING") as captor:
+            self.assertEqual(getXML(hhea.toXML), HHEA_XML)
+        self.assertTrue(
+            len([r for r in captor.records
+                 if "Table version value is a float" in r.msg]) == 1)
+
+
+class HheaDecompileOrFromXMLTest(unittest.TestCase):
+
+    def setUp(self):
+        hhea = newTable('hhea')
+        self.font = TTFont(sfntVersion='OTTO')
+        self.font['hhea'] = hhea
+
+    def test_decompile(self):
+        hhea = self.font['hhea']
+        hhea.decompile(HHEA_DATA, self.font)
+        for key in hhea.__dict__:
+            self.assertEqual(getattr(hhea, key), HHEA_AS_DICT[key])
+
+    def test_fromXML(self):
+        hhea = self.font['hhea']
+        for name, attrs, content in parseXML(HHEA_XML):
+            hhea.fromXML(name, attrs, content, self.font)
+        for key in hhea.__dict__:
+            self.assertEqual(getattr(hhea, key), HHEA_AS_DICT[key])
+
+    def test_fromXML_version_as_float(self):
+        hhea = self.font['hhea']
+        with CapturingLogHandler(log, "WARNING") as captor:
+            for name, attrs, content in parseXML(HHEA_XML_VERSION_AS_FLOAT):
+                hhea.fromXML(name, attrs, content, self.font)
+        self.assertTrue(
+            len([r for r in captor.records
+                 if "Table version value is a float" in r.msg]) == 1)
+        for key in hhea.__dict__:
+            self.assertEqual(getattr(hhea, key), HHEA_AS_DICT[key])
+
+
+class HheaRecalcTest(unittest.TestCase):
+
+    def test_recalc_TTF(self):
+        font = TTFont()
+        font.importXML(os.path.join(DATA_DIR, '_h_h_e_a_recalc_TTF.ttx'))
+        hhea = font['hhea']
+        hhea.recalc(font)
+        self.assertEqual(hhea.advanceWidthMax, 600)
+        self.assertEqual(hhea.minLeftSideBearing, -56)
+        self.assertEqual(hhea.minRightSideBearing, 100)
+        self.assertEqual(hhea.xMaxExtent, 400)
+
+    def test_recalc_OTF(self):
+        font = TTFont()
+        font.importXML(os.path.join(DATA_DIR, '_h_h_e_a_recalc_OTF.ttx'))
+        hhea = font['hhea']
+        hhea.recalc(font)
+        self.assertEqual(hhea.advanceWidthMax, 600)
+        self.assertEqual(hhea.minLeftSideBearing, -56)
+        self.assertEqual(hhea.minRightSideBearing, 100)
+        self.assertEqual(hhea.xMaxExtent, 400)
+
+    def test_recalc_empty(self):
+        font = TTFont()
+        font.importXML(os.path.join(DATA_DIR, '_h_h_e_a_recalc_empty.ttx'))
+        hhea = font['hhea']
+        hhea.recalc(font)
+        self.assertEqual(hhea.advanceWidthMax, 600)
+        self.assertEqual(hhea.minLeftSideBearing, 0)
+        self.assertEqual(hhea.minRightSideBearing, 0)
+        self.assertEqual(hhea.xMaxExtent, 0)
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/_h_m_t_x_test.py b/Tests/ttLib/tables/_h_m_t_x_test.py
new file mode 100644
index 0000000..007f3cd
--- /dev/null
+++ b/Tests/ttLib/tables/_h_m_t_x_test.py
@@ -0,0 +1,219 @@
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.misc.testTools import parseXML, getXML
+from fontTools.misc.textTools import deHexStr
+from fontTools.ttLib import TTFont, newTable, TTLibError
+from fontTools.misc.loggingTools import CapturingLogHandler
+from fontTools.ttLib.tables._h_m_t_x import table__h_m_t_x, log
+import struct
+import unittest
+
+
+class HmtxTableTest(unittest.TestCase):
+
+    def __init__(self, methodName):
+        unittest.TestCase.__init__(self, methodName)
+        # Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
+        # and fires deprecation warnings if a program uses the old name.
+        if not hasattr(self, "assertRaisesRegex"):
+            self.assertRaisesRegex = self.assertRaisesRegexp
+
+    @classmethod
+    def setUpClass(cls):
+        cls.tableClass = table__h_m_t_x
+        cls.tag = "hmtx"
+
+    def makeFont(self, numGlyphs, numberOfMetrics):
+        font = TTFont()
+        maxp = font['maxp'] = newTable('maxp')
+        maxp.numGlyphs = numGlyphs
+        # from A to ...
+        font.glyphOrder = [chr(i) for i in range(65, 65+numGlyphs)]
+        headerTag = self.tableClass.headerTag
+        font[headerTag] = newTable(headerTag)
+        numberOfMetricsName = self.tableClass.numberOfMetricsName
+        setattr(font[headerTag], numberOfMetricsName, numberOfMetrics)
+        return font
+
+    def test_decompile(self):
+        font = self.makeFont(numGlyphs=3, numberOfMetrics=3)
+        data = deHexStr("02A2 FFF5 0278 004F 02C6 0036")
+
+        mtxTable = newTable(self.tag)
+        mtxTable.decompile(data, font)
+
+        self.assertEqual(mtxTable['A'], (674, -11))
+        self.assertEqual(mtxTable['B'], (632, 79))
+        self.assertEqual(mtxTable['C'], (710, 54))
+
+    def test_decompile_additional_SB(self):
+        font = self.makeFont(numGlyphs=4, numberOfMetrics=2)
+        metrics = deHexStr("02A2 FFF5 0278 004F")
+        extraSideBearings = deHexStr("0036 FFFC")
+        data = metrics + extraSideBearings
+
+        mtxTable = newTable(self.tag)
+        mtxTable.decompile(data, font)
+
+        self.assertEqual(mtxTable['A'], (674, -11))
+        self.assertEqual(mtxTable['B'], (632, 79))
+        # all following have same width as the previous
+        self.assertEqual(mtxTable['C'], (632, 54))
+        self.assertEqual(mtxTable['D'], (632, -4))
+
+    def test_decompile_not_enough_data(self):
+        font = self.makeFont(numGlyphs=1, numberOfMetrics=1)
+        mtxTable = newTable(self.tag)
+        msg = "not enough '%s' table data" % self.tag
+
+        with self.assertRaisesRegex(TTLibError, msg):
+            mtxTable.decompile(b"\0\0\0", font)
+
+    def test_decompile_too_much_data(self):
+        font = self.makeFont(numGlyphs=1, numberOfMetrics=1)
+        mtxTable = newTable(self.tag)
+        msg = "too much '%s' table data" % self.tag
+
+        with CapturingLogHandler(log, "WARNING") as captor:
+            mtxTable.decompile(b"\0\0\0\0\0", font)
+
+        self.assertTrue(
+            len([r for r in captor.records if msg == r.msg]) == 1)
+
+    def test_decompile_num_metrics_greater_than_glyphs(self):
+        font = self.makeFont(numGlyphs=1, numberOfMetrics=2)
+        mtxTable = newTable(self.tag)
+        msg = "The %s.%s exceeds the maxp.numGlyphs" % (
+            self.tableClass.headerTag, self.tableClass.numberOfMetricsName)
+
+        with CapturingLogHandler(log, "WARNING") as captor:
+            mtxTable.decompile(b"\0\0\0\0", font)
+
+        self.assertTrue(
+            len([r for r in captor.records if msg == r.msg]) == 1)
+
+    def test_decompile_possibly_negative_advance(self):
+        font = self.makeFont(numGlyphs=1, numberOfMetrics=1)
+        # we warn if advance is > 0x7FFF as it might be interpreted as signed
+        # by some authoring tools
+        data = deHexStr("8000 0000")
+        mtxTable = newTable(self.tag)
+
+        with CapturingLogHandler(log, "WARNING") as captor:
+            mtxTable.decompile(data, font)
+
+        self.assertTrue(
+            len([r for r in captor.records
+                if "has a huge advance" in r.msg]) == 1)
+
+    def test_compile(self):
+        # we set the wrong 'numberOfMetrics' to check it gets adjusted
+        font = self.makeFont(numGlyphs=3, numberOfMetrics=4)
+        mtxTable = font[self.tag] = newTable(self.tag)
+        mtxTable.metrics = {
+            'A': (674, -11),
+            'B': (632, 79),
+            'C': (710, 54),
+        }
+
+        data = mtxTable.compile(font)
+
+        self.assertEqual(data, deHexStr("02A2 FFF5 0278 004F 02C6 0036"))
+
+        headerTable = font[self.tableClass.headerTag]
+        self.assertEqual(
+            getattr(headerTable, self.tableClass.numberOfMetricsName), 3)
+
+    def test_compile_additional_SB(self):
+        font = self.makeFont(numGlyphs=4, numberOfMetrics=1)
+        mtxTable = font[self.tag] = newTable(self.tag)
+        mtxTable.metrics = {
+            'A': (632, -11),
+            'B': (632, 79),
+            'C': (632, 54),
+            'D': (632, -4),
+        }
+
+        data = mtxTable.compile(font)
+
+        self.assertEqual(data, deHexStr("0278 FFF5 004F 0036 FFFC"))
+
+    def test_compile_negative_advance(self):
+        font = self.makeFont(numGlyphs=1, numberOfMetrics=1)
+        mtxTable = font[self.tag] = newTable(self.tag)
+        mtxTable.metrics = {'A': [-1, 0]}
+
+        with CapturingLogHandler(log, "ERROR") as captor:
+            with self.assertRaisesRegex(TTLibError, "negative advance"):
+                mtxTable.compile(font)
+
+        self.assertTrue(
+            len([r for r in captor.records
+                if "Glyph 'A' has negative advance" in r.msg]) == 1)
+
+    def test_compile_struct_out_of_range(self):
+        font = self.makeFont(numGlyphs=1, numberOfMetrics=1)
+        mtxTable = font[self.tag] = newTable(self.tag)
+        mtxTable.metrics = {'A': (0xFFFF+1, -0x8001)}
+
+        with self.assertRaises(struct.error):
+            mtxTable.compile(font)
+
+    def test_compile_round_float_values(self):
+        font = self.makeFont(numGlyphs=3, numberOfMetrics=2)
+        mtxTable = font[self.tag] = newTable(self.tag)
+        mtxTable.metrics = {
+            'A': (0.5, 0.5),  # round -> (1, 1)
+            'B': (0.1, 0.9),  # round -> (0, 1)
+            'C': (0.1, 0.1),  # round -> (0, 0)
+        }
+
+        data = mtxTable.compile(font)
+
+        self.assertEqual(data, deHexStr("0001 0001 0000 0001 0000"))
+
+    def test_toXML(self):
+        font = self.makeFont(numGlyphs=2, numberOfMetrics=2)
+        mtxTable = font[self.tag] = newTable(self.tag)
+        mtxTable.metrics = {'B': (632, 79), 'A': (674, -11)}
+
+        self.assertEqual(
+            getXML(mtxTable.toXML),
+            ('<mtx name="A" %s="674" %s="-11"/>\n'
+             '<mtx name="B" %s="632" %s="79"/>' % (
+                (self.tableClass.advanceName,
+                 self.tableClass.sideBearingName) * 2)).split('\n'))
+
+    def test_fromXML(self):
+        mtxTable = newTable(self.tag)
+
+        for name, attrs, content in parseXML(
+                '<mtx name="A" %s="674" %s="-11"/>'
+                '<mtx name="B" %s="632" %s="79"/>' % (
+                    (self.tableClass.advanceName,
+                     self.tableClass.sideBearingName) * 2)):
+            mtxTable.fromXML(name, attrs, content, ttFont=None)
+
+        self.assertEqual(
+            mtxTable.metrics, {'A': (674, -11), 'B': (632, 79)})
+
+    def test_delitem(self):
+        mtxTable = newTable(self.tag)
+        mtxTable.metrics = {'A': (0, 0)}
+
+        del mtxTable['A']
+
+        self.assertTrue('A' not in mtxTable.metrics)
+
+    def test_setitem(self):
+        mtxTable = newTable(self.tag)
+        mtxTable.metrics = {'A': (674, -11), 'B': (632, 79)}
+        mtxTable['B'] = [0, 0]  # list is converted to tuple
+
+        self.assertEqual(mtxTable.metrics, {'A': (674, -11), 'B': (0, 0)})
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/_k_e_r_n_test.py b/Tests/ttLib/tables/_k_e_r_n_test.py
new file mode 100644
index 0000000..8ab1b1c
--- /dev/null
+++ b/Tests/ttLib/tables/_k_e_r_n_test.py
@@ -0,0 +1,370 @@
+from __future__ import print_function, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.ttLib import newTable
+from fontTools.ttLib.tables._k_e_r_n import (
+    KernTable_format_0, KernTable_format_unkown)
+from fontTools.misc.textTools import deHexStr
+from fontTools.misc.testTools import FakeFont, getXML, parseXML
+import pytest
+
+
+KERN_VER_0_FMT_0_DATA = deHexStr(
+    '0000 '            #  0: version=0
+    '0001 '            #  2: nTables=1
+    '0000 '            #  4: version=0 (bogus field, unused)
+    '0020 '            #  6: length=32
+    '00 '              #  8: format=0
+    '01 '              #  9: coverage=1
+    '0003 '            # 10: nPairs=3
+    '000C '            # 12: searchRange=12
+    '0001 '            # 14: entrySelector=1
+    '0006 '            # 16: rangeShift=6
+    '0004 000C FFD8 '  # 18: l=4, r=12, v=-40
+    '0004 001C 0028 '  # 24: l=4, r=28, v=40
+    '0005 0028 FFCE '  # 30: l=5, r=40, v=-50
+)
+assert len(KERN_VER_0_FMT_0_DATA) == 36
+
+KERN_VER_0_FMT_0_XML = [
+    '<version value="0"/>',
+    '<kernsubtable coverage="1" format="0">',
+    '  <pair l="E" r="M" v="-40"/>',
+    '  <pair l="E" r="c" v="40"/>',
+    '  <pair l="F" r="o" v="-50"/>',
+    '</kernsubtable>',
+]
+
+KERN_VER_1_FMT_0_DATA = deHexStr(
+    '0001 0000 '       #  0: version=1
+    '0000 0001 '       #  4: nTables=1
+    '0000 0022 '       #  8: length=34
+    '00 '              # 12: coverage=0
+    '00 '              # 13: format=0
+    '0000 '            # 14: tupleIndex=0
+    '0003 '            # 16: nPairs=3
+    '000C '            # 18: searchRange=12
+    '0001 '            # 20: entrySelector=1
+    '0006 '            # 22: rangeShift=6
+    '0004 000C FFD8 '  # 24: l=4, r=12, v=-40
+    '0004 001C 0028 '  # 30: l=4, r=28, v=40
+    '0005 0028 FFCE '  # 36: l=5, r=40, v=-50
+)
+assert len(KERN_VER_1_FMT_0_DATA) == 42
+
+KERN_VER_1_FMT_0_XML = [
+    '<version value="1.0"/>',
+    '<kernsubtable coverage="0" format="0" tupleIndex="0">',
+    '  <pair l="E" r="M" v="-40"/>',
+    '  <pair l="E" r="c" v="40"/>',
+    '  <pair l="F" r="o" v="-50"/>',
+    '</kernsubtable>',
+]
+
+KERN_VER_0_FMT_UNKNOWN_DATA = deHexStr(
+    '0000 '            #  0: version=0
+    '0002 '            #  2: nTables=2
+    '0000 '            #  4: version=0
+    '000A '            #  6: length=10
+    '04 '              #  8: format=4  (format 4 doesn't exist)
+    '01 '              #  9: coverage=1
+    '1234 5678 '       # 10: garbage...
+    '0000 '            # 14: version=0
+    '000A '            # 16: length=10
+    '05 '              # 18: format=5  (format 5 doesn't exist)
+    '01 '              # 19: coverage=1
+    '9ABC DEF0 '       # 20: garbage...
+)
+assert len(KERN_VER_0_FMT_UNKNOWN_DATA) == 24
+
+KERN_VER_0_FMT_UNKNOWN_XML = [
+    '<version value="0"/>',
+    '<kernsubtable format="4">',
+    "  <!-- unknown 'kern' subtable format -->",
+    '  0000000A 04011234',
+    '  5678             ',
+    '</kernsubtable>',
+    '<kernsubtable format="5">',
+    "<!-- unknown 'kern' subtable format -->",
+    '  0000000A 05019ABC',
+    '  DEF0             ',
+    '</kernsubtable>',
+]
+
+KERN_VER_1_FMT_UNKNOWN_DATA = deHexStr(
+    '0001 0000 '       #  0: version=1
+    '0000 0002 '       #  4: nTables=2
+    '0000 000C '       #  8: length=12
+    '00 '              # 12: coverage=0
+    '04 '              # 13: format=4  (format 4 doesn't exist)
+    '0000 '            # 14: tupleIndex=0
+    '1234 5678'        # 16: garbage...
+    '0000 000C '       # 20: length=12
+    '00 '              # 24: coverage=0
+    '05 '              # 25: format=5  (format 5 doesn't exist)
+    '0000 '            # 26: tupleIndex=0
+    '9ABC DEF0 '       # 28: garbage...
+)
+assert len(KERN_VER_1_FMT_UNKNOWN_DATA) == 32
+
+KERN_VER_1_FMT_UNKNOWN_XML = [
+    '<version value="1"/>',
+    '<kernsubtable format="4">',
+    "  <!-- unknown 'kern' subtable format -->",
+    '  0000000C 00040000',
+    '  12345678         ',
+    '</kernsubtable>',
+    '<kernsubtable format="5">',
+    "  <!-- unknown 'kern' subtable format -->",
+    '  0000000C 00050000',
+    '  9ABCDEF0         ',
+    '</kernsubtable>',
+]
+
+
+@pytest.fixture
+def font():
+    return FakeFont(list("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                         "abcdefghijklmnopqrstuvwxyz"))
+
+
+class KernTableTest(object):
+
+    @pytest.mark.parametrize(
+        "data, version",
+        [
+            (KERN_VER_0_FMT_0_DATA, 0),
+            (KERN_VER_1_FMT_0_DATA, 1.0),
+        ],
+        ids=["version_0", "version_1"]
+    )
+    def test_decompile_single_format_0(self, data, font, version):
+        kern = newTable("kern")
+        kern.decompile(data, font)
+
+        assert kern.version == version
+        assert len(kern.kernTables) == 1
+
+        st = kern.kernTables[0]
+        assert st.apple is (version == 1.0)
+        assert st.format == 0
+        # horizontal kerning in OT kern is coverage 0x01, while in
+        # AAT kern it's the default (0)
+        assert st.coverage == (0 if st.apple else 1)
+        assert st.tupleIndex == (0 if st.apple else None)
+        assert len(st.kernTable) == 3
+        assert st.kernTable == {
+            ('E', 'M'): -40,
+            ('E', 'c'): 40,
+            ('F', 'o'): -50
+        }
+
+    @pytest.mark.parametrize(
+        "version, expected",
+        [
+            (0, KERN_VER_0_FMT_0_DATA),
+            (1.0, KERN_VER_1_FMT_0_DATA),
+        ],
+        ids=["version_0", "version_1"]
+    )
+    def test_compile_single_format_0(self, font, version, expected):
+        kern = newTable("kern")
+        kern.version = version
+        apple = version == 1.0
+        st = KernTable_format_0(apple)
+        kern.kernTables = [st]
+        st.coverage = (0 if apple else 1)
+        st.tupleIndex = 0 if apple else None
+        st.kernTable = {
+            ('E', 'M'): -40,
+            ('E', 'c'): 40,
+            ('F', 'o'): -50
+        }
+        data = kern.compile(font)
+        assert data == expected
+
+    @pytest.mark.parametrize(
+        "xml, version",
+        [
+            (KERN_VER_0_FMT_0_XML, 0),
+            (KERN_VER_1_FMT_0_XML, 1.0),
+        ],
+        ids=["version_0", "version_1"]
+    )
+    def test_fromXML_single_format_0(self, xml, font, version):
+        kern = newTable("kern")
+        for name, attrs, content in parseXML(xml):
+            kern.fromXML(name, attrs, content, ttFont=font)
+
+        assert kern.version == version
+        assert len(kern.kernTables) == 1
+
+        st = kern.kernTables[0]
+        assert st.apple is (version == 1.0)
+        assert st.format == 0
+        assert st.coverage == (0 if st.apple else 1)
+        assert st.tupleIndex == (0 if st.apple else None)
+        assert len(st.kernTable) == 3
+        assert st.kernTable == {
+            ('E', 'M'): -40,
+            ('E', 'c'): 40,
+            ('F', 'o'): -50
+        }
+
+    @pytest.mark.parametrize(
+        "version, expected",
+        [
+            (0, KERN_VER_0_FMT_0_XML),
+            (1.0, KERN_VER_1_FMT_0_XML),
+        ],
+        ids=["version_0", "version_1"]
+    )
+    def test_toXML_single_format_0(self, font, version, expected):
+        kern = newTable("kern")
+        kern.version = version
+        apple = version == 1.0
+        st = KernTable_format_0(apple)
+        kern.kernTables = [st]
+        st.coverage = 0 if apple else 1
+        st.tupleIndex = 0 if apple else None
+        st.kernTable = {
+            ('E', 'M'): -40,
+            ('E', 'c'): 40,
+            ('F', 'o'): -50
+        }
+        xml = getXML(kern.toXML, font)
+        assert xml == expected
+
+    @pytest.mark.parametrize(
+        "data, version, header_length, st_length",
+        [
+            (KERN_VER_0_FMT_UNKNOWN_DATA, 0, 4, 10),
+            (KERN_VER_1_FMT_UNKNOWN_DATA, 1.0, 8, 12),
+        ],
+        ids=["version_0", "version_1"]
+    )
+    def test_decompile_format_unknown(
+            self, data, font, version, header_length, st_length):
+        kern = newTable("kern")
+        kern.decompile(data, font)
+
+        assert kern.version == version
+        assert len(kern.kernTables) == 2
+
+        st_data = data[header_length:]
+        st0 = kern.kernTables[0]
+        assert st0.format == 4
+        assert st0.data == st_data[:st_length]
+        st_data = st_data[st_length:]
+
+        st1 = kern.kernTables[1]
+        assert st1.format == 5
+        assert st1.data == st_data[:st_length]
+
+    @pytest.mark.parametrize(
+        "version, st_length, expected",
+        [
+            (0, 10, KERN_VER_0_FMT_UNKNOWN_DATA),
+            (1.0, 12, KERN_VER_1_FMT_UNKNOWN_DATA),
+        ],
+        ids=["version_0", "version_1"]
+    )
+    def test_compile_format_unknown(self, version, st_length, expected):
+        kern = newTable("kern")
+        kern.version = version
+        kern.kernTables = []
+
+        for unknown_fmt, kern_data in zip((4, 5), ("1234 5678", "9ABC DEF0")):
+            if version > 0:
+                coverage = 0
+                header_fmt = deHexStr(
+                    "%08X %02X %02X %04X" % (
+                        st_length, coverage, unknown_fmt, 0))
+            else:
+                coverage = 1
+                header_fmt = deHexStr(
+                    "%04X %04X %02X %02X" % (
+                        0, st_length, unknown_fmt, coverage))
+            st = KernTable_format_unkown(unknown_fmt)
+            st.data = header_fmt + deHexStr(kern_data)
+            kern.kernTables.append(st)
+
+        data = kern.compile(font)
+        assert data == expected
+
+    @pytest.mark.parametrize(
+        "xml, version, st_length",
+        [
+            (KERN_VER_0_FMT_UNKNOWN_XML, 0, 10),
+            (KERN_VER_1_FMT_UNKNOWN_XML, 1.0, 12),
+        ],
+        ids=["version_0", "version_1"]
+    )
+    def test_fromXML_format_unknown(self, xml, font, version, st_length):
+        kern = newTable("kern")
+        for name, attrs, content in parseXML(xml):
+            kern.fromXML(name, attrs, content, ttFont=font)
+
+        assert kern.version == version
+        assert len(kern.kernTables) == 2
+
+        st0 = kern.kernTables[0]
+        assert st0.format == 4
+        assert len(st0.data) == st_length
+
+        st1 = kern.kernTables[1]
+        assert st1.format == 5
+        assert len(st1.data) == st_length
+
+    @pytest.mark.parametrize(
+        "version", [0, 1.0], ids=["version_0", "version_1"])
+    def test_toXML_format_unknown(self, font, version):
+        kern = newTable("kern")
+        kern.version = version
+        st = KernTable_format_unkown(4)
+        st.data = b"ABCD"
+        kern.kernTables = [st]
+
+        xml = getXML(kern.toXML, font)
+
+        assert xml == [
+            '<version value="%s"/>' % version,
+            '<kernsubtable format="4">',
+            '  <!-- unknown \'kern\' subtable format -->',
+            '  41424344   ',
+            '</kernsubtable>',
+        ]
+
+    def test_getkern(self):
+        table = newTable("kern")
+        table.version = 0
+        table.kernTables = []
+
+        assert table.getkern(0) is None
+
+        st0 = KernTable_format_0()
+        table.kernTables.append(st0)
+
+        assert table.getkern(0) is st0
+        assert table.getkern(4) is None
+
+        st1 = KernTable_format_unkown(4)
+        table.kernTables.append(st1)
+
+
+class KernTable_format_0_Test(object):
+
+    def test_decompileBadGlyphId(self, font):
+        subtable = KernTable_format_0()
+        subtable.decompile(
+            b'\x00' + b'\x00' + b'\x00' + b'\x1a' + b'\x00' + b'\x00' +
+            b'\x00' + b'\x02' + b'\x00' * 6 +
+            b'\x00' + b'\x01' + b'\x00' + b'\x03' + b'\x00' + b'\x01' +
+            b'\x00' + b'\x01' + b'\xFF' + b'\xFF' + b'\x00' + b'\x02',
+            font)
+        assert subtable[("B", "D")] == 1
+        assert subtable[("B", "glyph65535")] == 2
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(pytest.main(sys.argv))
diff --git a/Tests/ttLib/tables/_l_c_a_r_test.py b/Tests/ttLib/tables/_l_c_a_r_test.py
new file mode 100644
index 0000000..4525def
--- /dev/null
+++ b/Tests/ttLib/tables/_l_c_a_r_test.py
@@ -0,0 +1,109 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.testTools import FakeFont, getXML, parseXML
+from fontTools.misc.textTools import deHexStr, hexStr
+from fontTools.ttLib import newTable
+import unittest
+
+
+# Example: Format 0 Ligature Caret Table
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6lcar.html
+LCAR_FORMAT_0_DATA = deHexStr(
+    '0001 0000 0000 '  #  0: Version=1.0, Format=0
+    '0006 0004 0002 '  #  6: LookupFormat=6, UnitSize=4, NUnits=2
+    '0008 0001 0000 '  # 12: SearchRange=8, EntrySelector=1, RangeShift=0
+    '0001 001E '       # 18: Glyph=1 (f_r), OffsetOfLigCaretEntry=30
+    '0003 0022 '       # 22: Glyph=3 (f_f_l), OffsetOfLigCaretEntry=34
+    'FFFF 0000 '       # 26: Glyph=<end>, OffsetOfLigCaretEntry=0
+    '0001 00DC '       # 30: DivisionPointCount=1, DivisionPoint=[220]
+    '0002 00EF 01D8 '  # 34: DivisionPointCount=2, DivisionPoint=[239, 475]
+)                      # 40: <end>
+assert(len(LCAR_FORMAT_0_DATA) == 40)
+
+
+LCAR_FORMAT_0_XML = [
+    '<Version value="0x00010000"/>',
+    '<LigatureCarets Format="0">',
+    '  <Carets>',
+    '    <Lookup glyph="f_f_l">',
+    '      <!-- DivsionPointCount=2 -->',
+    '      <DivisionPoint index="0" value="239"/>',
+    '      <DivisionPoint index="1" value="472"/>',
+    '    </Lookup>',
+    '    <Lookup glyph="f_r">',
+    '      <!-- DivsionPointCount=1 -->',
+    '      <DivisionPoint index="0" value="220"/>',
+    '    </Lookup>',
+    '  </Carets>',
+    '</LigatureCarets>',
+]
+
+
+# Example: Format 1 Ligature Caret Table
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6lcar.html
+LCAR_FORMAT_1_DATA = deHexStr(
+    '0001 0000 0001 '  #  0: Version=1.0, Format=1
+    '0006 0004 0002 '  #  6: LookupFormat=6, UnitSize=4, NUnits=2
+    '0008 0001 0000 '  # 12: SearchRange=8, EntrySelector=1, RangeShift=0
+    '0001 001E '       # 18: Glyph=1 (f_r), OffsetOfLigCaretEntry=30
+    '0003 0022 '       # 22: Glyph=3 (f_f_l), OffsetOfLigCaretEntry=34
+    'FFFF 0000 '       # 26: Glyph=<end>, OffsetOfLigCaretEntry=0
+    '0001 0032 '       # 30: DivisionPointCount=1, DivisionPoint=[50]
+    '0002 0037 004B '  # 34: DivisionPointCount=2, DivisionPoint=[55, 75]
+)                      # 40: <end>
+assert(len(LCAR_FORMAT_1_DATA) == 40)
+
+
+LCAR_FORMAT_1_XML = [
+    '<Version value="0x00010000"/>',
+    '<LigatureCarets Format="1">',
+    '  <Carets>',
+    '    <Lookup glyph="f_f_l">',
+    '      <!-- DivsionPointCount=2 -->',
+    '      <DivisionPoint index="0" value="55"/>',
+    '      <DivisionPoint index="1" value="75"/>',
+    '    </Lookup>',
+    '    <Lookup glyph="f_r">',
+    '      <!-- DivsionPointCount=1 -->',
+    '      <DivisionPoint index="0" value="50"/>',
+    '    </Lookup>',
+    '  </Carets>',
+    '</LigatureCarets>',
+]
+
+
+class LCARTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.maxDiff = None
+        cls.font = FakeFont(['.notdef', 'f_r', 'X', 'f_f_l'])
+
+    def test_decompile_toXML_format0(self):
+        table = newTable('lcar')
+        table.decompile(LCAR_FORMAT_0_DATA, self.font)
+        self.assertEqual(getXML(table.toXML), LCAR_FORMAT_0_XML)
+
+    def test_compile_fromXML_format0(self):
+        table = newTable('lcar')
+        for name, attrs, content in parseXML(LCAR_FORMAT_0_XML):
+            table.fromXML(name, attrs, content, font=self.font)
+        self.assertEqual(hexStr(table.compile(self.font)),
+                         hexStr(LCAR_FORMAT_0_DATA))
+
+    def test_decompile_toXML_format1(self):
+        table = newTable('lcar')
+        table.decompile(LCAR_FORMAT_1_DATA, self.font)
+        self.assertEqual(getXML(table.toXML), LCAR_FORMAT_1_XML)
+
+    def test_compile_fromXML_format1(self):
+        table = newTable('lcar')
+        for name, attrs, content in parseXML(LCAR_FORMAT_1_XML):
+            table.fromXML(name, attrs, content, font=self.font)
+        self.assertEqual(hexStr(table.compile(self.font)),
+                         hexStr(LCAR_FORMAT_1_DATA))
+
+
+if __name__ == '__main__':
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/_l_t_a_g_test.py b/Tests/ttLib/tables/_l_t_a_g_test.py
new file mode 100644
index 0000000..a4c5a0e
--- /dev/null
+++ b/Tests/ttLib/tables/_l_t_a_g_test.py
@@ -0,0 +1,63 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.testTools import parseXML
+from fontTools.misc.xmlWriter import XMLWriter
+import os
+import struct
+import unittest
+from fontTools.ttLib import newTable
+
+
+class Test_l_t_a_g(unittest.TestCase):
+
+	DATA_ = struct.pack(b">LLLHHHHHH", 1, 0, 3, 24 + 0, 2, 24 + 2, 7, 24 + 2, 2) + b"enzh-Hant"
+	TAGS_ = ["en", "zh-Hant", "zh"]
+
+	def test_addTag(self):
+		table = newTable("ltag")
+		self.assertEqual(table.addTag("de-CH"), 0)
+		self.assertEqual(table.addTag("gsw-LI"), 1)
+		self.assertEqual(table.addTag("de-CH"), 0)
+		self.assertEqual(table.tags, ["de-CH", "gsw-LI"])
+
+	def test_decompile_compile(self):
+		table = newTable("ltag")
+		table.decompile(self.DATA_, ttFont=None)
+		self.assertEqual(1, table.version)
+		self.assertEqual(0, table.flags)
+		self.assertEqual(self.TAGS_, table.tags)
+		compiled = table.compile(ttFont=None)
+		self.assertEqual(self.DATA_, compiled)
+		self.assertIsInstance(compiled, bytes)
+
+	def test_fromXML(self):
+		table = newTable("ltag")
+		for name, attrs, content in parseXML(
+				'<version value="1"/>'
+				'<flags value="777"/>'
+				'<LanguageTag tag="sr-Latn"/>'
+				'<LanguageTag tag="fa"/>'):
+			table.fromXML(name, attrs, content, ttFont=None)
+		self.assertEqual(1, table.version)
+		self.assertEqual(777, table.flags)
+		self.assertEqual(["sr-Latn", "fa"], table.tags)
+
+	def test_toXML(self):
+		writer = XMLWriter(BytesIO())
+		table = newTable("ltag")
+		table.decompile(self.DATA_, ttFont=None)
+		table.toXML(writer, ttFont=None)
+		expected = os.linesep.join([
+			'<?xml version="1.0" encoding="UTF-8"?>',
+			'<version value="1"/>',
+			'<flags value="0"/>',
+			'<LanguageTag tag="en"/>',
+			'<LanguageTag tag="zh-Hant"/>',
+			'<LanguageTag tag="zh"/>'
+		]) + os.linesep
+		self.assertEqual(expected.encode("utf_8"), writer.file.getvalue())
+
+
+if __name__ == '__main__':
+	import sys
+	sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/_m_e_t_a_test.py b/Tests/ttLib/tables/_m_e_t_a_test.py
new file mode 100644
index 0000000..dc0f8cd
--- /dev/null
+++ b/Tests/ttLib/tables/_m_e_t_a_test.py
@@ -0,0 +1,95 @@
+from __future__ import print_function, division, absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.misc.testTools import parseXML
+from fontTools.misc.textTools import deHexStr
+from fontTools.misc.xmlWriter import XMLWriter
+from fontTools.ttLib import TTLibError
+from fontTools.ttLib.tables._m_e_t_a import table__m_e_t_a
+import unittest
+
+
+# From a real font on MacOS X, but substituted 'bild' tag by 'TEST',
+# and shortened the payload.
+META_DATA = deHexStr(
+    "00 00 00 01 00 00 00 00 00 00 00 1C 00 00 00 01 "
+    "54 45 53 54 00 00 00 1C 00 00 00 04 CA FE BE EF")
+
+# The 'dlng' and 'slng' tag with text data containing "augmented" BCP 47
+# comma-separated or comma-space-separated tags. These should be UTF-8 encoded
+# text.
+META_DATA_TEXT = deHexStr(
+    "00 00 00 01 00 00 00 00 00 00 00 28 00 00 00 02 "
+    "64 6C 6E 67 00 00 00 28 00 00 00 0E 73 6C 6E 67 "
+    "00 00 00 36 00 00 00 0E 4C 61 74 6E 2C 47 72 65 "
+    "6B 2C 43 79 72 6C 4C 61 74 6E 2C 47 72 65 6B 2C "
+    "43 79 72 6C")
+
+class MetaTableTest(unittest.TestCase):
+    def test_decompile(self):
+        table = table__m_e_t_a()
+        table.decompile(META_DATA, ttFont={"meta": table})
+        self.assertEqual({"TEST": b"\xCA\xFE\xBE\xEF"}, table.data)
+
+    def test_compile(self):
+        table = table__m_e_t_a()
+        table.data["TEST"] = b"\xCA\xFE\xBE\xEF"
+        self.assertEqual(META_DATA, table.compile(ttFont={"meta": table}))
+
+    def test_decompile_text(self):
+        table = table__m_e_t_a()
+        table.decompile(META_DATA_TEXT, ttFont={"meta": table})
+        self.assertEqual({"dlng": u"Latn,Grek,Cyrl",
+                          "slng": u"Latn,Grek,Cyrl"}, table.data)
+
+    def test_compile_text(self):
+        table = table__m_e_t_a()
+        table.data["dlng"] = u"Latn,Grek,Cyrl"
+        table.data["slng"] = u"Latn,Grek,Cyrl"
+        self.assertEqual(META_DATA_TEXT, table.compile(ttFont={"meta": table}))
+
+    def test_toXML(self):
+        table = table__m_e_t_a()
+        table.data["TEST"] = b"\xCA\xFE\xBE\xEF"
+        writer = XMLWriter(BytesIO())
+        table.toXML(writer, {"meta": table})
+        xml = writer.file.getvalue().decode("utf-8")
+        self.assertEqual([
+            '<hexdata tag="TEST">',
+                'cafebeef',
+            '</hexdata>'
+        ], [line.strip() for line in xml.splitlines()][1:])
+
+    def test_fromXML(self):
+        table = table__m_e_t_a()
+        for name, attrs, content in parseXML(
+                '<hexdata tag="TEST">'
+                '    cafebeef'
+                '</hexdata>'):
+            table.fromXML(name, attrs, content, ttFont=None)
+        self.assertEqual({"TEST": b"\xCA\xFE\xBE\xEF"}, table.data)
+
+    def test_toXML_text(self):
+        table = table__m_e_t_a()
+        table.data["dlng"] = u"Latn,Grek,Cyrl"
+        writer = XMLWriter(BytesIO())
+        table.toXML(writer, {"meta": table})
+        xml = writer.file.getvalue().decode("utf-8")
+        self.assertEqual([
+            '<text tag="dlng">',
+            'Latn,Grek,Cyrl',
+            '</text>'
+        ], [line.strip() for line in xml.splitlines()][1:])
+
+    def test_fromXML_text(self):
+        table = table__m_e_t_a()
+        for name, attrs, content in parseXML(
+                '<text tag="dlng">'
+                '    Latn,Grek,Cyrl'
+                '</text>'):
+            table.fromXML(name, attrs, content, ttFont=None)
+        self.assertEqual({"dlng": u"Latn,Grek,Cyrl"}, table.data)
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/_m_o_r_t_test.py b/Tests/ttLib/tables/_m_o_r_t_test.py
new file mode 100644
index 0000000..70696e8
--- /dev/null
+++ b/Tests/ttLib/tables/_m_o_r_t_test.py
@@ -0,0 +1,115 @@
+from __future__ import print_function, division, absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.misc.testTools import FakeFont, getXML, parseXML
+from fontTools.misc.textTools import deHexStr, hexStr
+from fontTools.ttLib import newTable
+import unittest
+
+
+# Glyph Metamorphosis Table Examples
+# Example 1: Non-contextual Glyph Substitution
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6mort.html
+# The example given by Apple's 'mort' specification is suboptimally
+# encoded: it uses AAT lookup format 6 even though format 8 would be
+# more compact.  Because our encoder always uses the most compact
+# encoding, this breaks our round-trip testing. Therefore, we changed
+# the example to use GlyphID 13 instead of 12 for the 'parenright'
+# character; the non-contiguous glyph range for the AAT lookup makes
+# format 6 to be most compact.
+MORT_NONCONTEXTUAL_DATA = deHexStr(
+    '0001 0000 '  #  0: Version=1.0
+    '0000 0001 '  #  4: MorphChainCount=1
+    '0000 0001 '  #  8: DefaultFlags=1
+    '0000 0050 '  # 12: StructLength=80
+    '0003 0001 '  # 16: MorphFeatureCount=3, MorphSubtableCount=1
+    '0004 0000 '  # 20: Feature[0].FeatureType=4/VertSubst, .FeatureSetting=on
+    '0000 0001 '  # 24: Feature[0].EnableFlags=0x00000001
+    'FFFF FFFF '  # 28: Feature[0].DisableFlags=0xFFFFFFFF
+    '0004 0001 '  # 32: Feature[1].FeatureType=4/VertSubst, .FeatureSetting=off
+    '0000 0000 '  # 36: Feature[1].EnableFlags=0x00000000
+    'FFFF FFFE '  # 40: Feature[1].DisableFlags=0xFFFFFFFE
+    '0000 0001 '  # 44: Feature[2].FeatureType=0/GlyphEffects, .FeatSetting=off
+    '0000 0000 '  # 48: Feature[2].EnableFlags=0 (required for last feature)
+    '0000 0000 '  # 52: Feature[2].EnableFlags=0 (required for last feature)
+    '0020 '       # 56: Subtable[0].StructLength=32
+    '80 '         # 58: Subtable[0].CoverageFlags=0x80
+    '04 '         # 59: Subtable[0].MorphType=4/NoncontextualMorph
+    '0000 0001 '  # 60: Subtable[0].SubFeatureFlags=0x1
+    '0006 0004 '  # 64: LookupFormat=6, UnitSize=4
+    '0002 0008 '  # 68: NUnits=2, SearchRange=8
+    '0001 0000 '  # 72: EntrySelector=1, RangeShift=0
+    '000B 0087 '  # 76: Glyph=11 (parenleft); Value=135 (parenleft.vertical)
+    '000D 0088 '  # 80: Glyph=13 (parenright); Value=136 (parenright.vertical)
+    'FFFF 0000 '  # 84: Glyph=<end>; Value=0
+)                 # 88: <end>
+assert len(MORT_NONCONTEXTUAL_DATA) == 88
+
+
+MORT_NONCONTEXTUAL_XML = [
+    '<Version value="0x00010000"/>',
+    '<!-- MorphChainCount=1 -->',
+    '<MorphChain index="0">',
+    '  <DefaultFlags value="0x00000001"/>',
+    '  <!-- StructLength=80 -->',
+    '  <!-- MorphFeatureCount=3 -->',
+    '  <!-- MorphSubtableCount=1 -->',
+    '  <MorphFeature index="0">',
+    '    <FeatureType value="4"/>',
+    '    <FeatureSetting value="0"/>',
+    '    <EnableFlags value="0x00000001"/>',
+    '    <DisableFlags value="0xFFFFFFFF"/>',
+    '  </MorphFeature>',
+    '  <MorphFeature index="1">',
+    '    <FeatureType value="4"/>',
+    '    <FeatureSetting value="1"/>',
+    '    <EnableFlags value="0x00000000"/>',
+    '    <DisableFlags value="0xFFFFFFFE"/>',
+    '  </MorphFeature>',
+    '  <MorphFeature index="2">',
+    '    <FeatureType value="0"/>',
+    '    <FeatureSetting value="1"/>',
+    '    <EnableFlags value="0x00000000"/>',
+    '    <DisableFlags value="0x00000000"/>',
+    '  </MorphFeature>',
+    '  <MorphSubtable index="0">',
+    '    <!-- StructLength=32 -->',
+    '    <CoverageFlags value="128"/>',
+    '    <!-- MorphType=4 -->',
+    '    <SubFeatureFlags value="0x00000001"/>',
+    '    <NoncontextualMorph>',
+    '      <Substitution>',
+    '        <Lookup glyph="parenleft" value="parenleft.vertical"/>',
+    '        <Lookup glyph="parenright" value="parenright.vertical"/>',
+    '      </Substitution>',
+    '    </NoncontextualMorph>',
+    '  </MorphSubtable>',
+    '</MorphChain>',
+]
+
+
+class MORTNoncontextualGlyphSubstitutionTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.maxDiff = None
+        glyphs = ['.notdef'] + ['g.%d' % i for i in range (1, 140)]
+        glyphs[11], glyphs[13] = 'parenleft', 'parenright'
+        glyphs[135], glyphs[136] = 'parenleft.vertical', 'parenright.vertical'
+        cls.font = FakeFont(glyphs)
+
+    def test_decompile_toXML(self):
+        table = newTable('mort')
+        table.decompile(MORT_NONCONTEXTUAL_DATA, self.font)
+        self.assertEqual(getXML(table.toXML), MORT_NONCONTEXTUAL_XML)
+
+    def test_compile_fromXML(self):
+        table = newTable('mort')
+        for name, attrs, content in parseXML(MORT_NONCONTEXTUAL_XML):
+            table.fromXML(name, attrs, content, font=self.font)
+        self.assertEqual(hexStr(table.compile(self.font)),
+                         hexStr(MORT_NONCONTEXTUAL_DATA))
+
+
+if __name__ == '__main__':
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/_m_o_r_x_test.py b/Tests/ttLib/tables/_m_o_r_x_test.py
new file mode 100644
index 0000000..7deb39f
--- /dev/null
+++ b/Tests/ttLib/tables/_m_o_r_x_test.py
@@ -0,0 +1,903 @@
+# coding: utf-8
+from __future__ import print_function, division, absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.misc.testTools import FakeFont, getXML, parseXML
+from fontTools.misc.textTools import deHexStr, hexStr
+from fontTools.ttLib import newTable
+import unittest
+
+
+# A simple 'morx' table with non-contextual glyph substitution.
+# Unfortunately, the Apple spec for 'morx' does not contain a complete example.
+# The test case has therefore been adapted from the example 'mort' table in
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6mort.html
+MORX_NONCONTEXTUAL_DATA = deHexStr(
+    '0002 0000 '  #  0: Version=2, Reserved=0
+    '0000 0001 '  #  4: MorphChainCount=1
+    '0000 0001 '  #  8: DefaultFlags=1
+    '0000 0058 '  # 12: StructLength=88
+    '0000 0003 '  # 16: MorphFeatureCount=3
+    '0000 0001 '  # 20: MorphSubtableCount=1
+    '0004 0000 '  # 24: Feature[0].FeatureType=4/VertSubst, .FeatureSetting=on
+    '0000 0001 '  # 28: Feature[0].EnableFlags=0x00000001
+    'FFFF FFFF '  # 32: Feature[0].DisableFlags=0xFFFFFFFF
+    '0004 0001 '  # 36: Feature[1].FeatureType=4/VertSubst, .FeatureSetting=off
+    '0000 0000 '  # 40: Feature[1].EnableFlags=0x00000000
+    'FFFF FFFE '  # 44: Feature[1].DisableFlags=0xFFFFFFFE
+    '0000 0001 '  # 48: Feature[2].FeatureType=0/GlyphEffects, .FeatSetting=off
+    '0000 0000 '  # 52: Feature[2].EnableFlags=0 (required for last feature)
+    '0000 0000 '  # 56: Feature[2].EnableFlags=0 (required for last feature)
+    '0000 0024 '  # 60: Subtable[0].StructLength=36
+    '80 '         # 64: Subtable[0].CoverageFlags=0x80
+    '00 00 '      # 65: Subtable[0].Reserved=0
+    '04 '         # 67: Subtable[0].MorphType=4/NoncontextualMorph
+    '0000 0001 '  # 68: Subtable[0].SubFeatureFlags=0x1
+    '0006 0004 '  # 72: LookupFormat=6, UnitSize=4
+    '0002 0008 '  # 76: NUnits=2, SearchRange=8
+    '0001 0000 '  # 80: EntrySelector=1, RangeShift=0
+    '000B 0087 '  # 84: Glyph=11 (parenleft); Value=135 (parenleft.vertical)
+    '000D 0088 '  # 88: Glyph=13 (parenright); Value=136 (parenright.vertical)
+    'FFFF 0000 '  # 92: Glyph=<end>; Value=0
+)                 # 96: <end>
+assert len(MORX_NONCONTEXTUAL_DATA) == 96
+
+
+MORX_NONCONTEXTUAL_XML = [
+    '<Version value="2"/>',
+    '<Reserved value="0"/>',
+    '<!-- MorphChainCount=1 -->',
+    '<MorphChain index="0">',
+    '  <DefaultFlags value="0x00000001"/>',
+    '  <!-- StructLength=88 -->',
+    '  <!-- MorphFeatureCount=3 -->',
+    '  <!-- MorphSubtableCount=1 -->',
+    '  <MorphFeature index="0">',
+    '    <FeatureType value="4"/>',
+    '    <FeatureSetting value="0"/>',
+    '    <EnableFlags value="0x00000001"/>',
+    '    <DisableFlags value="0xFFFFFFFF"/>',
+    '  </MorphFeature>',
+    '  <MorphFeature index="1">',
+    '    <FeatureType value="4"/>',
+    '    <FeatureSetting value="1"/>',
+    '    <EnableFlags value="0x00000000"/>',
+    '    <DisableFlags value="0xFFFFFFFE"/>',
+    '  </MorphFeature>',
+    '  <MorphFeature index="2">',
+    '    <FeatureType value="0"/>',
+    '    <FeatureSetting value="1"/>',
+    '    <EnableFlags value="0x00000000"/>',
+    '    <DisableFlags value="0x00000000"/>',
+    '  </MorphFeature>',
+    '  <MorphSubtable index="0">',
+    '    <!-- StructLength=36 -->',
+    '    <TextDirection value="Vertical"/>',
+    '    <ProcessingOrder value="LayoutOrder"/>',
+    '    <!-- MorphType=4 -->',
+    '    <SubFeatureFlags value="0x00000001"/>',
+    '    <NoncontextualMorph>',
+    '      <Substitution>',
+    '        <Lookup glyph="parenleft" value="parenleft.vertical"/>',
+    '        <Lookup glyph="parenright" value="parenright.vertical"/>',
+    '      </Substitution>',
+    '    </NoncontextualMorph>',
+    '  </MorphSubtable>',
+    '</MorphChain>',
+]
+
+
+MORX_REARRANGEMENT_DATA = deHexStr(
+    '0002 0000 '  #  0: Version=2, Reserved=0
+    '0000 0001 '  #  4: MorphChainCount=1
+    '0000 0001 '  #  8: DefaultFlags=1
+    '0000 0078 '  # 12: StructLength=120 (+8=128)
+    '0000 0000 '  # 16: MorphFeatureCount=0
+    '0000 0001 '  # 20: MorphSubtableCount=1
+    '0000 0068 '  # 24: Subtable[0].StructLength=104 (+24=128)
+    '80 '         # 28: Subtable[0].CoverageFlags=0x80
+    '00 00 '      # 29: Subtable[0].Reserved=0
+    '00 '         # 31: Subtable[0].MorphType=0/RearrangementMorph
+    '0000 0001 '  # 32: Subtable[0].SubFeatureFlags=0x1
+    '0000 0006 '  # 36: STXHeader.ClassCount=6
+    '0000 0010 '  # 40: STXHeader.ClassTableOffset=16 (+36=52)
+    '0000 0028 '  # 44: STXHeader.StateArrayOffset=40 (+36=76)
+    '0000 004C '  # 48: STXHeader.EntryTableOffset=76 (+36=112)
+    '0006 0004 '  # 52: ClassTable.LookupFormat=6, .UnitSize=4
+    '0002 0008 '  # 56:   .NUnits=2, .SearchRange=8
+    '0001 0000 '  # 60:   .EntrySelector=1, .RangeShift=0
+    '0001 0005 '  # 64:   Glyph=A; Class=5
+    '0003 0004 '  # 68:   Glyph=C; Class=4
+    'FFFF 0000 '  # 72:   Glyph=<end>; Value=0
+    '0000 0001 0002 0003 0002 0001 '  #  76: State[0][0..5]
+    '0003 0003 0003 0003 0003 0003 '  #  88: State[1][0..5]
+    '0001 0003 0003 0003 0002 0002 '  # 100: State[2][0..5]
+    '0002 FFFF '  # 112: Entries[0].NewState=2, .Flags=0xFFFF
+    '0001 A00D '  # 116: Entries[1].NewState=1, .Flags=0xA00D
+    '0000 8006 '  # 120: Entries[2].NewState=0, .Flags=0x8006
+    '0002 0000 '  # 124: Entries[3].NewState=2, .Flags=0x0000
+)                 # 128: <end>
+assert len(MORX_REARRANGEMENT_DATA) == 128, len(MORX_REARRANGEMENT_DATA)
+
+
+MORX_REARRANGEMENT_XML = [
+    '<Version value="2"/>',
+    '<Reserved value="0"/>',
+    '<!-- MorphChainCount=1 -->',
+    '<MorphChain index="0">',
+    '  <DefaultFlags value="0x00000001"/>',
+    '  <!-- StructLength=120 -->',
+    '  <!-- MorphFeatureCount=0 -->',
+    '  <!-- MorphSubtableCount=1 -->',
+    '  <MorphSubtable index="0">',
+    '    <!-- StructLength=104 -->',
+    '    <TextDirection value="Vertical"/>',
+    '    <ProcessingOrder value="LayoutOrder"/>',
+    '    <!-- MorphType=0 -->',
+    '    <SubFeatureFlags value="0x00000001"/>',
+    '    <RearrangementMorph>',
+    '      <StateTable>',
+    '        <!-- GlyphClassCount=6 -->',
+    '        <GlyphClass glyph="A" value="5"/>',
+    '        <GlyphClass glyph="C" value="4"/>',
+    '        <State index="0">',
+    '          <Transition onGlyphClass="0">',
+    '            <NewState value="2"/>',
+    '            <Flags value="MarkFirst,DontAdvance,MarkLast"/>',
+    '            <ReservedFlags value="0x1FF0"/>',
+    '            <Verb value="15"/><!-- ABxCD ⇒ DCxBA -->',
+    '          </Transition>',
+    '          <Transition onGlyphClass="1">',
+    '            <NewState value="1"/>',
+    '            <Flags value="MarkFirst,MarkLast"/>',
+    '            <Verb value="13"/><!-- ABxCD ⇒ CDxBA -->',
+    '          </Transition>',
+    '          <Transition onGlyphClass="2">',
+    '            <NewState value="0"/>',
+    '            <Flags value="MarkFirst"/>',
+    '            <Verb value="6"/><!-- xCD ⇒ CDx -->',
+    '          </Transition>',
+    '          <Transition onGlyphClass="3">',
+    '            <NewState value="2"/>',
+    '            <Verb value="0"/><!-- no change -->',
+    '          </Transition>',
+    '          <Transition onGlyphClass="4">',
+    '            <NewState value="0"/>',
+    '            <Flags value="MarkFirst"/>',
+    '            <Verb value="6"/><!-- xCD ⇒ CDx -->',
+    '          </Transition>',
+    '          <Transition onGlyphClass="5">',
+    '            <NewState value="1"/>',
+    '            <Flags value="MarkFirst,MarkLast"/>',
+    '            <Verb value="13"/><!-- ABxCD ⇒ CDxBA -->',
+    '          </Transition>',
+    '        </State>',
+    '        <State index="1">',
+    '          <Transition onGlyphClass="0">',
+    '            <NewState value="2"/>',
+    '            <Verb value="0"/><!-- no change -->',
+    '          </Transition>',
+    '          <Transition onGlyphClass="1">',
+    '            <NewState value="2"/>',
+    '            <Verb value="0"/><!-- no change -->',
+    '          </Transition>',
+    '          <Transition onGlyphClass="2">',
+    '            <NewState value="2"/>',
+    '            <Verb value="0"/><!-- no change -->',
+    '          </Transition>',
+    '          <Transition onGlyphClass="3">',
+    '            <NewState value="2"/>',
+    '            <Verb value="0"/><!-- no change -->',
+    '          </Transition>',
+    '          <Transition onGlyphClass="4">',
+    '            <NewState value="2"/>',
+    '            <Verb value="0"/><!-- no change -->',
+    '          </Transition>',
+    '          <Transition onGlyphClass="5">',
+    '            <NewState value="2"/>',
+    '            <Verb value="0"/><!-- no change -->',
+    '          </Transition>',
+    '        </State>',
+    '        <State index="2">',
+    '          <Transition onGlyphClass="0">',
+    '            <NewState value="1"/>',
+    '            <Flags value="MarkFirst,MarkLast"/>',
+    '            <Verb value="13"/><!-- ABxCD ⇒ CDxBA -->',
+    '          </Transition>',
+    '          <Transition onGlyphClass="1">',
+    '            <NewState value="2"/>',
+    '            <Verb value="0"/><!-- no change -->',
+    '          </Transition>',
+    '          <Transition onGlyphClass="2">',
+    '            <NewState value="2"/>',
+    '            <Verb value="0"/><!-- no change -->',
+    '          </Transition>',
+    '          <Transition onGlyphClass="3">',
+    '            <NewState value="2"/>',
+    '            <Verb value="0"/><!-- no change -->',
+    '          </Transition>',
+    '          <Transition onGlyphClass="4">',
+    '            <NewState value="0"/>',
+    '            <Flags value="MarkFirst"/>',
+    '            <Verb value="6"/><!-- xCD ⇒ CDx -->',
+    '          </Transition>',
+    '          <Transition onGlyphClass="5">',
+    '            <NewState value="0"/>',
+    '            <Flags value="MarkFirst"/>',
+    '            <Verb value="6"/><!-- xCD ⇒ CDx -->',
+    '          </Transition>',
+    '        </State>',
+    '      </StateTable>',
+    '    </RearrangementMorph>',
+    '  </MorphSubtable>',
+    '</MorphChain>',
+]
+
+
+# Taken from “Example 1: A contextal substituation table” in
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html
+# as retrieved on 2017-09-05.
+#
+# Compared to the example table in Apple’s specification, we’ve
+# made the following changes:
+#
+# * at offsets 0..35, we’ve prepended 36 bytes of boilerplate
+#   to make the data a structurally valid ‘morx’ table;
+#
+# * at offset 36 (offset 0 in Apple’s document), we’ve changed
+#   the number of glyph classes from 5 to 6 because the encoded
+#   finite-state machine has transitions for six different glyph
+#   classes (0..5);
+#
+# * at offset 52 (offset 16 in Apple’s document), we’ve replaced
+#   the presumably leftover ‘XXX’ mark by an actual data offset;
+#
+# * at offset 72 (offset 36 in Apple’s document), we’ve changed
+#   the input GlyphID from 51 to 52. With the original value of 51,
+#   the glyph class lookup table can be encoded with equally many
+#   bytes in either format 2 or 6; after changing the GlyphID to 52,
+#   the most compact encoding is lookup format 6, as used in Apple’s
+#   example;
+#
+# * at offset 90 (offset 54 in Apple’s document), we’ve changed
+#   the value for the lookup end-of-table marker from 1 to 0.
+#   Fonttools always uses zero for this value, whereas Apple’s
+#   spec examples are inconsistently using one of {0, 1, 0xFFFF}
+#   for this filler value;
+#
+# * at offset 172 (offset 136 in Apple’s document), we’ve again changed
+#   the input GlyphID from 51 to 52, for the same reason as above.
+#
+# TODO: Ask Apple to fix “Example 1” in the ‘morx’ specification.
+MORX_CONTEXTUAL_DATA = deHexStr(
+    '0002 0000 '  #  0: Version=2, Reserved=0
+    '0000 0001 '  #  4: MorphChainCount=1
+    '0000 0001 '  #  8: DefaultFlags=1
+    '0000 00B4 '  # 12: StructLength=180 (+8=188)
+    '0000 0000 '  # 16: MorphFeatureCount=0
+    '0000 0001 '  # 20: MorphSubtableCount=1
+    '0000 00A4 '  # 24: Subtable[0].StructLength=164 (+24=188)
+    '80 '         # 28: Subtable[0].CoverageFlags=0x80
+    '00 00 '      # 29: Subtable[0].Reserved=0
+    '01 '         # 31: Subtable[0].MorphType=1/ContextualMorph
+    '0000 0001 '  # 32: Subtable[0].SubFeatureFlags=0x1
+    '0000 0006 '  # 36: STXHeader.ClassCount=6
+    '0000 0014 '  # 40: STXHeader.ClassTableOffset=20 (+36=56)
+    '0000 0038 '  # 44: STXHeader.StateArrayOffset=56 (+36=92)
+    '0000 005C '  # 48: STXHeader.EntryTableOffset=92 (+36=128)
+    '0000 0074 '  # 52: STXHeader.PerGlyphTableOffset=116 (+36=152)
+
+    # Glyph class table.
+    '0006 0004 '  # 56: ClassTable.LookupFormat=6, .UnitSize=4
+    '0005 0010 '  # 60:   .NUnits=5, .SearchRange=16
+    '0002 0004 '  # 64:   .EntrySelector=2, .RangeShift=4
+    '0032 0004 '  # 68:   Glyph=50; Class=4
+    '0034 0004 '  # 72:   Glyph=52; Class=4
+    '0050 0005 '  # 76:   Glyph=80; Class=5
+    '00C9 0004 '  # 80:   Glyph=201; Class=4
+    '00CA 0004 '  # 84:   Glyph=202; Class=4
+    'FFFF 0000 '  # 88:   Glyph=<end>; Value=<filler>
+
+    # State array.
+    '0000 0000 0000 0000 0000 0001 '  #  92: State[0][0..5]
+    '0000 0000 0000 0000 0000 0001 '  # 104: State[1][0..5]
+    '0000 0000 0000 0000 0002 0001 '  # 116: State[2][0..5]
+
+    # Entry table.
+    '0000 0000 '  # 128: Entries[0].NewState=0, .Flags=0
+    'FFFF FFFF '  # 132: Entries[0].MarkSubst=None, .CurSubst=None
+    '0002 0000 '  # 136: Entries[1].NewState=2, .Flags=0
+    'FFFF FFFF '  # 140: Entries[1].MarkSubst=None, .CurSubst=None
+    '0000 0000 '  # 144: Entries[2].NewState=0, .Flags=0
+    'FFFF 0000 '  # 148: Entries[2].MarkSubst=None, .CurSubst=PerGlyph #0
+                  # 152: <no padding needed for 4-byte alignment>
+
+    # Per-glyph lookup tables.
+    '0000 0004 '  # 152: Offset from this point to per-glyph lookup #0.
+
+    # Per-glyph lookup #0.
+    '0006 0004 '  # 156: ClassTable.LookupFormat=6, .UnitSize=4
+    '0004 0010 '  # 160:   .NUnits=4, .SearchRange=16
+    '0002 0000 '  # 164:   .EntrySelector=2, .RangeShift=0
+    '0032 0258 '  # 168:   Glyph=50; ReplacementGlyph=600
+    '0034 0259 '  # 172:   Glyph=52; ReplacementGlyph=601
+    '00C9 025A '  # 176:   Glyph=201; ReplacementGlyph=602
+    '00CA 0384 '  # 180:   Glyph=202; ReplacementGlyph=900
+    'FFFF 0000 '  # 184:   Glyph=<end>; Value=<filler>
+
+)                 # 188: <end>
+assert len(MORX_CONTEXTUAL_DATA) == 188, len(MORX_CONTEXTUAL_DATA)
+
+
+MORX_CONTEXTUAL_XML = [
+    '<Version value="2"/>',
+    '<Reserved value="0"/>',
+    '<!-- MorphChainCount=1 -->',
+    '<MorphChain index="0">',
+    '  <DefaultFlags value="0x00000001"/>',
+    '  <!-- StructLength=180 -->',
+    '  <!-- MorphFeatureCount=0 -->',
+    '  <!-- MorphSubtableCount=1 -->',
+    '  <MorphSubtable index="0">',
+    '    <!-- StructLength=164 -->',
+    '    <TextDirection value="Vertical"/>',
+    '    <ProcessingOrder value="LayoutOrder"/>',
+    '    <!-- MorphType=1 -->',
+    '    <SubFeatureFlags value="0x00000001"/>',
+    '    <ContextualMorph>',
+    '      <StateTable>',
+    '        <!-- GlyphClassCount=6 -->',
+    '        <GlyphClass glyph="A" value="4"/>',
+    '        <GlyphClass glyph="B" value="4"/>',
+    '        <GlyphClass glyph="C" value="5"/>',
+    '        <GlyphClass glyph="X" value="4"/>',
+    '        <GlyphClass glyph="Y" value="4"/>',
+    '        <State index="0">',
+    '          <Transition onGlyphClass="0">',
+    '            <NewState value="0"/>',
+    '            <MarkIndex value="65535"/>',
+    '            <CurrentIndex value="65535"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="1">',
+    '            <NewState value="0"/>',
+    '            <MarkIndex value="65535"/>',
+    '            <CurrentIndex value="65535"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="2">',
+    '            <NewState value="0"/>',
+    '            <MarkIndex value="65535"/>',
+    '            <CurrentIndex value="65535"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="3">',
+    '            <NewState value="0"/>',
+    '            <MarkIndex value="65535"/>',
+    '            <CurrentIndex value="65535"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="4">',
+    '            <NewState value="0"/>',
+    '            <MarkIndex value="65535"/>',
+    '            <CurrentIndex value="65535"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="5">',
+    '            <NewState value="2"/>',
+    '            <MarkIndex value="65535"/>',
+    '            <CurrentIndex value="65535"/>',
+    '          </Transition>',
+    '        </State>',
+    '        <State index="1">',
+    '          <Transition onGlyphClass="0">',
+    '            <NewState value="0"/>',
+    '            <MarkIndex value="65535"/>',
+    '            <CurrentIndex value="65535"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="1">',
+    '            <NewState value="0"/>',
+    '            <MarkIndex value="65535"/>',
+    '            <CurrentIndex value="65535"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="2">',
+    '            <NewState value="0"/>',
+    '            <MarkIndex value="65535"/>',
+    '            <CurrentIndex value="65535"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="3">',
+    '            <NewState value="0"/>',
+    '            <MarkIndex value="65535"/>',
+    '            <CurrentIndex value="65535"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="4">',
+    '            <NewState value="0"/>',
+    '            <MarkIndex value="65535"/>',
+    '            <CurrentIndex value="65535"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="5">',
+    '            <NewState value="2"/>',
+    '            <MarkIndex value="65535"/>',
+    '            <CurrentIndex value="65535"/>',
+    '          </Transition>',
+    '        </State>',
+    '        <State index="2">',
+    '          <Transition onGlyphClass="0">',
+    '            <NewState value="0"/>',
+    '            <MarkIndex value="65535"/>',
+    '            <CurrentIndex value="65535"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="1">',
+    '            <NewState value="0"/>',
+    '            <MarkIndex value="65535"/>',
+    '            <CurrentIndex value="65535"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="2">',
+    '            <NewState value="0"/>',
+    '            <MarkIndex value="65535"/>',
+    '            <CurrentIndex value="65535"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="3">',
+    '            <NewState value="0"/>',
+    '            <MarkIndex value="65535"/>',
+    '            <CurrentIndex value="65535"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="4">',
+    '            <NewState value="0"/>',
+    '            <MarkIndex value="65535"/>',
+    '            <CurrentIndex value="0"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="5">',
+    '            <NewState value="2"/>',
+    '            <MarkIndex value="65535"/>',
+    '            <CurrentIndex value="65535"/>',
+    '          </Transition>',
+    '        </State>',
+    '        <PerGlyphLookup index="0">',
+    '          <Lookup glyph="A" value="A.swash"/>',
+    '          <Lookup glyph="B" value="B.swash"/>',
+    '          <Lookup glyph="X" value="X.swash"/>',
+    '          <Lookup glyph="Y" value="Y.swash"/>',
+    '        </PerGlyphLookup>',
+    '      </StateTable>',
+    '    </ContextualMorph>',
+    '  </MorphSubtable>',
+    '</MorphChain>',
+]
+
+
+# Taken from “Example 2: A ligature table” in
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html
+# as retrieved on 2017-09-11.
+#
+# Compared to the example table in Apple’s specification, we’ve
+# made the following changes:
+#
+# * at offsets 0..35, we’ve prepended 36 bytes of boilerplate
+#   to make the data a structurally valid ‘morx’ table;
+#
+# * at offsets 88..91 (offsets 52..55 in Apple’s document), we’ve
+#   changed the range of the third segment from 23..24 to 26..28.
+#   The hexdump values in Apple’s specification are completely wrong;
+#   the values from the comments would work, but they can be encoded
+#   more compactly than in the specification example. For round-trip
+#   testing, we omit the ‘f’ glyph, which makes AAT lookup format 2
+#   the most compact encoding;
+#
+# * at offsets 92..93 (offsets 56..57 in Apple’s document), we’ve
+#   changed the glyph class of the third segment from 5 to 6, which
+#   matches the values from the comments to the spec (but not the
+#   Apple’s hexdump).
+#
+# TODO: Ask Apple to fix “Example 2” in the ‘morx’ specification.
+MORX_LIGATURE_DATA = deHexStr(
+    '0002 0000 '  #  0: Version=2, Reserved=0
+    '0000 0001 '  #  4: MorphChainCount=1
+    '0000 0001 '  #  8: DefaultFlags=1
+    '0000 00DA '  # 12: StructLength=218 (+8=226)
+    '0000 0000 '  # 16: MorphFeatureCount=0
+    '0000 0001 '  # 20: MorphSubtableCount=1
+    '0000 00CA '  # 24: Subtable[0].StructLength=202 (+24=226)
+    '80 '         # 28: Subtable[0].CoverageFlags=0x80
+    '00 00 '      # 29: Subtable[0].Reserved=0
+    '02 '         # 31: Subtable[0].MorphType=2/LigatureMorph
+    '0000 0001 '  # 32: Subtable[0].SubFeatureFlags=0x1
+
+    # State table header.
+    '0000 0007 '  # 36: STXHeader.ClassCount=7
+    '0000 001C '  # 40: STXHeader.ClassTableOffset=28 (+36=64)
+    '0000 0040 '  # 44: STXHeader.StateArrayOffset=64 (+36=100)
+    '0000 0078 '  # 48: STXHeader.EntryTableOffset=120 (+36=156)
+    '0000 0090 '  # 52: STXHeader.LigActionsOffset=144 (+36=180)
+    '0000 009C '  # 56: STXHeader.LigComponentsOffset=156 (+36=192)
+    '0000 00AE '  # 60: STXHeader.LigListOffset=174 (+36=210)
+
+    # Glyph class table.
+    '0002 0006 '       # 64: ClassTable.LookupFormat=2, .UnitSize=6
+    '0003 000C '       # 68:   .NUnits=3, .SearchRange=12
+    '0001 0006 '       # 72:   .EntrySelector=1, .RangeShift=6
+    '0016 0014 0004 '  # 76: GlyphID 20..22 [a..c] -> GlyphClass 4
+    '0018 0017 0005 '  # 82: GlyphID 23..24 [d..e] -> GlyphClass 5
+    '001C 001A 0006 '  # 88: GlyphID 26..28 [g..i] -> GlyphClass 6
+    'FFFF FFFF 0000 '  # 94: <end of lookup>
+
+    # State array.
+    '0000 0000 0000 0000 0001 0000 0000 '  # 100: State[0][0..6]
+    '0000 0000 0000 0000 0001 0000 0000 '  # 114: State[1][0..6]
+    '0000 0000 0000 0000 0001 0002 0000 '  # 128: State[2][0..6]
+    '0000 0000 0000 0000 0001 0002 0003 '  # 142: State[3][0..6]
+
+    # Entry table.
+    '0000 0000 '  # 156: Entries[0].NewState=0, .Flags=0
+    '0000 '       # 160: Entries[0].ActionIndex=<n/a> because no 0x2000 flag
+    '0002 8000 '  # 162: Entries[1].NewState=2, .Flags=0x8000 (SetComponent)
+    '0000 '       # 166: Entries[1].ActionIndex=<n/a> because no 0x2000 flag
+    '0003 8000 '  # 168: Entries[2].NewState=3, .Flags=0x8000 (SetComponent)
+    '0000 '       # 172: Entries[2].ActionIndex=<n/a> because no 0x2000 flag
+    '0000 A000 '  # 174: Entries[3].NewState=0, .Flags=0xA000 (SetComponent,Act)
+    '0000 '       # 178: Entries[3].ActionIndex=0 (start at Action[0])
+
+    # Ligature actions table.
+    '3FFF FFE7 '  # 180: Action[0].Flags=0, .GlyphIndexDelta=-25
+    '3FFF FFED '  # 184: Action[1].Flags=0, .GlyphIndexDelta=-19
+    'BFFF FFF2 '  # 188: Action[2].Flags=<end of list>, .GlyphIndexDelta=-14
+
+    # Ligature component table.
+    '0000 0001 '  # 192: LigComponent[0]=0, LigComponent[1]=1
+    '0002 0003 '  # 196: LigComponent[2]=2, LigComponent[3]=3
+    '0000 0004 '  # 200: LigComponent[4]=0, LigComponent[5]=4
+    '0000 0008 '  # 204: LigComponent[6]=0, LigComponent[7]=8
+    '0010      '  # 208: LigComponent[8]=16
+
+    # Ligature list.
+    '03E8 03E9 '  # 210: LigList[0]=1000, LigList[1]=1001
+    '03EA 03EB '  # 214: LigList[2]=1002, LigList[3]=1003
+    '03EC 03ED '  # 218: LigList[4]=1004, LigList[3]=1005
+    '03EE 03EF '  # 222: LigList[5]=1006, LigList[6]=1007
+)   # 226: <end>
+assert len(MORX_LIGATURE_DATA) == 226, len(MORX_LIGATURE_DATA)
+
+
+MORX_LIGATURE_XML = [
+    '<Version value="2"/>',
+    '<Reserved value="0"/>',
+    '<!-- MorphChainCount=1 -->',
+    '<MorphChain index="0">',
+    '  <DefaultFlags value="0x00000001"/>',
+    '  <!-- StructLength=218 -->',
+    '  <!-- MorphFeatureCount=0 -->',
+    '  <!-- MorphSubtableCount=1 -->',
+    '  <MorphSubtable index="0">',
+    '    <!-- StructLength=202 -->',
+    '    <TextDirection value="Vertical"/>',
+    '    <ProcessingOrder value="LayoutOrder"/>',
+    '    <!-- MorphType=2 -->',
+    '    <SubFeatureFlags value="0x00000001"/>',
+    '    <LigatureMorph>',
+    '      <StateTable>',
+    '        <!-- GlyphClassCount=7 -->',
+    '        <GlyphClass glyph="a" value="4"/>',
+    '        <GlyphClass glyph="b" value="4"/>',
+    '        <GlyphClass glyph="c" value="4"/>',
+    '        <GlyphClass glyph="d" value="5"/>',
+    '        <GlyphClass glyph="e" value="5"/>',
+    '        <GlyphClass glyph="g" value="6"/>',
+    '        <GlyphClass glyph="h" value="6"/>',
+    '        <GlyphClass glyph="i" value="6"/>',
+    '        <State index="0">',
+    '          <Transition onGlyphClass="0">',
+    '            <NewState value="0"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="1">',
+    '            <NewState value="0"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="2">',
+    '            <NewState value="0"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="3">',
+    '            <NewState value="0"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="4">',
+    '            <NewState value="2"/>',
+    '            <Flags value="SetComponent"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="5">',
+    '            <NewState value="0"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="6">',
+    '            <NewState value="0"/>',
+    '          </Transition>',
+    '        </State>',
+    '        <State index="1">',
+    '          <Transition onGlyphClass="0">',
+    '            <NewState value="0"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="1">',
+    '            <NewState value="0"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="2">',
+    '            <NewState value="0"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="3">',
+    '            <NewState value="0"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="4">',
+    '            <NewState value="2"/>',
+    '            <Flags value="SetComponent"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="5">',
+    '            <NewState value="0"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="6">',
+    '            <NewState value="0"/>',
+    '          </Transition>',
+    '        </State>',
+    '        <State index="2">',
+    '          <Transition onGlyphClass="0">',
+    '            <NewState value="0"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="1">',
+    '            <NewState value="0"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="2">',
+    '            <NewState value="0"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="3">',
+    '            <NewState value="0"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="4">',
+    '            <NewState value="2"/>',
+    '            <Flags value="SetComponent"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="5">',
+    '            <NewState value="3"/>',
+    '            <Flags value="SetComponent"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="6">',
+    '            <NewState value="0"/>',
+    '          </Transition>',
+    '        </State>',
+    '        <State index="3">',
+    '          <Transition onGlyphClass="0">',
+    '            <NewState value="0"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="1">',
+    '            <NewState value="0"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="2">',
+    '            <NewState value="0"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="3">',
+    '            <NewState value="0"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="4">',
+    '            <NewState value="2"/>',
+    '            <Flags value="SetComponent"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="5">',
+    '            <NewState value="3"/>',
+    '            <Flags value="SetComponent"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="6">',
+    '            <NewState value="0"/>',
+    '            <Flags value="SetComponent"/>',
+    '            <Action GlyphIndexDelta="-25"/>',
+    '            <Action GlyphIndexDelta="-19"/>',
+    '            <Action GlyphIndexDelta="-14"/>',
+    '          </Transition>',
+    '        </State>',
+    '        <LigComponents>',
+    '          <LigComponent index="0" value="0"/>',
+    '          <LigComponent index="1" value="1"/>',
+    '          <LigComponent index="2" value="2"/>',
+    '          <LigComponent index="3" value="3"/>',
+    '          <LigComponent index="4" value="0"/>',
+    '          <LigComponent index="5" value="4"/>',
+    '          <LigComponent index="6" value="0"/>',
+    '          <LigComponent index="7" value="8"/>',
+    '          <LigComponent index="8" value="16"/>',
+    '        </LigComponents>',
+    '        <Ligatures>',
+    '          <Ligature glyph="adf" index="0"/>',
+    '          <Ligature glyph="adg" index="1"/>',
+    '          <Ligature glyph="adh" index="2"/>',
+    '          <Ligature glyph="adi" index="3"/>',
+    '          <Ligature glyph="aef" index="4"/>',
+    '          <Ligature glyph="aeg" index="5"/>',
+    '          <Ligature glyph="aeh" index="6"/>',
+    '          <Ligature glyph="aei" index="7"/>',
+    '        </Ligatures>',
+    '      </StateTable>',
+    '    </LigatureMorph>',
+    '  </MorphSubtable>',
+    '</MorphChain>',
+]
+
+
+class MORXNoncontextualGlyphSubstitutionTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.maxDiff = None
+        glyphs = ['.notdef'] + ['g.%d' % i for i in range (1, 140)]
+        glyphs[11], glyphs[13] = 'parenleft', 'parenright'
+        glyphs[135], glyphs[136] = 'parenleft.vertical', 'parenright.vertical'
+        cls.font = FakeFont(glyphs)
+
+    def test_decompile_toXML(self):
+        table = newTable('morx')
+        table.decompile(MORX_NONCONTEXTUAL_DATA, self.font)
+        self.assertEqual(getXML(table.toXML), MORX_NONCONTEXTUAL_XML)
+
+    def test_compile_fromXML(self):
+        table = newTable('morx')
+        for name, attrs, content in parseXML(MORX_NONCONTEXTUAL_XML):
+            table.fromXML(name, attrs, content, font=self.font)
+        self.assertEqual(hexStr(table.compile(self.font)),
+                         hexStr(MORX_NONCONTEXTUAL_DATA))
+
+
+class MORXRearrangementTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.maxDiff = None
+        cls.font = FakeFont(['.nodef', 'A', 'B', 'C'])
+
+    def test_decompile_toXML(self):
+        table = newTable('morx')
+        table.decompile(MORX_REARRANGEMENT_DATA, self.font)
+        self.assertEqual(getXML(table.toXML), MORX_REARRANGEMENT_XML)
+
+    def test_compile_fromXML(self):
+        table = newTable('morx')
+        for name, attrs, content in parseXML(MORX_REARRANGEMENT_XML):
+            table.fromXML(name, attrs, content, font=self.font)
+        self.assertEqual(hexStr(table.compile(self.font)),
+                         hexStr(MORX_REARRANGEMENT_DATA))
+
+
+class MORXContextualSubstitutionTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.maxDiff = None
+        g = ['.notdef'] + ['g.%d' % i for i in range (1, 910)]
+        g[80] = 'C'
+        g[50], g[52], g[201], g[202] = 'A', 'B', 'X', 'Y'
+        g[600], g[601], g[602], g[900] = (
+            'A.swash', 'B.swash', 'X.swash', 'Y.swash')
+        cls.font = FakeFont(g)
+
+    def test_decompile_toXML(self):
+        table = newTable('morx')
+        table.decompile(MORX_CONTEXTUAL_DATA, self.font)
+        self.assertEqual(getXML(table.toXML), MORX_CONTEXTUAL_XML)
+
+    def test_compile_fromXML(self):
+        table = newTable('morx')
+        for name, attrs, content in parseXML(MORX_CONTEXTUAL_XML):
+            table.fromXML(name, attrs, content, font=self.font)
+        self.assertEqual(hexStr(table.compile(self.font)),
+                         hexStr(MORX_CONTEXTUAL_DATA))
+
+
+class MORXLigatureSubstitutionTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.maxDiff = None
+        g = ['.notdef'] + ['g.%d' % i for i in range (1, 1515)]
+        g[20:29] = 'a b c d e f g h i'.split()
+        g[1000:1008] = 'adf adg adh adi aef aeg aeh aei'.split()
+        g[1008:1016] = 'bdf bdg bdh bdi bef beg beh bei'.split()
+        g[1500:1507] = 'cdf cdg cdh cdi cef ceg ceh'.split()
+        g[1511] = 'cei'
+        cls.font = FakeFont(g)
+
+    def test_decompile_toXML(self):
+        table = newTable('morx')
+        table.decompile(MORX_LIGATURE_DATA, self.font)
+        self.assertEqual(getXML(table.toXML), MORX_LIGATURE_XML)
+
+    def test_compile_fromXML(self):
+        table = newTable('morx')
+        for name, attrs, content in parseXML(MORX_LIGATURE_XML):
+            table.fromXML(name, attrs, content, font=self.font)
+        self.assertEqual(hexStr(table.compile(self.font)),
+                         hexStr(MORX_LIGATURE_DATA))
+
+
+class MORXCoverageFlagsTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.maxDiff = None
+        cls.font = FakeFont(['.notdef', 'A', 'B', 'C'])
+
+    def checkFlags(self, flags, textDirection, processingOrder,
+                   checkCompile=True):
+        data = bytesjoin([
+            MORX_REARRANGEMENT_DATA[:28],
+            bytechr(flags << 4),
+            MORX_REARRANGEMENT_DATA[29:]])
+        xml = []
+        for line in MORX_REARRANGEMENT_XML:
+            if line.startswith('    <TextDirection '):
+                line = '    <TextDirection value="%s"/>' % textDirection
+            elif line.startswith('    <ProcessingOrder '):
+                line = '    <ProcessingOrder value="%s"/>' % processingOrder
+            xml.append(line)
+        table1 = newTable('morx')
+        table1.decompile(data, self.font)
+        self.assertEqual(getXML(table1.toXML), xml)
+        if checkCompile:
+            table2 = newTable('morx')
+            for name, attrs, content in parseXML(xml):
+                table2.fromXML(name, attrs, content, font=self.font)
+            self.assertEqual(hexStr(table2.compile(self.font)), hexStr(data))
+
+    def test_CoverageFlags(self):
+        self.checkFlags(0x0, "Horizontal", "LayoutOrder")
+        self.checkFlags(0x1, "Horizontal", "LogicalOrder")
+        self.checkFlags(0x2, "Any", "LayoutOrder")
+        self.checkFlags(0x3, "Any", "LogicalOrder")
+        self.checkFlags(0x4, "Horizontal", "ReversedLayoutOrder")
+        self.checkFlags(0x5, "Horizontal", "ReversedLogicalOrder")
+        self.checkFlags(0x6, "Any", "ReversedLayoutOrder")
+        self.checkFlags(0x7, "Any", "ReversedLogicalOrder")
+        self.checkFlags(0x8, "Vertical", "LayoutOrder")
+        self.checkFlags(0x9, "Vertical", "LogicalOrder")
+        # We do not always check the compilation to binary data:
+        # some flag combinations do not make sense to emit in binary.
+        # Specifically, if bit 28 (TextDirection=Any) is set in
+        # CoverageFlags, bit 30 (TextDirection=Vertical) is to be
+        # ignored according to the 'morx' specification. We still want
+        # to test the _decoding_ of 'morx' subtables whose CoverageFlags
+        # have both bits 28 and 30 set, since this is a valid flag
+        # combination with defined semantics.  However, our encoder
+        # does not set TextDirection=Vertical when TextDirection=Any.
+        self.checkFlags(0xA, "Any", "LayoutOrder", checkCompile=False)
+        self.checkFlags(0xB, "Any", "LogicalOrder", checkCompile=False)
+        self.checkFlags(0xC, "Vertical", "ReversedLayoutOrder")
+        self.checkFlags(0xD, "Vertical", "ReversedLogicalOrder")
+        self.checkFlags(0xE, "Any", "ReversedLayoutOrder", checkCompile=False)
+        self.checkFlags(0xF, "Any", "ReversedLogicalOrder", checkCompile=False)
+
+    def test_ReservedCoverageFlags(self):
+        # 8A BC DE = TextDirection=Vertical, Reserved=0xABCDE
+        # Note that the lower 4 bits of the first byte are already
+        # part of the Reserved value. We test the full round-trip
+        # to encoding and decoding is quite hairy.
+        data = bytesjoin([
+            MORX_REARRANGEMENT_DATA[:28],
+            bytechr(0x8A), bytechr(0xBC), bytechr(0xDE),
+            MORX_REARRANGEMENT_DATA[31:]])
+        table = newTable('morx')
+        table.decompile(data, self.font)
+        subtable = table.table.MorphChain[0].MorphSubtable[0]
+        self.assertEqual(subtable.Reserved, 0xABCDE)
+        xml = getXML(table.toXML)
+        self.assertIn('    <Reserved value="0xabcde"/>', xml)
+        table2 = newTable('morx')
+        for name, attrs, content in parseXML(xml):
+            table2.fromXML(name, attrs, content, font=self.font)
+        self.assertEqual(hexStr(table2.compile(self.font)[28:31]), "8abcde")
+
+
+class UnsupportedMorxLookupTest(unittest.TestCase):
+    def __init__(self, methodName):
+        unittest.TestCase.__init__(self, methodName)
+        # Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
+        # and fires deprecation warnings if a program uses the old name.
+        if not hasattr(self, "assertRaisesRegex"):
+            self.assertRaisesRegex = self.assertRaisesRegexp
+
+    def test_unsupportedLookupType(self):
+        data = bytesjoin([
+            MORX_NONCONTEXTUAL_DATA[:67],
+            bytechr(66),
+            MORX_NONCONTEXTUAL_DATA[69:]])
+        with self.assertRaisesRegex(AssertionError,
+                                    r"unsupported 'morx' lookup type 66"):
+            morx = newTable('morx')
+            morx.decompile(data, FakeFont(['.notdef']))
+
+
+if __name__ == '__main__':
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/_n_a_m_e_test.py b/Tests/ttLib/tables/_n_a_m_e_test.py
new file mode 100644
index 0000000..a27e3c1
--- /dev/null
+++ b/Tests/ttLib/tables/_n_a_m_e_test.py
@@ -0,0 +1,303 @@
+# -*- coding: utf-8 -*-
+from __future__ import print_function, division, absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.misc import sstruct
+from fontTools.misc.loggingTools import CapturingLogHandler
+from fontTools.misc.testTools import FakeFont
+from fontTools.misc.xmlWriter import XMLWriter
+import struct
+import unittest
+from fontTools.ttLib import newTable
+from fontTools.ttLib.tables._n_a_m_e import (
+	table__n_a_m_e, NameRecord, nameRecordFormat, nameRecordSize, makeName, log)
+
+
+def names(nameTable):
+	result = [(n.nameID, n.platformID, n.platEncID, n.langID, n.string)
+                  for n in nameTable.names]
+	result.sort()
+	return result
+
+
+class NameTableTest(unittest.TestCase):
+
+	def test_getDebugName(self):
+		table = table__n_a_m_e()
+		table.names = [
+			makeName("Bold", 258, 1, 0, 0),  # Mac, MacRoman, English
+			makeName("Gras", 258, 1, 0, 1),  # Mac, MacRoman, French
+			makeName("Fett", 258, 1, 0, 2),  # Mac, MacRoman, German
+			makeName("Sem Fracções", 292, 1, 0, 8)  # Mac, MacRoman, Portuguese
+		]
+		self.assertEqual("Bold", table.getDebugName(258))
+		self.assertEqual("Sem Fracções", table.getDebugName(292))
+		self.assertEqual(None, table.getDebugName(999))
+
+	def test_setName(self):
+		table = table__n_a_m_e()
+		table.setName("Regular", 2, 1, 0, 0)
+		table.setName("Version 1.000", 5, 3, 1, 0x409)
+		table.setName("寬鬆", 276, 1, 2, 0x13)
+		self.assertEqual("Regular", table.getName(2, 1, 0, 0).toUnicode())
+		self.assertEqual("Version 1.000", table.getName(5, 3, 1, 0x409).toUnicode())
+		self.assertEqual("寬鬆", table.getName(276, 1, 2, 0x13).toUnicode())
+		self.assertTrue(len(table.names) == 3)
+		table.setName("緊縮", 276, 1, 2, 0x13)
+		self.assertEqual("緊縮", table.getName(276, 1, 2, 0x13).toUnicode())
+		self.assertTrue(len(table.names) == 3)
+		# passing bytes issues a warning
+		with CapturingLogHandler(log, "WARNING") as captor:
+			table.setName(b"abc", 0, 1, 0, 0)
+		self.assertTrue(
+			len([r for r in captor.records if "string is bytes" in r.msg]) == 1)
+		# anything other than unicode or bytes raises an error
+		with self.assertRaises(TypeError):
+			table.setName(1.000, 5, 1, 0, 0)
+
+	def test_addName(self):
+		table = table__n_a_m_e()
+		nameIDs = []
+		for string in ("Width", "Weight", "Custom"):
+			nameIDs.append(table.addName(string))
+
+		self.assertEqual(nameIDs[0], 256)
+		self.assertEqual(nameIDs[1], 257)
+		self.assertEqual(nameIDs[2], 258)
+		self.assertEqual(len(table.names), 6)
+		self.assertEqual(table.names[0].string, "Width")
+		self.assertEqual(table.names[1].string, "Width")
+		self.assertEqual(table.names[2].string, "Weight")
+		self.assertEqual(table.names[3].string, "Weight")
+		self.assertEqual(table.names[4].string, "Custom")
+		self.assertEqual(table.names[5].string, "Custom")
+
+		with self.assertRaises(ValueError):
+			table.addName('Invalid nameID', minNameID=32767)
+		with self.assertRaises(TypeError):
+			table.addName(b"abc")  # must be unicode string
+
+	def test_addMultilingualName(self):
+		# Microsoft Windows has language codes for “English” (en)
+		# and for “Standard German as used in Switzerland” (de-CH).
+		# In this case, we expect that the implementation just
+		# encodes the name for the Windows platform; Apple platforms
+		# have been able to decode Windows names since the early days
+		# of OSX (~2001). However, Windows has no language code for
+		# “Swiss German as used in Liechtenstein” (gsw-LI), so we
+		# expect that the implementation populates the 'ltag' table
+ 		# to represent that particular, rather exotic BCP47 code.
+		font = FakeFont(glyphs=[".notdef", "A"])
+		nameTable = font.tables['name'] = newTable("name")
+		with CapturingLogHandler(log, "WARNING") as captor:
+			widthID = nameTable.addMultilingualName({
+				"en": "Width",
+				"de-CH": "Breite",
+				"gsw-LI": "Bräiti",
+			}, ttFont=font)
+			self.assertEqual(widthID, 256)
+			xHeightID = nameTable.addMultilingualName({
+				"en": "X-Height",
+				"gsw-LI": "X-Hööchi"
+			}, ttFont=font)
+			self.assertEqual(xHeightID, 257)
+		captor.assertRegex("cannot add Windows name in language gsw-LI")
+		self.assertEqual(names(nameTable), [
+			(256, 0, 4,      0, "Bräiti"),
+			(256, 3, 1, 0x0409, "Width"),
+			(256, 3, 1, 0x0807, "Breite"),
+			(257, 0, 4,      0, "X-Hööchi"),
+			(257, 3, 1, 0x0409, "X-Height"),
+		])
+		self.assertEqual(set(font.tables.keys()), {"ltag", "name"})
+		self.assertEqual(font["ltag"].tags, ["gsw-LI"])
+
+	def test_addMultilingualName_legacyMacEncoding(self):
+		# Windows has no language code for Latin; MacOS has a code;
+		# and we actually can convert the name to the legacy MacRoman
+		# encoding. In this case, we expect that the name gets encoded
+		# as Macintosh name (platformID 1) with the corresponding Mac
+		# language code (133); the 'ltag' table should not be used.
+		font = FakeFont(glyphs=[".notdef", "A"])
+		nameTable = font.tables['name'] = newTable("name")
+		with CapturingLogHandler(log, "WARNING") as captor:
+			nameTable.addMultilingualName({"la": "SPQR"},
+			                              ttFont=font)
+		captor.assertRegex("cannot add Windows name in language la")
+		self.assertEqual(names(nameTable), [(256, 1, 0, 131, "SPQR")])
+		self.assertNotIn("ltag", font.tables.keys())
+
+	def test_addMultilingualName_legacyMacEncodingButUnencodableName(self):
+		# Windows has no language code for Latin; MacOS has a code;
+		# but we cannot encode the name into this encoding because
+		# it contains characters that are not representable.
+		# In this case, we expect that the name gets encoded as
+		# Unicode name (platformID 0) with the language tag being
+		# added to the 'ltag' table.
+		font = FakeFont(glyphs=[".notdef", "A"])
+		nameTable = font.tables['name'] = newTable("name")
+		with CapturingLogHandler(log, "WARNING") as captor:
+			nameTable.addMultilingualName({"la": "ⱾƤℚⱤ"},
+			                              ttFont=font)
+		captor.assertRegex("cannot add Windows name in language la")
+		self.assertEqual(names(nameTable), [(256, 0, 4, 0, "ⱾƤℚⱤ")])
+		self.assertIn("ltag", font.tables)
+		self.assertEqual(font["ltag"].tags, ["la"])
+
+	def test_addMultilingualName_legacyMacEncodingButNoCodec(self):
+		# Windows has no language code for “Azeri written in the
+		# Arabic script” (az-Arab); MacOS would have a code (50);
+		# but we cannot encode the name into the legacy encoding
+		# because we have no codec for MacArabic in fonttools.
+		# In this case, we expect that the name gets encoded as
+		# Unicode name (platformID 0) with the language tag being
+		# added to the 'ltag' table.
+		font = FakeFont(glyphs=[".notdef", "A"])
+		nameTable = font.tables['name'] = newTable("name")
+		with CapturingLogHandler(log, "WARNING") as captor:
+			nameTable.addMultilingualName({"az-Arab": "آذربايجان ديلی"},
+			                              ttFont=font)
+		captor.assertRegex("cannot add Windows name in language az-Arab")
+		self.assertEqual(names(nameTable), [(256, 0, 4, 0, "آذربايجان ديلی")])
+		self.assertIn("ltag", font.tables)
+		self.assertEqual(font["ltag"].tags, ["az-Arab"])
+
+	def test_addMultilingualName_noTTFont(self):
+		# If the ttFont argument is not passed, the implementation
+		# should add whatever names it can, but it should not crash
+		# just because it cannot build an ltag table.
+		nameTable = newTable("name")
+		with CapturingLogHandler(log, "WARNING") as captor:
+			nameTable.addMultilingualName({"en": "A", "la": "ⱾƤℚⱤ"})
+		captor.assertRegex("cannot store language la into 'ltag' table")
+
+	def test_decompile_badOffset(self):
+                # https://github.com/behdad/fonttools/issues/525
+		table = table__n_a_m_e()
+		badRecord = {
+			"platformID": 1,
+			"platEncID": 3,
+			"langID": 7,
+			"nameID": 1,
+			"length": 3,
+			"offset": 8765  # out of range
+		}
+		data = bytesjoin([
+                        struct.pack(tostr(">HHH"), 1, 1, 6 + nameRecordSize),
+                        sstruct.pack(nameRecordFormat, badRecord)])
+		table.decompile(data, ttFont=None)
+		self.assertEqual(table.names, [])
+
+
+class NameRecordTest(unittest.TestCase):
+
+	def test_toUnicode_utf16be(self):
+		name = makeName("Foo Bold", 111, 0, 2, 7)
+		self.assertEqual("utf_16_be", name.getEncoding())
+		self.assertEqual("Foo Bold", name.toUnicode())
+
+	def test_toUnicode_macroman(self):
+		name = makeName("Foo Italic", 222, 1, 0, 7)  # MacRoman
+		self.assertEqual("mac_roman", name.getEncoding())
+		self.assertEqual("Foo Italic", name.toUnicode())
+
+	def test_toUnicode_macromanian(self):
+		name = makeName(b"Foo Italic\xfb", 222, 1, 0, 37)  # Mac Romanian
+		self.assertEqual("mac_romanian", name.getEncoding())
+		self.assertEqual("Foo Italic"+unichr(0x02DA), name.toUnicode())
+
+	def test_toUnicode_UnicodeDecodeError(self):
+		name = makeName(b"\1", 111, 0, 2, 7)
+		self.assertEqual("utf_16_be", name.getEncoding())
+		self.assertRaises(UnicodeDecodeError, name.toUnicode)
+
+	def toXML(self, name):
+		writer = XMLWriter(BytesIO())
+		name.toXML(writer, ttFont=None)
+		xml = writer.file.getvalue().decode("utf_8").strip()
+		return xml.split(writer.newlinestr.decode("utf_8"))[1:]
+
+	def test_toXML_utf16be(self):
+		name = makeName("Foo Bold", 111, 0, 2, 7)
+		self.assertEqual([
+                    '<namerecord nameID="111" platformID="0" platEncID="2" langID="0x7">',
+                    '  Foo Bold',
+                    '</namerecord>'
+		], self.toXML(name))
+
+	def test_toXML_utf16be_odd_length1(self):
+		name = makeName(b"\0F\0o\0o\0", 111, 0, 2, 7)
+		self.assertEqual([
+                    '<namerecord nameID="111" platformID="0" platEncID="2" langID="0x7">',
+                    '  Foo',
+                    '</namerecord>'
+		], self.toXML(name))
+
+	def test_toXML_utf16be_odd_length2(self):
+		name = makeName(b"\0Fooz", 111, 0, 2, 7)
+		self.assertEqual([
+                    '<namerecord nameID="111" platformID="0" platEncID="2" langID="0x7">',
+                    '  Fooz',
+                    '</namerecord>'
+		], self.toXML(name))
+
+	def test_toXML_utf16be_double_encoded(self):
+		name = makeName(b"\0\0\0F\0\0\0o", 111, 0, 2, 7)
+		self.assertEqual([
+                    '<namerecord nameID="111" platformID="0" platEncID="2" langID="0x7">',
+                    '  Fo',
+                    '</namerecord>'
+		], self.toXML(name))
+
+	def test_toXML_macroman(self):
+		name = makeName("Foo Italic", 222, 1, 0, 7)  # MacRoman
+		self.assertEqual([
+                    '<namerecord nameID="222" platformID="1" platEncID="0" langID="0x7" unicode="True">',
+                    '  Foo Italic',
+                    '</namerecord>'
+		], self.toXML(name))
+
+	def test_toXML_macroman_actual_utf16be(self):
+		name = makeName("\0F\0o\0o", 222, 1, 0, 7)
+		self.assertEqual([
+                    '<namerecord nameID="222" platformID="1" platEncID="0" langID="0x7" unicode="True">',
+                    '  Foo',
+                    '</namerecord>'
+		], self.toXML(name))
+
+	def test_toXML_unknownPlatEncID_nonASCII(self):
+		name = makeName(b"B\x8arli", 333, 1, 9876, 7) # Unknown Mac encodingID
+		self.assertEqual([
+                    '<namerecord nameID="333" platformID="1" platEncID="9876" langID="0x7" unicode="False">',
+                    '  B&#138;rli',
+                    '</namerecord>'
+		], self.toXML(name))
+
+	def test_toXML_unknownPlatEncID_ASCII(self):
+		name = makeName(b"Barli", 333, 1, 9876, 7) # Unknown Mac encodingID
+		self.assertEqual([
+                    '<namerecord nameID="333" platformID="1" platEncID="9876" langID="0x7" unicode="True">',
+                    '  Barli',
+                    '</namerecord>'
+		], self.toXML(name))
+
+	def test_encoding_macroman_misc(self):
+		name = makeName('', 123, 1, 0, 17) # Mac Turkish
+		self.assertEqual(name.getEncoding(), "mac_turkish")
+		name.langID = 37
+		self.assertEqual(name.getEncoding(), "mac_romanian")
+		name.langID = 45 # Other
+		self.assertEqual(name.getEncoding(), "mac_roman")
+
+	def test_extended_mac_encodings(self):
+		name = makeName(b'\xfe', 123, 1, 1, 0) # Mac Japanese
+		self.assertEqual(name.toUnicode(), unichr(0x2122))
+
+	def test_extended_unknown(self):
+		name = makeName(b'\xfe', 123, 10, 11, 12)
+		self.assertEqual(name.getEncoding(), "ascii")
+		self.assertEqual(name.getEncoding(None), None)
+		self.assertEqual(name.getEncoding(default=None), None)
+
+if __name__ == "__main__":
+	import sys
+	sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/_o_p_b_d_test.py b/Tests/ttLib/tables/_o_p_b_d_test.py
new file mode 100644
index 0000000..131d3f9
--- /dev/null
+++ b/Tests/ttLib/tables/_o_p_b_d_test.py
@@ -0,0 +1,183 @@
+from __future__ import print_function, division, absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.misc.testTools import FakeFont, getXML, parseXML
+from fontTools.misc.textTools import deHexStr, hexStr
+from fontTools.ttLib import newTable
+import unittest
+
+
+# Example: Format 0 Optical Bounds Table
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6opbd.html
+OPBD_FORMAT_0_DATA = deHexStr(
+    '0001 0000 0000 '       #  0: Version=1.0, Format=0
+    '0006 0004 0002 '       #  6: LookupFormat=6, UnitSize=4, NUnits=2
+    '0008 0001 0000 '       # 12: SearchRange=8, EntrySelector=1, RangeShift=0
+    '000A 001E '            # 18: Glyph=10(=C), OffsetOfOpticalBoundsDeltas=30
+    '002B 0026 '            # 22: Glyph=43(=A), OffsetOfOpticalBoundsDeltas=38
+    'FFFF 0000 '            # 26: Glyph=<end>, OffsetOfOpticalBoundsDeltas=0
+    'FFCE 0005 0037 FFFB '  # 30: Bounds[C].Left=-50 .Top=5 .Right=55 .Bottom=-5
+    'FFF6 000F 0000 0000 '  # 38: Bounds[A].Left=-10 .Top=15 .Right=0 .Bottom=0
+)                           # 46: <end>
+assert(len(OPBD_FORMAT_0_DATA) == 46)
+
+
+OPBD_FORMAT_0_XML = [
+    '<Version value="0x00010000"/>',
+    '<OpticalBounds Format="0">',
+    '  <OpticalBoundsDeltas>',
+    '    <Lookup glyph="A">',
+    '      <Left value="-10"/>',
+    '      <Top value="15"/>',
+    '      <Right value="0"/>',
+    '      <Bottom value="0"/>',
+    '    </Lookup>',
+    '    <Lookup glyph="C">',
+    '      <Left value="-50"/>',
+    '      <Top value="5"/>',
+    '      <Right value="55"/>',
+    '      <Bottom value="-5"/>',
+    '    </Lookup>',
+    '  </OpticalBoundsDeltas>',
+    '</OpticalBounds>',
+]
+
+
+# Example: Format 1 Optical Bounds Table
+# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6opbd.html
+OPBD_FORMAT_1_DATA = deHexStr(
+    '0001 0000 0001 '       #  0: Version=1.0, Format=1
+    '0006 0004 0002 '       #  6: LookupFormat=6, UnitSize=4, NUnits=2
+    '0008 0001 0000 '       # 12: SearchRange=8, EntrySelector=1, RangeShift=0
+    '000A 001E '            # 18: Glyph=10(=C), OffsetOfOpticalBoundsPoints=30
+    '002B 0026 '            # 22: Glyph=43(=A), OffsetOfOpticalBoundsPoints=38
+    'FFFF 0000 '            # 26: Glyph=<end>, OffsetOfOpticalBoundsPoints=0
+    '0024 0025 0026 0027 '  # 30: Bounds[C].Left=36 .Top=37 .Right=38 .Bottom=39
+    '0020 0029 FFFF FFFF '  # 38: Bounds[A].Left=32 .Top=41 .Right=-1 .Bottom=-1
+)                           # 46: <end>
+assert(len(OPBD_FORMAT_1_DATA) == 46)
+
+
+OPBD_FORMAT_1_XML = [
+    '<Version value="0x00010000"/>',
+    '<OpticalBounds Format="1">',
+    '  <OpticalBoundsPoints>',
+    '    <Lookup glyph="A">',
+    '      <Left value="32"/>',
+    '      <Top value="41"/>',
+    '      <Right value="-1"/>',
+    '      <Bottom value="-1"/>',
+    '    </Lookup>',
+    '    <Lookup glyph="C">',
+    '      <Left value="36"/>',
+    '      <Top value="37"/>',
+    '      <Right value="38"/>',
+    '      <Bottom value="39"/>',
+    '    </Lookup>',
+    '  </OpticalBoundsPoints>',
+    '</OpticalBounds>',
+]
+
+
+# This is the content of the Optical Bounds table in AppleChancery.ttf,
+# font version 8.0d1e1 of 2013-02-06. An early version of fontTools
+# was crashing when trying to decompile this table.
+# https://github.com/fonttools/fonttools/issues/1031
+OPBD_APPLE_CHANCERY_DATA = deHexStr(
+    '0001 0000 0000 '  #   0: Version=1.0, Format=0
+    '0004 0006 0011 '  #   6: LookupFormat=4, UnitSize=6, NUnits=17
+    '0060 0004 0006 '  #  12: SearchRange=96, EntrySelector=4, RangeShift=6
+    '017d 017d 0072 '  #  18: Seg[0].LastGlyph=381, FirstGlyph=381, Off=114(+6)
+    '0183 0180 0074 '  #  24: Seg[1].LastGlyph=387, FirstGlyph=384, Off=116(+6)
+    '0186 0185 007c '  #  30: Seg[2].LastGlyph=390, FirstGlyph=389, Off=124(+6)
+    '018f 018b 0080 '  #  36: Seg[3].LastGlyph=399, FirstGlyph=395, Off=128(+6)
+    '01a0 0196 008a '  #  42: Seg[4].LastGlyph=416, FirstGlyph=406, Off=138(+6)
+    '01a5 01a3 00a0 '  #  48: Seg[5].LastGlyph=421, FirstGlyph=419, Off=160(+6)
+    '01aa 01aa 00a6 '  #  54: Seg[6].LastGlyph=426, FirstGlyph=426, Off=166(+6)
+    '01ac 01ac 00a8 '  #  60: Seg[7].LastGlyph=428, FirstGlyph=428, Off=168(+6)
+    '01fb 01f1 00aa '  #  66: Seg[8].LastGlyph=507, FirstGlyph=497, Off=170(+6)
+    '0214 0209 00c0 '  #  72: Seg[9].LastGlyph=532, FirstGlyph=521, Off=192(+6)
+    '021d 0216 00d8 '  #  78: Seg[10].LastGlyph=541, FirstGlyph=534, Off=216(+6)
+    '0222 0220 00e8 '  #  84: Seg[11].LastGlyph=546, FirstGlyph=544, Off=232(+6)
+    '0227 0225 00ee '  #  90: Seg[12].LastGlyph=551, FirstGlyph=549, Off=238(+6)
+    '0229 0229 00f4 '  #  96: Seg[13].LastGlyph=553, FirstGlyph=553, Off=244(+6)
+    '023b 023b 00f6 '  # 102: Seg[14].LastGlyph=571, FirstGlyph=571, Off=246(+6)
+    '023e 023e 00f8 '  # 108: Seg[15].LastGlyph=574, FirstGlyph=574, Off=248(+6)
+    'ffff ffff 00fa '  # 114: Seg[16]=<end>
+    '0100 0108 0110 0118 0120 0128 0130 0138 0140 0148 0150 0158 '
+    '0160 0168 0170 0178 0180 0188 0190 0198 01a0 01a8 01b0 01b8 '
+    '01c0 01c8 01d0 01d8 01e0 01e8 01f0 01f8 0200 0208 0210 0218 '
+    '0220 0228 0230 0238 0240 0248 0250 0258 0260 0268 0270 0278 '
+    '0280 0288 0290 0298 02a0 02a8 02b0 02b8 02c0 02c8 02d0 02d8 '
+    '02e0 02e8 02f0 02f8 0300 0308 0310 0318 fd98 0000 0000 0000 '
+    'fdbc 0000 0000 0000 fdbc 0000 0000 0000 fdbf 0000 0000 0000 '
+    'fdbc 0000 0000 0000 fd98 0000 0000 0000 fda9 0000 0000 0000 '
+    'fd98 0000 0000 0000 fd98 0000 0000 0000 fd98 0000 0000 0000 '
+    '0000 0000 0205 0000 0000 0000 0205 0000 0000 0000 02a4 0000 '
+    '0000 0000 027e 0000 0000 0000 02f4 0000 0000 0000 02a4 0000 '
+    '0000 0000 0365 0000 0000 0000 0291 0000 0000 0000 0291 0000 '
+    '0000 0000 026a 0000 0000 0000 02b8 0000 0000 0000 02cb 0000 '
+    '0000 0000 02a4 0000 0000 0000 01a9 0000 0000 0000 0244 0000 '
+    '0000 0000 02a4 0000 0000 0000 02cb 0000 0000 0000 0244 0000 '
+    '0000 0000 0307 0000 0000 0000 0307 0000 0000 0000 037f 0000 '
+    '0000 0000 0307 0000 0000 0000 0307 0000 0000 0000 0307 0000 '
+    '0000 0000 0307 0000 0000 0000 0307 0000 0000 0000 03e3 0000 '
+    '0000 0000 030c 0000 0000 0000 0307 0000 fe30 0000 0000 0000 '
+    'fe7e 0000 0000 0000 fe91 0000 0000 0000 fe6a 0000 0000 0000 '
+    'fe6a 0000 0000 0000 fecb 0000 0000 0000 fe6a 0000 0000 0000 '
+    'fe7e 0000 0000 0000 fea4 0000 0000 0000 fe7e 0000 0000 0000 '
+    'fe44 0000 0000 0000 fea4 0000 0000 0000 feb8 0000 0000 0000 '
+    'fe7e 0000 0000 0000 fe5e 0000 0000 0000 fe37 0000 0000 0000 '
+    'fe37 0000 0000 0000 fcbd 0000 0000 0000 fd84 0000 0000 0000 '
+    'fd98 0000 0000 0000 fd82 0000 0000 0000 fcbd 0000 0000 0000 '
+    'fd84 0000 0000 0000 fcbd 0000 0000 0000 fcbd 0000 0000 0000 '
+    'fe72 0000 0000 0000 ff9d 0000 0000 0000 0000 0000 032f 0000 '
+    '0000 0000 03ba 0000 '
+)
+assert len(OPBD_APPLE_CHANCERY_DATA) == 800
+
+
+class OPBDTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.maxDiff = None
+        glyphs = ['.notdef'] + ['X.alt%d' for g in range(1, 50)]
+        glyphs[10] = 'C'
+        glyphs[43] = 'A'
+        cls.font = FakeFont(glyphs)
+
+    def test_decompile_toXML_format0(self):
+        table = newTable('opbd')
+        table.decompile(OPBD_FORMAT_0_DATA, self.font)
+        self.assertEqual(getXML(table.toXML), OPBD_FORMAT_0_XML)
+
+    def test_compile_fromXML_format0(self):
+        table = newTable('opbd')
+        for name, attrs, content in parseXML(OPBD_FORMAT_0_XML):
+            table.fromXML(name, attrs, content, font=self.font)
+        self.assertEqual(hexStr(table.compile(self.font)),
+                         hexStr(OPBD_FORMAT_0_DATA))
+
+    def test_decompile_toXML_format1(self):
+        table = newTable('opbd')
+        table.decompile(OPBD_FORMAT_1_DATA, self.font)
+        self.assertEqual(getXML(table.toXML), OPBD_FORMAT_1_XML)
+
+    def test_compile_fromXML_format1(self):
+        table = newTable('opbd')
+        for name, attrs, content in parseXML(OPBD_FORMAT_1_XML):
+            table.fromXML(name, attrs, content, font=self.font)
+        self.assertEqual(hexStr(table.compile(self.font)),
+                         hexStr(OPBD_FORMAT_1_DATA))
+
+    def test_decompile_AppleChancery(self):
+        # Make sure we do not crash when decompiling the 'opbd' table of
+        # AppleChancery.ttf. https://github.com/fonttools/fonttools/issues/1031
+        table = newTable('opbd')
+        table.decompile(OPBD_APPLE_CHANCERY_DATA, self.font)
+        self.assertIn('<OpticalBounds Format="0">', getXML(table.toXML))
+
+
+if __name__ == '__main__':
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/_p_r_o_p_test.py b/Tests/ttLib/tables/_p_r_o_p_test.py
new file mode 100644
index 0000000..edac915
--- /dev/null
+++ b/Tests/ttLib/tables/_p_r_o_p_test.py
@@ -0,0 +1,84 @@
+from __future__ import print_function, division, absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.misc.testTools import FakeFont, getXML, parseXML
+from fontTools.misc.textTools import deHexStr, hexStr
+from fontTools.ttLib import newTable
+import unittest
+
+
+PROP_FORMAT_0_DATA = deHexStr(
+    '0001 0000 0000 '  #  0: Version=1.0, Format=0
+    '0005 '            #  6: DefaultProperties=European number terminator
+)                      #  8: <end>
+assert(len(PROP_FORMAT_0_DATA) == 8)
+
+
+PROP_FORMAT_0_XML = [
+    '<Version value="1.0"/>',
+    '<GlyphProperties Format="0">',
+    '  <DefaultProperties value="5"/>',
+    '</GlyphProperties>',
+]
+
+
+PROP_FORMAT_1_DATA = deHexStr(
+    '0003 0000 0001 '  #  0: Version=3.0, Format=1
+    '0000 '            #  6: DefaultProperties=left-to-right; non-whitespace
+    '0008 0003 0004 '  #  8: LookupFormat=8, FirstGlyph=3, GlyphCount=4
+    '000B '            # 14: Properties[C]=other neutral
+    '000A '            # 16: Properties[D]=whitespace
+    '600B '            # 18: Properties[E]=other neutral; hanging punct
+    '0005 '            # 20: Properties[F]=European number terminator
+)                      # 22: <end>
+assert(len(PROP_FORMAT_1_DATA) == 22)
+
+
+PROP_FORMAT_1_XML = [
+    '<Version value="3.0"/>',
+    '<GlyphProperties Format="1">',
+    '  <DefaultProperties value="0"/>',
+    '  <Properties>',
+    '    <Lookup glyph="C" value="11"/>',
+    '    <Lookup glyph="D" value="10"/>',
+    '    <Lookup glyph="E" value="24587"/>',
+    '    <Lookup glyph="F" value="5"/>',
+    '  </Properties>',
+    '</GlyphProperties>',
+]
+
+
+class PROPTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.maxDiff = None
+        cls.font = FakeFont(['.notdef', 'A', 'B', 'C', 'D', 'E', 'F', 'G'])
+
+    def test_decompile_toXML_format0(self):
+        table = newTable('prop')
+        table.decompile(PROP_FORMAT_0_DATA, self.font)
+        self.assertEqual(getXML(table.toXML), PROP_FORMAT_0_XML)
+
+    def test_compile_fromXML_format0(self):
+        table = newTable('prop')
+        for name, attrs, content in parseXML(PROP_FORMAT_0_XML):
+            table.fromXML(name, attrs, content, font=self.font)
+        self.assertEqual(hexStr(table.compile(self.font)),
+                         hexStr(PROP_FORMAT_0_DATA))
+
+    def test_decompile_toXML_format1(self):
+        table = newTable('prop')
+        table.decompile(PROP_FORMAT_1_DATA, self.font)
+        self.assertEqual(getXML(table.toXML), PROP_FORMAT_1_XML)
+
+    def test_compile_fromXML_format1(self):
+        table = newTable('prop')
+        for name, attrs, content in parseXML(PROP_FORMAT_1_XML):
+            table.fromXML(name, attrs, content, font=self.font)
+        self.assertEqual(hexStr(table.compile(self.font)),
+                         hexStr(PROP_FORMAT_1_DATA))
+
+
+if __name__ == '__main__':
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/_t_r_a_k_test.py b/Tests/ttLib/tables/_t_r_a_k_test.py
new file mode 100644
index 0000000..c223c81
--- /dev/null
+++ b/Tests/ttLib/tables/_t_r_a_k_test.py
@@ -0,0 +1,341 @@
+from __future__ import print_function, division, absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.misc.testTools import parseXML, getXML
+from fontTools.misc.textTools import deHexStr
+from fontTools.ttLib import TTFont, TTLibError
+from fontTools.ttLib.tables._t_r_a_k import *
+from fontTools.ttLib.tables._n_a_m_e import table__n_a_m_e, NameRecord
+import unittest
+
+
+# /Library/Fonts/Osaka.ttf from OSX has trak table with both horiz and vertData
+OSAKA_TRAK_TABLE_DATA = deHexStr(
+	'00 01 00 00 00 00 00 0c 00 40 00 00 00 03 00 02 00 00 00 2c ff ff '
+	'00 00 01 06 00 34 00 00 00 00 01 07 00 38 00 01 00 00 01 08 00 3c '
+	'00 0c 00 00 00 18 00 00 ff f4 ff f4 00 00 00 00 00 0c 00 0c 00 03 '
+	'00 02 00 00 00 60 ff ff 00 00 01 09 00 68 00 00 00 00 01 0a 00 6c '
+	'00 01 00 00 01 0b 00 70 00 0c 00 00 00 18 00 00 ff f4 ff f4 00 00 '
+	'00 00 00 0c 00 0c')
+
+# decompiled horizData and vertData entries from Osaka.ttf 
+OSAKA_HORIZ_TRACK_ENTRIES = {
+	-1.0: TrackTableEntry({24.0: -12, 12.0: -12}, nameIndex=262),
+	 0.0: TrackTableEntry({24.0: 0, 12.0: 0}, nameIndex=263),
+	 1.0: TrackTableEntry({24.0: 12, 12.0: 12}, nameIndex=264)
+	}
+
+OSAKA_VERT_TRACK_ENTRIES = {
+	-1.0: TrackTableEntry({24.0: -12, 12.0: -12}, nameIndex=265),
+	 0.0: TrackTableEntry({24.0: 0, 12.0: 0}, nameIndex=266),
+	 1.0: TrackTableEntry({24.0: 12, 12.0: 12}, nameIndex=267)
+	}
+
+OSAKA_TRAK_TABLE_XML = [
+	'<version value="1.0"/>',
+	'<format value="0"/>',
+	'<horizData>',
+	'  <!-- nTracks=3, nSizes=2 -->',
+	'  <trackEntry value="-1.0" nameIndex="262">',
+	'    <!-- Tight -->',
+	'    <track size="12.0" value="-12"/>',
+	'    <track size="24.0" value="-12"/>',
+	'  </trackEntry>',
+	'  <trackEntry value="0.0" nameIndex="263">',
+	'    <!-- Normal -->',
+	'    <track size="12.0" value="0"/>',
+	'    <track size="24.0" value="0"/>',
+	'  </trackEntry>',
+	'  <trackEntry value="1.0" nameIndex="264">',
+	'    <!-- Loose -->',
+	'    <track size="12.0" value="12"/>',
+	'    <track size="24.0" value="12"/>',
+	'  </trackEntry>',
+	'</horizData>',
+	'<vertData>',
+	'  <!-- nTracks=3, nSizes=2 -->',
+	'  <trackEntry value="-1.0" nameIndex="265">',
+	'    <!-- Tight -->',
+	'    <track size="12.0" value="-12"/>',
+	'    <track size="24.0" value="-12"/>',
+	'  </trackEntry>',
+	'  <trackEntry value="0.0" nameIndex="266">',
+	'    <!-- Normal -->',
+	'    <track size="12.0" value="0"/>',
+	'    <track size="24.0" value="0"/>',
+	'  </trackEntry>',
+	'  <trackEntry value="1.0" nameIndex="267">',
+	'    <!-- Loose -->',
+	'    <track size="12.0" value="12"/>',
+	'    <track size="24.0" value="12"/>',
+	'  </trackEntry>',
+	'</vertData>',
+]
+
+# made-up table containing only vertData (no horizData)
+OSAKA_VERT_ONLY_TRAK_TABLE_DATA = deHexStr(
+	'00 01 00 00 00 00 00 00 00 0c 00 00 00 03 00 02 00 00 00 2c ff ff '
+	'00 00 01 09 00 34 00 00 00 00 01 0a 00 38 00 01 00 00 01 0b 00 3c '
+	'00 0c 00 00 00 18 00 00 ff f4 ff f4 00 00 00 00 00 0c 00 0c')
+
+OSAKA_VERT_ONLY_TRAK_TABLE_XML = [
+	'<version value="1.0"/>',
+	'<format value="0"/>',
+	'<horizData>',
+	'  <!-- nTracks=0, nSizes=0 -->',
+	'</horizData>',
+	'<vertData>',
+	'  <!-- nTracks=3, nSizes=2 -->',
+	'  <trackEntry value="-1.0" nameIndex="265">',
+	'    <!-- Tight -->',
+	'    <track size="12.0" value="-12"/>',
+	'    <track size="24.0" value="-12"/>',
+	'  </trackEntry>',
+	'  <trackEntry value="0.0" nameIndex="266">',
+	'    <!-- Normal -->',
+	'    <track size="12.0" value="0"/>',
+	'    <track size="24.0" value="0"/>',
+	'  </trackEntry>',
+	'  <trackEntry value="1.0" nameIndex="267">',
+	'    <!-- Loose -->',
+	'    <track size="12.0" value="12"/>',
+	'    <track size="24.0" value="12"/>',
+	'  </trackEntry>',
+	'</vertData>',
+]
+
+
+# also /Library/Fonts/Skia.ttf contains a trak table with horizData
+SKIA_TRAK_TABLE_DATA = deHexStr(
+	'00 01 00 00 00 00 00 0c 00 00 00 00 00 03 00 05 00 00 00 2c ff ff '
+	'00 00 01 13 00 40 00 00 00 00 01 2f 00 4a 00 01 00 00 01 14 00 54 '
+	'00 09 00 00 00 0a 00 00 00 0c 00 00 00 12 00 00 00 13 00 00 ff f6 '
+	'ff e2 ff c4 ff c1 ff c1 00 0f 00 00 ff fb ff e7 ff e7 00 8c 00 82 '
+	'00 7d 00 73 00 73')
+
+SKIA_TRACK_ENTRIES = {
+	-1.0: TrackTableEntry(
+		{9.0: -10, 10.0: -30, 19.0: -63, 12.0: -60, 18.0: -63}, nameIndex=275),
+	 0.0: TrackTableEntry(
+	 	{9.0: 15, 10.0: 0, 19.0: -25, 12.0: -5, 18.0: -25}, nameIndex=303),
+	 1.0: TrackTableEntry(
+	 	{9.0: 140, 10.0: 130, 19.0: 115, 12.0: 125, 18.0: 115}, nameIndex=276)
+	}
+
+SKIA_TRAK_TABLE_XML = [
+	'<version value="1.0"/>',
+	'<format value="0"/>',
+	'<horizData>',
+	'  <!-- nTracks=3, nSizes=5 -->',
+	'  <trackEntry value="-1.0" nameIndex="275">',
+	'    <!-- Tight -->',
+	'    <track size="9.0" value="-10"/>',
+	'    <track size="10.0" value="-30"/>',
+	'    <track size="12.0" value="-60"/>',
+	'    <track size="18.0" value="-63"/>',
+	'    <track size="19.0" value="-63"/>',
+	'  </trackEntry>',
+	'  <trackEntry value="0.0" nameIndex="303">',
+	'    <!-- Normal -->',
+	'    <track size="9.0" value="15"/>',
+	'    <track size="10.0" value="0"/>',
+	'    <track size="12.0" value="-5"/>',
+	'    <track size="18.0" value="-25"/>',
+	'    <track size="19.0" value="-25"/>',
+	'  </trackEntry>',
+	'  <trackEntry value="1.0" nameIndex="276">',
+	'    <!-- Loose -->',
+	'    <track size="9.0" value="140"/>',
+	'    <track size="10.0" value="130"/>',
+	'    <track size="12.0" value="125"/>',
+	'    <track size="18.0" value="115"/>',
+	'    <track size="19.0" value="115"/>',
+	'  </trackEntry>',
+	'</horizData>',
+	'<vertData>',
+	'  <!-- nTracks=0, nSizes=0 -->',
+	'</vertData>',
+]
+
+
+class TrackingTableTest(unittest.TestCase):
+
+	def __init__(self, methodName):
+		unittest.TestCase.__init__(self, methodName)
+		# Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
+		# and fires deprecation warnings if a program uses the old name.
+		if not hasattr(self, "assertRaisesRegex"):
+			self.assertRaisesRegex = self.assertRaisesRegexp
+
+	def setUp(self):
+		table = table__t_r_a_k()
+		table.version = 1.0
+		table.format = 0
+		self.font = {'trak': table}
+
+	def test_compile_horiz(self):
+		table = self.font['trak']
+		table.horizData = TrackData(SKIA_TRACK_ENTRIES)
+		trakData = table.compile(self.font)
+		self.assertEqual(trakData, SKIA_TRAK_TABLE_DATA)
+
+	def test_compile_vert(self):
+		table = self.font['trak']
+		table.vertData = TrackData(OSAKA_VERT_TRACK_ENTRIES)
+		trakData = table.compile(self.font)
+		self.assertEqual(trakData, OSAKA_VERT_ONLY_TRAK_TABLE_DATA)
+
+	def test_compile_horiz_and_vert(self):
+		table = self.font['trak']
+		table.horizData = TrackData(OSAKA_HORIZ_TRACK_ENTRIES)
+		table.vertData = TrackData(OSAKA_VERT_TRACK_ENTRIES)
+		trakData = table.compile(self.font)
+		self.assertEqual(trakData, OSAKA_TRAK_TABLE_DATA)
+
+	def test_compile_longword_aligned(self):
+		table = self.font['trak']
+		# without padding, this 'horizData' would end up 46 byte long
+		table.horizData = TrackData({
+			0.0: TrackTableEntry(nameIndex=256, values={12.0: 0, 24.0: 0, 36.0: 0})
+			})
+		table.vertData = TrackData({
+			0.0: TrackTableEntry(nameIndex=257, values={12.0: 0, 24.0: 0, 36.0: 0})
+			})
+		trakData = table.compile(self.font)
+		self.assertTrue(table.vertOffset % 4 == 0)
+
+	def test_compile_sizes_mismatch(self):
+		table = self.font['trak']
+		table.horizData = TrackData({
+			-1.0: TrackTableEntry(nameIndex=256, values={9.0: -10, 10.0: -30}),
+			 0.0: TrackTableEntry(nameIndex=257, values={8.0: 20, 12.0: 0})
+			})
+		with self.assertRaisesRegex(TTLibError, 'entries must specify the same sizes'):
+			table.compile(self.font)
+
+	def test_decompile_horiz(self):
+		table = self.font['trak']
+		table.decompile(SKIA_TRAK_TABLE_DATA, self.font)
+		self.assertEqual(table.horizData, SKIA_TRACK_ENTRIES)
+		self.assertEqual(table.vertData, TrackData())
+
+	def test_decompile_vert(self):
+		table = self.font['trak']
+		table.decompile(OSAKA_VERT_ONLY_TRAK_TABLE_DATA, self.font)
+		self.assertEqual(table.horizData, TrackData())
+		self.assertEqual(table.vertData, OSAKA_VERT_TRACK_ENTRIES)
+
+	def test_decompile_horiz_and_vert(self):
+		table = self.font['trak']
+		table.decompile(OSAKA_TRAK_TABLE_DATA, self.font)
+		self.assertEqual(table.horizData, OSAKA_HORIZ_TRACK_ENTRIES)
+		self.assertEqual(table.vertData, OSAKA_VERT_TRACK_ENTRIES)
+
+	def test_roundtrip_decompile_compile(self):
+		for trakData in (
+				OSAKA_TRAK_TABLE_DATA,
+				OSAKA_VERT_ONLY_TRAK_TABLE_DATA,
+				SKIA_TRAK_TABLE_DATA):
+			table = table__t_r_a_k()
+			table.decompile(trakData, ttFont=None)
+			newTrakData = table.compile(ttFont=None)
+			self.assertEqual(trakData, newTrakData)
+
+	def test_fromXML_horiz(self):
+		table = self.font['trak']
+		for name, attrs, content in parseXML(SKIA_TRAK_TABLE_XML):
+			table.fromXML(name, attrs, content, self.font)
+		self.assertEqual(table.version, 1.0)
+		self.assertEqual(table.format, 0)
+		self.assertEqual(table.horizData, SKIA_TRACK_ENTRIES)
+		self.assertEqual(table.vertData, TrackData())
+
+	def test_fromXML_horiz_and_vert(self):
+		table = self.font['trak']
+		for name, attrs, content in parseXML(OSAKA_TRAK_TABLE_XML):
+			table.fromXML(name, attrs, content, self.font)
+		self.assertEqual(table.version, 1.0)
+		self.assertEqual(table.format, 0)
+		self.assertEqual(table.horizData, OSAKA_HORIZ_TRACK_ENTRIES)
+		self.assertEqual(table.vertData, OSAKA_VERT_TRACK_ENTRIES)
+
+	def test_fromXML_vert(self):
+		table = self.font['trak']
+		for name, attrs, content in parseXML(OSAKA_VERT_ONLY_TRAK_TABLE_XML):
+			table.fromXML(name, attrs, content, self.font)
+		self.assertEqual(table.version, 1.0)
+		self.assertEqual(table.format, 0)
+		self.assertEqual(table.horizData, TrackData())
+		self.assertEqual(table.vertData, OSAKA_VERT_TRACK_ENTRIES)
+
+	def test_toXML_horiz(self):
+		table = self.font['trak']
+		table.horizData = TrackData(SKIA_TRACK_ENTRIES)
+		add_name(self.font, 'Tight', nameID=275)
+		add_name(self.font, 'Normal', nameID=303)
+		add_name(self.font, 'Loose', nameID=276)
+		self.assertEqual(
+			SKIA_TRAK_TABLE_XML,
+			getXML(table.toXML, self.font))
+
+	def test_toXML_horiz_and_vert(self):
+		table = self.font['trak']
+		table.horizData = TrackData(OSAKA_HORIZ_TRACK_ENTRIES)
+		table.vertData = TrackData(OSAKA_VERT_TRACK_ENTRIES)
+		add_name(self.font, 'Tight', nameID=262)
+		add_name(self.font, 'Normal', nameID=263)
+		add_name(self.font, 'Loose', nameID=264)
+		add_name(self.font, 'Tight', nameID=265)
+		add_name(self.font, 'Normal', nameID=266)
+		add_name(self.font, 'Loose', nameID=267)
+		self.assertEqual(
+			OSAKA_TRAK_TABLE_XML,
+			getXML(table.toXML, self.font))
+
+	def test_toXML_vert(self):
+		table = self.font['trak']
+		table.vertData = TrackData(OSAKA_VERT_TRACK_ENTRIES)
+		add_name(self.font, 'Tight', nameID=265)
+		add_name(self.font, 'Normal', nameID=266)
+		add_name(self.font, 'Loose', nameID=267)
+		self.assertEqual(
+			OSAKA_VERT_ONLY_TRAK_TABLE_XML,
+			getXML(table.toXML, self.font))
+
+	def test_roundtrip_fromXML_toXML(self):
+		font = {}
+		add_name(font, 'Tight', nameID=275)
+		add_name(font, 'Normal', nameID=303)
+		add_name(font, 'Loose', nameID=276)
+		add_name(font, 'Tight', nameID=262)
+		add_name(font, 'Normal', nameID=263)
+		add_name(font, 'Loose', nameID=264)
+		add_name(font, 'Tight', nameID=265)
+		add_name(font, 'Normal', nameID=266)
+		add_name(font, 'Loose', nameID=267)
+		for input_xml in (
+				SKIA_TRAK_TABLE_XML,
+				OSAKA_TRAK_TABLE_XML,
+				OSAKA_VERT_ONLY_TRAK_TABLE_XML):
+			table = table__t_r_a_k()
+			font['trak'] = table
+			for name, attrs, content in parseXML(input_xml):
+				table.fromXML(name, attrs, content, font)
+			output_xml = getXML(table.toXML, font)
+			self.assertEqual(input_xml, output_xml)
+
+
+def add_name(font, string, nameID):
+	nameTable = font.get("name")
+	if nameTable is None:
+		nameTable = font["name"] = table__n_a_m_e()
+		nameTable.names = []
+	namerec = NameRecord()
+	namerec.nameID = nameID
+	namerec.string = string.encode('mac_roman')
+	namerec.platformID, namerec.platEncID, namerec.langID = (1, 0, 0)
+	nameTable.names.append(namerec)
+
+
+if __name__ == "__main__":
+	import sys
+	sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/_v_h_e_a_test.py b/Tests/ttLib/tables/_v_h_e_a_test.py
new file mode 100644
index 0000000..8979e92
--- /dev/null
+++ b/Tests/ttLib/tables/_v_h_e_a_test.py
@@ -0,0 +1,275 @@
+from __future__ import absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.misc.loggingTools import CapturingLogHandler
+from fontTools.misc.testTools import parseXML, getXML
+from fontTools.misc.textTools import deHexStr
+from fontTools.ttLib import TTFont, newTable
+from fontTools.misc.fixedTools import log
+import os
+import unittest
+
+
+CURR_DIR = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
+DATA_DIR = os.path.join(CURR_DIR, 'data')
+
+VHEA_DATA_VERSION_11 = deHexStr(
+    '0001 1000 '  # 1.1   version
+    '01F4 '       # 500   ascent
+    'FE0C '       # -500  descent
+    '0000 '       # 0     lineGap
+    '0BB8 '       # 3000  advanceHeightMax
+    'FC16 '       # -1002 minTopSideBearing
+    'FD5B '       # -677  minBottomSideBearing
+    '0B70 '       # 2928  yMaxExtent
+    '0000 '       # 0     caretSlopeRise
+    '0001 '       # 1     caretSlopeRun
+    '0000 '       # 0     caretOffset
+    '0000 '       # 0     reserved1
+    '0000 '       # 0     reserved2
+    '0000 '       # 0     reserved3
+    '0000 '       # 0     reserved4
+    '0000 '       # 0     metricDataFormat
+    '000C '       # 12    numberOfVMetrics
+)
+
+VHEA_DATA_VERSION_10 = deHexStr('00010000') + VHEA_DATA_VERSION_11[4:]
+
+VHEA_VERSION_11_AS_DICT = {
+    'tableTag': 'vhea',
+    'tableVersion': 0x00011000,
+    'ascent': 500,
+    'descent': -500,
+    'lineGap': 0,
+    'advanceHeightMax': 3000,
+    'minTopSideBearing': -1002,
+    'minBottomSideBearing': -677,
+    'yMaxExtent': 2928,
+    'caretSlopeRise': 0,
+    'caretSlopeRun': 1,
+    'caretOffset': 0,
+    'reserved1': 0,
+    'reserved2': 0,
+    'reserved3': 0,
+    'reserved4': 0,
+    'metricDataFormat': 0,
+    'numberOfVMetrics': 12,
+}
+
+VHEA_VERSION_10_AS_DICT = dict(VHEA_VERSION_11_AS_DICT)
+VHEA_VERSION_10_AS_DICT['tableVersion'] = 0x00010000
+
+VHEA_XML_VERSION_11 = [
+    '<tableVersion value="0x00011000"/>',
+    '<ascent value="500"/>',
+    '<descent value="-500"/>',
+    '<lineGap value="0"/>',
+    '<advanceHeightMax value="3000"/>',
+    '<minTopSideBearing value="-1002"/>',
+    '<minBottomSideBearing value="-677"/>',
+    '<yMaxExtent value="2928"/>',
+    '<caretSlopeRise value="0"/>',
+    '<caretSlopeRun value="1"/>',
+    '<caretOffset value="0"/>',
+    '<reserved1 value="0"/>',
+    '<reserved2 value="0"/>',
+    '<reserved3 value="0"/>',
+    '<reserved4 value="0"/>',
+    '<metricDataFormat value="0"/>',
+    '<numberOfVMetrics value="12"/>',
+]
+
+VHEA_XML_VERSION_11_AS_FLOAT = [
+    '<tableVersion value="1.0625"/>',
+] + VHEA_XML_VERSION_11[1:]
+
+VHEA_XML_VERSION_10 = [
+    '<tableVersion value="0x00010000"/>',
+] + VHEA_XML_VERSION_11[1:]
+
+VHEA_XML_VERSION_10_AS_FLOAT = [
+    '<tableVersion value="1.0"/>',
+] + VHEA_XML_VERSION_11[1:]
+
+
+class VheaCompileOrToXMLTest(unittest.TestCase):
+
+    def setUp(self):
+        vhea = newTable('vhea')
+        vhea.tableVersion = 0x00010000
+        vhea.ascent = 500
+        vhea.descent = -500
+        vhea.lineGap = 0
+        vhea.advanceHeightMax = 3000
+        vhea.minTopSideBearing = -1002
+        vhea.minBottomSideBearing = -677
+        vhea.yMaxExtent = 2928
+        vhea.caretSlopeRise = 0
+        vhea.caretSlopeRun = 1
+        vhea.caretOffset = 0
+        vhea.metricDataFormat = 0
+        vhea.numberOfVMetrics = 12
+        vhea.reserved1 = vhea.reserved2 = vhea.reserved3 = vhea.reserved4 = 0
+        self.font = TTFont(sfntVersion='OTTO')
+        self.font['vhea'] = vhea
+
+    def test_compile_caretOffset_as_reserved0(self):
+        vhea = self.font['vhea']
+        del vhea.caretOffset
+        vhea.reserved0 = 0
+        self.assertEqual(VHEA_DATA_VERSION_10, vhea.compile(self.font))
+
+    def test_compile_version_10(self):
+        vhea = self.font['vhea']
+        vhea.tableVersion = 0x00010000
+        self.assertEqual(VHEA_DATA_VERSION_10, vhea.compile(self.font))
+
+    def test_compile_version_10_as_float(self):
+        vhea = self.font['vhea']
+        vhea.tableVersion = 1.0
+        with CapturingLogHandler(log, "WARNING") as captor:
+            self.assertEqual(VHEA_DATA_VERSION_10, vhea.compile(self.font))
+        self.assertTrue(
+            len([r for r in captor.records
+                 if "Table version value is a float" in r.msg]) == 1)
+
+    def test_compile_version_11(self):
+        vhea = self.font['vhea']
+        vhea.tableVersion = 0x00011000
+        self.assertEqual(VHEA_DATA_VERSION_11, vhea.compile(self.font))
+
+    def test_compile_version_11_as_float(self):
+        vhea = self.font['vhea']
+        vhea.tableVersion = 1.0625
+        with CapturingLogHandler(log, "WARNING") as captor:
+            self.assertEqual(VHEA_DATA_VERSION_11, vhea.compile(self.font))
+        self.assertTrue(
+            len([r for r in captor.records
+                 if "Table version value is a float" in r.msg]) == 1)
+
+    def test_toXML_caretOffset_as_reserved0(self):
+        vhea = self.font['vhea']
+        del vhea.caretOffset
+        vhea.reserved0 = 0
+        self.assertEqual(getXML(vhea.toXML), VHEA_XML_VERSION_10)
+
+    def test_toXML_version_10(self):
+        vhea = self.font['vhea']
+        self.font['vhea'].tableVersion = 0x00010000
+        self.assertEqual(getXML(vhea.toXML), VHEA_XML_VERSION_10)
+
+    def test_toXML_version_10_as_float(self):
+        vhea = self.font['vhea']
+        vhea.tableVersion = 1.0
+        with CapturingLogHandler(log, "WARNING") as captor:
+            self.assertEqual(getXML(vhea.toXML), VHEA_XML_VERSION_10)
+        self.assertTrue(
+            len([r for r in captor.records
+                 if "Table version value is a float" in r.msg]) == 1)
+
+    def test_toXML_version_11(self):
+        vhea = self.font['vhea']
+        self.font['vhea'].tableVersion = 0x00011000
+        self.assertEqual(getXML(vhea.toXML), VHEA_XML_VERSION_11)
+
+    def test_toXML_version_11_as_float(self):
+        vhea = self.font['vhea']
+        vhea.tableVersion = 1.0625
+        with CapturingLogHandler(log, "WARNING") as captor:
+            self.assertEqual(getXML(vhea.toXML), VHEA_XML_VERSION_11)
+        self.assertTrue(
+            len([r for r in captor.records
+                 if "Table version value is a float" in r.msg]) == 1)
+
+
+class VheaDecompileOrFromXMLTest(unittest.TestCase):
+
+    def setUp(self):
+        vhea = newTable('vhea')
+        self.font = TTFont(sfntVersion='OTTO')
+        self.font['vhea'] = vhea
+
+    def test_decompile_version_10(self):
+        vhea = self.font['vhea']
+        vhea.decompile(VHEA_DATA_VERSION_10, self.font)
+        for key in vhea.__dict__:
+            self.assertEqual(getattr(vhea, key), VHEA_VERSION_10_AS_DICT[key])
+
+    def test_decompile_version_11(self):
+        vhea = self.font['vhea']
+        vhea.decompile(VHEA_DATA_VERSION_11, self.font)
+        for key in vhea.__dict__:
+            self.assertEqual(getattr(vhea, key), VHEA_VERSION_11_AS_DICT[key])
+
+    def test_fromXML_version_10(self):
+        vhea = self.font['vhea']
+        for name, attrs, content in parseXML(VHEA_XML_VERSION_10):
+            vhea.fromXML(name, attrs, content, self.font)
+        for key in vhea.__dict__:
+            self.assertEqual(getattr(vhea, key), VHEA_VERSION_10_AS_DICT[key])
+
+    def test_fromXML_version_10_as_float(self):
+        vhea = self.font['vhea']
+        with CapturingLogHandler(log, "WARNING") as captor:
+            for name, attrs, content in parseXML(VHEA_XML_VERSION_10_AS_FLOAT):
+                vhea.fromXML(name, attrs, content, self.font)
+        self.assertTrue(
+            len([r for r in captor.records
+                 if "Table version value is a float" in r.msg]) == 1)
+        for key in vhea.__dict__:
+            self.assertEqual(getattr(vhea, key), VHEA_VERSION_10_AS_DICT[key])
+
+    def test_fromXML_version_11(self):
+        vhea = self.font['vhea']
+        for name, attrs, content in parseXML(VHEA_XML_VERSION_11):
+            vhea.fromXML(name, attrs, content, self.font)
+        for key in vhea.__dict__:
+            self.assertEqual(getattr(vhea, key), VHEA_VERSION_11_AS_DICT[key])
+
+    def test_fromXML_version_11_as_float(self):
+        vhea = self.font['vhea']
+        with CapturingLogHandler(log, "WARNING") as captor:
+            for name, attrs, content in parseXML(VHEA_XML_VERSION_11_AS_FLOAT):
+                vhea.fromXML(name, attrs, content, self.font)
+        self.assertTrue(
+            len([r for r in captor.records
+                 if "Table version value is a float" in r.msg]) == 1)
+        for key in vhea.__dict__:
+            self.assertEqual(getattr(vhea, key), VHEA_VERSION_11_AS_DICT[key])
+
+
+class VheaRecalcTest(unittest.TestCase):
+
+    def test_recalc_TTF(self):
+        font = TTFont()
+        font.importXML(os.path.join(DATA_DIR, '_v_h_e_a_recalc_TTF.ttx'))
+        vhea = font['vhea']
+        vhea.recalc(font)
+        self.assertEqual(vhea.advanceHeightMax, 900)
+        self.assertEqual(vhea.minTopSideBearing, 200)
+        self.assertEqual(vhea.minBottomSideBearing, 377)
+        self.assertEqual(vhea.yMaxExtent, 312)
+
+    def test_recalc_OTF(self):
+        font = TTFont()
+        font.importXML(os.path.join(DATA_DIR, '_v_h_e_a_recalc_OTF.ttx'))
+        vhea = font['vhea']
+        vhea.recalc(font)
+        self.assertEqual(vhea.advanceHeightMax, 900)
+        self.assertEqual(vhea.minTopSideBearing, 200)
+        self.assertEqual(vhea.minBottomSideBearing, 377)
+        self.assertEqual(vhea.yMaxExtent, 312)
+
+    def test_recalc_empty(self):
+        font = TTFont()
+        font.importXML(os.path.join(DATA_DIR, '_v_h_e_a_recalc_empty.ttx'))
+        vhea = font['vhea']
+        vhea.recalc(font)
+        self.assertEqual(vhea.advanceHeightMax, 900)
+        self.assertEqual(vhea.minTopSideBearing, 0)
+        self.assertEqual(vhea.minBottomSideBearing, 0)
+        self.assertEqual(vhea.yMaxExtent, 0)
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/_v_m_t_x_test.py b/Tests/ttLib/tables/_v_m_t_x_test.py
new file mode 100644
index 0000000..f55dbec
--- /dev/null
+++ b/Tests/ttLib/tables/_v_m_t_x_test.py
@@ -0,0 +1,19 @@
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.ttLib.tables._v_m_t_x import table__v_m_t_x
+import _h_m_t_x_test
+import unittest
+
+
+class VmtxTableTest(_h_m_t_x_test.HmtxTableTest):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.tableClass = table__v_m_t_x
+        cls.tag = "vmtx"
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/data/C_F_F_.bin b/Tests/ttLib/tables/data/C_F_F_.bin
new file mode 100644
index 0000000..0099b3b
--- /dev/null
+++ b/Tests/ttLib/tables/data/C_F_F_.bin
Binary files differ
diff --git a/Tests/ttLib/tables/data/C_F_F_.ttx b/Tests/ttLib/tables/data/C_F_F_.ttx
new file mode 100644
index 0000000..dd09577
--- /dev/null
+++ b/Tests/ttLib/tables/data/C_F_F_.ttx
@@ -0,0 +1,282 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.1">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="dollar"/>
+    <GlyphID id="2" name="one"/>
+    <GlyphID id="3" name="two"/>
+    <GlyphID id="4" name="three"/>
+    <GlyphID id="5" name="A"/>
+    <GlyphID id="6" name="B"/>
+    <GlyphID id="7" name="C"/>
+    <GlyphID id="8" name="dollar.black"/>
+  </GlyphOrder>
+
+  <CFF>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="CFF2TestFont1Master-0">
+      <version value="1.0"/>
+      <FullName value="CFF2 Test Font1 Master 0"/>
+      <FamilyName value="CFF2 Test Font1 Master"/>
+      <isFixedPitch value="0"/>
+      <ItalicAngle value="0"/>
+      <UnderlinePosition value="-100"/>
+      <UnderlineThickness value="50"/>
+      <PaintType value="0"/>
+      <CharstringType value="2"/>
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FontBBox value="0 -128 651 762"/>
+      <StrokeWidth value="0"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <Encoding name="StandardEncoding"/>
+      <Private>
+        <BlueValues value="-20 0 466 484 531 546 652 667 677 697 738 758"/>
+        <OtherBlues value="-255 -245"/>
+        <FamilyBlues value="-20 0 473 491 525 540 644 659 669 689 729 749"/>
+        <FamilyOtherBlues value="-249 -239"/>
+        <BlueScale value="0.0375"/>
+        <BlueShift value="7"/>
+        <BlueFuzz value="0"/>
+        <StdHW value="26"/>
+        <StdVW value="28"/>
+        <StemSnapH value="20 26"/>
+        <StemSnapV value="28 32"/>
+        <ForceBold value="0"/>
+        <LanguageGroup value="0"/>
+        <ExpansionFactor value="0.06"/>
+        <initialRandomSeed value="0"/>
+        <defaultWidthX value="490"/>
+        <nominalWidthX value="597"/>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          -97 0 50 600 50 hstem
+          0 50 400 50 vstem
+          0 0 rmoveto
+          500 0 rlineto
+          0 700 rlineto
+          -500 0 rlineto
+          0 -700 rlineto
+          250 395 rmoveto
+          -170 255 rlineto
+          340 0 rlineto
+          -170 -255 rlineto
+          30 -45 rmoveto
+          170 255 rlineto
+          0 -510 rlineto
+          -170 255 rlineto
+          -200 -300 rmoveto
+          170 255 rlineto
+          170 -255 rlineto
+          -340 0 rlineto
+          -30 555 rmoveto
+          170 -255 rlineto
+          -170 -255 rlineto
+          0 510 rlineto
+          endchar
+        </CharString>
+        <CharString name="A">
+          56 523 26 rmoveto
+          -120 -6 rlineto
+          0 -20 rlineto
+          248 0 rlineto
+          0 20 rlineto
+          -114 6 rlineto
+          -14 0 rlineto
+          -424 0 rmoveto
+          -87 -6 rlineto
+          0 -20 rlineto
+          198 0 rlineto
+          0 20 rlineto
+          -97 6 rlineto
+          -14 0 rlineto
+          369 221 rmoveto
+          -8 20 rlineto
+          -278 0 rlineto
+          -9 -20 rlineto
+          295 0 rlineto
+          -161 430 rmoveto
+          -222 -677 rlineto
+          27 0 rlineto
+          211 660 rlineto
+          -17 -10 rlineto
+          216 -650 rlineto
+          34 0 rlineto
+          -229 677 rlineto
+          -20 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="B">
+          -3 167 310 rmoveto
+          0 -104 0 -104 -2 -102 rrcurveto
+          34 0 rlineto
+          -2 102 0 104 0 144 rrcurveto
+          0 7 rlineto
+          0 114 0 104 2 102 rrcurveto
+          -34 0 rlineto
+          2 -102 0 -104 0 -104 rrcurveto
+          0 -57 rlineto
+          8 340 rmoveto
+          7 0 rlineto
+          0 27 rlineto
+          -124 0 rlineto
+          0 -20 rlineto
+          117 -7 rlineto
+          0 -623 rmoveto
+          -117 -7 rlineto
+          0 -20 rlineto
+          124 0 rlineto
+          0 27 rlineto
+          -7 0 rlineto
+          7 316 rmoveto
+          101 0 rlineto
+          162 0 69 -60 0 -102 rrcurveto
+          0 -102 -75 -57 -125 0 rrcurveto
+          -132 0 rlineto
+          0 -22 rlineto
+          131 0 rlineto
+          156 0 75 77 0 102 rrcurveto
+          0 100 -68 76 -162 2 rrcurveto
+          -10 -8 rlineto
+          141 11 64 75 0 84 rrcurveto
+          0 95 -66 63 -146 0 rrcurveto
+          -115 0 rlineto
+          0 -22 rlineto
+          104 0 rlineto
+          145 0 50 -57 0 -76 rrcurveto
+          0 -95 -75 -64 -136 0 rrcurveto
+          -88 0 rlineto
+          0 -20 rlineto
+          endchar
+        </CharString>
+        <CharString name="C">
+          47 386 7 rmoveto
+          -167 0 -123 128 0 203 rrcurveto
+          0 199 116 133 180 0 rrcurveto
+          73 0 40 -17 56 -37 rrcurveto
+          -21 29 rlineto
+          18 -145 rlineto
+          24 0 rlineto
+          -4 139 rlineto
+          -60 35 -49 18 -80 0 rrcurveto
+          -190 0 -135 -144 0 -210 rrcurveto
+          0 -209 129 -144 195 0 rrcurveto
+          72 0 57 12 67 41 rrcurveto
+          4 139 rlineto
+          -24 0 rlineto
+          -18 -139 rlineto
+          17 20 rlineto
+          -55 -37 -55 -14 -67 0 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="dollar">
+          245 5 rmoveto
+          -65 0 -39 15 -46 50 rrcurveto
+          36 -48 rlineto
+          -28 100 rlineto
+          -6 15 -10 5 -11 0 rrcurveto
+          -14 0 -8 -7 -1 -14 rrcurveto
+          24 -85 61 -51 107 0 rrcurveto
+          91 0 90 54 0 112 rrcurveto
+          0 70 -26 66 -134 57 rrcurveto
+          -19 8 rlineto
+          -93 39 -42 49 0 68 rrcurveto
+          0 91 60 48 88 0 rrcurveto
+          56 0 35 -14 44 -50 rrcurveto
+          -38 47 rlineto
+          28 -100 rlineto
+          6 -15 10 -5 11 0 rrcurveto
+          14 0 8 7 1 14 rrcurveto
+          -24 88 -67 48 -84 0 rrcurveto
+          -92 0 -82 -51 0 -108 rrcurveto
+          0 -80 45 -53 92 -42 rrcurveto
+          37 -17 rlineto
+          114 -52 26 -46 0 -65 rrcurveto
+          0 -93 -65 -55 -90 0 rrcurveto
+          18 318 rmoveto
+          0 439 rlineto
+          -22 0 rlineto
+          0 -425 rlineto
+          22 -14 rlineto
+          -20 -438 rmoveto
+          22 0 rlineto
+          0 438 rlineto
+          -22 14 rlineto
+          0 -452 rlineto
+          endchar
+        </CharString>
+        <CharString name="dollar.black">
+          3 245 5 rmoveto
+          -65 0 -39 15 -46 50 rrcurveto
+          36 -48 rlineto
+          -28 100 rlineto
+          -6 15 -10 5 -11 0 rrcurveto
+          -14 0 -8 -7 -1 -14 rrcurveto
+          24 -85 61 -51 107 0 rrcurveto
+          91 0 90 54 0 112 rrcurveto
+          0 70 -26 66 -134 57 rrcurveto
+          -19 8 rlineto
+          -93 39 -42 49 0 68 rrcurveto
+          0 91 60 48 88 0 rrcurveto
+          56 0 35 -14 44 -50 rrcurveto
+          -38 47 rlineto
+          28 -100 rlineto
+          6 -15 10 -5 11 0 rrcurveto
+          14 0 8 7 1 14 rrcurveto
+          -24 88 -67 48 -84 0 rrcurveto
+          -92 0 -82 -51 0 -108 rrcurveto
+          0 -80 45 -53 92 -42 rrcurveto
+          37 -17 rlineto
+          114 -52 26 -46 0 -65 rrcurveto
+          0 -93 -65 -55 -90 0 rrcurveto
+          17 651 rmoveto
+          1 106 rlineto
+          -22 0 rlineto
+          1 -107 rlineto
+          20 1 rlineto
+          -15 -784 rmoveto
+          22 0 rlineto
+          -3 121 rlineto
+          -20 2 rlineto
+          1 -123 rlineto
+          endchar
+        </CharString>
+        <CharString name="one">
+          91 618 rmoveto
+          0 -20 rlineto
+          155 35 rlineto
+          0 -421 rlineto
+          0 -70 -1 -71 -2 -72 rrcurveto
+          34 0 rlineto
+          -2 72 -1 71 0 70 rrcurveto
+          0 297 rlineto
+          4 146 rlineto
+          -14 12 rlineto
+          -173 -49 rlineto
+          176 -593 rmoveto
+          -14 0 rlineto
+          -170 -6 rlineto
+          0 -20 rlineto
+          344 0 rlineto
+          0 20 rlineto
+          -160 6 rlineto
+          endchar
+        </CharString>
+        <CharString name="three">
+          endchar
+        </CharString>
+        <CharString name="two">
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/C_F_F__2.bin b/Tests/ttLib/tables/data/C_F_F__2.bin
new file mode 100644
index 0000000..faa0e91
--- /dev/null
+++ b/Tests/ttLib/tables/data/C_F_F__2.bin
Binary files differ
diff --git a/Tests/ttLib/tables/data/C_F_F__2.ttx b/Tests/ttLib/tables/data/C_F_F__2.ttx
new file mode 100644
index 0000000..c86252b
--- /dev/null
+++ b/Tests/ttLib/tables/data/C_F_F__2.ttx
@@ -0,0 +1,395 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.18">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="dollar"/>
+    <GlyphID id="2" name="dollar.nostroke"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.00099"/>
+    <checkSumAdjustment value="0x3e077bd3"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri Jan  6 11:41:20 2017"/>
+    <modified value="Thu Jan 12 22:37:13 2017"/>
+    <xMin value="51"/>
+    <yMin value="-115"/>
+    <xMax value="560"/>
+    <yMax value="762"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="3"/>
+  </maxp>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name="dollar.nostroke"/>
+    </extraNames>
+  </post>
+
+  <CFF2>
+    <major value="2"/>
+    <minor value="0"/>
+    <CFFFont name="CFF2Font">
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FDArray>
+        <FontDict index="0">
+          <Private>
+            <BlueValues>
+                <blend value="-20 5 7 0 5 7"/>
+                <blend value="0 -5 -7 0 -5 -7"/>
+                <blend value="487 -13 -17 0 -13 -17"/>
+                <blend value="503 -3 -3 0 -3 -3"/>
+                <blend value="515 28 39 0 28 39"/>
+                <blend value="531 -3 -3 0 -3 -3"/>
+                <blend value="536 5 4 0 5 4"/>
+                <blend value="552 -3 -3 0 -3 -3"/>
+                <blend value="624 12 13 0 12 13"/>
+                <blend value="640 -3 -3 0 -3 -3"/>
+                <blend value="652 -2 -2 0 -2 -2"/>
+                <blend value="672 -5 -7 0 -5 -7"/>
+                <blend value="711 6 9 0 6 9"/>
+                <blend value="731 0 0 0 0 0"/>
+            </BlueValues>
+            <OtherBlues>
+                <blend value="-232 -18 -23 0 -18 -23"/>
+                <blend value="-222 0 0 0 0 0"/>
+            </OtherBlues>
+            <FamilyBlues value="-20 0 473 491 525 540 549 562 644 659 669 689 729 749"/>
+            <FamilyOtherBlues value="-249 -239"/>
+            <BlueScale value="0.0375"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW>
+                <blend value="74 -19 -48 -24 -28 -48"/>
+            </StdHW>
+            <StdVW>
+                <blend value="190 -110 -162 0 -110 -162"/>
+            </StdVW>
+            <StemSnapH>
+                <blend value="60 -20 -40 -22 -28 -40"/>
+                <blend value="74 1 -8 -2 0 -8"/>
+            </StemSnapH>
+            <StemSnapV>
+                <blend value="190 -110 -162 0 -110 -162"/>
+                <blend value="200 0 -6 0 0 -6"/>
+            </StemSnapV>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef">
+          80 0 0 10 -6 -10 1 blend
+          0 rmoveto
+          80 -20 -55 -40 25 40 1 blend
+          0 rlineto
+          400 652 20 55 20 -13 -20 18 25 0 0 0 2 blend
+          rlineto
+          -80 20 55 40 -25 -40 1 blend
+          0 rlineto
+          -400 -652 -20 -55 -20 13 20 -18 -25 0 0 0 2 blend
+          rlineto
+          480 0 0 -20 12 20 1 blend
+          0 rmoveto
+          -400 652 -20 -55 -20 13 20 18 25 0 0 0 2 blend
+          rlineto
+          -80 20 55 40 -25 -40 1 blend
+          0 rlineto
+          400 -652 20 55 20 -13 -20 -18 -25 0 0 0 2 blend
+          rlineto
+          80 -20 -55 -40 25 40 1 blend
+          0 rlineto
+          -410 60 -10 -45 10 -6 -10 -10 -38 0 0 0 2 blend
+          rmoveto
+          0 532 38 101 0 0 0 1 blend
+          rlineto
+          340 20 90 0 0 0 1 blend
+          0 rlineto
+          0 -532 -38 -101 0 0 0 1 blend
+          rlineto
+          -340 -20 -90 0 0 0 1 blend
+          0 rlineto
+          -70 -60 10 45 0 0 0 10 38 0 0 0 2 blend
+          rmoveto
+          480 0 rlineto
+          0 652 18 25 0 0 0 1 blend
+          rlineto
+          -480 0 rlineto
+          0 -652 -18 -25 0 0 0 1 blend
+          rlineto
+        </CharString>
+        <CharString name="dollar">
+          260 39 -12 -15 0 0 0 -4 -32 -20 13 20 2 blend
+          rmoveto
+          -65 26 0 0 0 0 1 blend
+          0 -28 11 -49 24 -17 -11 0 0 0 -6 4 0 0 0 3 3 0 0 0 -6 26 0 0 0 4 blend
+          rrcurveto
+          78 -55 -25 -42 0 0 0 19 7 5 -4 -5 2 blend
+          rlineto
+          -8 85 -9 -20 0 0 0 -9 15 15 -9 -15 2 blend
+          rlineto
+          -5 52 -22 20 -43 -7 1 1 -1 -1 1 -36 0 0 0 0 10 -1 1 1 -7 -16 0 0 0 19 32 0 0 0 5 blend
+          0 rrcurveto
+          -26 4 12 0 0 0 1 blend
+          0 -27 -14 -14 -38 13 19 0 0 0 3 7 0 0 0 5 13 0 0 0 18 24 0 0 0 4 blend
+          rrcurveto
+          0 -90 71 -50 139 4 24 0 0 0 3 5 0 0 0 10 -10 0 0 0 -9 -1 0 0 0 -32 -32 0 0 0 5 blend
+          0 rrcurveto
+          163 -27 -72 0 0 0 1 blend
+          0 99 84 -17 -9 0 0 0 -8 -31 2 -1 -2 2 blend
+          0 108 -1 3 0 0 0 1 blend
+          rrcurveto
+          0 107 -56 54 -138 56 -25 -37 0 0 0 15 30 0 0 0 11 12 -2 1 2 3 4 0 0 0 -9 1 0 0 0 5 blend
+          rrcurveto
+          -32 13 -6 13 0 0 0 0 -5 0 0 0 2 blend
+          rlineto
+          -63 25 -30 18 -8 -30 0 0 0 -2 14 0 0 0 -10 -12 0 0 0 17 31 0 0 0 4 blend
+          0 48 16 20 8 -5 -8 1 blend
+          rrcurveto
+          0 63 43 25 61 12 28 -6 4 6 14 17 -2 1 2 12 23 18 -12 -18 13 27 2 -1 -2 4 blend
+          0 rrcurveto
+          42 -12 14 0 0 0 1 blend
+          0 27 -4 52 -24 9 8 0 0 0 -1 -10 0 0 0 -10 -8 0 0 0 7 -26 0 0 0 4 blend
+          rrcurveto
+          -85 47 33 47 -3 2 3 -11 0 5 -3 -5 2 blend
+          rlineto
+          10 -67 7 18 3 -2 -3 -9 -33 -25 16 25 2 blend
+          rlineto
+          11 -75 37 -14 39 1 -5 -1 1 1 23 60 1 -1 -1 -12 -27 1 -1 -1 0 9 -1 1 1 -17 -28 -1 1 1 5 blend
+          0 rrcurveto
+          26 -7 -12 1 -1 -1 1 blend
+          0 29 15 5 41 -12 -21 -2 1 2 -5 -8 1 -1 -1 3 -4 2 -1 -2 -20 -27 -1 1 1 4 blend
+          rrcurveto
+          0 84 -84 52 -121 -6 -24 0 0 0 2 4 0 0 0 4 17 8 -5 -8 8 -4 0 0 0 20 37 -8 5 8 5 blend
+          0 rrcurveto
+          -158 43 66 0 0 0 1 blend
+          0 -85 -80 2 3 0 0 0 0 29 0 0 0 2 blend
+          0 -103 1 -5 0 0 0 1 blend
+          rrcurveto
+          0 -105 64 -55 117 -49 5 25 0 0 0 -2 -19 0 0 0 1 2 0 0 0 -12 -25 0 0 0 12 7 0 0 0 5 blend
+          rrcurveto
+          31 -13 6 6 0 0 0 0 -4 0 0 0 2 blend
+          rlineto
+          72 -30 28 -19 13 42 0 0 0 0 -22 0 0 0 8 -2 0 0 0 -11 -27 0 0 0 4 blend
+          0 -63 0 -2 -5 3 5 1 blend
+          rrcurveto
+          0 -49 -39 -35 -66 -25 -43 -2 1 2 -14 -26 -2 1 2 -7 -19 -13 9 13 -16 -24 2 -1 -2 4 blend
+          0 rrcurveto
+          65 275 -34 -47 -10 6 10 12 52 20 -13 -20 2 blend
+          rmoveto
+          0 417 11 11 0 0 0 1 blend
+          rlineto
+          -71 31 49 20 -12 -20 1 blend
+          0 rlineto
+          0 -417 -11 -11 0 0 0 1 blend
+          rlineto
+          71 -31 -49 -20 12 20 1 blend
+          0 rlineto
+          -79 -429 38 57 20 -12 -20 -8 -20 0 0 0 2 blend
+          rmoveto
+          71 -31 -49 -20 12 20 1 blend
+          0 rlineto
+          0 429 8 20 0 0 0 1 blend
+          rlineto
+          -71 31 49 20 -12 -20 1 blend
+          0 rlineto
+          0 -429 -8 -20 0 0 0 1 blend
+          rlineto
+        </CharString>
+        <CharString name="dollar.nostroke">
+          260 39 -12 -15 0 0 0 -4 -32 -20 13 20 2 blend
+          rmoveto
+          -65 26 0 0 0 0 1 blend
+          0 -28 11 -49 24 -17 -11 0 0 0 -6 4 0 0 0 3 3 0 0 0 -6 26 0 0 0 4 blend
+          rrcurveto
+          78 -55 -25 -42 0 0 0 19 7 5 -4 -5 2 blend
+          rlineto
+          -8 85 -9 -20 0 0 0 -9 15 15 -9 -15 2 blend
+          rlineto
+          -5 52 -22 20 -43 -7 1 1 -1 -1 1 -36 0 0 0 0 10 -1 1 1 -7 -16 0 0 0 19 32 0 0 0 5 blend
+          0 rrcurveto
+          -26 4 12 0 0 0 1 blend
+          0 -27 -14 -14 -38 13 19 0 0 0 3 7 0 0 0 5 13 0 0 0 18 24 0 0 0 4 blend
+          rrcurveto
+          0 -90 71 -50 139 4 24 0 0 0 3 5 0 0 0 10 -10 0 0 0 -9 -1 0 0 0 -32 -32 0 0 0 5 blend
+          0 rrcurveto
+          163 -27 -72 0 0 0 1 blend
+          0 99 84 -17 -9 0 0 0 -8 -31 2 -1 -2 2 blend
+          0 108 -1 3 0 0 0 1 blend
+          rrcurveto
+          0 107 -59 47 -135 63 -25 -37 0 0 0 18 33 0 0 0 18 19 -2 1 2 0 1 0 0 0 -16 -6 0 0 0 5 blend
+          rrcurveto
+          -32 15 -6 13 0 0 0 -2 -7 0 0 0 2 blend
+          rlineto
+          -55 26 -26 21 -16 -38 4 -3 -4 -3 13 -2 1 2 -14 -16 -2 2 2 14 28 4 -2 -4 4 blend
+          0 45 19 23 8 -5 -8 1 blend
+          rrcurveto
+          0 60 38 25 53 15 31 -6 3 6 19 22 -3 2 3 12 23 16 -10 -16 21 35 2 -2 -2 4 blend
+          0 rrcurveto
+          43 -13 13 -1 1 1 1 blend
+          0 27 -4 52 -24 9 8 0 0 0 -1 -10 0 0 0 -10 -8 0 0 0 7 -26 0 0 0 4 blend
+          rrcurveto
+          -85 47 33 47 -3 2 3 -11 0 5 -3 -5 2 blend
+          rlineto
+          10 -67 7 18 3 -2 -3 -9 -33 -25 16 25 2 blend
+          rlineto
+          11 -75 37 -14 39 1 -5 -1 1 1 23 60 1 -1 -1 -12 -27 1 -1 -1 0 9 -1 1 1 -17 -28 -1 1 1 5 blend
+          0 rrcurveto
+          26 -7 -12 1 -1 -1 1 blend
+          0 29 15 5 41 -12 -21 -2 1 2 -5 -8 1 -1 -1 3 -4 2 -1 -2 -20 -27 -1 1 1 4 blend
+          rrcurveto
+          0 84 -84 52 -121 -6 -24 0 0 0 2 4 0 0 0 4 17 8 -5 -8 8 -4 0 0 0 20 37 -8 5 8 5 blend
+          0 rrcurveto
+          -155 40 63 0 0 0 1 blend
+          0 -84 -80 1 2 0 0 0 0 29 0 0 0 2 blend
+          0 -103 1 -5 0 0 0 1 blend
+          rrcurveto
+          0 -104 65 -49 112 -54 4 24 0 0 0 -3 -20 0 0 0 -5 -4 0 0 0 -7 -20 0 0 0 17 12 0 0 0 5 blend
+          rrcurveto
+          31 -15 6 6 0 0 0 2 -2 0 0 0 2 blend
+          rlineto
+          66 -32 28 -22 19 48 0 0 0 2 -20 0 0 0 8 -2 -4 3 4 -8 -24 -10 6 10 4 blend
+          0 -55 -8 -10 -4 3 4 1 blend
+          rrcurveto
+          0 -49 -41 -38 -58 -25 -43 -3 2 3 -12 -24 1 -1 -1 -4 -16 -3 2 3 -24 -32 3 -2 -3 4 blend
+          0 rrcurveto
+          65 573 -34 -47 -10 6 10 27 77 32 -21 -32 2 blend
+          rmoveto
+          0 119 -4 -14 -12 8 12 1 blend
+          rlineto
+          -71 31 49 20 -12 -20 1 blend
+          0 rlineto
+          0 -119 4 14 12 -8 -12 1 blend
+          rlineto
+          71 -31 -49 -20 12 20 1 blend
+          0 rlineto
+          -69 -727 28 47 10 -6 -10 -23 -45 -12 8 12 2 blend
+          rmoveto
+          71 -31 -49 -20 13 20 1 blend
+          0 rlineto
+          0 129 -2 -18 -10 6 10 1 blend
+          rlineto
+          -71 31 49 20 -13 -20 1 blend
+          0 rlineto
+          0 -129 2 18 10 -6 -10 1 blend
+          rlineto
+        </CharString>
+      </CharStrings>
+      <VarStore Format="1">
+        <Format value="1"/>
+        <VarRegionList>
+          <!-- RegionAxisCount=2 -->
+          <!-- RegionCount=5 -->
+          <Region index="0">
+            <VarRegionAxis index="0">
+              <StartCoord value="-1.0"/>
+              <PeakCoord value="-0.632"/>
+              <EndCoord value="0.0"/>
+            </VarRegionAxis>
+            <VarRegionAxis index="1">
+              <StartCoord value="0.0"/>
+              <PeakCoord value="0.0"/>
+              <EndCoord value="0.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="1">
+            <VarRegionAxis index="0">
+              <StartCoord value="-1.0"/>
+              <PeakCoord value="-1.0"/>
+              <EndCoord value="-0.632"/>
+            </VarRegionAxis>
+            <VarRegionAxis index="1">
+              <StartCoord value="0.0"/>
+              <PeakCoord value="0.0"/>
+              <EndCoord value="0.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="2">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.0"/>
+              <PeakCoord value="0.0"/>
+              <EndCoord value="0.0"/>
+            </VarRegionAxis>
+            <VarRegionAxis index="1">
+              <StartCoord value="0.0"/>
+              <PeakCoord value="1.0"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="3">
+            <VarRegionAxis index="0">
+              <StartCoord value="-1.0"/>
+              <PeakCoord value="-0.632"/>
+              <EndCoord value="0.0"/>
+            </VarRegionAxis>
+            <VarRegionAxis index="1">
+              <StartCoord value="0.0"/>
+              <PeakCoord value="1.0"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="4">
+            <VarRegionAxis index="0">
+              <StartCoord value="-1.0"/>
+              <PeakCoord value="-1.0"/>
+              <EndCoord value="-0.632"/>
+            </VarRegionAxis>
+            <VarRegionAxis index="1">
+              <StartCoord value="0.0"/>
+              <PeakCoord value="1.0"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+        </VarRegionList>
+        <!-- VarDataCount=1 -->
+        <VarData index="0">
+          <!-- ItemCount=0 -->
+          <NumShorts value="0"/>
+          <!-- VarRegionCount=5 -->
+          <VarRegionIndex index="0" value="0"/>
+          <VarRegionIndex index="1" value="1"/>
+          <VarRegionIndex index="2" value="2"/>
+          <VarRegionIndex index="3" value="3"/>
+          <VarRegionIndex index="4" value="4"/>
+        </VarData>
+      </VarStore>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF2>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/_h_h_e_a_recalc_OTF.ttx b/Tests/ttLib/tables/data/_h_h_e_a_recalc_OTF.ttx
new file mode 100644
index 0000000..ff84817
--- /dev/null
+++ b/Tests/ttLib/tables/data/_h_h_e_a_recalc_OTF.ttx
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.12">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="A"/>
+    <GlyphID id="2" name="B"/>
+    <GlyphID id="3" name="C"/>
+  </GlyphOrder>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="800"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="999"/>
+    <minLeftSideBearing value="99"/>
+    <minRightSideBearing value="99"/>
+    <xMaxExtent value="99"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="4"/>
+  </hhea>
+
+  <CFF>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="Test">
+      <Private>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          300
+          0 0 rmoveto
+          1 1 rlineto
+          endchar
+        </CharString>
+        <CharString name="A">
+          400
+          -55.2 -55.2 rmoveto
+          110.4 110.4 rlineto
+          endchar
+        </CharString>
+        <CharString name="B">
+          500
+          100 0 rmoveto
+          300 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="C">
+          600
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+  </CFF>
+
+  <hmtx>
+    <mtx name=".notdef" width="300" lsb="0"/>
+    <mtx name="A" width="400" lsb="-56"/>
+    <mtx name="B" width="500" lsb="100"/>
+    <mtx name="C" width="600" lsb="0"/>
+  </hmtx>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/_h_h_e_a_recalc_TTF.ttx b/Tests/ttLib/tables/data/_h_h_e_a_recalc_TTF.ttx
new file mode 100644
index 0000000..1167842
--- /dev/null
+++ b/Tests/ttLib/tables/data/_h_h_e_a_recalc_TTF.ttx
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.12">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="A"/>
+    <GlyphID id="2" name="B"/>
+    <GlyphID id="3" name="C"/>
+  </GlyphOrder>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="800"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="999"/>
+    <minLeftSideBearing value="99"/>
+    <minRightSideBearing value="99"/>
+    <xMaxExtent value="99"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="4"/>
+  </hhea>
+
+  <glyf>
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="1" yMax="1">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="1" y="1" on="1"/>
+      </contour>
+    </TTGlyph>
+    <TTGlyph name="A" xMin="-56" yMin="-56" xMax="56" yMax="56">
+      <contour>
+        <pt x="-56" y="-56" on="1"/>
+        <pt x="56" y="56" on="1"/>
+      </contour>
+    </TTGlyph>
+    <TTGlyph name="B" xMin="100" yMin="0" xMax="400" yMax="0">
+      <contour>
+        <pt x="100" y="0" on="1"/>
+        <pt x="400" y="0" on="1"/>
+      </contour>
+    </TTGlyph>
+    <TTGlyph name="C"/><!-- contains no outline data -->
+  </glyf>
+
+  <hmtx>
+    <mtx name=".notdef" width="300" lsb="0"/>
+    <mtx name="A" width="400" lsb="-56"/>
+    <mtx name="B" width="500" lsb="100"/>
+    <mtx name="C" width="600" lsb="0"/>
+  </hmtx>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/_h_h_e_a_recalc_empty.ttx b/Tests/ttLib/tables/data/_h_h_e_a_recalc_empty.ttx
new file mode 100644
index 0000000..0bc5b80
--- /dev/null
+++ b/Tests/ttLib/tables/data/_h_h_e_a_recalc_empty.ttx
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.12">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="A"/>
+    <GlyphID id="2" name="B"/>
+    <GlyphID id="3" name="C"/>
+  </GlyphOrder>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="800"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="999"/>
+    <minLeftSideBearing value="99"/>
+    <minRightSideBearing value="99"/>
+    <xMaxExtent value="99"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="4"/>
+  </hhea>
+
+  <CFF>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="Test">
+      <Private>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          300 endchar
+        </CharString>
+        <CharString name="A">
+          400 endchar
+        </CharString>
+        <CharString name="B">
+          500 endchar
+        </CharString>
+        <CharString name="C">
+          600 endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+  </CFF>
+
+  <hmtx>
+    <mtx name=".notdef" width="300" lsb="0"/>
+    <mtx name="A" width="400" lsb="0"/>
+    <mtx name="B" width="500" lsb="0"/>
+    <mtx name="C" width="600" lsb="0"/>
+  </hmtx>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/_v_h_e_a_recalc_OTF.ttx b/Tests/ttLib/tables/data/_v_h_e_a_recalc_OTF.ttx
new file mode 100644
index 0000000..d9851db
--- /dev/null
+++ b/Tests/ttLib/tables/data/_v_h_e_a_recalc_OTF.ttx
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.12">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="A"/>
+    <GlyphID id="2" name="B"/>
+    <GlyphID id="3" name="C"/>
+  </GlyphOrder>
+
+  <vhea>
+    <tableVersion value="0x00011000"/>
+    <ascent value="500"/>
+    <descent value="-500"/>
+    <lineGap value="0"/>
+    <advanceHeightMax value="999"/>
+    <minTopSideBearing value="99"/>
+    <minBottomSideBearing value="99"/>
+    <yMaxExtent value="99"/>
+    <caretSlopeRise value="0"/>
+    <caretSlopeRun value="1"/>
+    <caretOffset value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <reserved4 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfVMetrics value="4"/>
+  </vhea>
+
+  <CFF>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="Test">
+      <Private>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          300
+          0 0 rmoveto
+          1 1 rlineto
+          endchar
+        </CharString>
+        <CharString name="A">
+          400
+          -55.2 -55.2 rmoveto
+          110.4 110.4 rlineto
+          endchar
+        </CharString>
+        <CharString name="B">
+          500
+          100 0 rmoveto
+          300 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="C">
+          600
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+  </CFF>
+
+  <vmtx>
+    <mtx name=".notdef" height="600" tsb="222"/>
+    <mtx name="A" height="700" tsb="200"/>
+    <mtx name="B" height="800" tsb="300"/>
+    <mtx name="C" height="900" tsb="0"/>
+  </vmtx>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/_v_h_e_a_recalc_TTF.ttx b/Tests/ttLib/tables/data/_v_h_e_a_recalc_TTF.ttx
new file mode 100644
index 0000000..ef3035d
--- /dev/null
+++ b/Tests/ttLib/tables/data/_v_h_e_a_recalc_TTF.ttx
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.12">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="A"/>
+    <GlyphID id="2" name="B"/>
+    <GlyphID id="3" name="C"/>
+  </GlyphOrder>
+
+  <vhea>
+    <tableVersion value="0x00011000"/>
+    <ascent value="500"/>
+    <descent value="-500"/>
+    <lineGap value="0"/>
+    <advanceHeightMax value="999"/>
+    <minTopSideBearing value="99"/>
+    <minBottomSideBearing value="99"/>
+    <yMaxExtent value="99"/>
+    <caretSlopeRise value="0"/>
+    <caretSlopeRun value="1"/>
+    <caretOffset value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <reserved4 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfVMetrics value="4"/>
+  </vhea>
+
+  <glyf>
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="1" yMax="1">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="1" y="1" on="1"/>
+      </contour>
+    </TTGlyph>
+    <TTGlyph name="A" xMin="-56" yMin="-56" xMax="56" yMax="56">
+      <contour>
+        <pt x="-56" y="-56" on="1"/>
+        <pt x="56" y="56" on="1"/>
+      </contour>
+    </TTGlyph>
+    <TTGlyph name="B" xMin="100" yMin="0" xMax="400" yMax="0">
+      <contour>
+        <pt x="100" y="0" on="1"/>
+        <pt x="400" y="0" on="1"/>
+      </contour>
+    </TTGlyph>
+    <TTGlyph name="C"/><!-- contains no outline data -->
+  </glyf>
+
+  <vmtx>
+    <mtx name=".notdef" height="600" tsb="222"/>
+    <mtx name="A" height="700" tsb="200"/>
+    <mtx name="B" height="800" tsb="300"/>
+    <mtx name="C" height="900" tsb="0"/>
+  </vmtx>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/_v_h_e_a_recalc_empty.ttx b/Tests/ttLib/tables/data/_v_h_e_a_recalc_empty.ttx
new file mode 100644
index 0000000..2960020
--- /dev/null
+++ b/Tests/ttLib/tables/data/_v_h_e_a_recalc_empty.ttx
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.12">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="A"/>
+    <GlyphID id="2" name="B"/>
+    <GlyphID id="3" name="C"/>
+  </GlyphOrder>
+
+  <vhea>
+    <tableVersion value="0x00011000"/>
+    <ascent value="500"/>
+    <descent value="-500"/>
+    <lineGap value="0"/>
+    <advanceHeightMax value="999"/>
+    <minTopSideBearing value="99"/>
+    <minBottomSideBearing value="99"/>
+    <yMaxExtent value="99"/>
+    <caretSlopeRise value="0"/>
+    <caretSlopeRun value="1"/>
+    <caretOffset value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <reserved4 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfVMetrics value="4"/>
+  </vhea>
+
+  <CFF>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="Test">
+      <Private>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          300 endchar
+        </CharString>
+        <CharString name="A">
+          400 endchar
+        </CharString>
+        <CharString name="B">
+          500 endchar
+        </CharString>
+        <CharString name="C">
+          600 endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+  </CFF>
+
+  <vmtx>
+    <mtx name=".notdef" height="600" tsb="0"/>
+    <mtx name="A" height="700" tsb="0"/>
+    <mtx name="B" height="800" tsb="0"/>
+    <mtx name="C" height="900" tsb="0"/>
+  </vmtx>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/README b/Tests/ttLib/tables/data/aots/README
new file mode 100644
index 0000000..1b673bd
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/README
@@ -0,0 +1,10 @@
+The *.otf data in this directory was built from:
+
+https://github.com/adobe-type-tools/aots
+
+at the following revision:
+
+1c41fd20d2b020177625541a228c4c7c934879ef
+
+Fonts were built by running "make" and copying tests/*.otf over.
+Original .xml files were not copied to save space.
diff --git a/Tests/ttLib/tables/data/aots/base.otf b/Tests/ttLib/tables/data/aots/base.otf
new file mode 100644
index 0000000..2ed6f1f
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/base.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/base.ttx.CFF b/Tests/ttLib/tables/data/aots/base.ttx.CFF
new file mode 100644
index 0000000..89d5aaf
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/base.ttx.CFF
@@ -0,0 +1,735 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <CFF>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="dummy">
+      <version value="001.000"/>
+      <Notice value="Copyright (c) 2002 Adobe Systems Incorporated. All Rights Reserved."/>
+      <FullName value="dummy"/>
+      <FamilyName value="dummy"/>
+      <Weight value="Regular"/>
+      <isFixedPitch value="0"/>
+      <ItalicAngle value="0"/>
+      <UnderlinePosition value="-125"/>
+      <UnderlineThickness value="50"/>
+      <PaintType value="0"/>
+      <CharstringType value="2"/>
+      <FontMatrix value="0.0008 0 0 0.0008 0 0"/>
+      <UniqueID value="44788"/>
+      <FontBBox value="0 0 2500 2150"/>
+      <StrokeWidth value="0"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <Encoding name="StandardEncoding"/>
+      <Private>
+        <BlueValues value="-25 0 657 682 439 464 640 653 708 733 475 500"/>
+        <OtherBlues value="283 308 -251 -226 -154 -129 -194 -169"/>
+        <FamilyBlues value="-25 0 657 682 439 464 640 653 708 733 475 500"/>
+        <FamilyOtherBlues value="283 308 -251 -226 -154 -129 -194 -169"/>
+        <BlueScale value="0.039625"/>
+        <BlueShift value="7"/>
+        <BlueFuzz value="1"/>
+        <StdHW value="32"/>
+        <StdVW value="85"/>
+        <StemSnapH value="32"/>
+        <StemSnapV value="85 90"/>
+        <ForceBold value="0"/>
+        <LanguageGroup value="0"/>
+        <ExpansionFactor value="0.06"/>
+        <initialRandomSeed value="0"/>
+        <defaultWidthX value="2500"/>
+        <nominalWidthX value="2500"/>
+        <Subrs>
+          <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+          <CharString index="0">
+            145 665 rmoveto
+            -74 -43 -28 -166 -6 -75 -10 -124 21 -220 148 -44 rrcurveto
+            -11 37 40 7 39 hhcurveto
+            6 8 3 4 4 hvcurveto
+            69 60 39 31 2 103 1 44 5 43 -4 43 -7 87 -50 217 -88 45 -24 13 -29 1 -28 7 -12 -1 -8 -4 -6 -7 -16 -2 -16 -3 -13 -8 rrcurveto
+            122 -50 rmoveto
+            97 -69 31 -246 -15 -107 -13 -95 -42 -80 -111 33 -52 16 -30 55 -16 46 -32 98 -1 279 95 68 13 9 18 -2 15 4 1 1 2 1 2 1 14 -2 13 -2 11 -8 rrcurveto
+            233 -615 rmoveto
+            return
+          </CharString>
+          <CharString index="1">
+            175 661 rmoveto
+            1 -215 6 -215 1 -215 4 -42 54 3 2 41 -1 216 -6 214 -1 215 -11 35 -42 0 -7 -37 rrcurveto
+            325 -661 rmoveto
+            return
+          </CharString>
+          <CharString index="2">
+            143 536 rmoveto
+            59 22 61 39 64 3 78 5 3 -97 -32 -48 -76 -117 -268 -55 -9 -168 -2 -31 11 -30 5 -31 5 -10 5 -5 10 -5 50 -15 58 8 50 1 65 1 66 3 65 1 37 7 0 42 -35 11 rrcurveto
+            -106 -2 -108 -7 -107 4 -2 18 -8 18 2 17 16 141 259 55 69 117 72 122 -67 142 -156 -40 -52 -14 -48 -26 -51 -19 -40 -14 16 -51 41 8 rrcurveto
+            357 -536 rmoveto
+            return
+          </CharString>
+          <CharString index="3">
+            92 580 rmoveto
+            13 6 13 7 14 4 54 16 184 1 9 -81 1 -13 -3 -13 -3 -14 -9 -45 -124 -14 -42 -8 rrcurveto
+            -2 -2 1 -1 hhcurveto
+            -2 vlineto
+            -30 -15 5 -40 35 -4 60 -5 62 -4 47 -43 83 -75 -108 -134 -82 -20 -75 -17 -101 91 -42 -14 -22 -8 -7 -18 10 -21 2 -2 2 -2 1 -2 10 -10 11 -3 10 2 rrcurveto
+            2 2 -1 1 hhcurveto
+            16 -7 15 -7 15 -7 33 -14 33 -14 35 -7 103 -18 81 94 48 78 51 83 -64 98 -77 36 -4 1 -3 2 -4 2 17 7 16 9 15 12 77 61 -32 107 -79 40 -91 47 -115 -9 -91 -40 rrcurveto
+            -27 -24 18 -37 36 7 rrcurveto
+            408 -580 rmoveto
+            return
+          </CharString>
+          <CharString index="4">
+            336 627 rmoveto
+            -73 -94 -78 -92 -70 -97 -32 -45 -39 -39 -2 -56 2 -16 5 -7 14 -7 76 -39 130 16 102 10 -2 -44 -2 -44 -1 -43 4 -42 54 3 2 41 1 45 2 45 2 45 rrcurveto
+            6 6 0 1 5 hvcurveto
+            41 8 -6 54 -42 -3 rrcurveto
+            -2 -3 -1 -2 hhcurveto
+            4 135 -3 133 -49 127 -2 3 -3 2 -2 2 -6 6 -8 4 -9 -1 rrcurveto
+            -6 -6 -3 -4 -4 hvcurveto
+            -2 -1 -1 -1 -1 -1 rrcurveto
+            -230 -408 rmoveto
+            9 14 6 14 9 13 16 24 37 51 17 22 48 64 50 62 50 62 29 -105 1 -110 -4 -109 -87 -9 -131 -13 -50 20 rrcurveto
+            394 -219 rmoveto
+            return
+          </CharString>
+          <CharString index="5">
+            41 642 rmoveto
+            1 -2 1 -1 -1 vvcurveto
+            -7 2 -7 5 -5 vhcurveto
+            15 -69 -71 -105 61 -45 71 -50 214 60 48 -116 9 -20 3 -24 -3 -22 -13 -128 -51 -35 -120 -6 -38 -1 -62 -5 -26 34 -29 22 -33 -28 16 -33 39 -51 75 0 59 2 83 5 76 21 49 69 rrcurveto
+            25 36 0 48 11 42 19 72 -43 43 -42 45 -62 68 -159 -25 -76 26 -20 43 44 56 -6 66 101 14 102 -5 103 -1 37 7 0 42 -35 11 -109 1 -110 5 -108 -17 rrcurveto
+            -1 1 0 0 1 vvcurveto
+            -25 33 -45 -26 18 -38 rrcurveto
+            407 -673 rmoveto
+            return
+          </CharString>
+          <CharString index="6">
+            399 660 rmoveto
+            -36 2 -37 10 -35 -8 -152 -32 -56 -137 -37 -134 -35 -130 55 -175 141 -42 156 -46 135 253 -64 123 -39 78 -32 -3 -81 14 -26 5 -36 -14 -24 -10 -36 -15 -28 -18 -26 -26 19 101 63 130 114 18 rrcurveto
+            32 5 31 -8 32 -1 37 7 0 42 -35 11 rrcurveto
+            -263 -360 rmoveto
+            52 57 149 71 42 -110 33 -84 -77 -193 -113 33 -98 30 -29 103 4 92 9 -7 14 -1 14 9 rrcurveto
+            401 -299 rmoveto
+            return
+          </CharString>
+          <CharString index="7">
+            99 610 rmoveto
+            63 14 62 -15 64 -2 rrcurveto
+            22 23 1 2 22 hvcurveto
+            -24 -33 -19 -38 -22 -38 -85 -149 -77 -149 -19 -173 4 -37 43 -4 12 34 19 165 74 145 83 142 34 57 25 61 56 36 21 24 -14 30 -32 -2 rrcurveto
+            -6 -47 -49 -8 -48 hhcurveto
+            -71 2 -67 15 -70 -17 -40 -14 16 -51 41 8 rrcurveto
+            418 -667 rmoveto
+            return
+          </CharString>
+          <CharString index="8">
+            289 676 rmoveto
+            -88 12 -105 -100 -7 -86 -1 -23 -10 -26 9 -22 9 -21 8 -23 13 -20 6 -8 8 -7 9 -5 -42 -15 -31 -26 -21 -57 -31 -83 41 -138 89 -34 25 -9 24 -16 27 1 90 2 -6 -5 70 46 rrcurveto
+            60 39 -5 113 -8 58 -2 24 -13 22 -9 22 -8 20 -18 15 -15 16 -7 7 -9 4 -9 3 3 5 3 5 3 6 43 84 -21 87 -3 90 -6 20 -17 8 -14 -3 -10 9 -11 8 -13 1 rrcurveto
+            -12 -364 rmoveto
+            2 -2 2 -1 3 -1 12 -4 13 -1 9 -8 26 -18 13 -38 6 -28 24 -103 -43 -94 -120 16 -104 15 -73 140 80 83 31 33 22 -2 42 7 19 -4 19 3 17 7 rrcurveto
+            32 196 rmoveto
+            2 -48 -9 -48 -33 -37 -30 -34 -85 64 -8 41 -11 56 73 136 70 -23 8 -3 8 -6 7 -6 2 -31 4 -31 2 -30 rrcurveto
+            191 -508 rmoveto
+            return
+          </CharString>
+          <CharString index="9">
+            379 635 rmoveto
+            -50 16 -48 25 -52 6 -169 23 -32 -255 81 -95 66 -76 -16 4 97 -2 rrcurveto
+            6 9 3 4 4 hvcurveto
+            21 21 19 16 16 17 8 -65 4 -65 -6 -62 -4 -33 -9 -54 -40 -14 -66 -23 -78 47 -54 20 -40 13 -19 -50 37 -19 46 -17 45 -17 45 -16 31 -11 34 12 32 2 104 6 0 190 -4 62 rrcurveto
+            -1 36 -5 36 -5 36 -2 23 -4 24 -3 23 13 51 -17 20 19 51 5 16 -4 13 -9 8 15 11 0 23 -20 16 rrcurveto
+            -72 -84 rmoveto
+            2 -34 4 -35 5 -35 -3 -19 -4 -16 -6 -7 -19 -22 -22 -20 -21 -21 -14 1 -14 0 -15 1 -53 58 -34 59 18 84 5 21 15 17 10 18 21 7 21 16 22 -3 41 -5 38 -19 40 -14 rrcurveto
+            -1 -2 -2 -1 -1 -2 -14 3 -15 -9 -4 -21 rrcurveto
+            193 -551 rmoveto
+            return
+          </CharString>
+          <CharString index="10" raw="1">
+            f75af910 158c838c 828d8387 5d8a7d7a
+            4d5ffb37 3afb2878 fb3e8f66 b68797ad
+            92c79ac5 9dc3c287 bf99c18f 9d559f55
+            a4569e66 bd9e7eb3 0838f74a 65f7516b
+            f7570892 8c938c93 1e8da478 977a887a
+            8d797d8e 7208acfb 50159847 9b489e49
+            61866381 618ca4d2 a8d2a1d4 08f7a1fc
+            54150b   
+          </CharString>
+          <CharString index="11" raw="1">
+            c0f8f115 78538277 884f8830 a6318e30
+            8e468891 7e480888 8c878c89 1e867b95
+            78a389c9 91f72b8d c3c1a0a0 a49d9aa4
+            c7f22be8 2baea298 a39ba6a1 cfc272f6
+            57be799e 71937497 50aa4068 55790871
+            82897396 7d898989 898a8808 b4fba915
+            dd8daf97 d367d665 9f323c5c 47625089
+            428593b6 8e9f89c0 89b584b6 84b708f7
+            28f7bd15 ce79b23d 5852564f 3b7f3f7f
+            088a8b8a 8a1b8c84 07898b8a 8c8a1e73
+            7b9168aa 86d696bb 96bda779 9179907a
+            8d618f61 85608a86 b98ab995 b990a194
+            9e92a008 928a9188 901ebe9d a79ac37d
+            08fb36fb 85158f78 90798f78 088c0688
+            9f889e89 9f08f833 fc17150b 
+          </CharString>
+          <CharString index="12" raw="1">
+            f83bf8f9 155ea564 b85791fb 5ba649fc
+            1bb1fb10 bafb2b70 a9f70734 08879092
+            89911bda 90d09eb3 cc9ba696 a1a29fa4
+            a771ac69 7f7e8080 82807d7d 78745176
+            85698168 83688276 9b6e967d a143f70b
+            9df7b1f4 ec089e9d aa8ba393 b175b075
+            b075b179 a5b86aa4 08d4fcf9 150b
+          </CharString>
+          <CharString index="13" raw="1">
+            cef8ec15 93948c94 1b6efb1f 9efb1d9d
+            fb200889 0791578a 998e4408 8807838d
+            848c848e 89997f97 778a0888 888b8a88
+            1f808787 88848008 8a8b8a8a 891e887c
+            8e829680 df3ff75a cbc2e0e8 f724a2f7
+            0e4ef734 81a6729e 79a14dd6 fb1b7c34
+            81088a85 858b851b 62898855 b58708f7
+            9a6b15f7 052b64fb 6f38266a 624c6e54
+            82088e07 84f7455a f743b4f7 42089007
+            c991c88a bf5f08f7 3ffccc15 0b
+          </CharString>
+          <CharString index="14" raw="1">
+            bff8fa15 9f36903a 87338957 88678757
+            08857bfb 4392751e 9669b487 a98a08f3
+            ecb091f2 1fb49385 c1618820 85215cfb
+            019a94c4 8ac48ec4 8daa8dab 8daabd8d
+            be90bd8c b0928bb5 6896598a 5a865889
+            8ed988d9 7bd908e8 a0f7088b e7799e77
+            ab958dab 8a8e8b8e 8a8e869a 85927b8f
+            21a3fb2e 88216d08 7a85857f 801a8a88
+            8b888c88 08f854fc fa150b 
+          </CharString>
+          <CharString index="15" raw="1">
+            cef90215 90068e6c 7969876d 876b8c7f
+            8a61082d 0783808c 7d93828d fb0190fb
+            018cfb01 8f61c18e 8db48af5 85f689f6
+            d79ad97b d99caa9d 7fb46789 437b4298
+            44818ab6 8cb68db6 088db59e b48cb2f7
+            0287f584 f702a3aa 9d7fb467 89fb0174
+            fb0095fb 028f7ba1 7286817b 67858b5a
+            b38708f8 45fd0215 0b 
+          </CharString>
+          <CharString index="16">
+            500 0 rmoveto
+            return
+          </CharString>
+          <CharString index="17" raw="1">
+            0b   
+          </CharString>
+        </Subrs>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          -91 callsubr
+          -91 callsubr
+          endchar
+        </CharString>
+        <CharString name="g1">
+          -107 callsubr
+          -106 callsubr
+          endchar
+        </CharString>
+        <CharString name="g10">
+          -106 callsubr
+          -107 callsubr
+          endchar
+        </CharString>
+        <CharString name="g11">
+          -106 callsubr
+          -106 callsubr
+          endchar
+        </CharString>
+        <CharString name="g12">
+          -106 callsubr
+          -105 callsubr
+          endchar
+        </CharString>
+        <CharString name="g13">
+          -106 callsubr
+          -104 callsubr
+          endchar
+        </CharString>
+        <CharString name="g14">
+          -106 callsubr
+          -103 callsubr
+          endchar
+        </CharString>
+        <CharString name="g15">
+          -106 callsubr
+          -102 callsubr
+          endchar
+        </CharString>
+        <CharString name="g16">
+          -106 callsubr
+          -101 callsubr
+          endchar
+        </CharString>
+        <CharString name="g17">
+          -106 callsubr
+          -100 callsubr
+          endchar
+        </CharString>
+        <CharString name="g18">
+          -106 callsubr
+          -99 callsubr
+          endchar
+        </CharString>
+        <CharString name="g19">
+          -106 callsubr
+          -98 callsubr
+          endchar
+        </CharString>
+        <CharString name="g2">
+          -107 callsubr
+          -105 callsubr
+          endchar
+        </CharString>
+        <CharString name="g20">
+          -105 callsubr
+          -107 callsubr
+          endchar
+        </CharString>
+        <CharString name="g21">
+          -105 callsubr
+          -106 callsubr
+          endchar
+        </CharString>
+        <CharString name="g22">
+          -105 callsubr
+          -105 callsubr
+          endchar
+        </CharString>
+        <CharString name="g23">
+          -105 callsubr
+          -104 callsubr
+          endchar
+        </CharString>
+        <CharString name="g24">
+          -105 callsubr
+          -103 callsubr
+          endchar
+        </CharString>
+        <CharString name="g25">
+          -105 callsubr
+          -102 callsubr
+          endchar
+        </CharString>
+        <CharString name="g26">
+          -105 callsubr
+          -101 callsubr
+          endchar
+        </CharString>
+        <CharString name="g27">
+          -105 callsubr
+          -100 callsubr
+          endchar
+        </CharString>
+        <CharString name="g28">
+          -105 callsubr
+          -99 callsubr
+          endchar
+        </CharString>
+        <CharString name="g29">
+          -105 callsubr
+          -98 callsubr
+          endchar
+        </CharString>
+        <CharString name="g3">
+          -107 callsubr
+          -104 callsubr
+          endchar
+        </CharString>
+        <CharString name="g30">
+          -104 callsubr
+          -107 callsubr
+          endchar
+        </CharString>
+        <CharString name="g31">
+          -104 callsubr
+          -106 callsubr
+          endchar
+        </CharString>
+        <CharString name="g32">
+          -104 callsubr
+          -105 callsubr
+          endchar
+        </CharString>
+        <CharString name="g33">
+          -104 callsubr
+          -104 callsubr
+          endchar
+        </CharString>
+        <CharString name="g34">
+          -104 callsubr
+          -103 callsubr
+          endchar
+        </CharString>
+        <CharString name="g35">
+          -104 callsubr
+          -102 callsubr
+          endchar
+        </CharString>
+        <CharString name="g36">
+          -104 callsubr
+          -101 callsubr
+          endchar
+        </CharString>
+        <CharString name="g37">
+          -104 callsubr
+          -100 callsubr
+          endchar
+        </CharString>
+        <CharString name="g38">
+          -104 callsubr
+          -99 callsubr
+          endchar
+        </CharString>
+        <CharString name="g39">
+          -104 callsubr
+          -98 callsubr
+          endchar
+        </CharString>
+        <CharString name="g4">
+          -107 callsubr
+          -103 callsubr
+          endchar
+        </CharString>
+        <CharString name="g40">
+          -103 callsubr
+          -107 callsubr
+          endchar
+        </CharString>
+        <CharString name="g41">
+          -103 callsubr
+          -106 callsubr
+          endchar
+        </CharString>
+        <CharString name="g42">
+          -103 callsubr
+          -105 callsubr
+          endchar
+        </CharString>
+        <CharString name="g43">
+          -103 callsubr
+          -104 callsubr
+          endchar
+        </CharString>
+        <CharString name="g44">
+          -103 callsubr
+          -103 callsubr
+          endchar
+        </CharString>
+        <CharString name="g45">
+          -103 callsubr
+          -102 callsubr
+          endchar
+        </CharString>
+        <CharString name="g46">
+          -103 callsubr
+          -101 callsubr
+          endchar
+        </CharString>
+        <CharString name="g47">
+          -103 callsubr
+          -100 callsubr
+          endchar
+        </CharString>
+        <CharString name="g48">
+          -103 callsubr
+          -99 callsubr
+          endchar
+        </CharString>
+        <CharString name="g49">
+          -103 callsubr
+          -98 callsubr
+          endchar
+        </CharString>
+        <CharString name="g5">
+          -107 callsubr
+          -102 callsubr
+          endchar
+        </CharString>
+        <CharString name="g50">
+          -102 callsubr
+          -107 callsubr
+          endchar
+        </CharString>
+        <CharString name="g51">
+          -102 callsubr
+          -106 callsubr
+          endchar
+        </CharString>
+        <CharString name="g52">
+          -102 callsubr
+          -105 callsubr
+          endchar
+        </CharString>
+        <CharString name="g53">
+          -102 callsubr
+          -104 callsubr
+          endchar
+        </CharString>
+        <CharString name="g54">
+          -102 callsubr
+          -103 callsubr
+          endchar
+        </CharString>
+        <CharString name="g55">
+          -102 callsubr
+          -102 callsubr
+          endchar
+        </CharString>
+        <CharString name="g56">
+          -102 callsubr
+          -101 callsubr
+          endchar
+        </CharString>
+        <CharString name="g57">
+          -102 callsubr
+          -100 callsubr
+          endchar
+        </CharString>
+        <CharString name="g58">
+          -102 callsubr
+          -99 callsubr
+          endchar
+        </CharString>
+        <CharString name="g59">
+          -102 callsubr
+          -98 callsubr
+          endchar
+        </CharString>
+        <CharString name="g6">
+          -107 callsubr
+          -101 callsubr
+          endchar
+        </CharString>
+        <CharString name="g60">
+          -101 callsubr
+          -107 callsubr
+          endchar
+        </CharString>
+        <CharString name="g61">
+          -101 callsubr
+          -106 callsubr
+          endchar
+        </CharString>
+        <CharString name="g62">
+          -101 callsubr
+          -105 callsubr
+          endchar
+        </CharString>
+        <CharString name="g63">
+          -101 callsubr
+          -104 callsubr
+          endchar
+        </CharString>
+        <CharString name="g64">
+          -101 callsubr
+          -103 callsubr
+          endchar
+        </CharString>
+        <CharString name="g65">
+          -101 callsubr
+          -102 callsubr
+          endchar
+        </CharString>
+        <CharString name="g66">
+          -101 callsubr
+          -101 callsubr
+          endchar
+        </CharString>
+        <CharString name="g67">
+          -101 callsubr
+          -100 callsubr
+          endchar
+        </CharString>
+        <CharString name="g68">
+          -101 callsubr
+          -99 callsubr
+          endchar
+        </CharString>
+        <CharString name="g69">
+          -101 callsubr
+          -98 callsubr
+          endchar
+        </CharString>
+        <CharString name="g7">
+          -107 callsubr
+          -100 callsubr
+          endchar
+        </CharString>
+        <CharString name="g70">
+          -100 callsubr
+          -107 callsubr
+          endchar
+        </CharString>
+        <CharString name="g71">
+          -100 callsubr
+          -106 callsubr
+          endchar
+        </CharString>
+        <CharString name="g72">
+          -100 callsubr
+          -105 callsubr
+          endchar
+        </CharString>
+        <CharString name="g73">
+          -100 callsubr
+          -104 callsubr
+          endchar
+        </CharString>
+        <CharString name="g74">
+          -100 callsubr
+          -103 callsubr
+          endchar
+        </CharString>
+        <CharString name="g75">
+          -100 callsubr
+          -102 callsubr
+          endchar
+        </CharString>
+        <CharString name="g76">
+          -100 callsubr
+          -101 callsubr
+          endchar
+        </CharString>
+        <CharString name="g77">
+          -100 callsubr
+          -100 callsubr
+          endchar
+        </CharString>
+        <CharString name="g78">
+          -100 callsubr
+          -99 callsubr
+          endchar
+        </CharString>
+        <CharString name="g79">
+          -100 callsubr
+          -98 callsubr
+          endchar
+        </CharString>
+        <CharString name="g8">
+          -107 callsubr
+          -99 callsubr
+          endchar
+        </CharString>
+        <CharString name="g80">
+          -99 callsubr
+          -107 callsubr
+          endchar
+        </CharString>
+        <CharString name="g81">
+          -99 callsubr
+          -106 callsubr
+          endchar
+        </CharString>
+        <CharString name="g82">
+          -99 callsubr
+          -105 callsubr
+          endchar
+        </CharString>
+        <CharString name="g83">
+          -99 callsubr
+          -104 callsubr
+          endchar
+        </CharString>
+        <CharString name="g84">
+          -99 callsubr
+          -103 callsubr
+          endchar
+        </CharString>
+        <CharString name="g85">
+          -99 callsubr
+          -102 callsubr
+          endchar
+        </CharString>
+        <CharString name="g86">
+          -99 callsubr
+          -101 callsubr
+          endchar
+        </CharString>
+        <CharString name="g87">
+          -99 callsubr
+          -100 callsubr
+          endchar
+        </CharString>
+        <CharString name="g88">
+          -99 callsubr
+          -99 callsubr
+          endchar
+        </CharString>
+        <CharString name="g89">
+          -99 callsubr
+          -98 callsubr
+          endchar
+        </CharString>
+        <CharString name="g9">
+          -107 callsubr
+          -98 callsubr
+          endchar
+        </CharString>
+        <CharString name="g90">
+          -98 callsubr
+          -107 callsubr
+          endchar
+        </CharString>
+        <CharString name="g91">
+          -98 callsubr
+          -106 callsubr
+          endchar
+        </CharString>
+        <CharString name="g92">
+          -98 callsubr
+          -105 callsubr
+          endchar
+        </CharString>
+        <CharString name="g93">
+          -98 callsubr
+          -104 callsubr
+          endchar
+        </CharString>
+        <CharString name="g94">
+          -98 callsubr
+          -103 callsubr
+          endchar
+        </CharString>
+        <CharString name="g95">
+          -98 callsubr
+          -102 callsubr
+          endchar
+        </CharString>
+        <CharString name="g96">
+          -98 callsubr
+          -101 callsubr
+          endchar
+        </CharString>
+        <CharString name="g97">
+          -98 callsubr
+          -100 callsubr
+          endchar
+        </CharString>
+        <CharString name="g98">
+          -98 callsubr
+          -99 callsubr
+          endchar
+        </CharString>
+        <CharString name="g99">
+          -98 callsubr
+          -98 callsubr
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/base.ttx.OS_2 b/Tests/ttLib/tables/data/aots/base.ttx.OS_2
new file mode 100644
index 0000000..ffd7124
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/base.ttx.OS_2
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="2"/>
+    <xAvgCharWidth value="2500"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001100"/>
+    <ySubscriptXSize value="500"/>
+    <ySubscriptYSize value="500"/>
+    <ySubscriptXOffset value="250"/>
+    <ySubscriptYOffset value="50"/>
+    <ySuperscriptXSize value="500"/>
+    <ySuperscriptYSize value="500"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="500"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="500"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="10"/>
+      <bWeight value="6"/>
+      <bProportion value="3"/>
+      <bContrast value="6"/>
+      <bStrokeVariation value="5"/>
+      <bArmStyle value="11"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBE"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="65"/>
+    <usLastCharIndex value="80"/>
+    <sTypoAscender value="2500"/>
+    <sTypoDescender value="0"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="2500"/>
+    <usWinDescent value="0"/>
+    <ulCodePageRange1 value="11100000 00111111 00000001 11111111"/>
+    <ulCodePageRange2 value="11111111 11111111 00000000 00000000"/>
+    <sxHeight value="2500"/>
+    <sCapHeight value="2500"/>
+    <usDefaultChar value="65"/>
+    <usBreakChar value="65"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/base.ttx.cmap b/Tests/ttLib/tables/data/aots/base.ttx.cmap
new file mode 100644
index 0000000..0f3e822
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/base.ttx.cmap
@@ -0,0 +1,210 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x1" name="g1"/><!-- ???? -->
+      <map code="0x2" name="g2"/><!-- ???? -->
+      <map code="0x3" name="g3"/><!-- ???? -->
+      <map code="0x4" name="g4"/><!-- ???? -->
+      <map code="0x5" name="g5"/><!-- ???? -->
+      <map code="0x6" name="g6"/><!-- ???? -->
+      <map code="0x7" name="g7"/><!-- ???? -->
+      <map code="0x8" name="g8"/><!-- ???? -->
+      <map code="0x9" name="g9"/><!-- ???? -->
+      <map code="0xa" name="g10"/><!-- ???? -->
+      <map code="0xb" name="g11"/><!-- ???? -->
+      <map code="0xc" name="g12"/><!-- ???? -->
+      <map code="0xd" name="g13"/><!-- ???? -->
+      <map code="0xe" name="g14"/><!-- ???? -->
+      <map code="0xf" name="g15"/><!-- ???? -->
+      <map code="0x10" name="g16"/><!-- ???? -->
+      <map code="0x11" name="g17"/><!-- ???? -->
+      <map code="0x12" name="g18"/><!-- ???? -->
+      <map code="0x13" name="g19"/><!-- ???? -->
+      <map code="0x14" name="g20"/><!-- ???? -->
+      <map code="0x15" name="g21"/><!-- ???? -->
+      <map code="0x16" name="g22"/><!-- ???? -->
+      <map code="0x17" name="g23"/><!-- ???? -->
+      <map code="0x18" name="g24"/><!-- ???? -->
+      <map code="0x19" name="g25"/><!-- ???? -->
+      <map code="0x1a" name="g26"/><!-- ???? -->
+      <map code="0x1b" name="g27"/><!-- ???? -->
+      <map code="0x1c" name="g28"/><!-- ???? -->
+      <map code="0x1d" name="g29"/><!-- ???? -->
+      <map code="0x1e" name="g30"/><!-- ???? -->
+      <map code="0x1f" name="g31"/><!-- ???? -->
+      <map code="0x20" name="g32"/><!-- SPACE -->
+      <map code="0x21" name="g33"/><!-- EXCLAMATION MARK -->
+      <map code="0x22" name="g34"/><!-- QUOTATION MARK -->
+      <map code="0x23" name="g35"/><!-- NUMBER SIGN -->
+      <map code="0x24" name="g36"/><!-- DOLLAR SIGN -->
+      <map code="0x25" name="g37"/><!-- PERCENT SIGN -->
+      <map code="0x26" name="g38"/><!-- AMPERSAND -->
+      <map code="0x27" name="g39"/><!-- APOSTROPHE -->
+      <map code="0x28" name="g40"/><!-- LEFT PARENTHESIS -->
+      <map code="0x29" name="g41"/><!-- RIGHT PARENTHESIS -->
+      <map code="0x2a" name="g42"/><!-- ASTERISK -->
+      <map code="0x2b" name="g43"/><!-- PLUS SIGN -->
+      <map code="0x2c" name="g44"/><!-- COMMA -->
+      <map code="0x2d" name="g45"/><!-- HYPHEN-MINUS -->
+      <map code="0x2e" name="g46"/><!-- FULL STOP -->
+      <map code="0x2f" name="g47"/><!-- SOLIDUS -->
+      <map code="0x30" name="g48"/><!-- DIGIT ZERO -->
+      <map code="0x31" name="g49"/><!-- DIGIT ONE -->
+      <map code="0x32" name="g50"/><!-- DIGIT TWO -->
+      <map code="0x33" name="g51"/><!-- DIGIT THREE -->
+      <map code="0x34" name="g52"/><!-- DIGIT FOUR -->
+      <map code="0x35" name="g53"/><!-- DIGIT FIVE -->
+      <map code="0x36" name="g54"/><!-- DIGIT SIX -->
+      <map code="0x37" name="g55"/><!-- DIGIT SEVEN -->
+      <map code="0x38" name="g56"/><!-- DIGIT EIGHT -->
+      <map code="0x39" name="g57"/><!-- DIGIT NINE -->
+      <map code="0x3a" name="g58"/><!-- COLON -->
+      <map code="0x3b" name="g59"/><!-- SEMICOLON -->
+      <map code="0x3c" name="g60"/><!-- LESS-THAN SIGN -->
+      <map code="0x3d" name="g61"/><!-- EQUALS SIGN -->
+      <map code="0x3e" name="g62"/><!-- GREATER-THAN SIGN -->
+      <map code="0x3f" name="g63"/><!-- QUESTION MARK -->
+      <map code="0x40" name="g64"/><!-- COMMERCIAL AT -->
+      <map code="0x41" name="g65"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x42" name="g66"/><!-- LATIN CAPITAL LETTER B -->
+      <map code="0x43" name="g67"/><!-- LATIN CAPITAL LETTER C -->
+      <map code="0x44" name="g68"/><!-- LATIN CAPITAL LETTER D -->
+      <map code="0x45" name="g69"/><!-- LATIN CAPITAL LETTER E -->
+      <map code="0x46" name="g70"/><!-- LATIN CAPITAL LETTER F -->
+      <map code="0x47" name="g71"/><!-- LATIN CAPITAL LETTER G -->
+      <map code="0x48" name="g72"/><!-- LATIN CAPITAL LETTER H -->
+      <map code="0x49" name="g73"/><!-- LATIN CAPITAL LETTER I -->
+      <map code="0x4a" name="g74"/><!-- LATIN CAPITAL LETTER J -->
+      <map code="0x4b" name="g75"/><!-- LATIN CAPITAL LETTER K -->
+      <map code="0x4c" name="g76"/><!-- LATIN CAPITAL LETTER L -->
+      <map code="0x4d" name="g77"/><!-- LATIN CAPITAL LETTER M -->
+      <map code="0x4e" name="g78"/><!-- LATIN CAPITAL LETTER N -->
+      <map code="0x4f" name="g79"/><!-- LATIN CAPITAL LETTER O -->
+      <map code="0x50" name="g80"/><!-- LATIN CAPITAL LETTER P -->
+      <map code="0x51" name="g81"/><!-- LATIN CAPITAL LETTER Q -->
+      <map code="0x52" name="g82"/><!-- LATIN CAPITAL LETTER R -->
+      <map code="0x53" name="g83"/><!-- LATIN CAPITAL LETTER S -->
+      <map code="0x54" name="g84"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0x55" name="g85"/><!-- LATIN CAPITAL LETTER U -->
+      <map code="0x56" name="g86"/><!-- LATIN CAPITAL LETTER V -->
+      <map code="0x57" name="g87"/><!-- LATIN CAPITAL LETTER W -->
+      <map code="0x58" name="g88"/><!-- LATIN CAPITAL LETTER X -->
+      <map code="0x59" name="g89"/><!-- LATIN CAPITAL LETTER Y -->
+      <map code="0x5a" name="g90"/><!-- LATIN CAPITAL LETTER Z -->
+      <map code="0x5b" name="g91"/><!-- LEFT SQUARE BRACKET -->
+      <map code="0x5c" name="g92"/><!-- REVERSE SOLIDUS -->
+      <map code="0x5d" name="g93"/><!-- RIGHT SQUARE BRACKET -->
+      <map code="0x5e" name="g94"/><!-- CIRCUMFLEX ACCENT -->
+      <map code="0x5f" name="g95"/><!-- LOW LINE -->
+      <map code="0x60" name="g96"/><!-- GRAVE ACCENT -->
+      <map code="0x61" name="g97"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x62" name="g98"/><!-- LATIN SMALL LETTER B -->
+      <map code="0x63" name="g99"/><!-- LATIN SMALL LETTER C -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="28" language="0" nGroups="1">
+      <map code="0x1" name="g1"/><!-- ???? -->
+      <map code="0x2" name="g2"/><!-- ???? -->
+      <map code="0x3" name="g3"/><!-- ???? -->
+      <map code="0x4" name="g4"/><!-- ???? -->
+      <map code="0x5" name="g5"/><!-- ???? -->
+      <map code="0x6" name="g6"/><!-- ???? -->
+      <map code="0x7" name="g7"/><!-- ???? -->
+      <map code="0x8" name="g8"/><!-- ???? -->
+      <map code="0x9" name="g9"/><!-- ???? -->
+      <map code="0xa" name="g10"/><!-- ???? -->
+      <map code="0xb" name="g11"/><!-- ???? -->
+      <map code="0xc" name="g12"/><!-- ???? -->
+      <map code="0xd" name="g13"/><!-- ???? -->
+      <map code="0xe" name="g14"/><!-- ???? -->
+      <map code="0xf" name="g15"/><!-- ???? -->
+      <map code="0x10" name="g16"/><!-- ???? -->
+      <map code="0x11" name="g17"/><!-- ???? -->
+      <map code="0x12" name="g18"/><!-- ???? -->
+      <map code="0x13" name="g19"/><!-- ???? -->
+      <map code="0x14" name="g20"/><!-- ???? -->
+      <map code="0x15" name="g21"/><!-- ???? -->
+      <map code="0x16" name="g22"/><!-- ???? -->
+      <map code="0x17" name="g23"/><!-- ???? -->
+      <map code="0x18" name="g24"/><!-- ???? -->
+      <map code="0x19" name="g25"/><!-- ???? -->
+      <map code="0x1a" name="g26"/><!-- ???? -->
+      <map code="0x1b" name="g27"/><!-- ???? -->
+      <map code="0x1c" name="g28"/><!-- ???? -->
+      <map code="0x1d" name="g29"/><!-- ???? -->
+      <map code="0x1e" name="g30"/><!-- ???? -->
+      <map code="0x1f" name="g31"/><!-- ???? -->
+      <map code="0x20" name="g32"/><!-- SPACE -->
+      <map code="0x21" name="g33"/><!-- EXCLAMATION MARK -->
+      <map code="0x22" name="g34"/><!-- QUOTATION MARK -->
+      <map code="0x23" name="g35"/><!-- NUMBER SIGN -->
+      <map code="0x24" name="g36"/><!-- DOLLAR SIGN -->
+      <map code="0x25" name="g37"/><!-- PERCENT SIGN -->
+      <map code="0x26" name="g38"/><!-- AMPERSAND -->
+      <map code="0x27" name="g39"/><!-- APOSTROPHE -->
+      <map code="0x28" name="g40"/><!-- LEFT PARENTHESIS -->
+      <map code="0x29" name="g41"/><!-- RIGHT PARENTHESIS -->
+      <map code="0x2a" name="g42"/><!-- ASTERISK -->
+      <map code="0x2b" name="g43"/><!-- PLUS SIGN -->
+      <map code="0x2c" name="g44"/><!-- COMMA -->
+      <map code="0x2d" name="g45"/><!-- HYPHEN-MINUS -->
+      <map code="0x2e" name="g46"/><!-- FULL STOP -->
+      <map code="0x2f" name="g47"/><!-- SOLIDUS -->
+      <map code="0x30" name="g48"/><!-- DIGIT ZERO -->
+      <map code="0x31" name="g49"/><!-- DIGIT ONE -->
+      <map code="0x32" name="g50"/><!-- DIGIT TWO -->
+      <map code="0x33" name="g51"/><!-- DIGIT THREE -->
+      <map code="0x34" name="g52"/><!-- DIGIT FOUR -->
+      <map code="0x35" name="g53"/><!-- DIGIT FIVE -->
+      <map code="0x36" name="g54"/><!-- DIGIT SIX -->
+      <map code="0x37" name="g55"/><!-- DIGIT SEVEN -->
+      <map code="0x38" name="g56"/><!-- DIGIT EIGHT -->
+      <map code="0x39" name="g57"/><!-- DIGIT NINE -->
+      <map code="0x3a" name="g58"/><!-- COLON -->
+      <map code="0x3b" name="g59"/><!-- SEMICOLON -->
+      <map code="0x3c" name="g60"/><!-- LESS-THAN SIGN -->
+      <map code="0x3d" name="g61"/><!-- EQUALS SIGN -->
+      <map code="0x3e" name="g62"/><!-- GREATER-THAN SIGN -->
+      <map code="0x3f" name="g63"/><!-- QUESTION MARK -->
+      <map code="0x40" name="g64"/><!-- COMMERCIAL AT -->
+      <map code="0x41" name="g65"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x42" name="g66"/><!-- LATIN CAPITAL LETTER B -->
+      <map code="0x43" name="g67"/><!-- LATIN CAPITAL LETTER C -->
+      <map code="0x44" name="g68"/><!-- LATIN CAPITAL LETTER D -->
+      <map code="0x45" name="g69"/><!-- LATIN CAPITAL LETTER E -->
+      <map code="0x46" name="g70"/><!-- LATIN CAPITAL LETTER F -->
+      <map code="0x47" name="g71"/><!-- LATIN CAPITAL LETTER G -->
+      <map code="0x48" name="g72"/><!-- LATIN CAPITAL LETTER H -->
+      <map code="0x49" name="g73"/><!-- LATIN CAPITAL LETTER I -->
+      <map code="0x4a" name="g74"/><!-- LATIN CAPITAL LETTER J -->
+      <map code="0x4b" name="g75"/><!-- LATIN CAPITAL LETTER K -->
+      <map code="0x4c" name="g76"/><!-- LATIN CAPITAL LETTER L -->
+      <map code="0x4d" name="g77"/><!-- LATIN CAPITAL LETTER M -->
+      <map code="0x4e" name="g78"/><!-- LATIN CAPITAL LETTER N -->
+      <map code="0x4f" name="g79"/><!-- LATIN CAPITAL LETTER O -->
+      <map code="0x50" name="g80"/><!-- LATIN CAPITAL LETTER P -->
+      <map code="0x51" name="g81"/><!-- LATIN CAPITAL LETTER Q -->
+      <map code="0x52" name="g82"/><!-- LATIN CAPITAL LETTER R -->
+      <map code="0x53" name="g83"/><!-- LATIN CAPITAL LETTER S -->
+      <map code="0x54" name="g84"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0x55" name="g85"/><!-- LATIN CAPITAL LETTER U -->
+      <map code="0x56" name="g86"/><!-- LATIN CAPITAL LETTER V -->
+      <map code="0x57" name="g87"/><!-- LATIN CAPITAL LETTER W -->
+      <map code="0x58" name="g88"/><!-- LATIN CAPITAL LETTER X -->
+      <map code="0x59" name="g89"/><!-- LATIN CAPITAL LETTER Y -->
+      <map code="0x5a" name="g90"/><!-- LATIN CAPITAL LETTER Z -->
+      <map code="0x5b" name="g91"/><!-- LEFT SQUARE BRACKET -->
+      <map code="0x5c" name="g92"/><!-- REVERSE SOLIDUS -->
+      <map code="0x5d" name="g93"/><!-- RIGHT SQUARE BRACKET -->
+      <map code="0x5e" name="g94"/><!-- CIRCUMFLEX ACCENT -->
+      <map code="0x5f" name="g95"/><!-- LOW LINE -->
+      <map code="0x60" name="g96"/><!-- GRAVE ACCENT -->
+      <map code="0x61" name="g97"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x62" name="g98"/><!-- LATIN SMALL LETTER B -->
+      <map code="0x63" name="g99"/><!-- LATIN SMALL LETTER C -->
+    </cmap_format_12>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/base.ttx.head b/Tests/ttLib/tables/data/aots/base.ttx.head
new file mode 100644
index 0000000..8ac917a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/base.ttx.head
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x3a1f8a27"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000001"/>
+    <unitsPerEm value="1500"/>
+    <created value="Thu Jan  1 00:00:00 1970"/>
+    <modified value="Thu Jan  1 00:00:00 1970"/>
+    <xMin value="0"/>
+    <yMin value="-10"/>
+    <xMax value="2500"/>
+    <yMax value="2150"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/base.ttx.hhea b/Tests/ttLib/tables/data/aots/base.ttx.hhea
new file mode 100644
index 0000000..ce8b339
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/base.ttx.hhea
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="2500"/>
+    <descent value="0"/>
+    <lineGap value="200"/>
+    <advanceWidthMax value="2500"/>
+    <minLeftSideBearing value="300"/>
+    <minRightSideBearing value="300"/>
+    <xMaxExtent value="2500"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="1"/>
+  </hhea>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/base.ttx.hmtx b/Tests/ttLib/tables/data/aots/base.ttx.hmtx
new file mode 100644
index 0000000..0efe1f9
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/base.ttx.hmtx
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <hmtx>
+    <mtx name=".notdef" width="1500" lsb="300"/>
+    <mtx name="g1" width="1500" lsb="300"/>
+    <mtx name="g10" width="1500" lsb="300"/>
+    <mtx name="g11" width="1500" lsb="300"/>
+    <mtx name="g12" width="1500" lsb="300"/>
+    <mtx name="g13" width="1500" lsb="300"/>
+    <mtx name="g14" width="1500" lsb="300"/>
+    <mtx name="g15" width="1500" lsb="300"/>
+    <mtx name="g16" width="1500" lsb="300"/>
+    <mtx name="g17" width="1500" lsb="300"/>
+    <mtx name="g18" width="1500" lsb="300"/>
+    <mtx name="g19" width="1500" lsb="300"/>
+    <mtx name="g2" width="1500" lsb="300"/>
+    <mtx name="g20" width="1500" lsb="300"/>
+    <mtx name="g21" width="1500" lsb="300"/>
+    <mtx name="g22" width="1500" lsb="300"/>
+    <mtx name="g23" width="1500" lsb="300"/>
+    <mtx name="g24" width="1500" lsb="300"/>
+    <mtx name="g25" width="1500" lsb="300"/>
+    <mtx name="g26" width="1500" lsb="300"/>
+    <mtx name="g27" width="1500" lsb="300"/>
+    <mtx name="g28" width="1500" lsb="300"/>
+    <mtx name="g29" width="1500" lsb="300"/>
+    <mtx name="g3" width="1500" lsb="300"/>
+    <mtx name="g30" width="1500" lsb="300"/>
+    <mtx name="g31" width="1500" lsb="300"/>
+    <mtx name="g32" width="1500" lsb="300"/>
+    <mtx name="g33" width="1500" lsb="300"/>
+    <mtx name="g34" width="1500" lsb="300"/>
+    <mtx name="g35" width="1500" lsb="300"/>
+    <mtx name="g36" width="1500" lsb="300"/>
+    <mtx name="g37" width="1500" lsb="300"/>
+    <mtx name="g38" width="1500" lsb="300"/>
+    <mtx name="g39" width="1500" lsb="300"/>
+    <mtx name="g4" width="1500" lsb="300"/>
+    <mtx name="g40" width="1500" lsb="300"/>
+    <mtx name="g41" width="1500" lsb="300"/>
+    <mtx name="g42" width="1500" lsb="300"/>
+    <mtx name="g43" width="1500" lsb="300"/>
+    <mtx name="g44" width="1500" lsb="300"/>
+    <mtx name="g45" width="1500" lsb="300"/>
+    <mtx name="g46" width="1500" lsb="300"/>
+    <mtx name="g47" width="1500" lsb="300"/>
+    <mtx name="g48" width="1500" lsb="300"/>
+    <mtx name="g49" width="1500" lsb="300"/>
+    <mtx name="g5" width="1500" lsb="300"/>
+    <mtx name="g50" width="1500" lsb="300"/>
+    <mtx name="g51" width="1500" lsb="300"/>
+    <mtx name="g52" width="1500" lsb="300"/>
+    <mtx name="g53" width="1500" lsb="300"/>
+    <mtx name="g54" width="1500" lsb="300"/>
+    <mtx name="g55" width="1500" lsb="300"/>
+    <mtx name="g56" width="1500" lsb="300"/>
+    <mtx name="g57" width="1500" lsb="300"/>
+    <mtx name="g58" width="1500" lsb="300"/>
+    <mtx name="g59" width="1500" lsb="300"/>
+    <mtx name="g6" width="1500" lsb="300"/>
+    <mtx name="g60" width="1500" lsb="300"/>
+    <mtx name="g61" width="1500" lsb="300"/>
+    <mtx name="g62" width="1500" lsb="300"/>
+    <mtx name="g63" width="1500" lsb="300"/>
+    <mtx name="g64" width="1500" lsb="300"/>
+    <mtx name="g65" width="1500" lsb="300"/>
+    <mtx name="g66" width="1500" lsb="300"/>
+    <mtx name="g67" width="1500" lsb="300"/>
+    <mtx name="g68" width="1500" lsb="300"/>
+    <mtx name="g69" width="1500" lsb="300"/>
+    <mtx name="g7" width="1500" lsb="300"/>
+    <mtx name="g70" width="1500" lsb="300"/>
+    <mtx name="g71" width="1500" lsb="300"/>
+    <mtx name="g72" width="1500" lsb="300"/>
+    <mtx name="g73" width="1500" lsb="300"/>
+    <mtx name="g74" width="1500" lsb="300"/>
+    <mtx name="g75" width="1500" lsb="300"/>
+    <mtx name="g76" width="1500" lsb="300"/>
+    <mtx name="g77" width="1500" lsb="300"/>
+    <mtx name="g78" width="1500" lsb="300"/>
+    <mtx name="g79" width="1500" lsb="300"/>
+    <mtx name="g8" width="1500" lsb="300"/>
+    <mtx name="g80" width="1500" lsb="300"/>
+    <mtx name="g81" width="1500" lsb="300"/>
+    <mtx name="g82" width="1500" lsb="300"/>
+    <mtx name="g83" width="1500" lsb="300"/>
+    <mtx name="g84" width="1500" lsb="300"/>
+    <mtx name="g85" width="1500" lsb="300"/>
+    <mtx name="g86" width="1500" lsb="300"/>
+    <mtx name="g87" width="1500" lsb="300"/>
+    <mtx name="g88" width="1500" lsb="300"/>
+    <mtx name="g89" width="1500" lsb="300"/>
+    <mtx name="g9" width="1500" lsb="300"/>
+    <mtx name="g90" width="1500" lsb="300"/>
+    <mtx name="g91" width="1500" lsb="300"/>
+    <mtx name="g92" width="1500" lsb="300"/>
+    <mtx name="g93" width="1500" lsb="300"/>
+    <mtx name="g94" width="1500" lsb="300"/>
+    <mtx name="g95" width="1500" lsb="300"/>
+    <mtx name="g96" width="1500" lsb="300"/>
+    <mtx name="g97" width="1500" lsb="300"/>
+    <mtx name="g98" width="1500" lsb="300"/>
+    <mtx name="g99" width="1500" lsb="300"/>
+  </hmtx>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/base.ttx.maxp b/Tests/ttLib/tables/data/aots/base.ttx.maxp
new file mode 100644
index 0000000..408b67e
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/base.ttx.maxp
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="100"/>
+  </maxp>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/base.ttx.name b/Tests/ttLib/tables/data/aots/base.ttx.name
new file mode 100644
index 0000000..6e54516
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/base.ttx.name
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      base
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      base
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      base
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version1.0
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      base
+    </namerecord>
+  </name>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/base.ttx.post b/Tests/ttLib/tables/data/aots/base.ttx.post
new file mode 100644
index 0000000..b042c3a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/base.ttx.post
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-100"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/classdef1_font1.otf b/Tests/ttLib/tables/data/aots/classdef1_font1.otf
new file mode 100644
index 0000000..f0add69
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/classdef1_font1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/classdef1_font1.ttx.GSUB b/Tests/ttLib/tables/data/aots/classdef1_font1.ttx.GSUB
new file mode 100644
index 0000000..21f1ab1
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/classdef1_font1.ttx.GSUB
@@ -0,0 +1,483 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="3"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=4 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in=".notdef" out="g3"/>
+          <Substitution in="g1" out="g4"/>
+          <Substitution in="g10" out="g13"/>
+          <Substitution in="g11" out="g14"/>
+          <Substitution in="g12" out="g15"/>
+          <Substitution in="g13" out="g16"/>
+          <Substitution in="g14" out="g17"/>
+          <Substitution in="g15" out="g18"/>
+          <Substitution in="g16" out="g19"/>
+          <Substitution in="g17" out="g20"/>
+          <Substitution in="g18" out="g21"/>
+          <Substitution in="g19" out="g22"/>
+          <Substitution in="g2" out="g5"/>
+          <Substitution in="g20" out="g23"/>
+          <Substitution in="g21" out="g24"/>
+          <Substitution in="g22" out="g25"/>
+          <Substitution in="g23" out="g26"/>
+          <Substitution in="g24" out="g27"/>
+          <Substitution in="g25" out="g28"/>
+          <Substitution in="g26" out="g29"/>
+          <Substitution in="g27" out="g30"/>
+          <Substitution in="g28" out="g31"/>
+          <Substitution in="g29" out="g32"/>
+          <Substitution in="g3" out="g6"/>
+          <Substitution in="g30" out="g33"/>
+          <Substitution in="g31" out="g34"/>
+          <Substitution in="g32" out="g35"/>
+          <Substitution in="g33" out="g36"/>
+          <Substitution in="g34" out="g37"/>
+          <Substitution in="g35" out="g38"/>
+          <Substitution in="g36" out="g39"/>
+          <Substitution in="g37" out="g40"/>
+          <Substitution in="g38" out="g41"/>
+          <Substitution in="g39" out="g42"/>
+          <Substitution in="g4" out="g7"/>
+          <Substitution in="g40" out="g43"/>
+          <Substitution in="g41" out="g44"/>
+          <Substitution in="g42" out="g45"/>
+          <Substitution in="g43" out="g46"/>
+          <Substitution in="g44" out="g47"/>
+          <Substitution in="g45" out="g48"/>
+          <Substitution in="g46" out="g49"/>
+          <Substitution in="g47" out="g50"/>
+          <Substitution in="g48" out="g51"/>
+          <Substitution in="g49" out="g52"/>
+          <Substitution in="g5" out="g8"/>
+          <Substitution in="g50" out="g53"/>
+          <Substitution in="g51" out="g54"/>
+          <Substitution in="g52" out="g55"/>
+          <Substitution in="g53" out="g56"/>
+          <Substitution in="g54" out="g57"/>
+          <Substitution in="g55" out="g58"/>
+          <Substitution in="g56" out="g59"/>
+          <Substitution in="g57" out="g60"/>
+          <Substitution in="g58" out="g61"/>
+          <Substitution in="g59" out="g62"/>
+          <Substitution in="g6" out="g9"/>
+          <Substitution in="g60" out="g63"/>
+          <Substitution in="g61" out="g64"/>
+          <Substitution in="g62" out="g65"/>
+          <Substitution in="g63" out="g66"/>
+          <Substitution in="g64" out="g67"/>
+          <Substitution in="g65" out="g68"/>
+          <Substitution in="g66" out="g69"/>
+          <Substitution in="g67" out="g70"/>
+          <Substitution in="g68" out="g71"/>
+          <Substitution in="g69" out="g72"/>
+          <Substitution in="g7" out="g10"/>
+          <Substitution in="g70" out="g73"/>
+          <Substitution in="g71" out="g74"/>
+          <Substitution in="g72" out="g75"/>
+          <Substitution in="g73" out="g76"/>
+          <Substitution in="g74" out="g77"/>
+          <Substitution in="g75" out="g78"/>
+          <Substitution in="g76" out="g79"/>
+          <Substitution in="g77" out="g80"/>
+          <Substitution in="g78" out="g81"/>
+          <Substitution in="g79" out="g82"/>
+          <Substitution in="g8" out="g11"/>
+          <Substitution in="g80" out="g83"/>
+          <Substitution in="g81" out="g84"/>
+          <Substitution in="g82" out="g85"/>
+          <Substitution in="g83" out="g86"/>
+          <Substitution in="g84" out="g87"/>
+          <Substitution in="g85" out="g88"/>
+          <Substitution in="g86" out="g89"/>
+          <Substitution in="g87" out="g90"/>
+          <Substitution in="g88" out="g91"/>
+          <Substitution in="g89" out="g92"/>
+          <Substitution in="g9" out="g12"/>
+          <Substitution in="g90" out="g93"/>
+          <Substitution in="g91" out="g94"/>
+          <Substitution in="g92" out="g95"/>
+          <Substitution in="g93" out="g96"/>
+          <Substitution in="g94" out="g97"/>
+          <Substitution in="g95" out="g98"/>
+          <Substitution in="g96" out="g99"/>
+          <Substitution in="g97" out="glyph00100"/>
+          <Substitution in="g98" out="glyph00101"/>
+          <Substitution in="g99" out="glyph00102"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in=".notdef" out="g4"/>
+          <Substitution in="g1" out="g5"/>
+          <Substitution in="g10" out="g14"/>
+          <Substitution in="g11" out="g15"/>
+          <Substitution in="g12" out="g16"/>
+          <Substitution in="g13" out="g17"/>
+          <Substitution in="g14" out="g18"/>
+          <Substitution in="g15" out="g19"/>
+          <Substitution in="g16" out="g20"/>
+          <Substitution in="g17" out="g21"/>
+          <Substitution in="g18" out="g22"/>
+          <Substitution in="g19" out="g23"/>
+          <Substitution in="g2" out="g6"/>
+          <Substitution in="g20" out="g24"/>
+          <Substitution in="g21" out="g25"/>
+          <Substitution in="g22" out="g26"/>
+          <Substitution in="g23" out="g27"/>
+          <Substitution in="g24" out="g28"/>
+          <Substitution in="g25" out="g29"/>
+          <Substitution in="g26" out="g30"/>
+          <Substitution in="g27" out="g31"/>
+          <Substitution in="g28" out="g32"/>
+          <Substitution in="g29" out="g33"/>
+          <Substitution in="g3" out="g7"/>
+          <Substitution in="g30" out="g34"/>
+          <Substitution in="g31" out="g35"/>
+          <Substitution in="g32" out="g36"/>
+          <Substitution in="g33" out="g37"/>
+          <Substitution in="g34" out="g38"/>
+          <Substitution in="g35" out="g39"/>
+          <Substitution in="g36" out="g40"/>
+          <Substitution in="g37" out="g41"/>
+          <Substitution in="g38" out="g42"/>
+          <Substitution in="g39" out="g43"/>
+          <Substitution in="g4" out="g8"/>
+          <Substitution in="g40" out="g44"/>
+          <Substitution in="g41" out="g45"/>
+          <Substitution in="g42" out="g46"/>
+          <Substitution in="g43" out="g47"/>
+          <Substitution in="g44" out="g48"/>
+          <Substitution in="g45" out="g49"/>
+          <Substitution in="g46" out="g50"/>
+          <Substitution in="g47" out="g51"/>
+          <Substitution in="g48" out="g52"/>
+          <Substitution in="g49" out="g53"/>
+          <Substitution in="g5" out="g9"/>
+          <Substitution in="g50" out="g54"/>
+          <Substitution in="g51" out="g55"/>
+          <Substitution in="g52" out="g56"/>
+          <Substitution in="g53" out="g57"/>
+          <Substitution in="g54" out="g58"/>
+          <Substitution in="g55" out="g59"/>
+          <Substitution in="g56" out="g60"/>
+          <Substitution in="g57" out="g61"/>
+          <Substitution in="g58" out="g62"/>
+          <Substitution in="g59" out="g63"/>
+          <Substitution in="g6" out="g10"/>
+          <Substitution in="g60" out="g64"/>
+          <Substitution in="g61" out="g65"/>
+          <Substitution in="g62" out="g66"/>
+          <Substitution in="g63" out="g67"/>
+          <Substitution in="g64" out="g68"/>
+          <Substitution in="g65" out="g69"/>
+          <Substitution in="g66" out="g70"/>
+          <Substitution in="g67" out="g71"/>
+          <Substitution in="g68" out="g72"/>
+          <Substitution in="g69" out="g73"/>
+          <Substitution in="g7" out="g11"/>
+          <Substitution in="g70" out="g74"/>
+          <Substitution in="g71" out="g75"/>
+          <Substitution in="g72" out="g76"/>
+          <Substitution in="g73" out="g77"/>
+          <Substitution in="g74" out="g78"/>
+          <Substitution in="g75" out="g79"/>
+          <Substitution in="g76" out="g80"/>
+          <Substitution in="g77" out="g81"/>
+          <Substitution in="g78" out="g82"/>
+          <Substitution in="g79" out="g83"/>
+          <Substitution in="g8" out="g12"/>
+          <Substitution in="g80" out="g84"/>
+          <Substitution in="g81" out="g85"/>
+          <Substitution in="g82" out="g86"/>
+          <Substitution in="g83" out="g87"/>
+          <Substitution in="g84" out="g88"/>
+          <Substitution in="g85" out="g89"/>
+          <Substitution in="g86" out="g90"/>
+          <Substitution in="g87" out="g91"/>
+          <Substitution in="g88" out="g92"/>
+          <Substitution in="g89" out="g93"/>
+          <Substitution in="g9" out="g13"/>
+          <Substitution in="g90" out="g94"/>
+          <Substitution in="g91" out="g95"/>
+          <Substitution in="g92" out="g96"/>
+          <Substitution in="g93" out="g97"/>
+          <Substitution in="g94" out="g98"/>
+          <Substitution in="g95" out="g99"/>
+          <Substitution in="g96" out="glyph00100"/>
+          <Substitution in="g97" out="glyph00101"/>
+          <Substitution in="g98" out="glyph00102"/>
+          <Substitution in="g99" out="glyph00103"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in=".notdef" out="g5"/>
+          <Substitution in="g1" out="g6"/>
+          <Substitution in="g10" out="g15"/>
+          <Substitution in="g11" out="g16"/>
+          <Substitution in="g12" out="g17"/>
+          <Substitution in="g13" out="g18"/>
+          <Substitution in="g14" out="g19"/>
+          <Substitution in="g15" out="g20"/>
+          <Substitution in="g16" out="g21"/>
+          <Substitution in="g17" out="g22"/>
+          <Substitution in="g18" out="g23"/>
+          <Substitution in="g19" out="g24"/>
+          <Substitution in="g2" out="g7"/>
+          <Substitution in="g20" out="g25"/>
+          <Substitution in="g21" out="g26"/>
+          <Substitution in="g22" out="g27"/>
+          <Substitution in="g23" out="g28"/>
+          <Substitution in="g24" out="g29"/>
+          <Substitution in="g25" out="g30"/>
+          <Substitution in="g26" out="g31"/>
+          <Substitution in="g27" out="g32"/>
+          <Substitution in="g28" out="g33"/>
+          <Substitution in="g29" out="g34"/>
+          <Substitution in="g3" out="g8"/>
+          <Substitution in="g30" out="g35"/>
+          <Substitution in="g31" out="g36"/>
+          <Substitution in="g32" out="g37"/>
+          <Substitution in="g33" out="g38"/>
+          <Substitution in="g34" out="g39"/>
+          <Substitution in="g35" out="g40"/>
+          <Substitution in="g36" out="g41"/>
+          <Substitution in="g37" out="g42"/>
+          <Substitution in="g38" out="g43"/>
+          <Substitution in="g39" out="g44"/>
+          <Substitution in="g4" out="g9"/>
+          <Substitution in="g40" out="g45"/>
+          <Substitution in="g41" out="g46"/>
+          <Substitution in="g42" out="g47"/>
+          <Substitution in="g43" out="g48"/>
+          <Substitution in="g44" out="g49"/>
+          <Substitution in="g45" out="g50"/>
+          <Substitution in="g46" out="g51"/>
+          <Substitution in="g47" out="g52"/>
+          <Substitution in="g48" out="g53"/>
+          <Substitution in="g49" out="g54"/>
+          <Substitution in="g5" out="g10"/>
+          <Substitution in="g50" out="g55"/>
+          <Substitution in="g51" out="g56"/>
+          <Substitution in="g52" out="g57"/>
+          <Substitution in="g53" out="g58"/>
+          <Substitution in="g54" out="g59"/>
+          <Substitution in="g55" out="g60"/>
+          <Substitution in="g56" out="g61"/>
+          <Substitution in="g57" out="g62"/>
+          <Substitution in="g58" out="g63"/>
+          <Substitution in="g59" out="g64"/>
+          <Substitution in="g6" out="g11"/>
+          <Substitution in="g60" out="g65"/>
+          <Substitution in="g61" out="g66"/>
+          <Substitution in="g62" out="g67"/>
+          <Substitution in="g63" out="g68"/>
+          <Substitution in="g64" out="g69"/>
+          <Substitution in="g65" out="g70"/>
+          <Substitution in="g66" out="g71"/>
+          <Substitution in="g67" out="g72"/>
+          <Substitution in="g68" out="g73"/>
+          <Substitution in="g69" out="g74"/>
+          <Substitution in="g7" out="g12"/>
+          <Substitution in="g70" out="g75"/>
+          <Substitution in="g71" out="g76"/>
+          <Substitution in="g72" out="g77"/>
+          <Substitution in="g73" out="g78"/>
+          <Substitution in="g74" out="g79"/>
+          <Substitution in="g75" out="g80"/>
+          <Substitution in="g76" out="g81"/>
+          <Substitution in="g77" out="g82"/>
+          <Substitution in="g78" out="g83"/>
+          <Substitution in="g79" out="g84"/>
+          <Substitution in="g8" out="g13"/>
+          <Substitution in="g80" out="g85"/>
+          <Substitution in="g81" out="g86"/>
+          <Substitution in="g82" out="g87"/>
+          <Substitution in="g83" out="g88"/>
+          <Substitution in="g84" out="g89"/>
+          <Substitution in="g85" out="g90"/>
+          <Substitution in="g86" out="g91"/>
+          <Substitution in="g87" out="g92"/>
+          <Substitution in="g88" out="g93"/>
+          <Substitution in="g89" out="g94"/>
+          <Substitution in="g9" out="g14"/>
+          <Substitution in="g90" out="g95"/>
+          <Substitution in="g91" out="g96"/>
+          <Substitution in="g92" out="g97"/>
+          <Substitution in="g93" out="g98"/>
+          <Substitution in="g94" out="g99"/>
+          <Substitution in="g95" out="glyph00100"/>
+          <Substitution in="g96" out="glyph00101"/>
+          <Substitution in="g97" out="glyph00102"/>
+          <Substitution in="g98" out="glyph00103"/>
+          <Substitution in="g99" out="glyph00104"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value=".notdef"/>
+            <Glyph value="g1"/>
+            <Glyph value="g2"/>
+            <Glyph value="g3"/>
+            <Glyph value="g4"/>
+            <Glyph value="g5"/>
+            <Glyph value="g6"/>
+            <Glyph value="g7"/>
+            <Glyph value="g8"/>
+            <Glyph value="g9"/>
+            <Glyph value="g10"/>
+            <Glyph value="g11"/>
+            <Glyph value="g12"/>
+            <Glyph value="g13"/>
+            <Glyph value="g14"/>
+            <Glyph value="g15"/>
+            <Glyph value="g16"/>
+            <Glyph value="g17"/>
+            <Glyph value="g18"/>
+            <Glyph value="g19"/>
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+            <Glyph value="g30"/>
+            <Glyph value="g31"/>
+            <Glyph value="g32"/>
+            <Glyph value="g33"/>
+            <Glyph value="g34"/>
+            <Glyph value="g35"/>
+            <Glyph value="g36"/>
+            <Glyph value="g37"/>
+            <Glyph value="g38"/>
+            <Glyph value="g39"/>
+            <Glyph value="g40"/>
+            <Glyph value="g41"/>
+            <Glyph value="g42"/>
+            <Glyph value="g43"/>
+            <Glyph value="g44"/>
+            <Glyph value="g45"/>
+            <Glyph value="g46"/>
+            <Glyph value="g47"/>
+            <Glyph value="g48"/>
+            <Glyph value="g49"/>
+            <Glyph value="g50"/>
+            <Glyph value="g51"/>
+            <Glyph value="g52"/>
+            <Glyph value="g53"/>
+            <Glyph value="g54"/>
+            <Glyph value="g55"/>
+            <Glyph value="g56"/>
+            <Glyph value="g57"/>
+            <Glyph value="g58"/>
+            <Glyph value="g59"/>
+            <Glyph value="g60"/>
+            <Glyph value="g61"/>
+            <Glyph value="g62"/>
+            <Glyph value="g63"/>
+            <Glyph value="g64"/>
+            <Glyph value="g65"/>
+            <Glyph value="g66"/>
+            <Glyph value="g67"/>
+            <Glyph value="g68"/>
+            <Glyph value="g69"/>
+            <Glyph value="g70"/>
+            <Glyph value="g71"/>
+            <Glyph value="g72"/>
+            <Glyph value="g73"/>
+            <Glyph value="g74"/>
+            <Glyph value="g75"/>
+            <Glyph value="g76"/>
+            <Glyph value="g77"/>
+            <Glyph value="g78"/>
+            <Glyph value="g79"/>
+            <Glyph value="g80"/>
+            <Glyph value="g81"/>
+            <Glyph value="g82"/>
+            <Glyph value="g83"/>
+            <Glyph value="g84"/>
+            <Glyph value="g85"/>
+            <Glyph value="g86"/>
+            <Glyph value="g87"/>
+            <Glyph value="g88"/>
+            <Glyph value="g89"/>
+            <Glyph value="g90"/>
+            <Glyph value="g91"/>
+            <Glyph value="g92"/>
+            <Glyph value="g93"/>
+            <Glyph value="g94"/>
+            <Glyph value="g95"/>
+            <Glyph value="g96"/>
+            <Glyph value="g97"/>
+            <Glyph value="g98"/>
+            <Glyph value="g99"/>
+          </Coverage>
+          <ClassDef Format="1">
+            <ClassDef glyph="g18" class="1"/>
+            <ClassDef glyph="g19" class="1"/>
+            <ClassDef glyph="g20" class="1"/>
+          </ClassDef>
+          <!-- SubClassSetCount=2 -->
+          <SubClassSet index="0" empty="1"/>
+          <SubClassSet index="1">
+            <!-- SubClassRuleCount=1 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=1 -->
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="2"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+          </SubClassSet>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/classdef1_font2.otf b/Tests/ttLib/tables/data/aots/classdef1_font2.otf
new file mode 100644
index 0000000..f01876d
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/classdef1_font2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/classdef1_font2.ttx.GSUB b/Tests/ttLib/tables/data/aots/classdef1_font2.ttx.GSUB
new file mode 100644
index 0000000..ec7278e
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/classdef1_font2.ttx.GSUB
@@ -0,0 +1,494 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="3"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=4 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in=".notdef" out="g3"/>
+          <Substitution in="g1" out="g4"/>
+          <Substitution in="g10" out="g13"/>
+          <Substitution in="g11" out="g14"/>
+          <Substitution in="g12" out="g15"/>
+          <Substitution in="g13" out="g16"/>
+          <Substitution in="g14" out="g17"/>
+          <Substitution in="g15" out="g18"/>
+          <Substitution in="g16" out="g19"/>
+          <Substitution in="g17" out="g20"/>
+          <Substitution in="g18" out="g21"/>
+          <Substitution in="g19" out="g22"/>
+          <Substitution in="g2" out="g5"/>
+          <Substitution in="g20" out="g23"/>
+          <Substitution in="g21" out="g24"/>
+          <Substitution in="g22" out="g25"/>
+          <Substitution in="g23" out="g26"/>
+          <Substitution in="g24" out="g27"/>
+          <Substitution in="g25" out="g28"/>
+          <Substitution in="g26" out="g29"/>
+          <Substitution in="g27" out="g30"/>
+          <Substitution in="g28" out="g31"/>
+          <Substitution in="g29" out="g32"/>
+          <Substitution in="g3" out="g6"/>
+          <Substitution in="g30" out="g33"/>
+          <Substitution in="g31" out="g34"/>
+          <Substitution in="g32" out="g35"/>
+          <Substitution in="g33" out="g36"/>
+          <Substitution in="g34" out="g37"/>
+          <Substitution in="g35" out="g38"/>
+          <Substitution in="g36" out="g39"/>
+          <Substitution in="g37" out="g40"/>
+          <Substitution in="g38" out="g41"/>
+          <Substitution in="g39" out="g42"/>
+          <Substitution in="g4" out="g7"/>
+          <Substitution in="g40" out="g43"/>
+          <Substitution in="g41" out="g44"/>
+          <Substitution in="g42" out="g45"/>
+          <Substitution in="g43" out="g46"/>
+          <Substitution in="g44" out="g47"/>
+          <Substitution in="g45" out="g48"/>
+          <Substitution in="g46" out="g49"/>
+          <Substitution in="g47" out="g50"/>
+          <Substitution in="g48" out="g51"/>
+          <Substitution in="g49" out="g52"/>
+          <Substitution in="g5" out="g8"/>
+          <Substitution in="g50" out="g53"/>
+          <Substitution in="g51" out="g54"/>
+          <Substitution in="g52" out="g55"/>
+          <Substitution in="g53" out="g56"/>
+          <Substitution in="g54" out="g57"/>
+          <Substitution in="g55" out="g58"/>
+          <Substitution in="g56" out="g59"/>
+          <Substitution in="g57" out="g60"/>
+          <Substitution in="g58" out="g61"/>
+          <Substitution in="g59" out="g62"/>
+          <Substitution in="g6" out="g9"/>
+          <Substitution in="g60" out="g63"/>
+          <Substitution in="g61" out="g64"/>
+          <Substitution in="g62" out="g65"/>
+          <Substitution in="g63" out="g66"/>
+          <Substitution in="g64" out="g67"/>
+          <Substitution in="g65" out="g68"/>
+          <Substitution in="g66" out="g69"/>
+          <Substitution in="g67" out="g70"/>
+          <Substitution in="g68" out="g71"/>
+          <Substitution in="g69" out="g72"/>
+          <Substitution in="g7" out="g10"/>
+          <Substitution in="g70" out="g73"/>
+          <Substitution in="g71" out="g74"/>
+          <Substitution in="g72" out="g75"/>
+          <Substitution in="g73" out="g76"/>
+          <Substitution in="g74" out="g77"/>
+          <Substitution in="g75" out="g78"/>
+          <Substitution in="g76" out="g79"/>
+          <Substitution in="g77" out="g80"/>
+          <Substitution in="g78" out="g81"/>
+          <Substitution in="g79" out="g82"/>
+          <Substitution in="g8" out="g11"/>
+          <Substitution in="g80" out="g83"/>
+          <Substitution in="g81" out="g84"/>
+          <Substitution in="g82" out="g85"/>
+          <Substitution in="g83" out="g86"/>
+          <Substitution in="g84" out="g87"/>
+          <Substitution in="g85" out="g88"/>
+          <Substitution in="g86" out="g89"/>
+          <Substitution in="g87" out="g90"/>
+          <Substitution in="g88" out="g91"/>
+          <Substitution in="g89" out="g92"/>
+          <Substitution in="g9" out="g12"/>
+          <Substitution in="g90" out="g93"/>
+          <Substitution in="g91" out="g94"/>
+          <Substitution in="g92" out="g95"/>
+          <Substitution in="g93" out="g96"/>
+          <Substitution in="g94" out="g97"/>
+          <Substitution in="g95" out="g98"/>
+          <Substitution in="g96" out="g99"/>
+          <Substitution in="g97" out="glyph00100"/>
+          <Substitution in="g98" out="glyph00101"/>
+          <Substitution in="g99" out="glyph00102"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in=".notdef" out="g4"/>
+          <Substitution in="g1" out="g5"/>
+          <Substitution in="g10" out="g14"/>
+          <Substitution in="g11" out="g15"/>
+          <Substitution in="g12" out="g16"/>
+          <Substitution in="g13" out="g17"/>
+          <Substitution in="g14" out="g18"/>
+          <Substitution in="g15" out="g19"/>
+          <Substitution in="g16" out="g20"/>
+          <Substitution in="g17" out="g21"/>
+          <Substitution in="g18" out="g22"/>
+          <Substitution in="g19" out="g23"/>
+          <Substitution in="g2" out="g6"/>
+          <Substitution in="g20" out="g24"/>
+          <Substitution in="g21" out="g25"/>
+          <Substitution in="g22" out="g26"/>
+          <Substitution in="g23" out="g27"/>
+          <Substitution in="g24" out="g28"/>
+          <Substitution in="g25" out="g29"/>
+          <Substitution in="g26" out="g30"/>
+          <Substitution in="g27" out="g31"/>
+          <Substitution in="g28" out="g32"/>
+          <Substitution in="g29" out="g33"/>
+          <Substitution in="g3" out="g7"/>
+          <Substitution in="g30" out="g34"/>
+          <Substitution in="g31" out="g35"/>
+          <Substitution in="g32" out="g36"/>
+          <Substitution in="g33" out="g37"/>
+          <Substitution in="g34" out="g38"/>
+          <Substitution in="g35" out="g39"/>
+          <Substitution in="g36" out="g40"/>
+          <Substitution in="g37" out="g41"/>
+          <Substitution in="g38" out="g42"/>
+          <Substitution in="g39" out="g43"/>
+          <Substitution in="g4" out="g8"/>
+          <Substitution in="g40" out="g44"/>
+          <Substitution in="g41" out="g45"/>
+          <Substitution in="g42" out="g46"/>
+          <Substitution in="g43" out="g47"/>
+          <Substitution in="g44" out="g48"/>
+          <Substitution in="g45" out="g49"/>
+          <Substitution in="g46" out="g50"/>
+          <Substitution in="g47" out="g51"/>
+          <Substitution in="g48" out="g52"/>
+          <Substitution in="g49" out="g53"/>
+          <Substitution in="g5" out="g9"/>
+          <Substitution in="g50" out="g54"/>
+          <Substitution in="g51" out="g55"/>
+          <Substitution in="g52" out="g56"/>
+          <Substitution in="g53" out="g57"/>
+          <Substitution in="g54" out="g58"/>
+          <Substitution in="g55" out="g59"/>
+          <Substitution in="g56" out="g60"/>
+          <Substitution in="g57" out="g61"/>
+          <Substitution in="g58" out="g62"/>
+          <Substitution in="g59" out="g63"/>
+          <Substitution in="g6" out="g10"/>
+          <Substitution in="g60" out="g64"/>
+          <Substitution in="g61" out="g65"/>
+          <Substitution in="g62" out="g66"/>
+          <Substitution in="g63" out="g67"/>
+          <Substitution in="g64" out="g68"/>
+          <Substitution in="g65" out="g69"/>
+          <Substitution in="g66" out="g70"/>
+          <Substitution in="g67" out="g71"/>
+          <Substitution in="g68" out="g72"/>
+          <Substitution in="g69" out="g73"/>
+          <Substitution in="g7" out="g11"/>
+          <Substitution in="g70" out="g74"/>
+          <Substitution in="g71" out="g75"/>
+          <Substitution in="g72" out="g76"/>
+          <Substitution in="g73" out="g77"/>
+          <Substitution in="g74" out="g78"/>
+          <Substitution in="g75" out="g79"/>
+          <Substitution in="g76" out="g80"/>
+          <Substitution in="g77" out="g81"/>
+          <Substitution in="g78" out="g82"/>
+          <Substitution in="g79" out="g83"/>
+          <Substitution in="g8" out="g12"/>
+          <Substitution in="g80" out="g84"/>
+          <Substitution in="g81" out="g85"/>
+          <Substitution in="g82" out="g86"/>
+          <Substitution in="g83" out="g87"/>
+          <Substitution in="g84" out="g88"/>
+          <Substitution in="g85" out="g89"/>
+          <Substitution in="g86" out="g90"/>
+          <Substitution in="g87" out="g91"/>
+          <Substitution in="g88" out="g92"/>
+          <Substitution in="g89" out="g93"/>
+          <Substitution in="g9" out="g13"/>
+          <Substitution in="g90" out="g94"/>
+          <Substitution in="g91" out="g95"/>
+          <Substitution in="g92" out="g96"/>
+          <Substitution in="g93" out="g97"/>
+          <Substitution in="g94" out="g98"/>
+          <Substitution in="g95" out="g99"/>
+          <Substitution in="g96" out="glyph00100"/>
+          <Substitution in="g97" out="glyph00101"/>
+          <Substitution in="g98" out="glyph00102"/>
+          <Substitution in="g99" out="glyph00103"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in=".notdef" out="g5"/>
+          <Substitution in="g1" out="g6"/>
+          <Substitution in="g10" out="g15"/>
+          <Substitution in="g11" out="g16"/>
+          <Substitution in="g12" out="g17"/>
+          <Substitution in="g13" out="g18"/>
+          <Substitution in="g14" out="g19"/>
+          <Substitution in="g15" out="g20"/>
+          <Substitution in="g16" out="g21"/>
+          <Substitution in="g17" out="g22"/>
+          <Substitution in="g18" out="g23"/>
+          <Substitution in="g19" out="g24"/>
+          <Substitution in="g2" out="g7"/>
+          <Substitution in="g20" out="g25"/>
+          <Substitution in="g21" out="g26"/>
+          <Substitution in="g22" out="g27"/>
+          <Substitution in="g23" out="g28"/>
+          <Substitution in="g24" out="g29"/>
+          <Substitution in="g25" out="g30"/>
+          <Substitution in="g26" out="g31"/>
+          <Substitution in="g27" out="g32"/>
+          <Substitution in="g28" out="g33"/>
+          <Substitution in="g29" out="g34"/>
+          <Substitution in="g3" out="g8"/>
+          <Substitution in="g30" out="g35"/>
+          <Substitution in="g31" out="g36"/>
+          <Substitution in="g32" out="g37"/>
+          <Substitution in="g33" out="g38"/>
+          <Substitution in="g34" out="g39"/>
+          <Substitution in="g35" out="g40"/>
+          <Substitution in="g36" out="g41"/>
+          <Substitution in="g37" out="g42"/>
+          <Substitution in="g38" out="g43"/>
+          <Substitution in="g39" out="g44"/>
+          <Substitution in="g4" out="g9"/>
+          <Substitution in="g40" out="g45"/>
+          <Substitution in="g41" out="g46"/>
+          <Substitution in="g42" out="g47"/>
+          <Substitution in="g43" out="g48"/>
+          <Substitution in="g44" out="g49"/>
+          <Substitution in="g45" out="g50"/>
+          <Substitution in="g46" out="g51"/>
+          <Substitution in="g47" out="g52"/>
+          <Substitution in="g48" out="g53"/>
+          <Substitution in="g49" out="g54"/>
+          <Substitution in="g5" out="g10"/>
+          <Substitution in="g50" out="g55"/>
+          <Substitution in="g51" out="g56"/>
+          <Substitution in="g52" out="g57"/>
+          <Substitution in="g53" out="g58"/>
+          <Substitution in="g54" out="g59"/>
+          <Substitution in="g55" out="g60"/>
+          <Substitution in="g56" out="g61"/>
+          <Substitution in="g57" out="g62"/>
+          <Substitution in="g58" out="g63"/>
+          <Substitution in="g59" out="g64"/>
+          <Substitution in="g6" out="g11"/>
+          <Substitution in="g60" out="g65"/>
+          <Substitution in="g61" out="g66"/>
+          <Substitution in="g62" out="g67"/>
+          <Substitution in="g63" out="g68"/>
+          <Substitution in="g64" out="g69"/>
+          <Substitution in="g65" out="g70"/>
+          <Substitution in="g66" out="g71"/>
+          <Substitution in="g67" out="g72"/>
+          <Substitution in="g68" out="g73"/>
+          <Substitution in="g69" out="g74"/>
+          <Substitution in="g7" out="g12"/>
+          <Substitution in="g70" out="g75"/>
+          <Substitution in="g71" out="g76"/>
+          <Substitution in="g72" out="g77"/>
+          <Substitution in="g73" out="g78"/>
+          <Substitution in="g74" out="g79"/>
+          <Substitution in="g75" out="g80"/>
+          <Substitution in="g76" out="g81"/>
+          <Substitution in="g77" out="g82"/>
+          <Substitution in="g78" out="g83"/>
+          <Substitution in="g79" out="g84"/>
+          <Substitution in="g8" out="g13"/>
+          <Substitution in="g80" out="g85"/>
+          <Substitution in="g81" out="g86"/>
+          <Substitution in="g82" out="g87"/>
+          <Substitution in="g83" out="g88"/>
+          <Substitution in="g84" out="g89"/>
+          <Substitution in="g85" out="g90"/>
+          <Substitution in="g86" out="g91"/>
+          <Substitution in="g87" out="g92"/>
+          <Substitution in="g88" out="g93"/>
+          <Substitution in="g89" out="g94"/>
+          <Substitution in="g9" out="g14"/>
+          <Substitution in="g90" out="g95"/>
+          <Substitution in="g91" out="g96"/>
+          <Substitution in="g92" out="g97"/>
+          <Substitution in="g93" out="g98"/>
+          <Substitution in="g94" out="g99"/>
+          <Substitution in="g95" out="glyph00100"/>
+          <Substitution in="g96" out="glyph00101"/>
+          <Substitution in="g97" out="glyph00102"/>
+          <Substitution in="g98" out="glyph00103"/>
+          <Substitution in="g99" out="glyph00104"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value=".notdef"/>
+            <Glyph value="g1"/>
+            <Glyph value="g2"/>
+            <Glyph value="g3"/>
+            <Glyph value="g4"/>
+            <Glyph value="g5"/>
+            <Glyph value="g6"/>
+            <Glyph value="g7"/>
+            <Glyph value="g8"/>
+            <Glyph value="g9"/>
+            <Glyph value="g10"/>
+            <Glyph value="g11"/>
+            <Glyph value="g12"/>
+            <Glyph value="g13"/>
+            <Glyph value="g14"/>
+            <Glyph value="g15"/>
+            <Glyph value="g16"/>
+            <Glyph value="g17"/>
+            <Glyph value="g18"/>
+            <Glyph value="g19"/>
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+            <Glyph value="g30"/>
+            <Glyph value="g31"/>
+            <Glyph value="g32"/>
+            <Glyph value="g33"/>
+            <Glyph value="g34"/>
+            <Glyph value="g35"/>
+            <Glyph value="g36"/>
+            <Glyph value="g37"/>
+            <Glyph value="g38"/>
+            <Glyph value="g39"/>
+            <Glyph value="g40"/>
+            <Glyph value="g41"/>
+            <Glyph value="g42"/>
+            <Glyph value="g43"/>
+            <Glyph value="g44"/>
+            <Glyph value="g45"/>
+            <Glyph value="g46"/>
+            <Glyph value="g47"/>
+            <Glyph value="g48"/>
+            <Glyph value="g49"/>
+            <Glyph value="g50"/>
+            <Glyph value="g51"/>
+            <Glyph value="g52"/>
+            <Glyph value="g53"/>
+            <Glyph value="g54"/>
+            <Glyph value="g55"/>
+            <Glyph value="g56"/>
+            <Glyph value="g57"/>
+            <Glyph value="g58"/>
+            <Glyph value="g59"/>
+            <Glyph value="g60"/>
+            <Glyph value="g61"/>
+            <Glyph value="g62"/>
+            <Glyph value="g63"/>
+            <Glyph value="g64"/>
+            <Glyph value="g65"/>
+            <Glyph value="g66"/>
+            <Glyph value="g67"/>
+            <Glyph value="g68"/>
+            <Glyph value="g69"/>
+            <Glyph value="g70"/>
+            <Glyph value="g71"/>
+            <Glyph value="g72"/>
+            <Glyph value="g73"/>
+            <Glyph value="g74"/>
+            <Glyph value="g75"/>
+            <Glyph value="g76"/>
+            <Glyph value="g77"/>
+            <Glyph value="g78"/>
+            <Glyph value="g79"/>
+            <Glyph value="g80"/>
+            <Glyph value="g81"/>
+            <Glyph value="g82"/>
+            <Glyph value="g83"/>
+            <Glyph value="g84"/>
+            <Glyph value="g85"/>
+            <Glyph value="g86"/>
+            <Glyph value="g87"/>
+            <Glyph value="g88"/>
+            <Glyph value="g89"/>
+            <Glyph value="g90"/>
+            <Glyph value="g91"/>
+            <Glyph value="g92"/>
+            <Glyph value="g93"/>
+            <Glyph value="g94"/>
+            <Glyph value="g95"/>
+            <Glyph value="g96"/>
+            <Glyph value="g97"/>
+            <Glyph value="g98"/>
+            <Glyph value="g99"/>
+          </Coverage>
+          <ClassDef Format="1">
+            <ClassDef glyph="g18" class="2"/>
+            <ClassDef glyph="g19" class="2"/>
+            <ClassDef glyph="g20" class="2"/>
+          </ClassDef>
+          <!-- SubClassSetCount=3 -->
+          <SubClassSet index="0" empty="1"/>
+          <SubClassSet index="1">
+            <!-- SubClassRuleCount=1 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=1 -->
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="1"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+          </SubClassSet>
+          <SubClassSet index="2">
+            <!-- SubClassRuleCount=1 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=1 -->
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="2"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+          </SubClassSet>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/classdef1_font3.otf b/Tests/ttLib/tables/data/aots/classdef1_font3.otf
new file mode 100644
index 0000000..2a0f9cc
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/classdef1_font3.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/classdef1_font3.ttx.GSUB b/Tests/ttLib/tables/data/aots/classdef1_font3.ttx.GSUB
new file mode 100644
index 0000000..9bc1e43
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/classdef1_font3.ttx.GSUB
@@ -0,0 +1,510 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="3"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=4 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in=".notdef" out="g3"/>
+          <Substitution in="g1" out="g4"/>
+          <Substitution in="g10" out="g13"/>
+          <Substitution in="g11" out="g14"/>
+          <Substitution in="g12" out="g15"/>
+          <Substitution in="g13" out="g16"/>
+          <Substitution in="g14" out="g17"/>
+          <Substitution in="g15" out="g18"/>
+          <Substitution in="g16" out="g19"/>
+          <Substitution in="g17" out="g20"/>
+          <Substitution in="g18" out="g21"/>
+          <Substitution in="g19" out="g22"/>
+          <Substitution in="g2" out="g5"/>
+          <Substitution in="g20" out="g23"/>
+          <Substitution in="g21" out="g24"/>
+          <Substitution in="g22" out="g25"/>
+          <Substitution in="g23" out="g26"/>
+          <Substitution in="g24" out="g27"/>
+          <Substitution in="g25" out="g28"/>
+          <Substitution in="g26" out="g29"/>
+          <Substitution in="g27" out="g30"/>
+          <Substitution in="g28" out="g31"/>
+          <Substitution in="g29" out="g32"/>
+          <Substitution in="g3" out="g6"/>
+          <Substitution in="g30" out="g33"/>
+          <Substitution in="g31" out="g34"/>
+          <Substitution in="g32" out="g35"/>
+          <Substitution in="g33" out="g36"/>
+          <Substitution in="g34" out="g37"/>
+          <Substitution in="g35" out="g38"/>
+          <Substitution in="g36" out="g39"/>
+          <Substitution in="g37" out="g40"/>
+          <Substitution in="g38" out="g41"/>
+          <Substitution in="g39" out="g42"/>
+          <Substitution in="g4" out="g7"/>
+          <Substitution in="g40" out="g43"/>
+          <Substitution in="g41" out="g44"/>
+          <Substitution in="g42" out="g45"/>
+          <Substitution in="g43" out="g46"/>
+          <Substitution in="g44" out="g47"/>
+          <Substitution in="g45" out="g48"/>
+          <Substitution in="g46" out="g49"/>
+          <Substitution in="g47" out="g50"/>
+          <Substitution in="g48" out="g51"/>
+          <Substitution in="g49" out="g52"/>
+          <Substitution in="g5" out="g8"/>
+          <Substitution in="g50" out="g53"/>
+          <Substitution in="g51" out="g54"/>
+          <Substitution in="g52" out="g55"/>
+          <Substitution in="g53" out="g56"/>
+          <Substitution in="g54" out="g57"/>
+          <Substitution in="g55" out="g58"/>
+          <Substitution in="g56" out="g59"/>
+          <Substitution in="g57" out="g60"/>
+          <Substitution in="g58" out="g61"/>
+          <Substitution in="g59" out="g62"/>
+          <Substitution in="g6" out="g9"/>
+          <Substitution in="g60" out="g63"/>
+          <Substitution in="g61" out="g64"/>
+          <Substitution in="g62" out="g65"/>
+          <Substitution in="g63" out="g66"/>
+          <Substitution in="g64" out="g67"/>
+          <Substitution in="g65" out="g68"/>
+          <Substitution in="g66" out="g69"/>
+          <Substitution in="g67" out="g70"/>
+          <Substitution in="g68" out="g71"/>
+          <Substitution in="g69" out="g72"/>
+          <Substitution in="g7" out="g10"/>
+          <Substitution in="g70" out="g73"/>
+          <Substitution in="g71" out="g74"/>
+          <Substitution in="g72" out="g75"/>
+          <Substitution in="g73" out="g76"/>
+          <Substitution in="g74" out="g77"/>
+          <Substitution in="g75" out="g78"/>
+          <Substitution in="g76" out="g79"/>
+          <Substitution in="g77" out="g80"/>
+          <Substitution in="g78" out="g81"/>
+          <Substitution in="g79" out="g82"/>
+          <Substitution in="g8" out="g11"/>
+          <Substitution in="g80" out="g83"/>
+          <Substitution in="g81" out="g84"/>
+          <Substitution in="g82" out="g85"/>
+          <Substitution in="g83" out="g86"/>
+          <Substitution in="g84" out="g87"/>
+          <Substitution in="g85" out="g88"/>
+          <Substitution in="g86" out="g89"/>
+          <Substitution in="g87" out="g90"/>
+          <Substitution in="g88" out="g91"/>
+          <Substitution in="g89" out="g92"/>
+          <Substitution in="g9" out="g12"/>
+          <Substitution in="g90" out="g93"/>
+          <Substitution in="g91" out="g94"/>
+          <Substitution in="g92" out="g95"/>
+          <Substitution in="g93" out="g96"/>
+          <Substitution in="g94" out="g97"/>
+          <Substitution in="g95" out="g98"/>
+          <Substitution in="g96" out="g99"/>
+          <Substitution in="g97" out="glyph00100"/>
+          <Substitution in="g98" out="glyph00101"/>
+          <Substitution in="g99" out="glyph00102"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in=".notdef" out="g4"/>
+          <Substitution in="g1" out="g5"/>
+          <Substitution in="g10" out="g14"/>
+          <Substitution in="g11" out="g15"/>
+          <Substitution in="g12" out="g16"/>
+          <Substitution in="g13" out="g17"/>
+          <Substitution in="g14" out="g18"/>
+          <Substitution in="g15" out="g19"/>
+          <Substitution in="g16" out="g20"/>
+          <Substitution in="g17" out="g21"/>
+          <Substitution in="g18" out="g22"/>
+          <Substitution in="g19" out="g23"/>
+          <Substitution in="g2" out="g6"/>
+          <Substitution in="g20" out="g24"/>
+          <Substitution in="g21" out="g25"/>
+          <Substitution in="g22" out="g26"/>
+          <Substitution in="g23" out="g27"/>
+          <Substitution in="g24" out="g28"/>
+          <Substitution in="g25" out="g29"/>
+          <Substitution in="g26" out="g30"/>
+          <Substitution in="g27" out="g31"/>
+          <Substitution in="g28" out="g32"/>
+          <Substitution in="g29" out="g33"/>
+          <Substitution in="g3" out="g7"/>
+          <Substitution in="g30" out="g34"/>
+          <Substitution in="g31" out="g35"/>
+          <Substitution in="g32" out="g36"/>
+          <Substitution in="g33" out="g37"/>
+          <Substitution in="g34" out="g38"/>
+          <Substitution in="g35" out="g39"/>
+          <Substitution in="g36" out="g40"/>
+          <Substitution in="g37" out="g41"/>
+          <Substitution in="g38" out="g42"/>
+          <Substitution in="g39" out="g43"/>
+          <Substitution in="g4" out="g8"/>
+          <Substitution in="g40" out="g44"/>
+          <Substitution in="g41" out="g45"/>
+          <Substitution in="g42" out="g46"/>
+          <Substitution in="g43" out="g47"/>
+          <Substitution in="g44" out="g48"/>
+          <Substitution in="g45" out="g49"/>
+          <Substitution in="g46" out="g50"/>
+          <Substitution in="g47" out="g51"/>
+          <Substitution in="g48" out="g52"/>
+          <Substitution in="g49" out="g53"/>
+          <Substitution in="g5" out="g9"/>
+          <Substitution in="g50" out="g54"/>
+          <Substitution in="g51" out="g55"/>
+          <Substitution in="g52" out="g56"/>
+          <Substitution in="g53" out="g57"/>
+          <Substitution in="g54" out="g58"/>
+          <Substitution in="g55" out="g59"/>
+          <Substitution in="g56" out="g60"/>
+          <Substitution in="g57" out="g61"/>
+          <Substitution in="g58" out="g62"/>
+          <Substitution in="g59" out="g63"/>
+          <Substitution in="g6" out="g10"/>
+          <Substitution in="g60" out="g64"/>
+          <Substitution in="g61" out="g65"/>
+          <Substitution in="g62" out="g66"/>
+          <Substitution in="g63" out="g67"/>
+          <Substitution in="g64" out="g68"/>
+          <Substitution in="g65" out="g69"/>
+          <Substitution in="g66" out="g70"/>
+          <Substitution in="g67" out="g71"/>
+          <Substitution in="g68" out="g72"/>
+          <Substitution in="g69" out="g73"/>
+          <Substitution in="g7" out="g11"/>
+          <Substitution in="g70" out="g74"/>
+          <Substitution in="g71" out="g75"/>
+          <Substitution in="g72" out="g76"/>
+          <Substitution in="g73" out="g77"/>
+          <Substitution in="g74" out="g78"/>
+          <Substitution in="g75" out="g79"/>
+          <Substitution in="g76" out="g80"/>
+          <Substitution in="g77" out="g81"/>
+          <Substitution in="g78" out="g82"/>
+          <Substitution in="g79" out="g83"/>
+          <Substitution in="g8" out="g12"/>
+          <Substitution in="g80" out="g84"/>
+          <Substitution in="g81" out="g85"/>
+          <Substitution in="g82" out="g86"/>
+          <Substitution in="g83" out="g87"/>
+          <Substitution in="g84" out="g88"/>
+          <Substitution in="g85" out="g89"/>
+          <Substitution in="g86" out="g90"/>
+          <Substitution in="g87" out="g91"/>
+          <Substitution in="g88" out="g92"/>
+          <Substitution in="g89" out="g93"/>
+          <Substitution in="g9" out="g13"/>
+          <Substitution in="g90" out="g94"/>
+          <Substitution in="g91" out="g95"/>
+          <Substitution in="g92" out="g96"/>
+          <Substitution in="g93" out="g97"/>
+          <Substitution in="g94" out="g98"/>
+          <Substitution in="g95" out="g99"/>
+          <Substitution in="g96" out="glyph00100"/>
+          <Substitution in="g97" out="glyph00101"/>
+          <Substitution in="g98" out="glyph00102"/>
+          <Substitution in="g99" out="glyph00103"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in=".notdef" out="g5"/>
+          <Substitution in="g1" out="g6"/>
+          <Substitution in="g10" out="g15"/>
+          <Substitution in="g11" out="g16"/>
+          <Substitution in="g12" out="g17"/>
+          <Substitution in="g13" out="g18"/>
+          <Substitution in="g14" out="g19"/>
+          <Substitution in="g15" out="g20"/>
+          <Substitution in="g16" out="g21"/>
+          <Substitution in="g17" out="g22"/>
+          <Substitution in="g18" out="g23"/>
+          <Substitution in="g19" out="g24"/>
+          <Substitution in="g2" out="g7"/>
+          <Substitution in="g20" out="g25"/>
+          <Substitution in="g21" out="g26"/>
+          <Substitution in="g22" out="g27"/>
+          <Substitution in="g23" out="g28"/>
+          <Substitution in="g24" out="g29"/>
+          <Substitution in="g25" out="g30"/>
+          <Substitution in="g26" out="g31"/>
+          <Substitution in="g27" out="g32"/>
+          <Substitution in="g28" out="g33"/>
+          <Substitution in="g29" out="g34"/>
+          <Substitution in="g3" out="g8"/>
+          <Substitution in="g30" out="g35"/>
+          <Substitution in="g31" out="g36"/>
+          <Substitution in="g32" out="g37"/>
+          <Substitution in="g33" out="g38"/>
+          <Substitution in="g34" out="g39"/>
+          <Substitution in="g35" out="g40"/>
+          <Substitution in="g36" out="g41"/>
+          <Substitution in="g37" out="g42"/>
+          <Substitution in="g38" out="g43"/>
+          <Substitution in="g39" out="g44"/>
+          <Substitution in="g4" out="g9"/>
+          <Substitution in="g40" out="g45"/>
+          <Substitution in="g41" out="g46"/>
+          <Substitution in="g42" out="g47"/>
+          <Substitution in="g43" out="g48"/>
+          <Substitution in="g44" out="g49"/>
+          <Substitution in="g45" out="g50"/>
+          <Substitution in="g46" out="g51"/>
+          <Substitution in="g47" out="g52"/>
+          <Substitution in="g48" out="g53"/>
+          <Substitution in="g49" out="g54"/>
+          <Substitution in="g5" out="g10"/>
+          <Substitution in="g50" out="g55"/>
+          <Substitution in="g51" out="g56"/>
+          <Substitution in="g52" out="g57"/>
+          <Substitution in="g53" out="g58"/>
+          <Substitution in="g54" out="g59"/>
+          <Substitution in="g55" out="g60"/>
+          <Substitution in="g56" out="g61"/>
+          <Substitution in="g57" out="g62"/>
+          <Substitution in="g58" out="g63"/>
+          <Substitution in="g59" out="g64"/>
+          <Substitution in="g6" out="g11"/>
+          <Substitution in="g60" out="g65"/>
+          <Substitution in="g61" out="g66"/>
+          <Substitution in="g62" out="g67"/>
+          <Substitution in="g63" out="g68"/>
+          <Substitution in="g64" out="g69"/>
+          <Substitution in="g65" out="g70"/>
+          <Substitution in="g66" out="g71"/>
+          <Substitution in="g67" out="g72"/>
+          <Substitution in="g68" out="g73"/>
+          <Substitution in="g69" out="g74"/>
+          <Substitution in="g7" out="g12"/>
+          <Substitution in="g70" out="g75"/>
+          <Substitution in="g71" out="g76"/>
+          <Substitution in="g72" out="g77"/>
+          <Substitution in="g73" out="g78"/>
+          <Substitution in="g74" out="g79"/>
+          <Substitution in="g75" out="g80"/>
+          <Substitution in="g76" out="g81"/>
+          <Substitution in="g77" out="g82"/>
+          <Substitution in="g78" out="g83"/>
+          <Substitution in="g79" out="g84"/>
+          <Substitution in="g8" out="g13"/>
+          <Substitution in="g80" out="g85"/>
+          <Substitution in="g81" out="g86"/>
+          <Substitution in="g82" out="g87"/>
+          <Substitution in="g83" out="g88"/>
+          <Substitution in="g84" out="g89"/>
+          <Substitution in="g85" out="g90"/>
+          <Substitution in="g86" out="g91"/>
+          <Substitution in="g87" out="g92"/>
+          <Substitution in="g88" out="g93"/>
+          <Substitution in="g89" out="g94"/>
+          <Substitution in="g9" out="g14"/>
+          <Substitution in="g90" out="g95"/>
+          <Substitution in="g91" out="g96"/>
+          <Substitution in="g92" out="g97"/>
+          <Substitution in="g93" out="g98"/>
+          <Substitution in="g94" out="g99"/>
+          <Substitution in="g95" out="glyph00100"/>
+          <Substitution in="g96" out="glyph00101"/>
+          <Substitution in="g97" out="glyph00102"/>
+          <Substitution in="g98" out="glyph00103"/>
+          <Substitution in="g99" out="glyph00104"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value=".notdef"/>
+            <Glyph value="g1"/>
+            <Glyph value="g2"/>
+            <Glyph value="g3"/>
+            <Glyph value="g4"/>
+            <Glyph value="g5"/>
+            <Glyph value="g6"/>
+            <Glyph value="g7"/>
+            <Glyph value="g8"/>
+            <Glyph value="g9"/>
+            <Glyph value="g10"/>
+            <Glyph value="g11"/>
+            <Glyph value="g12"/>
+            <Glyph value="g13"/>
+            <Glyph value="g14"/>
+            <Glyph value="g15"/>
+            <Glyph value="g16"/>
+            <Glyph value="g17"/>
+            <Glyph value="g18"/>
+            <Glyph value="g19"/>
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+            <Glyph value="g30"/>
+            <Glyph value="g31"/>
+            <Glyph value="g32"/>
+            <Glyph value="g33"/>
+            <Glyph value="g34"/>
+            <Glyph value="g35"/>
+            <Glyph value="g36"/>
+            <Glyph value="g37"/>
+            <Glyph value="g38"/>
+            <Glyph value="g39"/>
+            <Glyph value="g40"/>
+            <Glyph value="g41"/>
+            <Glyph value="g42"/>
+            <Glyph value="g43"/>
+            <Glyph value="g44"/>
+            <Glyph value="g45"/>
+            <Glyph value="g46"/>
+            <Glyph value="g47"/>
+            <Glyph value="g48"/>
+            <Glyph value="g49"/>
+            <Glyph value="g50"/>
+            <Glyph value="g51"/>
+            <Glyph value="g52"/>
+            <Glyph value="g53"/>
+            <Glyph value="g54"/>
+            <Glyph value="g55"/>
+            <Glyph value="g56"/>
+            <Glyph value="g57"/>
+            <Glyph value="g58"/>
+            <Glyph value="g59"/>
+            <Glyph value="g60"/>
+            <Glyph value="g61"/>
+            <Glyph value="g62"/>
+            <Glyph value="g63"/>
+            <Glyph value="g64"/>
+            <Glyph value="g65"/>
+            <Glyph value="g66"/>
+            <Glyph value="g67"/>
+            <Glyph value="g68"/>
+            <Glyph value="g69"/>
+            <Glyph value="g70"/>
+            <Glyph value="g71"/>
+            <Glyph value="g72"/>
+            <Glyph value="g73"/>
+            <Glyph value="g74"/>
+            <Glyph value="g75"/>
+            <Glyph value="g76"/>
+            <Glyph value="g77"/>
+            <Glyph value="g78"/>
+            <Glyph value="g79"/>
+            <Glyph value="g80"/>
+            <Glyph value="g81"/>
+            <Glyph value="g82"/>
+            <Glyph value="g83"/>
+            <Glyph value="g84"/>
+            <Glyph value="g85"/>
+            <Glyph value="g86"/>
+            <Glyph value="g87"/>
+            <Glyph value="g88"/>
+            <Glyph value="g89"/>
+            <Glyph value="g90"/>
+            <Glyph value="g91"/>
+            <Glyph value="g92"/>
+            <Glyph value="g93"/>
+            <Glyph value="g94"/>
+            <Glyph value="g95"/>
+            <Glyph value="g96"/>
+            <Glyph value="g97"/>
+            <Glyph value="g98"/>
+            <Glyph value="g99"/>
+          </Coverage>
+          <ClassDef Format="1">
+            <ClassDef glyph="g18" class="2"/>
+            <ClassDef glyph="g19" class="2"/>
+            <ClassDef glyph="g20" class="2"/>
+            <ClassDef glyph="g22" class="1"/>
+            <ClassDef glyph="g23" class="1"/>
+            <ClassDef glyph="g24" class="1"/>
+            <ClassDef glyph="g29" class="2"/>
+            <ClassDef glyph="g33" class="1"/>
+            <ClassDef glyph="g34" class="1"/>
+          </ClassDef>
+          <!-- SubClassSetCount=3 -->
+          <SubClassSet index="0">
+            <!-- SubClassRuleCount=1 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=1 -->
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+          </SubClassSet>
+          <SubClassSet index="1">
+            <!-- SubClassRuleCount=1 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=1 -->
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="1"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+          </SubClassSet>
+          <SubClassSet index="2">
+            <!-- SubClassRuleCount=1 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=1 -->
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="2"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+          </SubClassSet>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/classdef1_font4.otf b/Tests/ttLib/tables/data/aots/classdef1_font4.otf
new file mode 100644
index 0000000..9c0f41c
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/classdef1_font4.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/classdef1_font4.ttx.GSUB b/Tests/ttLib/tables/data/aots/classdef1_font4.ttx.GSUB
new file mode 100644
index 0000000..681240d
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/classdef1_font4.ttx.GSUB
@@ -0,0 +1,469 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="3"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=4 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in=".notdef" out="g3"/>
+          <Substitution in="g1" out="g4"/>
+          <Substitution in="g10" out="g13"/>
+          <Substitution in="g11" out="g14"/>
+          <Substitution in="g12" out="g15"/>
+          <Substitution in="g13" out="g16"/>
+          <Substitution in="g14" out="g17"/>
+          <Substitution in="g15" out="g18"/>
+          <Substitution in="g16" out="g19"/>
+          <Substitution in="g17" out="g20"/>
+          <Substitution in="g18" out="g21"/>
+          <Substitution in="g19" out="g22"/>
+          <Substitution in="g2" out="g5"/>
+          <Substitution in="g20" out="g23"/>
+          <Substitution in="g21" out="g24"/>
+          <Substitution in="g22" out="g25"/>
+          <Substitution in="g23" out="g26"/>
+          <Substitution in="g24" out="g27"/>
+          <Substitution in="g25" out="g28"/>
+          <Substitution in="g26" out="g29"/>
+          <Substitution in="g27" out="g30"/>
+          <Substitution in="g28" out="g31"/>
+          <Substitution in="g29" out="g32"/>
+          <Substitution in="g3" out="g6"/>
+          <Substitution in="g30" out="g33"/>
+          <Substitution in="g31" out="g34"/>
+          <Substitution in="g32" out="g35"/>
+          <Substitution in="g33" out="g36"/>
+          <Substitution in="g34" out="g37"/>
+          <Substitution in="g35" out="g38"/>
+          <Substitution in="g36" out="g39"/>
+          <Substitution in="g37" out="g40"/>
+          <Substitution in="g38" out="g41"/>
+          <Substitution in="g39" out="g42"/>
+          <Substitution in="g4" out="g7"/>
+          <Substitution in="g40" out="g43"/>
+          <Substitution in="g41" out="g44"/>
+          <Substitution in="g42" out="g45"/>
+          <Substitution in="g43" out="g46"/>
+          <Substitution in="g44" out="g47"/>
+          <Substitution in="g45" out="g48"/>
+          <Substitution in="g46" out="g49"/>
+          <Substitution in="g47" out="g50"/>
+          <Substitution in="g48" out="g51"/>
+          <Substitution in="g49" out="g52"/>
+          <Substitution in="g5" out="g8"/>
+          <Substitution in="g50" out="g53"/>
+          <Substitution in="g51" out="g54"/>
+          <Substitution in="g52" out="g55"/>
+          <Substitution in="g53" out="g56"/>
+          <Substitution in="g54" out="g57"/>
+          <Substitution in="g55" out="g58"/>
+          <Substitution in="g56" out="g59"/>
+          <Substitution in="g57" out="g60"/>
+          <Substitution in="g58" out="g61"/>
+          <Substitution in="g59" out="g62"/>
+          <Substitution in="g6" out="g9"/>
+          <Substitution in="g60" out="g63"/>
+          <Substitution in="g61" out="g64"/>
+          <Substitution in="g62" out="g65"/>
+          <Substitution in="g63" out="g66"/>
+          <Substitution in="g64" out="g67"/>
+          <Substitution in="g65" out="g68"/>
+          <Substitution in="g66" out="g69"/>
+          <Substitution in="g67" out="g70"/>
+          <Substitution in="g68" out="g71"/>
+          <Substitution in="g69" out="g72"/>
+          <Substitution in="g7" out="g10"/>
+          <Substitution in="g70" out="g73"/>
+          <Substitution in="g71" out="g74"/>
+          <Substitution in="g72" out="g75"/>
+          <Substitution in="g73" out="g76"/>
+          <Substitution in="g74" out="g77"/>
+          <Substitution in="g75" out="g78"/>
+          <Substitution in="g76" out="g79"/>
+          <Substitution in="g77" out="g80"/>
+          <Substitution in="g78" out="g81"/>
+          <Substitution in="g79" out="g82"/>
+          <Substitution in="g8" out="g11"/>
+          <Substitution in="g80" out="g83"/>
+          <Substitution in="g81" out="g84"/>
+          <Substitution in="g82" out="g85"/>
+          <Substitution in="g83" out="g86"/>
+          <Substitution in="g84" out="g87"/>
+          <Substitution in="g85" out="g88"/>
+          <Substitution in="g86" out="g89"/>
+          <Substitution in="g87" out="g90"/>
+          <Substitution in="g88" out="g91"/>
+          <Substitution in="g89" out="g92"/>
+          <Substitution in="g9" out="g12"/>
+          <Substitution in="g90" out="g93"/>
+          <Substitution in="g91" out="g94"/>
+          <Substitution in="g92" out="g95"/>
+          <Substitution in="g93" out="g96"/>
+          <Substitution in="g94" out="g97"/>
+          <Substitution in="g95" out="g98"/>
+          <Substitution in="g96" out="g99"/>
+          <Substitution in="g97" out="glyph00100"/>
+          <Substitution in="g98" out="glyph00101"/>
+          <Substitution in="g99" out="glyph00102"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in=".notdef" out="g4"/>
+          <Substitution in="g1" out="g5"/>
+          <Substitution in="g10" out="g14"/>
+          <Substitution in="g11" out="g15"/>
+          <Substitution in="g12" out="g16"/>
+          <Substitution in="g13" out="g17"/>
+          <Substitution in="g14" out="g18"/>
+          <Substitution in="g15" out="g19"/>
+          <Substitution in="g16" out="g20"/>
+          <Substitution in="g17" out="g21"/>
+          <Substitution in="g18" out="g22"/>
+          <Substitution in="g19" out="g23"/>
+          <Substitution in="g2" out="g6"/>
+          <Substitution in="g20" out="g24"/>
+          <Substitution in="g21" out="g25"/>
+          <Substitution in="g22" out="g26"/>
+          <Substitution in="g23" out="g27"/>
+          <Substitution in="g24" out="g28"/>
+          <Substitution in="g25" out="g29"/>
+          <Substitution in="g26" out="g30"/>
+          <Substitution in="g27" out="g31"/>
+          <Substitution in="g28" out="g32"/>
+          <Substitution in="g29" out="g33"/>
+          <Substitution in="g3" out="g7"/>
+          <Substitution in="g30" out="g34"/>
+          <Substitution in="g31" out="g35"/>
+          <Substitution in="g32" out="g36"/>
+          <Substitution in="g33" out="g37"/>
+          <Substitution in="g34" out="g38"/>
+          <Substitution in="g35" out="g39"/>
+          <Substitution in="g36" out="g40"/>
+          <Substitution in="g37" out="g41"/>
+          <Substitution in="g38" out="g42"/>
+          <Substitution in="g39" out="g43"/>
+          <Substitution in="g4" out="g8"/>
+          <Substitution in="g40" out="g44"/>
+          <Substitution in="g41" out="g45"/>
+          <Substitution in="g42" out="g46"/>
+          <Substitution in="g43" out="g47"/>
+          <Substitution in="g44" out="g48"/>
+          <Substitution in="g45" out="g49"/>
+          <Substitution in="g46" out="g50"/>
+          <Substitution in="g47" out="g51"/>
+          <Substitution in="g48" out="g52"/>
+          <Substitution in="g49" out="g53"/>
+          <Substitution in="g5" out="g9"/>
+          <Substitution in="g50" out="g54"/>
+          <Substitution in="g51" out="g55"/>
+          <Substitution in="g52" out="g56"/>
+          <Substitution in="g53" out="g57"/>
+          <Substitution in="g54" out="g58"/>
+          <Substitution in="g55" out="g59"/>
+          <Substitution in="g56" out="g60"/>
+          <Substitution in="g57" out="g61"/>
+          <Substitution in="g58" out="g62"/>
+          <Substitution in="g59" out="g63"/>
+          <Substitution in="g6" out="g10"/>
+          <Substitution in="g60" out="g64"/>
+          <Substitution in="g61" out="g65"/>
+          <Substitution in="g62" out="g66"/>
+          <Substitution in="g63" out="g67"/>
+          <Substitution in="g64" out="g68"/>
+          <Substitution in="g65" out="g69"/>
+          <Substitution in="g66" out="g70"/>
+          <Substitution in="g67" out="g71"/>
+          <Substitution in="g68" out="g72"/>
+          <Substitution in="g69" out="g73"/>
+          <Substitution in="g7" out="g11"/>
+          <Substitution in="g70" out="g74"/>
+          <Substitution in="g71" out="g75"/>
+          <Substitution in="g72" out="g76"/>
+          <Substitution in="g73" out="g77"/>
+          <Substitution in="g74" out="g78"/>
+          <Substitution in="g75" out="g79"/>
+          <Substitution in="g76" out="g80"/>
+          <Substitution in="g77" out="g81"/>
+          <Substitution in="g78" out="g82"/>
+          <Substitution in="g79" out="g83"/>
+          <Substitution in="g8" out="g12"/>
+          <Substitution in="g80" out="g84"/>
+          <Substitution in="g81" out="g85"/>
+          <Substitution in="g82" out="g86"/>
+          <Substitution in="g83" out="g87"/>
+          <Substitution in="g84" out="g88"/>
+          <Substitution in="g85" out="g89"/>
+          <Substitution in="g86" out="g90"/>
+          <Substitution in="g87" out="g91"/>
+          <Substitution in="g88" out="g92"/>
+          <Substitution in="g89" out="g93"/>
+          <Substitution in="g9" out="g13"/>
+          <Substitution in="g90" out="g94"/>
+          <Substitution in="g91" out="g95"/>
+          <Substitution in="g92" out="g96"/>
+          <Substitution in="g93" out="g97"/>
+          <Substitution in="g94" out="g98"/>
+          <Substitution in="g95" out="g99"/>
+          <Substitution in="g96" out="glyph00100"/>
+          <Substitution in="g97" out="glyph00101"/>
+          <Substitution in="g98" out="glyph00102"/>
+          <Substitution in="g99" out="glyph00103"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in=".notdef" out="g5"/>
+          <Substitution in="g1" out="g6"/>
+          <Substitution in="g10" out="g15"/>
+          <Substitution in="g11" out="g16"/>
+          <Substitution in="g12" out="g17"/>
+          <Substitution in="g13" out="g18"/>
+          <Substitution in="g14" out="g19"/>
+          <Substitution in="g15" out="g20"/>
+          <Substitution in="g16" out="g21"/>
+          <Substitution in="g17" out="g22"/>
+          <Substitution in="g18" out="g23"/>
+          <Substitution in="g19" out="g24"/>
+          <Substitution in="g2" out="g7"/>
+          <Substitution in="g20" out="g25"/>
+          <Substitution in="g21" out="g26"/>
+          <Substitution in="g22" out="g27"/>
+          <Substitution in="g23" out="g28"/>
+          <Substitution in="g24" out="g29"/>
+          <Substitution in="g25" out="g30"/>
+          <Substitution in="g26" out="g31"/>
+          <Substitution in="g27" out="g32"/>
+          <Substitution in="g28" out="g33"/>
+          <Substitution in="g29" out="g34"/>
+          <Substitution in="g3" out="g8"/>
+          <Substitution in="g30" out="g35"/>
+          <Substitution in="g31" out="g36"/>
+          <Substitution in="g32" out="g37"/>
+          <Substitution in="g33" out="g38"/>
+          <Substitution in="g34" out="g39"/>
+          <Substitution in="g35" out="g40"/>
+          <Substitution in="g36" out="g41"/>
+          <Substitution in="g37" out="g42"/>
+          <Substitution in="g38" out="g43"/>
+          <Substitution in="g39" out="g44"/>
+          <Substitution in="g4" out="g9"/>
+          <Substitution in="g40" out="g45"/>
+          <Substitution in="g41" out="g46"/>
+          <Substitution in="g42" out="g47"/>
+          <Substitution in="g43" out="g48"/>
+          <Substitution in="g44" out="g49"/>
+          <Substitution in="g45" out="g50"/>
+          <Substitution in="g46" out="g51"/>
+          <Substitution in="g47" out="g52"/>
+          <Substitution in="g48" out="g53"/>
+          <Substitution in="g49" out="g54"/>
+          <Substitution in="g5" out="g10"/>
+          <Substitution in="g50" out="g55"/>
+          <Substitution in="g51" out="g56"/>
+          <Substitution in="g52" out="g57"/>
+          <Substitution in="g53" out="g58"/>
+          <Substitution in="g54" out="g59"/>
+          <Substitution in="g55" out="g60"/>
+          <Substitution in="g56" out="g61"/>
+          <Substitution in="g57" out="g62"/>
+          <Substitution in="g58" out="g63"/>
+          <Substitution in="g59" out="g64"/>
+          <Substitution in="g6" out="g11"/>
+          <Substitution in="g60" out="g65"/>
+          <Substitution in="g61" out="g66"/>
+          <Substitution in="g62" out="g67"/>
+          <Substitution in="g63" out="g68"/>
+          <Substitution in="g64" out="g69"/>
+          <Substitution in="g65" out="g70"/>
+          <Substitution in="g66" out="g71"/>
+          <Substitution in="g67" out="g72"/>
+          <Substitution in="g68" out="g73"/>
+          <Substitution in="g69" out="g74"/>
+          <Substitution in="g7" out="g12"/>
+          <Substitution in="g70" out="g75"/>
+          <Substitution in="g71" out="g76"/>
+          <Substitution in="g72" out="g77"/>
+          <Substitution in="g73" out="g78"/>
+          <Substitution in="g74" out="g79"/>
+          <Substitution in="g75" out="g80"/>
+          <Substitution in="g76" out="g81"/>
+          <Substitution in="g77" out="g82"/>
+          <Substitution in="g78" out="g83"/>
+          <Substitution in="g79" out="g84"/>
+          <Substitution in="g8" out="g13"/>
+          <Substitution in="g80" out="g85"/>
+          <Substitution in="g81" out="g86"/>
+          <Substitution in="g82" out="g87"/>
+          <Substitution in="g83" out="g88"/>
+          <Substitution in="g84" out="g89"/>
+          <Substitution in="g85" out="g90"/>
+          <Substitution in="g86" out="g91"/>
+          <Substitution in="g87" out="g92"/>
+          <Substitution in="g88" out="g93"/>
+          <Substitution in="g89" out="g94"/>
+          <Substitution in="g9" out="g14"/>
+          <Substitution in="g90" out="g95"/>
+          <Substitution in="g91" out="g96"/>
+          <Substitution in="g92" out="g97"/>
+          <Substitution in="g93" out="g98"/>
+          <Substitution in="g94" out="g99"/>
+          <Substitution in="g95" out="glyph00100"/>
+          <Substitution in="g96" out="glyph00101"/>
+          <Substitution in="g97" out="glyph00102"/>
+          <Substitution in="g98" out="glyph00103"/>
+          <Substitution in="g99" out="glyph00104"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value=".notdef"/>
+            <Glyph value="g1"/>
+            <Glyph value="g2"/>
+            <Glyph value="g3"/>
+            <Glyph value="g4"/>
+            <Glyph value="g5"/>
+            <Glyph value="g6"/>
+            <Glyph value="g7"/>
+            <Glyph value="g8"/>
+            <Glyph value="g9"/>
+            <Glyph value="g10"/>
+            <Glyph value="g11"/>
+            <Glyph value="g12"/>
+            <Glyph value="g13"/>
+            <Glyph value="g14"/>
+            <Glyph value="g15"/>
+            <Glyph value="g16"/>
+            <Glyph value="g17"/>
+            <Glyph value="g18"/>
+            <Glyph value="g19"/>
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+            <Glyph value="g30"/>
+            <Glyph value="g31"/>
+            <Glyph value="g32"/>
+            <Glyph value="g33"/>
+            <Glyph value="g34"/>
+            <Glyph value="g35"/>
+            <Glyph value="g36"/>
+            <Glyph value="g37"/>
+            <Glyph value="g38"/>
+            <Glyph value="g39"/>
+            <Glyph value="g40"/>
+            <Glyph value="g41"/>
+            <Glyph value="g42"/>
+            <Glyph value="g43"/>
+            <Glyph value="g44"/>
+            <Glyph value="g45"/>
+            <Glyph value="g46"/>
+            <Glyph value="g47"/>
+            <Glyph value="g48"/>
+            <Glyph value="g49"/>
+            <Glyph value="g50"/>
+            <Glyph value="g51"/>
+            <Glyph value="g52"/>
+            <Glyph value="g53"/>
+            <Glyph value="g54"/>
+            <Glyph value="g55"/>
+            <Glyph value="g56"/>
+            <Glyph value="g57"/>
+            <Glyph value="g58"/>
+            <Glyph value="g59"/>
+            <Glyph value="g60"/>
+            <Glyph value="g61"/>
+            <Glyph value="g62"/>
+            <Glyph value="g63"/>
+            <Glyph value="g64"/>
+            <Glyph value="g65"/>
+            <Glyph value="g66"/>
+            <Glyph value="g67"/>
+            <Glyph value="g68"/>
+            <Glyph value="g69"/>
+            <Glyph value="g70"/>
+            <Glyph value="g71"/>
+            <Glyph value="g72"/>
+            <Glyph value="g73"/>
+            <Glyph value="g74"/>
+            <Glyph value="g75"/>
+            <Glyph value="g76"/>
+            <Glyph value="g77"/>
+            <Glyph value="g78"/>
+            <Glyph value="g79"/>
+            <Glyph value="g80"/>
+            <Glyph value="g81"/>
+            <Glyph value="g82"/>
+            <Glyph value="g83"/>
+            <Glyph value="g84"/>
+            <Glyph value="g85"/>
+            <Glyph value="g86"/>
+            <Glyph value="g87"/>
+            <Glyph value="g88"/>
+            <Glyph value="g89"/>
+            <Glyph value="g90"/>
+            <Glyph value="g91"/>
+            <Glyph value="g92"/>
+            <Glyph value="g93"/>
+            <Glyph value="g94"/>
+            <Glyph value="g95"/>
+            <Glyph value="g96"/>
+            <Glyph value="g97"/>
+            <Glyph value="g98"/>
+            <Glyph value="g99"/>
+          </Coverage>
+          <ClassDef Format="1">
+          </ClassDef>
+          <!-- SubClassSetCount=1 -->
+          <SubClassSet index="0" empty="1"/>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/classdef2_font1.otf b/Tests/ttLib/tables/data/aots/classdef2_font1.otf
new file mode 100644
index 0000000..2e2faaf
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/classdef2_font1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/classdef2_font1.ttx.GSUB b/Tests/ttLib/tables/data/aots/classdef2_font1.ttx.GSUB
new file mode 100644
index 0000000..f602354
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/classdef2_font1.ttx.GSUB
@@ -0,0 +1,483 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="3"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=4 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in=".notdef" out="g3"/>
+          <Substitution in="g1" out="g4"/>
+          <Substitution in="g10" out="g13"/>
+          <Substitution in="g11" out="g14"/>
+          <Substitution in="g12" out="g15"/>
+          <Substitution in="g13" out="g16"/>
+          <Substitution in="g14" out="g17"/>
+          <Substitution in="g15" out="g18"/>
+          <Substitution in="g16" out="g19"/>
+          <Substitution in="g17" out="g20"/>
+          <Substitution in="g18" out="g21"/>
+          <Substitution in="g19" out="g22"/>
+          <Substitution in="g2" out="g5"/>
+          <Substitution in="g20" out="g23"/>
+          <Substitution in="g21" out="g24"/>
+          <Substitution in="g22" out="g25"/>
+          <Substitution in="g23" out="g26"/>
+          <Substitution in="g24" out="g27"/>
+          <Substitution in="g25" out="g28"/>
+          <Substitution in="g26" out="g29"/>
+          <Substitution in="g27" out="g30"/>
+          <Substitution in="g28" out="g31"/>
+          <Substitution in="g29" out="g32"/>
+          <Substitution in="g3" out="g6"/>
+          <Substitution in="g30" out="g33"/>
+          <Substitution in="g31" out="g34"/>
+          <Substitution in="g32" out="g35"/>
+          <Substitution in="g33" out="g36"/>
+          <Substitution in="g34" out="g37"/>
+          <Substitution in="g35" out="g38"/>
+          <Substitution in="g36" out="g39"/>
+          <Substitution in="g37" out="g40"/>
+          <Substitution in="g38" out="g41"/>
+          <Substitution in="g39" out="g42"/>
+          <Substitution in="g4" out="g7"/>
+          <Substitution in="g40" out="g43"/>
+          <Substitution in="g41" out="g44"/>
+          <Substitution in="g42" out="g45"/>
+          <Substitution in="g43" out="g46"/>
+          <Substitution in="g44" out="g47"/>
+          <Substitution in="g45" out="g48"/>
+          <Substitution in="g46" out="g49"/>
+          <Substitution in="g47" out="g50"/>
+          <Substitution in="g48" out="g51"/>
+          <Substitution in="g49" out="g52"/>
+          <Substitution in="g5" out="g8"/>
+          <Substitution in="g50" out="g53"/>
+          <Substitution in="g51" out="g54"/>
+          <Substitution in="g52" out="g55"/>
+          <Substitution in="g53" out="g56"/>
+          <Substitution in="g54" out="g57"/>
+          <Substitution in="g55" out="g58"/>
+          <Substitution in="g56" out="g59"/>
+          <Substitution in="g57" out="g60"/>
+          <Substitution in="g58" out="g61"/>
+          <Substitution in="g59" out="g62"/>
+          <Substitution in="g6" out="g9"/>
+          <Substitution in="g60" out="g63"/>
+          <Substitution in="g61" out="g64"/>
+          <Substitution in="g62" out="g65"/>
+          <Substitution in="g63" out="g66"/>
+          <Substitution in="g64" out="g67"/>
+          <Substitution in="g65" out="g68"/>
+          <Substitution in="g66" out="g69"/>
+          <Substitution in="g67" out="g70"/>
+          <Substitution in="g68" out="g71"/>
+          <Substitution in="g69" out="g72"/>
+          <Substitution in="g7" out="g10"/>
+          <Substitution in="g70" out="g73"/>
+          <Substitution in="g71" out="g74"/>
+          <Substitution in="g72" out="g75"/>
+          <Substitution in="g73" out="g76"/>
+          <Substitution in="g74" out="g77"/>
+          <Substitution in="g75" out="g78"/>
+          <Substitution in="g76" out="g79"/>
+          <Substitution in="g77" out="g80"/>
+          <Substitution in="g78" out="g81"/>
+          <Substitution in="g79" out="g82"/>
+          <Substitution in="g8" out="g11"/>
+          <Substitution in="g80" out="g83"/>
+          <Substitution in="g81" out="g84"/>
+          <Substitution in="g82" out="g85"/>
+          <Substitution in="g83" out="g86"/>
+          <Substitution in="g84" out="g87"/>
+          <Substitution in="g85" out="g88"/>
+          <Substitution in="g86" out="g89"/>
+          <Substitution in="g87" out="g90"/>
+          <Substitution in="g88" out="g91"/>
+          <Substitution in="g89" out="g92"/>
+          <Substitution in="g9" out="g12"/>
+          <Substitution in="g90" out="g93"/>
+          <Substitution in="g91" out="g94"/>
+          <Substitution in="g92" out="g95"/>
+          <Substitution in="g93" out="g96"/>
+          <Substitution in="g94" out="g97"/>
+          <Substitution in="g95" out="g98"/>
+          <Substitution in="g96" out="g99"/>
+          <Substitution in="g97" out="glyph00100"/>
+          <Substitution in="g98" out="glyph00101"/>
+          <Substitution in="g99" out="glyph00102"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in=".notdef" out="g4"/>
+          <Substitution in="g1" out="g5"/>
+          <Substitution in="g10" out="g14"/>
+          <Substitution in="g11" out="g15"/>
+          <Substitution in="g12" out="g16"/>
+          <Substitution in="g13" out="g17"/>
+          <Substitution in="g14" out="g18"/>
+          <Substitution in="g15" out="g19"/>
+          <Substitution in="g16" out="g20"/>
+          <Substitution in="g17" out="g21"/>
+          <Substitution in="g18" out="g22"/>
+          <Substitution in="g19" out="g23"/>
+          <Substitution in="g2" out="g6"/>
+          <Substitution in="g20" out="g24"/>
+          <Substitution in="g21" out="g25"/>
+          <Substitution in="g22" out="g26"/>
+          <Substitution in="g23" out="g27"/>
+          <Substitution in="g24" out="g28"/>
+          <Substitution in="g25" out="g29"/>
+          <Substitution in="g26" out="g30"/>
+          <Substitution in="g27" out="g31"/>
+          <Substitution in="g28" out="g32"/>
+          <Substitution in="g29" out="g33"/>
+          <Substitution in="g3" out="g7"/>
+          <Substitution in="g30" out="g34"/>
+          <Substitution in="g31" out="g35"/>
+          <Substitution in="g32" out="g36"/>
+          <Substitution in="g33" out="g37"/>
+          <Substitution in="g34" out="g38"/>
+          <Substitution in="g35" out="g39"/>
+          <Substitution in="g36" out="g40"/>
+          <Substitution in="g37" out="g41"/>
+          <Substitution in="g38" out="g42"/>
+          <Substitution in="g39" out="g43"/>
+          <Substitution in="g4" out="g8"/>
+          <Substitution in="g40" out="g44"/>
+          <Substitution in="g41" out="g45"/>
+          <Substitution in="g42" out="g46"/>
+          <Substitution in="g43" out="g47"/>
+          <Substitution in="g44" out="g48"/>
+          <Substitution in="g45" out="g49"/>
+          <Substitution in="g46" out="g50"/>
+          <Substitution in="g47" out="g51"/>
+          <Substitution in="g48" out="g52"/>
+          <Substitution in="g49" out="g53"/>
+          <Substitution in="g5" out="g9"/>
+          <Substitution in="g50" out="g54"/>
+          <Substitution in="g51" out="g55"/>
+          <Substitution in="g52" out="g56"/>
+          <Substitution in="g53" out="g57"/>
+          <Substitution in="g54" out="g58"/>
+          <Substitution in="g55" out="g59"/>
+          <Substitution in="g56" out="g60"/>
+          <Substitution in="g57" out="g61"/>
+          <Substitution in="g58" out="g62"/>
+          <Substitution in="g59" out="g63"/>
+          <Substitution in="g6" out="g10"/>
+          <Substitution in="g60" out="g64"/>
+          <Substitution in="g61" out="g65"/>
+          <Substitution in="g62" out="g66"/>
+          <Substitution in="g63" out="g67"/>
+          <Substitution in="g64" out="g68"/>
+          <Substitution in="g65" out="g69"/>
+          <Substitution in="g66" out="g70"/>
+          <Substitution in="g67" out="g71"/>
+          <Substitution in="g68" out="g72"/>
+          <Substitution in="g69" out="g73"/>
+          <Substitution in="g7" out="g11"/>
+          <Substitution in="g70" out="g74"/>
+          <Substitution in="g71" out="g75"/>
+          <Substitution in="g72" out="g76"/>
+          <Substitution in="g73" out="g77"/>
+          <Substitution in="g74" out="g78"/>
+          <Substitution in="g75" out="g79"/>
+          <Substitution in="g76" out="g80"/>
+          <Substitution in="g77" out="g81"/>
+          <Substitution in="g78" out="g82"/>
+          <Substitution in="g79" out="g83"/>
+          <Substitution in="g8" out="g12"/>
+          <Substitution in="g80" out="g84"/>
+          <Substitution in="g81" out="g85"/>
+          <Substitution in="g82" out="g86"/>
+          <Substitution in="g83" out="g87"/>
+          <Substitution in="g84" out="g88"/>
+          <Substitution in="g85" out="g89"/>
+          <Substitution in="g86" out="g90"/>
+          <Substitution in="g87" out="g91"/>
+          <Substitution in="g88" out="g92"/>
+          <Substitution in="g89" out="g93"/>
+          <Substitution in="g9" out="g13"/>
+          <Substitution in="g90" out="g94"/>
+          <Substitution in="g91" out="g95"/>
+          <Substitution in="g92" out="g96"/>
+          <Substitution in="g93" out="g97"/>
+          <Substitution in="g94" out="g98"/>
+          <Substitution in="g95" out="g99"/>
+          <Substitution in="g96" out="glyph00100"/>
+          <Substitution in="g97" out="glyph00101"/>
+          <Substitution in="g98" out="glyph00102"/>
+          <Substitution in="g99" out="glyph00103"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in=".notdef" out="g5"/>
+          <Substitution in="g1" out="g6"/>
+          <Substitution in="g10" out="g15"/>
+          <Substitution in="g11" out="g16"/>
+          <Substitution in="g12" out="g17"/>
+          <Substitution in="g13" out="g18"/>
+          <Substitution in="g14" out="g19"/>
+          <Substitution in="g15" out="g20"/>
+          <Substitution in="g16" out="g21"/>
+          <Substitution in="g17" out="g22"/>
+          <Substitution in="g18" out="g23"/>
+          <Substitution in="g19" out="g24"/>
+          <Substitution in="g2" out="g7"/>
+          <Substitution in="g20" out="g25"/>
+          <Substitution in="g21" out="g26"/>
+          <Substitution in="g22" out="g27"/>
+          <Substitution in="g23" out="g28"/>
+          <Substitution in="g24" out="g29"/>
+          <Substitution in="g25" out="g30"/>
+          <Substitution in="g26" out="g31"/>
+          <Substitution in="g27" out="g32"/>
+          <Substitution in="g28" out="g33"/>
+          <Substitution in="g29" out="g34"/>
+          <Substitution in="g3" out="g8"/>
+          <Substitution in="g30" out="g35"/>
+          <Substitution in="g31" out="g36"/>
+          <Substitution in="g32" out="g37"/>
+          <Substitution in="g33" out="g38"/>
+          <Substitution in="g34" out="g39"/>
+          <Substitution in="g35" out="g40"/>
+          <Substitution in="g36" out="g41"/>
+          <Substitution in="g37" out="g42"/>
+          <Substitution in="g38" out="g43"/>
+          <Substitution in="g39" out="g44"/>
+          <Substitution in="g4" out="g9"/>
+          <Substitution in="g40" out="g45"/>
+          <Substitution in="g41" out="g46"/>
+          <Substitution in="g42" out="g47"/>
+          <Substitution in="g43" out="g48"/>
+          <Substitution in="g44" out="g49"/>
+          <Substitution in="g45" out="g50"/>
+          <Substitution in="g46" out="g51"/>
+          <Substitution in="g47" out="g52"/>
+          <Substitution in="g48" out="g53"/>
+          <Substitution in="g49" out="g54"/>
+          <Substitution in="g5" out="g10"/>
+          <Substitution in="g50" out="g55"/>
+          <Substitution in="g51" out="g56"/>
+          <Substitution in="g52" out="g57"/>
+          <Substitution in="g53" out="g58"/>
+          <Substitution in="g54" out="g59"/>
+          <Substitution in="g55" out="g60"/>
+          <Substitution in="g56" out="g61"/>
+          <Substitution in="g57" out="g62"/>
+          <Substitution in="g58" out="g63"/>
+          <Substitution in="g59" out="g64"/>
+          <Substitution in="g6" out="g11"/>
+          <Substitution in="g60" out="g65"/>
+          <Substitution in="g61" out="g66"/>
+          <Substitution in="g62" out="g67"/>
+          <Substitution in="g63" out="g68"/>
+          <Substitution in="g64" out="g69"/>
+          <Substitution in="g65" out="g70"/>
+          <Substitution in="g66" out="g71"/>
+          <Substitution in="g67" out="g72"/>
+          <Substitution in="g68" out="g73"/>
+          <Substitution in="g69" out="g74"/>
+          <Substitution in="g7" out="g12"/>
+          <Substitution in="g70" out="g75"/>
+          <Substitution in="g71" out="g76"/>
+          <Substitution in="g72" out="g77"/>
+          <Substitution in="g73" out="g78"/>
+          <Substitution in="g74" out="g79"/>
+          <Substitution in="g75" out="g80"/>
+          <Substitution in="g76" out="g81"/>
+          <Substitution in="g77" out="g82"/>
+          <Substitution in="g78" out="g83"/>
+          <Substitution in="g79" out="g84"/>
+          <Substitution in="g8" out="g13"/>
+          <Substitution in="g80" out="g85"/>
+          <Substitution in="g81" out="g86"/>
+          <Substitution in="g82" out="g87"/>
+          <Substitution in="g83" out="g88"/>
+          <Substitution in="g84" out="g89"/>
+          <Substitution in="g85" out="g90"/>
+          <Substitution in="g86" out="g91"/>
+          <Substitution in="g87" out="g92"/>
+          <Substitution in="g88" out="g93"/>
+          <Substitution in="g89" out="g94"/>
+          <Substitution in="g9" out="g14"/>
+          <Substitution in="g90" out="g95"/>
+          <Substitution in="g91" out="g96"/>
+          <Substitution in="g92" out="g97"/>
+          <Substitution in="g93" out="g98"/>
+          <Substitution in="g94" out="g99"/>
+          <Substitution in="g95" out="glyph00100"/>
+          <Substitution in="g96" out="glyph00101"/>
+          <Substitution in="g97" out="glyph00102"/>
+          <Substitution in="g98" out="glyph00103"/>
+          <Substitution in="g99" out="glyph00104"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value=".notdef"/>
+            <Glyph value="g1"/>
+            <Glyph value="g2"/>
+            <Glyph value="g3"/>
+            <Glyph value="g4"/>
+            <Glyph value="g5"/>
+            <Glyph value="g6"/>
+            <Glyph value="g7"/>
+            <Glyph value="g8"/>
+            <Glyph value="g9"/>
+            <Glyph value="g10"/>
+            <Glyph value="g11"/>
+            <Glyph value="g12"/>
+            <Glyph value="g13"/>
+            <Glyph value="g14"/>
+            <Glyph value="g15"/>
+            <Glyph value="g16"/>
+            <Glyph value="g17"/>
+            <Glyph value="g18"/>
+            <Glyph value="g19"/>
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+            <Glyph value="g30"/>
+            <Glyph value="g31"/>
+            <Glyph value="g32"/>
+            <Glyph value="g33"/>
+            <Glyph value="g34"/>
+            <Glyph value="g35"/>
+            <Glyph value="g36"/>
+            <Glyph value="g37"/>
+            <Glyph value="g38"/>
+            <Glyph value="g39"/>
+            <Glyph value="g40"/>
+            <Glyph value="g41"/>
+            <Glyph value="g42"/>
+            <Glyph value="g43"/>
+            <Glyph value="g44"/>
+            <Glyph value="g45"/>
+            <Glyph value="g46"/>
+            <Glyph value="g47"/>
+            <Glyph value="g48"/>
+            <Glyph value="g49"/>
+            <Glyph value="g50"/>
+            <Glyph value="g51"/>
+            <Glyph value="g52"/>
+            <Glyph value="g53"/>
+            <Glyph value="g54"/>
+            <Glyph value="g55"/>
+            <Glyph value="g56"/>
+            <Glyph value="g57"/>
+            <Glyph value="g58"/>
+            <Glyph value="g59"/>
+            <Glyph value="g60"/>
+            <Glyph value="g61"/>
+            <Glyph value="g62"/>
+            <Glyph value="g63"/>
+            <Glyph value="g64"/>
+            <Glyph value="g65"/>
+            <Glyph value="g66"/>
+            <Glyph value="g67"/>
+            <Glyph value="g68"/>
+            <Glyph value="g69"/>
+            <Glyph value="g70"/>
+            <Glyph value="g71"/>
+            <Glyph value="g72"/>
+            <Glyph value="g73"/>
+            <Glyph value="g74"/>
+            <Glyph value="g75"/>
+            <Glyph value="g76"/>
+            <Glyph value="g77"/>
+            <Glyph value="g78"/>
+            <Glyph value="g79"/>
+            <Glyph value="g80"/>
+            <Glyph value="g81"/>
+            <Glyph value="g82"/>
+            <Glyph value="g83"/>
+            <Glyph value="g84"/>
+            <Glyph value="g85"/>
+            <Glyph value="g86"/>
+            <Glyph value="g87"/>
+            <Glyph value="g88"/>
+            <Glyph value="g89"/>
+            <Glyph value="g90"/>
+            <Glyph value="g91"/>
+            <Glyph value="g92"/>
+            <Glyph value="g93"/>
+            <Glyph value="g94"/>
+            <Glyph value="g95"/>
+            <Glyph value="g96"/>
+            <Glyph value="g97"/>
+            <Glyph value="g98"/>
+            <Glyph value="g99"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g18" class="1"/>
+            <ClassDef glyph="g19" class="1"/>
+            <ClassDef glyph="g20" class="1"/>
+          </ClassDef>
+          <!-- SubClassSetCount=2 -->
+          <SubClassSet index="0" empty="1"/>
+          <SubClassSet index="1">
+            <!-- SubClassRuleCount=1 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=1 -->
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="2"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+          </SubClassSet>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/classdef2_font2.otf b/Tests/ttLib/tables/data/aots/classdef2_font2.otf
new file mode 100644
index 0000000..2e2a1af
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/classdef2_font2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/classdef2_font2.ttx.GSUB b/Tests/ttLib/tables/data/aots/classdef2_font2.ttx.GSUB
new file mode 100644
index 0000000..d4650bd
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/classdef2_font2.ttx.GSUB
@@ -0,0 +1,494 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="3"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=4 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in=".notdef" out="g3"/>
+          <Substitution in="g1" out="g4"/>
+          <Substitution in="g10" out="g13"/>
+          <Substitution in="g11" out="g14"/>
+          <Substitution in="g12" out="g15"/>
+          <Substitution in="g13" out="g16"/>
+          <Substitution in="g14" out="g17"/>
+          <Substitution in="g15" out="g18"/>
+          <Substitution in="g16" out="g19"/>
+          <Substitution in="g17" out="g20"/>
+          <Substitution in="g18" out="g21"/>
+          <Substitution in="g19" out="g22"/>
+          <Substitution in="g2" out="g5"/>
+          <Substitution in="g20" out="g23"/>
+          <Substitution in="g21" out="g24"/>
+          <Substitution in="g22" out="g25"/>
+          <Substitution in="g23" out="g26"/>
+          <Substitution in="g24" out="g27"/>
+          <Substitution in="g25" out="g28"/>
+          <Substitution in="g26" out="g29"/>
+          <Substitution in="g27" out="g30"/>
+          <Substitution in="g28" out="g31"/>
+          <Substitution in="g29" out="g32"/>
+          <Substitution in="g3" out="g6"/>
+          <Substitution in="g30" out="g33"/>
+          <Substitution in="g31" out="g34"/>
+          <Substitution in="g32" out="g35"/>
+          <Substitution in="g33" out="g36"/>
+          <Substitution in="g34" out="g37"/>
+          <Substitution in="g35" out="g38"/>
+          <Substitution in="g36" out="g39"/>
+          <Substitution in="g37" out="g40"/>
+          <Substitution in="g38" out="g41"/>
+          <Substitution in="g39" out="g42"/>
+          <Substitution in="g4" out="g7"/>
+          <Substitution in="g40" out="g43"/>
+          <Substitution in="g41" out="g44"/>
+          <Substitution in="g42" out="g45"/>
+          <Substitution in="g43" out="g46"/>
+          <Substitution in="g44" out="g47"/>
+          <Substitution in="g45" out="g48"/>
+          <Substitution in="g46" out="g49"/>
+          <Substitution in="g47" out="g50"/>
+          <Substitution in="g48" out="g51"/>
+          <Substitution in="g49" out="g52"/>
+          <Substitution in="g5" out="g8"/>
+          <Substitution in="g50" out="g53"/>
+          <Substitution in="g51" out="g54"/>
+          <Substitution in="g52" out="g55"/>
+          <Substitution in="g53" out="g56"/>
+          <Substitution in="g54" out="g57"/>
+          <Substitution in="g55" out="g58"/>
+          <Substitution in="g56" out="g59"/>
+          <Substitution in="g57" out="g60"/>
+          <Substitution in="g58" out="g61"/>
+          <Substitution in="g59" out="g62"/>
+          <Substitution in="g6" out="g9"/>
+          <Substitution in="g60" out="g63"/>
+          <Substitution in="g61" out="g64"/>
+          <Substitution in="g62" out="g65"/>
+          <Substitution in="g63" out="g66"/>
+          <Substitution in="g64" out="g67"/>
+          <Substitution in="g65" out="g68"/>
+          <Substitution in="g66" out="g69"/>
+          <Substitution in="g67" out="g70"/>
+          <Substitution in="g68" out="g71"/>
+          <Substitution in="g69" out="g72"/>
+          <Substitution in="g7" out="g10"/>
+          <Substitution in="g70" out="g73"/>
+          <Substitution in="g71" out="g74"/>
+          <Substitution in="g72" out="g75"/>
+          <Substitution in="g73" out="g76"/>
+          <Substitution in="g74" out="g77"/>
+          <Substitution in="g75" out="g78"/>
+          <Substitution in="g76" out="g79"/>
+          <Substitution in="g77" out="g80"/>
+          <Substitution in="g78" out="g81"/>
+          <Substitution in="g79" out="g82"/>
+          <Substitution in="g8" out="g11"/>
+          <Substitution in="g80" out="g83"/>
+          <Substitution in="g81" out="g84"/>
+          <Substitution in="g82" out="g85"/>
+          <Substitution in="g83" out="g86"/>
+          <Substitution in="g84" out="g87"/>
+          <Substitution in="g85" out="g88"/>
+          <Substitution in="g86" out="g89"/>
+          <Substitution in="g87" out="g90"/>
+          <Substitution in="g88" out="g91"/>
+          <Substitution in="g89" out="g92"/>
+          <Substitution in="g9" out="g12"/>
+          <Substitution in="g90" out="g93"/>
+          <Substitution in="g91" out="g94"/>
+          <Substitution in="g92" out="g95"/>
+          <Substitution in="g93" out="g96"/>
+          <Substitution in="g94" out="g97"/>
+          <Substitution in="g95" out="g98"/>
+          <Substitution in="g96" out="g99"/>
+          <Substitution in="g97" out="glyph00100"/>
+          <Substitution in="g98" out="glyph00101"/>
+          <Substitution in="g99" out="glyph00102"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in=".notdef" out="g4"/>
+          <Substitution in="g1" out="g5"/>
+          <Substitution in="g10" out="g14"/>
+          <Substitution in="g11" out="g15"/>
+          <Substitution in="g12" out="g16"/>
+          <Substitution in="g13" out="g17"/>
+          <Substitution in="g14" out="g18"/>
+          <Substitution in="g15" out="g19"/>
+          <Substitution in="g16" out="g20"/>
+          <Substitution in="g17" out="g21"/>
+          <Substitution in="g18" out="g22"/>
+          <Substitution in="g19" out="g23"/>
+          <Substitution in="g2" out="g6"/>
+          <Substitution in="g20" out="g24"/>
+          <Substitution in="g21" out="g25"/>
+          <Substitution in="g22" out="g26"/>
+          <Substitution in="g23" out="g27"/>
+          <Substitution in="g24" out="g28"/>
+          <Substitution in="g25" out="g29"/>
+          <Substitution in="g26" out="g30"/>
+          <Substitution in="g27" out="g31"/>
+          <Substitution in="g28" out="g32"/>
+          <Substitution in="g29" out="g33"/>
+          <Substitution in="g3" out="g7"/>
+          <Substitution in="g30" out="g34"/>
+          <Substitution in="g31" out="g35"/>
+          <Substitution in="g32" out="g36"/>
+          <Substitution in="g33" out="g37"/>
+          <Substitution in="g34" out="g38"/>
+          <Substitution in="g35" out="g39"/>
+          <Substitution in="g36" out="g40"/>
+          <Substitution in="g37" out="g41"/>
+          <Substitution in="g38" out="g42"/>
+          <Substitution in="g39" out="g43"/>
+          <Substitution in="g4" out="g8"/>
+          <Substitution in="g40" out="g44"/>
+          <Substitution in="g41" out="g45"/>
+          <Substitution in="g42" out="g46"/>
+          <Substitution in="g43" out="g47"/>
+          <Substitution in="g44" out="g48"/>
+          <Substitution in="g45" out="g49"/>
+          <Substitution in="g46" out="g50"/>
+          <Substitution in="g47" out="g51"/>
+          <Substitution in="g48" out="g52"/>
+          <Substitution in="g49" out="g53"/>
+          <Substitution in="g5" out="g9"/>
+          <Substitution in="g50" out="g54"/>
+          <Substitution in="g51" out="g55"/>
+          <Substitution in="g52" out="g56"/>
+          <Substitution in="g53" out="g57"/>
+          <Substitution in="g54" out="g58"/>
+          <Substitution in="g55" out="g59"/>
+          <Substitution in="g56" out="g60"/>
+          <Substitution in="g57" out="g61"/>
+          <Substitution in="g58" out="g62"/>
+          <Substitution in="g59" out="g63"/>
+          <Substitution in="g6" out="g10"/>
+          <Substitution in="g60" out="g64"/>
+          <Substitution in="g61" out="g65"/>
+          <Substitution in="g62" out="g66"/>
+          <Substitution in="g63" out="g67"/>
+          <Substitution in="g64" out="g68"/>
+          <Substitution in="g65" out="g69"/>
+          <Substitution in="g66" out="g70"/>
+          <Substitution in="g67" out="g71"/>
+          <Substitution in="g68" out="g72"/>
+          <Substitution in="g69" out="g73"/>
+          <Substitution in="g7" out="g11"/>
+          <Substitution in="g70" out="g74"/>
+          <Substitution in="g71" out="g75"/>
+          <Substitution in="g72" out="g76"/>
+          <Substitution in="g73" out="g77"/>
+          <Substitution in="g74" out="g78"/>
+          <Substitution in="g75" out="g79"/>
+          <Substitution in="g76" out="g80"/>
+          <Substitution in="g77" out="g81"/>
+          <Substitution in="g78" out="g82"/>
+          <Substitution in="g79" out="g83"/>
+          <Substitution in="g8" out="g12"/>
+          <Substitution in="g80" out="g84"/>
+          <Substitution in="g81" out="g85"/>
+          <Substitution in="g82" out="g86"/>
+          <Substitution in="g83" out="g87"/>
+          <Substitution in="g84" out="g88"/>
+          <Substitution in="g85" out="g89"/>
+          <Substitution in="g86" out="g90"/>
+          <Substitution in="g87" out="g91"/>
+          <Substitution in="g88" out="g92"/>
+          <Substitution in="g89" out="g93"/>
+          <Substitution in="g9" out="g13"/>
+          <Substitution in="g90" out="g94"/>
+          <Substitution in="g91" out="g95"/>
+          <Substitution in="g92" out="g96"/>
+          <Substitution in="g93" out="g97"/>
+          <Substitution in="g94" out="g98"/>
+          <Substitution in="g95" out="g99"/>
+          <Substitution in="g96" out="glyph00100"/>
+          <Substitution in="g97" out="glyph00101"/>
+          <Substitution in="g98" out="glyph00102"/>
+          <Substitution in="g99" out="glyph00103"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in=".notdef" out="g5"/>
+          <Substitution in="g1" out="g6"/>
+          <Substitution in="g10" out="g15"/>
+          <Substitution in="g11" out="g16"/>
+          <Substitution in="g12" out="g17"/>
+          <Substitution in="g13" out="g18"/>
+          <Substitution in="g14" out="g19"/>
+          <Substitution in="g15" out="g20"/>
+          <Substitution in="g16" out="g21"/>
+          <Substitution in="g17" out="g22"/>
+          <Substitution in="g18" out="g23"/>
+          <Substitution in="g19" out="g24"/>
+          <Substitution in="g2" out="g7"/>
+          <Substitution in="g20" out="g25"/>
+          <Substitution in="g21" out="g26"/>
+          <Substitution in="g22" out="g27"/>
+          <Substitution in="g23" out="g28"/>
+          <Substitution in="g24" out="g29"/>
+          <Substitution in="g25" out="g30"/>
+          <Substitution in="g26" out="g31"/>
+          <Substitution in="g27" out="g32"/>
+          <Substitution in="g28" out="g33"/>
+          <Substitution in="g29" out="g34"/>
+          <Substitution in="g3" out="g8"/>
+          <Substitution in="g30" out="g35"/>
+          <Substitution in="g31" out="g36"/>
+          <Substitution in="g32" out="g37"/>
+          <Substitution in="g33" out="g38"/>
+          <Substitution in="g34" out="g39"/>
+          <Substitution in="g35" out="g40"/>
+          <Substitution in="g36" out="g41"/>
+          <Substitution in="g37" out="g42"/>
+          <Substitution in="g38" out="g43"/>
+          <Substitution in="g39" out="g44"/>
+          <Substitution in="g4" out="g9"/>
+          <Substitution in="g40" out="g45"/>
+          <Substitution in="g41" out="g46"/>
+          <Substitution in="g42" out="g47"/>
+          <Substitution in="g43" out="g48"/>
+          <Substitution in="g44" out="g49"/>
+          <Substitution in="g45" out="g50"/>
+          <Substitution in="g46" out="g51"/>
+          <Substitution in="g47" out="g52"/>
+          <Substitution in="g48" out="g53"/>
+          <Substitution in="g49" out="g54"/>
+          <Substitution in="g5" out="g10"/>
+          <Substitution in="g50" out="g55"/>
+          <Substitution in="g51" out="g56"/>
+          <Substitution in="g52" out="g57"/>
+          <Substitution in="g53" out="g58"/>
+          <Substitution in="g54" out="g59"/>
+          <Substitution in="g55" out="g60"/>
+          <Substitution in="g56" out="g61"/>
+          <Substitution in="g57" out="g62"/>
+          <Substitution in="g58" out="g63"/>
+          <Substitution in="g59" out="g64"/>
+          <Substitution in="g6" out="g11"/>
+          <Substitution in="g60" out="g65"/>
+          <Substitution in="g61" out="g66"/>
+          <Substitution in="g62" out="g67"/>
+          <Substitution in="g63" out="g68"/>
+          <Substitution in="g64" out="g69"/>
+          <Substitution in="g65" out="g70"/>
+          <Substitution in="g66" out="g71"/>
+          <Substitution in="g67" out="g72"/>
+          <Substitution in="g68" out="g73"/>
+          <Substitution in="g69" out="g74"/>
+          <Substitution in="g7" out="g12"/>
+          <Substitution in="g70" out="g75"/>
+          <Substitution in="g71" out="g76"/>
+          <Substitution in="g72" out="g77"/>
+          <Substitution in="g73" out="g78"/>
+          <Substitution in="g74" out="g79"/>
+          <Substitution in="g75" out="g80"/>
+          <Substitution in="g76" out="g81"/>
+          <Substitution in="g77" out="g82"/>
+          <Substitution in="g78" out="g83"/>
+          <Substitution in="g79" out="g84"/>
+          <Substitution in="g8" out="g13"/>
+          <Substitution in="g80" out="g85"/>
+          <Substitution in="g81" out="g86"/>
+          <Substitution in="g82" out="g87"/>
+          <Substitution in="g83" out="g88"/>
+          <Substitution in="g84" out="g89"/>
+          <Substitution in="g85" out="g90"/>
+          <Substitution in="g86" out="g91"/>
+          <Substitution in="g87" out="g92"/>
+          <Substitution in="g88" out="g93"/>
+          <Substitution in="g89" out="g94"/>
+          <Substitution in="g9" out="g14"/>
+          <Substitution in="g90" out="g95"/>
+          <Substitution in="g91" out="g96"/>
+          <Substitution in="g92" out="g97"/>
+          <Substitution in="g93" out="g98"/>
+          <Substitution in="g94" out="g99"/>
+          <Substitution in="g95" out="glyph00100"/>
+          <Substitution in="g96" out="glyph00101"/>
+          <Substitution in="g97" out="glyph00102"/>
+          <Substitution in="g98" out="glyph00103"/>
+          <Substitution in="g99" out="glyph00104"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value=".notdef"/>
+            <Glyph value="g1"/>
+            <Glyph value="g2"/>
+            <Glyph value="g3"/>
+            <Glyph value="g4"/>
+            <Glyph value="g5"/>
+            <Glyph value="g6"/>
+            <Glyph value="g7"/>
+            <Glyph value="g8"/>
+            <Glyph value="g9"/>
+            <Glyph value="g10"/>
+            <Glyph value="g11"/>
+            <Glyph value="g12"/>
+            <Glyph value="g13"/>
+            <Glyph value="g14"/>
+            <Glyph value="g15"/>
+            <Glyph value="g16"/>
+            <Glyph value="g17"/>
+            <Glyph value="g18"/>
+            <Glyph value="g19"/>
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+            <Glyph value="g30"/>
+            <Glyph value="g31"/>
+            <Glyph value="g32"/>
+            <Glyph value="g33"/>
+            <Glyph value="g34"/>
+            <Glyph value="g35"/>
+            <Glyph value="g36"/>
+            <Glyph value="g37"/>
+            <Glyph value="g38"/>
+            <Glyph value="g39"/>
+            <Glyph value="g40"/>
+            <Glyph value="g41"/>
+            <Glyph value="g42"/>
+            <Glyph value="g43"/>
+            <Glyph value="g44"/>
+            <Glyph value="g45"/>
+            <Glyph value="g46"/>
+            <Glyph value="g47"/>
+            <Glyph value="g48"/>
+            <Glyph value="g49"/>
+            <Glyph value="g50"/>
+            <Glyph value="g51"/>
+            <Glyph value="g52"/>
+            <Glyph value="g53"/>
+            <Glyph value="g54"/>
+            <Glyph value="g55"/>
+            <Glyph value="g56"/>
+            <Glyph value="g57"/>
+            <Glyph value="g58"/>
+            <Glyph value="g59"/>
+            <Glyph value="g60"/>
+            <Glyph value="g61"/>
+            <Glyph value="g62"/>
+            <Glyph value="g63"/>
+            <Glyph value="g64"/>
+            <Glyph value="g65"/>
+            <Glyph value="g66"/>
+            <Glyph value="g67"/>
+            <Glyph value="g68"/>
+            <Glyph value="g69"/>
+            <Glyph value="g70"/>
+            <Glyph value="g71"/>
+            <Glyph value="g72"/>
+            <Glyph value="g73"/>
+            <Glyph value="g74"/>
+            <Glyph value="g75"/>
+            <Glyph value="g76"/>
+            <Glyph value="g77"/>
+            <Glyph value="g78"/>
+            <Glyph value="g79"/>
+            <Glyph value="g80"/>
+            <Glyph value="g81"/>
+            <Glyph value="g82"/>
+            <Glyph value="g83"/>
+            <Glyph value="g84"/>
+            <Glyph value="g85"/>
+            <Glyph value="g86"/>
+            <Glyph value="g87"/>
+            <Glyph value="g88"/>
+            <Glyph value="g89"/>
+            <Glyph value="g90"/>
+            <Glyph value="g91"/>
+            <Glyph value="g92"/>
+            <Glyph value="g93"/>
+            <Glyph value="g94"/>
+            <Glyph value="g95"/>
+            <Glyph value="g96"/>
+            <Glyph value="g97"/>
+            <Glyph value="g98"/>
+            <Glyph value="g99"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g18" class="2"/>
+            <ClassDef glyph="g19" class="2"/>
+            <ClassDef glyph="g20" class="2"/>
+          </ClassDef>
+          <!-- SubClassSetCount=3 -->
+          <SubClassSet index="0" empty="1"/>
+          <SubClassSet index="1">
+            <!-- SubClassRuleCount=1 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=1 -->
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="1"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+          </SubClassSet>
+          <SubClassSet index="2">
+            <!-- SubClassRuleCount=1 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=1 -->
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="2"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+          </SubClassSet>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/classdef2_font3.otf b/Tests/ttLib/tables/data/aots/classdef2_font3.otf
new file mode 100644
index 0000000..14c9119
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/classdef2_font3.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/classdef2_font3.ttx.GSUB b/Tests/ttLib/tables/data/aots/classdef2_font3.ttx.GSUB
new file mode 100644
index 0000000..7cb045f
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/classdef2_font3.ttx.GSUB
@@ -0,0 +1,510 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="3"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=4 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in=".notdef" out="g3"/>
+          <Substitution in="g1" out="g4"/>
+          <Substitution in="g10" out="g13"/>
+          <Substitution in="g11" out="g14"/>
+          <Substitution in="g12" out="g15"/>
+          <Substitution in="g13" out="g16"/>
+          <Substitution in="g14" out="g17"/>
+          <Substitution in="g15" out="g18"/>
+          <Substitution in="g16" out="g19"/>
+          <Substitution in="g17" out="g20"/>
+          <Substitution in="g18" out="g21"/>
+          <Substitution in="g19" out="g22"/>
+          <Substitution in="g2" out="g5"/>
+          <Substitution in="g20" out="g23"/>
+          <Substitution in="g21" out="g24"/>
+          <Substitution in="g22" out="g25"/>
+          <Substitution in="g23" out="g26"/>
+          <Substitution in="g24" out="g27"/>
+          <Substitution in="g25" out="g28"/>
+          <Substitution in="g26" out="g29"/>
+          <Substitution in="g27" out="g30"/>
+          <Substitution in="g28" out="g31"/>
+          <Substitution in="g29" out="g32"/>
+          <Substitution in="g3" out="g6"/>
+          <Substitution in="g30" out="g33"/>
+          <Substitution in="g31" out="g34"/>
+          <Substitution in="g32" out="g35"/>
+          <Substitution in="g33" out="g36"/>
+          <Substitution in="g34" out="g37"/>
+          <Substitution in="g35" out="g38"/>
+          <Substitution in="g36" out="g39"/>
+          <Substitution in="g37" out="g40"/>
+          <Substitution in="g38" out="g41"/>
+          <Substitution in="g39" out="g42"/>
+          <Substitution in="g4" out="g7"/>
+          <Substitution in="g40" out="g43"/>
+          <Substitution in="g41" out="g44"/>
+          <Substitution in="g42" out="g45"/>
+          <Substitution in="g43" out="g46"/>
+          <Substitution in="g44" out="g47"/>
+          <Substitution in="g45" out="g48"/>
+          <Substitution in="g46" out="g49"/>
+          <Substitution in="g47" out="g50"/>
+          <Substitution in="g48" out="g51"/>
+          <Substitution in="g49" out="g52"/>
+          <Substitution in="g5" out="g8"/>
+          <Substitution in="g50" out="g53"/>
+          <Substitution in="g51" out="g54"/>
+          <Substitution in="g52" out="g55"/>
+          <Substitution in="g53" out="g56"/>
+          <Substitution in="g54" out="g57"/>
+          <Substitution in="g55" out="g58"/>
+          <Substitution in="g56" out="g59"/>
+          <Substitution in="g57" out="g60"/>
+          <Substitution in="g58" out="g61"/>
+          <Substitution in="g59" out="g62"/>
+          <Substitution in="g6" out="g9"/>
+          <Substitution in="g60" out="g63"/>
+          <Substitution in="g61" out="g64"/>
+          <Substitution in="g62" out="g65"/>
+          <Substitution in="g63" out="g66"/>
+          <Substitution in="g64" out="g67"/>
+          <Substitution in="g65" out="g68"/>
+          <Substitution in="g66" out="g69"/>
+          <Substitution in="g67" out="g70"/>
+          <Substitution in="g68" out="g71"/>
+          <Substitution in="g69" out="g72"/>
+          <Substitution in="g7" out="g10"/>
+          <Substitution in="g70" out="g73"/>
+          <Substitution in="g71" out="g74"/>
+          <Substitution in="g72" out="g75"/>
+          <Substitution in="g73" out="g76"/>
+          <Substitution in="g74" out="g77"/>
+          <Substitution in="g75" out="g78"/>
+          <Substitution in="g76" out="g79"/>
+          <Substitution in="g77" out="g80"/>
+          <Substitution in="g78" out="g81"/>
+          <Substitution in="g79" out="g82"/>
+          <Substitution in="g8" out="g11"/>
+          <Substitution in="g80" out="g83"/>
+          <Substitution in="g81" out="g84"/>
+          <Substitution in="g82" out="g85"/>
+          <Substitution in="g83" out="g86"/>
+          <Substitution in="g84" out="g87"/>
+          <Substitution in="g85" out="g88"/>
+          <Substitution in="g86" out="g89"/>
+          <Substitution in="g87" out="g90"/>
+          <Substitution in="g88" out="g91"/>
+          <Substitution in="g89" out="g92"/>
+          <Substitution in="g9" out="g12"/>
+          <Substitution in="g90" out="g93"/>
+          <Substitution in="g91" out="g94"/>
+          <Substitution in="g92" out="g95"/>
+          <Substitution in="g93" out="g96"/>
+          <Substitution in="g94" out="g97"/>
+          <Substitution in="g95" out="g98"/>
+          <Substitution in="g96" out="g99"/>
+          <Substitution in="g97" out="glyph00100"/>
+          <Substitution in="g98" out="glyph00101"/>
+          <Substitution in="g99" out="glyph00102"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in=".notdef" out="g4"/>
+          <Substitution in="g1" out="g5"/>
+          <Substitution in="g10" out="g14"/>
+          <Substitution in="g11" out="g15"/>
+          <Substitution in="g12" out="g16"/>
+          <Substitution in="g13" out="g17"/>
+          <Substitution in="g14" out="g18"/>
+          <Substitution in="g15" out="g19"/>
+          <Substitution in="g16" out="g20"/>
+          <Substitution in="g17" out="g21"/>
+          <Substitution in="g18" out="g22"/>
+          <Substitution in="g19" out="g23"/>
+          <Substitution in="g2" out="g6"/>
+          <Substitution in="g20" out="g24"/>
+          <Substitution in="g21" out="g25"/>
+          <Substitution in="g22" out="g26"/>
+          <Substitution in="g23" out="g27"/>
+          <Substitution in="g24" out="g28"/>
+          <Substitution in="g25" out="g29"/>
+          <Substitution in="g26" out="g30"/>
+          <Substitution in="g27" out="g31"/>
+          <Substitution in="g28" out="g32"/>
+          <Substitution in="g29" out="g33"/>
+          <Substitution in="g3" out="g7"/>
+          <Substitution in="g30" out="g34"/>
+          <Substitution in="g31" out="g35"/>
+          <Substitution in="g32" out="g36"/>
+          <Substitution in="g33" out="g37"/>
+          <Substitution in="g34" out="g38"/>
+          <Substitution in="g35" out="g39"/>
+          <Substitution in="g36" out="g40"/>
+          <Substitution in="g37" out="g41"/>
+          <Substitution in="g38" out="g42"/>
+          <Substitution in="g39" out="g43"/>
+          <Substitution in="g4" out="g8"/>
+          <Substitution in="g40" out="g44"/>
+          <Substitution in="g41" out="g45"/>
+          <Substitution in="g42" out="g46"/>
+          <Substitution in="g43" out="g47"/>
+          <Substitution in="g44" out="g48"/>
+          <Substitution in="g45" out="g49"/>
+          <Substitution in="g46" out="g50"/>
+          <Substitution in="g47" out="g51"/>
+          <Substitution in="g48" out="g52"/>
+          <Substitution in="g49" out="g53"/>
+          <Substitution in="g5" out="g9"/>
+          <Substitution in="g50" out="g54"/>
+          <Substitution in="g51" out="g55"/>
+          <Substitution in="g52" out="g56"/>
+          <Substitution in="g53" out="g57"/>
+          <Substitution in="g54" out="g58"/>
+          <Substitution in="g55" out="g59"/>
+          <Substitution in="g56" out="g60"/>
+          <Substitution in="g57" out="g61"/>
+          <Substitution in="g58" out="g62"/>
+          <Substitution in="g59" out="g63"/>
+          <Substitution in="g6" out="g10"/>
+          <Substitution in="g60" out="g64"/>
+          <Substitution in="g61" out="g65"/>
+          <Substitution in="g62" out="g66"/>
+          <Substitution in="g63" out="g67"/>
+          <Substitution in="g64" out="g68"/>
+          <Substitution in="g65" out="g69"/>
+          <Substitution in="g66" out="g70"/>
+          <Substitution in="g67" out="g71"/>
+          <Substitution in="g68" out="g72"/>
+          <Substitution in="g69" out="g73"/>
+          <Substitution in="g7" out="g11"/>
+          <Substitution in="g70" out="g74"/>
+          <Substitution in="g71" out="g75"/>
+          <Substitution in="g72" out="g76"/>
+          <Substitution in="g73" out="g77"/>
+          <Substitution in="g74" out="g78"/>
+          <Substitution in="g75" out="g79"/>
+          <Substitution in="g76" out="g80"/>
+          <Substitution in="g77" out="g81"/>
+          <Substitution in="g78" out="g82"/>
+          <Substitution in="g79" out="g83"/>
+          <Substitution in="g8" out="g12"/>
+          <Substitution in="g80" out="g84"/>
+          <Substitution in="g81" out="g85"/>
+          <Substitution in="g82" out="g86"/>
+          <Substitution in="g83" out="g87"/>
+          <Substitution in="g84" out="g88"/>
+          <Substitution in="g85" out="g89"/>
+          <Substitution in="g86" out="g90"/>
+          <Substitution in="g87" out="g91"/>
+          <Substitution in="g88" out="g92"/>
+          <Substitution in="g89" out="g93"/>
+          <Substitution in="g9" out="g13"/>
+          <Substitution in="g90" out="g94"/>
+          <Substitution in="g91" out="g95"/>
+          <Substitution in="g92" out="g96"/>
+          <Substitution in="g93" out="g97"/>
+          <Substitution in="g94" out="g98"/>
+          <Substitution in="g95" out="g99"/>
+          <Substitution in="g96" out="glyph00100"/>
+          <Substitution in="g97" out="glyph00101"/>
+          <Substitution in="g98" out="glyph00102"/>
+          <Substitution in="g99" out="glyph00103"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in=".notdef" out="g5"/>
+          <Substitution in="g1" out="g6"/>
+          <Substitution in="g10" out="g15"/>
+          <Substitution in="g11" out="g16"/>
+          <Substitution in="g12" out="g17"/>
+          <Substitution in="g13" out="g18"/>
+          <Substitution in="g14" out="g19"/>
+          <Substitution in="g15" out="g20"/>
+          <Substitution in="g16" out="g21"/>
+          <Substitution in="g17" out="g22"/>
+          <Substitution in="g18" out="g23"/>
+          <Substitution in="g19" out="g24"/>
+          <Substitution in="g2" out="g7"/>
+          <Substitution in="g20" out="g25"/>
+          <Substitution in="g21" out="g26"/>
+          <Substitution in="g22" out="g27"/>
+          <Substitution in="g23" out="g28"/>
+          <Substitution in="g24" out="g29"/>
+          <Substitution in="g25" out="g30"/>
+          <Substitution in="g26" out="g31"/>
+          <Substitution in="g27" out="g32"/>
+          <Substitution in="g28" out="g33"/>
+          <Substitution in="g29" out="g34"/>
+          <Substitution in="g3" out="g8"/>
+          <Substitution in="g30" out="g35"/>
+          <Substitution in="g31" out="g36"/>
+          <Substitution in="g32" out="g37"/>
+          <Substitution in="g33" out="g38"/>
+          <Substitution in="g34" out="g39"/>
+          <Substitution in="g35" out="g40"/>
+          <Substitution in="g36" out="g41"/>
+          <Substitution in="g37" out="g42"/>
+          <Substitution in="g38" out="g43"/>
+          <Substitution in="g39" out="g44"/>
+          <Substitution in="g4" out="g9"/>
+          <Substitution in="g40" out="g45"/>
+          <Substitution in="g41" out="g46"/>
+          <Substitution in="g42" out="g47"/>
+          <Substitution in="g43" out="g48"/>
+          <Substitution in="g44" out="g49"/>
+          <Substitution in="g45" out="g50"/>
+          <Substitution in="g46" out="g51"/>
+          <Substitution in="g47" out="g52"/>
+          <Substitution in="g48" out="g53"/>
+          <Substitution in="g49" out="g54"/>
+          <Substitution in="g5" out="g10"/>
+          <Substitution in="g50" out="g55"/>
+          <Substitution in="g51" out="g56"/>
+          <Substitution in="g52" out="g57"/>
+          <Substitution in="g53" out="g58"/>
+          <Substitution in="g54" out="g59"/>
+          <Substitution in="g55" out="g60"/>
+          <Substitution in="g56" out="g61"/>
+          <Substitution in="g57" out="g62"/>
+          <Substitution in="g58" out="g63"/>
+          <Substitution in="g59" out="g64"/>
+          <Substitution in="g6" out="g11"/>
+          <Substitution in="g60" out="g65"/>
+          <Substitution in="g61" out="g66"/>
+          <Substitution in="g62" out="g67"/>
+          <Substitution in="g63" out="g68"/>
+          <Substitution in="g64" out="g69"/>
+          <Substitution in="g65" out="g70"/>
+          <Substitution in="g66" out="g71"/>
+          <Substitution in="g67" out="g72"/>
+          <Substitution in="g68" out="g73"/>
+          <Substitution in="g69" out="g74"/>
+          <Substitution in="g7" out="g12"/>
+          <Substitution in="g70" out="g75"/>
+          <Substitution in="g71" out="g76"/>
+          <Substitution in="g72" out="g77"/>
+          <Substitution in="g73" out="g78"/>
+          <Substitution in="g74" out="g79"/>
+          <Substitution in="g75" out="g80"/>
+          <Substitution in="g76" out="g81"/>
+          <Substitution in="g77" out="g82"/>
+          <Substitution in="g78" out="g83"/>
+          <Substitution in="g79" out="g84"/>
+          <Substitution in="g8" out="g13"/>
+          <Substitution in="g80" out="g85"/>
+          <Substitution in="g81" out="g86"/>
+          <Substitution in="g82" out="g87"/>
+          <Substitution in="g83" out="g88"/>
+          <Substitution in="g84" out="g89"/>
+          <Substitution in="g85" out="g90"/>
+          <Substitution in="g86" out="g91"/>
+          <Substitution in="g87" out="g92"/>
+          <Substitution in="g88" out="g93"/>
+          <Substitution in="g89" out="g94"/>
+          <Substitution in="g9" out="g14"/>
+          <Substitution in="g90" out="g95"/>
+          <Substitution in="g91" out="g96"/>
+          <Substitution in="g92" out="g97"/>
+          <Substitution in="g93" out="g98"/>
+          <Substitution in="g94" out="g99"/>
+          <Substitution in="g95" out="glyph00100"/>
+          <Substitution in="g96" out="glyph00101"/>
+          <Substitution in="g97" out="glyph00102"/>
+          <Substitution in="g98" out="glyph00103"/>
+          <Substitution in="g99" out="glyph00104"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value=".notdef"/>
+            <Glyph value="g1"/>
+            <Glyph value="g2"/>
+            <Glyph value="g3"/>
+            <Glyph value="g4"/>
+            <Glyph value="g5"/>
+            <Glyph value="g6"/>
+            <Glyph value="g7"/>
+            <Glyph value="g8"/>
+            <Glyph value="g9"/>
+            <Glyph value="g10"/>
+            <Glyph value="g11"/>
+            <Glyph value="g12"/>
+            <Glyph value="g13"/>
+            <Glyph value="g14"/>
+            <Glyph value="g15"/>
+            <Glyph value="g16"/>
+            <Glyph value="g17"/>
+            <Glyph value="g18"/>
+            <Glyph value="g19"/>
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+            <Glyph value="g30"/>
+            <Glyph value="g31"/>
+            <Glyph value="g32"/>
+            <Glyph value="g33"/>
+            <Glyph value="g34"/>
+            <Glyph value="g35"/>
+            <Glyph value="g36"/>
+            <Glyph value="g37"/>
+            <Glyph value="g38"/>
+            <Glyph value="g39"/>
+            <Glyph value="g40"/>
+            <Glyph value="g41"/>
+            <Glyph value="g42"/>
+            <Glyph value="g43"/>
+            <Glyph value="g44"/>
+            <Glyph value="g45"/>
+            <Glyph value="g46"/>
+            <Glyph value="g47"/>
+            <Glyph value="g48"/>
+            <Glyph value="g49"/>
+            <Glyph value="g50"/>
+            <Glyph value="g51"/>
+            <Glyph value="g52"/>
+            <Glyph value="g53"/>
+            <Glyph value="g54"/>
+            <Glyph value="g55"/>
+            <Glyph value="g56"/>
+            <Glyph value="g57"/>
+            <Glyph value="g58"/>
+            <Glyph value="g59"/>
+            <Glyph value="g60"/>
+            <Glyph value="g61"/>
+            <Glyph value="g62"/>
+            <Glyph value="g63"/>
+            <Glyph value="g64"/>
+            <Glyph value="g65"/>
+            <Glyph value="g66"/>
+            <Glyph value="g67"/>
+            <Glyph value="g68"/>
+            <Glyph value="g69"/>
+            <Glyph value="g70"/>
+            <Glyph value="g71"/>
+            <Glyph value="g72"/>
+            <Glyph value="g73"/>
+            <Glyph value="g74"/>
+            <Glyph value="g75"/>
+            <Glyph value="g76"/>
+            <Glyph value="g77"/>
+            <Glyph value="g78"/>
+            <Glyph value="g79"/>
+            <Glyph value="g80"/>
+            <Glyph value="g81"/>
+            <Glyph value="g82"/>
+            <Glyph value="g83"/>
+            <Glyph value="g84"/>
+            <Glyph value="g85"/>
+            <Glyph value="g86"/>
+            <Glyph value="g87"/>
+            <Glyph value="g88"/>
+            <Glyph value="g89"/>
+            <Glyph value="g90"/>
+            <Glyph value="g91"/>
+            <Glyph value="g92"/>
+            <Glyph value="g93"/>
+            <Glyph value="g94"/>
+            <Glyph value="g95"/>
+            <Glyph value="g96"/>
+            <Glyph value="g97"/>
+            <Glyph value="g98"/>
+            <Glyph value="g99"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g18" class="2"/>
+            <ClassDef glyph="g19" class="2"/>
+            <ClassDef glyph="g20" class="2"/>
+            <ClassDef glyph="g22" class="1"/>
+            <ClassDef glyph="g23" class="1"/>
+            <ClassDef glyph="g24" class="1"/>
+            <ClassDef glyph="g29" class="2"/>
+            <ClassDef glyph="g33" class="1"/>
+            <ClassDef glyph="g34" class="1"/>
+          </ClassDef>
+          <!-- SubClassSetCount=3 -->
+          <SubClassSet index="0">
+            <!-- SubClassRuleCount=1 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=1 -->
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+          </SubClassSet>
+          <SubClassSet index="1">
+            <!-- SubClassRuleCount=1 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=1 -->
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="1"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+          </SubClassSet>
+          <SubClassSet index="2">
+            <!-- SubClassRuleCount=1 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=1 -->
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="2"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+          </SubClassSet>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/classdef2_font4.otf b/Tests/ttLib/tables/data/aots/classdef2_font4.otf
new file mode 100644
index 0000000..c75c883
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/classdef2_font4.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/classdef2_font4.ttx.GSUB b/Tests/ttLib/tables/data/aots/classdef2_font4.ttx.GSUB
new file mode 100644
index 0000000..90f0b93
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/classdef2_font4.ttx.GSUB
@@ -0,0 +1,469 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="3"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=4 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in=".notdef" out="g3"/>
+          <Substitution in="g1" out="g4"/>
+          <Substitution in="g10" out="g13"/>
+          <Substitution in="g11" out="g14"/>
+          <Substitution in="g12" out="g15"/>
+          <Substitution in="g13" out="g16"/>
+          <Substitution in="g14" out="g17"/>
+          <Substitution in="g15" out="g18"/>
+          <Substitution in="g16" out="g19"/>
+          <Substitution in="g17" out="g20"/>
+          <Substitution in="g18" out="g21"/>
+          <Substitution in="g19" out="g22"/>
+          <Substitution in="g2" out="g5"/>
+          <Substitution in="g20" out="g23"/>
+          <Substitution in="g21" out="g24"/>
+          <Substitution in="g22" out="g25"/>
+          <Substitution in="g23" out="g26"/>
+          <Substitution in="g24" out="g27"/>
+          <Substitution in="g25" out="g28"/>
+          <Substitution in="g26" out="g29"/>
+          <Substitution in="g27" out="g30"/>
+          <Substitution in="g28" out="g31"/>
+          <Substitution in="g29" out="g32"/>
+          <Substitution in="g3" out="g6"/>
+          <Substitution in="g30" out="g33"/>
+          <Substitution in="g31" out="g34"/>
+          <Substitution in="g32" out="g35"/>
+          <Substitution in="g33" out="g36"/>
+          <Substitution in="g34" out="g37"/>
+          <Substitution in="g35" out="g38"/>
+          <Substitution in="g36" out="g39"/>
+          <Substitution in="g37" out="g40"/>
+          <Substitution in="g38" out="g41"/>
+          <Substitution in="g39" out="g42"/>
+          <Substitution in="g4" out="g7"/>
+          <Substitution in="g40" out="g43"/>
+          <Substitution in="g41" out="g44"/>
+          <Substitution in="g42" out="g45"/>
+          <Substitution in="g43" out="g46"/>
+          <Substitution in="g44" out="g47"/>
+          <Substitution in="g45" out="g48"/>
+          <Substitution in="g46" out="g49"/>
+          <Substitution in="g47" out="g50"/>
+          <Substitution in="g48" out="g51"/>
+          <Substitution in="g49" out="g52"/>
+          <Substitution in="g5" out="g8"/>
+          <Substitution in="g50" out="g53"/>
+          <Substitution in="g51" out="g54"/>
+          <Substitution in="g52" out="g55"/>
+          <Substitution in="g53" out="g56"/>
+          <Substitution in="g54" out="g57"/>
+          <Substitution in="g55" out="g58"/>
+          <Substitution in="g56" out="g59"/>
+          <Substitution in="g57" out="g60"/>
+          <Substitution in="g58" out="g61"/>
+          <Substitution in="g59" out="g62"/>
+          <Substitution in="g6" out="g9"/>
+          <Substitution in="g60" out="g63"/>
+          <Substitution in="g61" out="g64"/>
+          <Substitution in="g62" out="g65"/>
+          <Substitution in="g63" out="g66"/>
+          <Substitution in="g64" out="g67"/>
+          <Substitution in="g65" out="g68"/>
+          <Substitution in="g66" out="g69"/>
+          <Substitution in="g67" out="g70"/>
+          <Substitution in="g68" out="g71"/>
+          <Substitution in="g69" out="g72"/>
+          <Substitution in="g7" out="g10"/>
+          <Substitution in="g70" out="g73"/>
+          <Substitution in="g71" out="g74"/>
+          <Substitution in="g72" out="g75"/>
+          <Substitution in="g73" out="g76"/>
+          <Substitution in="g74" out="g77"/>
+          <Substitution in="g75" out="g78"/>
+          <Substitution in="g76" out="g79"/>
+          <Substitution in="g77" out="g80"/>
+          <Substitution in="g78" out="g81"/>
+          <Substitution in="g79" out="g82"/>
+          <Substitution in="g8" out="g11"/>
+          <Substitution in="g80" out="g83"/>
+          <Substitution in="g81" out="g84"/>
+          <Substitution in="g82" out="g85"/>
+          <Substitution in="g83" out="g86"/>
+          <Substitution in="g84" out="g87"/>
+          <Substitution in="g85" out="g88"/>
+          <Substitution in="g86" out="g89"/>
+          <Substitution in="g87" out="g90"/>
+          <Substitution in="g88" out="g91"/>
+          <Substitution in="g89" out="g92"/>
+          <Substitution in="g9" out="g12"/>
+          <Substitution in="g90" out="g93"/>
+          <Substitution in="g91" out="g94"/>
+          <Substitution in="g92" out="g95"/>
+          <Substitution in="g93" out="g96"/>
+          <Substitution in="g94" out="g97"/>
+          <Substitution in="g95" out="g98"/>
+          <Substitution in="g96" out="g99"/>
+          <Substitution in="g97" out="glyph00100"/>
+          <Substitution in="g98" out="glyph00101"/>
+          <Substitution in="g99" out="glyph00102"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in=".notdef" out="g4"/>
+          <Substitution in="g1" out="g5"/>
+          <Substitution in="g10" out="g14"/>
+          <Substitution in="g11" out="g15"/>
+          <Substitution in="g12" out="g16"/>
+          <Substitution in="g13" out="g17"/>
+          <Substitution in="g14" out="g18"/>
+          <Substitution in="g15" out="g19"/>
+          <Substitution in="g16" out="g20"/>
+          <Substitution in="g17" out="g21"/>
+          <Substitution in="g18" out="g22"/>
+          <Substitution in="g19" out="g23"/>
+          <Substitution in="g2" out="g6"/>
+          <Substitution in="g20" out="g24"/>
+          <Substitution in="g21" out="g25"/>
+          <Substitution in="g22" out="g26"/>
+          <Substitution in="g23" out="g27"/>
+          <Substitution in="g24" out="g28"/>
+          <Substitution in="g25" out="g29"/>
+          <Substitution in="g26" out="g30"/>
+          <Substitution in="g27" out="g31"/>
+          <Substitution in="g28" out="g32"/>
+          <Substitution in="g29" out="g33"/>
+          <Substitution in="g3" out="g7"/>
+          <Substitution in="g30" out="g34"/>
+          <Substitution in="g31" out="g35"/>
+          <Substitution in="g32" out="g36"/>
+          <Substitution in="g33" out="g37"/>
+          <Substitution in="g34" out="g38"/>
+          <Substitution in="g35" out="g39"/>
+          <Substitution in="g36" out="g40"/>
+          <Substitution in="g37" out="g41"/>
+          <Substitution in="g38" out="g42"/>
+          <Substitution in="g39" out="g43"/>
+          <Substitution in="g4" out="g8"/>
+          <Substitution in="g40" out="g44"/>
+          <Substitution in="g41" out="g45"/>
+          <Substitution in="g42" out="g46"/>
+          <Substitution in="g43" out="g47"/>
+          <Substitution in="g44" out="g48"/>
+          <Substitution in="g45" out="g49"/>
+          <Substitution in="g46" out="g50"/>
+          <Substitution in="g47" out="g51"/>
+          <Substitution in="g48" out="g52"/>
+          <Substitution in="g49" out="g53"/>
+          <Substitution in="g5" out="g9"/>
+          <Substitution in="g50" out="g54"/>
+          <Substitution in="g51" out="g55"/>
+          <Substitution in="g52" out="g56"/>
+          <Substitution in="g53" out="g57"/>
+          <Substitution in="g54" out="g58"/>
+          <Substitution in="g55" out="g59"/>
+          <Substitution in="g56" out="g60"/>
+          <Substitution in="g57" out="g61"/>
+          <Substitution in="g58" out="g62"/>
+          <Substitution in="g59" out="g63"/>
+          <Substitution in="g6" out="g10"/>
+          <Substitution in="g60" out="g64"/>
+          <Substitution in="g61" out="g65"/>
+          <Substitution in="g62" out="g66"/>
+          <Substitution in="g63" out="g67"/>
+          <Substitution in="g64" out="g68"/>
+          <Substitution in="g65" out="g69"/>
+          <Substitution in="g66" out="g70"/>
+          <Substitution in="g67" out="g71"/>
+          <Substitution in="g68" out="g72"/>
+          <Substitution in="g69" out="g73"/>
+          <Substitution in="g7" out="g11"/>
+          <Substitution in="g70" out="g74"/>
+          <Substitution in="g71" out="g75"/>
+          <Substitution in="g72" out="g76"/>
+          <Substitution in="g73" out="g77"/>
+          <Substitution in="g74" out="g78"/>
+          <Substitution in="g75" out="g79"/>
+          <Substitution in="g76" out="g80"/>
+          <Substitution in="g77" out="g81"/>
+          <Substitution in="g78" out="g82"/>
+          <Substitution in="g79" out="g83"/>
+          <Substitution in="g8" out="g12"/>
+          <Substitution in="g80" out="g84"/>
+          <Substitution in="g81" out="g85"/>
+          <Substitution in="g82" out="g86"/>
+          <Substitution in="g83" out="g87"/>
+          <Substitution in="g84" out="g88"/>
+          <Substitution in="g85" out="g89"/>
+          <Substitution in="g86" out="g90"/>
+          <Substitution in="g87" out="g91"/>
+          <Substitution in="g88" out="g92"/>
+          <Substitution in="g89" out="g93"/>
+          <Substitution in="g9" out="g13"/>
+          <Substitution in="g90" out="g94"/>
+          <Substitution in="g91" out="g95"/>
+          <Substitution in="g92" out="g96"/>
+          <Substitution in="g93" out="g97"/>
+          <Substitution in="g94" out="g98"/>
+          <Substitution in="g95" out="g99"/>
+          <Substitution in="g96" out="glyph00100"/>
+          <Substitution in="g97" out="glyph00101"/>
+          <Substitution in="g98" out="glyph00102"/>
+          <Substitution in="g99" out="glyph00103"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in=".notdef" out="g5"/>
+          <Substitution in="g1" out="g6"/>
+          <Substitution in="g10" out="g15"/>
+          <Substitution in="g11" out="g16"/>
+          <Substitution in="g12" out="g17"/>
+          <Substitution in="g13" out="g18"/>
+          <Substitution in="g14" out="g19"/>
+          <Substitution in="g15" out="g20"/>
+          <Substitution in="g16" out="g21"/>
+          <Substitution in="g17" out="g22"/>
+          <Substitution in="g18" out="g23"/>
+          <Substitution in="g19" out="g24"/>
+          <Substitution in="g2" out="g7"/>
+          <Substitution in="g20" out="g25"/>
+          <Substitution in="g21" out="g26"/>
+          <Substitution in="g22" out="g27"/>
+          <Substitution in="g23" out="g28"/>
+          <Substitution in="g24" out="g29"/>
+          <Substitution in="g25" out="g30"/>
+          <Substitution in="g26" out="g31"/>
+          <Substitution in="g27" out="g32"/>
+          <Substitution in="g28" out="g33"/>
+          <Substitution in="g29" out="g34"/>
+          <Substitution in="g3" out="g8"/>
+          <Substitution in="g30" out="g35"/>
+          <Substitution in="g31" out="g36"/>
+          <Substitution in="g32" out="g37"/>
+          <Substitution in="g33" out="g38"/>
+          <Substitution in="g34" out="g39"/>
+          <Substitution in="g35" out="g40"/>
+          <Substitution in="g36" out="g41"/>
+          <Substitution in="g37" out="g42"/>
+          <Substitution in="g38" out="g43"/>
+          <Substitution in="g39" out="g44"/>
+          <Substitution in="g4" out="g9"/>
+          <Substitution in="g40" out="g45"/>
+          <Substitution in="g41" out="g46"/>
+          <Substitution in="g42" out="g47"/>
+          <Substitution in="g43" out="g48"/>
+          <Substitution in="g44" out="g49"/>
+          <Substitution in="g45" out="g50"/>
+          <Substitution in="g46" out="g51"/>
+          <Substitution in="g47" out="g52"/>
+          <Substitution in="g48" out="g53"/>
+          <Substitution in="g49" out="g54"/>
+          <Substitution in="g5" out="g10"/>
+          <Substitution in="g50" out="g55"/>
+          <Substitution in="g51" out="g56"/>
+          <Substitution in="g52" out="g57"/>
+          <Substitution in="g53" out="g58"/>
+          <Substitution in="g54" out="g59"/>
+          <Substitution in="g55" out="g60"/>
+          <Substitution in="g56" out="g61"/>
+          <Substitution in="g57" out="g62"/>
+          <Substitution in="g58" out="g63"/>
+          <Substitution in="g59" out="g64"/>
+          <Substitution in="g6" out="g11"/>
+          <Substitution in="g60" out="g65"/>
+          <Substitution in="g61" out="g66"/>
+          <Substitution in="g62" out="g67"/>
+          <Substitution in="g63" out="g68"/>
+          <Substitution in="g64" out="g69"/>
+          <Substitution in="g65" out="g70"/>
+          <Substitution in="g66" out="g71"/>
+          <Substitution in="g67" out="g72"/>
+          <Substitution in="g68" out="g73"/>
+          <Substitution in="g69" out="g74"/>
+          <Substitution in="g7" out="g12"/>
+          <Substitution in="g70" out="g75"/>
+          <Substitution in="g71" out="g76"/>
+          <Substitution in="g72" out="g77"/>
+          <Substitution in="g73" out="g78"/>
+          <Substitution in="g74" out="g79"/>
+          <Substitution in="g75" out="g80"/>
+          <Substitution in="g76" out="g81"/>
+          <Substitution in="g77" out="g82"/>
+          <Substitution in="g78" out="g83"/>
+          <Substitution in="g79" out="g84"/>
+          <Substitution in="g8" out="g13"/>
+          <Substitution in="g80" out="g85"/>
+          <Substitution in="g81" out="g86"/>
+          <Substitution in="g82" out="g87"/>
+          <Substitution in="g83" out="g88"/>
+          <Substitution in="g84" out="g89"/>
+          <Substitution in="g85" out="g90"/>
+          <Substitution in="g86" out="g91"/>
+          <Substitution in="g87" out="g92"/>
+          <Substitution in="g88" out="g93"/>
+          <Substitution in="g89" out="g94"/>
+          <Substitution in="g9" out="g14"/>
+          <Substitution in="g90" out="g95"/>
+          <Substitution in="g91" out="g96"/>
+          <Substitution in="g92" out="g97"/>
+          <Substitution in="g93" out="g98"/>
+          <Substitution in="g94" out="g99"/>
+          <Substitution in="g95" out="glyph00100"/>
+          <Substitution in="g96" out="glyph00101"/>
+          <Substitution in="g97" out="glyph00102"/>
+          <Substitution in="g98" out="glyph00103"/>
+          <Substitution in="g99" out="glyph00104"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value=".notdef"/>
+            <Glyph value="g1"/>
+            <Glyph value="g2"/>
+            <Glyph value="g3"/>
+            <Glyph value="g4"/>
+            <Glyph value="g5"/>
+            <Glyph value="g6"/>
+            <Glyph value="g7"/>
+            <Glyph value="g8"/>
+            <Glyph value="g9"/>
+            <Glyph value="g10"/>
+            <Glyph value="g11"/>
+            <Glyph value="g12"/>
+            <Glyph value="g13"/>
+            <Glyph value="g14"/>
+            <Glyph value="g15"/>
+            <Glyph value="g16"/>
+            <Glyph value="g17"/>
+            <Glyph value="g18"/>
+            <Glyph value="g19"/>
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+            <Glyph value="g30"/>
+            <Glyph value="g31"/>
+            <Glyph value="g32"/>
+            <Glyph value="g33"/>
+            <Glyph value="g34"/>
+            <Glyph value="g35"/>
+            <Glyph value="g36"/>
+            <Glyph value="g37"/>
+            <Glyph value="g38"/>
+            <Glyph value="g39"/>
+            <Glyph value="g40"/>
+            <Glyph value="g41"/>
+            <Glyph value="g42"/>
+            <Glyph value="g43"/>
+            <Glyph value="g44"/>
+            <Glyph value="g45"/>
+            <Glyph value="g46"/>
+            <Glyph value="g47"/>
+            <Glyph value="g48"/>
+            <Glyph value="g49"/>
+            <Glyph value="g50"/>
+            <Glyph value="g51"/>
+            <Glyph value="g52"/>
+            <Glyph value="g53"/>
+            <Glyph value="g54"/>
+            <Glyph value="g55"/>
+            <Glyph value="g56"/>
+            <Glyph value="g57"/>
+            <Glyph value="g58"/>
+            <Glyph value="g59"/>
+            <Glyph value="g60"/>
+            <Glyph value="g61"/>
+            <Glyph value="g62"/>
+            <Glyph value="g63"/>
+            <Glyph value="g64"/>
+            <Glyph value="g65"/>
+            <Glyph value="g66"/>
+            <Glyph value="g67"/>
+            <Glyph value="g68"/>
+            <Glyph value="g69"/>
+            <Glyph value="g70"/>
+            <Glyph value="g71"/>
+            <Glyph value="g72"/>
+            <Glyph value="g73"/>
+            <Glyph value="g74"/>
+            <Glyph value="g75"/>
+            <Glyph value="g76"/>
+            <Glyph value="g77"/>
+            <Glyph value="g78"/>
+            <Glyph value="g79"/>
+            <Glyph value="g80"/>
+            <Glyph value="g81"/>
+            <Glyph value="g82"/>
+            <Glyph value="g83"/>
+            <Glyph value="g84"/>
+            <Glyph value="g85"/>
+            <Glyph value="g86"/>
+            <Glyph value="g87"/>
+            <Glyph value="g88"/>
+            <Glyph value="g89"/>
+            <Glyph value="g90"/>
+            <Glyph value="g91"/>
+            <Glyph value="g92"/>
+            <Glyph value="g93"/>
+            <Glyph value="g94"/>
+            <Glyph value="g95"/>
+            <Glyph value="g96"/>
+            <Glyph value="g97"/>
+            <Glyph value="g98"/>
+            <Glyph value="g99"/>
+          </Coverage>
+          <ClassDef Format="2">
+          </ClassDef>
+          <!-- SubClassSetCount=1 -->
+          <SubClassSet index="0" empty="1"/>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/cmap0_font1.otf b/Tests/ttLib/tables/data/aots/cmap0_font1.otf
new file mode 100644
index 0000000..772f9a7
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap0_font1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/cmap0_font1.ttx.cmap b/Tests/ttLib/tables/data/aots/cmap0_font1.ttx.cmap
new file mode 100644
index 0000000..bedbeba
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap0_font1.ttx.cmap
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_0 platformID="3" platEncID="1" language="0">
+      <map code="0x34" name="g17"/><!-- DIGIT FOUR -->
+      <map code="0x35" name="g56"/><!-- DIGIT FIVE -->
+      <map code="0x36" name="g12"/><!-- DIGIT SIX -->
+    </cmap_format_0>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/cmap10_font1.otf b/Tests/ttLib/tables/data/aots/cmap10_font1.otf
new file mode 100644
index 0000000..023e945
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap10_font1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/cmap10_font1.ttx.cmap b/Tests/ttLib/tables/data/aots/cmap10_font1.ttx.cmap
new file mode 100644
index 0000000..dc1b5f4
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap10_font1.ttx.cmap
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_10 platformID="3" platEncID="1">
+      000a0000 0000001a 00000000 00109423
+      00000003 001a001b 0020 
+    </cmap_format_10>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/cmap10_font2.otf b/Tests/ttLib/tables/data/aots/cmap10_font2.otf
new file mode 100644
index 0000000..5202f79
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap10_font2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/cmap10_font2.ttx.cmap b/Tests/ttLib/tables/data/aots/cmap10_font2.ttx.cmap
new file mode 100644
index 0000000..430c1b1
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap10_font2.ttx.cmap
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_10 platformID="3" platEncID="1">
+      000a0000 00000014 00000000 00000000
+      00000000   
+    </cmap_format_10>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/cmap12_font1.otf b/Tests/ttLib/tables/data/aots/cmap12_font1.otf
new file mode 100644
index 0000000..2d74b3a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap12_font1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/cmap12_font1.ttx.cmap b/Tests/ttLib/tables/data/aots/cmap12_font1.ttx.cmap
new file mode 100644
index 0000000..e47cebe
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap12_font1.ttx.cmap
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_12 platformID="3" platEncID="1" format="12" reserved="0" length="40" language="0" nGroups="2">
+      <map code="0x101723" name="g23"/><!-- ???? -->
+      <map code="0x101724" name="g24"/><!-- ???? -->
+      <map code="0x101725" name="g25"/><!-- ???? -->
+      <map code="0x101726" name="g26"/><!-- ???? -->
+      <map code="0x101727" name="g27"/><!-- ???? -->
+      <map code="0x102523" name="g53"/><!-- ???? -->
+      <map code="0x102524" name="g54"/><!-- ???? -->
+      <map code="0x102525" name="g55"/><!-- ???? -->
+      <map code="0x102526" name="g56"/><!-- ???? -->
+      <map code="0x102527" name="g57"/><!-- ???? -->
+    </cmap_format_12>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/cmap14_font1.otf b/Tests/ttLib/tables/data/aots/cmap14_font1.otf
new file mode 100644
index 0000000..a8e941d
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap14_font1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/cmap14_font1.ttx.cmap b/Tests/ttLib/tables/data/aots/cmap14_font1.ttx.cmap
new file mode 100644
index 0000000..ced8c8a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap14_font1.ttx.cmap
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_14 platformID="0" platEncID="5" format="14" length="47" numVarSelectorRecords="1">
+      <map uvs="0xe0100" uv="0x4e00" name="None"/>
+      <map uvs="0xe0100" uv="0x4e03" name="None"/>
+      <map uvs="0xe0100" uv="0x4e04" name="None"/>
+      <map uvs="0xe0100" uv="0x4e05" name="None"/>
+      <map uvs="0xe0100" uv="0x4e06" name="None"/>
+      <map uvs="0xe0100" uv="0x4e10" name="g25"/>
+      <map uvs="0xe0100" uv="0x4e11" name="g26"/>
+    </cmap_format_14>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x4e00" name="g10"/><!-- CJK UNIFIED IDEOGRAPH-4E00 -->
+      <map code="0x4e01" name="g11"/><!-- CJK UNIFIED IDEOGRAPH-4E01 -->
+      <map code="0x4e02" name="g12"/><!-- CJK UNIFIED IDEOGRAPH-4E02 -->
+      <map code="0x4e03" name="g13"/><!-- CJK UNIFIED IDEOGRAPH-4E03 -->
+      <map code="0x4e04" name="g14"/><!-- CJK UNIFIED IDEOGRAPH-4E04 -->
+      <map code="0x4e05" name="g15"/><!-- CJK UNIFIED IDEOGRAPH-4E05 -->
+      <map code="0x4e06" name="g16"/><!-- CJK UNIFIED IDEOGRAPH-4E06 -->
+      <map code="0x4e07" name="g17"/><!-- CJK UNIFIED IDEOGRAPH-4E07 -->
+      <map code="0x4e08" name="g18"/><!-- CJK UNIFIED IDEOGRAPH-4E08 -->
+      <map code="0x4e09" name="g19"/><!-- CJK UNIFIED IDEOGRAPH-4E09 -->
+    </cmap_format_4>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/cmap2_font1.otf b/Tests/ttLib/tables/data/aots/cmap2_font1.otf
new file mode 100644
index 0000000..a123d9c
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap2_font1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/cmap2_font1.ttx.cmap b/Tests/ttLib/tables/data/aots/cmap2_font1.ttx.cmap
new file mode 100644
index 0000000..813e750
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap2_font1.ttx.cmap
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_2 platformID="3" platEncID="1" language="0">
+      <map code="0x34" name="g17"/><!-- DIGIT FOUR -->
+      <map code="0x35" name="g56"/><!-- DIGIT FIVE -->
+      <map code="0x36" name="g12"/><!-- DIGIT SIX -->
+      <map code="0x8432" name="g20"/><!-- CJK UNIFIED IDEOGRAPH-8432 -->
+      <map code="0x8433" name="g21"/><!-- CJK UNIFIED IDEOGRAPH-8433 -->
+      <map code="0x8434" name="g22"/><!-- CJK UNIFIED IDEOGRAPH-8434 -->
+      <map code="0x9232" name="g23"/><!-- CJK UNIFIED IDEOGRAPH-9232 -->
+      <map code="0x9233" name="g24"/><!-- CJK UNIFIED IDEOGRAPH-9233 -->
+      <map code="0x9234" name="g25"/><!-- CJK UNIFIED IDEOGRAPH-9234 -->
+    </cmap_format_2>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/cmap4_font1.otf b/Tests/ttLib/tables/data/aots/cmap4_font1.otf
new file mode 100644
index 0000000..516ed8e
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap4_font1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/cmap4_font1.ttx.cmap b/Tests/ttLib/tables/data/aots/cmap4_font1.ttx.cmap
new file mode 100644
index 0000000..e194b71
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap4_font1.ttx.cmap
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x11" name="g40"/><!-- ???? -->
+      <map code="0x12" name="g41"/><!-- ???? -->
+      <map code="0x13" name="g42"/><!-- ???? -->
+      <map code="0x14" name="g43"/><!-- ???? -->
+      <map code="0x15" name="g44"/><!-- ???? -->
+      <map code="0x16" name="g45"/><!-- ???? -->
+      <map code="0x17" name="g46"/><!-- ???? -->
+      <map code="0x18" name="g47"/><!-- ???? -->
+      <map code="0x19" name="g48"/><!-- ???? -->
+      <map code="0x1a" name="g49"/><!-- ???? -->
+      <map code="0x1b" name="g50"/><!-- ???? -->
+      <map code="0x1c" name="g51"/><!-- ???? -->
+      <map code="0x1d" name="g52"/><!-- ???? -->
+      <map code="0x1e" name="g53"/><!-- ???? -->
+    </cmap_format_4>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/cmap4_font2.otf b/Tests/ttLib/tables/data/aots/cmap4_font2.otf
new file mode 100644
index 0000000..0f678a3
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap4_font2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/cmap4_font2.ttx.cmap b/Tests/ttLib/tables/data/aots/cmap4_font2.ttx.cmap
new file mode 100644
index 0000000..f76ce83
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap4_font2.ttx.cmap
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+    </cmap_format_4>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/cmap4_font3.otf b/Tests/ttLib/tables/data/aots/cmap4_font3.otf
new file mode 100644
index 0000000..2034ecd
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap4_font3.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/cmap4_font3.ttx.cmap b/Tests/ttLib/tables/data/aots/cmap4_font3.ttx.cmap
new file mode 100644
index 0000000..f76ce83
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap4_font3.ttx.cmap
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+    </cmap_format_4>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/cmap4_font4.otf b/Tests/ttLib/tables/data/aots/cmap4_font4.otf
new file mode 100644
index 0000000..450508e
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap4_font4.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/cmap4_font4.ttx.cmap b/Tests/ttLib/tables/data/aots/cmap4_font4.ttx.cmap
new file mode 100644
index 0000000..df81c68
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap4_font4.ttx.cmap
@@ -0,0 +1,1011 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x64" name="glyph65136"/><!-- LATIN SMALL LETTER D -->
+      <map code="0x65" name="glyph65137"/><!-- LATIN SMALL LETTER E -->
+      <map code="0x66" name="glyph65138"/><!-- LATIN SMALL LETTER F -->
+      <map code="0x67" name="glyph65139"/><!-- LATIN SMALL LETTER G -->
+      <map code="0x68" name="glyph65140"/><!-- LATIN SMALL LETTER H -->
+      <map code="0x69" name="glyph65141"/><!-- LATIN SMALL LETTER I -->
+      <map code="0x6a" name="glyph65142"/><!-- LATIN SMALL LETTER J -->
+      <map code="0x6b" name="glyph65143"/><!-- LATIN SMALL LETTER K -->
+      <map code="0x6c" name="glyph65144"/><!-- LATIN SMALL LETTER L -->
+      <map code="0x6d" name="glyph65145"/><!-- LATIN SMALL LETTER M -->
+      <map code="0x6e" name="glyph65146"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x6f" name="glyph65147"/><!-- LATIN SMALL LETTER O -->
+      <map code="0x70" name="glyph65148"/><!-- LATIN SMALL LETTER P -->
+      <map code="0x71" name="glyph65149"/><!-- LATIN SMALL LETTER Q -->
+      <map code="0x72" name="glyph65150"/><!-- LATIN SMALL LETTER R -->
+      <map code="0x73" name="glyph65151"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x74" name="glyph65152"/><!-- LATIN SMALL LETTER T -->
+      <map code="0x75" name="glyph65153"/><!-- LATIN SMALL LETTER U -->
+      <map code="0x76" name="glyph65154"/><!-- LATIN SMALL LETTER V -->
+      <map code="0x77" name="glyph65155"/><!-- LATIN SMALL LETTER W -->
+      <map code="0x78" name="glyph65156"/><!-- LATIN SMALL LETTER X -->
+      <map code="0x79" name="glyph65157"/><!-- LATIN SMALL LETTER Y -->
+      <map code="0x7a" name="glyph65158"/><!-- LATIN SMALL LETTER Z -->
+      <map code="0x7b" name="glyph65159"/><!-- LEFT CURLY BRACKET -->
+      <map code="0x7c" name="glyph65160"/><!-- VERTICAL LINE -->
+      <map code="0x7d" name="glyph65161"/><!-- RIGHT CURLY BRACKET -->
+      <map code="0x7e" name="glyph65162"/><!-- TILDE -->
+      <map code="0x7f" name="glyph65163"/><!-- ???? -->
+      <map code="0x80" name="glyph65164"/><!-- ???? -->
+      <map code="0x81" name="glyph65165"/><!-- ???? -->
+      <map code="0x82" name="glyph65166"/><!-- ???? -->
+      <map code="0x83" name="glyph65167"/><!-- ???? -->
+      <map code="0x84" name="glyph65168"/><!-- ???? -->
+      <map code="0x85" name="glyph65169"/><!-- ???? -->
+      <map code="0x86" name="glyph65170"/><!-- ???? -->
+      <map code="0x87" name="glyph65171"/><!-- ???? -->
+      <map code="0x88" name="glyph65172"/><!-- ???? -->
+      <map code="0x89" name="glyph65173"/><!-- ???? -->
+      <map code="0x8a" name="glyph65174"/><!-- ???? -->
+      <map code="0x8b" name="glyph65175"/><!-- ???? -->
+      <map code="0x8c" name="glyph65176"/><!-- ???? -->
+      <map code="0x8d" name="glyph65177"/><!-- ???? -->
+      <map code="0x8e" name="glyph65178"/><!-- ???? -->
+      <map code="0x8f" name="glyph65179"/><!-- ???? -->
+      <map code="0x90" name="glyph65180"/><!-- ???? -->
+      <map code="0x91" name="glyph65181"/><!-- ???? -->
+      <map code="0x92" name="glyph65182"/><!-- ???? -->
+      <map code="0x93" name="glyph65183"/><!-- ???? -->
+      <map code="0x94" name="glyph65184"/><!-- ???? -->
+      <map code="0x95" name="glyph65185"/><!-- ???? -->
+      <map code="0x96" name="glyph65186"/><!-- ???? -->
+      <map code="0x97" name="glyph65187"/><!-- ???? -->
+      <map code="0x98" name="glyph65188"/><!-- ???? -->
+      <map code="0x99" name="glyph65189"/><!-- ???? -->
+      <map code="0x9a" name="glyph65190"/><!-- ???? -->
+      <map code="0x9b" name="glyph65191"/><!-- ???? -->
+      <map code="0x9c" name="glyph65192"/><!-- ???? -->
+      <map code="0x9d" name="glyph65193"/><!-- ???? -->
+      <map code="0x9e" name="glyph65194"/><!-- ???? -->
+      <map code="0x9f" name="glyph65195"/><!-- ???? -->
+      <map code="0xa0" name="glyph65196"/><!-- NO-BREAK SPACE -->
+      <map code="0xa1" name="glyph65197"/><!-- INVERTED EXCLAMATION MARK -->
+      <map code="0xa2" name="glyph65198"/><!-- CENT SIGN -->
+      <map code="0xa3" name="glyph65199"/><!-- POUND SIGN -->
+      <map code="0xa4" name="glyph65200"/><!-- CURRENCY SIGN -->
+      <map code="0xa5" name="glyph65201"/><!-- YEN SIGN -->
+      <map code="0xa6" name="glyph65202"/><!-- BROKEN BAR -->
+      <map code="0xa7" name="glyph65203"/><!-- SECTION SIGN -->
+      <map code="0xa8" name="glyph65204"/><!-- DIAERESIS -->
+      <map code="0xa9" name="glyph65205"/><!-- COPYRIGHT SIGN -->
+      <map code="0xaa" name="glyph65206"/><!-- FEMININE ORDINAL INDICATOR -->
+      <map code="0xab" name="glyph65207"/><!-- LEFT-POINTING DOUBLE ANGLE QUOTATION MARK -->
+      <map code="0xac" name="glyph65208"/><!-- NOT SIGN -->
+      <map code="0xad" name="glyph65209"/><!-- SOFT HYPHEN -->
+      <map code="0xae" name="glyph65210"/><!-- REGISTERED SIGN -->
+      <map code="0xaf" name="glyph65211"/><!-- MACRON -->
+      <map code="0xb0" name="glyph65212"/><!-- DEGREE SIGN -->
+      <map code="0xb1" name="glyph65213"/><!-- PLUS-MINUS SIGN -->
+      <map code="0xb2" name="glyph65214"/><!-- SUPERSCRIPT TWO -->
+      <map code="0xb3" name="glyph65215"/><!-- SUPERSCRIPT THREE -->
+      <map code="0xb4" name="glyph65216"/><!-- ACUTE ACCENT -->
+      <map code="0xb5" name="glyph65217"/><!-- MICRO SIGN -->
+      <map code="0xb6" name="glyph65218"/><!-- PILCROW SIGN -->
+      <map code="0xb7" name="glyph65219"/><!-- MIDDLE DOT -->
+      <map code="0xb8" name="glyph65220"/><!-- CEDILLA -->
+      <map code="0xb9" name="glyph65221"/><!-- SUPERSCRIPT ONE -->
+      <map code="0xba" name="glyph65222"/><!-- MASCULINE ORDINAL INDICATOR -->
+      <map code="0xbb" name="glyph65223"/><!-- RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK -->
+      <map code="0xbc" name="glyph65224"/><!-- VULGAR FRACTION ONE QUARTER -->
+      <map code="0xbd" name="glyph65225"/><!-- VULGAR FRACTION ONE HALF -->
+      <map code="0xbe" name="glyph65226"/><!-- VULGAR FRACTION THREE QUARTERS -->
+      <map code="0xbf" name="glyph65227"/><!-- INVERTED QUESTION MARK -->
+      <map code="0xc0" name="glyph65228"/><!-- LATIN CAPITAL LETTER A WITH GRAVE -->
+      <map code="0xc1" name="glyph65229"/><!-- LATIN CAPITAL LETTER A WITH ACUTE -->
+      <map code="0xc2" name="glyph65230"/><!-- LATIN CAPITAL LETTER A WITH CIRCUMFLEX -->
+      <map code="0xc3" name="glyph65231"/><!-- LATIN CAPITAL LETTER A WITH TILDE -->
+      <map code="0xc4" name="glyph65232"/><!-- LATIN CAPITAL LETTER A WITH DIAERESIS -->
+      <map code="0xc5" name="glyph65233"/><!-- LATIN CAPITAL LETTER A WITH RING ABOVE -->
+      <map code="0xc6" name="glyph65234"/><!-- LATIN CAPITAL LETTER AE -->
+      <map code="0xc7" name="glyph65235"/><!-- LATIN CAPITAL LETTER C WITH CEDILLA -->
+      <map code="0xc8" name="glyph65236"/><!-- LATIN CAPITAL LETTER E WITH GRAVE -->
+      <map code="0xc9" name="glyph65237"/><!-- LATIN CAPITAL LETTER E WITH ACUTE -->
+      <map code="0xca" name="glyph65238"/><!-- LATIN CAPITAL LETTER E WITH CIRCUMFLEX -->
+      <map code="0xcb" name="glyph65239"/><!-- LATIN CAPITAL LETTER E WITH DIAERESIS -->
+      <map code="0xcc" name="glyph65240"/><!-- LATIN CAPITAL LETTER I WITH GRAVE -->
+      <map code="0xcd" name="glyph65241"/><!-- LATIN CAPITAL LETTER I WITH ACUTE -->
+      <map code="0xce" name="glyph65242"/><!-- LATIN CAPITAL LETTER I WITH CIRCUMFLEX -->
+      <map code="0xcf" name="glyph65243"/><!-- LATIN CAPITAL LETTER I WITH DIAERESIS -->
+      <map code="0xd0" name="glyph65244"/><!-- LATIN CAPITAL LETTER ETH -->
+      <map code="0xd1" name="glyph65245"/><!-- LATIN CAPITAL LETTER N WITH TILDE -->
+      <map code="0xd2" name="glyph65246"/><!-- LATIN CAPITAL LETTER O WITH GRAVE -->
+      <map code="0xd3" name="glyph65247"/><!-- LATIN CAPITAL LETTER O WITH ACUTE -->
+      <map code="0xd4" name="glyph65248"/><!-- LATIN CAPITAL LETTER O WITH CIRCUMFLEX -->
+      <map code="0xd5" name="glyph65249"/><!-- LATIN CAPITAL LETTER O WITH TILDE -->
+      <map code="0xd6" name="glyph65250"/><!-- LATIN CAPITAL LETTER O WITH DIAERESIS -->
+      <map code="0xd7" name="glyph65251"/><!-- MULTIPLICATION SIGN -->
+      <map code="0xd8" name="glyph65252"/><!-- LATIN CAPITAL LETTER O WITH STROKE -->
+      <map code="0xd9" name="glyph65253"/><!-- LATIN CAPITAL LETTER U WITH GRAVE -->
+      <map code="0xda" name="glyph65254"/><!-- LATIN CAPITAL LETTER U WITH ACUTE -->
+      <map code="0xdb" name="glyph65255"/><!-- LATIN CAPITAL LETTER U WITH CIRCUMFLEX -->
+      <map code="0xdc" name="glyph65256"/><!-- LATIN CAPITAL LETTER U WITH DIAERESIS -->
+      <map code="0xdd" name="glyph65257"/><!-- LATIN CAPITAL LETTER Y WITH ACUTE -->
+      <map code="0xde" name="glyph65258"/><!-- LATIN CAPITAL LETTER THORN -->
+      <map code="0xdf" name="glyph65259"/><!-- LATIN SMALL LETTER SHARP S -->
+      <map code="0xe0" name="glyph65260"/><!-- LATIN SMALL LETTER A WITH GRAVE -->
+      <map code="0xe1" name="glyph65261"/><!-- LATIN SMALL LETTER A WITH ACUTE -->
+      <map code="0xe2" name="glyph65262"/><!-- LATIN SMALL LETTER A WITH CIRCUMFLEX -->
+      <map code="0xe3" name="glyph65263"/><!-- LATIN SMALL LETTER A WITH TILDE -->
+      <map code="0xe4" name="glyph65264"/><!-- LATIN SMALL LETTER A WITH DIAERESIS -->
+      <map code="0xe5" name="glyph65265"/><!-- LATIN SMALL LETTER A WITH RING ABOVE -->
+      <map code="0xe6" name="glyph65266"/><!-- LATIN SMALL LETTER AE -->
+      <map code="0xe7" name="glyph65267"/><!-- LATIN SMALL LETTER C WITH CEDILLA -->
+      <map code="0xe8" name="glyph65268"/><!-- LATIN SMALL LETTER E WITH GRAVE -->
+      <map code="0xe9" name="glyph65269"/><!-- LATIN SMALL LETTER E WITH ACUTE -->
+      <map code="0xea" name="glyph65270"/><!-- LATIN SMALL LETTER E WITH CIRCUMFLEX -->
+      <map code="0xeb" name="glyph65271"/><!-- LATIN SMALL LETTER E WITH DIAERESIS -->
+      <map code="0xec" name="glyph65272"/><!-- LATIN SMALL LETTER I WITH GRAVE -->
+      <map code="0xed" name="glyph65273"/><!-- LATIN SMALL LETTER I WITH ACUTE -->
+      <map code="0xee" name="glyph65274"/><!-- LATIN SMALL LETTER I WITH CIRCUMFLEX -->
+      <map code="0xef" name="glyph65275"/><!-- LATIN SMALL LETTER I WITH DIAERESIS -->
+      <map code="0xf0" name="glyph65276"/><!-- LATIN SMALL LETTER ETH -->
+      <map code="0xf1" name="glyph65277"/><!-- LATIN SMALL LETTER N WITH TILDE -->
+      <map code="0xf2" name="glyph65278"/><!-- LATIN SMALL LETTER O WITH GRAVE -->
+      <map code="0xf3" name="glyph65279"/><!-- LATIN SMALL LETTER O WITH ACUTE -->
+      <map code="0xf4" name="glyph65280"/><!-- LATIN SMALL LETTER O WITH CIRCUMFLEX -->
+      <map code="0xf5" name="glyph65281"/><!-- LATIN SMALL LETTER O WITH TILDE -->
+      <map code="0xf6" name="glyph65282"/><!-- LATIN SMALL LETTER O WITH DIAERESIS -->
+      <map code="0xf7" name="glyph65283"/><!-- DIVISION SIGN -->
+      <map code="0xf8" name="glyph65284"/><!-- LATIN SMALL LETTER O WITH STROKE -->
+      <map code="0xf9" name="glyph65285"/><!-- LATIN SMALL LETTER U WITH GRAVE -->
+      <map code="0xfa" name="glyph65286"/><!-- LATIN SMALL LETTER U WITH ACUTE -->
+      <map code="0xfb" name="glyph65287"/><!-- LATIN SMALL LETTER U WITH CIRCUMFLEX -->
+      <map code="0xfc" name="glyph65288"/><!-- LATIN SMALL LETTER U WITH DIAERESIS -->
+      <map code="0xfd" name="glyph65289"/><!-- LATIN SMALL LETTER Y WITH ACUTE -->
+      <map code="0xfe" name="glyph65290"/><!-- LATIN SMALL LETTER THORN -->
+      <map code="0xff" name="glyph65291"/><!-- LATIN SMALL LETTER Y WITH DIAERESIS -->
+      <map code="0x100" name="glyph65292"/><!-- LATIN CAPITAL LETTER A WITH MACRON -->
+      <map code="0x101" name="glyph65293"/><!-- LATIN SMALL LETTER A WITH MACRON -->
+      <map code="0x102" name="glyph65294"/><!-- LATIN CAPITAL LETTER A WITH BREVE -->
+      <map code="0x103" name="glyph65295"/><!-- LATIN SMALL LETTER A WITH BREVE -->
+      <map code="0x104" name="glyph65296"/><!-- LATIN CAPITAL LETTER A WITH OGONEK -->
+      <map code="0x105" name="glyph65297"/><!-- LATIN SMALL LETTER A WITH OGONEK -->
+      <map code="0x106" name="glyph65298"/><!-- LATIN CAPITAL LETTER C WITH ACUTE -->
+      <map code="0x107" name="glyph65299"/><!-- LATIN SMALL LETTER C WITH ACUTE -->
+      <map code="0x108" name="glyph65300"/><!-- LATIN CAPITAL LETTER C WITH CIRCUMFLEX -->
+      <map code="0x109" name="glyph65301"/><!-- LATIN SMALL LETTER C WITH CIRCUMFLEX -->
+      <map code="0x10a" name="glyph65302"/><!-- LATIN CAPITAL LETTER C WITH DOT ABOVE -->
+      <map code="0x10b" name="glyph65303"/><!-- LATIN SMALL LETTER C WITH DOT ABOVE -->
+      <map code="0x10c" name="glyph65304"/><!-- LATIN CAPITAL LETTER C WITH CARON -->
+      <map code="0x10d" name="glyph65305"/><!-- LATIN SMALL LETTER C WITH CARON -->
+      <map code="0x10e" name="glyph65306"/><!-- LATIN CAPITAL LETTER D WITH CARON -->
+      <map code="0x10f" name="glyph65307"/><!-- LATIN SMALL LETTER D WITH CARON -->
+      <map code="0x110" name="glyph65308"/><!-- LATIN CAPITAL LETTER D WITH STROKE -->
+      <map code="0x111" name="glyph65309"/><!-- LATIN SMALL LETTER D WITH STROKE -->
+      <map code="0x112" name="glyph65310"/><!-- LATIN CAPITAL LETTER E WITH MACRON -->
+      <map code="0x113" name="glyph65311"/><!-- LATIN SMALL LETTER E WITH MACRON -->
+      <map code="0x114" name="glyph65312"/><!-- LATIN CAPITAL LETTER E WITH BREVE -->
+      <map code="0x115" name="glyph65313"/><!-- LATIN SMALL LETTER E WITH BREVE -->
+      <map code="0x116" name="glyph65314"/><!-- LATIN CAPITAL LETTER E WITH DOT ABOVE -->
+      <map code="0x117" name="glyph65315"/><!-- LATIN SMALL LETTER E WITH DOT ABOVE -->
+      <map code="0x118" name="glyph65316"/><!-- LATIN CAPITAL LETTER E WITH OGONEK -->
+      <map code="0x119" name="glyph65317"/><!-- LATIN SMALL LETTER E WITH OGONEK -->
+      <map code="0x11a" name="glyph65318"/><!-- LATIN CAPITAL LETTER E WITH CARON -->
+      <map code="0x11b" name="glyph65319"/><!-- LATIN SMALL LETTER E WITH CARON -->
+      <map code="0x11c" name="glyph65320"/><!-- LATIN CAPITAL LETTER G WITH CIRCUMFLEX -->
+      <map code="0x11d" name="glyph65321"/><!-- LATIN SMALL LETTER G WITH CIRCUMFLEX -->
+      <map code="0x11e" name="glyph65322"/><!-- LATIN CAPITAL LETTER G WITH BREVE -->
+      <map code="0x11f" name="glyph65323"/><!-- LATIN SMALL LETTER G WITH BREVE -->
+      <map code="0x120" name="glyph65324"/><!-- LATIN CAPITAL LETTER G WITH DOT ABOVE -->
+      <map code="0x121" name="glyph65325"/><!-- LATIN SMALL LETTER G WITH DOT ABOVE -->
+      <map code="0x122" name="glyph65326"/><!-- LATIN CAPITAL LETTER G WITH CEDILLA -->
+      <map code="0x123" name="glyph65327"/><!-- LATIN SMALL LETTER G WITH CEDILLA -->
+      <map code="0x124" name="glyph65328"/><!-- LATIN CAPITAL LETTER H WITH CIRCUMFLEX -->
+      <map code="0x125" name="glyph65329"/><!-- LATIN SMALL LETTER H WITH CIRCUMFLEX -->
+      <map code="0x126" name="glyph65330"/><!-- LATIN CAPITAL LETTER H WITH STROKE -->
+      <map code="0x127" name="glyph65331"/><!-- LATIN SMALL LETTER H WITH STROKE -->
+      <map code="0x128" name="glyph65332"/><!-- LATIN CAPITAL LETTER I WITH TILDE -->
+      <map code="0x129" name="glyph65333"/><!-- LATIN SMALL LETTER I WITH TILDE -->
+      <map code="0x12a" name="glyph65334"/><!-- LATIN CAPITAL LETTER I WITH MACRON -->
+      <map code="0x12b" name="glyph65335"/><!-- LATIN SMALL LETTER I WITH MACRON -->
+      <map code="0x12c" name="glyph65336"/><!-- LATIN CAPITAL LETTER I WITH BREVE -->
+      <map code="0x12d" name="glyph65337"/><!-- LATIN SMALL LETTER I WITH BREVE -->
+      <map code="0x12e" name="glyph65338"/><!-- LATIN CAPITAL LETTER I WITH OGONEK -->
+      <map code="0x12f" name="glyph65339"/><!-- LATIN SMALL LETTER I WITH OGONEK -->
+      <map code="0x130" name="glyph65340"/><!-- LATIN CAPITAL LETTER I WITH DOT ABOVE -->
+      <map code="0x131" name="glyph65341"/><!-- LATIN SMALL LETTER DOTLESS I -->
+      <map code="0x132" name="glyph65342"/><!-- LATIN CAPITAL LIGATURE IJ -->
+      <map code="0x133" name="glyph65343"/><!-- LATIN SMALL LIGATURE IJ -->
+      <map code="0x134" name="glyph65344"/><!-- LATIN CAPITAL LETTER J WITH CIRCUMFLEX -->
+      <map code="0x135" name="glyph65345"/><!-- LATIN SMALL LETTER J WITH CIRCUMFLEX -->
+      <map code="0x136" name="glyph65346"/><!-- LATIN CAPITAL LETTER K WITH CEDILLA -->
+      <map code="0x137" name="glyph65347"/><!-- LATIN SMALL LETTER K WITH CEDILLA -->
+      <map code="0x138" name="glyph65348"/><!-- LATIN SMALL LETTER KRA -->
+      <map code="0x139" name="glyph65349"/><!-- LATIN CAPITAL LETTER L WITH ACUTE -->
+      <map code="0x13a" name="glyph65350"/><!-- LATIN SMALL LETTER L WITH ACUTE -->
+      <map code="0x13b" name="glyph65351"/><!-- LATIN CAPITAL LETTER L WITH CEDILLA -->
+      <map code="0x13c" name="glyph65352"/><!-- LATIN SMALL LETTER L WITH CEDILLA -->
+      <map code="0x13d" name="glyph65353"/><!-- LATIN CAPITAL LETTER L WITH CARON -->
+      <map code="0x13e" name="glyph65354"/><!-- LATIN SMALL LETTER L WITH CARON -->
+      <map code="0x13f" name="glyph65355"/><!-- LATIN CAPITAL LETTER L WITH MIDDLE DOT -->
+      <map code="0x140" name="glyph65356"/><!-- LATIN SMALL LETTER L WITH MIDDLE DOT -->
+      <map code="0x141" name="glyph65357"/><!-- LATIN CAPITAL LETTER L WITH STROKE -->
+      <map code="0x142" name="glyph65358"/><!-- LATIN SMALL LETTER L WITH STROKE -->
+      <map code="0x143" name="glyph65359"/><!-- LATIN CAPITAL LETTER N WITH ACUTE -->
+      <map code="0x144" name="glyph65360"/><!-- LATIN SMALL LETTER N WITH ACUTE -->
+      <map code="0x145" name="glyph65361"/><!-- LATIN CAPITAL LETTER N WITH CEDILLA -->
+      <map code="0x146" name="glyph65362"/><!-- LATIN SMALL LETTER N WITH CEDILLA -->
+      <map code="0x147" name="glyph65363"/><!-- LATIN CAPITAL LETTER N WITH CARON -->
+      <map code="0x148" name="glyph65364"/><!-- LATIN SMALL LETTER N WITH CARON -->
+      <map code="0x149" name="glyph65365"/><!-- LATIN SMALL LETTER N PRECEDED BY APOSTROPHE -->
+      <map code="0x14a" name="glyph65366"/><!-- LATIN CAPITAL LETTER ENG -->
+      <map code="0x14b" name="glyph65367"/><!-- LATIN SMALL LETTER ENG -->
+      <map code="0x14c" name="glyph65368"/><!-- LATIN CAPITAL LETTER O WITH MACRON -->
+      <map code="0x14d" name="glyph65369"/><!-- LATIN SMALL LETTER O WITH MACRON -->
+      <map code="0x14e" name="glyph65370"/><!-- LATIN CAPITAL LETTER O WITH BREVE -->
+      <map code="0x14f" name="glyph65371"/><!-- LATIN SMALL LETTER O WITH BREVE -->
+      <map code="0x150" name="glyph65372"/><!-- LATIN CAPITAL LETTER O WITH DOUBLE ACUTE -->
+      <map code="0x151" name="glyph65373"/><!-- LATIN SMALL LETTER O WITH DOUBLE ACUTE -->
+      <map code="0x152" name="glyph65374"/><!-- LATIN CAPITAL LIGATURE OE -->
+      <map code="0x153" name="glyph65375"/><!-- LATIN SMALL LIGATURE OE -->
+      <map code="0x154" name="glyph65376"/><!-- LATIN CAPITAL LETTER R WITH ACUTE -->
+      <map code="0x155" name="glyph65377"/><!-- LATIN SMALL LETTER R WITH ACUTE -->
+      <map code="0x156" name="glyph65378"/><!-- LATIN CAPITAL LETTER R WITH CEDILLA -->
+      <map code="0x157" name="glyph65379"/><!-- LATIN SMALL LETTER R WITH CEDILLA -->
+      <map code="0x158" name="glyph65380"/><!-- LATIN CAPITAL LETTER R WITH CARON -->
+      <map code="0x159" name="glyph65381"/><!-- LATIN SMALL LETTER R WITH CARON -->
+      <map code="0x15a" name="glyph65382"/><!-- LATIN CAPITAL LETTER S WITH ACUTE -->
+      <map code="0x15b" name="glyph65383"/><!-- LATIN SMALL LETTER S WITH ACUTE -->
+      <map code="0x15c" name="glyph65384"/><!-- LATIN CAPITAL LETTER S WITH CIRCUMFLEX -->
+      <map code="0x15d" name="glyph65385"/><!-- LATIN SMALL LETTER S WITH CIRCUMFLEX -->
+      <map code="0x15e" name="glyph65386"/><!-- LATIN CAPITAL LETTER S WITH CEDILLA -->
+      <map code="0x15f" name="glyph65387"/><!-- LATIN SMALL LETTER S WITH CEDILLA -->
+      <map code="0x160" name="glyph65388"/><!-- LATIN CAPITAL LETTER S WITH CARON -->
+      <map code="0x161" name="glyph65389"/><!-- LATIN SMALL LETTER S WITH CARON -->
+      <map code="0x162" name="glyph65390"/><!-- LATIN CAPITAL LETTER T WITH CEDILLA -->
+      <map code="0x163" name="glyph65391"/><!-- LATIN SMALL LETTER T WITH CEDILLA -->
+      <map code="0x164" name="glyph65392"/><!-- LATIN CAPITAL LETTER T WITH CARON -->
+      <map code="0x165" name="glyph65393"/><!-- LATIN SMALL LETTER T WITH CARON -->
+      <map code="0x166" name="glyph65394"/><!-- LATIN CAPITAL LETTER T WITH STROKE -->
+      <map code="0x167" name="glyph65395"/><!-- LATIN SMALL LETTER T WITH STROKE -->
+      <map code="0x168" name="glyph65396"/><!-- LATIN CAPITAL LETTER U WITH TILDE -->
+      <map code="0x169" name="glyph65397"/><!-- LATIN SMALL LETTER U WITH TILDE -->
+      <map code="0x16a" name="glyph65398"/><!-- LATIN CAPITAL LETTER U WITH MACRON -->
+      <map code="0x16b" name="glyph65399"/><!-- LATIN SMALL LETTER U WITH MACRON -->
+      <map code="0x16c" name="glyph65400"/><!-- LATIN CAPITAL LETTER U WITH BREVE -->
+      <map code="0x16d" name="glyph65401"/><!-- LATIN SMALL LETTER U WITH BREVE -->
+      <map code="0x16e" name="glyph65402"/><!-- LATIN CAPITAL LETTER U WITH RING ABOVE -->
+      <map code="0x16f" name="glyph65403"/><!-- LATIN SMALL LETTER U WITH RING ABOVE -->
+      <map code="0x170" name="glyph65404"/><!-- LATIN CAPITAL LETTER U WITH DOUBLE ACUTE -->
+      <map code="0x171" name="glyph65405"/><!-- LATIN SMALL LETTER U WITH DOUBLE ACUTE -->
+      <map code="0x172" name="glyph65406"/><!-- LATIN CAPITAL LETTER U WITH OGONEK -->
+      <map code="0x173" name="glyph65407"/><!-- LATIN SMALL LETTER U WITH OGONEK -->
+      <map code="0x174" name="glyph65408"/><!-- LATIN CAPITAL LETTER W WITH CIRCUMFLEX -->
+      <map code="0x175" name="glyph65409"/><!-- LATIN SMALL LETTER W WITH CIRCUMFLEX -->
+      <map code="0x176" name="glyph65410"/><!-- LATIN CAPITAL LETTER Y WITH CIRCUMFLEX -->
+      <map code="0x177" name="glyph65411"/><!-- LATIN SMALL LETTER Y WITH CIRCUMFLEX -->
+      <map code="0x178" name="glyph65412"/><!-- LATIN CAPITAL LETTER Y WITH DIAERESIS -->
+      <map code="0x179" name="glyph65413"/><!-- LATIN CAPITAL LETTER Z WITH ACUTE -->
+      <map code="0x17a" name="glyph65414"/><!-- LATIN SMALL LETTER Z WITH ACUTE -->
+      <map code="0x17b" name="glyph65415"/><!-- LATIN CAPITAL LETTER Z WITH DOT ABOVE -->
+      <map code="0x17c" name="glyph65416"/><!-- LATIN SMALL LETTER Z WITH DOT ABOVE -->
+      <map code="0x17d" name="glyph65417"/><!-- LATIN CAPITAL LETTER Z WITH CARON -->
+      <map code="0x17e" name="glyph65418"/><!-- LATIN SMALL LETTER Z WITH CARON -->
+      <map code="0x17f" name="glyph65419"/><!-- LATIN SMALL LETTER LONG S -->
+      <map code="0x180" name="glyph65420"/><!-- LATIN SMALL LETTER B WITH STROKE -->
+      <map code="0x181" name="glyph65421"/><!-- LATIN CAPITAL LETTER B WITH HOOK -->
+      <map code="0x182" name="glyph65422"/><!-- LATIN CAPITAL LETTER B WITH TOPBAR -->
+      <map code="0x183" name="glyph65423"/><!-- LATIN SMALL LETTER B WITH TOPBAR -->
+      <map code="0x184" name="glyph65424"/><!-- LATIN CAPITAL LETTER TONE SIX -->
+      <map code="0x185" name="glyph65425"/><!-- LATIN SMALL LETTER TONE SIX -->
+      <map code="0x186" name="glyph65426"/><!-- LATIN CAPITAL LETTER OPEN O -->
+      <map code="0x187" name="glyph65427"/><!-- LATIN CAPITAL LETTER C WITH HOOK -->
+      <map code="0x188" name="glyph65428"/><!-- LATIN SMALL LETTER C WITH HOOK -->
+      <map code="0x189" name="glyph65429"/><!-- LATIN CAPITAL LETTER AFRICAN D -->
+      <map code="0x18a" name="glyph65430"/><!-- LATIN CAPITAL LETTER D WITH HOOK -->
+      <map code="0x18b" name="glyph65431"/><!-- LATIN CAPITAL LETTER D WITH TOPBAR -->
+      <map code="0x18c" name="glyph65432"/><!-- LATIN SMALL LETTER D WITH TOPBAR -->
+      <map code="0x18d" name="glyph65433"/><!-- LATIN SMALL LETTER TURNED DELTA -->
+      <map code="0x18e" name="glyph65434"/><!-- LATIN CAPITAL LETTER REVERSED E -->
+      <map code="0x18f" name="glyph65435"/><!-- LATIN CAPITAL LETTER SCHWA -->
+      <map code="0x190" name="glyph65436"/><!-- LATIN CAPITAL LETTER OPEN E -->
+      <map code="0x191" name="glyph65437"/><!-- LATIN CAPITAL LETTER F WITH HOOK -->
+      <map code="0x192" name="glyph65438"/><!-- LATIN SMALL LETTER F WITH HOOK -->
+      <map code="0x193" name="glyph65439"/><!-- LATIN CAPITAL LETTER G WITH HOOK -->
+      <map code="0x194" name="glyph65440"/><!-- LATIN CAPITAL LETTER GAMMA -->
+      <map code="0x195" name="glyph65441"/><!-- LATIN SMALL LETTER HV -->
+      <map code="0x196" name="glyph65442"/><!-- LATIN CAPITAL LETTER IOTA -->
+      <map code="0x197" name="glyph65443"/><!-- LATIN CAPITAL LETTER I WITH STROKE -->
+      <map code="0x198" name="glyph65444"/><!-- LATIN CAPITAL LETTER K WITH HOOK -->
+      <map code="0x199" name="glyph65445"/><!-- LATIN SMALL LETTER K WITH HOOK -->
+      <map code="0x19a" name="glyph65446"/><!-- LATIN SMALL LETTER L WITH BAR -->
+      <map code="0x19b" name="glyph65447"/><!-- LATIN SMALL LETTER LAMBDA WITH STROKE -->
+      <map code="0x19c" name="glyph65448"/><!-- LATIN CAPITAL LETTER TURNED M -->
+      <map code="0x19d" name="glyph65449"/><!-- LATIN CAPITAL LETTER N WITH LEFT HOOK -->
+      <map code="0x19e" name="glyph65450"/><!-- LATIN SMALL LETTER N WITH LONG RIGHT LEG -->
+      <map code="0x19f" name="glyph65451"/><!-- LATIN CAPITAL LETTER O WITH MIDDLE TILDE -->
+      <map code="0x1a0" name="glyph65452"/><!-- LATIN CAPITAL LETTER O WITH HORN -->
+      <map code="0x1a1" name="glyph65453"/><!-- LATIN SMALL LETTER O WITH HORN -->
+      <map code="0x1a2" name="glyph65454"/><!-- LATIN CAPITAL LETTER OI -->
+      <map code="0x1a3" name="glyph65455"/><!-- LATIN SMALL LETTER OI -->
+      <map code="0x1a4" name="glyph65456"/><!-- LATIN CAPITAL LETTER P WITH HOOK -->
+      <map code="0x1a5" name="glyph65457"/><!-- LATIN SMALL LETTER P WITH HOOK -->
+      <map code="0x1a6" name="glyph65458"/><!-- LATIN LETTER YR -->
+      <map code="0x1a7" name="glyph65459"/><!-- LATIN CAPITAL LETTER TONE TWO -->
+      <map code="0x1a8" name="glyph65460"/><!-- LATIN SMALL LETTER TONE TWO -->
+      <map code="0x1a9" name="glyph65461"/><!-- LATIN CAPITAL LETTER ESH -->
+      <map code="0x1aa" name="glyph65462"/><!-- LATIN LETTER REVERSED ESH LOOP -->
+      <map code="0x1ab" name="glyph65463"/><!-- LATIN SMALL LETTER T WITH PALATAL HOOK -->
+      <map code="0x1ac" name="glyph65464"/><!-- LATIN CAPITAL LETTER T WITH HOOK -->
+      <map code="0x1ad" name="glyph65465"/><!-- LATIN SMALL LETTER T WITH HOOK -->
+      <map code="0x1ae" name="glyph65466"/><!-- LATIN CAPITAL LETTER T WITH RETROFLEX HOOK -->
+      <map code="0x1af" name="glyph65467"/><!-- LATIN CAPITAL LETTER U WITH HORN -->
+      <map code="0x1b0" name="glyph65468"/><!-- LATIN SMALL LETTER U WITH HORN -->
+      <map code="0x1b1" name="glyph65469"/><!-- LATIN CAPITAL LETTER UPSILON -->
+      <map code="0x1b2" name="glyph65470"/><!-- LATIN CAPITAL LETTER V WITH HOOK -->
+      <map code="0x1b3" name="glyph65471"/><!-- LATIN CAPITAL LETTER Y WITH HOOK -->
+      <map code="0x1b4" name="glyph65472"/><!-- LATIN SMALL LETTER Y WITH HOOK -->
+      <map code="0x1b5" name="glyph65473"/><!-- LATIN CAPITAL LETTER Z WITH STROKE -->
+      <map code="0x1b6" name="glyph65474"/><!-- LATIN SMALL LETTER Z WITH STROKE -->
+      <map code="0x1b7" name="glyph65475"/><!-- LATIN CAPITAL LETTER EZH -->
+      <map code="0x1b8" name="glyph65476"/><!-- LATIN CAPITAL LETTER EZH REVERSED -->
+      <map code="0x1b9" name="glyph65477"/><!-- LATIN SMALL LETTER EZH REVERSED -->
+      <map code="0x1ba" name="glyph65478"/><!-- LATIN SMALL LETTER EZH WITH TAIL -->
+      <map code="0x1bb" name="glyph65479"/><!-- LATIN LETTER TWO WITH STROKE -->
+      <map code="0x1bc" name="glyph65480"/><!-- LATIN CAPITAL LETTER TONE FIVE -->
+      <map code="0x1bd" name="glyph65481"/><!-- LATIN SMALL LETTER TONE FIVE -->
+      <map code="0x1be" name="glyph65482"/><!-- LATIN LETTER INVERTED GLOTTAL STOP WITH STROKE -->
+      <map code="0x1bf" name="glyph65483"/><!-- LATIN LETTER WYNN -->
+      <map code="0x1c0" name="glyph65484"/><!-- LATIN LETTER DENTAL CLICK -->
+      <map code="0x1c1" name="glyph65485"/><!-- LATIN LETTER LATERAL CLICK -->
+      <map code="0x1c2" name="glyph65486"/><!-- LATIN LETTER ALVEOLAR CLICK -->
+      <map code="0x1c3" name="glyph65487"/><!-- LATIN LETTER RETROFLEX CLICK -->
+      <map code="0x1c4" name="glyph65488"/><!-- LATIN CAPITAL LETTER DZ WITH CARON -->
+      <map code="0x1c5" name="glyph65489"/><!-- LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON -->
+      <map code="0x1c6" name="glyph65490"/><!-- LATIN SMALL LETTER DZ WITH CARON -->
+      <map code="0x1c7" name="glyph65491"/><!-- LATIN CAPITAL LETTER LJ -->
+      <map code="0x1c8" name="glyph65492"/><!-- LATIN CAPITAL LETTER L WITH SMALL LETTER J -->
+      <map code="0x1c9" name="glyph65493"/><!-- LATIN SMALL LETTER LJ -->
+      <map code="0x1ca" name="glyph65494"/><!-- LATIN CAPITAL LETTER NJ -->
+      <map code="0x1cb" name="glyph65495"/><!-- LATIN CAPITAL LETTER N WITH SMALL LETTER J -->
+      <map code="0x1cc" name="glyph65496"/><!-- LATIN SMALL LETTER NJ -->
+      <map code="0x1cd" name="glyph65497"/><!-- LATIN CAPITAL LETTER A WITH CARON -->
+      <map code="0x1ce" name="glyph65498"/><!-- LATIN SMALL LETTER A WITH CARON -->
+      <map code="0x1cf" name="glyph65499"/><!-- LATIN CAPITAL LETTER I WITH CARON -->
+      <map code="0x1d0" name="glyph65500"/><!-- LATIN SMALL LETTER I WITH CARON -->
+      <map code="0x1d1" name="glyph65501"/><!-- LATIN CAPITAL LETTER O WITH CARON -->
+      <map code="0x1d2" name="glyph65502"/><!-- LATIN SMALL LETTER O WITH CARON -->
+      <map code="0x1d3" name="glyph65503"/><!-- LATIN CAPITAL LETTER U WITH CARON -->
+      <map code="0x1d4" name="glyph65504"/><!-- LATIN SMALL LETTER U WITH CARON -->
+      <map code="0x1d5" name="glyph65505"/><!-- LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON -->
+      <map code="0x1d6" name="glyph65506"/><!-- LATIN SMALL LETTER U WITH DIAERESIS AND MACRON -->
+      <map code="0x1d7" name="glyph65507"/><!-- LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE -->
+      <map code="0x1d8" name="glyph65508"/><!-- LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE -->
+      <map code="0x1d9" name="glyph65509"/><!-- LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON -->
+      <map code="0x1da" name="glyph65510"/><!-- LATIN SMALL LETTER U WITH DIAERESIS AND CARON -->
+      <map code="0x1db" name="glyph65511"/><!-- LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE -->
+      <map code="0x1dc" name="glyph65512"/><!-- LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE -->
+      <map code="0x1dd" name="glyph65513"/><!-- LATIN SMALL LETTER TURNED E -->
+      <map code="0x1de" name="glyph65514"/><!-- LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON -->
+      <map code="0x1df" name="glyph65515"/><!-- LATIN SMALL LETTER A WITH DIAERESIS AND MACRON -->
+      <map code="0x1e0" name="glyph65516"/><!-- LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON -->
+      <map code="0x1e1" name="glyph65517"/><!-- LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON -->
+      <map code="0x1e2" name="glyph65518"/><!-- LATIN CAPITAL LETTER AE WITH MACRON -->
+      <map code="0x1e3" name="glyph65519"/><!-- LATIN SMALL LETTER AE WITH MACRON -->
+      <map code="0x1e4" name="glyph65520"/><!-- LATIN CAPITAL LETTER G WITH STROKE -->
+      <map code="0x1e5" name="glyph65521"/><!-- LATIN SMALL LETTER G WITH STROKE -->
+      <map code="0x1e6" name="glyph65522"/><!-- LATIN CAPITAL LETTER G WITH CARON -->
+      <map code="0x1e7" name="glyph65523"/><!-- LATIN SMALL LETTER G WITH CARON -->
+      <map code="0x1e8" name="glyph65524"/><!-- LATIN CAPITAL LETTER K WITH CARON -->
+      <map code="0x1e9" name="glyph65525"/><!-- LATIN SMALL LETTER K WITH CARON -->
+      <map code="0x1ea" name="glyph65526"/><!-- LATIN CAPITAL LETTER O WITH OGONEK -->
+      <map code="0x1eb" name="glyph65527"/><!-- LATIN SMALL LETTER O WITH OGONEK -->
+      <map code="0x1ec" name="glyph65528"/><!-- LATIN CAPITAL LETTER O WITH OGONEK AND MACRON -->
+      <map code="0x1ed" name="glyph65529"/><!-- LATIN SMALL LETTER O WITH OGONEK AND MACRON -->
+      <map code="0x1ee" name="glyph65530"/><!-- LATIN CAPITAL LETTER EZH WITH CARON -->
+      <map code="0x1ef" name="glyph65531"/><!-- LATIN SMALL LETTER EZH WITH CARON -->
+      <map code="0x1f0" name="glyph65532"/><!-- LATIN SMALL LETTER J WITH CARON -->
+      <map code="0x1f1" name="glyph65533"/><!-- LATIN CAPITAL LETTER DZ -->
+      <map code="0x1f2" name="glyph65534"/><!-- LATIN CAPITAL LETTER D WITH SMALL LETTER Z -->
+      <map code="0x1f3" name="glyph65535"/><!-- LATIN SMALL LETTER DZ -->
+      <map code="0x1f5" name="g1"/><!-- LATIN SMALL LETTER G WITH ACUTE -->
+      <map code="0x1f6" name="g2"/><!-- LATIN CAPITAL LETTER HWAIR -->
+      <map code="0x1f7" name="g3"/><!-- LATIN CAPITAL LETTER WYNN -->
+      <map code="0x1f8" name="g4"/><!-- LATIN CAPITAL LETTER N WITH GRAVE -->
+      <map code="0x1f9" name="g5"/><!-- LATIN SMALL LETTER N WITH GRAVE -->
+      <map code="0x1fa" name="g6"/><!-- LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE -->
+      <map code="0x1fb" name="g7"/><!-- LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE -->
+      <map code="0x1fc" name="g8"/><!-- LATIN CAPITAL LETTER AE WITH ACUTE -->
+      <map code="0x1fd" name="g9"/><!-- LATIN SMALL LETTER AE WITH ACUTE -->
+      <map code="0x1fe" name="g10"/><!-- LATIN CAPITAL LETTER O WITH STROKE AND ACUTE -->
+      <map code="0x1ff" name="g11"/><!-- LATIN SMALL LETTER O WITH STROKE AND ACUTE -->
+      <map code="0x200" name="g12"/><!-- LATIN CAPITAL LETTER A WITH DOUBLE GRAVE -->
+      <map code="0x201" name="g13"/><!-- LATIN SMALL LETTER A WITH DOUBLE GRAVE -->
+      <map code="0x202" name="g14"/><!-- LATIN CAPITAL LETTER A WITH INVERTED BREVE -->
+      <map code="0x203" name="g15"/><!-- LATIN SMALL LETTER A WITH INVERTED BREVE -->
+      <map code="0x204" name="g16"/><!-- LATIN CAPITAL LETTER E WITH DOUBLE GRAVE -->
+      <map code="0x205" name="g17"/><!-- LATIN SMALL LETTER E WITH DOUBLE GRAVE -->
+      <map code="0x206" name="g18"/><!-- LATIN CAPITAL LETTER E WITH INVERTED BREVE -->
+      <map code="0x207" name="g19"/><!-- LATIN SMALL LETTER E WITH INVERTED BREVE -->
+      <map code="0x208" name="g20"/><!-- LATIN CAPITAL LETTER I WITH DOUBLE GRAVE -->
+      <map code="0x209" name="g21"/><!-- LATIN SMALL LETTER I WITH DOUBLE GRAVE -->
+      <map code="0x20a" name="g22"/><!-- LATIN CAPITAL LETTER I WITH INVERTED BREVE -->
+      <map code="0x20b" name="g23"/><!-- LATIN SMALL LETTER I WITH INVERTED BREVE -->
+      <map code="0x20c" name="g24"/><!-- LATIN CAPITAL LETTER O WITH DOUBLE GRAVE -->
+      <map code="0x20d" name="g25"/><!-- LATIN SMALL LETTER O WITH DOUBLE GRAVE -->
+      <map code="0x20e" name="g26"/><!-- LATIN CAPITAL LETTER O WITH INVERTED BREVE -->
+      <map code="0x20f" name="g27"/><!-- LATIN SMALL LETTER O WITH INVERTED BREVE -->
+      <map code="0x210" name="g28"/><!-- LATIN CAPITAL LETTER R WITH DOUBLE GRAVE -->
+      <map code="0x211" name="g29"/><!-- LATIN SMALL LETTER R WITH DOUBLE GRAVE -->
+      <map code="0x212" name="g30"/><!-- LATIN CAPITAL LETTER R WITH INVERTED BREVE -->
+      <map code="0x213" name="g31"/><!-- LATIN SMALL LETTER R WITH INVERTED BREVE -->
+      <map code="0x214" name="g32"/><!-- LATIN CAPITAL LETTER U WITH DOUBLE GRAVE -->
+      <map code="0x215" name="g33"/><!-- LATIN SMALL LETTER U WITH DOUBLE GRAVE -->
+      <map code="0x216" name="g34"/><!-- LATIN CAPITAL LETTER U WITH INVERTED BREVE -->
+      <map code="0x217" name="g35"/><!-- LATIN SMALL LETTER U WITH INVERTED BREVE -->
+      <map code="0x218" name="g36"/><!-- LATIN CAPITAL LETTER S WITH COMMA BELOW -->
+      <map code="0x219" name="g37"/><!-- LATIN SMALL LETTER S WITH COMMA BELOW -->
+      <map code="0x21a" name="g38"/><!-- LATIN CAPITAL LETTER T WITH COMMA BELOW -->
+      <map code="0x21b" name="g39"/><!-- LATIN SMALL LETTER T WITH COMMA BELOW -->
+      <map code="0x21c" name="g40"/><!-- LATIN CAPITAL LETTER YOGH -->
+      <map code="0x21d" name="g41"/><!-- LATIN SMALL LETTER YOGH -->
+      <map code="0x21e" name="g42"/><!-- LATIN CAPITAL LETTER H WITH CARON -->
+      <map code="0x21f" name="g43"/><!-- LATIN SMALL LETTER H WITH CARON -->
+      <map code="0x220" name="g44"/><!-- LATIN CAPITAL LETTER N WITH LONG RIGHT LEG -->
+      <map code="0x221" name="g45"/><!-- LATIN SMALL LETTER D WITH CURL -->
+      <map code="0x222" name="g46"/><!-- LATIN CAPITAL LETTER OU -->
+      <map code="0x223" name="g47"/><!-- LATIN SMALL LETTER OU -->
+      <map code="0x224" name="g48"/><!-- LATIN CAPITAL LETTER Z WITH HOOK -->
+      <map code="0x225" name="g49"/><!-- LATIN SMALL LETTER Z WITH HOOK -->
+      <map code="0x226" name="g50"/><!-- LATIN CAPITAL LETTER A WITH DOT ABOVE -->
+      <map code="0x227" name="g51"/><!-- LATIN SMALL LETTER A WITH DOT ABOVE -->
+      <map code="0x228" name="g52"/><!-- LATIN CAPITAL LETTER E WITH CEDILLA -->
+      <map code="0x229" name="g53"/><!-- LATIN SMALL LETTER E WITH CEDILLA -->
+      <map code="0x22a" name="g54"/><!-- LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON -->
+      <map code="0x22b" name="g55"/><!-- LATIN SMALL LETTER O WITH DIAERESIS AND MACRON -->
+      <map code="0x22c" name="g56"/><!-- LATIN CAPITAL LETTER O WITH TILDE AND MACRON -->
+      <map code="0x22d" name="g57"/><!-- LATIN SMALL LETTER O WITH TILDE AND MACRON -->
+      <map code="0x22e" name="g58"/><!-- LATIN CAPITAL LETTER O WITH DOT ABOVE -->
+      <map code="0x22f" name="g59"/><!-- LATIN SMALL LETTER O WITH DOT ABOVE -->
+      <map code="0x230" name="g60"/><!-- LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON -->
+      <map code="0x231" name="g61"/><!-- LATIN SMALL LETTER O WITH DOT ABOVE AND MACRON -->
+      <map code="0x232" name="g62"/><!-- LATIN CAPITAL LETTER Y WITH MACRON -->
+      <map code="0x233" name="g63"/><!-- LATIN SMALL LETTER Y WITH MACRON -->
+      <map code="0x234" name="g64"/><!-- LATIN SMALL LETTER L WITH CURL -->
+      <map code="0x235" name="g65"/><!-- LATIN SMALL LETTER N WITH CURL -->
+      <map code="0x236" name="g66"/><!-- LATIN SMALL LETTER T WITH CURL -->
+      <map code="0x237" name="g67"/><!-- LATIN SMALL LETTER DOTLESS J -->
+      <map code="0x238" name="g68"/><!-- LATIN SMALL LETTER DB DIGRAPH -->
+      <map code="0x239" name="g69"/><!-- LATIN SMALL LETTER QP DIGRAPH -->
+      <map code="0x23a" name="g70"/><!-- LATIN CAPITAL LETTER A WITH STROKE -->
+      <map code="0x23b" name="g71"/><!-- LATIN CAPITAL LETTER C WITH STROKE -->
+      <map code="0x23c" name="g72"/><!-- LATIN SMALL LETTER C WITH STROKE -->
+      <map code="0x23d" name="g73"/><!-- LATIN CAPITAL LETTER L WITH BAR -->
+      <map code="0x23e" name="g74"/><!-- LATIN CAPITAL LETTER T WITH DIAGONAL STROKE -->
+      <map code="0x23f" name="g75"/><!-- LATIN SMALL LETTER S WITH SWASH TAIL -->
+      <map code="0x240" name="g76"/><!-- LATIN SMALL LETTER Z WITH SWASH TAIL -->
+      <map code="0x241" name="g77"/><!-- LATIN CAPITAL LETTER GLOTTAL STOP -->
+      <map code="0x242" name="g78"/><!-- LATIN SMALL LETTER GLOTTAL STOP -->
+      <map code="0x243" name="g79"/><!-- LATIN CAPITAL LETTER B WITH STROKE -->
+      <map code="0x244" name="g80"/><!-- LATIN CAPITAL LETTER U BAR -->
+      <map code="0x245" name="g81"/><!-- LATIN CAPITAL LETTER TURNED V -->
+      <map code="0x246" name="g82"/><!-- LATIN CAPITAL LETTER E WITH STROKE -->
+      <map code="0x247" name="g83"/><!-- LATIN SMALL LETTER E WITH STROKE -->
+      <map code="0x248" name="g84"/><!-- LATIN CAPITAL LETTER J WITH STROKE -->
+      <map code="0x249" name="g85"/><!-- LATIN SMALL LETTER J WITH STROKE -->
+      <map code="0x24a" name="g86"/><!-- LATIN CAPITAL LETTER SMALL Q WITH HOOK TAIL -->
+      <map code="0x24b" name="g87"/><!-- LATIN SMALL LETTER Q WITH HOOK TAIL -->
+      <map code="0x24c" name="g88"/><!-- LATIN CAPITAL LETTER R WITH STROKE -->
+      <map code="0x24d" name="g89"/><!-- LATIN SMALL LETTER R WITH STROKE -->
+      <map code="0x24e" name="g90"/><!-- LATIN CAPITAL LETTER Y WITH STROKE -->
+      <map code="0x24f" name="g91"/><!-- LATIN SMALL LETTER Y WITH STROKE -->
+      <map code="0x250" name="g92"/><!-- LATIN SMALL LETTER TURNED A -->
+      <map code="0x251" name="g93"/><!-- LATIN SMALL LETTER ALPHA -->
+      <map code="0x252" name="g94"/><!-- LATIN SMALL LETTER TURNED ALPHA -->
+      <map code="0x253" name="g95"/><!-- LATIN SMALL LETTER B WITH HOOK -->
+      <map code="0x254" name="g96"/><!-- LATIN SMALL LETTER OPEN O -->
+      <map code="0x255" name="g97"/><!-- LATIN SMALL LETTER C WITH CURL -->
+      <map code="0x256" name="g98"/><!-- LATIN SMALL LETTER D WITH TAIL -->
+      <map code="0x257" name="g99"/><!-- LATIN SMALL LETTER D WITH HOOK -->
+      <map code="0x258" name="glyph00100"/><!-- LATIN SMALL LETTER REVERSED E -->
+      <map code="0x259" name="glyph00101"/><!-- LATIN SMALL LETTER SCHWA -->
+      <map code="0x25a" name="glyph00102"/><!-- LATIN SMALL LETTER SCHWA WITH HOOK -->
+      <map code="0x25b" name="glyph00103"/><!-- LATIN SMALL LETTER OPEN E -->
+      <map code="0x25c" name="glyph00104"/><!-- LATIN SMALL LETTER REVERSED OPEN E -->
+      <map code="0x25d" name="glyph00105"/><!-- LATIN SMALL LETTER REVERSED OPEN E WITH HOOK -->
+      <map code="0x25e" name="glyph00106"/><!-- LATIN SMALL LETTER CLOSED REVERSED OPEN E -->
+      <map code="0x25f" name="glyph00107"/><!-- LATIN SMALL LETTER DOTLESS J WITH STROKE -->
+      <map code="0x260" name="glyph00108"/><!-- LATIN SMALL LETTER G WITH HOOK -->
+      <map code="0x261" name="glyph00109"/><!-- LATIN SMALL LETTER SCRIPT G -->
+      <map code="0x262" name="glyph00110"/><!-- LATIN LETTER SMALL CAPITAL G -->
+      <map code="0x263" name="glyph00111"/><!-- LATIN SMALL LETTER GAMMA -->
+      <map code="0x264" name="glyph00112"/><!-- LATIN SMALL LETTER RAMS HORN -->
+      <map code="0x265" name="glyph00113"/><!-- LATIN SMALL LETTER TURNED H -->
+      <map code="0x266" name="glyph00114"/><!-- LATIN SMALL LETTER H WITH HOOK -->
+      <map code="0x267" name="glyph00115"/><!-- LATIN SMALL LETTER HENG WITH HOOK -->
+      <map code="0x268" name="glyph00116"/><!-- LATIN SMALL LETTER I WITH STROKE -->
+      <map code="0x269" name="glyph00117"/><!-- LATIN SMALL LETTER IOTA -->
+      <map code="0x26a" name="glyph00118"/><!-- LATIN LETTER SMALL CAPITAL I -->
+      <map code="0x26b" name="glyph00119"/><!-- LATIN SMALL LETTER L WITH MIDDLE TILDE -->
+      <map code="0x26c" name="glyph00120"/><!-- LATIN SMALL LETTER L WITH BELT -->
+      <map code="0x26d" name="glyph00121"/><!-- LATIN SMALL LETTER L WITH RETROFLEX HOOK -->
+      <map code="0x26e" name="glyph00122"/><!-- LATIN SMALL LETTER LEZH -->
+      <map code="0x26f" name="glyph00123"/><!-- LATIN SMALL LETTER TURNED M -->
+      <map code="0x270" name="glyph00124"/><!-- LATIN SMALL LETTER TURNED M WITH LONG LEG -->
+      <map code="0x271" name="glyph00125"/><!-- LATIN SMALL LETTER M WITH HOOK -->
+      <map code="0x272" name="glyph00126"/><!-- LATIN SMALL LETTER N WITH LEFT HOOK -->
+      <map code="0x273" name="glyph00127"/><!-- LATIN SMALL LETTER N WITH RETROFLEX HOOK -->
+      <map code="0x274" name="glyph00128"/><!-- LATIN LETTER SMALL CAPITAL N -->
+      <map code="0x275" name="glyph00129"/><!-- LATIN SMALL LETTER BARRED O -->
+      <map code="0x276" name="glyph00130"/><!-- LATIN LETTER SMALL CAPITAL OE -->
+      <map code="0x277" name="glyph00131"/><!-- LATIN SMALL LETTER CLOSED OMEGA -->
+      <map code="0x278" name="glyph00132"/><!-- LATIN SMALL LETTER PHI -->
+      <map code="0x279" name="glyph00133"/><!-- LATIN SMALL LETTER TURNED R -->
+      <map code="0x27a" name="glyph00134"/><!-- LATIN SMALL LETTER TURNED R WITH LONG LEG -->
+      <map code="0x27b" name="glyph00135"/><!-- LATIN SMALL LETTER TURNED R WITH HOOK -->
+      <map code="0x27c" name="glyph00136"/><!-- LATIN SMALL LETTER R WITH LONG LEG -->
+      <map code="0x27d" name="glyph00137"/><!-- LATIN SMALL LETTER R WITH TAIL -->
+      <map code="0x27e" name="glyph00138"/><!-- LATIN SMALL LETTER R WITH FISHHOOK -->
+      <map code="0x27f" name="glyph00139"/><!-- LATIN SMALL LETTER REVERSED R WITH FISHHOOK -->
+      <map code="0x280" name="glyph00140"/><!-- LATIN LETTER SMALL CAPITAL R -->
+      <map code="0x281" name="glyph00141"/><!-- LATIN LETTER SMALL CAPITAL INVERTED R -->
+      <map code="0x282" name="glyph00142"/><!-- LATIN SMALL LETTER S WITH HOOK -->
+      <map code="0x283" name="glyph00143"/><!-- LATIN SMALL LETTER ESH -->
+      <map code="0x284" name="glyph00144"/><!-- LATIN SMALL LETTER DOTLESS J WITH STROKE AND HOOK -->
+      <map code="0x285" name="glyph00145"/><!-- LATIN SMALL LETTER SQUAT REVERSED ESH -->
+      <map code="0x286" name="glyph00146"/><!-- LATIN SMALL LETTER ESH WITH CURL -->
+      <map code="0x287" name="glyph00147"/><!-- LATIN SMALL LETTER TURNED T -->
+      <map code="0x288" name="glyph00148"/><!-- LATIN SMALL LETTER T WITH RETROFLEX HOOK -->
+      <map code="0x289" name="glyph00149"/><!-- LATIN SMALL LETTER U BAR -->
+      <map code="0x28a" name="glyph00150"/><!-- LATIN SMALL LETTER UPSILON -->
+      <map code="0x28b" name="glyph00151"/><!-- LATIN SMALL LETTER V WITH HOOK -->
+      <map code="0x28c" name="glyph00152"/><!-- LATIN SMALL LETTER TURNED V -->
+      <map code="0x28d" name="glyph00153"/><!-- LATIN SMALL LETTER TURNED W -->
+      <map code="0x28e" name="glyph00154"/><!-- LATIN SMALL LETTER TURNED Y -->
+      <map code="0x28f" name="glyph00155"/><!-- LATIN LETTER SMALL CAPITAL Y -->
+      <map code="0x290" name="glyph00156"/><!-- LATIN SMALL LETTER Z WITH RETROFLEX HOOK -->
+      <map code="0x291" name="glyph00157"/><!-- LATIN SMALL LETTER Z WITH CURL -->
+      <map code="0x292" name="glyph00158"/><!-- LATIN SMALL LETTER EZH -->
+      <map code="0x293" name="glyph00159"/><!-- LATIN SMALL LETTER EZH WITH CURL -->
+      <map code="0x294" name="glyph00160"/><!-- LATIN LETTER GLOTTAL STOP -->
+      <map code="0x295" name="glyph00161"/><!-- LATIN LETTER PHARYNGEAL VOICED FRICATIVE -->
+      <map code="0x296" name="glyph00162"/><!-- LATIN LETTER INVERTED GLOTTAL STOP -->
+      <map code="0x297" name="glyph00163"/><!-- LATIN LETTER STRETCHED C -->
+      <map code="0x298" name="glyph00164"/><!-- LATIN LETTER BILABIAL CLICK -->
+      <map code="0x299" name="glyph00165"/><!-- LATIN LETTER SMALL CAPITAL B -->
+      <map code="0x29a" name="glyph00166"/><!-- LATIN SMALL LETTER CLOSED OPEN E -->
+      <map code="0x29b" name="glyph00167"/><!-- LATIN LETTER SMALL CAPITAL G WITH HOOK -->
+      <map code="0x29c" name="glyph00168"/><!-- LATIN LETTER SMALL CAPITAL H -->
+      <map code="0x29d" name="glyph00169"/><!-- LATIN SMALL LETTER J WITH CROSSED-TAIL -->
+      <map code="0x29e" name="glyph00170"/><!-- LATIN SMALL LETTER TURNED K -->
+      <map code="0x29f" name="glyph00171"/><!-- LATIN LETTER SMALL CAPITAL L -->
+      <map code="0x2a0" name="glyph00172"/><!-- LATIN SMALL LETTER Q WITH HOOK -->
+      <map code="0x2a1" name="glyph00173"/><!-- LATIN LETTER GLOTTAL STOP WITH STROKE -->
+      <map code="0x2a2" name="glyph00174"/><!-- LATIN LETTER REVERSED GLOTTAL STOP WITH STROKE -->
+      <map code="0x2a3" name="glyph00175"/><!-- LATIN SMALL LETTER DZ DIGRAPH -->
+      <map code="0x2a4" name="glyph00176"/><!-- LATIN SMALL LETTER DEZH DIGRAPH -->
+      <map code="0x2a5" name="glyph00177"/><!-- LATIN SMALL LETTER DZ DIGRAPH WITH CURL -->
+      <map code="0x2a6" name="glyph00178"/><!-- LATIN SMALL LETTER TS DIGRAPH -->
+      <map code="0x2a7" name="glyph00179"/><!-- LATIN SMALL LETTER TESH DIGRAPH -->
+      <map code="0x2a8" name="glyph00180"/><!-- LATIN SMALL LETTER TC DIGRAPH WITH CURL -->
+      <map code="0x2a9" name="glyph00181"/><!-- LATIN SMALL LETTER FENG DIGRAPH -->
+      <map code="0x2aa" name="glyph00182"/><!-- LATIN SMALL LETTER LS DIGRAPH -->
+      <map code="0x2ab" name="glyph00183"/><!-- LATIN SMALL LETTER LZ DIGRAPH -->
+      <map code="0x2ac" name="glyph00184"/><!-- LATIN LETTER BILABIAL PERCUSSIVE -->
+      <map code="0x2ad" name="glyph00185"/><!-- LATIN LETTER BIDENTAL PERCUSSIVE -->
+      <map code="0x2ae" name="glyph00186"/><!-- LATIN SMALL LETTER TURNED H WITH FISHHOOK -->
+      <map code="0x2af" name="glyph00187"/><!-- LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL -->
+      <map code="0x2b0" name="glyph00188"/><!-- MODIFIER LETTER SMALL H -->
+      <map code="0x2b1" name="glyph00189"/><!-- MODIFIER LETTER SMALL H WITH HOOK -->
+      <map code="0x2b2" name="glyph00190"/><!-- MODIFIER LETTER SMALL J -->
+      <map code="0x2b3" name="glyph00191"/><!-- MODIFIER LETTER SMALL R -->
+      <map code="0x2b4" name="glyph00192"/><!-- MODIFIER LETTER SMALL TURNED R -->
+      <map code="0x2b5" name="glyph00193"/><!-- MODIFIER LETTER SMALL TURNED R WITH HOOK -->
+      <map code="0x2b6" name="glyph00194"/><!-- MODIFIER LETTER SMALL CAPITAL INVERTED R -->
+      <map code="0x2b7" name="glyph00195"/><!-- MODIFIER LETTER SMALL W -->
+      <map code="0x2b8" name="glyph00196"/><!-- MODIFIER LETTER SMALL Y -->
+      <map code="0x2b9" name="glyph00197"/><!-- MODIFIER LETTER PRIME -->
+      <map code="0x2ba" name="glyph00198"/><!-- MODIFIER LETTER DOUBLE PRIME -->
+      <map code="0x2bb" name="glyph00199"/><!-- MODIFIER LETTER TURNED COMMA -->
+      <map code="0x2bc" name="glyph00200"/><!-- MODIFIER LETTER APOSTROPHE -->
+      <map code="0x2bd" name="glyph00201"/><!-- MODIFIER LETTER REVERSED COMMA -->
+      <map code="0x2be" name="glyph00202"/><!-- MODIFIER LETTER RIGHT HALF RING -->
+      <map code="0x2bf" name="glyph00203"/><!-- MODIFIER LETTER LEFT HALF RING -->
+      <map code="0x2c0" name="glyph00204"/><!-- MODIFIER LETTER GLOTTAL STOP -->
+      <map code="0x2c1" name="glyph00205"/><!-- MODIFIER LETTER REVERSED GLOTTAL STOP -->
+      <map code="0x2c2" name="glyph00206"/><!-- MODIFIER LETTER LEFT ARROWHEAD -->
+      <map code="0x2c3" name="glyph00207"/><!-- MODIFIER LETTER RIGHT ARROWHEAD -->
+      <map code="0x2c4" name="glyph00208"/><!-- MODIFIER LETTER UP ARROWHEAD -->
+      <map code="0x2c5" name="glyph00209"/><!-- MODIFIER LETTER DOWN ARROWHEAD -->
+      <map code="0x2c6" name="glyph00210"/><!-- MODIFIER LETTER CIRCUMFLEX ACCENT -->
+      <map code="0x2c7" name="glyph00211"/><!-- CARON -->
+      <map code="0x2c8" name="glyph00212"/><!-- MODIFIER LETTER VERTICAL LINE -->
+      <map code="0x2c9" name="glyph00213"/><!-- MODIFIER LETTER MACRON -->
+      <map code="0x2ca" name="glyph00214"/><!-- MODIFIER LETTER ACUTE ACCENT -->
+      <map code="0x2cb" name="glyph00215"/><!-- MODIFIER LETTER GRAVE ACCENT -->
+      <map code="0x2cc" name="glyph00216"/><!-- MODIFIER LETTER LOW VERTICAL LINE -->
+      <map code="0x2cd" name="glyph00217"/><!-- MODIFIER LETTER LOW MACRON -->
+      <map code="0x2ce" name="glyph00218"/><!-- MODIFIER LETTER LOW GRAVE ACCENT -->
+      <map code="0x2cf" name="glyph00219"/><!-- MODIFIER LETTER LOW ACUTE ACCENT -->
+      <map code="0x2d0" name="glyph00220"/><!-- MODIFIER LETTER TRIANGULAR COLON -->
+      <map code="0x2d1" name="glyph00221"/><!-- MODIFIER LETTER HALF TRIANGULAR COLON -->
+      <map code="0x2d2" name="glyph00222"/><!-- MODIFIER LETTER CENTRED RIGHT HALF RING -->
+      <map code="0x2d3" name="glyph00223"/><!-- MODIFIER LETTER CENTRED LEFT HALF RING -->
+      <map code="0x2d4" name="glyph00224"/><!-- MODIFIER LETTER UP TACK -->
+      <map code="0x2d5" name="glyph00225"/><!-- MODIFIER LETTER DOWN TACK -->
+      <map code="0x2d6" name="glyph00226"/><!-- MODIFIER LETTER PLUS SIGN -->
+      <map code="0x2d7" name="glyph00227"/><!-- MODIFIER LETTER MINUS SIGN -->
+      <map code="0x2d8" name="glyph00228"/><!-- BREVE -->
+      <map code="0x2d9" name="glyph00229"/><!-- DOT ABOVE -->
+      <map code="0x2da" name="glyph00230"/><!-- RING ABOVE -->
+      <map code="0x2db" name="glyph00231"/><!-- OGONEK -->
+      <map code="0x2dc" name="glyph00232"/><!-- SMALL TILDE -->
+      <map code="0x2dd" name="glyph00233"/><!-- DOUBLE ACUTE ACCENT -->
+      <map code="0x2de" name="glyph00234"/><!-- MODIFIER LETTER RHOTIC HOOK -->
+      <map code="0x2df" name="glyph00235"/><!-- MODIFIER LETTER CROSS ACCENT -->
+      <map code="0x2e0" name="glyph00236"/><!-- MODIFIER LETTER SMALL GAMMA -->
+      <map code="0x2e1" name="glyph00237"/><!-- MODIFIER LETTER SMALL L -->
+      <map code="0x2e2" name="glyph00238"/><!-- MODIFIER LETTER SMALL S -->
+      <map code="0x2e3" name="glyph00239"/><!-- MODIFIER LETTER SMALL X -->
+      <map code="0x2e4" name="glyph00240"/><!-- MODIFIER LETTER SMALL REVERSED GLOTTAL STOP -->
+      <map code="0x2e5" name="glyph00241"/><!-- MODIFIER LETTER EXTRA-HIGH TONE BAR -->
+      <map code="0x2e6" name="glyph00242"/><!-- MODIFIER LETTER HIGH TONE BAR -->
+      <map code="0x2e7" name="glyph00243"/><!-- MODIFIER LETTER MID TONE BAR -->
+      <map code="0x2e8" name="glyph00244"/><!-- MODIFIER LETTER LOW TONE BAR -->
+      <map code="0x2e9" name="glyph00245"/><!-- MODIFIER LETTER EXTRA-LOW TONE BAR -->
+      <map code="0x2ea" name="glyph00246"/><!-- MODIFIER LETTER YIN DEPARTING TONE MARK -->
+      <map code="0x2eb" name="glyph00247"/><!-- MODIFIER LETTER YANG DEPARTING TONE MARK -->
+      <map code="0x2ec" name="glyph00248"/><!-- MODIFIER LETTER VOICING -->
+      <map code="0x2ed" name="glyph00249"/><!-- MODIFIER LETTER UNASPIRATED -->
+      <map code="0x2ee" name="glyph00250"/><!-- MODIFIER LETTER DOUBLE APOSTROPHE -->
+      <map code="0x2ef" name="glyph00251"/><!-- MODIFIER LETTER LOW DOWN ARROWHEAD -->
+      <map code="0x2f0" name="glyph00252"/><!-- MODIFIER LETTER LOW UP ARROWHEAD -->
+      <map code="0x2f1" name="glyph00253"/><!-- MODIFIER LETTER LOW LEFT ARROWHEAD -->
+      <map code="0x2f2" name="glyph00254"/><!-- MODIFIER LETTER LOW RIGHT ARROWHEAD -->
+      <map code="0x2f3" name="glyph00255"/><!-- MODIFIER LETTER LOW RING -->
+      <map code="0x2f4" name="glyph00256"/><!-- MODIFIER LETTER MIDDLE GRAVE ACCENT -->
+      <map code="0x2f5" name="glyph00257"/><!-- MODIFIER LETTER MIDDLE DOUBLE GRAVE ACCENT -->
+      <map code="0x2f6" name="glyph00258"/><!-- MODIFIER LETTER MIDDLE DOUBLE ACUTE ACCENT -->
+      <map code="0x2f7" name="glyph00259"/><!-- MODIFIER LETTER LOW TILDE -->
+      <map code="0x2f8" name="glyph00260"/><!-- MODIFIER LETTER RAISED COLON -->
+      <map code="0x2f9" name="glyph00261"/><!-- MODIFIER LETTER BEGIN HIGH TONE -->
+      <map code="0x2fa" name="glyph00262"/><!-- MODIFIER LETTER END HIGH TONE -->
+      <map code="0x2fb" name="glyph00263"/><!-- MODIFIER LETTER BEGIN LOW TONE -->
+      <map code="0x2fc" name="glyph00264"/><!-- MODIFIER LETTER END LOW TONE -->
+      <map code="0x2fd" name="glyph00265"/><!-- MODIFIER LETTER SHELF -->
+      <map code="0x2fe" name="glyph00266"/><!-- MODIFIER LETTER OPEN SHELF -->
+      <map code="0x2ff" name="glyph00267"/><!-- MODIFIER LETTER LOW LEFT ARROW -->
+      <map code="0x300" name="glyph00268"/><!-- COMBINING GRAVE ACCENT -->
+      <map code="0x301" name="glyph00269"/><!-- COMBINING ACUTE ACCENT -->
+      <map code="0x302" name="glyph00270"/><!-- COMBINING CIRCUMFLEX ACCENT -->
+      <map code="0x303" name="glyph00271"/><!-- COMBINING TILDE -->
+      <map code="0x304" name="glyph00272"/><!-- COMBINING MACRON -->
+      <map code="0x305" name="glyph00273"/><!-- COMBINING OVERLINE -->
+      <map code="0x306" name="glyph00274"/><!-- COMBINING BREVE -->
+      <map code="0x307" name="glyph00275"/><!-- COMBINING DOT ABOVE -->
+      <map code="0x308" name="glyph00276"/><!-- COMBINING DIAERESIS -->
+      <map code="0x309" name="glyph00277"/><!-- COMBINING HOOK ABOVE -->
+      <map code="0x30a" name="glyph00278"/><!-- COMBINING RING ABOVE -->
+      <map code="0x30b" name="glyph00279"/><!-- COMBINING DOUBLE ACUTE ACCENT -->
+      <map code="0x30c" name="glyph00280"/><!-- COMBINING CARON -->
+      <map code="0x30d" name="glyph00281"/><!-- COMBINING VERTICAL LINE ABOVE -->
+      <map code="0x30e" name="glyph00282"/><!-- COMBINING DOUBLE VERTICAL LINE ABOVE -->
+      <map code="0x30f" name="glyph00283"/><!-- COMBINING DOUBLE GRAVE ACCENT -->
+      <map code="0x310" name="glyph00284"/><!-- COMBINING CANDRABINDU -->
+      <map code="0x311" name="glyph00285"/><!-- COMBINING INVERTED BREVE -->
+      <map code="0x312" name="glyph00286"/><!-- COMBINING TURNED COMMA ABOVE -->
+      <map code="0x313" name="glyph00287"/><!-- COMBINING COMMA ABOVE -->
+      <map code="0x314" name="glyph00288"/><!-- COMBINING REVERSED COMMA ABOVE -->
+      <map code="0x315" name="glyph00289"/><!-- COMBINING COMMA ABOVE RIGHT -->
+      <map code="0x316" name="glyph00290"/><!-- COMBINING GRAVE ACCENT BELOW -->
+      <map code="0x317" name="glyph00291"/><!-- COMBINING ACUTE ACCENT BELOW -->
+      <map code="0x318" name="glyph00292"/><!-- COMBINING LEFT TACK BELOW -->
+      <map code="0x319" name="glyph00293"/><!-- COMBINING RIGHT TACK BELOW -->
+      <map code="0x31a" name="glyph00294"/><!-- COMBINING LEFT ANGLE ABOVE -->
+      <map code="0x31b" name="glyph00295"/><!-- COMBINING HORN -->
+      <map code="0x31c" name="glyph00296"/><!-- COMBINING LEFT HALF RING BELOW -->
+      <map code="0x31d" name="glyph00297"/><!-- COMBINING UP TACK BELOW -->
+      <map code="0x31e" name="glyph00298"/><!-- COMBINING DOWN TACK BELOW -->
+      <map code="0x31f" name="glyph00299"/><!-- COMBINING PLUS SIGN BELOW -->
+      <map code="0x320" name="glyph00300"/><!-- COMBINING MINUS SIGN BELOW -->
+      <map code="0x321" name="glyph00301"/><!-- COMBINING PALATALIZED HOOK BELOW -->
+      <map code="0x322" name="glyph00302"/><!-- COMBINING RETROFLEX HOOK BELOW -->
+      <map code="0x323" name="glyph00303"/><!-- COMBINING DOT BELOW -->
+      <map code="0x324" name="glyph00304"/><!-- COMBINING DIAERESIS BELOW -->
+      <map code="0x325" name="glyph00305"/><!-- COMBINING RING BELOW -->
+      <map code="0x326" name="glyph00306"/><!-- COMBINING COMMA BELOW -->
+      <map code="0x327" name="glyph00307"/><!-- COMBINING CEDILLA -->
+      <map code="0x328" name="glyph00308"/><!-- COMBINING OGONEK -->
+      <map code="0x329" name="glyph00309"/><!-- COMBINING VERTICAL LINE BELOW -->
+      <map code="0x32a" name="glyph00310"/><!-- COMBINING BRIDGE BELOW -->
+      <map code="0x32b" name="glyph00311"/><!-- COMBINING INVERTED DOUBLE ARCH BELOW -->
+      <map code="0x32c" name="glyph00312"/><!-- COMBINING CARON BELOW -->
+      <map code="0x32d" name="glyph00313"/><!-- COMBINING CIRCUMFLEX ACCENT BELOW -->
+      <map code="0x32e" name="glyph00314"/><!-- COMBINING BREVE BELOW -->
+      <map code="0x32f" name="glyph00315"/><!-- COMBINING INVERTED BREVE BELOW -->
+      <map code="0x330" name="glyph00316"/><!-- COMBINING TILDE BELOW -->
+      <map code="0x331" name="glyph00317"/><!-- COMBINING MACRON BELOW -->
+      <map code="0x332" name="glyph00318"/><!-- COMBINING LOW LINE -->
+      <map code="0x333" name="glyph00319"/><!-- COMBINING DOUBLE LOW LINE -->
+      <map code="0x334" name="glyph00320"/><!-- COMBINING TILDE OVERLAY -->
+      <map code="0x335" name="glyph00321"/><!-- COMBINING SHORT STROKE OVERLAY -->
+      <map code="0x336" name="glyph00322"/><!-- COMBINING LONG STROKE OVERLAY -->
+      <map code="0x337" name="glyph00323"/><!-- COMBINING SHORT SOLIDUS OVERLAY -->
+      <map code="0x338" name="glyph00324"/><!-- COMBINING LONG SOLIDUS OVERLAY -->
+      <map code="0x339" name="glyph00325"/><!-- COMBINING RIGHT HALF RING BELOW -->
+      <map code="0x33a" name="glyph00326"/><!-- COMBINING INVERTED BRIDGE BELOW -->
+      <map code="0x33b" name="glyph00327"/><!-- COMBINING SQUARE BELOW -->
+      <map code="0x33c" name="glyph00328"/><!-- COMBINING SEAGULL BELOW -->
+      <map code="0x33d" name="glyph00329"/><!-- COMBINING X ABOVE -->
+      <map code="0x33e" name="glyph00330"/><!-- COMBINING VERTICAL TILDE -->
+      <map code="0x33f" name="glyph00331"/><!-- COMBINING DOUBLE OVERLINE -->
+      <map code="0x340" name="glyph00332"/><!-- COMBINING GRAVE TONE MARK -->
+      <map code="0x341" name="glyph00333"/><!-- COMBINING ACUTE TONE MARK -->
+      <map code="0x342" name="glyph00334"/><!-- COMBINING GREEK PERISPOMENI -->
+      <map code="0x343" name="glyph00335"/><!-- COMBINING GREEK KORONIS -->
+      <map code="0x344" name="glyph00336"/><!-- COMBINING GREEK DIALYTIKA TONOS -->
+      <map code="0x345" name="glyph00337"/><!-- COMBINING GREEK YPOGEGRAMMENI -->
+      <map code="0x346" name="glyph00338"/><!-- COMBINING BRIDGE ABOVE -->
+      <map code="0x347" name="glyph00339"/><!-- COMBINING EQUALS SIGN BELOW -->
+      <map code="0x348" name="glyph00340"/><!-- COMBINING DOUBLE VERTICAL LINE BELOW -->
+      <map code="0x349" name="glyph00341"/><!-- COMBINING LEFT ANGLE BELOW -->
+      <map code="0x34a" name="glyph00342"/><!-- COMBINING NOT TILDE ABOVE -->
+      <map code="0x34b" name="glyph00343"/><!-- COMBINING HOMOTHETIC ABOVE -->
+      <map code="0x34c" name="glyph00344"/><!-- COMBINING ALMOST EQUAL TO ABOVE -->
+      <map code="0x34d" name="glyph00345"/><!-- COMBINING LEFT RIGHT ARROW BELOW -->
+      <map code="0x34e" name="glyph00346"/><!-- COMBINING UPWARDS ARROW BELOW -->
+      <map code="0x34f" name="glyph00347"/><!-- COMBINING GRAPHEME JOINER -->
+      <map code="0x350" name="glyph00348"/><!-- COMBINING RIGHT ARROWHEAD ABOVE -->
+      <map code="0x351" name="glyph00349"/><!-- COMBINING LEFT HALF RING ABOVE -->
+      <map code="0x352" name="glyph00350"/><!-- COMBINING FERMATA -->
+      <map code="0x353" name="glyph00351"/><!-- COMBINING X BELOW -->
+      <map code="0x354" name="glyph00352"/><!-- COMBINING LEFT ARROWHEAD BELOW -->
+      <map code="0x355" name="glyph00353"/><!-- COMBINING RIGHT ARROWHEAD BELOW -->
+      <map code="0x356" name="glyph00354"/><!-- COMBINING RIGHT ARROWHEAD AND UP ARROWHEAD BELOW -->
+      <map code="0x357" name="glyph00355"/><!-- COMBINING RIGHT HALF RING ABOVE -->
+      <map code="0x358" name="glyph00356"/><!-- COMBINING DOT ABOVE RIGHT -->
+      <map code="0x359" name="glyph00357"/><!-- COMBINING ASTERISK BELOW -->
+      <map code="0x35a" name="glyph00358"/><!-- COMBINING DOUBLE RING BELOW -->
+      <map code="0x35b" name="glyph00359"/><!-- COMBINING ZIGZAG ABOVE -->
+      <map code="0x35c" name="glyph00360"/><!-- COMBINING DOUBLE BREVE BELOW -->
+      <map code="0x35d" name="glyph00361"/><!-- COMBINING DOUBLE BREVE -->
+      <map code="0x35e" name="glyph00362"/><!-- COMBINING DOUBLE MACRON -->
+      <map code="0x35f" name="glyph00363"/><!-- COMBINING DOUBLE MACRON BELOW -->
+      <map code="0x360" name="glyph00364"/><!-- COMBINING DOUBLE TILDE -->
+      <map code="0x361" name="glyph00365"/><!-- COMBINING DOUBLE INVERTED BREVE -->
+      <map code="0x362" name="glyph00366"/><!-- COMBINING DOUBLE RIGHTWARDS ARROW BELOW -->
+      <map code="0x363" name="glyph00367"/><!-- COMBINING LATIN SMALL LETTER A -->
+      <map code="0x364" name="glyph00368"/><!-- COMBINING LATIN SMALL LETTER E -->
+      <map code="0x365" name="glyph00369"/><!-- COMBINING LATIN SMALL LETTER I -->
+      <map code="0x366" name="glyph00370"/><!-- COMBINING LATIN SMALL LETTER O -->
+      <map code="0x367" name="glyph00371"/><!-- COMBINING LATIN SMALL LETTER U -->
+      <map code="0x368" name="glyph00372"/><!-- COMBINING LATIN SMALL LETTER C -->
+      <map code="0x369" name="glyph00373"/><!-- COMBINING LATIN SMALL LETTER D -->
+      <map code="0x36a" name="glyph00374"/><!-- COMBINING LATIN SMALL LETTER H -->
+      <map code="0x36b" name="glyph00375"/><!-- COMBINING LATIN SMALL LETTER M -->
+      <map code="0x36c" name="glyph00376"/><!-- COMBINING LATIN SMALL LETTER R -->
+      <map code="0x36d" name="glyph00377"/><!-- COMBINING LATIN SMALL LETTER T -->
+      <map code="0x36e" name="glyph00378"/><!-- COMBINING LATIN SMALL LETTER V -->
+      <map code="0x36f" name="glyph00379"/><!-- COMBINING LATIN SMALL LETTER X -->
+      <map code="0x370" name="glyph00380"/><!-- GREEK CAPITAL LETTER HETA -->
+      <map code="0x371" name="glyph00381"/><!-- GREEK SMALL LETTER HETA -->
+      <map code="0x372" name="glyph00382"/><!-- GREEK CAPITAL LETTER ARCHAIC SAMPI -->
+      <map code="0x373" name="glyph00383"/><!-- GREEK SMALL LETTER ARCHAIC SAMPI -->
+      <map code="0x374" name="glyph00384"/><!-- GREEK NUMERAL SIGN -->
+      <map code="0x375" name="glyph00385"/><!-- GREEK LOWER NUMERAL SIGN -->
+      <map code="0x376" name="glyph00386"/><!-- GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA -->
+      <map code="0x377" name="glyph00387"/><!-- GREEK SMALL LETTER PAMPHYLIAN DIGAMMA -->
+      <map code="0x378" name="glyph00388"/><!-- ???? -->
+      <map code="0x379" name="glyph00389"/><!-- ???? -->
+      <map code="0x37a" name="glyph00390"/><!-- GREEK YPOGEGRAMMENI -->
+      <map code="0x37b" name="glyph00391"/><!-- GREEK SMALL REVERSED LUNATE SIGMA SYMBOL -->
+      <map code="0x37c" name="glyph00392"/><!-- GREEK SMALL DOTTED LUNATE SIGMA SYMBOL -->
+      <map code="0x37d" name="glyph00393"/><!-- GREEK SMALL REVERSED DOTTED LUNATE SIGMA SYMBOL -->
+      <map code="0x37e" name="glyph00394"/><!-- GREEK QUESTION MARK -->
+      <map code="0x37f" name="glyph00395"/><!-- GREEK CAPITAL LETTER YOT -->
+      <map code="0x380" name="glyph00396"/><!-- ???? -->
+      <map code="0x381" name="glyph00397"/><!-- ???? -->
+      <map code="0x382" name="glyph00398"/><!-- ???? -->
+      <map code="0x383" name="glyph00399"/><!-- ???? -->
+      <map code="0x384" name="glyph00400"/><!-- GREEK TONOS -->
+      <map code="0x385" name="glyph00401"/><!-- GREEK DIALYTIKA TONOS -->
+      <map code="0x386" name="glyph00402"/><!-- GREEK CAPITAL LETTER ALPHA WITH TONOS -->
+      <map code="0x387" name="glyph00403"/><!-- GREEK ANO TELEIA -->
+      <map code="0x388" name="glyph00404"/><!-- GREEK CAPITAL LETTER EPSILON WITH TONOS -->
+      <map code="0x389" name="glyph00405"/><!-- GREEK CAPITAL LETTER ETA WITH TONOS -->
+      <map code="0x38a" name="glyph00406"/><!-- GREEK CAPITAL LETTER IOTA WITH TONOS -->
+      <map code="0x38b" name="glyph00407"/><!-- ???? -->
+      <map code="0x38c" name="glyph00408"/><!-- GREEK CAPITAL LETTER OMICRON WITH TONOS -->
+      <map code="0x38d" name="glyph00409"/><!-- ???? -->
+      <map code="0x38e" name="glyph00410"/><!-- GREEK CAPITAL LETTER UPSILON WITH TONOS -->
+      <map code="0x38f" name="glyph00411"/><!-- GREEK CAPITAL LETTER OMEGA WITH TONOS -->
+      <map code="0x390" name="glyph00412"/><!-- GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS -->
+      <map code="0x391" name="glyph00413"/><!-- GREEK CAPITAL LETTER ALPHA -->
+      <map code="0x392" name="glyph00414"/><!-- GREEK CAPITAL LETTER BETA -->
+      <map code="0x393" name="glyph00415"/><!-- GREEK CAPITAL LETTER GAMMA -->
+      <map code="0x394" name="glyph00416"/><!-- GREEK CAPITAL LETTER DELTA -->
+      <map code="0x395" name="glyph00417"/><!-- GREEK CAPITAL LETTER EPSILON -->
+      <map code="0x396" name="glyph00418"/><!-- GREEK CAPITAL LETTER ZETA -->
+      <map code="0x397" name="glyph00419"/><!-- GREEK CAPITAL LETTER ETA -->
+      <map code="0x398" name="glyph00420"/><!-- GREEK CAPITAL LETTER THETA -->
+      <map code="0x399" name="glyph00421"/><!-- GREEK CAPITAL LETTER IOTA -->
+      <map code="0x39a" name="glyph00422"/><!-- GREEK CAPITAL LETTER KAPPA -->
+      <map code="0x39b" name="glyph00423"/><!-- GREEK CAPITAL LETTER LAMDA -->
+      <map code="0x39c" name="glyph00424"/><!-- GREEK CAPITAL LETTER MU -->
+      <map code="0x39d" name="glyph00425"/><!-- GREEK CAPITAL LETTER NU -->
+      <map code="0x39e" name="glyph00426"/><!-- GREEK CAPITAL LETTER XI -->
+      <map code="0x39f" name="glyph00427"/><!-- GREEK CAPITAL LETTER OMICRON -->
+      <map code="0x3a0" name="glyph00428"/><!-- GREEK CAPITAL LETTER PI -->
+      <map code="0x3a1" name="glyph00429"/><!-- GREEK CAPITAL LETTER RHO -->
+      <map code="0x3a2" name="glyph00430"/><!-- ???? -->
+      <map code="0x3a3" name="glyph00431"/><!-- GREEK CAPITAL LETTER SIGMA -->
+      <map code="0x3a4" name="glyph00432"/><!-- GREEK CAPITAL LETTER TAU -->
+      <map code="0x3a5" name="glyph00433"/><!-- GREEK CAPITAL LETTER UPSILON -->
+      <map code="0x3a6" name="glyph00434"/><!-- GREEK CAPITAL LETTER PHI -->
+      <map code="0x3a7" name="glyph00435"/><!-- GREEK CAPITAL LETTER CHI -->
+      <map code="0x3a8" name="glyph00436"/><!-- GREEK CAPITAL LETTER PSI -->
+      <map code="0x3a9" name="glyph00437"/><!-- GREEK CAPITAL LETTER OMEGA -->
+      <map code="0x3aa" name="glyph00438"/><!-- GREEK CAPITAL LETTER IOTA WITH DIALYTIKA -->
+      <map code="0x3ab" name="glyph00439"/><!-- GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA -->
+      <map code="0x3ac" name="glyph00440"/><!-- GREEK SMALL LETTER ALPHA WITH TONOS -->
+      <map code="0x3ad" name="glyph00441"/><!-- GREEK SMALL LETTER EPSILON WITH TONOS -->
+      <map code="0x3ae" name="glyph00442"/><!-- GREEK SMALL LETTER ETA WITH TONOS -->
+      <map code="0x3af" name="glyph00443"/><!-- GREEK SMALL LETTER IOTA WITH TONOS -->
+      <map code="0x3b0" name="glyph00444"/><!-- GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS -->
+      <map code="0x3b1" name="glyph00445"/><!-- GREEK SMALL LETTER ALPHA -->
+      <map code="0x3b2" name="glyph00446"/><!-- GREEK SMALL LETTER BETA -->
+      <map code="0x3b3" name="glyph00447"/><!-- GREEK SMALL LETTER GAMMA -->
+      <map code="0x3b4" name="glyph00448"/><!-- GREEK SMALL LETTER DELTA -->
+      <map code="0x3b5" name="glyph00449"/><!-- GREEK SMALL LETTER EPSILON -->
+      <map code="0x3b6" name="glyph00450"/><!-- GREEK SMALL LETTER ZETA -->
+      <map code="0x3b7" name="glyph00451"/><!-- GREEK SMALL LETTER ETA -->
+      <map code="0x3b8" name="glyph00452"/><!-- GREEK SMALL LETTER THETA -->
+      <map code="0x3b9" name="glyph00453"/><!-- GREEK SMALL LETTER IOTA -->
+      <map code="0x3ba" name="glyph00454"/><!-- GREEK SMALL LETTER KAPPA -->
+      <map code="0x3bb" name="glyph00455"/><!-- GREEK SMALL LETTER LAMDA -->
+      <map code="0x3bc" name="glyph00456"/><!-- GREEK SMALL LETTER MU -->
+      <map code="0x3bd" name="glyph00457"/><!-- GREEK SMALL LETTER NU -->
+      <map code="0x3be" name="glyph00458"/><!-- GREEK SMALL LETTER XI -->
+      <map code="0x3bf" name="glyph00459"/><!-- GREEK SMALL LETTER OMICRON -->
+      <map code="0x3c0" name="glyph00460"/><!-- GREEK SMALL LETTER PI -->
+      <map code="0x3c1" name="glyph00461"/><!-- GREEK SMALL LETTER RHO -->
+      <map code="0x3c2" name="glyph00462"/><!-- GREEK SMALL LETTER FINAL SIGMA -->
+      <map code="0x3c3" name="glyph00463"/><!-- GREEK SMALL LETTER SIGMA -->
+      <map code="0x3c4" name="glyph00464"/><!-- GREEK SMALL LETTER TAU -->
+      <map code="0x3c5" name="glyph00465"/><!-- GREEK SMALL LETTER UPSILON -->
+      <map code="0x3c6" name="glyph00466"/><!-- GREEK SMALL LETTER PHI -->
+      <map code="0x3c7" name="glyph00467"/><!-- GREEK SMALL LETTER CHI -->
+      <map code="0x3c8" name="glyph00468"/><!-- GREEK SMALL LETTER PSI -->
+      <map code="0x3c9" name="glyph00469"/><!-- GREEK SMALL LETTER OMEGA -->
+      <map code="0x3ca" name="glyph00470"/><!-- GREEK SMALL LETTER IOTA WITH DIALYTIKA -->
+      <map code="0x3cb" name="glyph00471"/><!-- GREEK SMALL LETTER UPSILON WITH DIALYTIKA -->
+      <map code="0x3cc" name="glyph00472"/><!-- GREEK SMALL LETTER OMICRON WITH TONOS -->
+      <map code="0x3cd" name="glyph00473"/><!-- GREEK SMALL LETTER UPSILON WITH TONOS -->
+      <map code="0x3ce" name="glyph00474"/><!-- GREEK SMALL LETTER OMEGA WITH TONOS -->
+      <map code="0x3cf" name="glyph00475"/><!-- GREEK CAPITAL KAI SYMBOL -->
+      <map code="0x3d0" name="glyph00476"/><!-- GREEK BETA SYMBOL -->
+      <map code="0x3d1" name="glyph00477"/><!-- GREEK THETA SYMBOL -->
+      <map code="0x3d2" name="glyph00478"/><!-- GREEK UPSILON WITH HOOK SYMBOL -->
+      <map code="0x3d3" name="glyph00479"/><!-- GREEK UPSILON WITH ACUTE AND HOOK SYMBOL -->
+      <map code="0x3d4" name="glyph00480"/><!-- GREEK UPSILON WITH DIAERESIS AND HOOK SYMBOL -->
+      <map code="0x3d5" name="glyph00481"/><!-- GREEK PHI SYMBOL -->
+      <map code="0x3d6" name="glyph00482"/><!-- GREEK PI SYMBOL -->
+      <map code="0x3d7" name="glyph00483"/><!-- GREEK KAI SYMBOL -->
+      <map code="0x3d8" name="glyph00484"/><!-- GREEK LETTER ARCHAIC KOPPA -->
+      <map code="0x3d9" name="glyph00485"/><!-- GREEK SMALL LETTER ARCHAIC KOPPA -->
+      <map code="0x3da" name="glyph00486"/><!-- GREEK LETTER STIGMA -->
+      <map code="0x3db" name="glyph00487"/><!-- GREEK SMALL LETTER STIGMA -->
+      <map code="0x3dc" name="glyph00488"/><!-- GREEK LETTER DIGAMMA -->
+      <map code="0x3dd" name="glyph00489"/><!-- GREEK SMALL LETTER DIGAMMA -->
+      <map code="0x3de" name="glyph00490"/><!-- GREEK LETTER KOPPA -->
+      <map code="0x3df" name="glyph00491"/><!-- GREEK SMALL LETTER KOPPA -->
+      <map code="0x3e0" name="glyph00492"/><!-- GREEK LETTER SAMPI -->
+      <map code="0x3e1" name="glyph00493"/><!-- GREEK SMALL LETTER SAMPI -->
+      <map code="0x3e2" name="glyph00494"/><!-- COPTIC CAPITAL LETTER SHEI -->
+      <map code="0x3e3" name="glyph00495"/><!-- COPTIC SMALL LETTER SHEI -->
+      <map code="0x3e4" name="glyph00496"/><!-- COPTIC CAPITAL LETTER FEI -->
+      <map code="0x3e5" name="glyph00497"/><!-- COPTIC SMALL LETTER FEI -->
+      <map code="0x3e6" name="glyph00498"/><!-- COPTIC CAPITAL LETTER KHEI -->
+      <map code="0x3e7" name="glyph00499"/><!-- COPTIC SMALL LETTER KHEI -->
+      <map code="0x3e8" name="glyph00500"/><!-- COPTIC CAPITAL LETTER HORI -->
+      <map code="0xafc8" name="glyph44500"/><!-- HANGUL SYLLABLE GGUM -->
+      <map code="0xafc9" name="glyph44501"/><!-- HANGUL SYLLABLE GGUB -->
+      <map code="0xafca" name="glyph44502"/><!-- HANGUL SYLLABLE GGUBS -->
+      <map code="0xafcb" name="glyph44503"/><!-- HANGUL SYLLABLE GGUS -->
+      <map code="0xafcc" name="glyph44504"/><!-- HANGUL SYLLABLE GGUSS -->
+      <map code="0xafcd" name="glyph44505"/><!-- HANGUL SYLLABLE GGUNG -->
+      <map code="0xafce" name="glyph44506"/><!-- HANGUL SYLLABLE GGUJ -->
+      <map code="0xafcf" name="glyph44507"/><!-- HANGUL SYLLABLE GGUC -->
+      <map code="0xafd0" name="glyph44508"/><!-- HANGUL SYLLABLE GGUK -->
+      <map code="0xafd1" name="glyph44509"/><!-- HANGUL SYLLABLE GGUT -->
+      <map code="0xafd2" name="glyph44510"/><!-- HANGUL SYLLABLE GGUP -->
+      <map code="0xafd3" name="glyph44511"/><!-- HANGUL SYLLABLE GGUH -->
+      <map code="0xafd4" name="glyph44512"/><!-- HANGUL SYLLABLE GGWEO -->
+      <map code="0xafd5" name="glyph44513"/><!-- HANGUL SYLLABLE GGWEOG -->
+      <map code="0xafd6" name="glyph44514"/><!-- HANGUL SYLLABLE GGWEOGG -->
+      <map code="0xafd7" name="glyph44515"/><!-- HANGUL SYLLABLE GGWEOGS -->
+      <map code="0xafd8" name="glyph44516"/><!-- HANGUL SYLLABLE GGWEON -->
+      <map code="0xafd9" name="glyph44517"/><!-- HANGUL SYLLABLE GGWEONJ -->
+      <map code="0xafda" name="glyph44518"/><!-- HANGUL SYLLABLE GGWEONH -->
+      <map code="0xafdb" name="glyph44519"/><!-- HANGUL SYLLABLE GGWEOD -->
+      <map code="0xafdc" name="glyph44520"/><!-- HANGUL SYLLABLE GGWEOL -->
+      <map code="0xafdd" name="glyph44521"/><!-- HANGUL SYLLABLE GGWEOLG -->
+      <map code="0xafde" name="glyph44522"/><!-- HANGUL SYLLABLE GGWEOLM -->
+      <map code="0xafdf" name="glyph44523"/><!-- HANGUL SYLLABLE GGWEOLB -->
+      <map code="0xafe0" name="glyph44524"/><!-- HANGUL SYLLABLE GGWEOLS -->
+      <map code="0xafe1" name="glyph44525"/><!-- HANGUL SYLLABLE GGWEOLT -->
+      <map code="0xafe2" name="glyph44526"/><!-- HANGUL SYLLABLE GGWEOLP -->
+      <map code="0xafe3" name="glyph44527"/><!-- HANGUL SYLLABLE GGWEOLH -->
+      <map code="0xafe4" name="glyph44528"/><!-- HANGUL SYLLABLE GGWEOM -->
+      <map code="0xafe5" name="glyph44529"/><!-- HANGUL SYLLABLE GGWEOB -->
+      <map code="0xafe6" name="glyph44530"/><!-- HANGUL SYLLABLE GGWEOBS -->
+      <map code="0xafe7" name="glyph44531"/><!-- HANGUL SYLLABLE GGWEOS -->
+      <map code="0xafe8" name="glyph44532"/><!-- HANGUL SYLLABLE GGWEOSS -->
+      <map code="0xafe9" name="glyph44533"/><!-- HANGUL SYLLABLE GGWEONG -->
+      <map code="0xafea" name="glyph44534"/><!-- HANGUL SYLLABLE GGWEOJ -->
+      <map code="0xafeb" name="glyph44535"/><!-- HANGUL SYLLABLE GGWEOC -->
+      <map code="0xafec" name="glyph44536"/><!-- HANGUL SYLLABLE GGWEOK -->
+      <map code="0xafed" name="glyph44537"/><!-- HANGUL SYLLABLE GGWEOT -->
+      <map code="0xafee" name="glyph44538"/><!-- HANGUL SYLLABLE GGWEOP -->
+      <map code="0xafef" name="glyph44539"/><!-- HANGUL SYLLABLE GGWEOH -->
+      <map code="0xaff0" name="glyph44540"/><!-- HANGUL SYLLABLE GGWE -->
+      <map code="0xaff1" name="glyph44541"/><!-- HANGUL SYLLABLE GGWEG -->
+      <map code="0xaff2" name="glyph44542"/><!-- HANGUL SYLLABLE GGWEGG -->
+      <map code="0xaff3" name="glyph44543"/><!-- HANGUL SYLLABLE GGWEGS -->
+      <map code="0xaff4" name="glyph44544"/><!-- HANGUL SYLLABLE GGWEN -->
+      <map code="0xaff5" name="glyph44545"/><!-- HANGUL SYLLABLE GGWENJ -->
+      <map code="0xaff6" name="glyph44546"/><!-- HANGUL SYLLABLE GGWENH -->
+      <map code="0xaff7" name="glyph44547"/><!-- HANGUL SYLLABLE GGWED -->
+      <map code="0xaff8" name="glyph44548"/><!-- HANGUL SYLLABLE GGWEL -->
+      <map code="0xaff9" name="glyph44549"/><!-- HANGUL SYLLABLE GGWELG -->
+      <map code="0xaffa" name="glyph44550"/><!-- HANGUL SYLLABLE GGWELM -->
+      <map code="0xaffb" name="glyph44551"/><!-- HANGUL SYLLABLE GGWELB -->
+      <map code="0xaffc" name="glyph44552"/><!-- HANGUL SYLLABLE GGWELS -->
+      <map code="0xaffd" name="glyph44553"/><!-- HANGUL SYLLABLE GGWELT -->
+      <map code="0xaffe" name="glyph44554"/><!-- HANGUL SYLLABLE GGWELP -->
+      <map code="0xafff" name="glyph44555"/><!-- HANGUL SYLLABLE GGWELH -->
+      <map code="0xb000" name="glyph44556"/><!-- HANGUL SYLLABLE GGWEM -->
+      <map code="0xb001" name="glyph44557"/><!-- HANGUL SYLLABLE GGWEB -->
+      <map code="0xb002" name="glyph44558"/><!-- HANGUL SYLLABLE GGWEBS -->
+      <map code="0xb003" name="glyph44559"/><!-- HANGUL SYLLABLE GGWES -->
+      <map code="0xb004" name="glyph44560"/><!-- HANGUL SYLLABLE GGWESS -->
+      <map code="0xb005" name="glyph44561"/><!-- HANGUL SYLLABLE GGWENG -->
+      <map code="0xb006" name="glyph44562"/><!-- HANGUL SYLLABLE GGWEJ -->
+      <map code="0xb007" name="glyph44563"/><!-- HANGUL SYLLABLE GGWEC -->
+      <map code="0xb008" name="glyph44564"/><!-- HANGUL SYLLABLE GGWEK -->
+      <map code="0xb009" name="glyph44565"/><!-- HANGUL SYLLABLE GGWET -->
+      <map code="0xb00a" name="glyph44566"/><!-- HANGUL SYLLABLE GGWEP -->
+      <map code="0xb00b" name="glyph44567"/><!-- HANGUL SYLLABLE GGWEH -->
+      <map code="0xb00c" name="glyph44568"/><!-- HANGUL SYLLABLE GGWI -->
+      <map code="0xb00d" name="glyph44569"/><!-- HANGUL SYLLABLE GGWIG -->
+      <map code="0xb00e" name="glyph44570"/><!-- HANGUL SYLLABLE GGWIGG -->
+      <map code="0xb00f" name="glyph44571"/><!-- HANGUL SYLLABLE GGWIGS -->
+      <map code="0xb010" name="glyph44572"/><!-- HANGUL SYLLABLE GGWIN -->
+      <map code="0xb011" name="glyph44573"/><!-- HANGUL SYLLABLE GGWINJ -->
+      <map code="0xb012" name="glyph44574"/><!-- HANGUL SYLLABLE GGWINH -->
+      <map code="0xb013" name="glyph44575"/><!-- HANGUL SYLLABLE GGWID -->
+      <map code="0xb014" name="glyph44576"/><!-- HANGUL SYLLABLE GGWIL -->
+      <map code="0xb015" name="glyph44577"/><!-- HANGUL SYLLABLE GGWILG -->
+      <map code="0xb016" name="glyph44578"/><!-- HANGUL SYLLABLE GGWILM -->
+      <map code="0xb017" name="glyph44579"/><!-- HANGUL SYLLABLE GGWILB -->
+      <map code="0xb018" name="glyph44580"/><!-- HANGUL SYLLABLE GGWILS -->
+      <map code="0xb019" name="glyph44581"/><!-- HANGUL SYLLABLE GGWILT -->
+      <map code="0xb01a" name="glyph44582"/><!-- HANGUL SYLLABLE GGWILP -->
+      <map code="0xb01b" name="glyph44583"/><!-- HANGUL SYLLABLE GGWILH -->
+      <map code="0xb01c" name="glyph44584"/><!-- HANGUL SYLLABLE GGWIM -->
+      <map code="0xb01d" name="glyph44585"/><!-- HANGUL SYLLABLE GGWIB -->
+      <map code="0xb01e" name="glyph44586"/><!-- HANGUL SYLLABLE GGWIBS -->
+      <map code="0xb01f" name="glyph44587"/><!-- HANGUL SYLLABLE GGWIS -->
+      <map code="0xb020" name="glyph44588"/><!-- HANGUL SYLLABLE GGWISS -->
+      <map code="0xb021" name="glyph44589"/><!-- HANGUL SYLLABLE GGWING -->
+      <map code="0xb022" name="glyph44590"/><!-- HANGUL SYLLABLE GGWIJ -->
+      <map code="0xb023" name="glyph44591"/><!-- HANGUL SYLLABLE GGWIC -->
+      <map code="0xb024" name="glyph44592"/><!-- HANGUL SYLLABLE GGWIK -->
+      <map code="0xb025" name="glyph44593"/><!-- HANGUL SYLLABLE GGWIT -->
+      <map code="0xb026" name="glyph44594"/><!-- HANGUL SYLLABLE GGWIP -->
+      <map code="0xb027" name="glyph44595"/><!-- HANGUL SYLLABLE GGWIH -->
+      <map code="0xb028" name="glyph44596"/><!-- HANGUL SYLLABLE GGYU -->
+      <map code="0xb029" name="glyph44597"/><!-- HANGUL SYLLABLE GGYUG -->
+      <map code="0xb02a" name="glyph44598"/><!-- HANGUL SYLLABLE GGYUGG -->
+      <map code="0xb02b" name="glyph44599"/><!-- HANGUL SYLLABLE GGYUGS -->
+      <map code="0xb02c" name="glyph44600"/><!-- HANGUL SYLLABLE GGYUN -->
+    </cmap_format_4>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/cmap6_font1.otf b/Tests/ttLib/tables/data/aots/cmap6_font1.otf
new file mode 100644
index 0000000..10b64a7
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap6_font1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/cmap6_font1.ttx.cmap b/Tests/ttLib/tables/data/aots/cmap6_font1.ttx.cmap
new file mode 100644
index 0000000..85a7bc9
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap6_font1.ttx.cmap
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_6 platformID="3" platEncID="1" language="0">
+      <map code="0x22" name="g17"/><!-- QUOTATION MARK -->
+      <map code="0x23" name="g56"/><!-- NUMBER SIGN -->
+      <map code="0x24" name="g12"/><!-- DOLLAR SIGN -->
+    </cmap_format_6>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/cmap6_font2.otf b/Tests/ttLib/tables/data/aots/cmap6_font2.otf
new file mode 100644
index 0000000..2d2957f
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap6_font2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/cmap6_font2.ttx.cmap b/Tests/ttLib/tables/data/aots/cmap6_font2.ttx.cmap
new file mode 100644
index 0000000..c576ce7
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap6_font2.ttx.cmap
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_6 platformID="3" platEncID="1" language="0">
+    </cmap_format_6>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/cmap8_font1.otf b/Tests/ttLib/tables/data/aots/cmap8_font1.otf
new file mode 100644
index 0000000..791b9e3
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap8_font1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/cmap8_font1.ttx.cmap b/Tests/ttLib/tables/data/aots/cmap8_font1.ttx.cmap
new file mode 100644
index 0000000..ded3b1f
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap8_font1.ttx.cmap
@@ -0,0 +1,529 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_8 platformID="3" platEncID="1">
+      00080000 00002064 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000000
+      00000000 00000000 00000000 00000007
+      00000034 00000034 00000011 00000035
+      00000035 00000038 00000036 00000036
+      0000000c 00008432 00008434 00000014
+      00009232 00009234 00000017 00109423
+      00109424 0000001a 00109425 00109425
+      00000020   
+    </cmap_format_8>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/cmap_composition_font1.otf b/Tests/ttLib/tables/data/aots/cmap_composition_font1.otf
new file mode 100644
index 0000000..c790717
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap_composition_font1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/cmap_composition_font1.ttx.cmap b/Tests/ttLib/tables/data/aots/cmap_composition_font1.ttx.cmap
new file mode 100644
index 0000000..07468c1
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap_composition_font1.ttx.cmap
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="76" language="0" nGroups="5">
+      <map code="0xe9" name="g5"/><!-- LATIN SMALL LETTER E WITH ACUTE -->
+      <map code="0x1ec3" name="g6"/><!-- LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE -->
+      <map code="0x1fad" name="g7"/><!-- GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI -->
+      <map code="0x2adc" name="g8"/><!-- FORKING -->
+      <map code="0x1d163" name="g9"/><!-- MUSICAL SYMBOL SIXTY-FOURTH NOTE -->
+    </cmap_format_12>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/cmap_subtableselection_font1.otf b/Tests/ttLib/tables/data/aots/cmap_subtableselection_font1.otf
new file mode 100644
index 0000000..8929f8a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap_subtableselection_font1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/cmap_subtableselection_font1.ttx.cmap b/Tests/ttLib/tables/data/aots/cmap_subtableselection_font1.ttx.cmap
new file mode 100644
index 0000000..b83a043
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap_subtableselection_font1.ttx.cmap
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_0 platformID="0" platEncID="3" language="0">
+      <map code="0x30" name="g4"/><!-- DIGIT ZERO -->
+    </cmap_format_0>
+    <cmap_format_0 platformID="0" platEncID="4" language="0">
+      <map code="0x30" name="g2"/><!-- DIGIT ZERO -->
+    </cmap_format_0>
+    <cmap_format_0 platformID="1" platEncID="0" language="0">
+      <map code="0x30" name="g5"/>
+    </cmap_format_0>
+    <cmap_format_0 platformID="3" platEncID="1" language="0">
+      <map code="0x30" name="g3"/><!-- DIGIT ZERO -->
+    </cmap_format_0>
+    <cmap_format_0 platformID="3" platEncID="10" language="0">
+      <map code="0x30" name="g1"/><!-- DIGIT ZERO -->
+    </cmap_format_0>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/cmap_subtableselection_font2.otf b/Tests/ttLib/tables/data/aots/cmap_subtableselection_font2.otf
new file mode 100644
index 0000000..2611092
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap_subtableselection_font2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/cmap_subtableselection_font2.ttx.cmap b/Tests/ttLib/tables/data/aots/cmap_subtableselection_font2.ttx.cmap
new file mode 100644
index 0000000..60a5cbc
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap_subtableselection_font2.ttx.cmap
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_0 platformID="0" platEncID="3" language="0">
+      <map code="0x30" name="g4"/><!-- DIGIT ZERO -->
+    </cmap_format_0>
+    <cmap_format_0 platformID="0" platEncID="4" language="0">
+      <map code="0x30" name="g2"/><!-- DIGIT ZERO -->
+    </cmap_format_0>
+    <cmap_format_0 platformID="1" platEncID="0" language="0">
+      <map code="0x30" name="g5"/>
+    </cmap_format_0>
+    <cmap_format_0 platformID="3" platEncID="1" language="0">
+      <map code="0x30" name="g3"/><!-- DIGIT ZERO -->
+    </cmap_format_0>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/cmap_subtableselection_font3.otf b/Tests/ttLib/tables/data/aots/cmap_subtableselection_font3.otf
new file mode 100644
index 0000000..9f39331
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap_subtableselection_font3.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/cmap_subtableselection_font3.ttx.cmap b/Tests/ttLib/tables/data/aots/cmap_subtableselection_font3.ttx.cmap
new file mode 100644
index 0000000..d363b8e
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap_subtableselection_font3.ttx.cmap
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_0 platformID="0" platEncID="3" language="0">
+      <map code="0x30" name="g4"/><!-- DIGIT ZERO -->
+    </cmap_format_0>
+    <cmap_format_0 platformID="1" platEncID="0" language="0">
+      <map code="0x30" name="g5"/>
+    </cmap_format_0>
+    <cmap_format_0 platformID="3" platEncID="1" language="0">
+      <map code="0x30" name="g3"/><!-- DIGIT ZERO -->
+    </cmap_format_0>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/cmap_subtableselection_font4.otf b/Tests/ttLib/tables/data/aots/cmap_subtableselection_font4.otf
new file mode 100644
index 0000000..83ae88e
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap_subtableselection_font4.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/cmap_subtableselection_font4.ttx.cmap b/Tests/ttLib/tables/data/aots/cmap_subtableselection_font4.ttx.cmap
new file mode 100644
index 0000000..10c4b66
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap_subtableselection_font4.ttx.cmap
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_0 platformID="0" platEncID="3" language="0">
+      <map code="0x30" name="g4"/><!-- DIGIT ZERO -->
+    </cmap_format_0>
+    <cmap_format_0 platformID="1" platEncID="0" language="0">
+      <map code="0x30" name="g5"/>
+    </cmap_format_0>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/cmap_subtableselection_font5.otf b/Tests/ttLib/tables/data/aots/cmap_subtableselection_font5.otf
new file mode 100644
index 0000000..8b614ad
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap_subtableselection_font5.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/cmap_subtableselection_font5.ttx.cmap b/Tests/ttLib/tables/data/aots/cmap_subtableselection_font5.ttx.cmap
new file mode 100644
index 0000000..4005019
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/cmap_subtableselection_font5.ttx.cmap
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_0 platformID="1" platEncID="0" language="0">
+      <map code="0x30" name="g5"/>
+    </cmap_format_0>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos1_1_lookupflag_f1.otf b/Tests/ttLib/tables/data/aots/gpos1_1_lookupflag_f1.otf
new file mode 100644
index 0000000..3245425
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos1_1_lookupflag_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos1_1_lookupflag_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos1_1_lookupflag_f1.ttx.GDEF
new file mode 100644
index 0000000..971a3f1
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos1_1_lookupflag_f1.ttx.GDEF
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g18" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos1_1_lookupflag_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos1_1_lookupflag_f1.ttx.GPOS
new file mode 100644
index 0000000..93d3a05
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos1_1_lookupflag_f1.ttx.GPOS
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="2"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g18"/>
+            <Glyph value="g20"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="-300"/>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos1_1_simple_f1.otf b/Tests/ttLib/tables/data/aots/gpos1_1_simple_f1.otf
new file mode 100644
index 0000000..c5f8888
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos1_1_simple_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos1_1_simple_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos1_1_simple_f1.ttx.GPOS
new file mode 100644
index 0000000..00eacc3
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos1_1_simple_f1.ttx.GPOS
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g18"/>
+            <Glyph value="g20"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="-200"/>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos1_1_simple_f2.otf b/Tests/ttLib/tables/data/aots/gpos1_1_simple_f2.otf
new file mode 100644
index 0000000..905d0a3
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos1_1_simple_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos1_1_simple_f2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos1_1_simple_f2.ttx.GPOS
new file mode 100644
index 0000000..1eff021
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos1_1_simple_f2.ttx.GPOS
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g18"/>
+            <Glyph value="g20"/>
+          </Coverage>
+          <ValueFormat value="2"/>
+          <Value YPlacement="-200"/>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos1_1_simple_f3.otf b/Tests/ttLib/tables/data/aots/gpos1_1_simple_f3.otf
new file mode 100644
index 0000000..550be87
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos1_1_simple_f3.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos1_1_simple_f3.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos1_1_simple_f3.ttx.GPOS
new file mode 100644
index 0000000..c3850df
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos1_1_simple_f3.ttx.GPOS
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g18"/>
+            <Glyph value="g20"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="-200"/>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos1_1_simple_f4.otf b/Tests/ttLib/tables/data/aots/gpos1_1_simple_f4.otf
new file mode 100644
index 0000000..448bc8b
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos1_1_simple_f4.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos1_1_simple_f4.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos1_1_simple_f4.ttx.GPOS
new file mode 100644
index 0000000..f80286c
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos1_1_simple_f4.ttx.GPOS
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g18"/>
+            <Glyph value="g20"/>
+          </Coverage>
+          <ValueFormat value="8"/>
+          <Value YAdvance="-200"/>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos1_2_font1.otf b/Tests/ttLib/tables/data/aots/gpos1_2_font1.otf
new file mode 100644
index 0000000..3e7b7bc
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos1_2_font1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos1_2_font1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos1_2_font1.ttx.GPOS
new file mode 100644
index 0000000..11351a6
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos1_2_font1.ttx.GPOS
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g18"/>
+            <Glyph value="g20"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <!-- ValueCount=2 -->
+          <Value index="0" XPlacement="-200"/>
+          <Value index="1" XPlacement="-300"/>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos1_2_font2.otf b/Tests/ttLib/tables/data/aots/gpos1_2_font2.otf
new file mode 100644
index 0000000..ba9d224
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos1_2_font2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos1_2_font2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos1_2_font2.ttx.GDEF
new file mode 100644
index 0000000..971a3f1
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos1_2_font2.ttx.GDEF
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g18" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos1_2_font2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos1_2_font2.ttx.GPOS
new file mode 100644
index 0000000..88257ac
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos1_2_font2.ttx.GPOS
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="2"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g18"/>
+            <Glyph value="g20"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <!-- ValueCount=2 -->
+          <Value index="0" XPlacement="-200"/>
+          <Value index="1" XPlacement="-300"/>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos2_1_font6.otf b/Tests/ttLib/tables/data/aots/gpos2_1_font6.otf
new file mode 100644
index 0000000..cd4ea94
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_1_font6.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos2_1_font6.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos2_1_font6.ttx.GPOS
new file mode 100644
index 0000000..db1315b
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_1_font6.ttx.GPOS
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g18"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="2"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=2 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g19"/>
+              <Value1 XPlacement="-200"/>
+              <Value2 YPlacement="-100"/>
+            </PairValueRecord>
+            <PairValueRecord index="1">
+              <SecondGlyph value="g20"/>
+              <Value1 XPlacement="-300"/>
+              <Value2 YPlacement="-400"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos2_1_font7.otf b/Tests/ttLib/tables/data/aots/gpos2_1_font7.otf
new file mode 100644
index 0000000..2871acc
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_1_font7.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos2_1_font7.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos2_1_font7.ttx.GPOS
new file mode 100644
index 0000000..8b22294
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_1_font7.ttx.GPOS
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g18"/>
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="2"/>
+          <!-- PairSetCount=2 -->
+          <PairSet index="0">
+            <!-- PairValueCount=2 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g19"/>
+              <Value1 XPlacement="-200"/>
+              <Value2 YPlacement="-100"/>
+            </PairValueRecord>
+            <PairValueRecord index="1">
+              <SecondGlyph value="g20"/>
+              <Value1 XPlacement="-300"/>
+              <Value2 YPlacement="-400"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="1">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="-500"/>
+              <Value2 YPlacement="-600"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f1.otf b/Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f1.otf
new file mode 100644
index 0000000..6003782
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f1.ttx.GDEF
new file mode 100644
index 0000000..971a3f1
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f1.ttx.GDEF
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g18" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f1.ttx.GPOS
new file mode 100644
index 0000000..06b0691
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f1.ttx.GPOS
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="2"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g19"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="2"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g20"/>
+              <Value1 XPlacement="-200"/>
+              <Value2 YPlacement="-100"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f2.otf b/Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f2.otf
new file mode 100644
index 0000000..9d0a273
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f2.ttx.GDEF
new file mode 100644
index 0000000..971a3f1
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f2.ttx.GDEF
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g18" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f2.ttx.GPOS
new file mode 100644
index 0000000..03e9f8b
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f2.ttx.GPOS
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="2"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g19"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="2"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g20"/>
+              <Value1 XAdvance="-200"/>
+              <Value2 YPlacement="-100"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos2_1_next_glyph_f1.otf b/Tests/ttLib/tables/data/aots/gpos2_1_next_glyph_f1.otf
new file mode 100644
index 0000000..64d6c2c
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_1_next_glyph_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos2_1_next_glyph_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos2_1_next_glyph_f1.ttx.GPOS
new file mode 100644
index 0000000..d8b4e83
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_1_next_glyph_f1.ttx.GPOS
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g18"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="2"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g18"/>
+              <Value1 XPlacement="-100"/>
+              <Value2 YPlacement="-100"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos2_1_next_glyph_f2.otf b/Tests/ttLib/tables/data/aots/gpos2_1_next_glyph_f2.otf
new file mode 100644
index 0000000..3d8c37a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_1_next_glyph_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos2_1_next_glyph_f2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos2_1_next_glyph_f2.ttx.GPOS
new file mode 100644
index 0000000..cf71f47
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_1_next_glyph_f2.ttx.GPOS
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g18"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g18"/>
+              <Value1 XPlacement="-100"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos2_1_simple_f1.otf b/Tests/ttLib/tables/data/aots/gpos2_1_simple_f1.otf
new file mode 100644
index 0000000..c947776
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_1_simple_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos2_1_simple_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos2_1_simple_f1.ttx.GPOS
new file mode 100644
index 0000000..81b1720
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_1_simple_f1.ttx.GPOS
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g18"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="2"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g19"/>
+              <Value1 XPlacement="-200"/>
+              <Value2 YPlacement="-100"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos2_2_font1.otf b/Tests/ttLib/tables/data/aots/gpos2_2_font1.otf
new file mode 100644
index 0000000..dde370a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_2_font1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos2_2_font1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos2_2_font1.ttx.GPOS
new file mode 100644
index 0000000..5595b99
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_2_font1.ttx.GPOS
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g18"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="2"/>
+          <ClassDef1 Format="2">
+            <ClassDef glyph="g18" class="1"/>
+          </ClassDef1>
+          <ClassDef2 Format="2">
+            <ClassDef glyph="g19" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=2 -->
+          <!-- Class2Count=2 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XPlacement="0"/>
+              <Value2 YPlacement="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XPlacement="0"/>
+              <Value2 YPlacement="0"/>
+            </Class2Record>
+          </Class1Record>
+          <Class1Record index="1">
+            <Class2Record index="0">
+              <Value1 XPlacement="0"/>
+              <Value2 YPlacement="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XPlacement="-200"/>
+              <Value2 YPlacement="-100"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos2_2_font2.otf b/Tests/ttLib/tables/data/aots/gpos2_2_font2.otf
new file mode 100644
index 0000000..63d874a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_2_font2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos2_2_font2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos2_2_font2.ttx.GDEF
new file mode 100644
index 0000000..971a3f1
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_2_font2.ttx.GDEF
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g18" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos2_2_font2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos2_2_font2.ttx.GPOS
new file mode 100644
index 0000000..7896be3
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_2_font2.ttx.GPOS
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="2"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g19"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="2"/>
+          <ClassDef1 Format="2">
+            <ClassDef glyph="g19" class="1"/>
+          </ClassDef1>
+          <ClassDef2 Format="2">
+            <ClassDef glyph="g20" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=2 -->
+          <!-- Class2Count=2 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XPlacement="0"/>
+              <Value2 YPlacement="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XPlacement="0"/>
+              <Value2 YPlacement="0"/>
+            </Class2Record>
+          </Class1Record>
+          <Class1Record index="1">
+            <Class2Record index="0">
+              <Value1 XPlacement="0"/>
+              <Value2 YPlacement="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XPlacement="-200"/>
+              <Value2 YPlacement="-100"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos2_2_font3.otf b/Tests/ttLib/tables/data/aots/gpos2_2_font3.otf
new file mode 100644
index 0000000..b530676
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_2_font3.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos2_2_font3.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos2_2_font3.ttx.GDEF
new file mode 100644
index 0000000..971a3f1
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_2_font3.ttx.GDEF
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g18" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos2_2_font3.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos2_2_font3.ttx.GPOS
new file mode 100644
index 0000000..216a591
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_2_font3.ttx.GPOS
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="2"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g19"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="2"/>
+          <ClassDef1 Format="2">
+            <ClassDef glyph="g19" class="1"/>
+          </ClassDef1>
+          <ClassDef2 Format="2">
+            <ClassDef glyph="g20" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=2 -->
+          <!-- Class2Count=2 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+              <Value2 YPlacement="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="0"/>
+              <Value2 YPlacement="0"/>
+            </Class2Record>
+          </Class1Record>
+          <Class1Record index="1">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+              <Value2 YPlacement="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="-200"/>
+              <Value2 YPlacement="-100"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos2_2_font4.otf b/Tests/ttLib/tables/data/aots/gpos2_2_font4.otf
new file mode 100644
index 0000000..b549e02
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_2_font4.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos2_2_font4.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos2_2_font4.ttx.GPOS
new file mode 100644
index 0000000..a2e6017
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_2_font4.ttx.GPOS
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g18"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="2"/>
+          <ClassDef1 Format="2">
+            <ClassDef glyph="g18" class="1"/>
+          </ClassDef1>
+          <ClassDef2 Format="2">
+            <ClassDef glyph="g18" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=2 -->
+          <!-- Class2Count=2 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XPlacement="0"/>
+              <Value2 YPlacement="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XPlacement="0"/>
+              <Value2 YPlacement="0"/>
+            </Class2Record>
+          </Class1Record>
+          <Class1Record index="1">
+            <Class2Record index="0">
+              <Value1 XPlacement="0"/>
+              <Value2 YPlacement="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XPlacement="-100"/>
+              <Value2 YPlacement="-100"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos2_2_font5.otf b/Tests/ttLib/tables/data/aots/gpos2_2_font5.otf
new file mode 100644
index 0000000..64c40bb
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_2_font5.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos2_2_font5.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos2_2_font5.ttx.GPOS
new file mode 100644
index 0000000..d269735
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos2_2_font5.ttx.GPOS
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g18"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1 Format="2">
+            <ClassDef glyph="g18" class="1"/>
+          </ClassDef1>
+          <ClassDef2 Format="2">
+            <ClassDef glyph="g18" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=2 -->
+          <!-- Class2Count=2 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XPlacement="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XPlacement="0"/>
+            </Class2Record>
+          </Class1Record>
+          <Class1Record index="1">
+            <Class2Record index="0">
+              <Value1 XPlacement="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XPlacement="-100"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos3_font1.otf b/Tests/ttLib/tables/data/aots/gpos3_font1.otf
new file mode 100644
index 0000000..9b6d39a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos3_font1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos3_font1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos3_font1.ttx.GPOS
new file mode 100644
index 0000000..8cbdbc7
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos3_font1.ttx.GPOS
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="3"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <CursivePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g18"/>
+            <Glyph value="g19"/>
+          </Coverage>
+          <!-- EntryExitCount=2 -->
+          <EntryExitRecord index="0">
+            <EntryAnchor Format="1">
+              <XCoordinate value="100"/>
+              <YCoordinate value="150"/>
+            </EntryAnchor>
+            <ExitAnchor Format="1">
+              <XCoordinate value="200"/>
+              <YCoordinate value="250"/>
+            </ExitAnchor>
+          </EntryExitRecord>
+          <EntryExitRecord index="1">
+            <EntryAnchor Format="1">
+              <XCoordinate value="101"/>
+              <YCoordinate value="151"/>
+            </EntryAnchor>
+            <ExitAnchor Format="1">
+              <XCoordinate value="201"/>
+              <YCoordinate value="251"/>
+            </ExitAnchor>
+          </EntryExitRecord>
+        </CursivePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos3_font2.otf b/Tests/ttLib/tables/data/aots/gpos3_font2.otf
new file mode 100644
index 0000000..dee5785
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos3_font2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos3_font2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos3_font2.ttx.GDEF
new file mode 100644
index 0000000..b5ca1ed
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos3_font2.ttx.GDEF
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g21" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos3_font2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos3_font2.ttx.GPOS
new file mode 100644
index 0000000..3ded3a7
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos3_font2.ttx.GPOS
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="3"/>
+        <LookupFlag value="2"/>
+        <!-- SubTableCount=1 -->
+        <CursivePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g18"/>
+            <Glyph value="g19"/>
+          </Coverage>
+          <!-- EntryExitCount=2 -->
+          <EntryExitRecord index="0">
+            <EntryAnchor Format="1">
+              <XCoordinate value="100"/>
+              <YCoordinate value="150"/>
+            </EntryAnchor>
+            <ExitAnchor Format="1">
+              <XCoordinate value="200"/>
+              <YCoordinate value="250"/>
+            </ExitAnchor>
+          </EntryExitRecord>
+          <EntryExitRecord index="1">
+            <EntryAnchor Format="1">
+              <XCoordinate value="101"/>
+              <YCoordinate value="151"/>
+            </EntryAnchor>
+            <ExitAnchor Format="1">
+              <XCoordinate value="201"/>
+              <YCoordinate value="251"/>
+            </ExitAnchor>
+          </EntryExitRecord>
+        </CursivePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos3_font3.otf b/Tests/ttLib/tables/data/aots/gpos3_font3.otf
new file mode 100644
index 0000000..7522660
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos3_font3.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos3_font3.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos3_font3.ttx.GDEF
new file mode 100644
index 0000000..b5ca1ed
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos3_font3.ttx.GDEF
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g21" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos3_font3.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos3_font3.ttx.GPOS
new file mode 100644
index 0000000..e6a1c04
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos3_font3.ttx.GPOS
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="3"/>
+        <LookupFlag value="2"/>
+        <!-- SubTableCount=1 -->
+        <CursivePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g18"/>
+            <Glyph value="g19"/>
+            <Glyph value="g20"/>
+          </Coverage>
+          <!-- EntryExitCount=3 -->
+          <EntryExitRecord index="0">
+            <EntryAnchor Format="1">
+              <XCoordinate value="100"/>
+              <YCoordinate value="150"/>
+            </EntryAnchor>
+            <ExitAnchor Format="1">
+              <XCoordinate value="200"/>
+              <YCoordinate value="250"/>
+            </ExitAnchor>
+          </EntryExitRecord>
+          <EntryExitRecord index="1">
+            <EntryAnchor Format="1">
+              <XCoordinate value="101"/>
+              <YCoordinate value="151"/>
+            </EntryAnchor>
+          </EntryExitRecord>
+          <EntryExitRecord index="2">
+            <ExitAnchor Format="1">
+              <XCoordinate value="202"/>
+              <YCoordinate value="252"/>
+            </ExitAnchor>
+          </EntryExitRecord>
+        </CursivePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f1.otf b/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f1.otf
new file mode 100644
index 0000000..b141116
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f1.ttx.GDEF
new file mode 100644
index 0000000..5118ad8
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f1.ttx.GDEF
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g17" class="1"/>
+      <ClassDef glyph="g18" class="1"/>
+      <ClassDef glyph="g19" class="3"/>
+      <ClassDef glyph="g20" class="3"/>
+    </GlyphClassDef>
+    <MarkAttachClassDef Format="2">
+      <ClassDef glyph="g19" class="1"/>
+      <ClassDef glyph="g20" class="2"/>
+    </MarkAttachClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f1.ttx.GPOS
new file mode 100644
index 0000000..744eaaa
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f1.ttx.GPOS
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="2"/>
+        <!-- SubTableCount=1 -->
+        <MarkBasePos index="0" Format="1">
+          <MarkCoverage Format="1">
+            <Glyph value="g19"/>
+          </MarkCoverage>
+          <BaseCoverage Format="1">
+            <Glyph value="g18"/>
+          </BaseCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="200"/>
+                <YCoordinate value="230"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=1 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="100"/>
+                <YCoordinate value="150"/>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f2.otf b/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f2.otf
new file mode 100644
index 0000000..84e4843
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f2.ttx.GDEF
new file mode 100644
index 0000000..2627f21
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f2.ttx.GDEF
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g17" class="1"/>
+      <ClassDef glyph="g18" class="1"/>
+      <ClassDef glyph="g19" class="3"/>
+      <ClassDef glyph="g20" class="3"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f2.ttx.GPOS
new file mode 100644
index 0000000..7817336
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f2.ttx.GPOS
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <MarkBasePos index="0" Format="1">
+          <MarkCoverage Format="1">
+            <Glyph value="g19"/>
+          </MarkCoverage>
+          <BaseCoverage Format="1">
+            <Glyph value="g18"/>
+          </BaseCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="200"/>
+                <YCoordinate value="230"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=1 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="100"/>
+                <YCoordinate value="150"/>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos4_multiple_anchors_1.otf b/Tests/ttLib/tables/data/aots/gpos4_multiple_anchors_1.otf
new file mode 100644
index 0000000..025f69b
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos4_multiple_anchors_1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos4_multiple_anchors_1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos4_multiple_anchors_1.ttx.GDEF
new file mode 100644
index 0000000..4f9f64a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos4_multiple_anchors_1.ttx.GDEF
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g17" class="1"/>
+      <ClassDef glyph="g18" class="1"/>
+      <ClassDef glyph="g19" class="3"/>
+      <ClassDef glyph="g20" class="3"/>
+      <ClassDef glyph="g21" class="3"/>
+      <ClassDef glyph="g22" class="3"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos4_multiple_anchors_1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos4_multiple_anchors_1.ttx.GPOS
new file mode 100644
index 0000000..afe7e6a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos4_multiple_anchors_1.ttx.GPOS
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkBasePos index="0" Format="1">
+          <MarkCoverage Format="1">
+            <Glyph value="g19"/>
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+          </MarkCoverage>
+          <BaseCoverage Format="1">
+            <Glyph value="g17"/>
+            <Glyph value="g18"/>
+          </BaseCoverage>
+          <!-- ClassCount=2 -->
+          <MarkArray>
+            <!-- MarkCount=4 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="200"/>
+                <YCoordinate value="230"/>
+              </MarkAnchor>
+            </MarkRecord>
+            <MarkRecord index="1">
+              <Class value="1"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="201"/>
+                <YCoordinate value="231"/>
+              </MarkAnchor>
+            </MarkRecord>
+            <MarkRecord index="2">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="202"/>
+                <YCoordinate value="232"/>
+              </MarkAnchor>
+            </MarkRecord>
+            <MarkRecord index="3">
+              <Class value="1"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="203"/>
+                <YCoordinate value="233"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=2 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="100"/>
+                <YCoordinate value="150"/>
+              </BaseAnchor>
+              <BaseAnchor index="1" Format="1">
+                <XCoordinate value="110"/>
+                <YCoordinate value="160"/>
+              </BaseAnchor>
+            </BaseRecord>
+            <BaseRecord index="1">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="120"/>
+                <YCoordinate value="170"/>
+              </BaseAnchor>
+              <BaseAnchor index="1" Format="1">
+                <XCoordinate value="130"/>
+                <YCoordinate value="180"/>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos4_simple_1.otf b/Tests/ttLib/tables/data/aots/gpos4_simple_1.otf
new file mode 100644
index 0000000..da54a1f
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos4_simple_1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos4_simple_1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos4_simple_1.ttx.GDEF
new file mode 100644
index 0000000..2627f21
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos4_simple_1.ttx.GDEF
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g17" class="1"/>
+      <ClassDef glyph="g18" class="1"/>
+      <ClassDef glyph="g19" class="3"/>
+      <ClassDef glyph="g20" class="3"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos4_simple_1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos4_simple_1.ttx.GPOS
new file mode 100644
index 0000000..3a2e623
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos4_simple_1.ttx.GPOS
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkBasePos index="0" Format="1">
+          <MarkCoverage Format="1">
+            <Glyph value="g19"/>
+          </MarkCoverage>
+          <BaseCoverage Format="1">
+            <Glyph value="g18"/>
+          </BaseCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="200"/>
+                <YCoordinate value="230"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=1 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="100"/>
+                <YCoordinate value="150"/>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos5_font1.otf b/Tests/ttLib/tables/data/aots/gpos5_font1.otf
new file mode 100644
index 0000000..8c48fb6
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos5_font1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos5_font1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos5_font1.ttx.GDEF
new file mode 100644
index 0000000..ff2dc98
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos5_font1.ttx.GDEF
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g17" class="1"/>
+      <ClassDef glyph="g18" class="2"/>
+      <ClassDef glyph="g19" class="3"/>
+      <ClassDef glyph="g20" class="3"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos5_font1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos5_font1.ttx.GPOS
new file mode 100644
index 0000000..b5017b3
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos5_font1.ttx.GPOS
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkLigPos index="0" Format="1">
+          <MarkCoverage Format="1">
+            <Glyph value="g19"/>
+          </MarkCoverage>
+          <LigatureCoverage Format="1">
+            <Glyph value="g18"/>
+          </LigatureCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="200"/>
+                <YCoordinate value="230"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <LigatureArray>
+            <!-- LigatureCount=1 -->
+            <LigatureAttach index="0">
+              <!-- ComponentCount=2 -->
+              <ComponentRecord index="0">
+                <LigatureAnchor index="0" Format="1">
+                  <XCoordinate value="100"/>
+                  <YCoordinate value="150"/>
+                </LigatureAnchor>
+              </ComponentRecord>
+              <ComponentRecord index="1">
+                <LigatureAnchor index="0" Format="1">
+                  <XCoordinate value="101"/>
+                  <YCoordinate value="151"/>
+                </LigatureAnchor>
+              </ComponentRecord>
+            </LigatureAttach>
+          </LigatureArray>
+        </MarkLigPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos5_font1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gpos5_font1.ttx.GSUB
new file mode 100644
index 0000000..85d3308
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos5_font1.ttx.GSUB
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g30">
+            <Ligature components="g31" glyph="g18"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos6_font1.otf b/Tests/ttLib/tables/data/aots/gpos6_font1.otf
new file mode 100644
index 0000000..f7f92cc
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos6_font1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos6_font1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos6_font1.ttx.GDEF
new file mode 100644
index 0000000..640f3eb
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos6_font1.ttx.GDEF
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g17" class="1"/>
+      <ClassDef glyph="g18" class="3"/>
+      <ClassDef glyph="g19" class="3"/>
+      <ClassDef glyph="g20" class="3"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos6_font1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos6_font1.ttx.GPOS
new file mode 100644
index 0000000..d4c4da5
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos6_font1.ttx.GPOS
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkMarkPos index="0" Format="1">
+          <Mark1Coverage Format="1">
+            <Glyph value="g19"/>
+          </Mark1Coverage>
+          <Mark2Coverage Format="1">
+            <Glyph value="g18"/>
+          </Mark2Coverage>
+          <!-- ClassCount=1 -->
+          <Mark1Array>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="200"/>
+                <YCoordinate value="230"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </Mark1Array>
+          <Mark2Array>
+            <!-- Mark2Count=1 -->
+            <Mark2Record index="0">
+              <Mark2Anchor index="0" Format="1">
+                <XCoordinate value="100"/>
+                <YCoordinate value="150"/>
+              </Mark2Anchor>
+            </Mark2Record>
+          </Mark2Array>
+        </MarkMarkPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos7_1_font1.otf b/Tests/ttLib/tables/data/aots/gpos7_1_font1.otf
new file mode 100644
index 0000000..ced8907
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos7_1_font1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos7_1_font1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos7_1_font1.ttx.GPOS
new file mode 100644
index 0000000..3d82e68
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos7_1_font1.ttx.GPOS
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g18"/>
+            <Glyph value="g19"/>
+            <Glyph value="g20"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <!-- ValueCount=3 -->
+          <Value index="0" XPlacement="100"/>
+          <Value index="1" XPlacement="200"/>
+          <Value index="2" XPlacement="300"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g18"/>
+          </Coverage>
+          <!-- PosRuleSetCount=1 -->
+          <PosRuleSet index="0">
+            <!-- PosRuleCount=1 -->
+            <PosRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- PosCount=3 -->
+              <Input index="0" value="g19"/>
+              <Input index="1" value="g20"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+              <PosLookupRecord index="1">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+              <PosLookupRecord index="2">
+                <SequenceIndex value="2"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </PosRule>
+          </PosRuleSet>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos9_font1.otf b/Tests/ttLib/tables/data/aots/gpos9_font1.otf
new file mode 100644
index 0000000..e99c25a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos9_font1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos9_font1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos9_font1.ttx.GPOS
new file mode 100644
index 0000000..bbc1c38
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos9_font1.ttx.GPOS
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="9"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ExtensionPos index="0" Format="1">
+          <ExtensionLookupType value="1"/>
+          <SinglePos Format="1">
+            <Coverage Format="1">
+              <Glyph value="g18"/>
+              <Glyph value="g20"/>
+            </Coverage>
+            <ValueFormat value="1"/>
+            <Value XPlacement="-200"/>
+          </SinglePos>
+        </ExtensionPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos9_font2.otf b/Tests/ttLib/tables/data/aots/gpos9_font2.otf
new file mode 100644
index 0000000..9ae824b
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos9_font2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos9_font2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos9_font2.ttx.GPOS
new file mode 100644
index 0000000..ac6d6af
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos9_font2.ttx.GPOS
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="9"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=2 -->
+        <ExtensionPos index="0" Format="1">
+          <ExtensionLookupType value="1"/>
+          <SinglePos Format="1">
+            <Coverage Format="1">
+              <Glyph value="g18"/>
+              <Glyph value="g20"/>
+            </Coverage>
+            <ValueFormat value="1"/>
+            <Value XPlacement="-200"/>
+          </SinglePos>
+        </ExtensionPos>
+        <ExtensionPos index="1" Format="1">
+          <ExtensionLookupType value="1"/>
+          <SinglePos Format="1">
+            <Coverage Format="1">
+              <Glyph value="g19"/>
+              <Glyph value="g21"/>
+            </Coverage>
+            <ValueFormat value="1"/>
+            <Value XPlacement="-300"/>
+          </SinglePos>
+        </ExtensionPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f1.otf b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f1.otf
new file mode 100644
index 0000000..44c4117
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f1.ttx.GPOS
new file mode 100644
index 0000000..ea85d9f
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f1.ttx.GPOS
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <!-- ChainPosRuleSetCount=1 -->
+          <ChainPosRuleSet index="0">
+            <!-- ChainPosRuleCount=1 -->
+            <ChainPosRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="g20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="g22"/>
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="g23"/>
+              <!-- PosCount=0 -->
+            </ChainPosRule>
+          </ChainPosRuleSet>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f2.otf b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f2.otf
new file mode 100644
index 0000000..431b08f
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f2.ttx.GPOS
new file mode 100644
index 0000000..bd176ba
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f2.ttx.GPOS
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <!-- ChainPosRuleSetCount=1 -->
+          <ChainPosRuleSet index="0">
+            <!-- ChainPosRuleCount=1 -->
+            <ChainPosRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="g20"/>
+              <!-- InputGlyphCount=1 -->
+              <!-- LookAheadGlyphCount=2 -->
+              <LookAhead index="0" value="g22"/>
+              <LookAhead index="1" value="g23"/>
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </ChainPosRule>
+          </ChainPosRuleSet>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f3.otf b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f3.otf
new file mode 100644
index 0000000..1bac49a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f3.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f3.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f3.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f3.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f3.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f3.ttx.GPOS
new file mode 100644
index 0000000..b189067
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f3.ttx.GPOS
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <!-- ChainPosRuleSetCount=1 -->
+          <ChainPosRuleSet index="0">
+            <!-- ChainPosRuleCount=1 -->
+            <ChainPosRule index="0">
+              <!-- BacktrackGlyphCount=0 -->
+              <!-- InputGlyphCount=1 -->
+              <!-- LookAheadGlyphCount=2 -->
+              <LookAhead index="0" value="g22"/>
+              <LookAhead index="1" value="g23"/>
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </ChainPosRule>
+          </ChainPosRuleSet>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f4.otf b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f4.otf
new file mode 100644
index 0000000..3d37782
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f4.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f4.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f4.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f4.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f4.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f4.ttx.GPOS
new file mode 100644
index 0000000..f750652
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f4.ttx.GPOS
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g22"/>
+          </Coverage>
+          <!-- ChainPosRuleSetCount=1 -->
+          <ChainPosRuleSet index="0">
+            <!-- ChainPosRuleCount=1 -->
+            <ChainPosRule index="0">
+              <!-- BacktrackGlyphCount=2 -->
+              <Backtrack index="0" value="g21"/>
+              <Backtrack index="1" value="g20"/>
+              <!-- InputGlyphCount=1 -->
+              <!-- LookAheadGlyphCount=0 -->
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </ChainPosRule>
+          </ChainPosRuleSet>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_lookupflag_f1.otf b/Tests/ttLib/tables/data/aots/gpos_chaining1_lookupflag_f1.otf
new file mode 100644
index 0000000..a83342a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_lookupflag_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_lookupflag_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining1_lookupflag_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_lookupflag_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_lookupflag_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining1_lookupflag_f1.ttx.GPOS
new file mode 100644
index 0000000..b07a9ba
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_lookupflag_f1.ttx.GPOS
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g22"/>
+          </Coverage>
+          <!-- ChainPosRuleSetCount=1 -->
+          <ChainPosRuleSet index="0">
+            <!-- ChainPosRuleCount=1 -->
+            <ChainPosRule index="0">
+              <!-- BacktrackGlyphCount=2 -->
+              <Backtrack index="0" value="g21"/>
+              <Backtrack index="1" value="g20"/>
+              <!-- InputGlyphCount=3 -->
+              <Input index="0" value="g23"/>
+              <Input index="1" value="g24"/>
+              <!-- LookAheadGlyphCount=2 -->
+              <LookAhead index="0" value="g25"/>
+              <LookAhead index="1" value="g26"/>
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </ChainPosRule>
+          </ChainPosRuleSet>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_multiple_subrules_f1.otf b/Tests/ttLib/tables/data/aots/gpos_chaining1_multiple_subrules_f1.otf
new file mode 100644
index 0000000..07bf55c
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_multiple_subrules_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_multiple_subrules_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining1_multiple_subrules_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_multiple_subrules_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_multiple_subrules_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining1_multiple_subrules_f1.ttx.GPOS
new file mode 100644
index 0000000..09b5bd8
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_multiple_subrules_f1.ttx.GPOS
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <!-- ChainPosRuleSetCount=1 -->
+          <ChainPosRuleSet index="0">
+            <!-- ChainPosRuleCount=2 -->
+            <ChainPosRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="g20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="g22"/>
+              <!-- LookAheadGlyphCount=2 -->
+              <LookAhead index="0" value="g23"/>
+              <LookAhead index="1" value="g24"/>
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </ChainPosRule>
+            <ChainPosRule index="1">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="g20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="g22"/>
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="g23"/>
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </ChainPosRule>
+          </ChainPosRuleSet>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_multiple_subrules_f2.otf b/Tests/ttLib/tables/data/aots/gpos_chaining1_multiple_subrules_f2.otf
new file mode 100644
index 0000000..dc3754b
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_multiple_subrules_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_multiple_subrules_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining1_multiple_subrules_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_multiple_subrules_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_multiple_subrules_f2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining1_multiple_subrules_f2.ttx.GPOS
new file mode 100644
index 0000000..e8fb516
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_multiple_subrules_f2.ttx.GPOS
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <!-- ChainPosRuleSetCount=1 -->
+          <ChainPosRuleSet index="0">
+            <!-- ChainPosRuleCount=2 -->
+            <ChainPosRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="g20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="g22"/>
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="g23"/>
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </ChainPosRule>
+            <ChainPosRule index="1">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="g20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="g22"/>
+              <!-- LookAheadGlyphCount=2 -->
+              <LookAhead index="0" value="g23"/>
+              <LookAhead index="1" value="g24"/>
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </ChainPosRule>
+          </ChainPosRuleSet>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_next_glyph_f1.otf b/Tests/ttLib/tables/data/aots/gpos_chaining1_next_glyph_f1.otf
new file mode 100644
index 0000000..17852c2
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_next_glyph_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_next_glyph_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining1_next_glyph_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_next_glyph_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_next_glyph_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining1_next_glyph_f1.ttx.GPOS
new file mode 100644
index 0000000..1f7539e
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_next_glyph_f1.ttx.GPOS
@@ -0,0 +1,146 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+            <Glyph value="g23"/>
+          </Coverage>
+          <!-- ChainPosRuleSetCount=2 -->
+          <ChainPosRuleSet index="0">
+            <!-- ChainPosRuleCount=1 -->
+            <ChainPosRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="g20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="g22"/>
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="g23"/>
+              <!-- PosCount=2 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+              <PosLookupRecord index="1">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </ChainPosRule>
+          </ChainPosRuleSet>
+          <ChainPosRuleSet index="1">
+            <!-- ChainPosRuleCount=1 -->
+            <ChainPosRule index="0">
+              <!-- BacktrackGlyphCount=0 -->
+              <!-- InputGlyphCount=1 -->
+              <!-- LookAheadGlyphCount=0 -->
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </ChainPosRule>
+          </ChainPosRuleSet>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f1.otf b/Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f1.otf
new file mode 100644
index 0000000..31cbe77
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f1.ttx.GPOS
new file mode 100644
index 0000000..1780fda
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f1.ttx.GPOS
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <!-- ChainPosRuleSetCount=1 -->
+          <ChainPosRuleSet index="0">
+            <!-- ChainPosRuleCount=1 -->
+            <ChainPosRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="g20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="g22"/>
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="g23"/>
+              <!-- PosCount=2 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+              <PosLookupRecord index="1">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </ChainPosRule>
+          </ChainPosRuleSet>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f2.otf b/Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f2.otf
new file mode 100644
index 0000000..3293ad8
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f2.ttx.GPOS
new file mode 100644
index 0000000..c2d3411
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f2.ttx.GPOS
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g22"/>
+          </Coverage>
+          <!-- ChainPosRuleSetCount=1 -->
+          <ChainPosRuleSet index="0">
+            <!-- ChainPosRuleCount=1 -->
+            <ChainPosRule index="0">
+              <!-- BacktrackGlyphCount=2 -->
+              <Backtrack index="0" value="g21"/>
+              <Backtrack index="1" value="g20"/>
+              <!-- InputGlyphCount=3 -->
+              <Input index="0" value="g23"/>
+              <Input index="1" value="g24"/>
+              <!-- LookAheadGlyphCount=2 -->
+              <LookAhead index="0" value="g25"/>
+              <LookAhead index="1" value="g26"/>
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </ChainPosRule>
+          </ChainPosRuleSet>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_successive_f1.otf b/Tests/ttLib/tables/data/aots/gpos_chaining1_successive_f1.otf
new file mode 100644
index 0000000..4c86663
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_successive_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_successive_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining1_successive_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_successive_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining1_successive_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining1_successive_f1.ttx.GPOS
new file mode 100644
index 0000000..3960d8a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_successive_f1.ttx.GPOS
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <!-- ChainPosRuleSetCount=1 -->
+          <ChainPosRuleSet index="0">
+            <!-- ChainPosRuleCount=1 -->
+            <ChainPosRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="g25"/>
+              <!-- InputGlyphCount=4 -->
+              <Input index="0" value="g21"/>
+              <Input index="1" value="g22"/>
+              <Input index="2" value="g23"/>
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="g24"/>
+              <!-- PosCount=2 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="1"/>
+              </PosLookupRecord>
+              <PosLookupRecord index="1">
+                <SequenceIndex value="2"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </ChainPosRule>
+          </ChainPosRuleSet>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f1.otf b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f1.otf
new file mode 100644
index 0000000..49210fb
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f1.ttx.GPOS
new file mode 100644
index 0000000..fe06077
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f1.ttx.GPOS
@@ -0,0 +1,183 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+          </Coverage>
+          <BacktrackClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </BacktrackClassDef>
+          <InputClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </InputClassDef>
+          <LookAheadClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </LookAheadClassDef>
+          <!-- ChainPosClassSetCount=27 -->
+          <ChainPosClassSet index="0" empty="1"/>
+          <ChainPosClassSet index="1" empty="1"/>
+          <ChainPosClassSet index="2" empty="1"/>
+          <ChainPosClassSet index="3" empty="1"/>
+          <ChainPosClassSet index="4" empty="1"/>
+          <ChainPosClassSet index="5" empty="1"/>
+          <ChainPosClassSet index="6" empty="1"/>
+          <ChainPosClassSet index="7" empty="1"/>
+          <ChainPosClassSet index="8" empty="1"/>
+          <ChainPosClassSet index="9" empty="1"/>
+          <ChainPosClassSet index="10" empty="1"/>
+          <ChainPosClassSet index="11" empty="1"/>
+          <ChainPosClassSet index="12" empty="1"/>
+          <ChainPosClassSet index="13" empty="1"/>
+          <ChainPosClassSet index="14" empty="1"/>
+          <ChainPosClassSet index="15" empty="1"/>
+          <ChainPosClassSet index="16" empty="1"/>
+          <ChainPosClassSet index="17" empty="1"/>
+          <ChainPosClassSet index="18" empty="1"/>
+          <ChainPosClassSet index="19" empty="1"/>
+          <ChainPosClassSet index="20" empty="1"/>
+          <ChainPosClassSet index="21">
+            <!-- ChainPosClassRuleCount=1 -->
+            <ChainPosClassRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="22"/>
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="23"/>
+              <!-- PosCount=0 -->
+            </ChainPosClassRule>
+          </ChainPosClassSet>
+          <ChainPosClassSet index="22" empty="1"/>
+          <ChainPosClassSet index="23" empty="1"/>
+          <ChainPosClassSet index="24" empty="1"/>
+          <ChainPosClassSet index="25" empty="1"/>
+          <ChainPosClassSet index="26" empty="1"/>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f2.otf b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f2.otf
new file mode 100644
index 0000000..456fc9b
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f2.ttx.GPOS
new file mode 100644
index 0000000..c529371
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f2.ttx.GPOS
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+          </Coverage>
+          <BacktrackClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </BacktrackClassDef>
+          <InputClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </InputClassDef>
+          <LookAheadClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </LookAheadClassDef>
+          <!-- ChainPosClassSetCount=27 -->
+          <ChainPosClassSet index="0" empty="1"/>
+          <ChainPosClassSet index="1" empty="1"/>
+          <ChainPosClassSet index="2" empty="1"/>
+          <ChainPosClassSet index="3" empty="1"/>
+          <ChainPosClassSet index="4" empty="1"/>
+          <ChainPosClassSet index="5" empty="1"/>
+          <ChainPosClassSet index="6" empty="1"/>
+          <ChainPosClassSet index="7" empty="1"/>
+          <ChainPosClassSet index="8" empty="1"/>
+          <ChainPosClassSet index="9" empty="1"/>
+          <ChainPosClassSet index="10" empty="1"/>
+          <ChainPosClassSet index="11" empty="1"/>
+          <ChainPosClassSet index="12" empty="1"/>
+          <ChainPosClassSet index="13" empty="1"/>
+          <ChainPosClassSet index="14" empty="1"/>
+          <ChainPosClassSet index="15" empty="1"/>
+          <ChainPosClassSet index="16" empty="1"/>
+          <ChainPosClassSet index="17" empty="1"/>
+          <ChainPosClassSet index="18" empty="1"/>
+          <ChainPosClassSet index="19" empty="1"/>
+          <ChainPosClassSet index="20" empty="1"/>
+          <ChainPosClassSet index="21">
+            <!-- ChainPosClassRuleCount=1 -->
+            <ChainPosClassRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="20"/>
+              <!-- InputGlyphCount=1 -->
+              <!-- LookAheadGlyphCount=2 -->
+              <LookAhead index="0" value="22"/>
+              <LookAhead index="1" value="23"/>
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </ChainPosClassRule>
+          </ChainPosClassSet>
+          <ChainPosClassSet index="22" empty="1"/>
+          <ChainPosClassSet index="23" empty="1"/>
+          <ChainPosClassSet index="24" empty="1"/>
+          <ChainPosClassSet index="25" empty="1"/>
+          <ChainPosClassSet index="26" empty="1"/>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f3.otf b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f3.otf
new file mode 100644
index 0000000..768492a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f3.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f3.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f3.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f3.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f3.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f3.ttx.GPOS
new file mode 100644
index 0000000..fa55cf1
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f3.ttx.GPOS
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+          </Coverage>
+          <BacktrackClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </BacktrackClassDef>
+          <InputClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </InputClassDef>
+          <LookAheadClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </LookAheadClassDef>
+          <!-- ChainPosClassSetCount=27 -->
+          <ChainPosClassSet index="0" empty="1"/>
+          <ChainPosClassSet index="1" empty="1"/>
+          <ChainPosClassSet index="2" empty="1"/>
+          <ChainPosClassSet index="3" empty="1"/>
+          <ChainPosClassSet index="4" empty="1"/>
+          <ChainPosClassSet index="5" empty="1"/>
+          <ChainPosClassSet index="6" empty="1"/>
+          <ChainPosClassSet index="7" empty="1"/>
+          <ChainPosClassSet index="8" empty="1"/>
+          <ChainPosClassSet index="9" empty="1"/>
+          <ChainPosClassSet index="10" empty="1"/>
+          <ChainPosClassSet index="11" empty="1"/>
+          <ChainPosClassSet index="12" empty="1"/>
+          <ChainPosClassSet index="13" empty="1"/>
+          <ChainPosClassSet index="14" empty="1"/>
+          <ChainPosClassSet index="15" empty="1"/>
+          <ChainPosClassSet index="16" empty="1"/>
+          <ChainPosClassSet index="17" empty="1"/>
+          <ChainPosClassSet index="18" empty="1"/>
+          <ChainPosClassSet index="19" empty="1"/>
+          <ChainPosClassSet index="20" empty="1"/>
+          <ChainPosClassSet index="21">
+            <!-- ChainPosClassRuleCount=1 -->
+            <ChainPosClassRule index="0">
+              <!-- BacktrackGlyphCount=0 -->
+              <!-- InputGlyphCount=1 -->
+              <!-- LookAheadGlyphCount=2 -->
+              <LookAhead index="0" value="22"/>
+              <LookAhead index="1" value="23"/>
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </ChainPosClassRule>
+          </ChainPosClassSet>
+          <ChainPosClassSet index="22" empty="1"/>
+          <ChainPosClassSet index="23" empty="1"/>
+          <ChainPosClassSet index="24" empty="1"/>
+          <ChainPosClassSet index="25" empty="1"/>
+          <ChainPosClassSet index="26" empty="1"/>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f4.otf b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f4.otf
new file mode 100644
index 0000000..2670da6
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f4.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f4.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f4.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f4.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f4.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f4.ttx.GPOS
new file mode 100644
index 0000000..f943402
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f4.ttx.GPOS
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+          </Coverage>
+          <BacktrackClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </BacktrackClassDef>
+          <InputClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </InputClassDef>
+          <LookAheadClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </LookAheadClassDef>
+          <!-- ChainPosClassSetCount=27 -->
+          <ChainPosClassSet index="0" empty="1"/>
+          <ChainPosClassSet index="1" empty="1"/>
+          <ChainPosClassSet index="2" empty="1"/>
+          <ChainPosClassSet index="3" empty="1"/>
+          <ChainPosClassSet index="4" empty="1"/>
+          <ChainPosClassSet index="5" empty="1"/>
+          <ChainPosClassSet index="6" empty="1"/>
+          <ChainPosClassSet index="7" empty="1"/>
+          <ChainPosClassSet index="8" empty="1"/>
+          <ChainPosClassSet index="9" empty="1"/>
+          <ChainPosClassSet index="10" empty="1"/>
+          <ChainPosClassSet index="11" empty="1"/>
+          <ChainPosClassSet index="12" empty="1"/>
+          <ChainPosClassSet index="13" empty="1"/>
+          <ChainPosClassSet index="14" empty="1"/>
+          <ChainPosClassSet index="15" empty="1"/>
+          <ChainPosClassSet index="16" empty="1"/>
+          <ChainPosClassSet index="17" empty="1"/>
+          <ChainPosClassSet index="18" empty="1"/>
+          <ChainPosClassSet index="19" empty="1"/>
+          <ChainPosClassSet index="20" empty="1"/>
+          <ChainPosClassSet index="21" empty="1"/>
+          <ChainPosClassSet index="22">
+            <!-- ChainPosClassRuleCount=1 -->
+            <ChainPosClassRule index="0">
+              <!-- BacktrackGlyphCount=2 -->
+              <Backtrack index="0" value="21"/>
+              <Backtrack index="1" value="20"/>
+              <!-- InputGlyphCount=1 -->
+              <!-- LookAheadGlyphCount=0 -->
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </ChainPosClassRule>
+          </ChainPosClassSet>
+          <ChainPosClassSet index="23" empty="1"/>
+          <ChainPosClassSet index="24" empty="1"/>
+          <ChainPosClassSet index="25" empty="1"/>
+          <ChainPosClassSet index="26" empty="1"/>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_lookupflag_f1.otf b/Tests/ttLib/tables/data/aots/gpos_chaining2_lookupflag_f1.otf
new file mode 100644
index 0000000..e8cce56
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_lookupflag_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_lookupflag_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining2_lookupflag_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_lookupflag_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_lookupflag_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining2_lookupflag_f1.ttx.GPOS
new file mode 100644
index 0000000..b26d93b
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_lookupflag_f1.ttx.GPOS
@@ -0,0 +1,190 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+          </Coverage>
+          <BacktrackClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </BacktrackClassDef>
+          <InputClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </InputClassDef>
+          <LookAheadClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </LookAheadClassDef>
+          <!-- ChainPosClassSetCount=27 -->
+          <ChainPosClassSet index="0" empty="1"/>
+          <ChainPosClassSet index="1" empty="1"/>
+          <ChainPosClassSet index="2" empty="1"/>
+          <ChainPosClassSet index="3" empty="1"/>
+          <ChainPosClassSet index="4" empty="1"/>
+          <ChainPosClassSet index="5" empty="1"/>
+          <ChainPosClassSet index="6" empty="1"/>
+          <ChainPosClassSet index="7" empty="1"/>
+          <ChainPosClassSet index="8" empty="1"/>
+          <ChainPosClassSet index="9" empty="1"/>
+          <ChainPosClassSet index="10" empty="1"/>
+          <ChainPosClassSet index="11" empty="1"/>
+          <ChainPosClassSet index="12" empty="1"/>
+          <ChainPosClassSet index="13" empty="1"/>
+          <ChainPosClassSet index="14" empty="1"/>
+          <ChainPosClassSet index="15" empty="1"/>
+          <ChainPosClassSet index="16" empty="1"/>
+          <ChainPosClassSet index="17" empty="1"/>
+          <ChainPosClassSet index="18" empty="1"/>
+          <ChainPosClassSet index="19" empty="1"/>
+          <ChainPosClassSet index="20" empty="1"/>
+          <ChainPosClassSet index="21" empty="1"/>
+          <ChainPosClassSet index="22">
+            <!-- ChainPosClassRuleCount=1 -->
+            <ChainPosClassRule index="0">
+              <!-- BacktrackGlyphCount=2 -->
+              <Backtrack index="0" value="21"/>
+              <Backtrack index="1" value="20"/>
+              <!-- InputGlyphCount=3 -->
+              <Input index="0" value="23"/>
+              <Input index="1" value="24"/>
+              <!-- LookAheadGlyphCount=2 -->
+              <LookAhead index="0" value="25"/>
+              <LookAhead index="1" value="26"/>
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </ChainPosClassRule>
+          </ChainPosClassSet>
+          <ChainPosClassSet index="23" empty="1"/>
+          <ChainPosClassSet index="24" empty="1"/>
+          <ChainPosClassSet index="25" empty="1"/>
+          <ChainPosClassSet index="26" empty="1"/>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_multiple_subrules_f1.otf b/Tests/ttLib/tables/data/aots/gpos_chaining2_multiple_subrules_f1.otf
new file mode 100644
index 0000000..f182c7f
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_multiple_subrules_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_multiple_subrules_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining2_multiple_subrules_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_multiple_subrules_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_multiple_subrules_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining2_multiple_subrules_f1.ttx.GPOS
new file mode 100644
index 0000000..33399ba
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_multiple_subrules_f1.ttx.GPOS
@@ -0,0 +1,201 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+          </Coverage>
+          <BacktrackClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </BacktrackClassDef>
+          <InputClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </InputClassDef>
+          <LookAheadClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </LookAheadClassDef>
+          <!-- ChainPosClassSetCount=27 -->
+          <ChainPosClassSet index="0" empty="1"/>
+          <ChainPosClassSet index="1" empty="1"/>
+          <ChainPosClassSet index="2" empty="1"/>
+          <ChainPosClassSet index="3" empty="1"/>
+          <ChainPosClassSet index="4" empty="1"/>
+          <ChainPosClassSet index="5" empty="1"/>
+          <ChainPosClassSet index="6" empty="1"/>
+          <ChainPosClassSet index="7" empty="1"/>
+          <ChainPosClassSet index="8" empty="1"/>
+          <ChainPosClassSet index="9" empty="1"/>
+          <ChainPosClassSet index="10" empty="1"/>
+          <ChainPosClassSet index="11" empty="1"/>
+          <ChainPosClassSet index="12" empty="1"/>
+          <ChainPosClassSet index="13" empty="1"/>
+          <ChainPosClassSet index="14" empty="1"/>
+          <ChainPosClassSet index="15" empty="1"/>
+          <ChainPosClassSet index="16" empty="1"/>
+          <ChainPosClassSet index="17" empty="1"/>
+          <ChainPosClassSet index="18" empty="1"/>
+          <ChainPosClassSet index="19" empty="1"/>
+          <ChainPosClassSet index="20" empty="1"/>
+          <ChainPosClassSet index="21">
+            <!-- ChainPosClassRuleCount=2 -->
+            <ChainPosClassRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="22"/>
+              <!-- LookAheadGlyphCount=2 -->
+              <LookAhead index="0" value="23"/>
+              <LookAhead index="1" value="24"/>
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </ChainPosClassRule>
+            <ChainPosClassRule index="1">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="22"/>
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="23"/>
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </ChainPosClassRule>
+          </ChainPosClassSet>
+          <ChainPosClassSet index="22" empty="1"/>
+          <ChainPosClassSet index="23" empty="1"/>
+          <ChainPosClassSet index="24" empty="1"/>
+          <ChainPosClassSet index="25" empty="1"/>
+          <ChainPosClassSet index="26" empty="1"/>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_multiple_subrules_f2.otf b/Tests/ttLib/tables/data/aots/gpos_chaining2_multiple_subrules_f2.otf
new file mode 100644
index 0000000..d24896a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_multiple_subrules_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_multiple_subrules_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining2_multiple_subrules_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_multiple_subrules_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_multiple_subrules_f2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining2_multiple_subrules_f2.ttx.GPOS
new file mode 100644
index 0000000..dc10c2f
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_multiple_subrules_f2.ttx.GPOS
@@ -0,0 +1,201 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+          </Coverage>
+          <BacktrackClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </BacktrackClassDef>
+          <InputClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </InputClassDef>
+          <LookAheadClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </LookAheadClassDef>
+          <!-- ChainPosClassSetCount=27 -->
+          <ChainPosClassSet index="0" empty="1"/>
+          <ChainPosClassSet index="1" empty="1"/>
+          <ChainPosClassSet index="2" empty="1"/>
+          <ChainPosClassSet index="3" empty="1"/>
+          <ChainPosClassSet index="4" empty="1"/>
+          <ChainPosClassSet index="5" empty="1"/>
+          <ChainPosClassSet index="6" empty="1"/>
+          <ChainPosClassSet index="7" empty="1"/>
+          <ChainPosClassSet index="8" empty="1"/>
+          <ChainPosClassSet index="9" empty="1"/>
+          <ChainPosClassSet index="10" empty="1"/>
+          <ChainPosClassSet index="11" empty="1"/>
+          <ChainPosClassSet index="12" empty="1"/>
+          <ChainPosClassSet index="13" empty="1"/>
+          <ChainPosClassSet index="14" empty="1"/>
+          <ChainPosClassSet index="15" empty="1"/>
+          <ChainPosClassSet index="16" empty="1"/>
+          <ChainPosClassSet index="17" empty="1"/>
+          <ChainPosClassSet index="18" empty="1"/>
+          <ChainPosClassSet index="19" empty="1"/>
+          <ChainPosClassSet index="20" empty="1"/>
+          <ChainPosClassSet index="21">
+            <!-- ChainPosClassRuleCount=2 -->
+            <ChainPosClassRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="22"/>
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="23"/>
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </ChainPosClassRule>
+            <ChainPosClassRule index="1">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="22"/>
+              <!-- LookAheadGlyphCount=2 -->
+              <LookAhead index="0" value="23"/>
+              <LookAhead index="1" value="24"/>
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </ChainPosClassRule>
+          </ChainPosClassSet>
+          <ChainPosClassSet index="22" empty="1"/>
+          <ChainPosClassSet index="23" empty="1"/>
+          <ChainPosClassSet index="24" empty="1"/>
+          <ChainPosClassSet index="25" empty="1"/>
+          <ChainPosClassSet index="26" empty="1"/>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_next_glyph_f1.otf b/Tests/ttLib/tables/data/aots/gpos_chaining2_next_glyph_f1.otf
new file mode 100644
index 0000000..f6bbda4
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_next_glyph_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_next_glyph_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining2_next_glyph_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_next_glyph_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_next_glyph_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining2_next_glyph_f1.ttx.GPOS
new file mode 100644
index 0000000..9c18a9b
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_next_glyph_f1.ttx.GPOS
@@ -0,0 +1,203 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+          </Coverage>
+          <BacktrackClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </BacktrackClassDef>
+          <InputClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </InputClassDef>
+          <LookAheadClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </LookAheadClassDef>
+          <!-- ChainPosClassSetCount=27 -->
+          <ChainPosClassSet index="0" empty="1"/>
+          <ChainPosClassSet index="1" empty="1"/>
+          <ChainPosClassSet index="2" empty="1"/>
+          <ChainPosClassSet index="3" empty="1"/>
+          <ChainPosClassSet index="4" empty="1"/>
+          <ChainPosClassSet index="5" empty="1"/>
+          <ChainPosClassSet index="6" empty="1"/>
+          <ChainPosClassSet index="7" empty="1"/>
+          <ChainPosClassSet index="8" empty="1"/>
+          <ChainPosClassSet index="9" empty="1"/>
+          <ChainPosClassSet index="10" empty="1"/>
+          <ChainPosClassSet index="11" empty="1"/>
+          <ChainPosClassSet index="12" empty="1"/>
+          <ChainPosClassSet index="13" empty="1"/>
+          <ChainPosClassSet index="14" empty="1"/>
+          <ChainPosClassSet index="15" empty="1"/>
+          <ChainPosClassSet index="16" empty="1"/>
+          <ChainPosClassSet index="17" empty="1"/>
+          <ChainPosClassSet index="18" empty="1"/>
+          <ChainPosClassSet index="19" empty="1"/>
+          <ChainPosClassSet index="20" empty="1"/>
+          <ChainPosClassSet index="21">
+            <!-- ChainPosClassRuleCount=1 -->
+            <ChainPosClassRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="22"/>
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="23"/>
+              <!-- PosCount=2 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+              <PosLookupRecord index="1">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </ChainPosClassRule>
+          </ChainPosClassSet>
+          <ChainPosClassSet index="22" empty="1"/>
+          <ChainPosClassSet index="23">
+            <!-- ChainPosClassRuleCount=1 -->
+            <ChainPosClassRule index="0">
+              <!-- BacktrackGlyphCount=0 -->
+              <!-- InputGlyphCount=1 -->
+              <!-- LookAheadGlyphCount=0 -->
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </ChainPosClassRule>
+          </ChainPosClassSet>
+          <ChainPosClassSet index="24" empty="1"/>
+          <ChainPosClassSet index="25" empty="1"/>
+          <ChainPosClassSet index="26" empty="1"/>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f1.otf b/Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f1.otf
new file mode 100644
index 0000000..1805a03
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f1.ttx.GPOS
new file mode 100644
index 0000000..decc575
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f1.ttx.GPOS
@@ -0,0 +1,191 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+          </Coverage>
+          <BacktrackClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </BacktrackClassDef>
+          <InputClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </InputClassDef>
+          <LookAheadClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </LookAheadClassDef>
+          <!-- ChainPosClassSetCount=27 -->
+          <ChainPosClassSet index="0" empty="1"/>
+          <ChainPosClassSet index="1" empty="1"/>
+          <ChainPosClassSet index="2" empty="1"/>
+          <ChainPosClassSet index="3" empty="1"/>
+          <ChainPosClassSet index="4" empty="1"/>
+          <ChainPosClassSet index="5" empty="1"/>
+          <ChainPosClassSet index="6" empty="1"/>
+          <ChainPosClassSet index="7" empty="1"/>
+          <ChainPosClassSet index="8" empty="1"/>
+          <ChainPosClassSet index="9" empty="1"/>
+          <ChainPosClassSet index="10" empty="1"/>
+          <ChainPosClassSet index="11" empty="1"/>
+          <ChainPosClassSet index="12" empty="1"/>
+          <ChainPosClassSet index="13" empty="1"/>
+          <ChainPosClassSet index="14" empty="1"/>
+          <ChainPosClassSet index="15" empty="1"/>
+          <ChainPosClassSet index="16" empty="1"/>
+          <ChainPosClassSet index="17" empty="1"/>
+          <ChainPosClassSet index="18" empty="1"/>
+          <ChainPosClassSet index="19" empty="1"/>
+          <ChainPosClassSet index="20" empty="1"/>
+          <ChainPosClassSet index="21">
+            <!-- ChainPosClassRuleCount=1 -->
+            <ChainPosClassRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="22"/>
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="23"/>
+              <!-- PosCount=2 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+              <PosLookupRecord index="1">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </ChainPosClassRule>
+          </ChainPosClassSet>
+          <ChainPosClassSet index="22" empty="1"/>
+          <ChainPosClassSet index="23" empty="1"/>
+          <ChainPosClassSet index="24" empty="1"/>
+          <ChainPosClassSet index="25" empty="1"/>
+          <ChainPosClassSet index="26" empty="1"/>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f2.otf b/Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f2.otf
new file mode 100644
index 0000000..1df12f5
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f2.ttx.GPOS
new file mode 100644
index 0000000..a35678d
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f2.ttx.GPOS
@@ -0,0 +1,190 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+          </Coverage>
+          <BacktrackClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </BacktrackClassDef>
+          <InputClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </InputClassDef>
+          <LookAheadClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </LookAheadClassDef>
+          <!-- ChainPosClassSetCount=27 -->
+          <ChainPosClassSet index="0" empty="1"/>
+          <ChainPosClassSet index="1" empty="1"/>
+          <ChainPosClassSet index="2" empty="1"/>
+          <ChainPosClassSet index="3" empty="1"/>
+          <ChainPosClassSet index="4" empty="1"/>
+          <ChainPosClassSet index="5" empty="1"/>
+          <ChainPosClassSet index="6" empty="1"/>
+          <ChainPosClassSet index="7" empty="1"/>
+          <ChainPosClassSet index="8" empty="1"/>
+          <ChainPosClassSet index="9" empty="1"/>
+          <ChainPosClassSet index="10" empty="1"/>
+          <ChainPosClassSet index="11" empty="1"/>
+          <ChainPosClassSet index="12" empty="1"/>
+          <ChainPosClassSet index="13" empty="1"/>
+          <ChainPosClassSet index="14" empty="1"/>
+          <ChainPosClassSet index="15" empty="1"/>
+          <ChainPosClassSet index="16" empty="1"/>
+          <ChainPosClassSet index="17" empty="1"/>
+          <ChainPosClassSet index="18" empty="1"/>
+          <ChainPosClassSet index="19" empty="1"/>
+          <ChainPosClassSet index="20" empty="1"/>
+          <ChainPosClassSet index="21" empty="1"/>
+          <ChainPosClassSet index="22">
+            <!-- ChainPosClassRuleCount=1 -->
+            <ChainPosClassRule index="0">
+              <!-- BacktrackGlyphCount=2 -->
+              <Backtrack index="0" value="21"/>
+              <Backtrack index="1" value="20"/>
+              <!-- InputGlyphCount=3 -->
+              <Input index="0" value="23"/>
+              <Input index="1" value="24"/>
+              <!-- LookAheadGlyphCount=2 -->
+              <LookAhead index="0" value="25"/>
+              <LookAhead index="1" value="26"/>
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </ChainPosClassRule>
+          </ChainPosClassSet>
+          <ChainPosClassSet index="23" empty="1"/>
+          <ChainPosClassSet index="24" empty="1"/>
+          <ChainPosClassSet index="25" empty="1"/>
+          <ChainPosClassSet index="26" empty="1"/>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_successive_f1.otf b/Tests/ttLib/tables/data/aots/gpos_chaining2_successive_f1.otf
new file mode 100644
index 0000000..a3aadaf
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_successive_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_successive_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining2_successive_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_successive_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining2_successive_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining2_successive_f1.ttx.GPOS
new file mode 100644
index 0000000..6775e5b
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_successive_f1.ttx.GPOS
@@ -0,0 +1,193 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+          </Coverage>
+          <BacktrackClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </BacktrackClassDef>
+          <InputClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </InputClassDef>
+          <LookAheadClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </LookAheadClassDef>
+          <!-- ChainPosClassSetCount=27 -->
+          <ChainPosClassSet index="0" empty="1"/>
+          <ChainPosClassSet index="1" empty="1"/>
+          <ChainPosClassSet index="2" empty="1"/>
+          <ChainPosClassSet index="3" empty="1"/>
+          <ChainPosClassSet index="4" empty="1"/>
+          <ChainPosClassSet index="5" empty="1"/>
+          <ChainPosClassSet index="6" empty="1"/>
+          <ChainPosClassSet index="7" empty="1"/>
+          <ChainPosClassSet index="8" empty="1"/>
+          <ChainPosClassSet index="9" empty="1"/>
+          <ChainPosClassSet index="10" empty="1"/>
+          <ChainPosClassSet index="11" empty="1"/>
+          <ChainPosClassSet index="12" empty="1"/>
+          <ChainPosClassSet index="13" empty="1"/>
+          <ChainPosClassSet index="14" empty="1"/>
+          <ChainPosClassSet index="15" empty="1"/>
+          <ChainPosClassSet index="16" empty="1"/>
+          <ChainPosClassSet index="17" empty="1"/>
+          <ChainPosClassSet index="18" empty="1"/>
+          <ChainPosClassSet index="19" empty="1"/>
+          <ChainPosClassSet index="20">
+            <!-- ChainPosClassRuleCount=1 -->
+            <ChainPosClassRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="25"/>
+              <!-- InputGlyphCount=4 -->
+              <Input index="0" value="21"/>
+              <Input index="1" value="22"/>
+              <Input index="2" value="23"/>
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="24"/>
+              <!-- PosCount=2 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="1"/>
+              </PosLookupRecord>
+              <PosLookupRecord index="1">
+                <SequenceIndex value="2"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </ChainPosClassRule>
+          </ChainPosClassSet>
+          <ChainPosClassSet index="21" empty="1"/>
+          <ChainPosClassSet index="22" empty="1"/>
+          <ChainPosClassSet index="23" empty="1"/>
+          <ChainPosClassSet index="24" empty="1"/>
+          <ChainPosClassSet index="25" empty="1"/>
+          <ChainPosClassSet index="26" empty="1"/>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f1.otf b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f1.otf
new file mode 100644
index 0000000..4f13bdd
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f1.ttx.GPOS
new file mode 100644
index 0000000..c362427
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f1.ttx.GPOS
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0" Format="1">
+            <Glyph value="g20"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=2 -->
+          <InputCoverage index="0" Format="1">
+            <Glyph value="g21"/>
+          </InputCoverage>
+          <InputCoverage index="1" Format="1">
+            <Glyph value="g22"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0" Format="1">
+            <Glyph value="g23"/>
+          </LookAheadCoverage>
+          <!-- PosCount=0 -->
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f2.otf b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f2.otf
new file mode 100644
index 0000000..48be5dd
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f2.ttx.GPOS
new file mode 100644
index 0000000..7b27f90
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f2.ttx.GPOS
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0" Format="1">
+            <Glyph value="g20"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0" Format="1">
+            <Glyph value="g21"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=2 -->
+          <LookAheadCoverage index="0" Format="1">
+            <Glyph value="g22"/>
+          </LookAheadCoverage>
+          <LookAheadCoverage index="1" Format="1">
+            <Glyph value="g23"/>
+          </LookAheadCoverage>
+          <!-- PosCount=1 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f3.otf b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f3.otf
new file mode 100644
index 0000000..a10068b
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f3.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f3.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f3.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f3.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f3.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f3.ttx.GPOS
new file mode 100644
index 0000000..73df34c
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f3.ttx.GPOS
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0" Format="1">
+            <Glyph value="g21"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=2 -->
+          <LookAheadCoverage index="0" Format="1">
+            <Glyph value="g22"/>
+          </LookAheadCoverage>
+          <LookAheadCoverage index="1" Format="1">
+            <Glyph value="g23"/>
+          </LookAheadCoverage>
+          <!-- PosCount=1 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f4.otf b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f4.otf
new file mode 100644
index 0000000..8030ac0
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f4.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f4.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f4.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f4.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f4.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f4.ttx.GPOS
new file mode 100644
index 0000000..67bfc0e
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f4.ttx.GPOS
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="3">
+          <!-- BacktrackGlyphCount=2 -->
+          <BacktrackCoverage index="0" Format="1">
+            <Glyph value="g21"/>
+          </BacktrackCoverage>
+          <BacktrackCoverage index="1" Format="1">
+            <Glyph value="g20"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0" Format="1">
+            <Glyph value="g22"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- PosCount=1 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_lookupflag_f1.otf b/Tests/ttLib/tables/data/aots/gpos_chaining3_lookupflag_f1.otf
new file mode 100644
index 0000000..7864ce0
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_lookupflag_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_lookupflag_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining3_lookupflag_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_lookupflag_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_lookupflag_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining3_lookupflag_f1.ttx.GPOS
new file mode 100644
index 0000000..b12b665
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_lookupflag_f1.ttx.GPOS
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="3">
+          <!-- BacktrackGlyphCount=2 -->
+          <BacktrackCoverage index="0" Format="1">
+            <Glyph value="g21"/>
+          </BacktrackCoverage>
+          <BacktrackCoverage index="1" Format="1">
+            <Glyph value="g20"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=3 -->
+          <InputCoverage index="0" Format="1">
+            <Glyph value="g22"/>
+          </InputCoverage>
+          <InputCoverage index="1" Format="1">
+            <Glyph value="g23"/>
+          </InputCoverage>
+          <InputCoverage index="2" Format="1">
+            <Glyph value="g24"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=2 -->
+          <LookAheadCoverage index="0" Format="1">
+            <Glyph value="g25"/>
+          </LookAheadCoverage>
+          <LookAheadCoverage index="1" Format="1">
+            <Glyph value="g26"/>
+          </LookAheadCoverage>
+          <!-- PosCount=1 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="1"/>
+            <LookupListIndex value="0"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_next_glyph_f1.otf b/Tests/ttLib/tables/data/aots/gpos_chaining3_next_glyph_f1.otf
new file mode 100644
index 0000000..20a7966
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_next_glyph_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_next_glyph_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining3_next_glyph_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_next_glyph_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_next_glyph_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining3_next_glyph_f1.ttx.GPOS
new file mode 100644
index 0000000..d5b0c1f
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_next_glyph_f1.ttx.GPOS
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0" Format="1">
+            <Glyph value="g22"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=2 -->
+          <InputCoverage index="0" Format="1">
+            <Glyph value="g21"/>
+          </InputCoverage>
+          <InputCoverage index="1" Format="1">
+            <Glyph value="g22"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0" Format="1">
+            <Glyph value="g21"/>
+          </LookAheadCoverage>
+          <!-- PosCount=1 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f1.otf b/Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f1.otf
new file mode 100644
index 0000000..2c63328
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f1.ttx.GPOS
new file mode 100644
index 0000000..b9b0ce5
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f1.ttx.GPOS
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0" Format="1">
+            <Glyph value="g20"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=2 -->
+          <InputCoverage index="0" Format="1">
+            <Glyph value="g21"/>
+          </InputCoverage>
+          <InputCoverage index="1" Format="1">
+            <Glyph value="g22"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0" Format="1">
+            <Glyph value="g23"/>
+          </LookAheadCoverage>
+          <!-- PosCount=2 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </PosLookupRecord>
+          <PosLookupRecord index="1">
+            <SequenceIndex value="1"/>
+            <LookupListIndex value="0"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f2.otf b/Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f2.otf
new file mode 100644
index 0000000..6ef8fb1
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f2.ttx.GPOS
new file mode 100644
index 0000000..6d023ec
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f2.ttx.GPOS
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="3">
+          <!-- BacktrackGlyphCount=2 -->
+          <BacktrackCoverage index="0" Format="1">
+            <Glyph value="g21"/>
+          </BacktrackCoverage>
+          <BacktrackCoverage index="1" Format="1">
+            <Glyph value="g20"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=3 -->
+          <InputCoverage index="0" Format="1">
+            <Glyph value="g22"/>
+          </InputCoverage>
+          <InputCoverage index="1" Format="1">
+            <Glyph value="g23"/>
+          </InputCoverage>
+          <InputCoverage index="2" Format="1">
+            <Glyph value="g24"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=2 -->
+          <LookAheadCoverage index="0" Format="1">
+            <Glyph value="g25"/>
+          </LookAheadCoverage>
+          <LookAheadCoverage index="1" Format="1">
+            <Glyph value="g26"/>
+          </LookAheadCoverage>
+          <!-- PosCount=1 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_successive_f1.otf b/Tests/ttLib/tables/data/aots/gpos_chaining3_successive_f1.otf
new file mode 100644
index 0000000..8e8439d
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_successive_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_successive_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_chaining3_successive_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_successive_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_chaining3_successive_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_chaining3_successive_f1.ttx.GPOS
new file mode 100644
index 0000000..f7c85b6
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_successive_f1.ttx.GPOS
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0" Format="1">
+            <Glyph value="g25"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=4 -->
+          <InputCoverage index="0" Format="1">
+            <Glyph value="g20"/>
+          </InputCoverage>
+          <InputCoverage index="1" Format="1">
+            <Glyph value="g21"/>
+          </InputCoverage>
+          <InputCoverage index="2" Format="1">
+            <Glyph value="g22"/>
+          </InputCoverage>
+          <InputCoverage index="3" Format="1">
+            <Glyph value="g23"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0" Format="1">
+            <Glyph value="g24"/>
+          </LookAheadCoverage>
+          <!-- PosCount=2 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="1"/>
+            <LookupListIndex value="1"/>
+          </PosLookupRecord>
+          <PosLookupRecord index="1">
+            <SequenceIndex value="2"/>
+            <LookupListIndex value="0"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_boundary_f1.otf b/Tests/ttLib/tables/data/aots/gpos_context1_boundary_f1.otf
new file mode 100644
index 0000000..22bb3ea
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_boundary_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_boundary_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context1_boundary_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_boundary_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_boundary_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context1_boundary_f1.ttx.GPOS
new file mode 100644
index 0000000..662ae54
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_boundary_f1.ttx.GPOS
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <!-- PosRuleSetCount=1 -->
+          <PosRuleSet index="0">
+            <!-- PosRuleCount=1 -->
+            <PosRule index="0">
+              <!-- GlyphCount=2 -->
+              <!-- PosCount=0 -->
+              <Input index="0" value="g20"/>
+            </PosRule>
+          </PosRuleSet>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_boundary_f2.otf b/Tests/ttLib/tables/data/aots/gpos_context1_boundary_f2.otf
new file mode 100644
index 0000000..7e4c0e1
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_boundary_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_boundary_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context1_boundary_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_boundary_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_boundary_f2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context1_boundary_f2.ttx.GPOS
new file mode 100644
index 0000000..56a4f7b
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_boundary_f2.ttx.GPOS
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <!-- PosRuleSetCount=1 -->
+          <PosRuleSet index="0">
+            <!-- PosRuleCount=1 -->
+            <PosRule index="0">
+              <!-- GlyphCount=1 -->
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </PosRule>
+          </PosRuleSet>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_expansion_f1.otf b/Tests/ttLib/tables/data/aots/gpos_context1_expansion_f1.otf
new file mode 100644
index 0000000..f7c60f6
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_expansion_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_expansion_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context1_expansion_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_expansion_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_expansion_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context1_expansion_f1.ttx.GPOS
new file mode 100644
index 0000000..77d52eb
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_expansion_f1.ttx.GPOS
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <!-- PosRuleSetCount=1 -->
+          <PosRuleSet index="0">
+            <!-- PosRuleCount=1 -->
+            <PosRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- PosCount=1 -->
+              <Input index="0" value="g21"/>
+              <Input index="1" value="g22"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="3"/>
+              </PosLookupRecord>
+            </PosRule>
+          </PosRuleSet>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f1.otf b/Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f1.otf
new file mode 100644
index 0000000..e9b03ae
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f1.ttx.GPOS
new file mode 100644
index 0000000..2d5f796
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f1.ttx.GPOS
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <!-- PosRuleSetCount=1 -->
+          <PosRuleSet index="0">
+            <!-- PosRuleCount=1 -->
+            <PosRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- PosCount=3 -->
+              <Input index="0" value="g21"/>
+              <Input index="1" value="g22"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+              <PosLookupRecord index="1">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+              <PosLookupRecord index="2">
+                <SequenceIndex value="2"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </PosRule>
+          </PosRuleSet>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f2.otf b/Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f2.otf
new file mode 100644
index 0000000..b9998cd
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f2.ttx.GPOS
new file mode 100644
index 0000000..ef419ff
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f2.ttx.GPOS
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <!-- PosRuleSetCount=1 -->
+          <PosRuleSet index="0">
+            <!-- PosRuleCount=1 -->
+            <PosRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- PosCount=1 -->
+              <Input index="0" value="g21"/>
+              <Input index="1" value="g22"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="2"/>
+              </PosLookupRecord>
+            </PosRule>
+          </PosRuleSet>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_multiple_subrules_f1.otf b/Tests/ttLib/tables/data/aots/gpos_context1_multiple_subrules_f1.otf
new file mode 100644
index 0000000..6f1aafa
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_multiple_subrules_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_multiple_subrules_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context1_multiple_subrules_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_multiple_subrules_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_multiple_subrules_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context1_multiple_subrules_f1.ttx.GPOS
new file mode 100644
index 0000000..82750d5
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_multiple_subrules_f1.ttx.GPOS
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <!-- PosRuleSetCount=1 -->
+          <PosRuleSet index="0">
+            <!-- PosRuleCount=2 -->
+            <PosRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- PosCount=1 -->
+              <Input index="0" value="g21"/>
+              <Input index="1" value="g22"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </PosRule>
+            <PosRule index="1">
+              <!-- GlyphCount=2 -->
+              <!-- PosCount=1 -->
+              <Input index="0" value="g21"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </PosRule>
+          </PosRuleSet>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_multiple_subrules_f2.otf b/Tests/ttLib/tables/data/aots/gpos_context1_multiple_subrules_f2.otf
new file mode 100644
index 0000000..ed9a387
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_multiple_subrules_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_multiple_subrules_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context1_multiple_subrules_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_multiple_subrules_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_multiple_subrules_f2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context1_multiple_subrules_f2.ttx.GPOS
new file mode 100644
index 0000000..764703b
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_multiple_subrules_f2.ttx.GPOS
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <!-- PosRuleSetCount=1 -->
+          <PosRuleSet index="0">
+            <!-- PosRuleCount=2 -->
+            <PosRule index="0">
+              <!-- GlyphCount=2 -->
+              <!-- PosCount=1 -->
+              <Input index="0" value="g21"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </PosRule>
+            <PosRule index="1">
+              <!-- GlyphCount=3 -->
+              <!-- PosCount=1 -->
+              <Input index="0" value="g21"/>
+              <Input index="1" value="g22"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </PosRule>
+          </PosRuleSet>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_next_glyph_f1.otf b/Tests/ttLib/tables/data/aots/gpos_context1_next_glyph_f1.otf
new file mode 100644
index 0000000..6007d54
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_next_glyph_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_next_glyph_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context1_next_glyph_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_next_glyph_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_next_glyph_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context1_next_glyph_f1.ttx.GPOS
new file mode 100644
index 0000000..ac00f86
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_next_glyph_f1.ttx.GPOS
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <!-- PosRuleSetCount=1 -->
+          <PosRuleSet index="0">
+            <!-- PosRuleCount=1 -->
+            <PosRule index="0">
+              <!-- GlyphCount=2 -->
+              <!-- PosCount=1 -->
+              <Input index="0" value="g20"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </PosRule>
+          </PosRuleSet>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_simple_f1.otf b/Tests/ttLib/tables/data/aots/gpos_context1_simple_f1.otf
new file mode 100644
index 0000000..5e6cd9e
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_simple_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_simple_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context1_simple_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_simple_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_simple_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context1_simple_f1.ttx.GPOS
new file mode 100644
index 0000000..031f56f
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_simple_f1.ttx.GPOS
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <!-- PosRuleSetCount=1 -->
+          <PosRuleSet index="0">
+            <!-- PosRuleCount=1 -->
+            <PosRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- PosCount=3 -->
+              <Input index="0" value="g21"/>
+              <Input index="1" value="g22"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+              <PosLookupRecord index="1">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+              <PosLookupRecord index="2">
+                <SequenceIndex value="2"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </PosRule>
+          </PosRuleSet>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_simple_f2.otf b/Tests/ttLib/tables/data/aots/gpos_context1_simple_f2.otf
new file mode 100644
index 0000000..b4fddb3
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_simple_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_simple_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context1_simple_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_simple_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_simple_f2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context1_simple_f2.ttx.GPOS
new file mode 100644
index 0000000..bb3d01d
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_simple_f2.ttx.GPOS
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <!-- PosRuleSetCount=1 -->
+          <PosRuleSet index="0">
+            <!-- PosRuleCount=1 -->
+            <PosRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- PosCount=1 -->
+              <Input index="0" value="g20"/>
+              <Input index="1" value="g20"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </PosRule>
+          </PosRuleSet>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_successive_f1.otf b/Tests/ttLib/tables/data/aots/gpos_context1_successive_f1.otf
new file mode 100644
index 0000000..eb0f962
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_successive_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_successive_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context1_successive_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_successive_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context1_successive_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context1_successive_f1.ttx.GPOS
new file mode 100644
index 0000000..2d17ca5
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_successive_f1.ttx.GPOS
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <!-- PosRuleSetCount=1 -->
+          <PosRuleSet index="0">
+            <!-- PosRuleCount=1 -->
+            <PosRule index="0">
+              <!-- GlyphCount=4 -->
+              <!-- PosCount=2 -->
+              <Input index="0" value="g21"/>
+              <Input index="1" value="g22"/>
+              <Input index="2" value="g23"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="1"/>
+              </PosLookupRecord>
+              <PosLookupRecord index="1">
+                <SequenceIndex value="2"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </PosRule>
+          </PosRuleSet>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_boundary_f1.otf b/Tests/ttLib/tables/data/aots/gpos_context2_boundary_f1.otf
new file mode 100644
index 0000000..3fce497
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_boundary_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_boundary_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context2_boundary_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_boundary_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_boundary_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context2_boundary_f1.ttx.GPOS
new file mode 100644
index 0000000..b830138
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_boundary_f1.ttx.GPOS
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g20" class="1"/>
+          </ClassDef>
+          <!-- PosClassSetCount=2 -->
+          <PosClassSet index="0" empty="1"/>
+          <PosClassSet index="1">
+            <!-- PosClassRuleCount=1 -->
+            <PosClassRule index="0">
+              <!-- GlyphCount=2 -->
+              <!-- PosCount=0 -->
+              <Class index="0" value="1"/>
+            </PosClassRule>
+          </PosClassSet>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_boundary_f2.otf b/Tests/ttLib/tables/data/aots/gpos_context2_boundary_f2.otf
new file mode 100644
index 0000000..5b4e012
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_boundary_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_boundary_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context2_boundary_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_boundary_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_boundary_f2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context2_boundary_f2.ttx.GPOS
new file mode 100644
index 0000000..a48dc6a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_boundary_f2.ttx.GPOS
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g20" class="1"/>
+          </ClassDef>
+          <!-- PosClassSetCount=2 -->
+          <PosClassSet index="0" empty="1"/>
+          <PosClassSet index="1">
+            <!-- PosClassRuleCount=1 -->
+            <PosClassRule index="0">
+              <!-- GlyphCount=1 -->
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </PosClassRule>
+          </PosClassSet>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_classes_f1.otf b/Tests/ttLib/tables/data/aots/gpos_context2_classes_f1.otf
new file mode 100644
index 0000000..585b511
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_classes_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_classes_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context2_classes_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_classes_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_classes_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context2_classes_f1.ttx.GPOS
new file mode 100644
index 0000000..7573e48
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_classes_f1.ttx.GPOS
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g20" class="1"/>
+            <ClassDef glyph="g21" class="1"/>
+            <ClassDef glyph="g22" class="1"/>
+            <ClassDef glyph="g24" class="3"/>
+            <ClassDef glyph="g26" class="2"/>
+            <ClassDef glyph="g27" class="2"/>
+            <ClassDef glyph="g28" class="2"/>
+            <ClassDef glyph="g29" class="2"/>
+          </ClassDef>
+          <!-- PosClassSetCount=4 -->
+          <PosClassSet index="0" empty="1"/>
+          <PosClassSet index="1">
+            <!-- PosClassRuleCount=1 -->
+            <PosClassRule index="0">
+              <!-- GlyphCount=4 -->
+              <!-- PosCount=1 -->
+              <Class index="0" value="2"/>
+              <Class index="1" value="2"/>
+              <Class index="2" value="3"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </PosClassRule>
+          </PosClassSet>
+          <PosClassSet index="2" empty="1"/>
+          <PosClassSet index="3" empty="1"/>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_classes_f2.otf b/Tests/ttLib/tables/data/aots/gpos_context2_classes_f2.otf
new file mode 100644
index 0000000..411d58e
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_classes_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_classes_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context2_classes_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_classes_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_classes_f2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context2_classes_f2.ttx.GPOS
new file mode 100644
index 0000000..4435c02
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_classes_f2.ttx.GPOS
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g24"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g20" class="1"/>
+            <ClassDef glyph="g21" class="1"/>
+            <ClassDef glyph="g22" class="1"/>
+            <ClassDef glyph="g24" class="3"/>
+            <ClassDef glyph="g26" class="2"/>
+            <ClassDef glyph="g27" class="2"/>
+            <ClassDef glyph="g28" class="2"/>
+            <ClassDef glyph="g29" class="2"/>
+          </ClassDef>
+          <!-- PosClassSetCount=4 -->
+          <PosClassSet index="0" empty="1"/>
+          <PosClassSet index="1">
+            <!-- PosClassRuleCount=1 -->
+            <PosClassRule index="0">
+              <!-- GlyphCount=4 -->
+              <!-- PosCount=1 -->
+              <Class index="0" value="2"/>
+              <Class index="1" value="2"/>
+              <Class index="2" value="3"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </PosClassRule>
+          </PosClassSet>
+          <PosClassSet index="2" empty="1"/>
+          <PosClassSet index="3">
+            <!-- PosClassRuleCount=1 -->
+            <PosClassRule index="0">
+              <!-- GlyphCount=4 -->
+              <!-- PosCount=1 -->
+              <Class index="0" value="3"/>
+              <Class index="1" value="2"/>
+              <Class index="2" value="1"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="2"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </PosClassRule>
+          </PosClassSet>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_expansion_f1.otf b/Tests/ttLib/tables/data/aots/gpos_context2_expansion_f1.otf
new file mode 100644
index 0000000..4b2d36a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_expansion_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_expansion_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context2_expansion_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_expansion_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_expansion_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context2_expansion_f1.ttx.GPOS
new file mode 100644
index 0000000..584892f
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_expansion_f1.ttx.GPOS
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g20" class="1"/>
+            <ClassDef glyph="g21" class="2"/>
+            <ClassDef glyph="g22" class="3"/>
+          </ClassDef>
+          <!-- PosClassSetCount=4 -->
+          <PosClassSet index="0" empty="1"/>
+          <PosClassSet index="1">
+            <!-- PosClassRuleCount=1 -->
+            <PosClassRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- PosCount=1 -->
+              <Class index="0" value="2"/>
+              <Class index="1" value="3"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="3"/>
+              </PosLookupRecord>
+            </PosClassRule>
+          </PosClassSet>
+          <PosClassSet index="2" empty="1"/>
+          <PosClassSet index="3" empty="1"/>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f1.otf b/Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f1.otf
new file mode 100644
index 0000000..1c0c480
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f1.ttx.GPOS
new file mode 100644
index 0000000..99546b5
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f1.ttx.GPOS
@@ -0,0 +1,141 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g20" class="1"/>
+            <ClassDef glyph="g21" class="2"/>
+            <ClassDef glyph="g22" class="3"/>
+          </ClassDef>
+          <!-- PosClassSetCount=4 -->
+          <PosClassSet index="0" empty="1"/>
+          <PosClassSet index="1">
+            <!-- PosClassRuleCount=1 -->
+            <PosClassRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- PosCount=3 -->
+              <Class index="0" value="2"/>
+              <Class index="1" value="3"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+              <PosLookupRecord index="1">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+              <PosLookupRecord index="2">
+                <SequenceIndex value="2"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </PosClassRule>
+          </PosClassSet>
+          <PosClassSet index="2" empty="1"/>
+          <PosClassSet index="3" empty="1"/>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f2.otf b/Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f2.otf
new file mode 100644
index 0000000..bf20d84
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f2.ttx.GPOS
new file mode 100644
index 0000000..4a0fcb8
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f2.ttx.GPOS
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g20" class="1"/>
+            <ClassDef glyph="g21" class="2"/>
+            <ClassDef glyph="g22" class="3"/>
+          </ClassDef>
+          <!-- PosClassSetCount=4 -->
+          <PosClassSet index="0" empty="1"/>
+          <PosClassSet index="1">
+            <!-- PosClassRuleCount=1 -->
+            <PosClassRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- PosCount=1 -->
+              <Class index="0" value="2"/>
+              <Class index="1" value="3"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="2"/>
+              </PosLookupRecord>
+            </PosClassRule>
+          </PosClassSet>
+          <PosClassSet index="2" empty="1"/>
+          <PosClassSet index="3" empty="1"/>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_multiple_subrules_f1.otf b/Tests/ttLib/tables/data/aots/gpos_context2_multiple_subrules_f1.otf
new file mode 100644
index 0000000..05b6b73
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_multiple_subrules_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_multiple_subrules_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context2_multiple_subrules_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_multiple_subrules_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_multiple_subrules_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context2_multiple_subrules_f1.ttx.GPOS
new file mode 100644
index 0000000..d2f38c3
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_multiple_subrules_f1.ttx.GPOS
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g20" class="1"/>
+            <ClassDef glyph="g21" class="2"/>
+            <ClassDef glyph="g22" class="3"/>
+          </ClassDef>
+          <!-- PosClassSetCount=4 -->
+          <PosClassSet index="0" empty="1"/>
+          <PosClassSet index="1">
+            <!-- PosClassRuleCount=2 -->
+            <PosClassRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- PosCount=1 -->
+              <Class index="0" value="2"/>
+              <Class index="1" value="3"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </PosClassRule>
+            <PosClassRule index="1">
+              <!-- GlyphCount=2 -->
+              <!-- PosCount=1 -->
+              <Class index="0" value="2"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </PosClassRule>
+          </PosClassSet>
+          <PosClassSet index="2" empty="1"/>
+          <PosClassSet index="3" empty="1"/>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_multiple_subrules_f2.otf b/Tests/ttLib/tables/data/aots/gpos_context2_multiple_subrules_f2.otf
new file mode 100644
index 0000000..f79712c
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_multiple_subrules_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_multiple_subrules_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context2_multiple_subrules_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_multiple_subrules_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_multiple_subrules_f2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context2_multiple_subrules_f2.ttx.GPOS
new file mode 100644
index 0000000..e5d0a63
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_multiple_subrules_f2.ttx.GPOS
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g20" class="1"/>
+            <ClassDef glyph="g21" class="2"/>
+            <ClassDef glyph="g22" class="3"/>
+          </ClassDef>
+          <!-- PosClassSetCount=4 -->
+          <PosClassSet index="0" empty="1"/>
+          <PosClassSet index="1">
+            <!-- PosClassRuleCount=2 -->
+            <PosClassRule index="0">
+              <!-- GlyphCount=2 -->
+              <!-- PosCount=1 -->
+              <Class index="0" value="2"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </PosClassRule>
+            <PosClassRule index="1">
+              <!-- GlyphCount=3 -->
+              <!-- PosCount=1 -->
+              <Class index="0" value="2"/>
+              <Class index="1" value="3"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </PosClassRule>
+          </PosClassSet>
+          <PosClassSet index="2" empty="1"/>
+          <PosClassSet index="3" empty="1"/>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_next_glyph_f1.otf b/Tests/ttLib/tables/data/aots/gpos_context2_next_glyph_f1.otf
new file mode 100644
index 0000000..1b5a256
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_next_glyph_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_next_glyph_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context2_next_glyph_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_next_glyph_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_next_glyph_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context2_next_glyph_f1.ttx.GPOS
new file mode 100644
index 0000000..c171781
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_next_glyph_f1.ttx.GPOS
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g20" class="1"/>
+          </ClassDef>
+          <!-- PosClassSetCount=2 -->
+          <PosClassSet index="0" empty="1"/>
+          <PosClassSet index="1">
+            <!-- PosClassRuleCount=1 -->
+            <PosClassRule index="0">
+              <!-- GlyphCount=2 -->
+              <!-- PosCount=1 -->
+              <Class index="0" value="1"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </PosClassRule>
+          </PosClassSet>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_simple_f1.otf b/Tests/ttLib/tables/data/aots/gpos_context2_simple_f1.otf
new file mode 100644
index 0000000..9aaec44
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_simple_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_simple_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context2_simple_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_simple_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_simple_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context2_simple_f1.ttx.GPOS
new file mode 100644
index 0000000..266f31b
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_simple_f1.ttx.GPOS
@@ -0,0 +1,141 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g20" class="1"/>
+            <ClassDef glyph="g21" class="2"/>
+            <ClassDef glyph="g22" class="3"/>
+          </ClassDef>
+          <!-- PosClassSetCount=4 -->
+          <PosClassSet index="0" empty="1"/>
+          <PosClassSet index="1">
+            <!-- PosClassRuleCount=1 -->
+            <PosClassRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- PosCount=3 -->
+              <Class index="0" value="2"/>
+              <Class index="1" value="3"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+              <PosLookupRecord index="1">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+              <PosLookupRecord index="2">
+                <SequenceIndex value="2"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </PosClassRule>
+          </PosClassSet>
+          <PosClassSet index="2" empty="1"/>
+          <PosClassSet index="3" empty="1"/>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_simple_f2.otf b/Tests/ttLib/tables/data/aots/gpos_context2_simple_f2.otf
new file mode 100644
index 0000000..c789b2e
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_simple_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_simple_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context2_simple_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_simple_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_simple_f2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context2_simple_f2.ttx.GPOS
new file mode 100644
index 0000000..8001658
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_simple_f2.ttx.GPOS
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g20" class="1"/>
+          </ClassDef>
+          <!-- PosClassSetCount=2 -->
+          <PosClassSet index="0" empty="1"/>
+          <PosClassSet index="1">
+            <!-- PosClassRuleCount=1 -->
+            <PosClassRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- PosCount=1 -->
+              <Class index="0" value="1"/>
+              <Class index="1" value="1"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </PosClassRule>
+          </PosClassSet>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_successive_f1.otf b/Tests/ttLib/tables/data/aots/gpos_context2_successive_f1.otf
new file mode 100644
index 0000000..b89bfb7
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_successive_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_successive_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context2_successive_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_successive_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context2_successive_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context2_successive_f1.ttx.GPOS
new file mode 100644
index 0000000..2de586f
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_successive_f1.ttx.GPOS
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g20" class="1"/>
+            <ClassDef glyph="g21" class="2"/>
+            <ClassDef glyph="g22" class="3"/>
+            <ClassDef glyph="g23" class="4"/>
+          </ClassDef>
+          <!-- PosClassSetCount=5 -->
+          <PosClassSet index="0" empty="1"/>
+          <PosClassSet index="1">
+            <!-- PosClassRuleCount=1 -->
+            <PosClassRule index="0">
+              <!-- GlyphCount=4 -->
+              <!-- PosCount=2 -->
+              <Class index="0" value="2"/>
+              <Class index="1" value="3"/>
+              <Class index="2" value="4"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="1"/>
+              </PosLookupRecord>
+              <PosLookupRecord index="1">
+                <SequenceIndex value="2"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+            </PosClassRule>
+          </PosClassSet>
+          <PosClassSet index="2" empty="1"/>
+          <PosClassSet index="3" empty="1"/>
+          <PosClassSet index="4" empty="1"/>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context3_boundary_f1.otf b/Tests/ttLib/tables/data/aots/gpos_context3_boundary_f1.otf
new file mode 100644
index 0000000..f8949d4
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_boundary_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context3_boundary_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context3_boundary_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_boundary_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context3_boundary_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context3_boundary_f1.ttx.GPOS
new file mode 100644
index 0000000..fd85d19
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_boundary_f1.ttx.GPOS
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="3">
+          <!-- GlyphCount=2 -->
+          <!-- PosCount=0 -->
+          <Coverage index="0" Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <Coverage index="1" Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context3_boundary_f2.otf b/Tests/ttLib/tables/data/aots/gpos_context3_boundary_f2.otf
new file mode 100644
index 0000000..ceb7452
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_boundary_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context3_boundary_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context3_boundary_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_boundary_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context3_boundary_f2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context3_boundary_f2.ttx.GPOS
new file mode 100644
index 0000000..bee96ff
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_boundary_f2.ttx.GPOS
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="3">
+          <!-- GlyphCount=1 -->
+          <!-- PosCount=1 -->
+          <Coverage index="0" Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </PosLookupRecord>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f1.otf b/Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f1.otf
new file mode 100644
index 0000000..40b55ee
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f1.ttx.GPOS
new file mode 100644
index 0000000..e9c854c
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f1.ttx.GPOS
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="3">
+          <!-- GlyphCount=3 -->
+          <!-- PosCount=3 -->
+          <Coverage index="0" Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <Coverage index="1" Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <Coverage index="2" Format="1">
+            <Glyph value="g22"/>
+          </Coverage>
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </PosLookupRecord>
+          <PosLookupRecord index="1">
+            <SequenceIndex value="1"/>
+            <LookupListIndex value="0"/>
+          </PosLookupRecord>
+          <PosLookupRecord index="2">
+            <SequenceIndex value="2"/>
+            <LookupListIndex value="0"/>
+          </PosLookupRecord>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f2.otf b/Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f2.otf
new file mode 100644
index 0000000..bcf4d17
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f2.ttx.GPOS
new file mode 100644
index 0000000..e2b4a23
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f2.ttx.GPOS
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="3">
+          <!-- GlyphCount=3 -->
+          <!-- PosCount=1 -->
+          <Coverage index="0" Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <Coverage index="1" Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <Coverage index="2" Format="1">
+            <Glyph value="g22"/>
+          </Coverage>
+          <PosLookupRecord index="0">
+            <SequenceIndex value="1"/>
+            <LookupListIndex value="2"/>
+          </PosLookupRecord>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context3_next_glyph_f1.otf b/Tests/ttLib/tables/data/aots/gpos_context3_next_glyph_f1.otf
new file mode 100644
index 0000000..6f9d919
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_next_glyph_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context3_next_glyph_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context3_next_glyph_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_next_glyph_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context3_next_glyph_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context3_next_glyph_f1.ttx.GPOS
new file mode 100644
index 0000000..44ce072
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_next_glyph_f1.ttx.GPOS
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="3">
+          <!-- GlyphCount=2 -->
+          <!-- PosCount=1 -->
+          <Coverage index="0" Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <Coverage index="1" Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </PosLookupRecord>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context3_simple_f1.otf b/Tests/ttLib/tables/data/aots/gpos_context3_simple_f1.otf
new file mode 100644
index 0000000..470c2ed
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_simple_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context3_simple_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context3_simple_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_simple_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context3_simple_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context3_simple_f1.ttx.GPOS
new file mode 100644
index 0000000..c559a7d
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_simple_f1.ttx.GPOS
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="3">
+          <!-- GlyphCount=3 -->
+          <!-- PosCount=3 -->
+          <Coverage index="0" Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <Coverage index="1" Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <Coverage index="2" Format="1">
+            <Glyph value="g22"/>
+          </Coverage>
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </PosLookupRecord>
+          <PosLookupRecord index="1">
+            <SequenceIndex value="1"/>
+            <LookupListIndex value="0"/>
+          </PosLookupRecord>
+          <PosLookupRecord index="2">
+            <SequenceIndex value="2"/>
+            <LookupListIndex value="0"/>
+          </PosLookupRecord>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context3_successive_f1.otf b/Tests/ttLib/tables/data/aots/gpos_context3_successive_f1.otf
new file mode 100644
index 0000000..aeb9bbd
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_successive_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gpos_context3_successive_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos_context3_successive_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_successive_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gpos_context3_successive_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos_context3_successive_f1.ttx.GPOS
new file mode 100644
index 0000000..8ff7eea
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_successive_f1.ttx.GPOS
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+            <Glyph value="g27"/>
+            <Glyph value="g28"/>
+            <Glyph value="g29"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <Value XPlacement="20"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="g22"/>
+              <Value1 XPlacement="20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=0 -->
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="3">
+          <!-- GlyphCount=4 -->
+          <!-- PosCount=2 -->
+          <Coverage index="0" Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <Coverage index="1" Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <Coverage index="2" Format="1">
+            <Glyph value="g22"/>
+          </Coverage>
+          <Coverage index="3" Format="1">
+            <Glyph value="g23"/>
+          </Coverage>
+          <PosLookupRecord index="0">
+            <SequenceIndex value="1"/>
+            <LookupListIndex value="1"/>
+          </PosLookupRecord>
+          <PosLookupRecord index="1">
+            <SequenceIndex value="2"/>
+            <LookupListIndex value="0"/>
+          </PosLookupRecord>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub1_1_lookupflag_f1.otf b/Tests/ttLib/tables/data/aots/gsub1_1_lookupflag_f1.otf
new file mode 100644
index 0000000..a539b95
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub1_1_lookupflag_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub1_1_lookupflag_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub1_1_lookupflag_f1.ttx.GDEF
new file mode 100644
index 0000000..971a3f1
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub1_1_lookupflag_f1.ttx.GDEF
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g18" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub1_1_lookupflag_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub1_1_lookupflag_f1.ttx.GSUB
new file mode 100644
index 0000000..ba7b68f
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub1_1_lookupflag_f1.ttx.GSUB
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="2"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g18" out="g23"/>
+          <Substitution in="g19" out="g24"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub1_1_modulo_f1.otf b/Tests/ttLib/tables/data/aots/gsub1_1_modulo_f1.otf
new file mode 100644
index 0000000..7ba2379
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub1_1_modulo_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub1_1_modulo_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub1_1_modulo_f1.ttx.GSUB
new file mode 100644
index 0000000..b9923da
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub1_1_modulo_f1.ttx.GSUB
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=2 -->
+          <LookupListIndex index="0" value="0"/>
+          <LookupListIndex index="1" value="1"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=2 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g21" out="glyph32787"/>
+          <Substitution in="g22" out="glyph32788"/>
+        </SingleSubst>
+        <SingleSubst index="1" Format="1">
+          <Substitution in="g19" out="glyph32789"/>
+          <Substitution in="g20" out="glyph32790"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=4 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="glyph32787" out="g23"/>
+        </SingleSubst>
+        <SingleSubst index="1" Format="1">
+          <Substitution in="glyph32788" out="g18"/>
+        </SingleSubst>
+        <SingleSubst index="2" Format="1">
+          <Substitution in="glyph32789" out="g17"/>
+        </SingleSubst>
+        <SingleSubst index="3" Format="1">
+          <Substitution in="glyph32790" out="g24"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub1_1_simple_f1.otf b/Tests/ttLib/tables/data/aots/gsub1_1_simple_f1.otf
new file mode 100644
index 0000000..c21fcd3
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub1_1_simple_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub1_1_simple_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub1_1_simple_f1.ttx.GSUB
new file mode 100644
index 0000000..e76ba74
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub1_1_simple_f1.ttx.GSUB
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g18" out="g23"/>
+          <Substitution in="g19" out="g24"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub1_2_lookupflag_f1.otf b/Tests/ttLib/tables/data/aots/gsub1_2_lookupflag_f1.otf
new file mode 100644
index 0000000..b13af6e
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub1_2_lookupflag_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub1_2_lookupflag_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub1_2_lookupflag_f1.ttx.GDEF
new file mode 100644
index 0000000..971a3f1
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub1_2_lookupflag_f1.ttx.GDEF
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g18" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub1_2_lookupflag_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub1_2_lookupflag_f1.ttx.GSUB
new file mode 100644
index 0000000..51bfbb1
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub1_2_lookupflag_f1.ttx.GSUB
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="2"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="2">
+          <Substitution in="g18" out="g22"/>
+          <Substitution in="g20" out="g25"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub1_2_simple_f1.otf b/Tests/ttLib/tables/data/aots/gsub1_2_simple_f1.otf
new file mode 100644
index 0000000..d3851b3
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub1_2_simple_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub1_2_simple_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub1_2_simple_f1.ttx.GSUB
new file mode 100644
index 0000000..55649d2
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub1_2_simple_f1.ttx.GSUB
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="2">
+          <Substitution in="g18" out="g22"/>
+          <Substitution in="g20" out="g25"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub2_1_lookupflag_f1.otf b/Tests/ttLib/tables/data/aots/gsub2_1_lookupflag_f1.otf
new file mode 100644
index 0000000..8330ad4
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub2_1_lookupflag_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub2_1_lookupflag_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub2_1_lookupflag_f1.ttx.GDEF
new file mode 100644
index 0000000..971a3f1
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub2_1_lookupflag_f1.ttx.GDEF
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g18" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub2_1_lookupflag_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub2_1_lookupflag_f1.ttx.GSUB
new file mode 100644
index 0000000..2c59793
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub2_1_lookupflag_f1.ttx.GSUB
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="2"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g18" out="g20,g21"/>
+          <Substitution in="g19" out="g22,g23"/>
+        </MultipleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub2_1_multiple_sequences_f1.otf b/Tests/ttLib/tables/data/aots/gsub2_1_multiple_sequences_f1.otf
new file mode 100644
index 0000000..c912937
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub2_1_multiple_sequences_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub2_1_multiple_sequences_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub2_1_multiple_sequences_f1.ttx.GSUB
new file mode 100644
index 0000000..5ec800e
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub2_1_multiple_sequences_f1.ttx.GSUB
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g18" out="g20,g21"/>
+          <Substitution in="g19" out="g22,g23"/>
+        </MultipleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub2_1_simple_f1.otf b/Tests/ttLib/tables/data/aots/gsub2_1_simple_f1.otf
new file mode 100644
index 0000000..584a7f5
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub2_1_simple_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub2_1_simple_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub2_1_simple_f1.ttx.GSUB
new file mode 100644
index 0000000..cae4f66
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub2_1_simple_f1.ttx.GSUB
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g18" out="g20,g21,g22"/>
+        </MultipleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub3_1_lookupflag_f1.otf b/Tests/ttLib/tables/data/aots/gsub3_1_lookupflag_f1.otf
new file mode 100644
index 0000000..4ccf55f
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub3_1_lookupflag_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub3_1_lookupflag_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub3_1_lookupflag_f1.ttx.GDEF
new file mode 100644
index 0000000..971a3f1
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub3_1_lookupflag_f1.ttx.GDEF
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g18" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub3_1_lookupflag_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub3_1_lookupflag_f1.ttx.GSUB
new file mode 100644
index 0000000..63c53b4
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub3_1_lookupflag_f1.ttx.GSUB
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="3"/>
+        <LookupFlag value="2"/>
+        <!-- SubTableCount=1 -->
+        <AlternateSubst index="0" Format="1">
+          <AlternateSet glyph="g18">
+            <Alternate glyph="g20"/>
+            <Alternate glyph="g21"/>
+          </AlternateSet>
+          <AlternateSet glyph="g19">
+            <Alternate glyph="g22"/>
+            <Alternate glyph="g23"/>
+          </AlternateSet>
+        </AlternateSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub3_1_multiple_f1.otf b/Tests/ttLib/tables/data/aots/gsub3_1_multiple_f1.otf
new file mode 100644
index 0000000..075f196
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub3_1_multiple_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub3_1_multiple_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub3_1_multiple_f1.ttx.GSUB
new file mode 100644
index 0000000..1eae117
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub3_1_multiple_f1.ttx.GSUB
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="3"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <AlternateSubst index="0" Format="1">
+          <AlternateSet glyph="g18">
+            <Alternate glyph="g20"/>
+            <Alternate glyph="g21"/>
+          </AlternateSet>
+          <AlternateSet glyph="g19">
+            <Alternate glyph="g22"/>
+            <Alternate glyph="g23"/>
+          </AlternateSet>
+        </AlternateSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub3_1_simple_f1.otf b/Tests/ttLib/tables/data/aots/gsub3_1_simple_f1.otf
new file mode 100644
index 0000000..201f0f2
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub3_1_simple_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub3_1_simple_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub3_1_simple_f1.ttx.GSUB
new file mode 100644
index 0000000..7372cd0
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub3_1_simple_f1.ttx.GSUB
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="3"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <AlternateSubst index="0" Format="1">
+          <AlternateSet glyph="g18">
+            <Alternate glyph="g20"/>
+            <Alternate glyph="g21"/>
+            <Alternate glyph="g22"/>
+          </AlternateSet>
+        </AlternateSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub4_1_lookupflag_f1.otf b/Tests/ttLib/tables/data/aots/gsub4_1_lookupflag_f1.otf
new file mode 100644
index 0000000..08ec01a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub4_1_lookupflag_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub4_1_lookupflag_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub4_1_lookupflag_f1.ttx.GDEF
new file mode 100644
index 0000000..ffcc7a1
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub4_1_lookupflag_f1.ttx.GDEF
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g24" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub4_1_lookupflag_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub4_1_lookupflag_f1.ttx.GSUB
new file mode 100644
index 0000000..0982ef6
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub4_1_lookupflag_f1.ttx.GSUB
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="2"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g18">
+            <Ligature components="g19,g20" glyph="g23"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub4_1_multiple_ligatures_f1.otf b/Tests/ttLib/tables/data/aots/gsub4_1_multiple_ligatures_f1.otf
new file mode 100644
index 0000000..90da331
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub4_1_multiple_ligatures_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub4_1_multiple_ligatures_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub4_1_multiple_ligatures_f1.ttx.GSUB
new file mode 100644
index 0000000..9466f91
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub4_1_multiple_ligatures_f1.ttx.GSUB
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g18">
+            <Ligature components="g19,g20" glyph="g23"/>
+            <Ligature components="g19" glyph="g24"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub4_1_multiple_ligatures_f2.otf b/Tests/ttLib/tables/data/aots/gsub4_1_multiple_ligatures_f2.otf
new file mode 100644
index 0000000..4383ba9
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub4_1_multiple_ligatures_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub4_1_multiple_ligatures_f2.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub4_1_multiple_ligatures_f2.ttx.GSUB
new file mode 100644
index 0000000..bc25e84
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub4_1_multiple_ligatures_f2.ttx.GSUB
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g18">
+            <Ligature components="g19" glyph="g24"/>
+            <Ligature components="g19,g20" glyph="g23"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub4_1_multiple_ligsets_f1.otf b/Tests/ttLib/tables/data/aots/gsub4_1_multiple_ligsets_f1.otf
new file mode 100644
index 0000000..cea1b1a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub4_1_multiple_ligsets_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub4_1_multiple_ligsets_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub4_1_multiple_ligsets_f1.ttx.GSUB
new file mode 100644
index 0000000..0ea6495
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub4_1_multiple_ligsets_f1.ttx.GSUB
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g18">
+            <Ligature components="g19" glyph="g23"/>
+          </LigatureSet>
+          <LigatureSet glyph="g20">
+            <Ligature components="g19" glyph="g24"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub4_1_simple_f1.otf b/Tests/ttLib/tables/data/aots/gsub4_1_simple_f1.otf
new file mode 100644
index 0000000..50c713a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub4_1_simple_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub4_1_simple_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub4_1_simple_f1.ttx.GSUB
new file mode 100644
index 0000000..ecc8fa6
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub4_1_simple_f1.ttx.GSUB
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g18">
+            <Ligature components="g19,g20" glyph="g23"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub7_font1.otf b/Tests/ttLib/tables/data/aots/gsub7_font1.otf
new file mode 100644
index 0000000..b920398
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub7_font1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub7_font1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub7_font1.ttx.GSUB
new file mode 100644
index 0000000..3f65d93
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub7_font1.ttx.GSUB
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ExtensionSubst index="0" Format="1">
+          <ExtensionLookupType value="1"/>
+          <SingleSubst Format="1">
+            <Substitution in="g18" out="g23"/>
+            <Substitution in="g19" out="g24"/>
+          </SingleSubst>
+        </ExtensionSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub7_font2.otf b/Tests/ttLib/tables/data/aots/gsub7_font2.otf
new file mode 100644
index 0000000..c98bafb
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub7_font2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub7_font2.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub7_font2.ttx.GSUB
new file mode 100644
index 0000000..98338ed
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub7_font2.ttx.GSUB
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=2 -->
+        <ExtensionSubst index="0" Format="1">
+          <ExtensionLookupType value="1"/>
+          <SingleSubst Format="1">
+            <Substitution in="g18" out="g23"/>
+          </SingleSubst>
+        </ExtensionSubst>
+        <ExtensionSubst index="1" Format="1">
+          <ExtensionLookupType value="1"/>
+          <SingleSubst Format="1">
+            <Substitution in="g19" out="g29"/>
+          </SingleSubst>
+        </ExtensionSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f1.otf b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f1.otf
new file mode 100644
index 0000000..444d931
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f1.ttx.GSUB
new file mode 100644
index 0000000..49ed83d
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f1.ttx.GSUB
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <!-- ChainSubRuleSetCount=1 -->
+          <ChainSubRuleSet index="0">
+            <!-- ChainSubRuleCount=1 -->
+            <ChainSubRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="g20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="g22"/>
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="g23"/>
+              <!-- SubstCount=0 -->
+            </ChainSubRule>
+          </ChainSubRuleSet>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f2.otf b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f2.otf
new file mode 100644
index 0000000..2268647
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f2.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f2.ttx.GSUB
new file mode 100644
index 0000000..7790bf9
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f2.ttx.GSUB
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <!-- ChainSubRuleSetCount=1 -->
+          <ChainSubRuleSet index="0">
+            <!-- ChainSubRuleCount=1 -->
+            <ChainSubRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="g20"/>
+              <!-- InputGlyphCount=1 -->
+              <!-- LookAheadGlyphCount=2 -->
+              <LookAhead index="0" value="g22"/>
+              <LookAhead index="1" value="g23"/>
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </ChainSubRule>
+          </ChainSubRuleSet>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f3.otf b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f3.otf
new file mode 100644
index 0000000..a592947
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f3.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f3.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f3.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f3.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f3.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f3.ttx.GSUB
new file mode 100644
index 0000000..c58e3d5
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f3.ttx.GSUB
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <!-- ChainSubRuleSetCount=1 -->
+          <ChainSubRuleSet index="0">
+            <!-- ChainSubRuleCount=1 -->
+            <ChainSubRule index="0">
+              <!-- BacktrackGlyphCount=0 -->
+              <!-- InputGlyphCount=1 -->
+              <!-- LookAheadGlyphCount=2 -->
+              <LookAhead index="0" value="g22"/>
+              <LookAhead index="1" value="g23"/>
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </ChainSubRule>
+          </ChainSubRuleSet>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f4.otf b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f4.otf
new file mode 100644
index 0000000..f3f6b8c
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f4.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f4.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f4.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f4.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f4.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f4.ttx.GSUB
new file mode 100644
index 0000000..43a1181
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f4.ttx.GSUB
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g22"/>
+          </Coverage>
+          <!-- ChainSubRuleSetCount=1 -->
+          <ChainSubRuleSet index="0">
+            <!-- ChainSubRuleCount=1 -->
+            <ChainSubRule index="0">
+              <!-- BacktrackGlyphCount=2 -->
+              <Backtrack index="0" value="g21"/>
+              <Backtrack index="1" value="g20"/>
+              <!-- InputGlyphCount=1 -->
+              <!-- LookAheadGlyphCount=0 -->
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </ChainSubRule>
+          </ChainSubRuleSet>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_lookupflag_f1.otf b/Tests/ttLib/tables/data/aots/gsub_chaining1_lookupflag_f1.otf
new file mode 100644
index 0000000..47e4c64
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_lookupflag_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_lookupflag_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining1_lookupflag_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_lookupflag_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_lookupflag_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining1_lookupflag_f1.ttx.GSUB
new file mode 100644
index 0000000..e3bdf7f
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_lookupflag_f1.ttx.GSUB
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g22"/>
+          </Coverage>
+          <!-- ChainSubRuleSetCount=1 -->
+          <ChainSubRuleSet index="0">
+            <!-- ChainSubRuleCount=1 -->
+            <ChainSubRule index="0">
+              <!-- BacktrackGlyphCount=2 -->
+              <Backtrack index="0" value="g21"/>
+              <Backtrack index="1" value="g20"/>
+              <!-- InputGlyphCount=3 -->
+              <Input index="0" value="g23"/>
+              <Input index="1" value="g24"/>
+              <!-- LookAheadGlyphCount=2 -->
+              <LookAhead index="0" value="g25"/>
+              <LookAhead index="1" value="g26"/>
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </ChainSubRule>
+          </ChainSubRuleSet>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_multiple_subrules_f1.otf b/Tests/ttLib/tables/data/aots/gsub_chaining1_multiple_subrules_f1.otf
new file mode 100644
index 0000000..741362c
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_multiple_subrules_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_multiple_subrules_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining1_multiple_subrules_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_multiple_subrules_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_multiple_subrules_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining1_multiple_subrules_f1.ttx.GSUB
new file mode 100644
index 0000000..a8ceb2b
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_multiple_subrules_f1.ttx.GSUB
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <!-- ChainSubRuleSetCount=1 -->
+          <ChainSubRuleSet index="0">
+            <!-- ChainSubRuleCount=2 -->
+            <ChainSubRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="g20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="g22"/>
+              <!-- LookAheadGlyphCount=2 -->
+              <LookAhead index="0" value="g23"/>
+              <LookAhead index="1" value="g24"/>
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </ChainSubRule>
+            <ChainSubRule index="1">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="g20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="g22"/>
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="g23"/>
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </ChainSubRule>
+          </ChainSubRuleSet>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_multiple_subrules_f2.otf b/Tests/ttLib/tables/data/aots/gsub_chaining1_multiple_subrules_f2.otf
new file mode 100644
index 0000000..67801f2
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_multiple_subrules_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_multiple_subrules_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining1_multiple_subrules_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_multiple_subrules_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_multiple_subrules_f2.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining1_multiple_subrules_f2.ttx.GSUB
new file mode 100644
index 0000000..f73b4ec
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_multiple_subrules_f2.ttx.GSUB
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <!-- ChainSubRuleSetCount=1 -->
+          <ChainSubRuleSet index="0">
+            <!-- ChainSubRuleCount=2 -->
+            <ChainSubRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="g20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="g22"/>
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="g23"/>
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </ChainSubRule>
+            <ChainSubRule index="1">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="g20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="g22"/>
+              <!-- LookAheadGlyphCount=2 -->
+              <LookAhead index="0" value="g23"/>
+              <LookAhead index="1" value="g24"/>
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </ChainSubRule>
+          </ChainSubRuleSet>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_next_glyph_f1.otf b/Tests/ttLib/tables/data/aots/gsub_chaining1_next_glyph_f1.otf
new file mode 100644
index 0000000..655000a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_next_glyph_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_next_glyph_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining1_next_glyph_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_next_glyph_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_next_glyph_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining1_next_glyph_f1.ttx.GSUB
new file mode 100644
index 0000000..3858160
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_next_glyph_f1.ttx.GSUB
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+            <Glyph value="g23"/>
+          </Coverage>
+          <!-- ChainSubRuleSetCount=2 -->
+          <ChainSubRuleSet index="0">
+            <!-- ChainSubRuleCount=1 -->
+            <ChainSubRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="g20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="g22"/>
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="g23"/>
+              <!-- SubstCount=2 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+              <SubstLookupRecord index="1">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </ChainSubRule>
+          </ChainSubRuleSet>
+          <ChainSubRuleSet index="1">
+            <!-- ChainSubRuleCount=1 -->
+            <ChainSubRule index="0">
+              <!-- BacktrackGlyphCount=0 -->
+              <!-- InputGlyphCount=1 -->
+              <!-- LookAheadGlyphCount=0 -->
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </ChainSubRule>
+          </ChainSubRuleSet>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f1.otf b/Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f1.otf
new file mode 100644
index 0000000..c770965
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f1.ttx.GSUB
new file mode 100644
index 0000000..d7dbd6b
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f1.ttx.GSUB
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <!-- ChainSubRuleSetCount=1 -->
+          <ChainSubRuleSet index="0">
+            <!-- ChainSubRuleCount=1 -->
+            <ChainSubRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="g20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="g22"/>
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="g23"/>
+              <!-- SubstCount=2 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+              <SubstLookupRecord index="1">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </ChainSubRule>
+          </ChainSubRuleSet>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f2.otf b/Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f2.otf
new file mode 100644
index 0000000..8999e3f
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f2.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f2.ttx.GSUB
new file mode 100644
index 0000000..e97a6b1
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f2.ttx.GSUB
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g22"/>
+          </Coverage>
+          <!-- ChainSubRuleSetCount=1 -->
+          <ChainSubRuleSet index="0">
+            <!-- ChainSubRuleCount=1 -->
+            <ChainSubRule index="0">
+              <!-- BacktrackGlyphCount=2 -->
+              <Backtrack index="0" value="g21"/>
+              <Backtrack index="1" value="g20"/>
+              <!-- InputGlyphCount=3 -->
+              <Input index="0" value="g23"/>
+              <Input index="1" value="g24"/>
+              <!-- LookAheadGlyphCount=2 -->
+              <LookAhead index="0" value="g25"/>
+              <LookAhead index="1" value="g26"/>
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </ChainSubRule>
+          </ChainSubRuleSet>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_successive_f1.otf b/Tests/ttLib/tables/data/aots/gsub_chaining1_successive_f1.otf
new file mode 100644
index 0000000..ad472ec
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_successive_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_successive_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining1_successive_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_successive_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining1_successive_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining1_successive_f1.ttx.GSUB
new file mode 100644
index 0000000..146dd2a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_successive_f1.ttx.GSUB
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <!-- ChainSubRuleSetCount=1 -->
+          <ChainSubRuleSet index="0">
+            <!-- ChainSubRuleCount=1 -->
+            <ChainSubRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="g25"/>
+              <!-- InputGlyphCount=4 -->
+              <Input index="0" value="g21"/>
+              <Input index="1" value="g22"/>
+              <Input index="2" value="g23"/>
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="g24"/>
+              <!-- SubstCount=2 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="1"/>
+              </SubstLookupRecord>
+              <SubstLookupRecord index="1">
+                <SequenceIndex value="2"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </ChainSubRule>
+          </ChainSubRuleSet>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f1.otf b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f1.otf
new file mode 100644
index 0000000..845c256
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f1.ttx.GSUB
new file mode 100644
index 0000000..ecd0a6b
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f1.ttx.GSUB
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+          </Coverage>
+          <BacktrackClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </BacktrackClassDef>
+          <InputClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </InputClassDef>
+          <LookAheadClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </LookAheadClassDef>
+          <!-- ChainSubClassSetCount=27 -->
+          <ChainSubClassSet index="0" empty="1"/>
+          <ChainSubClassSet index="1" empty="1"/>
+          <ChainSubClassSet index="2" empty="1"/>
+          <ChainSubClassSet index="3" empty="1"/>
+          <ChainSubClassSet index="4" empty="1"/>
+          <ChainSubClassSet index="5" empty="1"/>
+          <ChainSubClassSet index="6" empty="1"/>
+          <ChainSubClassSet index="7" empty="1"/>
+          <ChainSubClassSet index="8" empty="1"/>
+          <ChainSubClassSet index="9" empty="1"/>
+          <ChainSubClassSet index="10" empty="1"/>
+          <ChainSubClassSet index="11" empty="1"/>
+          <ChainSubClassSet index="12" empty="1"/>
+          <ChainSubClassSet index="13" empty="1"/>
+          <ChainSubClassSet index="14" empty="1"/>
+          <ChainSubClassSet index="15" empty="1"/>
+          <ChainSubClassSet index="16" empty="1"/>
+          <ChainSubClassSet index="17" empty="1"/>
+          <ChainSubClassSet index="18" empty="1"/>
+          <ChainSubClassSet index="19" empty="1"/>
+          <ChainSubClassSet index="20" empty="1"/>
+          <ChainSubClassSet index="21">
+            <!-- ChainSubClassRuleCount=1 -->
+            <ChainSubClassRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="22"/>
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="23"/>
+              <!-- SubstCount=0 -->
+            </ChainSubClassRule>
+          </ChainSubClassSet>
+          <ChainSubClassSet index="22" empty="1"/>
+          <ChainSubClassSet index="23" empty="1"/>
+          <ChainSubClassSet index="24" empty="1"/>
+          <ChainSubClassSet index="25" empty="1"/>
+          <ChainSubClassSet index="26" empty="1"/>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f2.otf b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f2.otf
new file mode 100644
index 0000000..af0ad1f
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f2.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f2.ttx.GSUB
new file mode 100644
index 0000000..076cb30
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f2.ttx.GSUB
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+          </Coverage>
+          <BacktrackClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </BacktrackClassDef>
+          <InputClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </InputClassDef>
+          <LookAheadClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </LookAheadClassDef>
+          <!-- ChainSubClassSetCount=27 -->
+          <ChainSubClassSet index="0" empty="1"/>
+          <ChainSubClassSet index="1" empty="1"/>
+          <ChainSubClassSet index="2" empty="1"/>
+          <ChainSubClassSet index="3" empty="1"/>
+          <ChainSubClassSet index="4" empty="1"/>
+          <ChainSubClassSet index="5" empty="1"/>
+          <ChainSubClassSet index="6" empty="1"/>
+          <ChainSubClassSet index="7" empty="1"/>
+          <ChainSubClassSet index="8" empty="1"/>
+          <ChainSubClassSet index="9" empty="1"/>
+          <ChainSubClassSet index="10" empty="1"/>
+          <ChainSubClassSet index="11" empty="1"/>
+          <ChainSubClassSet index="12" empty="1"/>
+          <ChainSubClassSet index="13" empty="1"/>
+          <ChainSubClassSet index="14" empty="1"/>
+          <ChainSubClassSet index="15" empty="1"/>
+          <ChainSubClassSet index="16" empty="1"/>
+          <ChainSubClassSet index="17" empty="1"/>
+          <ChainSubClassSet index="18" empty="1"/>
+          <ChainSubClassSet index="19" empty="1"/>
+          <ChainSubClassSet index="20" empty="1"/>
+          <ChainSubClassSet index="21">
+            <!-- ChainSubClassRuleCount=1 -->
+            <ChainSubClassRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="20"/>
+              <!-- InputGlyphCount=1 -->
+              <!-- LookAheadGlyphCount=2 -->
+              <LookAhead index="0" value="22"/>
+              <LookAhead index="1" value="23"/>
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </ChainSubClassRule>
+          </ChainSubClassSet>
+          <ChainSubClassSet index="22" empty="1"/>
+          <ChainSubClassSet index="23" empty="1"/>
+          <ChainSubClassSet index="24" empty="1"/>
+          <ChainSubClassSet index="25" empty="1"/>
+          <ChainSubClassSet index="26" empty="1"/>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f3.otf b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f3.otf
new file mode 100644
index 0000000..28679c8
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f3.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f3.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f3.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f3.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f3.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f3.ttx.GSUB
new file mode 100644
index 0000000..c6fc1dc
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f3.ttx.GSUB
@@ -0,0 +1,165 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+          </Coverage>
+          <BacktrackClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </BacktrackClassDef>
+          <InputClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </InputClassDef>
+          <LookAheadClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </LookAheadClassDef>
+          <!-- ChainSubClassSetCount=27 -->
+          <ChainSubClassSet index="0" empty="1"/>
+          <ChainSubClassSet index="1" empty="1"/>
+          <ChainSubClassSet index="2" empty="1"/>
+          <ChainSubClassSet index="3" empty="1"/>
+          <ChainSubClassSet index="4" empty="1"/>
+          <ChainSubClassSet index="5" empty="1"/>
+          <ChainSubClassSet index="6" empty="1"/>
+          <ChainSubClassSet index="7" empty="1"/>
+          <ChainSubClassSet index="8" empty="1"/>
+          <ChainSubClassSet index="9" empty="1"/>
+          <ChainSubClassSet index="10" empty="1"/>
+          <ChainSubClassSet index="11" empty="1"/>
+          <ChainSubClassSet index="12" empty="1"/>
+          <ChainSubClassSet index="13" empty="1"/>
+          <ChainSubClassSet index="14" empty="1"/>
+          <ChainSubClassSet index="15" empty="1"/>
+          <ChainSubClassSet index="16" empty="1"/>
+          <ChainSubClassSet index="17" empty="1"/>
+          <ChainSubClassSet index="18" empty="1"/>
+          <ChainSubClassSet index="19" empty="1"/>
+          <ChainSubClassSet index="20" empty="1"/>
+          <ChainSubClassSet index="21">
+            <!-- ChainSubClassRuleCount=1 -->
+            <ChainSubClassRule index="0">
+              <!-- BacktrackGlyphCount=0 -->
+              <!-- InputGlyphCount=1 -->
+              <!-- LookAheadGlyphCount=2 -->
+              <LookAhead index="0" value="22"/>
+              <LookAhead index="1" value="23"/>
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </ChainSubClassRule>
+          </ChainSubClassSet>
+          <ChainSubClassSet index="22" empty="1"/>
+          <ChainSubClassSet index="23" empty="1"/>
+          <ChainSubClassSet index="24" empty="1"/>
+          <ChainSubClassSet index="25" empty="1"/>
+          <ChainSubClassSet index="26" empty="1"/>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f4.otf b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f4.otf
new file mode 100644
index 0000000..14746c6
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f4.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f4.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f4.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f4.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f4.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f4.ttx.GSUB
new file mode 100644
index 0000000..4221c6c
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f4.ttx.GSUB
@@ -0,0 +1,165 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+          </Coverage>
+          <BacktrackClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </BacktrackClassDef>
+          <InputClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </InputClassDef>
+          <LookAheadClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </LookAheadClassDef>
+          <!-- ChainSubClassSetCount=27 -->
+          <ChainSubClassSet index="0" empty="1"/>
+          <ChainSubClassSet index="1" empty="1"/>
+          <ChainSubClassSet index="2" empty="1"/>
+          <ChainSubClassSet index="3" empty="1"/>
+          <ChainSubClassSet index="4" empty="1"/>
+          <ChainSubClassSet index="5" empty="1"/>
+          <ChainSubClassSet index="6" empty="1"/>
+          <ChainSubClassSet index="7" empty="1"/>
+          <ChainSubClassSet index="8" empty="1"/>
+          <ChainSubClassSet index="9" empty="1"/>
+          <ChainSubClassSet index="10" empty="1"/>
+          <ChainSubClassSet index="11" empty="1"/>
+          <ChainSubClassSet index="12" empty="1"/>
+          <ChainSubClassSet index="13" empty="1"/>
+          <ChainSubClassSet index="14" empty="1"/>
+          <ChainSubClassSet index="15" empty="1"/>
+          <ChainSubClassSet index="16" empty="1"/>
+          <ChainSubClassSet index="17" empty="1"/>
+          <ChainSubClassSet index="18" empty="1"/>
+          <ChainSubClassSet index="19" empty="1"/>
+          <ChainSubClassSet index="20" empty="1"/>
+          <ChainSubClassSet index="21" empty="1"/>
+          <ChainSubClassSet index="22">
+            <!-- ChainSubClassRuleCount=1 -->
+            <ChainSubClassRule index="0">
+              <!-- BacktrackGlyphCount=2 -->
+              <Backtrack index="0" value="21"/>
+              <Backtrack index="1" value="20"/>
+              <!-- InputGlyphCount=1 -->
+              <!-- LookAheadGlyphCount=0 -->
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </ChainSubClassRule>
+          </ChainSubClassSet>
+          <ChainSubClassSet index="23" empty="1"/>
+          <ChainSubClassSet index="24" empty="1"/>
+          <ChainSubClassSet index="25" empty="1"/>
+          <ChainSubClassSet index="26" empty="1"/>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_lookupflag_f1.otf b/Tests/ttLib/tables/data/aots/gsub_chaining2_lookupflag_f1.otf
new file mode 100644
index 0000000..2f4feed
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_lookupflag_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_lookupflag_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining2_lookupflag_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_lookupflag_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_lookupflag_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining2_lookupflag_f1.ttx.GSUB
new file mode 100644
index 0000000..eaccba0
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_lookupflag_f1.ttx.GSUB
@@ -0,0 +1,169 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+          </Coverage>
+          <BacktrackClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </BacktrackClassDef>
+          <InputClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </InputClassDef>
+          <LookAheadClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </LookAheadClassDef>
+          <!-- ChainSubClassSetCount=27 -->
+          <ChainSubClassSet index="0" empty="1"/>
+          <ChainSubClassSet index="1" empty="1"/>
+          <ChainSubClassSet index="2" empty="1"/>
+          <ChainSubClassSet index="3" empty="1"/>
+          <ChainSubClassSet index="4" empty="1"/>
+          <ChainSubClassSet index="5" empty="1"/>
+          <ChainSubClassSet index="6" empty="1"/>
+          <ChainSubClassSet index="7" empty="1"/>
+          <ChainSubClassSet index="8" empty="1"/>
+          <ChainSubClassSet index="9" empty="1"/>
+          <ChainSubClassSet index="10" empty="1"/>
+          <ChainSubClassSet index="11" empty="1"/>
+          <ChainSubClassSet index="12" empty="1"/>
+          <ChainSubClassSet index="13" empty="1"/>
+          <ChainSubClassSet index="14" empty="1"/>
+          <ChainSubClassSet index="15" empty="1"/>
+          <ChainSubClassSet index="16" empty="1"/>
+          <ChainSubClassSet index="17" empty="1"/>
+          <ChainSubClassSet index="18" empty="1"/>
+          <ChainSubClassSet index="19" empty="1"/>
+          <ChainSubClassSet index="20" empty="1"/>
+          <ChainSubClassSet index="21" empty="1"/>
+          <ChainSubClassSet index="22">
+            <!-- ChainSubClassRuleCount=1 -->
+            <ChainSubClassRule index="0">
+              <!-- BacktrackGlyphCount=2 -->
+              <Backtrack index="0" value="21"/>
+              <Backtrack index="1" value="20"/>
+              <!-- InputGlyphCount=3 -->
+              <Input index="0" value="23"/>
+              <Input index="1" value="24"/>
+              <!-- LookAheadGlyphCount=2 -->
+              <LookAhead index="0" value="25"/>
+              <LookAhead index="1" value="26"/>
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </ChainSubClassRule>
+          </ChainSubClassSet>
+          <ChainSubClassSet index="23" empty="1"/>
+          <ChainSubClassSet index="24" empty="1"/>
+          <ChainSubClassSet index="25" empty="1"/>
+          <ChainSubClassSet index="26" empty="1"/>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_multiple_subrules_f1.otf b/Tests/ttLib/tables/data/aots/gsub_chaining2_multiple_subrules_f1.otf
new file mode 100644
index 0000000..6edeb6b
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_multiple_subrules_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_multiple_subrules_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining2_multiple_subrules_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_multiple_subrules_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_multiple_subrules_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining2_multiple_subrules_f1.ttx.GSUB
new file mode 100644
index 0000000..c0447c2
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_multiple_subrules_f1.ttx.GSUB
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+          </Coverage>
+          <BacktrackClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </BacktrackClassDef>
+          <InputClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </InputClassDef>
+          <LookAheadClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </LookAheadClassDef>
+          <!-- ChainSubClassSetCount=27 -->
+          <ChainSubClassSet index="0" empty="1"/>
+          <ChainSubClassSet index="1" empty="1"/>
+          <ChainSubClassSet index="2" empty="1"/>
+          <ChainSubClassSet index="3" empty="1"/>
+          <ChainSubClassSet index="4" empty="1"/>
+          <ChainSubClassSet index="5" empty="1"/>
+          <ChainSubClassSet index="6" empty="1"/>
+          <ChainSubClassSet index="7" empty="1"/>
+          <ChainSubClassSet index="8" empty="1"/>
+          <ChainSubClassSet index="9" empty="1"/>
+          <ChainSubClassSet index="10" empty="1"/>
+          <ChainSubClassSet index="11" empty="1"/>
+          <ChainSubClassSet index="12" empty="1"/>
+          <ChainSubClassSet index="13" empty="1"/>
+          <ChainSubClassSet index="14" empty="1"/>
+          <ChainSubClassSet index="15" empty="1"/>
+          <ChainSubClassSet index="16" empty="1"/>
+          <ChainSubClassSet index="17" empty="1"/>
+          <ChainSubClassSet index="18" empty="1"/>
+          <ChainSubClassSet index="19" empty="1"/>
+          <ChainSubClassSet index="20" empty="1"/>
+          <ChainSubClassSet index="21">
+            <!-- ChainSubClassRuleCount=2 -->
+            <ChainSubClassRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="22"/>
+              <!-- LookAheadGlyphCount=2 -->
+              <LookAhead index="0" value="23"/>
+              <LookAhead index="1" value="24"/>
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </ChainSubClassRule>
+            <ChainSubClassRule index="1">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="22"/>
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="23"/>
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </ChainSubClassRule>
+          </ChainSubClassSet>
+          <ChainSubClassSet index="22" empty="1"/>
+          <ChainSubClassSet index="23" empty="1"/>
+          <ChainSubClassSet index="24" empty="1"/>
+          <ChainSubClassSet index="25" empty="1"/>
+          <ChainSubClassSet index="26" empty="1"/>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_multiple_subrules_f2.otf b/Tests/ttLib/tables/data/aots/gsub_chaining2_multiple_subrules_f2.otf
new file mode 100644
index 0000000..ae0298f
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_multiple_subrules_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_multiple_subrules_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining2_multiple_subrules_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_multiple_subrules_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_multiple_subrules_f2.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining2_multiple_subrules_f2.ttx.GSUB
new file mode 100644
index 0000000..6067dfd
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_multiple_subrules_f2.ttx.GSUB
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+          </Coverage>
+          <BacktrackClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </BacktrackClassDef>
+          <InputClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </InputClassDef>
+          <LookAheadClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </LookAheadClassDef>
+          <!-- ChainSubClassSetCount=27 -->
+          <ChainSubClassSet index="0" empty="1"/>
+          <ChainSubClassSet index="1" empty="1"/>
+          <ChainSubClassSet index="2" empty="1"/>
+          <ChainSubClassSet index="3" empty="1"/>
+          <ChainSubClassSet index="4" empty="1"/>
+          <ChainSubClassSet index="5" empty="1"/>
+          <ChainSubClassSet index="6" empty="1"/>
+          <ChainSubClassSet index="7" empty="1"/>
+          <ChainSubClassSet index="8" empty="1"/>
+          <ChainSubClassSet index="9" empty="1"/>
+          <ChainSubClassSet index="10" empty="1"/>
+          <ChainSubClassSet index="11" empty="1"/>
+          <ChainSubClassSet index="12" empty="1"/>
+          <ChainSubClassSet index="13" empty="1"/>
+          <ChainSubClassSet index="14" empty="1"/>
+          <ChainSubClassSet index="15" empty="1"/>
+          <ChainSubClassSet index="16" empty="1"/>
+          <ChainSubClassSet index="17" empty="1"/>
+          <ChainSubClassSet index="18" empty="1"/>
+          <ChainSubClassSet index="19" empty="1"/>
+          <ChainSubClassSet index="20" empty="1"/>
+          <ChainSubClassSet index="21">
+            <!-- ChainSubClassRuleCount=2 -->
+            <ChainSubClassRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="22"/>
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="23"/>
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </ChainSubClassRule>
+            <ChainSubClassRule index="1">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="22"/>
+              <!-- LookAheadGlyphCount=2 -->
+              <LookAhead index="0" value="23"/>
+              <LookAhead index="1" value="24"/>
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </ChainSubClassRule>
+          </ChainSubClassSet>
+          <ChainSubClassSet index="22" empty="1"/>
+          <ChainSubClassSet index="23" empty="1"/>
+          <ChainSubClassSet index="24" empty="1"/>
+          <ChainSubClassSet index="25" empty="1"/>
+          <ChainSubClassSet index="26" empty="1"/>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_next_glyph_f1.otf b/Tests/ttLib/tables/data/aots/gsub_chaining2_next_glyph_f1.otf
new file mode 100644
index 0000000..878666f
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_next_glyph_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_next_glyph_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining2_next_glyph_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_next_glyph_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_next_glyph_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining2_next_glyph_f1.ttx.GSUB
new file mode 100644
index 0000000..f3dcb64
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_next_glyph_f1.ttx.GSUB
@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+          </Coverage>
+          <BacktrackClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </BacktrackClassDef>
+          <InputClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </InputClassDef>
+          <LookAheadClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </LookAheadClassDef>
+          <!-- ChainSubClassSetCount=27 -->
+          <ChainSubClassSet index="0" empty="1"/>
+          <ChainSubClassSet index="1" empty="1"/>
+          <ChainSubClassSet index="2" empty="1"/>
+          <ChainSubClassSet index="3" empty="1"/>
+          <ChainSubClassSet index="4" empty="1"/>
+          <ChainSubClassSet index="5" empty="1"/>
+          <ChainSubClassSet index="6" empty="1"/>
+          <ChainSubClassSet index="7" empty="1"/>
+          <ChainSubClassSet index="8" empty="1"/>
+          <ChainSubClassSet index="9" empty="1"/>
+          <ChainSubClassSet index="10" empty="1"/>
+          <ChainSubClassSet index="11" empty="1"/>
+          <ChainSubClassSet index="12" empty="1"/>
+          <ChainSubClassSet index="13" empty="1"/>
+          <ChainSubClassSet index="14" empty="1"/>
+          <ChainSubClassSet index="15" empty="1"/>
+          <ChainSubClassSet index="16" empty="1"/>
+          <ChainSubClassSet index="17" empty="1"/>
+          <ChainSubClassSet index="18" empty="1"/>
+          <ChainSubClassSet index="19" empty="1"/>
+          <ChainSubClassSet index="20" empty="1"/>
+          <ChainSubClassSet index="21">
+            <!-- ChainSubClassRuleCount=1 -->
+            <ChainSubClassRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="22"/>
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="23"/>
+              <!-- SubstCount=2 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+              <SubstLookupRecord index="1">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </ChainSubClassRule>
+          </ChainSubClassSet>
+          <ChainSubClassSet index="22" empty="1"/>
+          <ChainSubClassSet index="23">
+            <!-- ChainSubClassRuleCount=1 -->
+            <ChainSubClassRule index="0">
+              <!-- BacktrackGlyphCount=0 -->
+              <!-- InputGlyphCount=1 -->
+              <!-- LookAheadGlyphCount=0 -->
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </ChainSubClassRule>
+          </ChainSubClassSet>
+          <ChainSubClassSet index="24" empty="1"/>
+          <ChainSubClassSet index="25" empty="1"/>
+          <ChainSubClassSet index="26" empty="1"/>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f1.otf b/Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f1.otf
new file mode 100644
index 0000000..e860930
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f1.ttx.GSUB
new file mode 100644
index 0000000..7997e3a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f1.ttx.GSUB
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+          </Coverage>
+          <BacktrackClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </BacktrackClassDef>
+          <InputClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </InputClassDef>
+          <LookAheadClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </LookAheadClassDef>
+          <!-- ChainSubClassSetCount=27 -->
+          <ChainSubClassSet index="0" empty="1"/>
+          <ChainSubClassSet index="1" empty="1"/>
+          <ChainSubClassSet index="2" empty="1"/>
+          <ChainSubClassSet index="3" empty="1"/>
+          <ChainSubClassSet index="4" empty="1"/>
+          <ChainSubClassSet index="5" empty="1"/>
+          <ChainSubClassSet index="6" empty="1"/>
+          <ChainSubClassSet index="7" empty="1"/>
+          <ChainSubClassSet index="8" empty="1"/>
+          <ChainSubClassSet index="9" empty="1"/>
+          <ChainSubClassSet index="10" empty="1"/>
+          <ChainSubClassSet index="11" empty="1"/>
+          <ChainSubClassSet index="12" empty="1"/>
+          <ChainSubClassSet index="13" empty="1"/>
+          <ChainSubClassSet index="14" empty="1"/>
+          <ChainSubClassSet index="15" empty="1"/>
+          <ChainSubClassSet index="16" empty="1"/>
+          <ChainSubClassSet index="17" empty="1"/>
+          <ChainSubClassSet index="18" empty="1"/>
+          <ChainSubClassSet index="19" empty="1"/>
+          <ChainSubClassSet index="20" empty="1"/>
+          <ChainSubClassSet index="21">
+            <!-- ChainSubClassRuleCount=1 -->
+            <ChainSubClassRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="20"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="22"/>
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="23"/>
+              <!-- SubstCount=2 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+              <SubstLookupRecord index="1">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </ChainSubClassRule>
+          </ChainSubClassSet>
+          <ChainSubClassSet index="22" empty="1"/>
+          <ChainSubClassSet index="23" empty="1"/>
+          <ChainSubClassSet index="24" empty="1"/>
+          <ChainSubClassSet index="25" empty="1"/>
+          <ChainSubClassSet index="26" empty="1"/>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f2.otf b/Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f2.otf
new file mode 100644
index 0000000..4fdde33
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f2.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f2.ttx.GSUB
new file mode 100644
index 0000000..bd3645a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f2.ttx.GSUB
@@ -0,0 +1,169 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+          </Coverage>
+          <BacktrackClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </BacktrackClassDef>
+          <InputClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </InputClassDef>
+          <LookAheadClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </LookAheadClassDef>
+          <!-- ChainSubClassSetCount=27 -->
+          <ChainSubClassSet index="0" empty="1"/>
+          <ChainSubClassSet index="1" empty="1"/>
+          <ChainSubClassSet index="2" empty="1"/>
+          <ChainSubClassSet index="3" empty="1"/>
+          <ChainSubClassSet index="4" empty="1"/>
+          <ChainSubClassSet index="5" empty="1"/>
+          <ChainSubClassSet index="6" empty="1"/>
+          <ChainSubClassSet index="7" empty="1"/>
+          <ChainSubClassSet index="8" empty="1"/>
+          <ChainSubClassSet index="9" empty="1"/>
+          <ChainSubClassSet index="10" empty="1"/>
+          <ChainSubClassSet index="11" empty="1"/>
+          <ChainSubClassSet index="12" empty="1"/>
+          <ChainSubClassSet index="13" empty="1"/>
+          <ChainSubClassSet index="14" empty="1"/>
+          <ChainSubClassSet index="15" empty="1"/>
+          <ChainSubClassSet index="16" empty="1"/>
+          <ChainSubClassSet index="17" empty="1"/>
+          <ChainSubClassSet index="18" empty="1"/>
+          <ChainSubClassSet index="19" empty="1"/>
+          <ChainSubClassSet index="20" empty="1"/>
+          <ChainSubClassSet index="21" empty="1"/>
+          <ChainSubClassSet index="22">
+            <!-- ChainSubClassRuleCount=1 -->
+            <ChainSubClassRule index="0">
+              <!-- BacktrackGlyphCount=2 -->
+              <Backtrack index="0" value="21"/>
+              <Backtrack index="1" value="20"/>
+              <!-- InputGlyphCount=3 -->
+              <Input index="0" value="23"/>
+              <Input index="1" value="24"/>
+              <!-- LookAheadGlyphCount=2 -->
+              <LookAhead index="0" value="25"/>
+              <LookAhead index="1" value="26"/>
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </ChainSubClassRule>
+          </ChainSubClassSet>
+          <ChainSubClassSet index="23" empty="1"/>
+          <ChainSubClassSet index="24" empty="1"/>
+          <ChainSubClassSet index="25" empty="1"/>
+          <ChainSubClassSet index="26" empty="1"/>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_successive_f1.otf b/Tests/ttLib/tables/data/aots/gsub_chaining2_successive_f1.otf
new file mode 100644
index 0000000..90f9f73
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_successive_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_successive_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining2_successive_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_successive_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining2_successive_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining2_successive_f1.ttx.GSUB
new file mode 100644
index 0000000..fd6ead4
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_successive_f1.ttx.GSUB
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g23"/>
+            <Glyph value="g24"/>
+            <Glyph value="g25"/>
+            <Glyph value="g26"/>
+          </Coverage>
+          <BacktrackClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </BacktrackClassDef>
+          <InputClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </InputClassDef>
+          <LookAheadClassDef Format="2">
+            <ClassDef glyph="g20" class="20"/>
+            <ClassDef glyph="g21" class="21"/>
+            <ClassDef glyph="g22" class="22"/>
+            <ClassDef glyph="g23" class="23"/>
+            <ClassDef glyph="g24" class="24"/>
+            <ClassDef glyph="g25" class="25"/>
+            <ClassDef glyph="g26" class="26"/>
+          </LookAheadClassDef>
+          <!-- ChainSubClassSetCount=27 -->
+          <ChainSubClassSet index="0" empty="1"/>
+          <ChainSubClassSet index="1" empty="1"/>
+          <ChainSubClassSet index="2" empty="1"/>
+          <ChainSubClassSet index="3" empty="1"/>
+          <ChainSubClassSet index="4" empty="1"/>
+          <ChainSubClassSet index="5" empty="1"/>
+          <ChainSubClassSet index="6" empty="1"/>
+          <ChainSubClassSet index="7" empty="1"/>
+          <ChainSubClassSet index="8" empty="1"/>
+          <ChainSubClassSet index="9" empty="1"/>
+          <ChainSubClassSet index="10" empty="1"/>
+          <ChainSubClassSet index="11" empty="1"/>
+          <ChainSubClassSet index="12" empty="1"/>
+          <ChainSubClassSet index="13" empty="1"/>
+          <ChainSubClassSet index="14" empty="1"/>
+          <ChainSubClassSet index="15" empty="1"/>
+          <ChainSubClassSet index="16" empty="1"/>
+          <ChainSubClassSet index="17" empty="1"/>
+          <ChainSubClassSet index="18" empty="1"/>
+          <ChainSubClassSet index="19" empty="1"/>
+          <ChainSubClassSet index="20">
+            <!-- ChainSubClassRuleCount=1 -->
+            <ChainSubClassRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="25"/>
+              <!-- InputGlyphCount=4 -->
+              <Input index="0" value="21"/>
+              <Input index="1" value="22"/>
+              <Input index="2" value="23"/>
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="24"/>
+              <!-- SubstCount=2 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="1"/>
+              </SubstLookupRecord>
+              <SubstLookupRecord index="1">
+                <SequenceIndex value="2"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </ChainSubClassRule>
+          </ChainSubClassSet>
+          <ChainSubClassSet index="21" empty="1"/>
+          <ChainSubClassSet index="22" empty="1"/>
+          <ChainSubClassSet index="23" empty="1"/>
+          <ChainSubClassSet index="24" empty="1"/>
+          <ChainSubClassSet index="25" empty="1"/>
+          <ChainSubClassSet index="26" empty="1"/>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f1.otf b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f1.otf
new file mode 100644
index 0000000..1aea8be
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f1.ttx.GSUB
new file mode 100644
index 0000000..579d6ce
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f1.ttx.GSUB
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0" Format="1">
+            <Glyph value="g20"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=2 -->
+          <InputCoverage index="0" Format="1">
+            <Glyph value="g21"/>
+          </InputCoverage>
+          <InputCoverage index="1" Format="1">
+            <Glyph value="g22"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0" Format="1">
+            <Glyph value="g23"/>
+          </LookAheadCoverage>
+          <!-- SubstCount=0 -->
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f2.otf b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f2.otf
new file mode 100644
index 0000000..97c92c3
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f2.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f2.ttx.GSUB
new file mode 100644
index 0000000..5098ceb
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f2.ttx.GSUB
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0" Format="1">
+            <Glyph value="g20"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0" Format="1">
+            <Glyph value="g21"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=2 -->
+          <LookAheadCoverage index="0" Format="1">
+            <Glyph value="g22"/>
+          </LookAheadCoverage>
+          <LookAheadCoverage index="1" Format="1">
+            <Glyph value="g23"/>
+          </LookAheadCoverage>
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f3.otf b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f3.otf
new file mode 100644
index 0000000..3b8513e
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f3.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f3.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f3.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f3.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f3.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f3.ttx.GSUB
new file mode 100644
index 0000000..3d6b966
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f3.ttx.GSUB
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0" Format="1">
+            <Glyph value="g21"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=2 -->
+          <LookAheadCoverage index="0" Format="1">
+            <Glyph value="g22"/>
+          </LookAheadCoverage>
+          <LookAheadCoverage index="1" Format="1">
+            <Glyph value="g23"/>
+          </LookAheadCoverage>
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f4.otf b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f4.otf
new file mode 100644
index 0000000..e81d00e
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f4.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f4.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f4.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f4.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f4.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f4.ttx.GSUB
new file mode 100644
index 0000000..9cf6009
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f4.ttx.GSUB
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=2 -->
+          <BacktrackCoverage index="0" Format="1">
+            <Glyph value="g21"/>
+          </BacktrackCoverage>
+          <BacktrackCoverage index="1" Format="1">
+            <Glyph value="g20"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0" Format="1">
+            <Glyph value="g22"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_lookupflag_f1.otf b/Tests/ttLib/tables/data/aots/gsub_chaining3_lookupflag_f1.otf
new file mode 100644
index 0000000..47c1007
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_lookupflag_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_lookupflag_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining3_lookupflag_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_lookupflag_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_lookupflag_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining3_lookupflag_f1.ttx.GSUB
new file mode 100644
index 0000000..9cc951e
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_lookupflag_f1.ttx.GSUB
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=2 -->
+          <BacktrackCoverage index="0" Format="1">
+            <Glyph value="g21"/>
+          </BacktrackCoverage>
+          <BacktrackCoverage index="1" Format="1">
+            <Glyph value="g20"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=3 -->
+          <InputCoverage index="0" Format="1">
+            <Glyph value="g22"/>
+          </InputCoverage>
+          <InputCoverage index="1" Format="1">
+            <Glyph value="g23"/>
+          </InputCoverage>
+          <InputCoverage index="2" Format="1">
+            <Glyph value="g24"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=2 -->
+          <LookAheadCoverage index="0" Format="1">
+            <Glyph value="g25"/>
+          </LookAheadCoverage>
+          <LookAheadCoverage index="1" Format="1">
+            <Glyph value="g26"/>
+          </LookAheadCoverage>
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="1"/>
+            <LookupListIndex value="0"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_next_glyph_f1.otf b/Tests/ttLib/tables/data/aots/gsub_chaining3_next_glyph_f1.otf
new file mode 100644
index 0000000..9160eda
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_next_glyph_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_next_glyph_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining3_next_glyph_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_next_glyph_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_next_glyph_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining3_next_glyph_f1.ttx.GSUB
new file mode 100644
index 0000000..00ff357
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_next_glyph_f1.ttx.GSUB
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0" Format="1">
+            <Glyph value="g22"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=2 -->
+          <InputCoverage index="0" Format="1">
+            <Glyph value="g21"/>
+          </InputCoverage>
+          <InputCoverage index="1" Format="1">
+            <Glyph value="g22"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0" Format="1">
+            <Glyph value="g21"/>
+          </LookAheadCoverage>
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f1.otf b/Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f1.otf
new file mode 100644
index 0000000..5982eb5
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f1.ttx.GSUB
new file mode 100644
index 0000000..6cbf5f9
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f1.ttx.GSUB
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0" Format="1">
+            <Glyph value="g20"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=2 -->
+          <InputCoverage index="0" Format="1">
+            <Glyph value="g21"/>
+          </InputCoverage>
+          <InputCoverage index="1" Format="1">
+            <Glyph value="g22"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0" Format="1">
+            <Glyph value="g23"/>
+          </LookAheadCoverage>
+          <!-- SubstCount=2 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </SubstLookupRecord>
+          <SubstLookupRecord index="1">
+            <SequenceIndex value="1"/>
+            <LookupListIndex value="0"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f2.otf b/Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f2.otf
new file mode 100644
index 0000000..359b126
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f2.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f2.ttx.GSUB
new file mode 100644
index 0000000..65fea5d
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f2.ttx.GSUB
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=2 -->
+          <BacktrackCoverage index="0" Format="1">
+            <Glyph value="g21"/>
+          </BacktrackCoverage>
+          <BacktrackCoverage index="1" Format="1">
+            <Glyph value="g20"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=3 -->
+          <InputCoverage index="0" Format="1">
+            <Glyph value="g22"/>
+          </InputCoverage>
+          <InputCoverage index="1" Format="1">
+            <Glyph value="g23"/>
+          </InputCoverage>
+          <InputCoverage index="2" Format="1">
+            <Glyph value="g24"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=2 -->
+          <LookAheadCoverage index="0" Format="1">
+            <Glyph value="g25"/>
+          </LookAheadCoverage>
+          <LookAheadCoverage index="1" Format="1">
+            <Glyph value="g26"/>
+          </LookAheadCoverage>
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_successive_f1.otf b/Tests/ttLib/tables/data/aots/gsub_chaining3_successive_f1.otf
new file mode 100644
index 0000000..ae39d92
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_successive_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_successive_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_chaining3_successive_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_successive_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_chaining3_successive_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_chaining3_successive_f1.ttx.GSUB
new file mode 100644
index 0000000..6e8918f
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_successive_f1.ttx.GSUB
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0" Format="1">
+            <Glyph value="g25"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=4 -->
+          <InputCoverage index="0" Format="1">
+            <Glyph value="g20"/>
+          </InputCoverage>
+          <InputCoverage index="1" Format="1">
+            <Glyph value="g21"/>
+          </InputCoverage>
+          <InputCoverage index="2" Format="1">
+            <Glyph value="g22"/>
+          </InputCoverage>
+          <InputCoverage index="3" Format="1">
+            <Glyph value="g23"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0" Format="1">
+            <Glyph value="g24"/>
+          </LookAheadCoverage>
+          <!-- SubstCount=2 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="1"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+          <SubstLookupRecord index="1">
+            <SequenceIndex value="2"/>
+            <LookupListIndex value="0"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_boundary_f1.otf b/Tests/ttLib/tables/data/aots/gsub_context1_boundary_f1.otf
new file mode 100644
index 0000000..0267cab
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_boundary_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_boundary_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context1_boundary_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_boundary_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_boundary_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context1_boundary_f1.ttx.GSUB
new file mode 100644
index 0000000..5ce2dbc
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_boundary_f1.ttx.GSUB
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <!-- SubRuleSetCount=1 -->
+          <SubRuleSet index="0">
+            <!-- SubRuleCount=1 -->
+            <SubRule index="0">
+              <!-- GlyphCount=2 -->
+              <!-- SubstCount=0 -->
+              <Input index="0" value="g20"/>
+            </SubRule>
+          </SubRuleSet>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_boundary_f2.otf b/Tests/ttLib/tables/data/aots/gsub_context1_boundary_f2.otf
new file mode 100644
index 0000000..24b1716
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_boundary_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_boundary_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context1_boundary_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_boundary_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_boundary_f2.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context1_boundary_f2.ttx.GSUB
new file mode 100644
index 0000000..f4df5df
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_boundary_f2.ttx.GSUB
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <!-- SubRuleSetCount=1 -->
+          <SubRuleSet index="0">
+            <!-- SubRuleCount=1 -->
+            <SubRule index="0">
+              <!-- GlyphCount=1 -->
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </SubRule>
+          </SubRuleSet>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_expansion_f1.otf b/Tests/ttLib/tables/data/aots/gsub_context1_expansion_f1.otf
new file mode 100644
index 0000000..c1dda80
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_expansion_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_expansion_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context1_expansion_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_expansion_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_expansion_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context1_expansion_f1.ttx.GSUB
new file mode 100644
index 0000000..e03d5c3
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_expansion_f1.ttx.GSUB
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <!-- SubRuleSetCount=1 -->
+          <SubRuleSet index="0">
+            <!-- SubRuleCount=1 -->
+            <SubRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- SubstCount=1 -->
+              <Input index="0" value="g21"/>
+              <Input index="1" value="g22"/>
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="3"/>
+              </SubstLookupRecord>
+            </SubRule>
+          </SubRuleSet>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f1.otf b/Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f1.otf
new file mode 100644
index 0000000..60676ab
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f1.ttx.GSUB
new file mode 100644
index 0000000..7f7dbae
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f1.ttx.GSUB
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <!-- SubRuleSetCount=1 -->
+          <SubRuleSet index="0">
+            <!-- SubRuleCount=1 -->
+            <SubRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- SubstCount=3 -->
+              <Input index="0" value="g21"/>
+              <Input index="1" value="g22"/>
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+              <SubstLookupRecord index="1">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+              <SubstLookupRecord index="2">
+                <SequenceIndex value="2"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </SubRule>
+          </SubRuleSet>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f2.otf b/Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f2.otf
new file mode 100644
index 0000000..bcb56e4
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f2.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f2.ttx.GSUB
new file mode 100644
index 0000000..921576c
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f2.ttx.GSUB
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <!-- SubRuleSetCount=1 -->
+          <SubRuleSet index="0">
+            <!-- SubRuleCount=1 -->
+            <SubRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- SubstCount=1 -->
+              <Input index="0" value="g21"/>
+              <Input index="1" value="g22"/>
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="2"/>
+              </SubstLookupRecord>
+            </SubRule>
+          </SubRuleSet>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_multiple_subrules_f1.otf b/Tests/ttLib/tables/data/aots/gsub_context1_multiple_subrules_f1.otf
new file mode 100644
index 0000000..52cd861
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_multiple_subrules_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_multiple_subrules_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context1_multiple_subrules_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_multiple_subrules_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_multiple_subrules_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context1_multiple_subrules_f1.ttx.GSUB
new file mode 100644
index 0000000..3bcb882
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_multiple_subrules_f1.ttx.GSUB
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <!-- SubRuleSetCount=1 -->
+          <SubRuleSet index="0">
+            <!-- SubRuleCount=2 -->
+            <SubRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- SubstCount=1 -->
+              <Input index="0" value="g21"/>
+              <Input index="1" value="g22"/>
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </SubRule>
+            <SubRule index="1">
+              <!-- GlyphCount=2 -->
+              <!-- SubstCount=1 -->
+              <Input index="0" value="g21"/>
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </SubRule>
+          </SubRuleSet>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_multiple_subrules_f2.otf b/Tests/ttLib/tables/data/aots/gsub_context1_multiple_subrules_f2.otf
new file mode 100644
index 0000000..891356a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_multiple_subrules_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_multiple_subrules_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context1_multiple_subrules_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_multiple_subrules_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_multiple_subrules_f2.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context1_multiple_subrules_f2.ttx.GSUB
new file mode 100644
index 0000000..04e9696
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_multiple_subrules_f2.ttx.GSUB
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <!-- SubRuleSetCount=1 -->
+          <SubRuleSet index="0">
+            <!-- SubRuleCount=2 -->
+            <SubRule index="0">
+              <!-- GlyphCount=2 -->
+              <!-- SubstCount=1 -->
+              <Input index="0" value="g21"/>
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </SubRule>
+            <SubRule index="1">
+              <!-- GlyphCount=3 -->
+              <!-- SubstCount=1 -->
+              <Input index="0" value="g21"/>
+              <Input index="1" value="g22"/>
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </SubRule>
+          </SubRuleSet>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_next_glyph_f1.otf b/Tests/ttLib/tables/data/aots/gsub_context1_next_glyph_f1.otf
new file mode 100644
index 0000000..2786ded
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_next_glyph_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_next_glyph_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context1_next_glyph_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_next_glyph_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_next_glyph_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context1_next_glyph_f1.ttx.GSUB
new file mode 100644
index 0000000..1276fb9
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_next_glyph_f1.ttx.GSUB
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <!-- SubRuleSetCount=1 -->
+          <SubRuleSet index="0">
+            <!-- SubRuleCount=1 -->
+            <SubRule index="0">
+              <!-- GlyphCount=2 -->
+              <!-- SubstCount=1 -->
+              <Input index="0" value="g20"/>
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </SubRule>
+          </SubRuleSet>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_simple_f1.otf b/Tests/ttLib/tables/data/aots/gsub_context1_simple_f1.otf
new file mode 100644
index 0000000..ebdaf22
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_simple_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_simple_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context1_simple_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_simple_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_simple_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context1_simple_f1.ttx.GSUB
new file mode 100644
index 0000000..4e135b5
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_simple_f1.ttx.GSUB
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <!-- SubRuleSetCount=1 -->
+          <SubRuleSet index="0">
+            <!-- SubRuleCount=1 -->
+            <SubRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- SubstCount=3 -->
+              <Input index="0" value="g21"/>
+              <Input index="1" value="g22"/>
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+              <SubstLookupRecord index="1">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+              <SubstLookupRecord index="2">
+                <SequenceIndex value="2"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </SubRule>
+          </SubRuleSet>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_simple_f2.otf b/Tests/ttLib/tables/data/aots/gsub_context1_simple_f2.otf
new file mode 100644
index 0000000..d4a3fbf
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_simple_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_simple_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context1_simple_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_simple_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_simple_f2.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context1_simple_f2.ttx.GSUB
new file mode 100644
index 0000000..787c216
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_simple_f2.ttx.GSUB
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <!-- SubRuleSetCount=1 -->
+          <SubRuleSet index="0">
+            <!-- SubRuleCount=1 -->
+            <SubRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- SubstCount=1 -->
+              <Input index="0" value="g20"/>
+              <Input index="1" value="g20"/>
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </SubRule>
+          </SubRuleSet>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_successive_f1.otf b/Tests/ttLib/tables/data/aots/gsub_context1_successive_f1.otf
new file mode 100644
index 0000000..ce2dce5
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_successive_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_successive_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context1_successive_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_successive_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context1_successive_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context1_successive_f1.ttx.GSUB
new file mode 100644
index 0000000..e0b1c54
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_successive_f1.ttx.GSUB
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <!-- SubRuleSetCount=1 -->
+          <SubRuleSet index="0">
+            <!-- SubRuleCount=1 -->
+            <SubRule index="0">
+              <!-- GlyphCount=4 -->
+              <!-- SubstCount=2 -->
+              <Input index="0" value="g21"/>
+              <Input index="1" value="g22"/>
+              <Input index="2" value="g23"/>
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="1"/>
+              </SubstLookupRecord>
+              <SubstLookupRecord index="1">
+                <SequenceIndex value="2"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </SubRule>
+          </SubRuleSet>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_boundary_f1.otf b/Tests/ttLib/tables/data/aots/gsub_context2_boundary_f1.otf
new file mode 100644
index 0000000..6f46192
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_boundary_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_boundary_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context2_boundary_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_boundary_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_boundary_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context2_boundary_f1.ttx.GSUB
new file mode 100644
index 0000000..705389b
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_boundary_f1.ttx.GSUB
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g20" class="1"/>
+          </ClassDef>
+          <!-- SubClassSetCount=2 -->
+          <SubClassSet index="0" empty="1"/>
+          <SubClassSet index="1">
+            <!-- SubClassRuleCount=1 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=2 -->
+              <!-- SubstCount=0 -->
+              <Class index="0" value="1"/>
+            </SubClassRule>
+          </SubClassSet>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_boundary_f2.otf b/Tests/ttLib/tables/data/aots/gsub_context2_boundary_f2.otf
new file mode 100644
index 0000000..ef19d87
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_boundary_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_boundary_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context2_boundary_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_boundary_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_boundary_f2.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context2_boundary_f2.ttx.GSUB
new file mode 100644
index 0000000..3a35d50
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_boundary_f2.ttx.GSUB
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g20" class="1"/>
+          </ClassDef>
+          <!-- SubClassSetCount=2 -->
+          <SubClassSet index="0" empty="1"/>
+          <SubClassSet index="1">
+            <!-- SubClassRuleCount=1 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=1 -->
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+          </SubClassSet>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_classes_f1.otf b/Tests/ttLib/tables/data/aots/gsub_context2_classes_f1.otf
new file mode 100644
index 0000000..8507760
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_classes_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_classes_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context2_classes_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_classes_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_classes_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context2_classes_f1.ttx.GSUB
new file mode 100644
index 0000000..6716203
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_classes_f1.ttx.GSUB
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g20" class="1"/>
+            <ClassDef glyph="g21" class="1"/>
+            <ClassDef glyph="g22" class="1"/>
+            <ClassDef glyph="g24" class="3"/>
+            <ClassDef glyph="g26" class="2"/>
+            <ClassDef glyph="g27" class="2"/>
+            <ClassDef glyph="g28" class="2"/>
+            <ClassDef glyph="g29" class="2"/>
+          </ClassDef>
+          <!-- SubClassSetCount=4 -->
+          <SubClassSet index="0" empty="1"/>
+          <SubClassSet index="1">
+            <!-- SubClassRuleCount=1 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=4 -->
+              <!-- SubstCount=1 -->
+              <Class index="0" value="2"/>
+              <Class index="1" value="2"/>
+              <Class index="2" value="3"/>
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+          </SubClassSet>
+          <SubClassSet index="2" empty="1"/>
+          <SubClassSet index="3" empty="1"/>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_classes_f2.otf b/Tests/ttLib/tables/data/aots/gsub_context2_classes_f2.otf
new file mode 100644
index 0000000..ceb74b2
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_classes_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_classes_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context2_classes_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_classes_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_classes_f2.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context2_classes_f2.ttx.GSUB
new file mode 100644
index 0000000..deeea52
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_classes_f2.ttx.GSUB
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+            <Glyph value="g21"/>
+            <Glyph value="g22"/>
+            <Glyph value="g24"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g20" class="1"/>
+            <ClassDef glyph="g21" class="1"/>
+            <ClassDef glyph="g22" class="1"/>
+            <ClassDef glyph="g24" class="3"/>
+            <ClassDef glyph="g26" class="2"/>
+            <ClassDef glyph="g27" class="2"/>
+            <ClassDef glyph="g28" class="2"/>
+            <ClassDef glyph="g29" class="2"/>
+          </ClassDef>
+          <!-- SubClassSetCount=4 -->
+          <SubClassSet index="0" empty="1"/>
+          <SubClassSet index="1">
+            <!-- SubClassRuleCount=1 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=4 -->
+              <!-- SubstCount=1 -->
+              <Class index="0" value="2"/>
+              <Class index="1" value="2"/>
+              <Class index="2" value="3"/>
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+          </SubClassSet>
+          <SubClassSet index="2" empty="1"/>
+          <SubClassSet index="3">
+            <!-- SubClassRuleCount=1 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=4 -->
+              <!-- SubstCount=1 -->
+              <Class index="0" value="3"/>
+              <Class index="1" value="2"/>
+              <Class index="2" value="1"/>
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="2"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+          </SubClassSet>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_expansion_f1.otf b/Tests/ttLib/tables/data/aots/gsub_context2_expansion_f1.otf
new file mode 100644
index 0000000..c12f0ac
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_expansion_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_expansion_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context2_expansion_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_expansion_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_expansion_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context2_expansion_f1.ttx.GSUB
new file mode 100644
index 0000000..3952840
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_expansion_f1.ttx.GSUB
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g20" class="1"/>
+            <ClassDef glyph="g21" class="2"/>
+            <ClassDef glyph="g22" class="3"/>
+          </ClassDef>
+          <!-- SubClassSetCount=4 -->
+          <SubClassSet index="0" empty="1"/>
+          <SubClassSet index="1">
+            <!-- SubClassRuleCount=1 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- SubstCount=1 -->
+              <Class index="0" value="2"/>
+              <Class index="1" value="3"/>
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="3"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+          </SubClassSet>
+          <SubClassSet index="2" empty="1"/>
+          <SubClassSet index="3" empty="1"/>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f1.otf b/Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f1.otf
new file mode 100644
index 0000000..a0fada4
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f1.ttx.GSUB
new file mode 100644
index 0000000..80a6237
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f1.ttx.GSUB
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g20" class="1"/>
+            <ClassDef glyph="g21" class="2"/>
+            <ClassDef glyph="g22" class="3"/>
+          </ClassDef>
+          <!-- SubClassSetCount=4 -->
+          <SubClassSet index="0" empty="1"/>
+          <SubClassSet index="1">
+            <!-- SubClassRuleCount=1 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- SubstCount=3 -->
+              <Class index="0" value="2"/>
+              <Class index="1" value="3"/>
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+              <SubstLookupRecord index="1">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+              <SubstLookupRecord index="2">
+                <SequenceIndex value="2"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+          </SubClassSet>
+          <SubClassSet index="2" empty="1"/>
+          <SubClassSet index="3" empty="1"/>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f2.otf b/Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f2.otf
new file mode 100644
index 0000000..cd40a5d
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f2.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f2.ttx.GSUB
new file mode 100644
index 0000000..715a1cd
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f2.ttx.GSUB
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g20" class="1"/>
+            <ClassDef glyph="g21" class="2"/>
+            <ClassDef glyph="g22" class="3"/>
+          </ClassDef>
+          <!-- SubClassSetCount=4 -->
+          <SubClassSet index="0" empty="1"/>
+          <SubClassSet index="1">
+            <!-- SubClassRuleCount=1 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- SubstCount=1 -->
+              <Class index="0" value="2"/>
+              <Class index="1" value="3"/>
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="2"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+          </SubClassSet>
+          <SubClassSet index="2" empty="1"/>
+          <SubClassSet index="3" empty="1"/>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_multiple_subrules_f1.otf b/Tests/ttLib/tables/data/aots/gsub_context2_multiple_subrules_f1.otf
new file mode 100644
index 0000000..53be20d
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_multiple_subrules_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_multiple_subrules_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context2_multiple_subrules_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_multiple_subrules_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_multiple_subrules_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context2_multiple_subrules_f1.ttx.GSUB
new file mode 100644
index 0000000..827db06
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_multiple_subrules_f1.ttx.GSUB
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g20" class="1"/>
+            <ClassDef glyph="g21" class="2"/>
+            <ClassDef glyph="g22" class="3"/>
+          </ClassDef>
+          <!-- SubClassSetCount=4 -->
+          <SubClassSet index="0" empty="1"/>
+          <SubClassSet index="1">
+            <!-- SubClassRuleCount=2 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- SubstCount=1 -->
+              <Class index="0" value="2"/>
+              <Class index="1" value="3"/>
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+            <SubClassRule index="1">
+              <!-- GlyphCount=2 -->
+              <!-- SubstCount=1 -->
+              <Class index="0" value="2"/>
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+          </SubClassSet>
+          <SubClassSet index="2" empty="1"/>
+          <SubClassSet index="3" empty="1"/>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_multiple_subrules_f2.otf b/Tests/ttLib/tables/data/aots/gsub_context2_multiple_subrules_f2.otf
new file mode 100644
index 0000000..6bcc0cb
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_multiple_subrules_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_multiple_subrules_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context2_multiple_subrules_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_multiple_subrules_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_multiple_subrules_f2.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context2_multiple_subrules_f2.ttx.GSUB
new file mode 100644
index 0000000..83d6af5
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_multiple_subrules_f2.ttx.GSUB
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g20" class="1"/>
+            <ClassDef glyph="g21" class="2"/>
+            <ClassDef glyph="g22" class="3"/>
+          </ClassDef>
+          <!-- SubClassSetCount=4 -->
+          <SubClassSet index="0" empty="1"/>
+          <SubClassSet index="1">
+            <!-- SubClassRuleCount=2 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=2 -->
+              <!-- SubstCount=1 -->
+              <Class index="0" value="2"/>
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+            <SubClassRule index="1">
+              <!-- GlyphCount=3 -->
+              <!-- SubstCount=1 -->
+              <Class index="0" value="2"/>
+              <Class index="1" value="3"/>
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+          </SubClassSet>
+          <SubClassSet index="2" empty="1"/>
+          <SubClassSet index="3" empty="1"/>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_next_glyph_f1.otf b/Tests/ttLib/tables/data/aots/gsub_context2_next_glyph_f1.otf
new file mode 100644
index 0000000..4ca4e48
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_next_glyph_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_next_glyph_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context2_next_glyph_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_next_glyph_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_next_glyph_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context2_next_glyph_f1.ttx.GSUB
new file mode 100644
index 0000000..09e9dc9
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_next_glyph_f1.ttx.GSUB
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g20" class="1"/>
+          </ClassDef>
+          <!-- SubClassSetCount=2 -->
+          <SubClassSet index="0" empty="1"/>
+          <SubClassSet index="1">
+            <!-- SubClassRuleCount=1 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=2 -->
+              <!-- SubstCount=1 -->
+              <Class index="0" value="1"/>
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+          </SubClassSet>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_simple_f1.otf b/Tests/ttLib/tables/data/aots/gsub_context2_simple_f1.otf
new file mode 100644
index 0000000..16aae9d
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_simple_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_simple_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context2_simple_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_simple_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_simple_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context2_simple_f1.ttx.GSUB
new file mode 100644
index 0000000..6bc8a46
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_simple_f1.ttx.GSUB
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g20" class="1"/>
+            <ClassDef glyph="g21" class="2"/>
+            <ClassDef glyph="g22" class="3"/>
+          </ClassDef>
+          <!-- SubClassSetCount=4 -->
+          <SubClassSet index="0" empty="1"/>
+          <SubClassSet index="1">
+            <!-- SubClassRuleCount=1 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- SubstCount=3 -->
+              <Class index="0" value="2"/>
+              <Class index="1" value="3"/>
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+              <SubstLookupRecord index="1">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+              <SubstLookupRecord index="2">
+                <SequenceIndex value="2"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+          </SubClassSet>
+          <SubClassSet index="2" empty="1"/>
+          <SubClassSet index="3" empty="1"/>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_simple_f2.otf b/Tests/ttLib/tables/data/aots/gsub_context2_simple_f2.otf
new file mode 100644
index 0000000..fc31262
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_simple_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_simple_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context2_simple_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_simple_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_simple_f2.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context2_simple_f2.ttx.GSUB
new file mode 100644
index 0000000..f65bf4d
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_simple_f2.ttx.GSUB
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g20" class="1"/>
+          </ClassDef>
+          <!-- SubClassSetCount=2 -->
+          <SubClassSet index="0" empty="1"/>
+          <SubClassSet index="1">
+            <!-- SubClassRuleCount=1 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- SubstCount=1 -->
+              <Class index="0" value="1"/>
+              <Class index="1" value="1"/>
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+          </SubClassSet>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_successive_f1.otf b/Tests/ttLib/tables/data/aots/gsub_context2_successive_f1.otf
new file mode 100644
index 0000000..cf1a89c
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_successive_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_successive_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context2_successive_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_successive_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context2_successive_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context2_successive_f1.ttx.GSUB
new file mode 100644
index 0000000..3c7e0f9
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_successive_f1.ttx.GSUB
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <ClassDef Format="2">
+            <ClassDef glyph="g20" class="1"/>
+            <ClassDef glyph="g21" class="2"/>
+            <ClassDef glyph="g22" class="3"/>
+            <ClassDef glyph="g23" class="4"/>
+          </ClassDef>
+          <!-- SubClassSetCount=5 -->
+          <SubClassSet index="0" empty="1"/>
+          <SubClassSet index="1">
+            <!-- SubClassRuleCount=1 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=4 -->
+              <!-- SubstCount=2 -->
+              <Class index="0" value="2"/>
+              <Class index="1" value="3"/>
+              <Class index="2" value="4"/>
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="1"/>
+              </SubstLookupRecord>
+              <SubstLookupRecord index="1">
+                <SequenceIndex value="2"/>
+                <LookupListIndex value="0"/>
+              </SubstLookupRecord>
+            </SubClassRule>
+          </SubClassSet>
+          <SubClassSet index="2" empty="1"/>
+          <SubClassSet index="3" empty="1"/>
+          <SubClassSet index="4" empty="1"/>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context3_boundary_f1.otf b/Tests/ttLib/tables/data/aots/gsub_context3_boundary_f1.otf
new file mode 100644
index 0000000..01cd29d
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_boundary_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context3_boundary_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context3_boundary_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_boundary_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context3_boundary_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context3_boundary_f1.ttx.GSUB
new file mode 100644
index 0000000..843e88d
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_boundary_f1.ttx.GSUB
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="3">
+          <!-- GlyphCount=2 -->
+          <!-- SubstCount=0 -->
+          <Coverage index="0" Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <Coverage index="1" Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context3_boundary_f2.otf b/Tests/ttLib/tables/data/aots/gsub_context3_boundary_f2.otf
new file mode 100644
index 0000000..6fa5f05a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_boundary_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context3_boundary_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context3_boundary_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_boundary_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context3_boundary_f2.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context3_boundary_f2.ttx.GSUB
new file mode 100644
index 0000000..cba8c31
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_boundary_f2.ttx.GSUB
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="3">
+          <!-- GlyphCount=1 -->
+          <!-- SubstCount=1 -->
+          <Coverage index="0" Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </SubstLookupRecord>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f1.otf b/Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f1.otf
new file mode 100644
index 0000000..94371b4
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f1.ttx.GSUB
new file mode 100644
index 0000000..d485a04
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f1.ttx.GSUB
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="3">
+          <!-- GlyphCount=3 -->
+          <!-- SubstCount=3 -->
+          <Coverage index="0" Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <Coverage index="1" Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <Coverage index="2" Format="1">
+            <Glyph value="g22"/>
+          </Coverage>
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </SubstLookupRecord>
+          <SubstLookupRecord index="1">
+            <SequenceIndex value="1"/>
+            <LookupListIndex value="0"/>
+          </SubstLookupRecord>
+          <SubstLookupRecord index="2">
+            <SequenceIndex value="2"/>
+            <LookupListIndex value="0"/>
+          </SubstLookupRecord>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f2.otf b/Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f2.otf
new file mode 100644
index 0000000..d8150df
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f2.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f2.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f2.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f2.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f2.ttx.GSUB
new file mode 100644
index 0000000..016350f
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f2.ttx.GSUB
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="3">
+          <!-- GlyphCount=3 -->
+          <!-- SubstCount=1 -->
+          <Coverage index="0" Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <Coverage index="1" Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <Coverage index="2" Format="1">
+            <Glyph value="g22"/>
+          </Coverage>
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="1"/>
+            <LookupListIndex value="2"/>
+          </SubstLookupRecord>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context3_next_glyph_f1.otf b/Tests/ttLib/tables/data/aots/gsub_context3_next_glyph_f1.otf
new file mode 100644
index 0000000..93533b8
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_next_glyph_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context3_next_glyph_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context3_next_glyph_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_next_glyph_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context3_next_glyph_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context3_next_glyph_f1.ttx.GSUB
new file mode 100644
index 0000000..10fcba5
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_next_glyph_f1.ttx.GSUB
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="3">
+          <!-- GlyphCount=2 -->
+          <!-- SubstCount=1 -->
+          <Coverage index="0" Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <Coverage index="1" Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </SubstLookupRecord>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context3_simple_f1.otf b/Tests/ttLib/tables/data/aots/gsub_context3_simple_f1.otf
new file mode 100644
index 0000000..a1cd98c
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_simple_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context3_simple_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context3_simple_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_simple_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context3_simple_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context3_simple_f1.ttx.GSUB
new file mode 100644
index 0000000..93f4bdd
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_simple_f1.ttx.GSUB
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="3">
+          <!-- GlyphCount=3 -->
+          <!-- SubstCount=3 -->
+          <Coverage index="0" Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <Coverage index="1" Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <Coverage index="2" Format="1">
+            <Glyph value="g22"/>
+          </Coverage>
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </SubstLookupRecord>
+          <SubstLookupRecord index="1">
+            <SequenceIndex value="1"/>
+            <LookupListIndex value="0"/>
+          </SubstLookupRecord>
+          <SubstLookupRecord index="2">
+            <SequenceIndex value="2"/>
+            <LookupListIndex value="0"/>
+          </SubstLookupRecord>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context3_successive_f1.otf b/Tests/ttLib/tables/data/aots/gsub_context3_successive_f1.otf
new file mode 100644
index 0000000..d8b3d5c
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_successive_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/gsub_context3_successive_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gsub_context3_successive_f1.ttx.GDEF
new file mode 100644
index 0000000..dba5550
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_successive_f1.ttx.GDEF
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g80" class="1"/>
+      <ClassDef glyph="g81" class="1"/>
+      <ClassDef glyph="g82" class="1"/>
+      <ClassDef glyph="g83" class="1"/>
+      <ClassDef glyph="g84" class="1"/>
+      <ClassDef glyph="g85" class="2"/>
+      <ClassDef glyph="g86" class="2"/>
+      <ClassDef glyph="g87" class="2"/>
+      <ClassDef glyph="g88" class="2"/>
+      <ClassDef glyph="g89" class="2"/>
+      <ClassDef glyph="g90" class="3"/>
+      <ClassDef glyph="g91" class="3"/>
+      <ClassDef glyph="g92" class="3"/>
+      <ClassDef glyph="g93" class="3"/>
+      <ClassDef glyph="g94" class="3"/>
+      <ClassDef glyph="g95" class="4"/>
+      <ClassDef glyph="g96" class="4"/>
+      <ClassDef glyph="g97" class="4"/>
+      <ClassDef glyph="g98" class="4"/>
+      <ClassDef glyph="g99" class="4"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/gsub_context3_successive_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub_context3_successive_f1.ttx.GSUB
new file mode 100644
index 0000000..73e3567
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_successive_f1.ttx.GSUB
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+          <Substitution in="g22" out="g62"/>
+          <Substitution in="g23" out="g63"/>
+          <Substitution in="g24" out="g64"/>
+          <Substitution in="g25" out="g65"/>
+          <Substitution in="g26" out="g66"/>
+          <Substitution in="g27" out="g67"/>
+          <Substitution in="g28" out="g68"/>
+          <Substitution in="g29" out="g69"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="g21" out="g61,g62,g63"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="3">
+          <!-- GlyphCount=4 -->
+          <!-- SubstCount=2 -->
+          <Coverage index="0" Format="1">
+            <Glyph value="g20"/>
+          </Coverage>
+          <Coverage index="1" Format="1">
+            <Glyph value="g21"/>
+          </Coverage>
+          <Coverage index="2" Format="1">
+            <Glyph value="g22"/>
+          </Coverage>
+          <Coverage index="3" Format="1">
+            <Glyph value="g23"/>
+          </Coverage>
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="1"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+          <SubstLookupRecord index="1">
+            <SequenceIndex value="2"/>
+            <LookupListIndex value="0"/>
+          </SubstLookupRecord>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/lookupflag_ignore_attach_f1.otf b/Tests/ttLib/tables/data/aots/lookupflag_ignore_attach_f1.otf
new file mode 100644
index 0000000..80651f1
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/lookupflag_ignore_attach_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/lookupflag_ignore_attach_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/lookupflag_ignore_attach_f1.ttx.GDEF
new file mode 100644
index 0000000..802351a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/lookupflag_ignore_attach_f1.ttx.GDEF
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g10" class="1"/>
+      <ClassDef glyph="g11" class="1"/>
+      <ClassDef glyph="g12" class="1"/>
+      <ClassDef glyph="g13" class="1"/>
+      <ClassDef glyph="g14" class="1"/>
+      <ClassDef glyph="g15" class="2"/>
+      <ClassDef glyph="g20" class="3"/>
+      <ClassDef glyph="g21" class="3"/>
+      <ClassDef glyph="g22" class="3"/>
+      <ClassDef glyph="g23" class="3"/>
+      <ClassDef glyph="g24" class="3"/>
+      <ClassDef glyph="g25" class="3"/>
+      <ClassDef glyph="g26" class="3"/>
+      <ClassDef glyph="g27" class="3"/>
+      <ClassDef glyph="g28" class="3"/>
+      <ClassDef glyph="g29" class="3"/>
+    </GlyphClassDef>
+    <MarkAttachClassDef Format="2">
+      <ClassDef glyph="g20" class="1"/>
+      <ClassDef glyph="g21" class="1"/>
+      <ClassDef glyph="g22" class="1"/>
+      <ClassDef glyph="g23" class="1"/>
+      <ClassDef glyph="g24" class="1"/>
+      <ClassDef glyph="g25" class="2"/>
+      <ClassDef glyph="g26" class="2"/>
+      <ClassDef glyph="g27" class="2"/>
+      <ClassDef glyph="g28" class="2"/>
+    </MarkAttachClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/lookupflag_ignore_attach_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/lookupflag_ignore_attach_f1.ttx.GSUB
new file mode 100644
index 0000000..f8064cb
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/lookupflag_ignore_attach_f1.ttx.GSUB
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="512"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g11">
+            <Ligature components="g13,g26" glyph="g15"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/lookupflag_ignore_base_f1.otf b/Tests/ttLib/tables/data/aots/lookupflag_ignore_base_f1.otf
new file mode 100644
index 0000000..3c242b0
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/lookupflag_ignore_base_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/lookupflag_ignore_base_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/lookupflag_ignore_base_f1.ttx.GDEF
new file mode 100644
index 0000000..2b0a686
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/lookupflag_ignore_base_f1.ttx.GDEF
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g24" class="1"/>
+      <ClassDef glyph="g25" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/lookupflag_ignore_base_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/lookupflag_ignore_base_f1.ttx.GSUB
new file mode 100644
index 0000000..0982ef6
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/lookupflag_ignore_base_f1.ttx.GSUB
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="2"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g18">
+            <Ligature components="g19,g20" glyph="g23"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/lookupflag_ignore_combination_f1.otf b/Tests/ttLib/tables/data/aots/lookupflag_ignore_combination_f1.otf
new file mode 100644
index 0000000..b88359a
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/lookupflag_ignore_combination_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/lookupflag_ignore_combination_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/lookupflag_ignore_combination_f1.ttx.GDEF
new file mode 100644
index 0000000..b67d75e
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/lookupflag_ignore_combination_f1.ttx.GDEF
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g24" class="1"/>
+      <ClassDef glyph="g25" class="1"/>
+      <ClassDef glyph="g26" class="1"/>
+      <ClassDef glyph="g27" class="2"/>
+      <ClassDef glyph="g28" class="2"/>
+      <ClassDef glyph="g29" class="2"/>
+      <ClassDef glyph="g30" class="3"/>
+      <ClassDef glyph="g31" class="3"/>
+      <ClassDef glyph="g32" class="3"/>
+    </GlyphClassDef>
+    <MarkAttachClassDef Format="2">
+      <ClassDef glyph="g25" class="1"/>
+      <ClassDef glyph="g26" class="2"/>
+      <ClassDef glyph="g28" class="1"/>
+      <ClassDef glyph="g29" class="2"/>
+      <ClassDef glyph="g31" class="1"/>
+      <ClassDef glyph="g32" class="2"/>
+      <ClassDef glyph="g34" class="1"/>
+      <ClassDef glyph="g35" class="2"/>
+    </MarkAttachClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/lookupflag_ignore_combination_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/lookupflag_ignore_combination_f1.ttx.GSUB
new file mode 100644
index 0000000..95047b3
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/lookupflag_ignore_combination_f1.ttx.GSUB
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="514"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g18">
+            <Ligature components="g19,g20" glyph="g23"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/lookupflag_ignore_ligatures_f1.otf b/Tests/ttLib/tables/data/aots/lookupflag_ignore_ligatures_f1.otf
new file mode 100644
index 0000000..1dc0c23
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/lookupflag_ignore_ligatures_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/lookupflag_ignore_ligatures_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/lookupflag_ignore_ligatures_f1.ttx.GDEF
new file mode 100644
index 0000000..19aa7aa
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/lookupflag_ignore_ligatures_f1.ttx.GDEF
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g24" class="1"/>
+      <ClassDef glyph="g25" class="1"/>
+      <ClassDef glyph="g26" class="2"/>
+      <ClassDef glyph="g27" class="2"/>
+      <ClassDef glyph="g28" class="3"/>
+      <ClassDef glyph="g29" class="3"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/lookupflag_ignore_ligatures_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/lookupflag_ignore_ligatures_f1.ttx.GSUB
new file mode 100644
index 0000000..3bc1e8d
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/lookupflag_ignore_ligatures_f1.ttx.GSUB
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="4"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g18">
+            <Ligature components="g19,g20" glyph="g23"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/lookupflag_ignore_marks_f1.otf b/Tests/ttLib/tables/data/aots/lookupflag_ignore_marks_f1.otf
new file mode 100644
index 0000000..aa429de
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/lookupflag_ignore_marks_f1.otf
Binary files differ
diff --git a/Tests/ttLib/tables/data/aots/lookupflag_ignore_marks_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/lookupflag_ignore_marks_f1.ttx.GDEF
new file mode 100644
index 0000000..19aa7aa
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/lookupflag_ignore_marks_f1.ttx.GDEF
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="2">
+      <ClassDef glyph="g24" class="1"/>
+      <ClassDef glyph="g25" class="1"/>
+      <ClassDef glyph="g26" class="2"/>
+      <ClassDef glyph="g27" class="2"/>
+      <ClassDef glyph="g28" class="3"/>
+      <ClassDef glyph="g29" class="3"/>
+    </GlyphClassDef>
+  </GDEF>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/lookupflag_ignore_marks_f1.ttx.GSUB b/Tests/ttLib/tables/data/aots/lookupflag_ignore_marks_f1.ttx.GSUB
new file mode 100644
index 0000000..28b4682
--- /dev/null
+++ b/Tests/ttLib/tables/data/aots/lookupflag_ignore_marks_f1.ttx.GSUB
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="g18">
+            <Ligature components="g19,g20" glyph="g23"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/graphite/graphite_tests.ttf b/Tests/ttLib/tables/data/graphite/graphite_tests.ttf
new file mode 100644
index 0000000..ab6a79b
--- /dev/null
+++ b/Tests/ttLib/tables/data/graphite/graphite_tests.ttf
Binary files differ
diff --git a/Tests/ttLib/tables/data/graphite/graphite_tests.ttx.Feat b/Tests/ttLib/tables/data/graphite/graphite_tests.ttx.Feat
new file mode 100644
index 0000000..9c23f4d
--- /dev/null
+++ b/Tests/ttLib/tables/data/graphite/graphite_tests.ttx.Feat
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00">
+
+  <Feat>
+    <version version="2.0"/>
+    <feature default="0" fid="1" flags="32768" label="258">
+    </feature>
+    <feature default="0" fid="nom " flags="32768" label="262">
+      <setting label="261" value="0"/>
+      <setting label="260" value="1"/>
+    </feature>
+    <feature default="0" fid="yesm" flags="32768" label="259">
+      <setting label="261" value="0"/>
+      <setting label="260" value="1"/>
+    </feature>
+  </Feat>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/graphite/graphite_tests.ttx.Glat b/Tests/ttLib/tables/data/graphite/graphite_tests.ttx.Glat
new file mode 100644
index 0000000..9788530
--- /dev/null
+++ b/Tests/ttLib/tables/data/graphite/graphite_tests.ttx.Glat
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00">
+
+  <Glat>
+    <version compressionScheme="0" version="1.0"/>
+    <glyph name=".notdef">
+      <attribute index="1" value="3"/>
+      <attribute index="2" value="30"/>
+    </glyph>
+    <glyph name="space">
+      <attribute index="1" value="3"/>
+      <attribute index="2" value="15"/>
+      <attribute index="3" value="9"/>
+    </glyph>
+    <glyph name="a">
+      <attribute index="1" value="2"/>
+      <attribute index="2" value="30"/>
+    </glyph>
+    <glyph name="b">
+      <attribute index="1" value="3"/>
+      <attribute index="2" value="30"/>
+    </glyph>
+    <glyph name="c">
+      <attribute index="1" value="1"/>
+      <attribute index="2" value="30"/>
+    </glyph>
+  </Glat>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/graphite/graphite_tests.ttx.Glat.setup b/Tests/ttLib/tables/data/graphite/graphite_tests.ttx.Glat.setup
new file mode 100644
index 0000000..8eac7d3
--- /dev/null
+++ b/Tests/ttLib/tables/data/graphite/graphite_tests.ttx.Glat.setup
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.15">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="space"/>
+    <GlyphID id="2" name="a"/>
+    <GlyphID id="3" name="b"/>
+    <GlyphID id="4" name="c"/>
+  </GlyphOrder>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="5"/>
+    <maxPoints value="0"/>
+    <maxContours value="0"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="2"/>
+    <maxTwilightPoints value="8"/>
+    <maxStorage value="64"/>
+    <maxFunctionDefs value="10"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="299"/>
+    <maxSizeOfInstructions value="207"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="space"/>
+      <map code="0x61" name="a"/>
+      <map code="0x62" name="b"/>
+      <map code="0x63" name="c"/>
+    </cmap_format_4>
+  </cmap>
+
+<!--
+  <Gloc>
+    <attributes number="8"/>
+  </Gloc>
+-->
+</ttFont>
+
diff --git a/Tests/ttLib/tables/data/graphite/graphite_tests.ttx.Silf b/Tests/ttLib/tables/data/graphite/graphite_tests.ttx.Silf
new file mode 100644
index 0000000..4b82d1c
--- /dev/null
+++ b/Tests/ttLib/tables/data/graphite/graphite_tests.ttx.Silf
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00">
+
+  <Silf>
+    <!-- Attributes starting with _ are informative only -->
+    <version compilerVersion="0" compressionScheme="0" version="2.0"/>
+    <silf>
+      <info direction="1" extraAscent="0" extraDescent="0" flags="4" lbGID="6" maxCompPerLig="0" maxGlyphID="7" numLigComp="0" numUserDefn="0"/>
+      <passindexes iBidi="255" iJust="1" iPos="1" iSubst="0"/>
+      <contexts maxPostContext="0" maxPreContext="0"/>
+      <attributes attCollisions="0" attrBreakWeight="2" attrDirectionality="3" attrMirroring="4" attrPseudo="0" attrSkipPasses="1"/>
+      <classes>
+        <linearClasses>
+          <linear _index="0">
+            c
+          </linear>
+        </linearClasses>
+        <nonLinearClasses>
+        </nonLinearClasses>
+      </classes>
+      <passes>
+        <pass _index="0">
+          <info collisionThreshold="0" flags="0" maxBackup="0" maxRuleContext="3" maxRuleLoop="5" maxRulePreContext="1" minRulePreContext="0"/>
+          <fsminfo numColumns="3" numRows="6" numSuccess="2" numTransitional="5"/>
+          <colmap>
+            .notdef=0 space=0 a=1 b=2 c=0
+          </colmap>
+          <staterulemap>
+            <state number="4" rules="1"/>
+            <state number="5" rules="0"/>
+          </staterulemap>
+          <rules>
+            <rule index="0" precontext="0" sortkey="2">
+              <action>
+                PUT_GLYPH_8BIT_OBS(0)
+                ASSOC(0, 1)
+                NEXT
+                DELETE
+                NEXT
+                RET_ZERO
+              </action>
+            </rule>
+            <rule index="1" precontext="1" sortkey="2">
+              <action>
+                PUT_GLYPH_8BIT_OBS(0)
+                NEXT
+                RET_ZERO
+              </action>
+            </rule>
+          </rules>
+          <fsm>
+            <starts>0 1</starts>
+            <row _i="0">1 1 2</row>
+            <row _i="1">0 3 0</row>
+            <row _i="2">0 4 0</row>
+            <row _i="3">0 0 5</row>
+            <row _i="4">0 0 5</row>
+          </fsm>
+        </pass>
+        <pass _index="1">
+          <info collisionThreshold="10" flags="0" maxBackup="0" maxRuleContext="2" maxRuleLoop="5" maxRulePreContext="0" minRulePreContext="0"/>
+          <fsminfo numColumns="2" numRows="3" numSuccess="1" numTransitional="2"/>
+          <colmap>
+            a=0 c=1
+          </colmap>
+          <staterulemap>
+            <state number="2" rules="0"/>
+          </staterulemap>
+          <rules>
+            <rule index="0" precontext="0" sortkey="2">
+              <action>
+                COPY_NEXT
+                PUT_COPY(0)
+                PUSH_BYTE(-1)
+                ATTR_SET_SLOT(2)
+                PUSH_BYTE(0)
+                ATTR_SET(17)
+                PUSH_GLYPH_ATTR_OBS(6, 0)
+                ATTR_SET(8)
+                PUSH_GLYPH_ATTR_OBS(7, 0)
+                ATTR_SET(9)
+                PUSH_ATT_TO_GATTR_OBS(6, 0)
+                ATTR_SET(3)
+                PUSH_ATT_TO_GATTR_OBS(7, 0)
+                ATTR_SET(4)
+                NEXT
+                RET_ZERO
+              </action>
+            </rule>
+          </rules>
+          <fsm>
+            <starts>0</starts>
+            <row _i="0">1 0</row>
+            <row _i="1">0 2</row>
+          </fsm>
+        </pass>
+      </passes>
+    </silf>
+  </Silf>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/graphite/graphite_tests.ttx.Silf.setup b/Tests/ttLib/tables/data/graphite/graphite_tests.ttx.Silf.setup
new file mode 100644
index 0000000..8eac7d3
--- /dev/null
+++ b/Tests/ttLib/tables/data/graphite/graphite_tests.ttx.Silf.setup
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.15">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="space"/>
+    <GlyphID id="2" name="a"/>
+    <GlyphID id="3" name="b"/>
+    <GlyphID id="4" name="c"/>
+  </GlyphOrder>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="5"/>
+    <maxPoints value="0"/>
+    <maxContours value="0"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="2"/>
+    <maxTwilightPoints value="8"/>
+    <maxStorage value="64"/>
+    <maxFunctionDefs value="10"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="299"/>
+    <maxSizeOfInstructions value="207"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="space"/>
+      <map code="0x61" name="a"/>
+      <map code="0x62" name="b"/>
+      <map code="0x63" name="c"/>
+    </cmap_format_4>
+  </cmap>
+
+<!--
+  <Gloc>
+    <attributes number="8"/>
+  </Gloc>
+-->
+</ttFont>
+
diff --git a/Tests/ttLib/tables/data/graphite/graphite_tests.ttx.Sill b/Tests/ttLib/tables/data/graphite/graphite_tests.ttx.Sill
new file mode 100644
index 0000000..4107059
--- /dev/null
+++ b/Tests/ttLib/tables/data/graphite/graphite_tests.ttx.Sill
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00">
+
+  <Sill>
+    <version version="1.0"/>
+    <lang name="fred">
+      <feature fid="yesm" val="1"/>
+      <feature fid="nom " val="0"/>
+    </lang>
+  </Sill>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/ttProgram.ttx b/Tests/ttLib/tables/data/ttProgram.ttx
new file mode 100644
index 0000000..7a99efd
--- /dev/null
+++ b/Tests/ttLib/tables/data/ttProgram.ttx
@@ -0,0 +1,1627 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<assembly>
+  NPUSHB[ ]	/* 59 values pushed */
+  58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34
+  33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9
+  8 7 6 5 4 3 2 1 0
+  FDEF[ ]	/* FunctionDefinition */
+    SVTCA[1]	/* SetFPVectorToAxis */
+    PUSHB[ ]	/* 1 value pushed */
+    24
+    RS[ ]	/* ReadStore */
+    IF[ ]	/* If */
+      RCVT[ ]	/* ReadCVT */
+      ROUND[10]	/* Round */
+      PUSHB[ ]	/* 1 value pushed */
+      25
+      RS[ ]	/* ReadStore */
+      ADD[ ]	/* Add */
+      PUSHB[ ]	/* 1 value pushed */
+      70
+      SWAP[ ]	/* SwapTopStack */
+      WCVTP[ ]	/* WriteCVTInPixels */
+      SWAP[ ]	/* SwapTopStack */
+      SRP0[ ]	/* SetRefPoint0 */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      70
+      FLIPOFF[ ]	/* SetAutoFlipOff */
+      MIRP[10000]	/* MoveIndirectRelPt */
+      FLIPON[ ]	/* SetAutoFlipOn */
+      MDAP[1]	/* MoveDirectAbsPt */
+      PUSHB[ ]	/* 1 value pushed */
+      0
+      SRP2[ ]	/* SetRefPoint2 */
+    ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      SWAP[ ]	/* SwapTopStack */
+      SRP1[ ]	/* SetRefPoint1 */
+      SHP[1]	/* ShiftPointByLastPoint */
+    EIF[ ]	/* EndIf */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    SVTCA[1]	/* SetFPVectorToAxis */
+    PUSHB[ ]	/* 1 value pushed */
+    24
+    RS[ ]	/* ReadStore */
+    IF[ ]	/* If */
+      PUSHB[ ]	/* 1 value pushed */
+      5
+      CALL[ ]	/* CallFunction */
+      PUSHB[ ]	/* 1 value pushed */
+      0
+      SZP0[ ]	/* SetZonePointer0 */
+      MPPEM[ ]	/* MeasurePixelPerEm */
+      PUSHB[ ]	/* 1 value pushed */
+      20
+      LT[ ]	/* LessThan */
+      IF[ ]	/* If */
+        PUSHB[ ]	/* 2 values pushed */
+        0 64
+        SHPIX[ ]	/* ShiftZoneByPixel */
+      EIF[ ]	/* EndIf */
+      PUSHB[ ]	/* 1 value pushed */
+      6
+      CALL[ ]	/* CallFunction */
+    ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      SWAP[ ]	/* SwapTopStack */
+      SRP1[ ]	/* SetRefPoint1 */
+      SHP[1]	/* ShiftPointByLastPoint */
+    EIF[ ]	/* EndIf */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    SVTCA[1]	/* SetFPVectorToAxis */
+    PUSHB[ ]	/* 1 value pushed */
+    24
+    RS[ ]	/* ReadStore */
+    IF[ ]	/* If */
+      FLIPOFF[ ]	/* SetAutoFlipOff */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      CINDEX[ ]	/* CopyXToTopStack */
+      SRP0[ ]	/* SetRefPoint0 */
+      MIRP[10010]	/* MoveIndirectRelPt */
+      POP[ ]	/* PopTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      0
+      SRP2[ ]	/* SetRefPoint2 */
+      FLIPON[ ]	/* SetAutoFlipOn */
+    ELSE[ ]	/* Else */
+      DUP[ ]	/* DuplicateTopStack */
+      RCVT[ ]	/* ReadCVT */
+      PUSHB[ ]	/* 1 value pushed */
+      4
+      CINDEX[ ]	/* CopyXToTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      4
+      CINDEX[ ]	/* CopyXToTopStack */
+      SWAP[ ]	/* SwapTopStack */
+      MD[1]	/* MeasureDistance */
+      SUB[ ]	/* Subtract */
+      ABS[ ]	/* Absolute */
+      PUSHB[ ]	/* 1 value pushed */
+      40
+      GT[ ]	/* GreaterThan */
+      IF[ ]	/* If */
+        POP[ ]	/* PopTopStack */
+        SWAP[ ]	/* SwapTopStack */
+        SRP0[ ]	/* SetRefPoint0 */
+        MDRP[10110]	/* MoveDirectRelPt */
+      ELSE[ ]	/* Else */
+        PUSHB[ ]	/* 1 value pushed */
+        3
+        CINDEX[ ]	/* CopyXToTopStack */
+        SRP0[ ]	/* SetRefPoint0 */
+        MIRP[10010]	/* MoveIndirectRelPt */
+        POP[ ]	/* PopTopStack */
+        PUSHB[ ]	/* 1 value pushed */
+        0
+        SRP2[ ]	/* SetRefPoint2 */
+      EIF[ ]	/* EndIf */
+    EIF[ ]	/* EndIf */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    PUSHB[ ]	/* 1 value pushed */
+    26
+    RS[ ]	/* ReadStore */
+    IF[ ]	/* If */
+      POP[ ]	/* PopTopStack */
+      POP[ ]	/* PopTopStack */
+    ELSE[ ]	/* Else */
+      PUSHB[ ]	/* 1 value pushed */
+      2
+      CINDEX[ ]	/* CopyXToTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      2
+      CINDEX[ ]	/* CopyXToTopStack */
+      MD[0]	/* MeasureDistance */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      CINDEX[ ]	/* CopyXToTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      CINDEX[ ]	/* CopyXToTopStack */
+      MD[1]	/* MeasureDistance */
+      SUB[ ]	/* Subtract */
+      DUP[ ]	/* DuplicateTopStack */
+      ABS[ ]	/* Absolute */
+      PUSHB[ ]	/* 1 value pushed */
+      16
+      LT[ ]	/* LessThan */
+      IF[ ]	/* If */
+        POP[ ]	/* PopTopStack */
+        POP[ ]	/* PopTopStack */
+        POP[ ]	/* PopTopStack */
+      ELSE[ ]	/* Else */
+        PUSHB[ ]	/* 1 value pushed */
+        3
+        CINDEX[ ]	/* CopyXToTopStack */
+        PUSHB[ ]	/* 1 value pushed */
+        3
+        CINDEX[ ]	/* CopyXToTopStack */
+        MD[0]	/* MeasureDistance */
+        PUSHB[ ]	/* 1 value pushed */
+        0
+        LT[ ]	/* LessThan */
+        IF[ ]	/* If */
+          PUSHB[ ]	/* 1 value pushed */
+          0
+          LT[ ]	/* LessThan */
+          IF[ ]	/* If */
+            PUSHW[ ]	/* 1 value pushed */
+            -30
+            SHPIX[ ]	/* ShiftZoneByPixel */
+            POP[ ]	/* PopTopStack */
+          ELSE[ ]	/* Else */
+            PUSHB[ ]	/* 1 value pushed */
+            16
+            SHPIX[ ]	/* ShiftZoneByPixel */
+            POP[ ]	/* PopTopStack */
+          EIF[ ]	/* EndIf */
+        ELSE[ ]	/* Else */
+          PUSHB[ ]	/* 1 value pushed */
+          0
+          GT[ ]	/* GreaterThan */
+          IF[ ]	/* If */
+            PUSHB[ ]	/* 1 value pushed */
+            30
+            SHPIX[ ]	/* ShiftZoneByPixel */
+            POP[ ]	/* PopTopStack */
+          ELSE[ ]	/* Else */
+            PUSHW[ ]	/* 1 value pushed */
+            -16
+            SHPIX[ ]	/* ShiftZoneByPixel */
+            POP[ ]	/* PopTopStack */
+          EIF[ ]	/* EndIf */
+        EIF[ ]	/* EndIf */
+      EIF[ ]	/* EndIf */
+    EIF[ ]	/* EndIf */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    SVTCA[1]	/* SetFPVectorToAxis */
+    PUSHB[ ]	/* 1 value pushed */
+    24
+    RS[ ]	/* ReadStore */
+    IF[ ]	/* If */
+      PUSHB[ ]	/* 1 value pushed */
+      5
+      CALL[ ]	/* CallFunction */
+      PUSHB[ ]	/* 1 value pushed */
+      0
+      SZP0[ ]	/* SetZonePointer0 */
+      MPPEM[ ]	/* MeasurePixelPerEm */
+      PUSHB[ ]	/* 1 value pushed */
+      20
+      LT[ ]	/* LessThan */
+      IF[ ]	/* If */
+        PUSHW[ ]	/* 2 values pushed */
+        0 -64
+        SHPIX[ ]	/* ShiftZoneByPixel */
+      EIF[ ]	/* EndIf */
+      PUSHB[ ]	/* 1 value pushed */
+      6
+      CALL[ ]	/* CallFunction */
+    ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      SWAP[ ]	/* SwapTopStack */
+      SRP1[ ]	/* SetRefPoint1 */
+      SHP[1]	/* ShiftPointByLastPoint */
+    EIF[ ]	/* EndIf */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    FLIPOFF[ ]	/* SetAutoFlipOff */
+    SVTCA[1]	/* SetFPVectorToAxis */
+    ROLL[ ]	/* RollTopThreeStack */
+    SRP0[ ]	/* SetRefPoint0 */
+    PUSHB[ ]	/* 2 values pushed */
+    70 25
+    RS[ ]	/* ReadStore */
+    WCVTP[ ]	/* WriteCVTInPixels */
+    PUSHB[ ]	/* 1 value pushed */
+    0
+    SZP1[ ]	/* SetZonePointer1 */
+    PUSHB[ ]	/* 2 values pushed */
+    0 70
+    MIRP[00010]	/* MoveIndirectRelPt */
+    PUSHB[ ]	/* 1 value pushed */
+    0
+    SZP2[ ]	/* SetZonePointer2 */
+    PUSHW[ ]	/* 2 values pushed */
+    0 -16
+    SHPIX[ ]	/* ShiftZoneByPixel */
+    SVTCA[0]	/* SetFPVectorToAxis */
+    PUSHB[ ]	/* 1 value pushed */
+    0
+    ALIGNRP[ ]	/* AlignRelativePt */
+    PUSHB[ ]	/* 1 value pushed */
+    40
+    CALL[ ]	/* CallFunction */
+    PUSHB[ ]	/* 1 value pushed */
+    2
+    CINDEX[ ]	/* CopyXToTopStack */
+    SRP0[ ]	/* SetRefPoint0 */
+    PUSHB[ ]	/* 1 value pushed */
+    0
+    ALIGNRP[ ]	/* AlignRelativePt */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    SVTCA[1]	/* SetFPVectorToAxis */
+    RTG[ ]	/* RoundToGrid */
+    PUSHB[ ]	/* 1 value pushed */
+    0
+    MDAP[1]	/* MoveDirectAbsPt */
+    PUSHB[ ]	/* 1 value pushed */
+    1
+    SZP1[ ]	/* SetZonePointer1 */
+    MIRP[10010]	/* MoveIndirectRelPt */
+    PUSHB[ ]	/* 1 value pushed */
+    1
+    SZP0[ ]	/* SetZonePointer0 */
+    PUSHB[ ]	/* 1 value pushed */
+    1
+    SZP2[ ]	/* SetZonePointer2 */
+    FLIPON[ ]	/* SetAutoFlipOn */
+    PUSHB[ ]	/* 1 value pushed */
+    0
+    SRP2[ ]	/* SetRefPoint2 */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    SVTCA[1]	/* SetFPVectorToAxis */
+    PUSHB[ ]	/* 1 value pushed */
+    24
+    RS[ ]	/* ReadStore */
+    IF[ ]	/* If */
+      PUSHB[ ]	/* 1 value pushed */
+      5
+      CALL[ ]	/* CallFunction */
+      PUSHB[ ]	/* 1 value pushed */
+      0
+      SZP0[ ]	/* SetZonePointer0 */
+      PUSHW[ ]	/* 2 values pushed */
+      0 -32
+      SHPIX[ ]	/* ShiftZoneByPixel */
+      PUSHB[ ]	/* 1 value pushed */
+      6
+      CALL[ ]	/* CallFunction */
+    ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      SWAP[ ]	/* SwapTopStack */
+      SRP1[ ]	/* SetRefPoint1 */
+      SHP[1]	/* ShiftPointByLastPoint */
+    EIF[ ]	/* EndIf */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    SVTCA[1]	/* SetFPVectorToAxis */
+    PUSHB[ ]	/* 1 value pushed */
+    24
+    RS[ ]	/* ReadStore */
+    IF[ ]	/* If */
+      RCVT[ ]	/* ReadCVT */
+      ABS[ ]	/* Absolute */
+      ROUND[10]	/* Round */
+      SWAP[ ]	/* SwapTopStack */
+      RCVT[ ]	/* ReadCVT */
+      ABS[ ]	/* Absolute */
+      ROUND[01]	/* Round */
+      PUSHB[ ]	/* 1 value pushed */
+      25
+      RS[ ]	/* ReadStore */
+      ABS[ ]	/* Absolute */
+      ADD[ ]	/* Add */
+      ADD[ ]	/* Add */
+      PUSHB[ ]	/* 1 value pushed */
+      70
+      SWAP[ ]	/* SwapTopStack */
+      WCVTP[ ]	/* WriteCVTInPixels */
+      SWAP[ ]	/* SwapTopStack */
+      SRP0[ ]	/* SetRefPoint0 */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      70
+      MIRP[10000]	/* MoveIndirectRelPt */
+      MDAP[1]	/* MoveDirectAbsPt */
+      PUSHB[ ]	/* 1 value pushed */
+      0
+      SRP2[ ]	/* SetRefPoint2 */
+    ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      POP[ ]	/* PopTopStack */
+      DUP[ ]	/* DuplicateTopStack */
+      ROLL[ ]	/* RollTopThreeStack */
+      DUP[ ]	/* DuplicateTopStack */
+      ROLL[ ]	/* RollTopThreeStack */
+      GT[ ]	/* GreaterThan */
+      IF[ ]	/* If */
+        SRP1[ ]	/* SetRefPoint1 */
+        SHP[1]	/* ShiftPointByLastPoint */
+      ELSE[ ]	/* Else */
+        POP[ ]	/* PopTopStack */
+        POP[ ]	/* PopTopStack */
+      EIF[ ]	/* EndIf */
+    EIF[ ]	/* EndIf */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    SVTCA[1]	/* SetFPVectorToAxis */
+    PUSHB[ ]	/* 2 values pushed */
+    11 10
+    RS[ ]	/* ReadStore */
+    SWAP[ ]	/* SwapTopStack */
+    RS[ ]	/* ReadStore */
+    NEG[ ]	/* Negate */
+    SPVFS[ ]	/* SetPVectorFromStack */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    SVTCA[0]	/* SetFPVectorToAxis */
+    PUSHB[ ]	/* 2 values pushed */
+    10 11
+    RS[ ]	/* ReadStore */
+    SWAP[ ]	/* SwapTopStack */
+    RS[ ]	/* ReadStore */
+    SFVFS[ ]	/* SetFVectorFromStack */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    SVTCA[0]	/* SetFPVectorToAxis */
+    PUSHB[ ]	/* 1 value pushed */
+    70
+    SWAP[ ]	/* SwapTopStack */
+    WCVTF[ ]	/* WriteCVTInFUnits */
+    PUSHB[ ]	/* 2 values pushed */
+    1 70
+    MIAP[0]	/* MoveIndirectAbsPt */
+    SVTCA[1]	/* SetFPVectorToAxis */
+    PUSHB[ ]	/* 1 value pushed */
+    70
+    SWAP[ ]	/* SwapTopStack */
+    WCVTF[ ]	/* WriteCVTInFUnits */
+    PUSHB[ ]	/* 2 values pushed */
+    2 70
+    RCVT[ ]	/* ReadCVT */
+    MSIRP[0]	/* MoveStackIndirRelPt */
+    PUSHB[ ]	/* 2 values pushed */
+    2 0
+    SFVTL[0]	/* SetFVectorToLine */
+    GFV[ ]	/* GetFVector */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    PUSHB[ ]	/* 1 value pushed */
+    18
+    CALL[ ]	/* CallFunction */
+    PUSHB[ ]	/* 1 value pushed */
+    2
+    CINDEX[ ]	/* CopyXToTopStack */
+    RCVT[ ]	/* ReadCVT */
+    PUSHB[ ]	/* 1 value pushed */
+    2
+    CINDEX[ ]	/* CopyXToTopStack */
+    RCVT[ ]	/* ReadCVT */
+    ROUND[10]	/* Round */
+    PUSHB[ ]	/* 1 value pushed */
+    64
+    MAX[ ]	/* Maximum */
+    ADD[ ]	/* Add */
+    PUSHB[ ]	/* 1 value pushed */
+    2
+    CINDEX[ ]	/* CopyXToTopStack */
+    SWAP[ ]	/* SwapTopStack */
+    WCVTP[ ]	/* WriteCVTInPixels */
+    POP[ ]	/* PopTopStack */
+    POP[ ]	/* PopTopStack */
+    POP[ ]	/* PopTopStack */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    PUSHB[ ]	/* 1 value pushed */
+    19
+    CALL[ ]	/* CallFunction */
+    PUSHB[ ]	/* 1 value pushed */
+    2
+    CINDEX[ ]	/* CopyXToTopStack */
+    RCVT[ ]	/* ReadCVT */
+    PUSHB[ ]	/* 1 value pushed */
+    2
+    CINDEX[ ]	/* CopyXToTopStack */
+    RCVT[ ]	/* ReadCVT */
+    ROUND[10]	/* Round */
+    PUSHW[ ]	/* 1 value pushed */
+    -64
+    MIN[ ]	/* Minimum */
+    ADD[ ]	/* Add */
+    PUSHB[ ]	/* 1 value pushed */
+    2
+    CINDEX[ ]	/* CopyXToTopStack */
+    SWAP[ ]	/* SwapTopStack */
+    WCVTP[ ]	/* WriteCVTInPixels */
+    POP[ ]	/* PopTopStack */
+    POP[ ]	/* PopTopStack */
+    POP[ ]	/* PopTopStack */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    PUSHB[ ]	/* 1 value pushed */
+    0
+    PUSHB[ ]	/* 1 value pushed */
+    18
+    CALL[ ]	/* CallFunction */
+    POP[ ]	/* PopTopStack */
+    POP[ ]	/* PopTopStack */
+    POP[ ]	/* PopTopStack */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    PUSHB[ ]	/* 1 value pushed */
+    0
+    PUSHB[ ]	/* 1 value pushed */
+    19
+    CALL[ ]	/* CallFunction */
+    POP[ ]	/* PopTopStack */
+    POP[ ]	/* PopTopStack */
+    POP[ ]	/* PopTopStack */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    SVTCA[1]	/* SetFPVectorToAxis */
+    PUSHB[ ]	/* 1 value pushed */
+    6
+    RS[ ]	/* ReadStore */
+    PUSHB[ ]	/* 1 value pushed */
+    7
+    RS[ ]	/* ReadStore */
+    NEG[ ]	/* Negate */
+    SPVFS[ ]	/* SetPVectorFromStack */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    DUP[ ]	/* DuplicateTopStack */
+    ROUND[01]	/* Round */
+    PUSHB[ ]	/* 1 value pushed */
+    64
+    SUB[ ]	/* Subtract */
+    PUSHB[ ]	/* 1 value pushed */
+    0
+    MAX[ ]	/* Maximum */
+    DUP[ ]	/* DuplicateTopStack */
+    PUSHB[ ]	/* 2 values pushed */
+    44 192
+    ROLL[ ]	/* RollTopThreeStack */
+    MIN[ ]	/* Minimum */
+    PUSHW[ ]	/* 1 value pushed */
+    4096
+    DIV[ ]	/* Divide */
+    ADD[ ]	/* Add */
+    CALL[ ]	/* CallFunction */
+    GPV[ ]	/* GetPVector */
+    ABS[ ]	/* Absolute */
+    SWAP[ ]	/* SwapTopStack */
+    ABS[ ]	/* Absolute */
+    SUB[ ]	/* Subtract */
+    NOT[ ]	/* LogicalNot */
+    IF[ ]	/* If */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      SUB[ ]	/* Subtract */
+    EIF[ ]	/* EndIf */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    PUSHB[ ]	/* 2 values pushed */
+    0 3
+    CINDEX[ ]	/* CopyXToTopStack */
+    RCVT[ ]	/* ReadCVT */
+    ROUND[00]	/* Round */
+    EQ[ ]	/* Equal */
+    PUSHB[ ]	/* 1 value pushed */
+    28
+    MPPEM[ ]	/* MeasurePixelPerEm */
+    LT[ ]	/* LessThan */
+    AND[ ]	/* LogicalAnd */
+    IF[ ]	/* If */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      CINDEX[ ]	/* CopyXToTopStack */
+      RCVT[ ]	/* ReadCVT */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      CINDEX[ ]	/* CopyXToTopStack */
+      RCVT[ ]	/* ReadCVT */
+      ADD[ ]	/* Add */
+      ROUND[00]	/* Round */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      4
+      CINDEX[ ]	/* CopyXToTopStack */
+      SWAP[ ]	/* SwapTopStack */
+      WCVTP[ ]	/* WriteCVTInPixels */
+      PUSHB[ ]	/* 1 value pushed */
+      4
+      CINDEX[ ]	/* CopyXToTopStack */
+      SWAP[ ]	/* SwapTopStack */
+      WCVTP[ ]	/* WriteCVTInPixels */
+    ELSE[ ]	/* Else */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      CINDEX[ ]	/* CopyXToTopStack */
+      DUP[ ]	/* DuplicateTopStack */
+      RCVT[ ]	/* ReadCVT */
+      ROUND[00]	/* Round */
+      DUP[ ]	/* DuplicateTopStack */
+      ROLL[ ]	/* RollTopThreeStack */
+      SWAP[ ]	/* SwapTopStack */
+      WCVTP[ ]	/* WriteCVTInPixels */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      CINDEX[ ]	/* CopyXToTopStack */
+      RCVT[ ]	/* ReadCVT */
+      ROUND[00]	/* Round */
+      ADD[ ]	/* Add */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      CINDEX[ ]	/* CopyXToTopStack */
+      SWAP[ ]	/* SwapTopStack */
+      WCVTP[ ]	/* WriteCVTInPixels */
+    EIF[ ]	/* EndIf */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    PUSHB[ ]	/* 1 value pushed */
+    3
+    CINDEX[ ]	/* CopyXToTopStack */
+    DUP[ ]	/* DuplicateTopStack */
+    RCVT[ ]	/* ReadCVT */
+    ROUND[00]	/* Round */
+    DUP[ ]	/* DuplicateTopStack */
+    ROLL[ ]	/* RollTopThreeStack */
+    SWAP[ ]	/* SwapTopStack */
+    WCVTP[ ]	/* WriteCVTInPixels */
+    PUSHB[ ]	/* 1 value pushed */
+    3
+    CINDEX[ ]	/* CopyXToTopStack */
+    RCVT[ ]	/* ReadCVT */
+    ABS[ ]	/* Absolute */
+    ROUND[00]	/* Round */
+    NEG[ ]	/* Negate */
+    ADD[ ]	/* Add */
+    PUSHB[ ]	/* 1 value pushed */
+    4
+    CINDEX[ ]	/* CopyXToTopStack */
+    PUSHB[ ]	/* 1 value pushed */
+    1
+    ADD[ ]	/* Add */
+    SWAP[ ]	/* SwapTopStack */
+    WCVTP[ ]	/* WriteCVTInPixels */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    PUSHB[ ]	/* 1 value pushed */
+    9
+    RS[ ]	/* ReadStore */
+    IF[ ]	/* If */
+      SDPVTL[1]	/* SetDualPVectorToLine */
+      POP[ ]	/* PopTopStack */
+      MDRP[00000]	/* MoveDirectRelPt */
+    ELSE[ ]	/* Else */
+      PUSHB[ ]	/* 1 value pushed */
+      18
+      RS[ ]	/* ReadStore */
+      IF[ ]	/* If */
+        SDPVTL[1]	/* SetDualPVectorToLine */
+        RCVT[ ]	/* ReadCVT */
+        PUSHB[ ]	/* 1 value pushed */
+        17
+        CALL[ ]	/* CallFunction */
+        PUSHB[ ]	/* 1 value pushed */
+        71
+        SWAP[ ]	/* SwapTopStack */
+        WCVTP[ ]	/* WriteCVTInPixels */
+        PUSHB[ ]	/* 1 value pushed */
+        71
+        ROFF[ ]	/* RoundOff */
+        MIRP[00100]	/* MoveIndirectRelPt */
+      ELSE[ ]	/* Else */
+        SPVTCA[1]	/* SetPVectorToAxis */
+        ROLL[ ]	/* RollTopThreeStack */
+        RCVT[ ]	/* ReadCVT */
+        RTG[ ]	/* RoundToGrid */
+        ROUND[01]	/* Round */
+        DUP[ ]	/* DuplicateTopStack */
+        PUSHB[ ]	/* 1 value pushed */
+        71
+        SWAP[ ]	/* SwapTopStack */
+        WCVTP[ ]	/* WriteCVTInPixels */
+        ROLL[ ]	/* RollTopThreeStack */
+        ROLL[ ]	/* RollTopThreeStack */
+        SDPVTL[1]	/* SetDualPVectorToLine */
+        DUP[ ]	/* DuplicateTopStack */
+        PUSHB[ ]	/* 1 value pushed */
+        160
+        LTEQ[ ]	/* LessThenOrEqual */
+        IF[ ]	/* If */
+          PUSHB[ ]	/* 1 value pushed */
+          17
+          CALL[ ]	/* CallFunction */
+          PUSHB[ ]	/* 1 value pushed */
+          71
+          SWAP[ ]	/* SwapTopStack */
+          WCVTP[ ]	/* WriteCVTInPixels */
+          PUSHB[ ]	/* 1 value pushed */
+          71
+          ROFF[ ]	/* RoundOff */
+          MIRP[00100]	/* MoveIndirectRelPt */
+        ELSE[ ]	/* Else */
+          POP[ ]	/* PopTopStack */
+          PUSHB[ ]	/* 1 value pushed */
+          71
+          ROFF[ ]	/* RoundOff */
+          MIRP[00100]	/* MoveIndirectRelPt */
+        EIF[ ]	/* EndIf */
+      EIF[ ]	/* EndIf */
+    EIF[ ]	/* EndIf */
+    RTG[ ]	/* RoundToGrid */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    DUP[ ]	/* DuplicateTopStack */
+    ROLL[ ]	/* RollTopThreeStack */
+    RCVT[ ]	/* ReadCVT */
+    SWAP[ ]	/* SwapTopStack */
+    RCVT[ ]	/* ReadCVT */
+    ROUND[00]	/* Round */
+    ADD[ ]	/* Add */
+    WCVTP[ ]	/* WriteCVTInPixels */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    RCVT[ ]	/* ReadCVT */
+    ROUND[10]	/* Round */
+    WS[ ]	/* WriteStore */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    SVTCA[1]	/* SetFPVectorToAxis */
+    RTG[ ]	/* RoundToGrid */
+    MDAP[1]	/* MoveDirectAbsPt */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    SVTCA[1]	/* SetFPVectorToAxis */
+    PUSHB[ ]	/* 1 value pushed */
+    24
+    RS[ ]	/* ReadStore */
+    IF[ ]	/* If */
+      PUSHB[ ]	/* 1 value pushed */
+      4
+      CINDEX[ ]	/* CopyXToTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      4
+      CINDEX[ ]	/* CopyXToTopStack */
+      MD[0]	/* MeasureDistance */
+      ABS[ ]	/* Absolute */
+      SWAP[ ]	/* SwapTopStack */
+      RCVT[ ]	/* ReadCVT */
+      ABS[ ]	/* Absolute */
+      ROUND[01]	/* Round */
+      PUSHB[ ]	/* 1 value pushed */
+      64
+      MAX[ ]	/* Maximum */
+      SUB[ ]	/* Subtract */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      128
+      DIV[ ]	/* Divide */
+      ROUND[10]	/* Round */
+      PUSHB[ ]	/* 1 value pushed */
+      2
+      CINDEX[ ]	/* CopyXToTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      2
+      CINDEX[ ]	/* CopyXToTopStack */
+      SUB[ ]	/* Subtract */
+      MIN[ ]	/* Minimum */
+      PUSHB[ ]	/* 1 value pushed */
+      25
+      RS[ ]	/* ReadStore */
+      ADD[ ]	/* Add */
+      PUSHB[ ]	/* 1 value pushed */
+      70
+      SWAP[ ]	/* SwapTopStack */
+      WCVTP[ ]	/* WriteCVTInPixels */
+      POP[ ]	/* PopTopStack */
+      ROLL[ ]	/* RollTopThreeStack */
+      SRP0[ ]	/* SetRefPoint0 */
+      PUSHB[ ]	/* 1 value pushed */
+      70
+      MIRP[10110]	/* MoveIndirectRelPt */
+      POP[ ]	/* PopTopStack */
+    ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      POP[ ]	/* PopTopStack */
+      POP[ ]	/* PopTopStack */
+      POP[ ]	/* PopTopStack */
+    EIF[ ]	/* EndIf */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    SVTCA[1]	/* SetFPVectorToAxis */
+    PUSHB[ ]	/* 1 value pushed */
+    24
+    RS[ ]	/* ReadStore */
+    IF[ ]	/* If */
+      PUSHB[ ]	/* 1 value pushed */
+      2
+      CINDEX[ ]	/* CopyXToTopStack */
+      RCVT[ ]	/* ReadCVT */
+      PUSHB[ ]	/* 1 value pushed */
+      2
+      CINDEX[ ]	/* CopyXToTopStack */
+      RCVT[ ]	/* ReadCVT */
+      ABS[ ]	/* Absolute */
+      ADD[ ]	/* Add */
+      ROUND[10]	/* Round */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      CINDEX[ ]	/* CopyXToTopStack */
+      RCVT[ ]	/* ReadCVT */
+      ROUND[10]	/* Round */
+      SUB[ ]	/* Subtract */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      4
+      CINDEX[ ]	/* CopyXToTopStack */
+      RCVT[ ]	/* ReadCVT */
+      ROUND[10]	/* Round */
+      DUP[ ]	/* DuplicateTopStack */
+      ROLL[ ]	/* RollTopThreeStack */
+      MAX[ ]	/* Maximum */
+      NEG[ ]	/* Negate */
+      PUSHB[ ]	/* 1 value pushed */
+      4
+      CINDEX[ ]	/* CopyXToTopStack */
+      SWAP[ ]	/* SwapTopStack */
+      WCVTP[ ]	/* WriteCVTInPixels */
+      MIN[ ]	/* Minimum */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      CINDEX[ ]	/* CopyXToTopStack */
+      SWAP[ ]	/* SwapTopStack */
+      WCVTP[ ]	/* WriteCVTInPixels */
+      POP[ ]	/* PopTopStack */
+      POP[ ]	/* PopTopStack */
+    ELSE[ ]	/* Else */
+      DUP[ ]	/* DuplicateTopStack */
+      RCVT[ ]	/* ReadCVT */
+      ROUND[10]	/* Round */
+      WCVTP[ ]	/* WriteCVTInPixels */
+      DUP[ ]	/* DuplicateTopStack */
+      RCVT[ ]	/* ReadCVT */
+      ROUND[10]	/* Round */
+      WCVTP[ ]	/* WriteCVTInPixels */
+    EIF[ ]	/* EndIf */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    SVTCA[1]	/* SetFPVectorToAxis */
+    DUP[ ]	/* DuplicateTopStack */
+    RCVT[ ]	/* ReadCVT */
+    PUSHB[ ]	/* 1 value pushed */
+    0
+    NEQ[ ]	/* NotEqual */
+    PUSHB[ ]	/* 1 value pushed */
+    24
+    RS[ ]	/* ReadStore */
+    AND[ ]	/* LogicalAnd */
+    IF[ ]	/* If */
+      RCVT[ ]	/* ReadCVT */
+      ROUND[00]	/* Round */
+      SWAP[ ]	/* SwapTopStack */
+      RCVT[ ]	/* ReadCVT */
+      ROUND[01]	/* Round */
+      PUSHB[ ]	/* 1 value pushed */
+      64
+      MAX[ ]	/* Maximum */
+      SUB[ ]	/* Subtract */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      128
+      DIV[ ]	/* Divide */
+      ROUND[10]	/* Round */
+      DUP[ ]	/* DuplicateTopStack */
+      ROLL[ ]	/* RollTopThreeStack */
+      SWAP[ ]	/* SwapTopStack */
+      SUB[ ]	/* Subtract */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      CINDEX[ ]	/* CopyXToTopStack */
+      MAX[ ]	/* Maximum */
+      NEG[ ]	/* Negate */
+      PUSHB[ ]	/* 1 value pushed */
+      4
+      CINDEX[ ]	/* CopyXToTopStack */
+      SWAP[ ]	/* SwapTopStack */
+      WCVTP[ ]	/* WriteCVTInPixels */
+      MIN[ ]	/* Minimum */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      CINDEX[ ]	/* CopyXToTopStack */
+      SWAP[ ]	/* SwapTopStack */
+      WCVTP[ ]	/* WriteCVTInPixels */
+      POP[ ]	/* PopTopStack */
+      POP[ ]	/* PopTopStack */
+    ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      POP[ ]	/* PopTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      25
+      CALL[ ]	/* CallFunction */
+    EIF[ ]	/* EndIf */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    SVTCA[1]	/* SetFPVectorToAxis */
+    ROLL[ ]	/* RollTopThreeStack */
+    ROLL[ ]	/* RollTopThreeStack */
+    RCVT[ ]	/* ReadCVT */
+    ABS[ ]	/* Absolute */
+    SWAP[ ]	/* SwapTopStack */
+    RCVT[ ]	/* ReadCVT */
+    ABS[ ]	/* Absolute */
+    SUB[ ]	/* Subtract */
+    ABS[ ]	/* Absolute */
+    WS[ ]	/* WriteStore */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    PUSHB[ ]	/* 1 value pushed */
+    4
+    CINDEX[ ]	/* CopyXToTopStack */
+    PUSHB[ ]	/* 1 value pushed */
+    4
+    CINDEX[ ]	/* CopyXToTopStack */
+    PUSHB[ ]	/* 1 value pushed */
+    25
+    CALL[ ]	/* CallFunction */
+    PUSHB[ ]	/* 1 value pushed */
+    24
+    RS[ ]	/* ReadStore */
+    IF[ ]	/* If */
+      PUSHB[ ]	/* 1 value pushed */
+      4
+      CINDEX[ ]	/* CopyXToTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      4
+      CINDEX[ ]	/* CopyXToTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      CINDEX[ ]	/* CopyXToTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      27
+      CALL[ ]	/* CallFunction */
+      SVTCA[1]	/* SetFPVectorToAxis */
+      PUSHB[ ]	/* 1 value pushed */
+      2
+      CINDEX[ ]	/* CopyXToTopStack */
+      RS[ ]	/* ReadStore */
+      PUSHB[ ]	/* 1 value pushed */
+      64
+      EQ[ ]	/* Equal */
+      PUSHB[ ]	/* 1 value pushed */
+      2
+      CINDEX[ ]	/* CopyXToTopStack */
+      RS[ ]	/* ReadStore */
+      PUSHB[ ]	/* 1 value pushed */
+      0
+      EQ[ ]	/* Equal */
+      AND[ ]	/* LogicalAnd */
+      IF[ ]	/* If */
+        PUSHB[ ]	/* 1 value pushed */
+        3
+        CINDEX[ ]	/* CopyXToTopStack */
+        DUP[ ]	/* DuplicateTopStack */
+        RCVT[ ]	/* ReadCVT */
+        PUSHB[ ]	/* 1 value pushed */
+        64
+        SUB[ ]	/* Subtract */
+        WCVTP[ ]	/* WriteCVTInPixels */
+      EIF[ ]	/* EndIf */
+      PUSHB[ ]	/* 1 value pushed */
+      2
+      CINDEX[ ]	/* CopyXToTopStack */
+      RS[ ]	/* ReadStore */
+      PUSHB[ ]	/* 1 value pushed */
+      0
+      EQ[ ]	/* Equal */
+      PUSHB[ ]	/* 1 value pushed */
+      2
+      CINDEX[ ]	/* CopyXToTopStack */
+      RS[ ]	/* ReadStore */
+      PUSHB[ ]	/* 1 value pushed */
+      64
+      EQ[ ]	/* Equal */
+      AND[ ]	/* LogicalAnd */
+      IF[ ]	/* If */
+        PUSHB[ ]	/* 1 value pushed */
+        4
+        CINDEX[ ]	/* CopyXToTopStack */
+        DUP[ ]	/* DuplicateTopStack */
+        RCVT[ ]	/* ReadCVT */
+        PUSHB[ ]	/* 1 value pushed */
+        64
+        ADD[ ]	/* Add */
+        WCVTP[ ]	/* WriteCVTInPixels */
+      EIF[ ]	/* EndIf */
+    EIF[ ]	/* EndIf */
+    POP[ ]	/* PopTopStack */
+    POP[ ]	/* PopTopStack */
+    POP[ ]	/* PopTopStack */
+    POP[ ]	/* PopTopStack */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    SVTCA[1]	/* SetFPVectorToAxis */
+    MPPEM[ ]	/* MeasurePixelPerEm */
+    GT[ ]	/* GreaterThan */
+    IF[ ]	/* If */
+      RS[ ]	/* ReadStore */
+      PUSHB[ ]	/* 1 value pushed */
+      2
+      CINDEX[ ]	/* CopyXToTopStack */
+      RCVT[ ]	/* ReadCVT */
+      SWAP[ ]	/* SwapTopStack */
+      SUB[ ]	/* Subtract */
+      WCVTP[ ]	/* WriteCVTInPixels */
+    ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      POP[ ]	/* PopTopStack */
+    EIF[ ]	/* EndIf */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    SVTCA[1]	/* SetFPVectorToAxis */
+    MPPEM[ ]	/* MeasurePixelPerEm */
+    GT[ ]	/* GreaterThan */
+    IF[ ]	/* If */
+      RS[ ]	/* ReadStore */
+      PUSHB[ ]	/* 1 value pushed */
+      2
+      CINDEX[ ]	/* CopyXToTopStack */
+      RCVT[ ]	/* ReadCVT */
+      SWAP[ ]	/* SwapTopStack */
+      ADD[ ]	/* Add */
+      WCVTP[ ]	/* WriteCVTInPixels */
+    ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      POP[ ]	/* PopTopStack */
+    EIF[ ]	/* EndIf */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    MPPEM[ ]	/* MeasurePixelPerEm */
+    GT[ ]	/* GreaterThan */
+    IF[ ]	/* If */
+      RCVT[ ]	/* ReadCVT */
+      WCVTP[ ]	/* WriteCVTInPixels */
+    ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      POP[ ]	/* PopTopStack */
+    EIF[ ]	/* EndIf */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    SVTCA[1]	/* SetFPVectorToAxis */
+    DUP[ ]	/* DuplicateTopStack */
+    PUSHB[ ]	/* 1 value pushed */
+    3
+    CINDEX[ ]	/* CopyXToTopStack */
+    SWAP[ ]	/* SwapTopStack */
+    MD[0]	/* MeasureDistance */
+    PUSHB[ ]	/* 1 value pushed */
+    64
+    ADD[ ]	/* Add */
+    PUSHB[ ]	/* 1 value pushed */
+    32
+    MUL[ ]	/* Multiply */
+    DUP[ ]	/* DuplicateTopStack */
+    PUSHB[ ]	/* 1 value pushed */
+    0
+    GT[ ]	/* GreaterThan */
+    IF[ ]	/* If */
+      SWAP[ ]	/* SwapTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      2
+      CINDEX[ ]	/* CopyXToTopStack */
+      SHPIX[ ]	/* ShiftZoneByPixel */
+      SWAP[ ]	/* SwapTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      2
+      CINDEX[ ]	/* CopyXToTopStack */
+      NEG[ ]	/* Negate */
+      SHPIX[ ]	/* ShiftZoneByPixel */
+      SVTCA[0]	/* SetFPVectorToAxis */
+      ROLL[ ]	/* RollTopThreeStack */
+      MUL[ ]	/* Multiply */
+      SHPIX[ ]	/* ShiftZoneByPixel */
+    ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      POP[ ]	/* PopTopStack */
+      POP[ ]	/* PopTopStack */
+      POP[ ]	/* PopTopStack */
+      POP[ ]	/* PopTopStack */
+    EIF[ ]	/* EndIf */
+    SVTCA[1]	/* SetFPVectorToAxis */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    MPPEM[ ]	/* MeasurePixelPerEm */
+    LT[ ]	/* LessThan */
+    IF[ ]	/* If */
+      RCVT[ ]	/* ReadCVT */
+      WCVTP[ ]	/* WriteCVTInPixels */
+    ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      POP[ ]	/* PopTopStack */
+    EIF[ ]	/* EndIf */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    SVTCA[1]	/* SetFPVectorToAxis */
+    PUSHB[ ]	/* 1 value pushed */
+    5
+    CINDEX[ ]	/* CopyXToTopStack */
+    SRP0[ ]	/* SetRefPoint0 */
+    SWAP[ ]	/* SwapTopStack */
+    DUP[ ]	/* DuplicateTopStack */
+    ROLL[ ]	/* RollTopThreeStack */
+    MIRP[10101]	/* MoveIndirectRelPt */
+    SVTCA[0]	/* SetFPVectorToAxis */
+    PUSHB[ ]	/* 1 value pushed */
+    1
+    ADD[ ]	/* Add */
+    SWAP[ ]	/* SwapTopStack */
+    MIRP[01101]	/* MoveIndirectRelPt */
+    MIRP[01100]	/* MoveIndirectRelPt */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    SVTCA[1]	/* SetFPVectorToAxis */
+    PUSHB[ ]	/* 1 value pushed */
+    5
+    CINDEX[ ]	/* CopyXToTopStack */
+    SRP0[ ]	/* SetRefPoint0 */
+    SWAP[ ]	/* SwapTopStack */
+    DUP[ ]	/* DuplicateTopStack */
+    ROLL[ ]	/* RollTopThreeStack */
+    MIRP[10101]	/* MoveIndirectRelPt */
+    SVTCA[0]	/* SetFPVectorToAxis */
+    PUSHB[ ]	/* 1 value pushed */
+    1
+    SUB[ ]	/* Subtract */
+    SWAP[ ]	/* SwapTopStack */
+    MIRP[01101]	/* MoveIndirectRelPt */
+    MIRP[01100]	/* MoveIndirectRelPt */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    SVTCA[1]	/* SetFPVectorToAxis */
+    PUSHB[ ]	/* 1 value pushed */
+    6
+    CINDEX[ ]	/* CopyXToTopStack */
+    SRP0[ ]	/* SetRefPoint0 */
+    MIRP[10101]	/* MoveIndirectRelPt */
+    SVTCA[0]	/* SetFPVectorToAxis */
+    MIRP[01101]	/* MoveIndirectRelPt */
+    MIRP[01100]	/* MoveIndirectRelPt */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    GC[0]	/* GetCoordOnPVector */
+    SWAP[ ]	/* SwapTopStack */
+    GC[0]	/* GetCoordOnPVector */
+    ADD[ ]	/* Add */
+    ROLL[ ]	/* RollTopThreeStack */
+    ROLL[ ]	/* RollTopThreeStack */
+    GC[0]	/* GetCoordOnPVector */
+    SWAP[ ]	/* SwapTopStack */
+    DUP[ ]	/* DuplicateTopStack */
+    GC[0]	/* GetCoordOnPVector */
+    ROLL[ ]	/* RollTopThreeStack */
+    ADD[ ]	/* Add */
+    ROLL[ ]	/* RollTopThreeStack */
+    SUB[ ]	/* Subtract */
+    PUSHW[ ]	/* 1 value pushed */
+    -128
+    DIV[ ]	/* Divide */
+    SWAP[ ]	/* SwapTopStack */
+    DUP[ ]	/* DuplicateTopStack */
+    SRP0[ ]	/* SetRefPoint0 */
+    SWAP[ ]	/* SwapTopStack */
+    ROLL[ ]	/* RollTopThreeStack */
+    PUSHB[ ]	/* 2 values pushed */
+    75 75
+    ROLL[ ]	/* RollTopThreeStack */
+    WCVTF[ ]	/* WriteCVTInFUnits */
+    RCVT[ ]	/* ReadCVT */
+    ADD[ ]	/* Add */
+    DUP[ ]	/* DuplicateTopStack */
+    PUSHB[ ]	/* 1 value pushed */
+    0
+    LT[ ]	/* LessThan */
+    IF[ ]	/* If */
+      PUSHB[ ]	/* 1 value pushed */
+      1
+      SUB[ ]	/* Subtract */
+      PUSHW[ ]	/* 1 value pushed */
+      -70
+      MAX[ ]	/* Maximum */
+    ELSE[ ]	/* Else */
+      PUSHB[ ]	/* 1 value pushed */
+      70
+      MIN[ ]	/* Minimum */
+    EIF[ ]	/* EndIf */
+    PUSHB[ ]	/* 1 value pushed */
+    16
+    ADD[ ]	/* Add */
+    ROUND[00]	/* Round */
+    SVTCA[1]	/* SetFPVectorToAxis */
+    MSIRP[0]	/* MoveStackIndirRelPt */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    DUP[ ]	/* DuplicateTopStack */
+    RCVT[ ]	/* ReadCVT */
+    PUSHB[ ]	/* 1 value pushed */
+    3
+    CINDEX[ ]	/* CopyXToTopStack */
+    GC[0]	/* GetCoordOnPVector */
+    GT[ ]	/* GreaterThan */
+    IF[ ]	/* If */
+      PUSHB[ ]	/* 1 value pushed */
+      2
+      CINDEX[ ]	/* CopyXToTopStack */
+      GC[0]	/* GetCoordOnPVector */
+      DUP[ ]	/* DuplicateTopStack */
+      ROUND[00]	/* Round */
+      SUB[ ]	/* Subtract */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      CINDEX[ ]	/* CopyXToTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      CINDEX[ ]	/* CopyXToTopStack */
+      MIAP[1]	/* MoveIndirectAbsPt */
+      SWAP[ ]	/* SwapTopStack */
+      POP[ ]	/* PopTopStack */
+      SHPIX[ ]	/* ShiftZoneByPixel */
+    ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      SRP1[ ]	/* SetRefPoint1 */
+    EIF[ ]	/* EndIf */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    DUP[ ]	/* DuplicateTopStack */
+    RCVT[ ]	/* ReadCVT */
+    PUSHB[ ]	/* 1 value pushed */
+    3
+    CINDEX[ ]	/* CopyXToTopStack */
+    GC[0]	/* GetCoordOnPVector */
+    LT[ ]	/* LessThan */
+    IF[ ]	/* If */
+      PUSHB[ ]	/* 1 value pushed */
+      2
+      CINDEX[ ]	/* CopyXToTopStack */
+      GC[0]	/* GetCoordOnPVector */
+      DUP[ ]	/* DuplicateTopStack */
+      ROUND[00]	/* Round */
+      SUB[ ]	/* Subtract */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      CINDEX[ ]	/* CopyXToTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      CINDEX[ ]	/* CopyXToTopStack */
+      MIAP[1]	/* MoveIndirectAbsPt */
+      SWAP[ ]	/* SwapTopStack */
+      POP[ ]	/* PopTopStack */
+      SHPIX[ ]	/* ShiftZoneByPixel */
+    ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      SRP1[ ]	/* SetRefPoint1 */
+    EIF[ ]	/* EndIf */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    SVTCA[0]	/* SetFPVectorToAxis */
+    PUSHB[ ]	/* 1 value pushed */
+    7
+    RS[ ]	/* ReadStore */
+    PUSHB[ ]	/* 1 value pushed */
+    6
+    RS[ ]	/* ReadStore */
+    SFVFS[ ]	/* SetFVectorFromStack */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    ROLL[ ]	/* RollTopThreeStack */
+    SRP0[ ]	/* SetRefPoint0 */
+    MIRP[01100]	/* MoveIndirectRelPt */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    PUSHB[ ]	/* 1 value pushed */
+    12
+    RS[ ]	/* ReadStore */
+    IF[ ]	/* If */
+      POP[ ]	/* PopTopStack */
+    ELSE[ ]	/* Else */
+      DUP[ ]	/* DuplicateTopStack */
+      GC[0]	/* GetCoordOnPVector */
+      PUSHB[ ]	/* 1 value pushed */
+      0
+      GT[ ]	/* GreaterThan */
+      IF[ ]	/* If */
+        PUSHW[ ]	/* 1 value pushed */
+        -16
+        SHPIX[ ]	/* ShiftZoneByPixel */
+      ELSE[ ]	/* Else */
+        PUSHB[ ]	/* 1 value pushed */
+        16
+        SHPIX[ ]	/* ShiftZoneByPixel */
+      EIF[ ]	/* EndIf */
+    EIF[ ]	/* EndIf */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    DUP[ ]	/* DuplicateTopStack */
+    PUSHB[ ]	/* 1 value pushed */
+    0
+    NEQ[ ]	/* NotEqual */
+    IF[ ]	/* If */
+      PUSHW[ ]	/* 1 value pushed */
+      4096
+      MUL[ ]	/* Multiply */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      CINDEX[ ]	/* CopyXToTopStack */
+      RCVT[ ]	/* ReadCVT */
+      ABS[ ]	/* Absolute */
+      PUSHB[ ]	/* 1 value pushed */
+      3
+      CINDEX[ ]	/* CopyXToTopStack */
+      RCVT[ ]	/* ReadCVT */
+      ABS[ ]	/* Absolute */
+      SUB[ ]	/* Subtract */
+      PUSHB[ ]	/* 1 value pushed */
+      0
+      GTEQ[ ]	/* GreaterThanOrEqual */
+      IF[ ]	/* If */
+        PUSHB[ ]	/* 1 value pushed */
+        2
+      ELSE[ ]	/* Else */
+        PUSHB[ ]	/* 1 value pushed */
+        64
+        SUB[ ]	/* Subtract */
+        PUSHB[ ]	/* 1 value pushed */
+        3
+      EIF[ ]	/* EndIf */
+      CINDEX[ ]	/* CopyXToTopStack */
+      RCVT[ ]	/* ReadCVT */
+      ROUND[01]	/* Round */
+      GTEQ[ ]	/* GreaterThanOrEqual */
+      IF[ ]	/* If */
+        RCVT[ ]	/* ReadCVT */
+        WCVTP[ ]	/* WriteCVTInPixels */
+      ELSE[ ]	/* Else */
+        POP[ ]	/* PopTopStack */
+        POP[ ]	/* PopTopStack */
+      EIF[ ]	/* EndIf */
+    ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      2
+      CINDEX[ ]	/* CopyXToTopStack */
+      RCVT[ ]	/* ReadCVT */
+      PUSHB[ ]	/* 1 value pushed */
+      2
+      CINDEX[ ]	/* CopyXToTopStack */
+      RCVT[ ]	/* ReadCVT */
+      SUB[ ]	/* Subtract */
+      ABS[ ]	/* Absolute */
+      PUSHB[ ]	/* 1 value pushed */
+      40
+      LTEQ[ ]	/* LessThenOrEqual */
+      IF[ ]	/* If */
+        RCVT[ ]	/* ReadCVT */
+        WCVTP[ ]	/* WriteCVTInPixels */
+      ELSE[ ]	/* Else */
+        POP[ ]	/* PopTopStack */
+        POP[ ]	/* PopTopStack */
+      EIF[ ]	/* EndIf */
+    EIF[ ]	/* EndIf */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    POP[ ]	/* PopTopStack */
+    POP[ ]	/* PopTopStack */
+    GPV[ ]	/* GetPVector */
+    ABS[ ]	/* Absolute */
+    SWAP[ ]	/* SwapTopStack */
+    ABS[ ]	/* Absolute */
+    MAX[ ]	/* Maximum */
+    PUSHW[ ]	/* 1 value pushed */
+    16384
+    DIV[ ]	/* Divide */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    POP[ ]	/* PopTopStack */
+    PUSHB[ ]	/* 1 value pushed */
+    128
+    LTEQ[ ]	/* LessThenOrEqual */
+    IF[ ]	/* If */
+      GPV[ ]	/* GetPVector */
+      ABS[ ]	/* Absolute */
+      SWAP[ ]	/* SwapTopStack */
+      ABS[ ]	/* Absolute */
+      MAX[ ]	/* Maximum */
+      PUSHW[ ]	/* 1 value pushed */
+      8192
+      DIV[ ]	/* Divide */
+    ELSE[ ]	/* Else */
+      PUSHB[ ]	/* 3 values pushed */
+      0 64 47
+      CALL[ ]	/* CallFunction */
+    EIF[ ]	/* EndIf */
+    PUSHB[ ]	/* 1 value pushed */
+    2
+    ADD[ ]	/* Add */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    POP[ ]	/* PopTopStack */
+    PUSHB[ ]	/* 1 value pushed */
+    192
+    LTEQ[ ]	/* LessThenOrEqual */
+    IF[ ]	/* If */
+      GPV[ ]	/* GetPVector */
+      ABS[ ]	/* Absolute */
+      SWAP[ ]	/* SwapTopStack */
+      ABS[ ]	/* Absolute */
+      MAX[ ]	/* Maximum */
+      PUSHW[ ]	/* 1 value pushed */
+      5461
+      DIV[ ]	/* Divide */
+    ELSE[ ]	/* Else */
+      PUSHB[ ]	/* 3 values pushed */
+      0 128 47
+      CALL[ ]	/* CallFunction */
+    EIF[ ]	/* EndIf */
+    PUSHB[ ]	/* 1 value pushed */
+    2
+    ADD[ ]	/* Add */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    GPV[ ]	/* GetPVector */
+    ABS[ ]	/* Absolute */
+    SWAP[ ]	/* SwapTopStack */
+    ABS[ ]	/* Absolute */
+    MAX[ ]	/* Maximum */
+    PUSHW[ ]	/* 1 value pushed */
+    16384
+    DIV[ ]	/* Divide */
+    ADD[ ]	/* Add */
+    SWAP[ ]	/* SwapTopStack */
+    POP[ ]	/* PopTopStack */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    MPPEM[ ]	/* MeasurePixelPerEm */
+    GTEQ[ ]	/* GreaterThanOrEqual */
+    IF[ ]	/* If */
+      PUSHB[ ]	/* 1 value pushed */
+      4
+      CINDEX[ ]	/* CopyXToTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      4
+      CINDEX[ ]	/* CopyXToTopStack */
+      MD[0]	/* MeasureDistance */
+      ABS[ ]	/* Absolute */
+      SWAP[ ]	/* SwapTopStack */
+      RCVT[ ]	/* ReadCVT */
+      ABS[ ]	/* Absolute */
+      ROUND[01]	/* Round */
+      PUSHB[ ]	/* 1 value pushed */
+      64
+      MAX[ ]	/* Maximum */
+      SUB[ ]	/* Subtract */
+      DUP[ ]	/* DuplicateTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      128
+      DIV[ ]	/* Divide */
+      ROUND[10]	/* Round */
+      PUSHB[ ]	/* 1 value pushed */
+      2
+      CINDEX[ ]	/* CopyXToTopStack */
+      PUSHB[ ]	/* 1 value pushed */
+      2
+      CINDEX[ ]	/* CopyXToTopStack */
+      SUB[ ]	/* Subtract */
+      MIN[ ]	/* Minimum */
+      PUSHB[ ]	/* 1 value pushed */
+      70
+      SWAP[ ]	/* SwapTopStack */
+      WCVTP[ ]	/* WriteCVTInPixels */
+      POP[ ]	/* PopTopStack */
+      ROLL[ ]	/* RollTopThreeStack */
+      SRP0[ ]	/* SetRefPoint0 */
+      PUSHB[ ]	/* 1 value pushed */
+      70
+      MIRP[10110]	/* MoveIndirectRelPt */
+      POP[ ]	/* PopTopStack */
+    ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      ROLL[ ]	/* RollTopThreeStack */
+      SRP1[ ]	/* SetRefPoint1 */
+      SWAP[ ]	/* SwapTopStack */
+      SRP2[ ]	/* SetRefPoint2 */
+      DUP[ ]	/* DuplicateTopStack */
+      IP[ ]	/* InterpolatePts */
+      MDAP[1]	/* MoveDirectAbsPt */
+    EIF[ ]	/* EndIf */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    PUSHB[ ]	/* 1 value pushed */
+    2
+    CINDEX[ ]	/* CopyXToTopStack */
+    PUSHB[ ]	/* 1 value pushed */
+    2
+    CINDEX[ ]	/* CopyXToTopStack */
+    MD[0]	/* MeasureDistance */
+    ABS[ ]	/* Absolute */
+    PUSHB[ ]	/* 1 value pushed */
+    192
+    EQ[ ]	/* Equal */
+    IF[ ]	/* If */
+      PUSHW[ ]	/* 1 value pushed */
+      -8
+      SHPIX[ ]	/* ShiftZoneByPixel */
+      PUSHB[ ]	/* 1 value pushed */
+      8
+      SHPIX[ ]	/* ShiftZoneByPixel */
+    ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      POP[ ]	/* PopTopStack */
+    EIF[ ]	/* EndIf */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    PUSHB[ ]	/* 1 value pushed */
+    19
+    RS[ ]	/* ReadStore */
+    IF[ ]	/* If */
+      SPVTCA[1]	/* SetPVectorToAxis */
+    ELSE[ ]	/* Else */
+      SPVTCA[0]	/* SetPVectorToAxis */
+    EIF[ ]	/* EndIf */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    PUSHB[ ]	/* 1 value pushed */
+    19
+    RS[ ]	/* ReadStore */
+    IF[ ]	/* If */
+      SPVTCA[0]	/* SetPVectorToAxis */
+    ELSE[ ]	/* Else */
+      SPVTCA[1]	/* SetPVectorToAxis */
+    EIF[ ]	/* EndIf */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    PUSHB[ ]	/* 1 value pushed */
+    10
+    CALL[ ]	/* CallFunction */
+    SWAP[ ]	/* SwapTopStack */
+    SRP0[ ]	/* SetRefPoint0 */
+    DUP[ ]	/* DuplicateTopStack */
+    ALIGNRP[ ]	/* AlignRelativePt */
+    PUSHB[ ]	/* 1 value pushed */
+    23
+    CALL[ ]	/* CallFunction */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    PUSHB[ ]	/* 1 value pushed */
+    2
+    CINDEX[ ]	/* CopyXToTopStack */
+    PUSHW[ ]	/* 1 value pushed */
+    -16
+    SHPIX[ ]	/* ShiftZoneByPixel */
+    PUSHB[ ]	/* 1 value pushed */
+    40
+    CALL[ ]	/* CallFunction */
+    ROLL[ ]	/* RollTopThreeStack */
+    SRP0[ ]	/* SetRefPoint0 */
+    SWAP[ ]	/* SwapTopStack */
+    DUP[ ]	/* DuplicateTopStack */
+    MDRP[10000]	/* MoveDirectRelPt */
+    SWAP[ ]	/* SwapTopStack */
+    PUSHB[ ]	/* 1 value pushed */
+    16
+    CALL[ ]	/* CallFunction */
+    PUSHB[ ]	/* 1 value pushed */
+    5
+    RS[ ]	/* ReadStore */
+    IF[ ]	/* If */
+      MDRP[00000]	/* MoveDirectRelPt */
+    ELSE[ ]	/* Else */
+      ALIGNRP[ ]	/* AlignRelativePt */
+    EIF[ ]	/* EndIf */
+    DUP[ ]	/* DuplicateTopStack */
+    SRP0[ ]	/* SetRefPoint0 */
+    SRP1[ ]	/* SetRefPoint1 */
+    PUSHB[ ]	/* 1 value pushed */
+    0
+    SRP2[ ]	/* SetRefPoint2 */
+    SVTCA[1]	/* SetFPVectorToAxis */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    MPPEM[ ]	/* MeasurePixelPerEm */
+    GTEQ[ ]	/* GreaterThanOrEqual */
+    SWAP[ ]	/* SwapTopStack */
+    MPPEM[ ]	/* MeasurePixelPerEm */
+    LTEQ[ ]	/* LessThenOrEqual */
+    AND[ ]	/* LogicalAnd */
+    IF[ ]	/* If */
+      SHPIX[ ]	/* ShiftZoneByPixel */
+    ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      POP[ ]	/* PopTopStack */
+    EIF[ ]	/* EndIf */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    SVTCA[1]	/* SetFPVectorToAxis */
+    PUSHB[ ]	/* 1 value pushed */
+    2
+    CINDEX[ ]	/* CopyXToTopStack */
+    SRP0[ ]	/* SetRefPoint0 */
+    MDRP[10000]	/* MoveDirectRelPt */
+    SWAP[ ]	/* SwapTopStack */
+    MDRP[01001]	/* MoveDirectRelPt */
+    SVTCA[1]	/* SetFPVectorToAxis */
+    PUSHB[ ]	/* 1 value pushed */
+    1
+    SZP0[ ]	/* SetZonePointer0 */
+    PUSHB[ ]	/* 1 value pushed */
+    0
+    SZP1[ ]	/* SetZonePointer1 */
+    SRP0[ ]	/* SetRefPoint0 */
+    PUSHB[ ]	/* 1 value pushed */
+    1
+    ALIGNRP[ ]	/* AlignRelativePt */
+    PUSHB[ ]	/* 1 value pushed */
+    1
+    SZPS[ ]	/* SetZonePointerS */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    SVTCA[1]	/* SetFPVectorToAxis */
+    PUSHB[ ]	/* 1 value pushed */
+    0
+    SZP0[ ]	/* SetZonePointer0 */
+    PUSHB[ ]	/* 1 value pushed */
+    1
+    PUSHB[ ]	/* 1 value pushed */
+    3
+    CINDEX[ ]	/* CopyXToTopStack */
+    MD[0]	/* MeasureDistance */
+    PUSHB[ ]	/* 1 value pushed */
+    3
+    SLOOP[ ]	/* SetLoopVariable */
+    SHPIX[ ]	/* ShiftZoneByPixel */
+    PUSHB[ ]	/* 1 value pushed */
+    1
+    SZP0[ ]	/* SetZonePointer0 */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    MPPEM[ ]	/* MeasurePixelPerEm */
+    GTEQ[ ]	/* GreaterThanOrEqual */
+    SWAP[ ]	/* SwapTopStack */
+    MPPEM[ ]	/* MeasurePixelPerEm */
+    LTEQ[ ]	/* LessThenOrEqual */
+    AND[ ]	/* LogicalAnd */
+    IF[ ]	/* If */
+      DUP[ ]	/* DuplicateTopStack */
+      RCVT[ ]	/* ReadCVT */
+      ROLL[ ]	/* RollTopThreeStack */
+      ADD[ ]	/* Add */
+      WCVTP[ ]	/* WriteCVTInPixels */
+    ELSE[ ]	/* Else */
+      POP[ ]	/* PopTopStack */
+      POP[ ]	/* PopTopStack */
+    EIF[ ]	/* EndIf */
+  ENDF[ ]	/* EndFunctionDefinition */
+  FDEF[ ]	/* FunctionDefinition */
+    DUP[ ]	/* DuplicateTopStack */
+    IP[ ]	/* InterpolatePts */
+    MDAP[1]	/* MoveDirectAbsPt */
+  ENDF[ ]	/* EndFunctionDefinition */
+</assembly>
diff --git a/Tests/ttLib/tables/otBase_test.py b/Tests/ttLib/tables/otBase_test.py
new file mode 100644
index 0000000..f1fa5b0
--- /dev/null
+++ b/Tests/ttLib/tables/otBase_test.py
@@ -0,0 +1,96 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.textTools import deHexStr
+from fontTools.ttLib.tables.otBase import OTTableReader, OTTableWriter
+import unittest
+
+
+class OTTableReaderTest(unittest.TestCase):
+    def test_readShort(self):
+        reader = OTTableReader(deHexStr("CA FE"))
+        self.assertEqual(reader.readShort(), -13570)
+        self.assertEqual(reader.pos, 2)
+
+    def test_readLong(self):
+        reader = OTTableReader(deHexStr("CA FE BE EF"))
+        self.assertEqual(reader.readLong(), -889274641)
+        self.assertEqual(reader.pos, 4)
+
+    def test_readUInt8(self):
+        reader = OTTableReader(deHexStr("C3"))
+        self.assertEqual(reader.readUInt8(), 0xC3)
+        self.assertEqual(reader.pos, 1)
+
+    def test_readUShort(self):
+        reader = OTTableReader(deHexStr("CA FE"))
+        self.assertEqual(reader.readUShort(), 0xCAFE)
+        self.assertEqual(reader.pos, 2)
+
+    def test_readUShortArray(self):
+        reader = OTTableReader(deHexStr("DE AD BE EF CA FE"))
+        self.assertEqual(list(reader.readUShortArray(3)),
+                         [0xDEAD, 0xBEEF, 0xCAFE])
+        self.assertEqual(reader.pos, 6)
+
+    def test_readUInt24(self):
+        reader = OTTableReader(deHexStr("C3 13 37"))
+        self.assertEqual(reader.readUInt24(), 0xC31337)
+        self.assertEqual(reader.pos, 3)
+
+    def test_readULong(self):
+        reader = OTTableReader(deHexStr("CA FE BE EF"))
+        self.assertEqual(reader.readULong(), 0xCAFEBEEF)
+        self.assertEqual(reader.pos, 4)
+
+    def test_readTag(self):
+        reader = OTTableReader(deHexStr("46 6F 6F 64"))
+        self.assertEqual(reader.readTag(), "Food")
+        self.assertEqual(reader.pos, 4)
+
+    def test_readData(self):
+        reader = OTTableReader(deHexStr("48 65 6C 6C 6F"))
+        self.assertEqual(reader.readData(5), b"Hello")
+        self.assertEqual(reader.pos, 5)
+
+    def test_getSubReader(self):
+        reader = OTTableReader(deHexStr("CAFE F00D"))
+        sub = reader.getSubReader(2)
+        self.assertEqual(sub.readUShort(), 0xF00D)
+        self.assertEqual(reader.readUShort(), 0xCAFE)
+
+
+class OTTableWriterTest(unittest.TestCase):
+    def test_writeShort(self):
+        writer = OTTableWriter()
+        writer.writeShort(-12345)
+        self.assertEqual(writer.getData(), deHexStr("CF C7"))
+
+    def test_writeLong(self):
+        writer = OTTableWriter()
+        writer.writeLong(-12345678)
+        self.assertEqual(writer.getData(), deHexStr("FF 43 9E B2"))
+
+    def test_writeUInt8(self):
+        writer = OTTableWriter()
+        writer.writeUInt8(0xBE)
+        self.assertEqual(writer.getData(), deHexStr("BE"))
+
+    def test_writeUShort(self):
+        writer = OTTableWriter()
+        writer.writeUShort(0xBEEF)
+        self.assertEqual(writer.getData(), deHexStr("BE EF"))
+
+    def test_writeUInt24(self):
+        writer = OTTableWriter()
+        writer.writeUInt24(0xBEEF77)
+        self.assertEqual(writer.getData(), deHexStr("BE EF 77"))
+
+    def test_writeULong(self):
+        writer = OTTableWriter()
+        writer.writeULong(0xBEEFCAFE)
+        self.assertEqual(writer.getData(), deHexStr("BE EF CA FE"))
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/otConverters_test.py b/Tests/ttLib/tables/otConverters_test.py
new file mode 100644
index 0000000..3b9d5e9
--- /dev/null
+++ b/Tests/ttLib/tables/otConverters_test.py
@@ -0,0 +1,433 @@
+# coding: utf-8
+from __future__ import print_function, division, absolute_import, \
+    unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.misc.loggingTools import CapturingLogHandler
+from fontTools.misc.testTools import FakeFont, makeXMLWriter
+from fontTools.misc.textTools import deHexStr
+import fontTools.ttLib.tables.otConverters as otConverters
+from fontTools.ttLib import newTable
+from fontTools.ttLib.tables.otBase import OTTableReader, OTTableWriter
+import unittest
+
+
+class Char64Test(unittest.TestCase):
+    font = FakeFont([])
+    converter = otConverters.Char64("char64", 0, None, None)
+
+    def test_read(self):
+        reader = OTTableReader(b"Hello\0junk after zero byte" + 100 * b"\0")
+        self.assertEqual(self.converter.read(reader, self.font, {}), "Hello")
+        self.assertEqual(reader.pos, 64)
+
+    def test_read_replace_not_ascii(self):
+        reader = OTTableReader(b"Hello \xE4 world" + 100 * b"\0")
+        with CapturingLogHandler(otConverters.log, "WARNING") as captor:
+            data = self.converter.read(reader, self.font, {})
+        self.assertEqual(data, "Hello � world")
+        self.assertEqual(reader.pos, 64)
+        self.assertIn('replaced non-ASCII characters in "Hello � world"',
+                      [r.msg for r in captor.records])
+
+    def test_write(self):
+        writer = OTTableWriter()
+        self.converter.write(writer, self.font, {}, "Hello world")
+        self.assertEqual(writer.getData(), b"Hello world" + 53 * b"\0")
+
+    def test_write_replace_not_ascii(self):
+        writer = OTTableWriter()
+        with CapturingLogHandler(otConverters.log, "WARNING") as captor:
+            self.converter.write(writer, self.font, {}, "Hello ☃")
+        self.assertEqual(writer.getData(), b"Hello ?" + 57 * b"\0")
+        self.assertIn('replacing non-ASCII characters in "Hello ☃"',
+                      [r.msg for r in captor.records])
+
+    def test_write_truncated(self):
+        writer = OTTableWriter()
+        with CapturingLogHandler(otConverters.log, "WARNING") as captor:
+            self.converter.write(writer, self.font, {}, "A" * 80)
+        self.assertEqual(writer.getData(), b"A" * 64)
+        self.assertIn('truncating overlong "' + "A" * 80 + '" to 64 bytes',
+                      [r.msg for r in captor.records])
+
+    def test_xmlRead(self):
+        value = self.converter.xmlRead({"value": "Foo"}, [], self.font)
+        self.assertEqual(value, "Foo")
+
+    def test_xmlWrite(self):
+        writer = makeXMLWriter()
+        self.converter.xmlWrite(writer, self.font, "Hello world", "Element",
+                                [("attr", "v")])
+        xml = writer.file.getvalue().decode("utf-8").rstrip()
+        self.assertEqual(xml, '<Element attr="v" value="Hello world"/>')
+
+
+class GlyphIDTest(unittest.TestCase):
+    font = FakeFont(".notdef A B C".split())
+    converter = otConverters.GlyphID('GlyphID', 0, None, None)
+
+    def test_readArray(self):
+        reader = OTTableReader(deHexStr("0002 0001 DEAD 0002"))
+        self.assertEqual(self.converter.readArray(reader, self.font, {}, 4),
+                         ["B", "A", "glyph57005", "B"])
+        self.assertEqual(reader.pos, 8)
+
+    def test_read(self):
+        reader = OTTableReader(deHexStr("0003"))
+        self.assertEqual(self.converter.read(reader, self.font, {}), "C")
+        self.assertEqual(reader.pos, 2)
+
+    def test_write(self):
+        writer = OTTableWriter()
+        self.converter.write(writer, self.font, {}, "B")
+        self.assertEqual(writer.getData(), deHexStr("0002"))
+
+
+class LongTest(unittest.TestCase):
+    font = FakeFont([])
+    converter = otConverters.Long('Long', 0, None, None)
+
+    def test_read(self):
+        reader = OTTableReader(deHexStr("FF0000EE"))
+        self.assertEqual(self.converter.read(reader, self.font, {}), -16776978)
+        self.assertEqual(reader.pos, 4)
+
+    def test_write(self):
+        writer = OTTableWriter()
+        self.converter.write(writer, self.font, {}, -16777213)
+        self.assertEqual(writer.getData(), deHexStr("FF000003"))
+
+    def test_xmlRead(self):
+        value = self.converter.xmlRead({"value": "314159"}, [], self.font)
+        self.assertEqual(value, 314159)
+
+    def test_xmlWrite(self):
+        writer = makeXMLWriter()
+        self.converter.xmlWrite(writer, self.font, 291, "Foo", [("attr", "v")])
+        xml = writer.file.getvalue().decode("utf-8").rstrip()
+        self.assertEqual(xml, '<Foo attr="v" value="291"/>')
+
+
+class NameIDTest(unittest.TestCase):
+    converter = otConverters.NameID('NameID', 0, None, None)
+
+    def makeFont(self):
+        nameTable = newTable('name')
+        nameTable.setName(u"Demibold Condensed", 0x123, 3, 0, 0x409)
+        nameTable.setName(u"Copyright 2018", 0, 3, 0, 0x409)
+        return {"name": nameTable}
+
+    def test_read(self):
+        font = self.makeFont()
+        reader = OTTableReader(deHexStr("0123"))
+        self.assertEqual(self.converter.read(reader, font, {}), 0x123)
+
+    def test_write(self):
+        writer = OTTableWriter()
+        self.converter.write(writer, self.makeFont(), {}, 0x123)
+        self.assertEqual(writer.getData(), deHexStr("0123"))
+
+    def test_xmlWrite(self):
+        writer = makeXMLWriter()
+        self.converter.xmlWrite(writer, self.makeFont(), 291,
+                                "FooNameID", [("attr", "val")])
+        xml = writer.file.getvalue().decode("utf-8").rstrip()
+        self.assertEqual(
+            xml,
+            '<FooNameID attr="val" value="291"/>  <!-- Demibold Condensed -->')
+
+    def test_xmlWrite_missingID(self):
+        writer = makeXMLWriter()
+        with CapturingLogHandler(otConverters.log, "WARNING") as captor:
+            self.converter.xmlWrite(writer, self.makeFont(), 666,
+                                    "Entity", [("attrib", "val")])
+        self.assertIn("name id 666 missing from name table",
+                      [r.msg for r in captor.records])
+        xml = writer.file.getvalue().decode("utf-8").rstrip()
+        self.assertEqual(
+            xml,
+            '<Entity attrib="val"'
+            ' value="666"/>  <!-- missing from name table -->')
+
+    def test_xmlWrite_NULL(self):
+        writer = makeXMLWriter()
+        self.converter.xmlWrite(writer, self.makeFont(), 0,
+                                "FooNameID", [("attr", "val")])
+        xml = writer.file.getvalue().decode("utf-8").rstrip()
+        self.assertEqual(
+            xml, '<FooNameID attr="val" value="0"/>')
+
+
+class UInt8Test(unittest.TestCase):
+    font = FakeFont([])
+    converter = otConverters.UInt8("UInt8", 0, None, None)
+
+    def test_read(self):
+        reader = OTTableReader(deHexStr("FE"))
+        self.assertEqual(self.converter.read(reader, self.font, {}), 254)
+        self.assertEqual(reader.pos, 1)
+
+    def test_write(self):
+        writer = OTTableWriter()
+        self.converter.write(writer, self.font, {}, 253)
+        self.assertEqual(writer.getData(), deHexStr("FD"))
+
+    def test_xmlRead(self):
+        value = self.converter.xmlRead({"value": "254"}, [], self.font)
+        self.assertEqual(value, 254)
+
+    def test_xmlWrite(self):
+        writer = makeXMLWriter()
+        self.converter.xmlWrite(writer, self.font, 251, "Foo", [("attr", "v")])
+        xml = writer.file.getvalue().decode("utf-8").rstrip()
+        self.assertEqual(xml, '<Foo attr="v" value="251"/>')
+
+
+class AATLookupTest(unittest.TestCase):
+    font = FakeFont(".notdef A B C D E F G H A.alt B.alt".split())
+    converter = otConverters.AATLookup("AATLookup", 0, None,
+                                       tableClass=otConverters.GlyphID)
+
+    def __init__(self, methodName):
+        unittest.TestCase.__init__(self, methodName)
+        # Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
+        # and fires deprecation warnings if a program uses the old name.
+        if not hasattr(self, "assertRaisesRegex"):
+            self.assertRaisesRegex = self.assertRaisesRegexp
+
+    def test_readFormat0(self):
+        reader = OTTableReader(deHexStr("0000 0000 0001 0002 0000 7D00 0001"))
+        self.assertEqual(self.converter.read(reader, self.font, None), {
+            ".notdef": ".notdef",
+            "A": "A",
+            "B": "B",
+            "C": ".notdef",
+            "D": "glyph32000",
+            "E": "A"
+        })
+
+    def test_readFormat2(self):
+        reader = OTTableReader(deHexStr(
+            "0002 0006 0002 000C 0001 0006 "
+            "0002 0001 0003 "   # glyph A..B: map to C
+            "0007 0005 0008 "   # glyph E..G: map to H
+            "FFFF FFFF FFFF"))  # end of search table
+        self.assertEqual(self.converter.read(reader, self.font, None), {
+            "A": "C",
+            "B": "C",
+            "E": "H",
+            "F": "H",
+            "G": "H",
+        })
+
+    def test_readFormat4(self):
+        reader = OTTableReader(deHexStr(
+            "0004 0006 0003 000C 0001 0006 "
+            "0002 0001 001E "  # glyph 1..2: mapping at offset 0x1E
+            "0005 0004 001E "  # glyph 4..5: mapping at offset 0x1E
+            "FFFF FFFF FFFF "  # end of search table
+            "0007 0008"))      # offset 0x18: glyphs [7, 8] = [G, H]
+        self.assertEqual(self.converter.read(reader, self.font, None), {
+            "A": "G",
+            "B": "H",
+            "D": "G",
+            "E": "H",
+        })
+
+    def test_readFormat6(self):
+        reader = OTTableReader(deHexStr(
+            "0006 0004 0002 0008 0001 0004 "
+            "0003 0001 "   # C --> A
+            "0005 0002 "   # E --> B
+            "FFFF FFFF"))  # end of search table
+        self.assertEqual(self.converter.read(reader, self.font, None), {
+            "C": "A",
+            "E": "B",
+        })
+
+    def test_readFormat8(self):
+        reader = OTTableReader(deHexStr(
+            "0008 "
+            "0003 0003 "        # first: C, count: 3
+            "0007 0001 0002"))  # [G, A, B]
+        self.assertEqual(self.converter.read(reader, self.font, None), {
+            "C": "G",
+            "D": "A",
+            "E": "B",
+        })
+
+    def test_readUnknownFormat(self):
+        reader = OTTableReader(deHexStr("0009"))
+        self.assertRaisesRegex(
+            AssertionError,
+            "unsupported lookup format: 9",
+            self.converter.read, reader, self.font, None)
+
+    def test_writeFormat0(self):
+        writer = OTTableWriter()
+        font = FakeFont(".notdef A B C".split())
+        self.converter.write(writer, font, {}, {
+            ".notdef": ".notdef",
+            "A": "C",
+            "B": "C",
+            "C": "A"
+        })
+        self.assertEqual(writer.getData(), deHexStr("0000 0000 0003 0003 0001"))
+
+    def test_writeFormat2(self):
+        writer = OTTableWriter()
+        font = FakeFont(".notdef A B C D E F G H".split())
+        self.converter.write(writer, font, {}, {
+            "B": "C",
+            "C": "C",
+            "D": "C",
+            "E": "C",
+            "G": "A",
+            "H": "A",
+        })
+        self.assertEqual(writer.getData(), deHexStr(
+            "0002 "            # format=2
+            "0006 "            # binSrchHeader.unitSize=6
+            "0002 "            # binSrchHeader.nUnits=2
+            "000C "            # binSrchHeader.searchRange=12
+            "0001 "            # binSrchHeader.entrySelector=1
+            "0000 "            # binSrchHeader.rangeShift=0
+            "0005 0002 0003 "  # segments[0].lastGlyph=E, firstGlyph=B, value=C
+            "0008 0007 0001 "  # segments[1].lastGlyph=H, firstGlyph=G, value=A
+            "FFFF FFFF 0000 "  # segments[2]=<END>
+        ))
+
+    def test_writeFormat6(self):
+        writer = OTTableWriter()
+        font = FakeFont(".notdef A B C D E".split())
+        self.converter.write(writer, font, {}, {
+            "A": "C",
+            "C": "B",
+            "D": "D",
+            "E": "E",
+        })
+        self.assertEqual(writer.getData(), deHexStr(
+            "0006 "         # format=6
+            "0004 "         # binSrchHeader.unitSize=4
+            "0004 "         # binSrchHeader.nUnits=4
+            "0010 "         # binSrchHeader.searchRange=16
+            "0002 "         # binSrchHeader.entrySelector=2
+            "0000 "         # binSrchHeader.rangeShift=0
+            "0001 0003 "    # entries[0].glyph=A, .value=C
+            "0003 0002 "    # entries[1].glyph=C, .value=B
+            "0004 0004 "    # entries[2].glyph=D, .value=D
+            "0005 0005 "    # entries[3].glyph=E, .value=E
+            "FFFF 0000 "    # entries[4]=<END>
+        ))
+
+    def test_writeFormat8(self):
+        writer = OTTableWriter()
+        font = FakeFont(".notdef A B C D E F G H".split())
+        self.converter.write(writer, font, {}, {
+            "B": "B",
+            "C": "A",
+            "D": "B",
+            "E": "C",
+            "F": "B",
+            "G": "A",
+        })
+        self.assertEqual(writer.getData(), deHexStr(
+            "0008 "                          # format=8
+            "0002 "                          # firstGlyph=B
+            "0006 "                          # glyphCount=6
+            "0002 0001 0002 0003 0002 0001"  # valueArray=[B, A, B, C, B, A]
+        ))
+
+    def test_xmlRead(self):
+        value = self.converter.xmlRead({}, [
+            ("Lookup", {"glyph": "A", "value": "A.alt"}, []),
+            ("Lookup", {"glyph": "B", "value": "B.alt"}, []),
+        ], self.font)
+        self.assertEqual(value, {"A": "A.alt", "B": "B.alt"})
+
+    def test_xmlWrite(self):
+        writer = makeXMLWriter()
+        self.converter.xmlWrite(writer, self.font,
+                                value={"A": "A.alt", "B": "B.alt"},
+                                name="Foo", attrs=[("attr", "val")])
+        xml = writer.file.getvalue().decode("utf-8").splitlines()
+        self.assertEqual(xml, [
+            '<Foo attr="val">',
+            '  <Lookup glyph="A" value="A.alt"/>',
+            '  <Lookup glyph="B" value="B.alt"/>',
+            '</Foo>',
+        ])
+
+
+class LazyListTest(unittest.TestCase):
+
+    def test_slice(self):
+        ll = otConverters._LazyList([10, 11, 12, 13])
+        sl = ll[:]
+
+        self.assertIsNot(sl, ll)
+        self.assertIsInstance(sl, list)
+        self.assertEqual([10, 11, 12, 13], sl)
+
+        self.assertEqual([11, 12], ll[1:3])
+
+    def test_getitem(self):
+        count = 2
+        reader = OTTableReader(b"\x00\xFE\xFF\x00\x00\x00", offset=1)
+        converter = otConverters.UInt8("UInt8", 0, None, None)
+        recordSize = converter.staticSize
+        l = otConverters._LazyList()
+        l.reader = reader
+        l.pos = l.reader.pos
+        l.font = None
+        l.conv = converter
+        l.recordSize = recordSize
+        l.extend(otConverters._MissingItem([i]) for i in range(count))
+        reader.advance(count * recordSize)
+
+        self.assertEqual(l[0], 254)
+        self.assertEqual(l[1], 255)
+
+    def test_add_both_LazyList(self):
+        ll1 = otConverters._LazyList([1])
+        ll2 = otConverters._LazyList([2])
+
+        l3 = ll1 + ll2
+
+        self.assertIsInstance(l3, list)
+        self.assertEqual([1, 2], l3)
+
+    def test_add_LazyList_and_list(self):
+        ll1 = otConverters._LazyList([1])
+        l2 = [2]
+
+        l3 = ll1 + l2
+
+        self.assertIsInstance(l3, list)
+        self.assertEqual([1, 2], l3)
+
+    def test_add_not_implemented(self):
+        with self.assertRaises(TypeError):
+            otConverters._LazyList() + 0
+        with self.assertRaises(TypeError):
+            otConverters._LazyList() + tuple()
+
+    def test_radd_list_and_LazyList(self):
+        l1 = [1]
+        ll2 = otConverters._LazyList([2])
+
+        l3 = l1 + ll2
+
+        self.assertIsInstance(l3, list)
+        self.assertEqual([1, 2], l3)
+
+    def test_radd_not_implemented(self):
+        with self.assertRaises(TypeError):
+            0 + otConverters._LazyList()
+        with self.assertRaises(TypeError):
+            tuple() + otConverters._LazyList()
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/otTables_test.py b/Tests/ttLib/tables/otTables_test.py
new file mode 100644
index 0000000..e02b22f
--- /dev/null
+++ b/Tests/ttLib/tables/otTables_test.py
@@ -0,0 +1,490 @@
+# coding: utf-8
+from __future__ import print_function, division, absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.misc.testTools import getXML, parseXML, FakeFont
+from fontTools.misc.textTools import deHexStr, hexStr
+from fontTools.misc.xmlWriter import XMLWriter
+from fontTools.ttLib.tables.otBase import OTTableReader, OTTableWriter
+import fontTools.ttLib.tables.otTables as otTables
+import unittest
+
+
+def makeCoverage(glyphs):
+    coverage = otTables.Coverage()
+    coverage.glyphs = glyphs
+    return coverage
+
+
+class SingleSubstTest(unittest.TestCase):
+    def setUp(self):
+        self.glyphs = ".notdef A B C D E a b c d e".split()
+        self.font = FakeFont(self.glyphs)
+
+    def test_postRead_format1(self):
+        table = otTables.SingleSubst()
+        table.Format = 1
+        rawTable = {
+            "Coverage": makeCoverage(["A", "B", "C"]),
+            "DeltaGlyphID": 5
+        }
+        table.postRead(rawTable, self.font)
+        self.assertEqual(table.mapping, {"A": "a", "B": "b", "C": "c"})
+
+    def test_postRead_format2(self):
+        table = otTables.SingleSubst()
+        table.Format = 2
+        rawTable = {
+            "Coverage": makeCoverage(["A", "B", "C"]),
+            "GlyphCount": 3,
+            "Substitute": ["c", "b", "a"]
+        }
+        table.postRead(rawTable, self.font)
+        self.assertEqual(table.mapping, {"A": "c", "B": "b", "C": "a"})
+
+    def test_postRead_formatUnknown(self):
+        table = otTables.SingleSubst()
+        table.Format = 987
+        rawTable = {"Coverage": makeCoverage(["A", "B", "C"])}
+        self.assertRaises(AssertionError, table.postRead, rawTable, self.font)
+
+    def test_preWrite_format1(self):
+        table = otTables.SingleSubst()
+        table.mapping = {"A": "a", "B": "b", "C": "c"}
+        rawTable = table.preWrite(self.font)
+        self.assertEqual(table.Format, 1)
+        self.assertEqual(rawTable["Coverage"].glyphs, ["A", "B", "C"])
+        self.assertEqual(rawTable["DeltaGlyphID"], 5)
+
+    def test_preWrite_format2(self):
+        table = otTables.SingleSubst()
+        table.mapping = {"A": "c", "B": "b", "C": "a"}
+        rawTable = table.preWrite(self.font)
+        self.assertEqual(table.Format, 2)
+        self.assertEqual(rawTable["Coverage"].glyphs, ["A", "B", "C"])
+        self.assertEqual(rawTable["Substitute"], ["c", "b", "a"])
+
+    def test_preWrite_emptyMapping(self):
+        table = otTables.SingleSubst()
+        table.mapping = {}
+        rawTable = table.preWrite(self.font)
+        self.assertEqual(table.Format, 2)
+        self.assertEqual(rawTable["Coverage"].glyphs, [])
+        self.assertEqual(rawTable["Substitute"], [])
+
+    def test_toXML2(self):
+        writer = XMLWriter(StringIO())
+        table = otTables.SingleSubst()
+        table.mapping = {"A": "a", "B": "b", "C": "c"}
+        table.toXML2(writer, self.font)
+        self.assertEqual(writer.file.getvalue().splitlines()[1:], [
+            '<Substitution in="A" out="a"/>',
+            '<Substitution in="B" out="b"/>',
+            '<Substitution in="C" out="c"/>',
+        ])
+
+    def test_fromXML(self):
+        table = otTables.SingleSubst()
+        for name, attrs, content in parseXML(
+                '<Substitution in="A" out="a"/>'
+                '<Substitution in="B" out="b"/>'
+                '<Substitution in="C" out="c"/>'):
+            table.fromXML(name, attrs, content, self.font)
+        self.assertEqual(table.mapping, {"A": "a", "B": "b", "C": "c"})
+
+
+class MultipleSubstTest(unittest.TestCase):
+    def setUp(self):
+        self.glyphs = ".notdef c f i t c_t f_f_i".split()
+        self.font = FakeFont(self.glyphs)
+
+    def test_postRead_format1(self):
+        makeSequence = otTables.MultipleSubst.makeSequence_
+        table = otTables.MultipleSubst()
+        table.Format = 1
+        rawTable = {
+            "Coverage": makeCoverage(["c_t", "f_f_i"]),
+            "Sequence": [
+                makeSequence(["c", "t"]),
+                makeSequence(["f", "f", "i"])
+            ]
+        }
+        table.postRead(rawTable, self.font)
+        self.assertEqual(table.mapping, {
+            "c_t": ["c", "t"],
+            "f_f_i": ["f", "f", "i"]
+        })
+
+    def test_postRead_formatUnknown(self):
+        table = otTables.MultipleSubst()
+        table.Format = 987
+        self.assertRaises(AssertionError, table.postRead, {}, self.font)
+
+    def test_preWrite_format1(self):
+        table = otTables.MultipleSubst()
+        table.mapping = {"c_t": ["c", "t"], "f_f_i": ["f", "f", "i"]}
+        rawTable = table.preWrite(self.font)
+        self.assertEqual(table.Format, 1)
+        self.assertEqual(rawTable["Coverage"].glyphs, ["c_t", "f_f_i"])
+
+    def test_toXML2(self):
+        writer = XMLWriter(StringIO())
+        table = otTables.MultipleSubst()
+        table.mapping = {"c_t": ["c", "t"], "f_f_i": ["f", "f", "i"]}
+        table.toXML2(writer, self.font)
+        self.assertEqual(writer.file.getvalue().splitlines()[1:], [
+            '<Substitution in="c_t" out="c,t"/>',
+            '<Substitution in="f_f_i" out="f,f,i"/>',
+        ])
+
+    def test_fromXML(self):
+        table = otTables.MultipleSubst()
+        for name, attrs, content in parseXML(
+                '<Substitution in="c_t" out="c,t"/>'
+                '<Substitution in="f_f_i" out="f,f,i"/>'):
+            table.fromXML(name, attrs, content, self.font)
+        self.assertEqual(table.mapping,
+                         {'c_t': ['c', 't'], 'f_f_i': ['f', 'f', 'i']})
+
+    def test_fromXML_oldFormat(self):
+        table = otTables.MultipleSubst()
+        for name, attrs, content in parseXML(
+                '<Coverage>'
+                '  <Glyph value="c_t"/>'
+                '  <Glyph value="f_f_i"/>'
+                '</Coverage>'
+                '<Sequence index="0">'
+                '  <Substitute index="0" value="c"/>'
+                '  <Substitute index="1" value="t"/>'
+                '</Sequence>'
+                '<Sequence index="1">'
+                '  <Substitute index="0" value="f"/>'
+                '  <Substitute index="1" value="f"/>'
+                '  <Substitute index="2" value="i"/>'
+                '</Sequence>'):
+            table.fromXML(name, attrs, content, self.font)
+        self.assertEqual(table.mapping,
+                         {'c_t': ['c', 't'], 'f_f_i': ['f', 'f', 'i']})
+
+    def test_fromXML_oldFormat_bug385(self):
+        # https://github.com/behdad/fonttools/issues/385
+        table = otTables.MultipleSubst()
+        table.Format = 1
+        for name, attrs, content in parseXML(
+                '<Coverage Format="1">'
+                '  <Glyph value="o"/>'
+                '  <Glyph value="l"/>'
+                '</Coverage>'
+                '<Sequence>'
+                '  <Substitute value="o"/>'
+                '  <Substitute value="l"/>'
+                '  <Substitute value="o"/>'
+                '</Sequence>'
+                '<Sequence>'
+                '  <Substitute value="o"/>'
+                '</Sequence>'):
+            table.fromXML(name, attrs, content, self.font)
+        self.assertEqual(table.mapping,
+                         {'o': ['o', 'l', 'o'], 'l': ['o']})
+
+
+class LigatureSubstTest(unittest.TestCase):
+    def setUp(self):
+        self.glyphs = ".notdef c f i t c_t f_f f_i f_f_i".split()
+        self.font = FakeFont(self.glyphs)
+
+    def makeLigature(self, s):
+        """'ffi' --> Ligature(LigGlyph='f_f_i', Component=['f', 'f', 'i'])"""
+        lig = otTables.Ligature()
+        lig.Component = list(s)
+        lig.LigGlyph = "_".join(lig.Component)
+        return lig
+
+    def makeLigatures(self, s):
+        """'ffi fi' --> [otTables.Ligature, otTables.Ligature]"""
+        return [self.makeLigature(lig) for lig in s.split()]
+
+    def test_postRead_format1(self):
+        table = otTables.LigatureSubst()
+        table.Format = 1
+        ligs_c = otTables.LigatureSet()
+        ligs_c.Ligature = self.makeLigatures("ct")
+        ligs_f = otTables.LigatureSet()
+        ligs_f.Ligature = self.makeLigatures("ffi ff fi")
+        rawTable = {
+            "Coverage": makeCoverage(["c", "f"]),
+            "LigatureSet": [ligs_c, ligs_f]
+        }
+        table.postRead(rawTable, self.font)
+        self.assertEqual(set(table.ligatures.keys()), {"c", "f"})
+        self.assertEqual(len(table.ligatures["c"]), 1)
+        self.assertEqual(table.ligatures["c"][0].LigGlyph, "c_t")
+        self.assertEqual(table.ligatures["c"][0].Component, ["c", "t"])
+        self.assertEqual(len(table.ligatures["f"]), 3)
+        self.assertEqual(table.ligatures["f"][0].LigGlyph, "f_f_i")
+        self.assertEqual(table.ligatures["f"][0].Component, ["f", "f", "i"])
+        self.assertEqual(table.ligatures["f"][1].LigGlyph, "f_f")
+        self.assertEqual(table.ligatures["f"][1].Component, ["f", "f"])
+        self.assertEqual(table.ligatures["f"][2].LigGlyph, "f_i")
+        self.assertEqual(table.ligatures["f"][2].Component, ["f", "i"])
+
+    def test_postRead_formatUnknown(self):
+        table = otTables.LigatureSubst()
+        table.Format = 987
+        rawTable = {"Coverage": makeCoverage(["f"])}
+        self.assertRaises(AssertionError, table.postRead, rawTable, self.font)
+
+    def test_preWrite_format1(self):
+        table = otTables.LigatureSubst()
+        table.ligatures = {
+            "c": self.makeLigatures("ct"),
+            "f": self.makeLigatures("ffi ff fi")
+        }
+        rawTable = table.preWrite(self.font)
+        self.assertEqual(table.Format, 1)
+        self.assertEqual(rawTable["Coverage"].glyphs, ["c", "f"])
+        [c, f] = rawTable["LigatureSet"]
+        self.assertIsInstance(c, otTables.LigatureSet)
+        self.assertIsInstance(f, otTables.LigatureSet)
+        [ct] = c.Ligature
+        self.assertIsInstance(ct, otTables.Ligature)
+        self.assertEqual(ct.LigGlyph, "c_t")
+        self.assertEqual(ct.Component, ["c", "t"])
+        [ffi, ff, fi] = f.Ligature
+        self.assertIsInstance(ffi, otTables.Ligature)
+        self.assertEqual(ffi.LigGlyph, "f_f_i")
+        self.assertEqual(ffi.Component, ["f", "f", "i"])
+        self.assertIsInstance(ff, otTables.Ligature)
+        self.assertEqual(ff.LigGlyph, "f_f")
+        self.assertEqual(ff.Component, ["f", "f"])
+        self.assertIsInstance(fi, otTables.Ligature)
+        self.assertEqual(fi.LigGlyph, "f_i")
+        self.assertEqual(fi.Component, ["f", "i"])
+
+    def test_toXML2(self):
+        writer = XMLWriter(StringIO())
+        table = otTables.LigatureSubst()
+        table.ligatures = {
+            "c": self.makeLigatures("ct"),
+            "f": self.makeLigatures("ffi ff fi")
+        }
+        table.toXML2(writer, self.font)
+        self.assertEqual(writer.file.getvalue().splitlines()[1:], [
+            '<LigatureSet glyph="c">',
+            '  <Ligature components="c,t" glyph="c_t"/>',
+            '</LigatureSet>',
+            '<LigatureSet glyph="f">',
+            '  <Ligature components="f,f,i" glyph="f_f_i"/>',
+            '  <Ligature components="f,f" glyph="f_f"/>',
+            '  <Ligature components="f,i" glyph="f_i"/>',
+            '</LigatureSet>'
+        ])
+
+    def test_fromXML(self):
+        table = otTables.LigatureSubst()
+        for name, attrs, content in parseXML(
+                '<LigatureSet glyph="f">'
+                '  <Ligature components="f,f,i" glyph="f_f_i"/>'
+                '  <Ligature components="f,f" glyph="f_f"/>'
+                '</LigatureSet>'):
+            table.fromXML(name, attrs, content, self.font)
+        self.assertEqual(set(table.ligatures.keys()), {"f"})
+        [ffi, ff] = table.ligatures["f"]
+        self.assertEqual(ffi.LigGlyph, "f_f_i")
+        self.assertEqual(ffi.Component, ["f", "f", "i"])
+        self.assertEqual(ff.LigGlyph, "f_f")
+        self.assertEqual(ff.Component, ["f", "f"])
+
+
+class AlternateSubstTest(unittest.TestCase):
+    def setUp(self):
+        self.glyphs = ".notdef G G.alt1 G.alt2 Z Z.fina".split()
+        self.font = FakeFont(self.glyphs)
+
+    def makeAlternateSet(self, s):
+        result = otTables.AlternateSet()
+        result.Alternate = s.split()
+        return result
+
+    def test_postRead_format1(self):
+        table = otTables.AlternateSubst()
+        table.Format = 1
+        rawTable = {
+            "Coverage": makeCoverage(["G", "Z"]),
+            "AlternateSet": [
+                self.makeAlternateSet("G.alt2 G.alt1"),
+                self.makeAlternateSet("Z.fina")
+            ]
+        }
+        table.postRead(rawTable, self.font)
+        self.assertEqual(table.alternates, {
+            "G": ["G.alt2", "G.alt1"],
+            "Z": ["Z.fina"]
+        })
+
+    def test_postRead_formatUnknown(self):
+        table = otTables.AlternateSubst()
+        table.Format = 987
+        self.assertRaises(AssertionError, table.postRead, {}, self.font)
+
+    def test_preWrite_format1(self):
+        table = otTables.AlternateSubst()
+        table.alternates = {"G": ["G.alt2", "G.alt1"], "Z": ["Z.fina"]}
+        rawTable = table.preWrite(self.font)
+        self.assertEqual(table.Format, 1)
+        self.assertEqual(rawTable["Coverage"].glyphs, ["G", "Z"])
+        [g, z] = rawTable["AlternateSet"]
+        self.assertIsInstance(g, otTables.AlternateSet)
+        self.assertEqual(g.Alternate, ["G.alt2", "G.alt1"])
+        self.assertIsInstance(z, otTables.AlternateSet)
+        self.assertEqual(z.Alternate, ["Z.fina"])
+
+    def test_toXML2(self):
+        writer = XMLWriter(StringIO())
+        table = otTables.AlternateSubst()
+        table.alternates = {"G": ["G.alt2", "G.alt1"], "Z": ["Z.fina"]}
+        table.toXML2(writer, self.font)
+        self.assertEqual(writer.file.getvalue().splitlines()[1:], [
+            '<AlternateSet glyph="G">',
+            '  <Alternate glyph="G.alt2"/>',
+            '  <Alternate glyph="G.alt1"/>',
+            '</AlternateSet>',
+            '<AlternateSet glyph="Z">',
+            '  <Alternate glyph="Z.fina"/>',
+            '</AlternateSet>'
+        ])
+
+    def test_fromXML(self):
+        table = otTables.AlternateSubst()
+        for name, attrs, content in parseXML(
+                '<AlternateSet glyph="G">'
+                '  <Alternate glyph="G.alt2"/>'
+                '  <Alternate glyph="G.alt1"/>'
+                '</AlternateSet>'
+                '<AlternateSet glyph="Z">'
+                '  <Alternate glyph="Z.fina"/>'
+                '</AlternateSet>'):
+            table.fromXML(name, attrs, content, self.font)
+        self.assertEqual(table.alternates, {
+            "G": ["G.alt2", "G.alt1"],
+            "Z": ["Z.fina"]
+        })
+
+
+class RearrangementMorphActionTest(unittest.TestCase):
+    def setUp(self):
+        self.font = FakeFont(['.notdef', 'A', 'B', 'C'])
+
+    def testCompile(self):
+        r = otTables.RearrangementMorphAction()
+        r.NewState = 0x1234
+        r.MarkFirst = r.DontAdvance = r.MarkLast = True
+        r.ReservedFlags, r.Verb = 0x1FF0, 0xD
+        writer = OTTableWriter()
+        r.compile(writer, self.font, actionIndex=None)
+        self.assertEqual(hexStr(writer.getAllData()), "1234fffd")
+
+    def testDecompileToXML(self):
+        r = otTables.RearrangementMorphAction()
+        r.decompile(OTTableReader(deHexStr("1234fffd")),
+                    self.font, actionReader=None)
+        toXML = lambda w, f: r.toXML(w, f, {"Test": "Foo"}, "Transition")
+        self.assertEqual(getXML(toXML, self.font), [
+                '<Transition Test="Foo">',
+                '  <NewState value="4660"/>',  # 0x1234 = 4660
+                '  <Flags value="MarkFirst,DontAdvance,MarkLast"/>',
+                '  <ReservedFlags value="0x1FF0"/>',
+                '  <Verb value="13"/><!-- ABxCD ⇒ CDxBA -->',
+                '</Transition>',
+        ])
+
+
+class ContextualMorphActionTest(unittest.TestCase):
+    def setUp(self):
+        self.font = FakeFont(['.notdef', 'A', 'B', 'C'])
+
+    def testCompile(self):
+        a = otTables.ContextualMorphAction()
+        a.NewState = 0x1234
+        a.SetMark, a.DontAdvance, a.ReservedFlags = True, True, 0x3117
+        a.MarkIndex, a.CurrentIndex = 0xDEAD, 0xBEEF
+        writer = OTTableWriter()
+        a.compile(writer, self.font, actionIndex=None)
+        self.assertEqual(hexStr(writer.getAllData()), "1234f117deadbeef")
+
+    def testDecompileToXML(self):
+        a = otTables.ContextualMorphAction()
+        a.decompile(OTTableReader(deHexStr("1234f117deadbeef")),
+                    self.font, actionReader=None)
+        toXML = lambda w, f: a.toXML(w, f, {"Test": "Foo"}, "Transition")
+        self.assertEqual(getXML(toXML, self.font), [
+                '<Transition Test="Foo">',
+                '  <NewState value="4660"/>',  # 0x1234 = 4660
+                '  <Flags value="SetMark,DontAdvance"/>',
+                '  <ReservedFlags value="0x3117"/>',
+                '  <MarkIndex value="57005"/>',  # 0xDEAD = 57005
+                '  <CurrentIndex value="48879"/>',  # 0xBEEF = 48879
+                '</Transition>',
+        ])
+
+
+class LigatureMorphActionTest(unittest.TestCase):
+    def setUp(self):
+        self.font = FakeFont(['.notdef', 'A', 'B', 'C'])
+
+    def testDecompileToXML(self):
+        a = otTables.LigatureMorphAction()
+        actionReader = OTTableReader(deHexStr("DEADBEEF 7FFFFFFE 80000003"))
+        a.decompile(OTTableReader(deHexStr("1234FAB30001")),
+                    self.font, actionReader)
+        toXML = lambda w, f: a.toXML(w, f, {"Test": "Foo"}, "Transition")
+        self.assertEqual(getXML(toXML, self.font), [
+                '<Transition Test="Foo">',
+                '  <NewState value="4660"/>',  # 0x1234 = 4660
+                '  <Flags value="SetComponent,DontAdvance"/>',
+                '  <ReservedFlags value="0x1AB3"/>',
+                '  <Action GlyphIndexDelta="-2" Flags="Store"/>',
+                '  <Action GlyphIndexDelta="3"/>',
+                '</Transition>',
+        ])
+
+
+class InsertionMorphActionTest(unittest.TestCase):
+    MORPH_ACTION_XML = [
+        '<Transition Test="Foo">',
+        '  <NewState value="4660"/>',  # 0x1234 = 4660
+        '  <Flags value="SetMark,DontAdvance,CurrentIsKashidaLike,'
+              'MarkedIsKashidaLike,CurrentInsertBefore,MarkedInsertBefore"/>',
+        '  <CurrentInsertionAction glyph="B"/>',
+        '  <CurrentInsertionAction glyph="C"/>',
+        '  <MarkedInsertionAction glyph="B"/>',
+        '  <MarkedInsertionAction glyph="A"/>',
+        '  <MarkedInsertionAction glyph="D"/>',
+        '</Transition>'
+    ]
+
+    def setUp(self):
+        self.font = FakeFont(['.notdef', 'A', 'B', 'C', 'D'])
+        self.maxDiff = None
+
+    def testDecompileToXML(self):
+        a = otTables.InsertionMorphAction()
+        actionReader = OTTableReader(
+            deHexStr("DEAD BEEF 0002 0001 0004 0002 0003 DEAD BEEF"))
+        a.decompile(OTTableReader(deHexStr("1234 FC43 0005 0002")),
+                    self.font, actionReader)
+        toXML = lambda w, f: a.toXML(w, f, {"Test": "Foo"}, "Transition")
+        self.assertEqual(getXML(toXML, self.font), self.MORPH_ACTION_XML)
+
+    def testCompileFromXML(self):
+        a = otTables.InsertionMorphAction()
+        for name, attrs, content in parseXML(self.MORPH_ACTION_XML):
+            a.fromXML(name, attrs, content, self.font)
+        writer = OTTableWriter()
+        a.compile(writer, self.font,
+	          actionIndex={('B', 'C'): 9, ('B', 'A', 'D'): 7})
+        self.assertEqual(hexStr(writer.getAllData()), "1234fc4300090007")
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/tables_test.py b/Tests/ttLib/tables/tables_test.py
new file mode 100644
index 0000000..9d03814
--- /dev/null
+++ b/Tests/ttLib/tables/tables_test.py
@@ -0,0 +1,329 @@
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.ttLib import TTFont, tagToXML
+import os
+import sys
+import re
+import contextlib
+import pytest
+
+try:
+    import unicodedata2
+except ImportError:
+    if sys.version_info[:2] < (3, 6):
+        unicodedata2 = None
+    else:
+        # on 3.6 the built-in unicodedata is the same as unicodedata2 backport
+        import unicodedata
+        unicodedata2 = unicodedata
+
+
+# Font files in data/*.{o,t}tf; output gets compared to data/*.ttx.*
+TESTS = {
+    "aots/base.otf":                                ('CFF ', 'cmap', 'head',
+                                                     'hhea', 'hmtx', 'maxp',
+                                                     'name', 'OS/2', 'post'),
+    "aots/classdef1_font1.otf":                     ('GSUB',),
+    "aots/classdef1_font2.otf":                     ('GSUB',),
+    "aots/classdef1_font3.otf":                     ('GSUB',),
+    "aots/classdef1_font4.otf":                     ('GSUB',),
+    "aots/classdef2_font1.otf":                     ('GSUB',),
+    "aots/classdef2_font2.otf":                     ('GSUB',),
+    "aots/classdef2_font3.otf":                     ('GSUB',),
+    "aots/classdef2_font4.otf":                     ('GSUB',),
+    "aots/cmap0_font1.otf":                         ('cmap',),
+    "aots/cmap10_font1.otf":                        ('cmap',),
+    "aots/cmap10_font2.otf":                        ('cmap',),
+    "aots/cmap12_font1.otf":                        ('cmap',),
+    "aots/cmap14_font1.otf":                        ('cmap',),
+    "aots/cmap2_font1.otf":                         ('cmap',),
+    "aots/cmap4_font1.otf":                         ('cmap',),
+    "aots/cmap4_font2.otf":                         ('cmap',),
+    "aots/cmap4_font3.otf":                         ('cmap',),
+    "aots/cmap4_font4.otf":                         ('cmap',),
+    "aots/cmap6_font1.otf":                         ('cmap',),
+    "aots/cmap6_font2.otf":                         ('cmap',),
+    "aots/cmap8_font1.otf":                         ('cmap',),
+    "aots/cmap_composition_font1.otf":              ('cmap',),
+    "aots/cmap_subtableselection_font1.otf":        ('cmap',),
+    "aots/cmap_subtableselection_font2.otf":        ('cmap',),
+    "aots/cmap_subtableselection_font3.otf":        ('cmap',),
+    "aots/cmap_subtableselection_font4.otf":        ('cmap',),
+    "aots/cmap_subtableselection_font5.otf":        ('cmap',),
+    "aots/gpos1_1_lookupflag_f1.otf":               ('GDEF', 'GPOS'),
+    "aots/gpos1_1_simple_f1.otf":                   ('GPOS',),
+    "aots/gpos1_1_simple_f2.otf":                   ('GPOS',),
+    "aots/gpos1_1_simple_f3.otf":                   ('GPOS',),
+    "aots/gpos1_1_simple_f4.otf":                   ('GPOS',),
+    "aots/gpos1_2_font1.otf":                       ('GPOS',),
+    "aots/gpos1_2_font2.otf":                       ('GDEF', 'GPOS'),
+    "aots/gpos2_1_font6.otf":                       ('GPOS',),
+    "aots/gpos2_1_font7.otf":                       ('GPOS',),
+    "aots/gpos2_1_lookupflag_f1.otf":               ('GDEF', 'GPOS'),
+    "aots/gpos2_1_lookupflag_f2.otf":               ('GDEF', 'GPOS'),
+    "aots/gpos2_1_next_glyph_f1.otf":               ('GPOS',),
+    "aots/gpos2_1_next_glyph_f2.otf":               ('GPOS',),
+    "aots/gpos2_1_simple_f1.otf":                   ('GPOS',),
+    "aots/gpos2_2_font1.otf":                       ('GPOS',),
+    "aots/gpos2_2_font2.otf":                       ('GDEF', 'GPOS'),
+    "aots/gpos2_2_font3.otf":                       ('GDEF', 'GPOS'),
+    "aots/gpos2_2_font4.otf":                       ('GPOS',),
+    "aots/gpos2_2_font5.otf":                       ('GPOS',),
+    "aots/gpos3_font1.otf":                         ('GPOS',),
+    "aots/gpos3_font2.otf":                         ('GDEF', 'GPOS'),
+    "aots/gpos3_font3.otf":                         ('GDEF', 'GPOS'),
+    "aots/gpos4_lookupflag_f1.otf":                 ('GDEF', 'GPOS'),
+    "aots/gpos4_lookupflag_f2.otf":                 ('GDEF', 'GPOS'),
+    "aots/gpos4_multiple_anchors_1.otf":            ('GDEF', 'GPOS'),
+    "aots/gpos4_simple_1.otf":                      ('GDEF', 'GPOS'),
+    "aots/gpos5_font1.otf":                         ('GDEF', 'GPOS', 'GSUB'),
+    "aots/gpos6_font1.otf":                         ('GDEF', 'GPOS'),
+    "aots/gpos7_1_font1.otf":                       ('GPOS',),
+    "aots/gpos9_font1.otf":                         ('GPOS',),
+    "aots/gpos9_font2.otf":                         ('GPOS',),
+    "aots/gpos_chaining1_boundary_f1.otf":          ('GDEF', 'GPOS'),
+    "aots/gpos_chaining1_boundary_f2.otf":          ('GDEF', 'GPOS'),
+    "aots/gpos_chaining1_boundary_f3.otf":          ('GDEF', 'GPOS'),
+    "aots/gpos_chaining1_boundary_f4.otf":          ('GDEF', 'GPOS'),
+    "aots/gpos_chaining1_lookupflag_f1.otf":        ('GDEF', 'GPOS'),
+    "aots/gpos_chaining1_multiple_subrules_f1.otf": ('GDEF', 'GPOS'),
+    "aots/gpos_chaining1_multiple_subrules_f2.otf": ('GDEF', 'GPOS'),
+    "aots/gpos_chaining1_next_glyph_f1.otf":        ('GDEF', 'GPOS'),
+    "aots/gpos_chaining1_simple_f1.otf":            ('GDEF', 'GPOS'),
+    "aots/gpos_chaining1_simple_f2.otf":            ('GDEF', 'GPOS'),
+    "aots/gpos_chaining1_successive_f1.otf":        ('GDEF', 'GPOS'),
+    "aots/gpos_chaining2_boundary_f1.otf":          ('GDEF', 'GPOS'),
+    "aots/gpos_chaining2_boundary_f2.otf":          ('GDEF', 'GPOS'),
+    "aots/gpos_chaining2_boundary_f3.otf":          ('GDEF', 'GPOS'),
+    "aots/gpos_chaining2_boundary_f4.otf":          ('GDEF', 'GPOS'),
+    "aots/gpos_chaining2_lookupflag_f1.otf":        ('GDEF', 'GPOS'),
+    "aots/gpos_chaining2_multiple_subrules_f1.otf": ('GDEF', 'GPOS'),
+    "aots/gpos_chaining2_multiple_subrules_f2.otf": ('GDEF', 'GPOS'),
+    "aots/gpos_chaining2_next_glyph_f1.otf":        ('GDEF', 'GPOS'),
+    "aots/gpos_chaining2_simple_f1.otf":            ('GDEF', 'GPOS'),
+    "aots/gpos_chaining2_simple_f2.otf":            ('GDEF', 'GPOS'),
+    "aots/gpos_chaining2_successive_f1.otf":        ('GDEF', 'GPOS'),
+    "aots/gpos_chaining3_boundary_f1.otf":          ('GDEF', 'GPOS'),
+    "aots/gpos_chaining3_boundary_f2.otf":          ('GDEF', 'GPOS'),
+    "aots/gpos_chaining3_boundary_f3.otf":          ('GDEF', 'GPOS'),
+    "aots/gpos_chaining3_boundary_f4.otf":          ('GDEF', 'GPOS'),
+    "aots/gpos_chaining3_lookupflag_f1.otf":        ('GDEF', 'GPOS'),
+    "aots/gpos_chaining3_next_glyph_f1.otf":        ('GDEF', 'GPOS'),
+    "aots/gpos_chaining3_simple_f1.otf":            ('GDEF', 'GPOS'),
+    "aots/gpos_chaining3_simple_f2.otf":            ('GDEF', 'GPOS'),
+    "aots/gpos_chaining3_successive_f1.otf":        ('GDEF', 'GPOS'),
+    "aots/gpos_context1_boundary_f1.otf":           ('GDEF', 'GPOS'),
+    "aots/gpos_context1_boundary_f2.otf":           ('GDEF', 'GPOS'),
+    "aots/gpos_context1_expansion_f1.otf":          ('GDEF', 'GPOS'),
+    "aots/gpos_context1_lookupflag_f1.otf":         ('GDEF', 'GPOS'),
+    "aots/gpos_context1_lookupflag_f2.otf":         ('GDEF', 'GPOS'),
+    "aots/gpos_context1_multiple_subrules_f1.otf":  ('GDEF', 'GPOS'),
+    "aots/gpos_context1_multiple_subrules_f2.otf":  ('GDEF', 'GPOS'),
+    "aots/gpos_context1_next_glyph_f1.otf":         ('GDEF', 'GPOS'),
+    "aots/gpos_context1_simple_f1.otf":             ('GDEF', 'GPOS'),
+    "aots/gpos_context1_simple_f2.otf":             ('GDEF', 'GPOS'),
+    "aots/gpos_context1_successive_f1.otf":         ('GDEF', 'GPOS'),
+    "aots/gpos_context2_boundary_f1.otf":           ('GDEF', 'GPOS'),
+    "aots/gpos_context2_boundary_f2.otf":           ('GDEF', 'GPOS'),
+    "aots/gpos_context2_classes_f1.otf":            ('GDEF', 'GPOS'),
+    "aots/gpos_context2_classes_f2.otf":            ('GDEF', 'GPOS'),
+    "aots/gpos_context2_expansion_f1.otf":          ('GDEF', 'GPOS'),
+    "aots/gpos_context2_lookupflag_f1.otf":         ('GDEF', 'GPOS'),
+    "aots/gpos_context2_lookupflag_f2.otf":         ('GDEF', 'GPOS'),
+    "aots/gpos_context2_multiple_subrules_f1.otf":  ('GDEF', 'GPOS'),
+    "aots/gpos_context2_multiple_subrules_f2.otf":  ('GDEF', 'GPOS'),
+    "aots/gpos_context2_next_glyph_f1.otf":         ('GDEF', 'GPOS'),
+    "aots/gpos_context2_simple_f1.otf":             ('GDEF', 'GPOS'),
+    "aots/gpos_context2_simple_f2.otf":             ('GDEF', 'GPOS'),
+    "aots/gpos_context2_successive_f1.otf":         ('GDEF', 'GPOS'),
+    "aots/gpos_context3_boundary_f1.otf":           ('GDEF', 'GPOS'),
+    "aots/gpos_context3_boundary_f2.otf":           ('GDEF', 'GPOS'),
+    "aots/gpos_context3_lookupflag_f1.otf":         ('GDEF', 'GPOS'),
+    "aots/gpos_context3_lookupflag_f2.otf":         ('GDEF', 'GPOS'),
+    "aots/gpos_context3_next_glyph_f1.otf":         ('GDEF', 'GPOS'),
+    "aots/gpos_context3_simple_f1.otf":             ('GDEF', 'GPOS'),
+    "aots/gpos_context3_successive_f1.otf":         ('GDEF', 'GPOS'),
+    "aots/gsub1_1_lookupflag_f1.otf":               ('GDEF', 'GSUB'),
+    "aots/gsub1_1_modulo_f1.otf":                   ('GSUB',),
+    "aots/gsub1_1_simple_f1.otf":                   ('GSUB',),
+    "aots/gsub1_2_lookupflag_f1.otf":               ('GDEF', 'GSUB'),
+    "aots/gsub1_2_simple_f1.otf":                   ('GSUB',),
+    "aots/gsub2_1_lookupflag_f1.otf":               ('GDEF', 'GSUB'),
+    "aots/gsub2_1_multiple_sequences_f1.otf":       ('GSUB',),
+    "aots/gsub2_1_simple_f1.otf":                   ('GSUB',),
+    "aots/gsub3_1_lookupflag_f1.otf":               ('GDEF', 'GSUB'),
+    "aots/gsub3_1_multiple_f1.otf":                 ('GSUB',),
+    "aots/gsub3_1_simple_f1.otf":                   ('GSUB',),
+    "aots/gsub4_1_lookupflag_f1.otf":               ('GDEF', 'GSUB'),
+    "aots/gsub4_1_multiple_ligatures_f1.otf":       ('GSUB',),
+    "aots/gsub4_1_multiple_ligatures_f2.otf":       ('GSUB',),
+    "aots/gsub4_1_multiple_ligsets_f1.otf":         ('GSUB',),
+    "aots/gsub4_1_simple_f1.otf":                   ('GSUB',),
+    "aots/gsub7_font1.otf":                         ('GSUB',),
+    "aots/gsub7_font2.otf":                         ('GSUB',),
+    "aots/gsub_chaining1_boundary_f1.otf":          ('GDEF', 'GSUB'),
+    "aots/gsub_chaining1_boundary_f2.otf":          ('GDEF', 'GSUB'),
+    "aots/gsub_chaining1_boundary_f3.otf":          ('GDEF', 'GSUB'),
+    "aots/gsub_chaining1_boundary_f4.otf":          ('GDEF', 'GSUB'),
+    "aots/gsub_chaining1_lookupflag_f1.otf":        ('GDEF', 'GSUB'),
+    "aots/gsub_chaining1_multiple_subrules_f1.otf": ('GDEF', 'GSUB'),
+    "aots/gsub_chaining1_multiple_subrules_f2.otf": ('GDEF', 'GSUB'),
+    "aots/gsub_chaining1_next_glyph_f1.otf":        ('GDEF', 'GSUB'),
+    "aots/gsub_chaining1_simple_f1.otf":            ('GDEF', 'GSUB'),
+    "aots/gsub_chaining1_simple_f2.otf":            ('GDEF', 'GSUB'),
+    "aots/gsub_chaining1_successive_f1.otf":        ('GDEF', 'GSUB'),
+    "aots/gsub_chaining2_boundary_f1.otf":          ('GDEF', 'GSUB'),
+    "aots/gsub_chaining2_boundary_f2.otf":          ('GDEF', 'GSUB'),
+    "aots/gsub_chaining2_boundary_f3.otf":          ('GDEF', 'GSUB'),
+    "aots/gsub_chaining2_boundary_f4.otf":          ('GDEF', 'GSUB'),
+    "aots/gsub_chaining2_lookupflag_f1.otf":        ('GDEF', 'GSUB'),
+    "aots/gsub_chaining2_multiple_subrules_f1.otf": ('GDEF', 'GSUB'),
+    "aots/gsub_chaining2_multiple_subrules_f2.otf": ('GDEF', 'GSUB'),
+    "aots/gsub_chaining2_next_glyph_f1.otf":        ('GDEF', 'GSUB'),
+    "aots/gsub_chaining2_simple_f1.otf":            ('GDEF', 'GSUB'),
+    "aots/gsub_chaining2_simple_f2.otf":            ('GDEF', 'GSUB'),
+    "aots/gsub_chaining2_successive_f1.otf":        ('GDEF', 'GSUB'),
+    "aots/gsub_chaining3_boundary_f1.otf":          ('GDEF', 'GSUB'),
+    "aots/gsub_chaining3_boundary_f2.otf":          ('GDEF', 'GSUB'),
+    "aots/gsub_chaining3_boundary_f3.otf":          ('GDEF', 'GSUB'),
+    "aots/gsub_chaining3_boundary_f4.otf":          ('GDEF', 'GSUB'),
+    "aots/gsub_chaining3_lookupflag_f1.otf":        ('GDEF', 'GSUB'),
+    "aots/gsub_chaining3_next_glyph_f1.otf":        ('GDEF', 'GSUB'),
+    "aots/gsub_chaining3_simple_f1.otf":            ('GDEF', 'GSUB'),
+    "aots/gsub_chaining3_simple_f2.otf":            ('GDEF', 'GSUB'),
+    "aots/gsub_chaining3_successive_f1.otf":        ('GDEF', 'GSUB'),
+    "aots/gsub_context1_boundary_f1.otf":           ('GDEF', 'GSUB'),
+    "aots/gsub_context1_boundary_f2.otf":           ('GDEF', 'GSUB'),
+    "aots/gsub_context1_expansion_f1.otf":          ('GDEF', 'GSUB'),
+    "aots/gsub_context1_lookupflag_f1.otf":         ('GDEF', 'GSUB'),
+    "aots/gsub_context1_lookupflag_f2.otf":         ('GDEF', 'GSUB'),
+    "aots/gsub_context1_multiple_subrules_f1.otf":  ('GDEF', 'GSUB'),
+    "aots/gsub_context1_multiple_subrules_f2.otf":  ('GDEF', 'GSUB'),
+    "aots/gsub_context1_next_glyph_f1.otf":         ('GDEF', 'GSUB'),
+    "aots/gsub_context1_simple_f1.otf":             ('GDEF', 'GSUB'),
+    "aots/gsub_context1_simple_f2.otf":             ('GDEF', 'GSUB'),
+    "aots/gsub_context1_successive_f1.otf":         ('GDEF', 'GSUB'),
+    "aots/gsub_context2_boundary_f1.otf":           ('GDEF', 'GSUB'),
+    "aots/gsub_context2_boundary_f2.otf":           ('GDEF', 'GSUB'),
+    "aots/gsub_context2_classes_f1.otf":            ('GDEF', 'GSUB'),
+    "aots/gsub_context2_classes_f2.otf":            ('GDEF', 'GSUB'),
+    "aots/gsub_context2_expansion_f1.otf":          ('GDEF', 'GSUB'),
+    "aots/gsub_context2_lookupflag_f1.otf":         ('GDEF', 'GSUB'),
+    "aots/gsub_context2_lookupflag_f2.otf":         ('GDEF', 'GSUB'),
+    "aots/gsub_context2_multiple_subrules_f1.otf":  ('GDEF', 'GSUB'),
+    "aots/gsub_context2_multiple_subrules_f2.otf":  ('GDEF', 'GSUB'),
+    "aots/gsub_context2_next_glyph_f1.otf":         ('GDEF', 'GSUB'),
+    "aots/gsub_context2_simple_f1.otf":             ('GDEF', 'GSUB'),
+    "aots/gsub_context2_simple_f2.otf":             ('GDEF', 'GSUB'),
+    "aots/gsub_context2_successive_f1.otf":         ('GDEF', 'GSUB'),
+    "aots/gsub_context3_boundary_f1.otf":           ('GDEF', 'GSUB'),
+    "aots/gsub_context3_boundary_f2.otf":           ('GDEF', 'GSUB'),
+    "aots/gsub_context3_lookupflag_f1.otf":         ('GDEF', 'GSUB'),
+    "aots/gsub_context3_lookupflag_f2.otf":         ('GDEF', 'GSUB'),
+    "aots/gsub_context3_next_glyph_f1.otf":         ('GDEF', 'GSUB'),
+    "aots/gsub_context3_simple_f1.otf":             ('GDEF', 'GSUB'),
+    "aots/gsub_context3_successive_f1.otf":         ('GDEF', 'GSUB'),
+    "aots/lookupflag_ignore_attach_f1.otf":         ('GDEF', 'GSUB'),
+    "aots/lookupflag_ignore_base_f1.otf":           ('GDEF', 'GSUB'),
+    "aots/lookupflag_ignore_combination_f1.otf":    ('GDEF', 'GSUB'),
+    "aots/lookupflag_ignore_ligatures_f1.otf":      ('GDEF', 'GSUB'),
+    "aots/lookupflag_ignore_marks_f1.otf":          ('GDEF', 'GSUB'),
+    "graphite/graphite_tests.ttf":                  ('Silf', 'Glat', 'Feat', 'Sill'),
+}
+
+
+TEST_REQUIREMENTS = {
+    "aots/cmap4_font4.otf":                         ("unicodedata2",),
+}
+
+
+ttLibVersion_RE = re.compile(r' ttLibVersion=".*"')
+
+
+def getpath(testfile):
+    path = os.path.dirname(__file__)
+    return os.path.join(path, "data", testfile)
+
+
+def read_expected_ttx(testfile, tableTag):
+    name = os.path.splitext(testfile)[0]
+    xml_expected_path = getpath("%s.ttx.%s" % (name, tagToXML(tableTag)))
+    with open(xml_expected_path, 'r', encoding="utf-8") as xml_file:
+        xml_expected = ttLibVersion_RE.sub('', xml_file.read())
+    return xml_expected
+
+
+def dump_ttx(font, tableTag):
+    f = UnicodeIO()
+    font.saveXML(f, newlinestr='\n', tables=[tableTag])
+    return ttLibVersion_RE.sub('', f.getvalue())
+
+
+def load_ttx(ttx):
+    f = UnicodeIO()
+    f.write(ttx)
+    f.seek(0)
+    font = TTFont()
+    font.importXML(f)
+    return font
+
+
+@contextlib.contextmanager
+def open_font(testfile):
+    font = TTFont(getpath(testfile))
+    try:
+        yield font
+    finally:
+        font.close()
+
+
+def _skip_if_requirement_missing(testfile):
+    if testfile in TEST_REQUIREMENTS:
+        for req in TEST_REQUIREMENTS[testfile]:
+            if globals()[req] is None:
+                pytest.skip('%s not installed' % req)
+
+
+def test_xml_from_binary(testfile, tableTag):
+    """Check XML from decompiled object."""
+    _skip_if_requirement_missing(testfile)
+
+    xml_expected = read_expected_ttx(testfile, tableTag)
+
+    with open_font(testfile) as font:
+        xml_from_binary = dump_ttx(font, tableTag)
+
+    assert xml_expected == xml_from_binary
+
+
+def test_xml_from_xml(testfile, tableTag):
+    """Check XML from object read from XML."""
+    _skip_if_requirement_missing(testfile)
+
+    xml_expected = read_expected_ttx(testfile, tableTag)
+
+    font = load_ttx(xml_expected)
+    name = os.path.splitext(testfile)[0]
+    setupfile = getpath("%s.ttx.%s.setup" % (name, tagToXML(tableTag)))
+    if os.path.exists(setupfile):
+#        import pdb; pdb.set_trace()
+        font.importXML(setupfile)
+    xml_from_xml = dump_ttx(font, tableTag)
+
+    assert xml_expected == xml_from_xml
+
+
+def pytest_generate_tests(metafunc):
+    # http://doc.pytest.org/en/latest/parametrize.html#basic-pytest-generate-tests-example
+    fixturenames = metafunc.fixturenames
+    argnames = ("testfile", "tableTag")
+    if all(fn in fixturenames for fn in argnames):
+        argvalues = [(testfile, tableTag)
+                     for testfile, tableTags in sorted(TESTS.items())
+                     for tableTag in tableTags]
+        metafunc.parametrize(argnames, argvalues)
+
+
+if __name__ == '__main__':
+    sys.exit(pytest.main(sys.argv))
diff --git a/Tests/ttLib/tables/ttProgram_test.py b/Tests/ttLib/tables/ttProgram_test.py
new file mode 100644
index 0000000..9a7d232
--- /dev/null
+++ b/Tests/ttLib/tables/ttProgram_test.py
@@ -0,0 +1,119 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.xmlWriter import XMLWriter
+from fontTools.ttLib.tables.ttProgram import Program
+from fontTools.misc.textTools import deHexStr
+import array
+import os
+import re
+import unittest
+
+CURR_DIR = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
+DATA_DIR = os.path.join(CURR_DIR, 'data')
+
+TTPROGRAM_TTX = os.path.join(DATA_DIR, "ttProgram.ttx")
+#TTPROGRAM_BIN = os.path.join(DATA_DIR, "ttProgram.bin")
+
+BYTECODE = deHexStr(
+    '403b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a'
+    '191817161514131211100f0e0d0c0b0a090807060504030201002c01b0184358456ab0'
+    '194360b0462344231020b0464ef04d2fb000121b21231133592d2c01b0184358b0052b'
+    'b000134bb0145058b100403859b0062b1b21231133592d2c01b01843584eb0032510f2'
+    '21b000124d1b2045b00425b00425234a6164b0285258212310d61bb0032510f221b000'
+    '1259592d2cb01a435821211bb00225b0022549b00325b003254a612064b01050582121'
+    '211bb00325b0032549b0005058b0005058b8ffe238211bb0103821591bb0005258b01e'
+    '38211bb8fff03821595959592d2c01b0184358b0052bb000134bb0145058b90000ffc0'
+    '3859b0062b1b21231133592d2c4e018a10b146194344b00014b10046e2b00015b90000'
+    'fff03800b0003cb0282bb0022510b0003c2d2c0118b0002fb00114f2b00113b001154d'
+    'b000122d2c01b0184358b0052bb00013b90000ffe038b0062b1b21231133592d2c01b0'
+    '18435845646a23456469b01943646060b0462344231020b046f02fb000121b2121208a'
+    '208a525811331b212159592d2c01b10b0a432343650a2d2c00b10a0b4323430b2d2c00'
+    'b0462370b101463e01b0462370b10246453ab10200080d2d2cb0122bb0022545b00225'
+    '456ab0408b60b0022523442121212d2cb0132bb0022545b00225456ab8ffc08c60b002'
+    '2523442121212d2cb000b0122b2121212d2cb000b0132b2121212d2c01b00643b00743'
+    '650a2d2c2069b04061b0008b20b12cc08a8cb8100062602b0c642364615c58b0036159'
+    '2d2cb1000325456854b01c4b505a58b0032545b0032545606820b004252344b0042523'
+    '441bb00325204568208a2344b00325456860b003252344592d2cb00325204568208a23'
+    '44b003254564686560b00425b0016023442d2cb00943588721c01bb01243588745b011'
+    '2bb0472344b0477ae41b038a45186920b04723448a8a8720b0a05158b0112bb0472344'
+    'b0477ae41b21b0477ae4595959182d2c208a4523456860442d2c456a422d2c01182f2d'
+    '2c01b0184358b00425b00425496423456469b0408b6120b080626ab00225b00225618c'
+    'b0194360b0462344218a10b046f6211b21212121592d2c01b0184358b0022545b00225'
+    '4564606ab00325456a6120b00425456a208a8b65b0042523448cb00325234421211b20'
+    '456a4420456a44592d2c012045b00055b018435a584568234569b0408b6120b080626a'
+    '208a236120b003258b65b0042523448cb00325234421211b2121b0192b592d2c018a8a'
+    '45642345646164422d2cb00425b00425b0192bb0184358b00425b00425b00325b01b2b'
+    '01b0022543b04054b0022543b000545a58b003252045b040614459b0022543b00054b0'
+    '022543b040545a58b004252045b04060445959212121212d2c014b525843b002254523'
+    '61441b2121592d2c014b525843b00225452360441b2121592d2c4b525845441b212159'
+    '2d2c0120b003252349b04060b0206320b000525823b002253823b002256538008a6338'
+    '1b212121212159012d2c4b505845441b2121592d2c01b005251023208af500b0016023'
+    'edec2d2c01b005251023208af500b0016123edec2d2c01b0062510f500edec2d2c4623'
+    '46608a8a462320468a608a61b8ff8062232010238ab14b4b8a70456020b0005058b001'
+    '61b8ffba8b1bb0468c59b0106068013a2d2c2045b00325465258b0022546206861b003'
+    '25b003253f2321381b2111592d2c2045b00325465058b0022546206861b00325b00325'
+    '3f2321381b2111592d2c00b00743b006430b2d2c8a10ec2d2cb00c4358211b2046b000'
+    '5258b8fff0381bb0103859592d2c20b0005558b8100063b003254564b00325456461b0'
+    '005358b0021bb04061b00359254569535845441b2121591b21b0022545b00225456164'
+    'b028515845441b212159592d2c21210c6423648bb84000622d2c21b08051580c642364'
+    '8bb82000621bb200402f2b59b002602d2c21b0c051580c6423648bb81555621bb20080'
+    '2f2b59b002602d2c0c6423648bb84000626023212d2c4b5358b00425b0042549642345'
+    '6469b0408b6120b080626ab00225b00225618cb0462344218a10b046f6211b218a1123'
+    '1220392f592d2cb00225b002254964b0c05458b8fff838b008381b2121592d2cb01343'
+    '58031b02592d2cb0134358021b03592d2cb00a2b2310203cb0172b2d2cb00225b8fff0'
+    '38b0282b8a102320d023b0102bb0054358c01b3c59201011b00012012d2c4b53234b51'
+    '5a58381b2121592d2c01b0022510d023c901b00113b0001410b0013cb001162d2c01b0'
+    '0013b001b0032549b0031738b001132d2c4b53234b515a5820458a60441b2121592d2c'
+    '20392f2d')
+
+
+class TestFont(object):
+    disassembleInstructions = True
+
+
+class ProgramTest(unittest.TestCase):
+
+    def test__bool__(self):
+        p = Program()
+        assert not bool(p)
+
+        bc = array.array("B", [0])
+        p.fromBytecode(bc)
+        assert bool(p)
+
+        assert p.bytecode.pop() == 0
+        assert not bool(p)
+
+        p = Program()
+        asm = ['SVTCA[0]']
+        p.fromAssembly(asm)
+        assert bool(p)
+
+        assert p.assembly.pop() == 'SVTCA[0]'
+        assert not bool(p)
+
+    def test_roundtrip(self):
+        p = Program()
+        p.fromBytecode(BYTECODE)
+        asm = p.getAssembly(preserve=True)
+        p.fromAssembly(asm)
+        assert BYTECODE == p.getBytecode()
+
+    def test_xml_indentation(self):
+        with open(TTPROGRAM_TTX, 'r', encoding='utf-8') as f:
+            ttProgramXML = f.read()
+        p = Program()
+        p.fromBytecode(BYTECODE)
+        ttfont = TestFont()
+        buf = UnicodeIO()
+        writer = XMLWriter(buf, newlinestr='\n')
+        try:
+            p.toXML(writer, ttfont)
+        finally:
+            output_string = buf.getvalue()
+        assert output_string == ttProgramXML
+        
+
+if __name__ == '__main__':
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/ttLib/woff2_test.py b/Tests/ttLib/woff2_test.py
new file mode 100644
index 0000000..b3ec6b2
--- /dev/null
+++ b/Tests/ttLib/woff2_test.py
@@ -0,0 +1,840 @@
+from __future__ import print_function, division, absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+from fontTools import ttLib
+from fontTools.ttLib.woff2 import (
+	WOFF2Reader, woff2DirectorySize, woff2DirectoryFormat,
+	woff2FlagsSize, woff2UnknownTagSize, woff2Base128MaxSize, WOFF2DirectoryEntry,
+	getKnownTagIndex, packBase128, base128Size, woff2UnknownTagIndex,
+	WOFF2FlavorData, woff2TransformedTableTags, WOFF2GlyfTable, WOFF2LocaTable,
+	WOFF2Writer, unpackBase128, unpack255UShort, pack255UShort)
+import unittest
+from fontTools.misc import sstruct
+import struct
+import os
+import random
+import copy
+from collections import OrderedDict
+
+haveBrotli = False
+try:
+	import brotli
+	haveBrotli = True
+except ImportError:
+	pass
+
+
+# Python 3 renamed 'assertRaisesRegexp' to 'assertRaisesRegex', and fires
+# deprecation warnings if a program uses the old name.
+if not hasattr(unittest.TestCase, 'assertRaisesRegex'):
+	unittest.TestCase.assertRaisesRegex = unittest.TestCase.assertRaisesRegexp
+
+
+current_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
+data_dir = os.path.join(current_dir, 'data')
+TTX = os.path.join(data_dir, 'TestTTF-Regular.ttx')
+OTX = os.path.join(data_dir, 'TestOTF-Regular.otx')
+METADATA = os.path.join(data_dir, 'test_woff2_metadata.xml')
+
+TT_WOFF2 = BytesIO()
+CFF_WOFF2 = BytesIO()
+
+
+def setUpModule():
+	if not haveBrotli:
+		raise unittest.SkipTest("No module named brotli")
+	assert os.path.exists(TTX)
+	assert os.path.exists(OTX)
+	# import TT-flavoured test font and save it as WOFF2
+	ttf = ttLib.TTFont(recalcBBoxes=False, recalcTimestamp=False)
+	ttf.importXML(TTX)
+	ttf.flavor = "woff2"
+	ttf.save(TT_WOFF2, reorderTables=None)
+	# import CFF-flavoured test font and save it as WOFF2
+	otf = ttLib.TTFont(recalcBBoxes=False, recalcTimestamp=False)
+	otf.importXML(OTX)
+	otf.flavor = "woff2"
+	otf.save(CFF_WOFF2, reorderTables=None)
+
+
+class WOFF2ReaderTest(unittest.TestCase):
+
+	@classmethod
+	def setUpClass(cls):
+		cls.file = BytesIO(CFF_WOFF2.getvalue())
+		cls.font = ttLib.TTFont(recalcBBoxes=False, recalcTimestamp=False)
+		cls.font.importXML(OTX)
+
+	def setUp(self):
+		self.file.seek(0)
+
+	def test_bad_signature(self):
+		with self.assertRaisesRegex(ttLib.TTLibError, 'bad signature'):
+			WOFF2Reader(BytesIO(b"wOFF"))
+
+	def test_not_enough_data_header(self):
+		incomplete_header = self.file.read(woff2DirectorySize - 1)
+		with self.assertRaisesRegex(ttLib.TTLibError, 'not enough data'):
+			WOFF2Reader(BytesIO(incomplete_header))
+
+	def test_incorrect_compressed_size(self):
+		data = self.file.read(woff2DirectorySize)
+		header = sstruct.unpack(woff2DirectoryFormat, data)
+		header['totalCompressedSize'] = 0
+		data = sstruct.pack(woff2DirectoryFormat, header)
+		with self.assertRaises((brotli.error, ttLib.TTLibError)):
+			WOFF2Reader(BytesIO(data + self.file.read()))
+
+	def test_incorrect_uncompressed_size(self):
+		decompress_backup = brotli.decompress
+		brotli.decompress = lambda data: b""  # return empty byte string
+		with self.assertRaisesRegex(ttLib.TTLibError, 'unexpected size for decompressed'):
+			WOFF2Reader(self.file)
+		brotli.decompress = decompress_backup
+
+	def test_incorrect_file_size(self):
+		data = self.file.read(woff2DirectorySize)
+		header = sstruct.unpack(woff2DirectoryFormat, data)
+		header['length'] -= 1
+		data = sstruct.pack(woff2DirectoryFormat, header)
+		with self.assertRaisesRegex(
+				ttLib.TTLibError, "doesn't match the actual file size"):
+			WOFF2Reader(BytesIO(data + self.file.read()))
+
+	def test_num_tables(self):
+		tags = [t for t in self.font.keys() if t not in ('GlyphOrder', 'DSIG')]
+		data = self.file.read(woff2DirectorySize)
+		header = sstruct.unpack(woff2DirectoryFormat, data)
+		self.assertEqual(header['numTables'], len(tags))
+
+	def test_table_tags(self):
+		tags = set([t for t in self.font.keys() if t not in ('GlyphOrder', 'DSIG')])
+		reader = WOFF2Reader(self.file)
+		self.assertEqual(set(reader.keys()), tags)
+
+	def test_get_normal_tables(self):
+		woff2Reader = WOFF2Reader(self.file)
+		specialTags = woff2TransformedTableTags + ('head', 'GlyphOrder', 'DSIG')
+		for tag in [t for t in self.font.keys() if t not in specialTags]:
+			origData = self.font.getTableData(tag)
+			decompressedData = woff2Reader[tag]
+			self.assertEqual(origData, decompressedData)
+
+	def test_reconstruct_unknown(self):
+		reader = WOFF2Reader(self.file)
+		with self.assertRaisesRegex(ttLib.TTLibError, 'transform for table .* unknown'):
+			reader.reconstructTable('ZZZZ')
+
+
+class WOFF2ReaderTTFTest(WOFF2ReaderTest):
+	""" Tests specific to TT-flavored fonts. """
+
+	@classmethod
+	def setUpClass(cls):
+		cls.file = BytesIO(TT_WOFF2.getvalue())
+		cls.font = ttLib.TTFont(recalcBBoxes=False, recalcTimestamp=False)
+		cls.font.importXML(TTX)
+
+	def setUp(self):
+		self.file.seek(0)
+
+	def test_reconstruct_glyf(self):
+		woff2Reader = WOFF2Reader(self.file)
+		reconstructedData = woff2Reader['glyf']
+		self.assertEqual(self.font.getTableData('glyf'), reconstructedData)
+
+	def test_reconstruct_loca(self):
+		woff2Reader = WOFF2Reader(self.file)
+		reconstructedData = woff2Reader['loca']
+		self.assertEqual(self.font.getTableData('loca'), reconstructedData)
+		self.assertTrue(hasattr(woff2Reader.tables['glyf'], 'data'))
+
+	def test_reconstruct_loca_not_match_orig_size(self):
+		reader = WOFF2Reader(self.file)
+		reader.tables['loca'].origLength -= 1
+		with self.assertRaisesRegex(
+				ttLib.TTLibError, "'loca' table doesn't match original size"):
+			reader.reconstructTable('loca')
+
+
+def normalise_table(font, tag, padding=4):
+	""" Return normalised table data. Keep 'font' instance unmodified. """
+	assert tag in ('glyf', 'loca', 'head')
+	assert tag in font
+	if tag == 'head':
+		origHeadFlags = font['head'].flags
+		font['head'].flags |= (1 << 11)
+		tableData = font['head'].compile(font)
+	if font.sfntVersion in ("\x00\x01\x00\x00", "true"):
+		assert {'glyf', 'loca', 'head'}.issubset(font.keys())
+		origIndexFormat = font['head'].indexToLocFormat
+		if hasattr(font['loca'], 'locations'):
+			origLocations = font['loca'].locations[:]
+		else:
+			origLocations = []
+		glyfTable = ttLib.newTable('glyf')
+		glyfTable.decompile(font.getTableData('glyf'), font)
+		glyfTable.padding = padding
+		if tag == 'glyf':
+			tableData = glyfTable.compile(font)
+		elif tag == 'loca':
+			glyfTable.compile(font)
+			tableData = font['loca'].compile(font)
+		if tag == 'head':
+			glyfTable.compile(font)
+			font['loca'].compile(font)
+			tableData = font['head'].compile(font)
+		font['head'].indexToLocFormat = origIndexFormat
+		font['loca'].set(origLocations)
+	if tag == 'head':
+		font['head'].flags = origHeadFlags
+	return tableData
+
+
+def normalise_font(font, padding=4):
+	""" Return normalised font data. Keep 'font' instance unmodified. """
+	# drop DSIG but keep a copy
+	DSIG_copy = copy.deepcopy(font['DSIG'])
+	del font['DSIG']
+	# ovverride TTFont attributes
+	origFlavor = font.flavor
+	origRecalcBBoxes = font.recalcBBoxes
+	origRecalcTimestamp = font.recalcTimestamp
+	origLazy = font.lazy
+	font.flavor = None
+	font.recalcBBoxes = False
+	font.recalcTimestamp = False
+	font.lazy = True
+	# save font to temporary stream
+	infile = BytesIO()
+	font.save(infile)
+	infile.seek(0)
+	# reorder tables alphabetically
+	outfile = BytesIO()
+	reader = ttLib.sfnt.SFNTReader(infile)
+	writer = ttLib.sfnt.SFNTWriter(
+		outfile, len(reader.tables), reader.sfntVersion, reader.flavor, reader.flavorData)
+	for tag in sorted(reader.keys()):
+		if tag in woff2TransformedTableTags + ('head',):
+			writer[tag] = normalise_table(font, tag, padding)
+		else:
+			writer[tag] = reader[tag]
+	writer.close()
+	# restore font attributes
+	font['DSIG'] = DSIG_copy
+	font.flavor = origFlavor
+	font.recalcBBoxes = origRecalcBBoxes
+	font.recalcTimestamp = origRecalcTimestamp
+	font.lazy = origLazy
+	return outfile.getvalue()
+
+
+class WOFF2DirectoryEntryTest(unittest.TestCase):
+
+	def setUp(self):
+		self.entry = WOFF2DirectoryEntry()
+
+	def test_not_enough_data_table_flags(self):
+		with self.assertRaisesRegex(ttLib.TTLibError, "can't read table 'flags'"):
+			self.entry.fromString(b"")
+
+	def test_not_enough_data_table_tag(self):
+		incompleteData = bytearray([0x3F, 0, 0, 0])
+		with self.assertRaisesRegex(ttLib.TTLibError, "can't read table 'tag'"):
+			self.entry.fromString(bytes(incompleteData))
+
+	def test_table_reserved_flags(self):
+		with self.assertRaisesRegex(ttLib.TTLibError, "bits 6-7 are reserved"):
+			self.entry.fromString(bytechr(0xC0))
+
+	def test_loca_zero_transformLength(self):
+		data = bytechr(getKnownTagIndex('loca'))  # flags
+		data += packBase128(random.randint(1, 100))  # origLength
+		data += packBase128(1)  # non-zero transformLength
+		with self.assertRaisesRegex(
+				ttLib.TTLibError, "transformLength of the 'loca' table must be 0"):
+			self.entry.fromString(data)
+
+	def test_fromFile(self):
+		unknownTag = Tag('ZZZZ')
+		data = bytechr(getKnownTagIndex(unknownTag))
+		data += unknownTag.tobytes()
+		data += packBase128(random.randint(1, 100))
+		expectedPos = len(data)
+		f = BytesIO(data + b'\0'*100)
+		self.entry.fromFile(f)
+		self.assertEqual(f.tell(), expectedPos)
+
+	def test_transformed_toString(self):
+		self.entry.tag = Tag('glyf')
+		self.entry.flags = getKnownTagIndex(self.entry.tag)
+		self.entry.origLength = random.randint(101, 200)
+		self.entry.length = random.randint(1, 100)
+		expectedSize = (woff2FlagsSize + base128Size(self.entry.origLength) +
+			base128Size(self.entry.length))
+		data = self.entry.toString()
+		self.assertEqual(len(data), expectedSize)
+
+	def test_known_toString(self):
+		self.entry.tag = Tag('head')
+		self.entry.flags = getKnownTagIndex(self.entry.tag)
+		self.entry.origLength = 54
+		expectedSize = (woff2FlagsSize + base128Size(self.entry.origLength))
+		data = self.entry.toString()
+		self.assertEqual(len(data), expectedSize)
+
+	def test_unknown_toString(self):
+		self.entry.tag = Tag('ZZZZ')
+		self.entry.flags = woff2UnknownTagIndex
+		self.entry.origLength = random.randint(1, 100)
+		expectedSize = (woff2FlagsSize + woff2UnknownTagSize +
+			base128Size(self.entry.origLength))
+		data = self.entry.toString()
+		self.assertEqual(len(data), expectedSize)
+
+
+class DummyReader(WOFF2Reader):
+
+	def __init__(self, file, checkChecksums=1, fontNumber=-1):
+		self.file = file
+		for attr in ('majorVersion', 'minorVersion', 'metaOffset', 'metaLength',
+				'metaOrigLength', 'privLength', 'privOffset'):
+			setattr(self, attr, 0)
+
+
+class WOFF2FlavorDataTest(unittest.TestCase):
+
+	@classmethod
+	def setUpClass(cls):
+		assert os.path.exists(METADATA)
+		with open(METADATA, 'rb') as f:
+			cls.xml_metadata = f.read()
+		cls.compressed_metadata = brotli.compress(cls.xml_metadata, mode=brotli.MODE_TEXT)
+		# make random byte strings; font data must be 4-byte aligned
+		cls.fontdata = bytes(bytearray(random.sample(range(0, 256), 80)))
+		cls.privData = bytes(bytearray(random.sample(range(0, 256), 20)))
+
+	def setUp(self):
+		self.file = BytesIO(self.fontdata)
+		self.file.seek(0, 2)
+
+	def test_get_metaData_no_privData(self):
+		self.file.write(self.compressed_metadata)
+		reader = DummyReader(self.file)
+		reader.metaOffset = len(self.fontdata)
+		reader.metaLength = len(self.compressed_metadata)
+		reader.metaOrigLength = len(self.xml_metadata)
+		flavorData = WOFF2FlavorData(reader)
+		self.assertEqual(self.xml_metadata, flavorData.metaData)
+
+	def test_get_privData_no_metaData(self):
+		self.file.write(self.privData)
+		reader = DummyReader(self.file)
+		reader.privOffset = len(self.fontdata)
+		reader.privLength = len(self.privData)
+		flavorData = WOFF2FlavorData(reader)
+		self.assertEqual(self.privData, flavorData.privData)
+
+	def test_get_metaData_and_privData(self):
+		self.file.write(self.compressed_metadata + self.privData)
+		reader = DummyReader(self.file)
+		reader.metaOffset = len(self.fontdata)
+		reader.metaLength = len(self.compressed_metadata)
+		reader.metaOrigLength = len(self.xml_metadata)
+		reader.privOffset = reader.metaOffset + reader.metaLength
+		reader.privLength = len(self.privData)
+		flavorData = WOFF2FlavorData(reader)
+		self.assertEqual(self.xml_metadata, flavorData.metaData)
+		self.assertEqual(self.privData, flavorData.privData)
+
+	def test_get_major_minorVersion(self):
+		reader = DummyReader(self.file)
+		reader.majorVersion = reader.minorVersion = 1
+		flavorData = WOFF2FlavorData(reader)
+		self.assertEqual(flavorData.majorVersion, 1)
+		self.assertEqual(flavorData.minorVersion, 1)
+
+
+class WOFF2WriterTest(unittest.TestCase):
+
+	@classmethod
+	def setUpClass(cls):
+		cls.font = ttLib.TTFont(recalcBBoxes=False, recalcTimestamp=False, flavor="woff2")
+		cls.font.importXML(OTX)
+		cls.tags = [t for t in cls.font.keys() if t != 'GlyphOrder']
+		cls.numTables = len(cls.tags)
+		cls.file = BytesIO(CFF_WOFF2.getvalue())
+		cls.file.seek(0, 2)
+		cls.length = (cls.file.tell() + 3) & ~3
+		cls.setUpFlavorData()
+
+	@classmethod
+	def setUpFlavorData(cls):
+		assert os.path.exists(METADATA)
+		with open(METADATA, 'rb') as f:
+			cls.xml_metadata = f.read()
+		cls.compressed_metadata = brotli.compress(cls.xml_metadata, mode=brotli.MODE_TEXT)
+		cls.privData = bytes(bytearray(random.sample(range(0, 256), 20)))
+
+	def setUp(self):
+		self.file.seek(0)
+		self.writer = WOFF2Writer(BytesIO(), self.numTables, self.font.sfntVersion)
+
+	def test_DSIG_dropped(self):
+		self.writer['DSIG'] = b"\0"
+		self.assertEqual(len(self.writer.tables), 0)
+		self.assertEqual(self.writer.numTables, self.numTables-1)
+
+	def test_no_rewrite_table(self):
+		self.writer['ZZZZ'] = b"\0"
+		with self.assertRaisesRegex(ttLib.TTLibError, "cannot rewrite"):
+			self.writer['ZZZZ'] = b"\0"
+
+	def test_num_tables(self):
+		self.writer['ABCD'] = b"\0"
+		with self.assertRaisesRegex(ttLib.TTLibError, "wrong number of tables"):
+			self.writer.close()
+
+	def test_required_tables(self):
+		font = ttLib.TTFont(flavor="woff2")
+		with self.assertRaisesRegex(ttLib.TTLibError, "missing required table"):
+			font.save(BytesIO())
+
+	def test_head_transform_flag(self):
+		headData = self.font.getTableData('head')
+		origFlags = byteord(headData[16])
+		woff2font = ttLib.TTFont(self.file)
+		newHeadData = woff2font.getTableData('head')
+		modifiedFlags = byteord(newHeadData[16])
+		self.assertNotEqual(origFlags, modifiedFlags)
+		restoredFlags = modifiedFlags & ~0x08  # turn off bit 11
+		self.assertEqual(origFlags, restoredFlags)
+
+	def test_tables_sorted_alphabetically(self):
+		expected = sorted([t for t in self.tags if t != 'DSIG'])
+		woff2font = ttLib.TTFont(self.file)
+		self.assertEqual(expected, list(woff2font.reader.keys()))
+
+	def test_checksums(self):
+		normFile = BytesIO(normalise_font(self.font, padding=4))
+		normFile.seek(0)
+		normFont = ttLib.TTFont(normFile, checkChecksums=2)
+		w2font = ttLib.TTFont(self.file)
+		# force reconstructing glyf table using 4-byte padding
+		w2font.reader.padding = 4
+		for tag in [t for t in self.tags if t != 'DSIG']:
+			w2data = w2font.reader[tag]
+			normData = normFont.reader[tag]
+			if tag == "head":
+				w2data = w2data[:8] + b'\0\0\0\0' + w2data[12:]
+				normData = normData[:8] + b'\0\0\0\0' + normData[12:]
+			w2CheckSum = ttLib.sfnt.calcChecksum(w2data)
+			normCheckSum = ttLib.sfnt.calcChecksum(normData)
+			self.assertEqual(w2CheckSum, normCheckSum)
+		normCheckSumAdjustment = normFont['head'].checkSumAdjustment
+		self.assertEqual(normCheckSumAdjustment, w2font['head'].checkSumAdjustment)
+
+	def test_calcSFNTChecksumsLengthsAndOffsets(self):
+		normFont = ttLib.TTFont(BytesIO(normalise_font(self.font, padding=4)))
+		for tag in self.tags:
+			self.writer[tag] = self.font.getTableData(tag)
+		self.writer._normaliseGlyfAndLoca(padding=4)
+		self.writer._setHeadTransformFlag()
+		self.writer.tables = OrderedDict(sorted(self.writer.tables.items()))
+		self.writer._calcSFNTChecksumsLengthsAndOffsets()
+		for tag, entry in normFont.reader.tables.items():
+			self.assertEqual(entry.offset, self.writer.tables[tag].origOffset)
+			self.assertEqual(entry.length, self.writer.tables[tag].origLength)
+			self.assertEqual(entry.checkSum, self.writer.tables[tag].checkSum)
+
+	def test_bad_sfntVersion(self):
+		for i in range(self.numTables):
+			self.writer[bytechr(65 + i)*4] = b"\0"
+		self.writer.sfntVersion = 'ZZZZ'
+		with self.assertRaisesRegex(ttLib.TTLibError, "bad sfntVersion"):
+			self.writer.close()
+
+	def test_calcTotalSize_no_flavorData(self):
+		expected = self.length
+		self.writer.file = BytesIO()
+		for tag in self.tags:
+			self.writer[tag] = self.font.getTableData(tag)
+		self.writer.close()
+		self.assertEqual(expected, self.writer.length)
+		self.assertEqual(expected, self.writer.file.tell())
+
+	def test_calcTotalSize_with_metaData(self):
+		expected = self.length + len(self.compressed_metadata)
+		flavorData = self.writer.flavorData = WOFF2FlavorData()
+		flavorData.metaData = self.xml_metadata
+		self.writer.file = BytesIO()
+		for tag in self.tags:
+			self.writer[tag] = self.font.getTableData(tag)
+		self.writer.close()
+		self.assertEqual(expected, self.writer.length)
+		self.assertEqual(expected, self.writer.file.tell())
+
+	def test_calcTotalSize_with_privData(self):
+		expected = self.length + len(self.privData)
+		flavorData = self.writer.flavorData = WOFF2FlavorData()
+		flavorData.privData = self.privData
+		self.writer.file = BytesIO()
+		for tag in self.tags:
+			self.writer[tag] = self.font.getTableData(tag)
+		self.writer.close()
+		self.assertEqual(expected, self.writer.length)
+		self.assertEqual(expected, self.writer.file.tell())
+
+	def test_calcTotalSize_with_metaData_and_privData(self):
+		metaDataLength = (len(self.compressed_metadata) + 3) & ~3
+		expected = self.length + metaDataLength + len(self.privData)
+		flavorData = self.writer.flavorData = WOFF2FlavorData()
+		flavorData.metaData = self.xml_metadata
+		flavorData.privData = self.privData
+		self.writer.file = BytesIO()
+		for tag in self.tags:
+			self.writer[tag] = self.font.getTableData(tag)
+		self.writer.close()
+		self.assertEqual(expected, self.writer.length)
+		self.assertEqual(expected, self.writer.file.tell())
+
+	def test_getVersion(self):
+		# no version
+		self.assertEqual((0, 0), self.writer._getVersion())
+		# version from head.fontRevision
+		fontRevision = self.font['head'].fontRevision
+		versionTuple = tuple(int(i) for i in str(fontRevision).split("."))
+		entry = self.writer.tables['head'] = ttLib.newTable('head')
+		entry.data = self.font.getTableData('head')
+		self.assertEqual(versionTuple, self.writer._getVersion())
+		# version from writer.flavorData
+		flavorData = self.writer.flavorData = WOFF2FlavorData()
+		flavorData.majorVersion, flavorData.minorVersion = (10, 11)
+		self.assertEqual((10, 11), self.writer._getVersion())
+
+
+class WOFF2WriterTTFTest(WOFF2WriterTest):
+
+	@classmethod
+	def setUpClass(cls):
+		cls.font = ttLib.TTFont(recalcBBoxes=False, recalcTimestamp=False, flavor="woff2")
+		cls.font.importXML(TTX)
+		cls.tags = [t for t in cls.font.keys() if t != 'GlyphOrder']
+		cls.numTables = len(cls.tags)
+		cls.file = BytesIO(TT_WOFF2.getvalue())
+		cls.file.seek(0, 2)
+		cls.length = (cls.file.tell() + 3) & ~3
+		cls.setUpFlavorData()
+
+	def test_normaliseGlyfAndLoca(self):
+		normTables = {}
+		for tag in ('head', 'loca', 'glyf'):
+			normTables[tag] = normalise_table(self.font, tag, padding=4)
+		for tag in self.tags:
+			tableData = self.font.getTableData(tag)
+			self.writer[tag] = tableData
+			if tag in normTables:
+				self.assertNotEqual(tableData, normTables[tag])
+		self.writer._normaliseGlyfAndLoca(padding=4)
+		self.writer._setHeadTransformFlag()
+		for tag in normTables:
+			self.assertEqual(self.writer.tables[tag].data, normTables[tag])
+
+
+class WOFF2LocaTableTest(unittest.TestCase):
+
+	def setUp(self):
+		self.font = font = ttLib.TTFont(recalcBBoxes=False, recalcTimestamp=False)
+		font['head'] = ttLib.newTable('head')
+		font['loca'] = WOFF2LocaTable()
+		font['glyf'] = WOFF2GlyfTable()
+
+	def test_compile_short_loca(self):
+		locaTable = self.font['loca']
+		locaTable.set(list(range(0, 0x20000, 2)))
+		self.font['glyf'].indexFormat = 0
+		locaData = locaTable.compile(self.font)
+		self.assertEqual(len(locaData), 0x20000)
+
+	def test_compile_short_loca_overflow(self):
+		locaTable = self.font['loca']
+		locaTable.set(list(range(0x20000 + 1)))
+		self.font['glyf'].indexFormat = 0
+		with self.assertRaisesRegex(
+				ttLib.TTLibError, "indexFormat is 0 but local offsets > 0x20000"):
+			locaTable.compile(self.font)
+
+	def test_compile_short_loca_not_multiples_of_2(self):
+		locaTable = self.font['loca']
+		locaTable.set([1, 3, 5, 7])
+		self.font['glyf'].indexFormat = 0
+		with self.assertRaisesRegex(ttLib.TTLibError, "offsets not multiples of 2"):
+			locaTable.compile(self.font)
+
+	def test_compile_long_loca(self):
+		locaTable = self.font['loca']
+		locaTable.set(list(range(0x20001)))
+		self.font['glyf'].indexFormat = 1
+		locaData = locaTable.compile(self.font)
+		self.assertEqual(len(locaData), 0x20001 * 4)
+
+	def test_compile_set_indexToLocFormat_0(self):
+		locaTable = self.font['loca']
+		# offsets are all multiples of 2 and max length is < 0x10000
+		locaTable.set(list(range(0, 0x20000, 2)))
+		locaTable.compile(self.font)
+		newIndexFormat = self.font['head'].indexToLocFormat
+		self.assertEqual(0, newIndexFormat)
+
+	def test_compile_set_indexToLocFormat_1(self):
+		locaTable = self.font['loca']
+		# offsets are not multiples of 2
+		locaTable.set(list(range(10)))
+		locaTable.compile(self.font)
+		newIndexFormat = self.font['head'].indexToLocFormat
+		self.assertEqual(1, newIndexFormat)
+		# max length is >= 0x10000
+		locaTable.set(list(range(0, 0x20000 + 1, 2)))
+		locaTable.compile(self.font)
+		newIndexFormat = self.font['head'].indexToLocFormat
+		self.assertEqual(1, newIndexFormat)
+
+
+class WOFF2GlyfTableTest(unittest.TestCase):
+
+	@classmethod
+	def setUpClass(cls):
+		font = ttLib.TTFont(recalcBBoxes=False, recalcTimestamp=False)
+		font.importXML(TTX)
+		cls.tables = {}
+		cls.transformedTags = ('maxp', 'head', 'loca', 'glyf')
+		for tag in reversed(cls.transformedTags):  # compile in inverse order
+			cls.tables[tag] = font.getTableData(tag)
+		infile = BytesIO(TT_WOFF2.getvalue())
+		reader = WOFF2Reader(infile)
+		cls.transformedGlyfData = reader.tables['glyf'].loadData(
+			reader.transformBuffer)
+		cls.glyphOrder = ['.notdef'] + ["glyph%.5d" % i for i in range(1, font['maxp'].numGlyphs)]
+
+	def setUp(self):
+		self.font = font = ttLib.TTFont(recalcBBoxes=False, recalcTimestamp=False)
+		font.setGlyphOrder(self.glyphOrder)
+		font['head'] = ttLib.newTable('head')
+		font['maxp'] = ttLib.newTable('maxp')
+		font['loca'] = WOFF2LocaTable()
+		font['glyf'] = WOFF2GlyfTable()
+		for tag in self.transformedTags:
+			font[tag].decompile(self.tables[tag], font)
+
+	def test_reconstruct_glyf_padded_4(self):
+		glyfTable = WOFF2GlyfTable()
+		glyfTable.reconstruct(self.transformedGlyfData, self.font)
+		glyfTable.padding = 4
+		data = glyfTable.compile(self.font)
+		normGlyfData = normalise_table(self.font, 'glyf', glyfTable.padding)
+		self.assertEqual(normGlyfData, data)
+
+	def test_reconstruct_glyf_padded_2(self):
+		glyfTable = WOFF2GlyfTable()
+		glyfTable.reconstruct(self.transformedGlyfData, self.font)
+		glyfTable.padding = 2
+		data = glyfTable.compile(self.font)
+		normGlyfData = normalise_table(self.font, 'glyf', glyfTable.padding)
+		self.assertEqual(normGlyfData, data)
+
+	def test_reconstruct_glyf_unpadded(self):
+		glyfTable = WOFF2GlyfTable()
+		glyfTable.reconstruct(self.transformedGlyfData, self.font)
+		data = glyfTable.compile(self.font)
+		self.assertEqual(self.tables['glyf'], data)
+
+	def test_reconstruct_glyf_incorrect_glyphOrder(self):
+		glyfTable = WOFF2GlyfTable()
+		badGlyphOrder = self.font.getGlyphOrder()[:-1]
+		self.font.setGlyphOrder(badGlyphOrder)
+		with self.assertRaisesRegex(ttLib.TTLibError, "incorrect glyphOrder"):
+			glyfTable.reconstruct(self.transformedGlyfData, self.font)
+
+	def test_reconstruct_glyf_missing_glyphOrder(self):
+		glyfTable = WOFF2GlyfTable()
+		del self.font.glyphOrder
+		numGlyphs = self.font['maxp'].numGlyphs
+		del self.font['maxp']
+		glyfTable.reconstruct(self.transformedGlyfData, self.font)
+		expected = [".notdef"]
+		expected.extend(["glyph%.5d" % i for i in range(1, numGlyphs)])
+		self.assertEqual(expected, glyfTable.glyphOrder)
+
+	def test_reconstruct_loca_padded_4(self):
+		locaTable = self.font['loca'] = WOFF2LocaTable()
+		glyfTable = self.font['glyf'] = WOFF2GlyfTable()
+		glyfTable.reconstruct(self.transformedGlyfData, self.font)
+		glyfTable.padding = 4
+		glyfTable.compile(self.font)
+		data = locaTable.compile(self.font)
+		normLocaData = normalise_table(self.font, 'loca', glyfTable.padding)
+		self.assertEqual(normLocaData, data)
+
+	def test_reconstruct_loca_padded_2(self):
+		locaTable = self.font['loca'] = WOFF2LocaTable()
+		glyfTable = self.font['glyf'] = WOFF2GlyfTable()
+		glyfTable.reconstruct(self.transformedGlyfData, self.font)
+		glyfTable.padding = 2
+		glyfTable.compile(self.font)
+		data = locaTable.compile(self.font)
+		normLocaData = normalise_table(self.font, 'loca', glyfTable.padding)
+		self.assertEqual(normLocaData, data)
+
+	def test_reconstruct_loca_unpadded(self):
+		locaTable = self.font['loca'] = WOFF2LocaTable()
+		glyfTable = self.font['glyf'] = WOFF2GlyfTable()
+		glyfTable.reconstruct(self.transformedGlyfData, self.font)
+		glyfTable.compile(self.font)
+		data = locaTable.compile(self.font)
+		self.assertEqual(self.tables['loca'], data)
+
+	def test_reconstruct_glyf_header_not_enough_data(self):
+		with self.assertRaisesRegex(ttLib.TTLibError, "not enough 'glyf' data"):
+			WOFF2GlyfTable().reconstruct(b"", self.font)
+
+	def test_reconstruct_glyf_table_incorrect_size(self):
+		msg = "incorrect size of transformed 'glyf'"
+		with self.assertRaisesRegex(ttLib.TTLibError, msg):
+			WOFF2GlyfTable().reconstruct(self.transformedGlyfData + b"\x00", self.font)
+		with self.assertRaisesRegex(ttLib.TTLibError, msg):
+			WOFF2GlyfTable().reconstruct(self.transformedGlyfData[:-1], self.font)
+
+	def test_transform_glyf(self):
+		glyfTable = self.font['glyf']
+		data = glyfTable.transform(self.font)
+		self.assertEqual(self.transformedGlyfData, data)
+
+	def test_transform_glyf_incorrect_glyphOrder(self):
+		glyfTable = self.font['glyf']
+		badGlyphOrder = self.font.getGlyphOrder()[:-1]
+		del glyfTable.glyphOrder
+		self.font.setGlyphOrder(badGlyphOrder)
+		with self.assertRaisesRegex(ttLib.TTLibError, "incorrect glyphOrder"):
+			glyfTable.transform(self.font)
+		glyfTable.glyphOrder = badGlyphOrder
+		with self.assertRaisesRegex(ttLib.TTLibError, "incorrect glyphOrder"):
+			glyfTable.transform(self.font)
+
+	def test_transform_glyf_missing_glyphOrder(self):
+		glyfTable = self.font['glyf']
+		del glyfTable.glyphOrder
+		del self.font.glyphOrder
+		numGlyphs = self.font['maxp'].numGlyphs
+		del self.font['maxp']
+		glyfTable.transform(self.font)
+		expected = [".notdef"]
+		expected.extend(["glyph%.5d" % i for i in range(1, numGlyphs)])
+		self.assertEqual(expected, glyfTable.glyphOrder)
+
+	def test_roundtrip_glyf_reconstruct_and_transform(self):
+		glyfTable = WOFF2GlyfTable()
+		glyfTable.reconstruct(self.transformedGlyfData, self.font)
+		data = glyfTable.transform(self.font)
+		self.assertEqual(self.transformedGlyfData, data)
+
+	def test_roundtrip_glyf_transform_and_reconstruct(self):
+		glyfTable = self.font['glyf']
+		transformedData = glyfTable.transform(self.font)
+		newGlyfTable = WOFF2GlyfTable()
+		newGlyfTable.reconstruct(transformedData, self.font)
+		newGlyfTable.padding = 4
+		reconstructedData = newGlyfTable.compile(self.font)
+		normGlyfData = normalise_table(self.font, 'glyf', newGlyfTable.padding)
+		self.assertEqual(normGlyfData, reconstructedData)
+
+
+class Base128Test(unittest.TestCase):
+
+	def test_unpackBase128(self):
+		self.assertEqual(unpackBase128(b'\x3f\x00\x00'), (63, b"\x00\x00"))
+		self.assertEqual(unpackBase128(b'\x8f\xff\xff\xff\x7f')[0], 4294967295)
+
+		self.assertRaisesRegex(
+			ttLib.TTLibError,
+			"UIntBase128 value must not start with leading zeros",
+			unpackBase128, b'\x80\x80\x3f')
+
+		self.assertRaisesRegex(
+			ttLib.TTLibError,
+			"UIntBase128-encoded sequence is longer than 5 bytes",
+			unpackBase128, b'\x8f\xff\xff\xff\xff\x7f')
+
+		self.assertRaisesRegex(
+			ttLib.TTLibError,
+			"UIntBase128 value exceeds 2\*\*32-1",
+			unpackBase128, b'\x90\x80\x80\x80\x00')
+
+		self.assertRaisesRegex(
+			ttLib.TTLibError,
+			"not enough data to unpack UIntBase128",
+			unpackBase128, b'')
+
+	def test_base128Size(self):
+		self.assertEqual(base128Size(0), 1)
+		self.assertEqual(base128Size(24567), 3)
+		self.assertEqual(base128Size(2**32-1), 5)
+
+	def test_packBase128(self):
+		self.assertEqual(packBase128(63), b"\x3f")
+		self.assertEqual(packBase128(2**32-1), b'\x8f\xff\xff\xff\x7f')
+		self.assertRaisesRegex(
+			ttLib.TTLibError,
+			"UIntBase128 format requires 0 <= integer <= 2\*\*32-1",
+			packBase128, 2**32+1)
+		self.assertRaisesRegex(
+			ttLib.TTLibError,
+			"UIntBase128 format requires 0 <= integer <= 2\*\*32-1",
+			packBase128, -1)
+
+
+class UShort255Test(unittest.TestCase):
+
+	def test_unpack255UShort(self):
+		self.assertEqual(unpack255UShort(bytechr(252))[0], 252)
+		# some numbers (e.g. 506) can have multiple encodings
+		self.assertEqual(
+			unpack255UShort(struct.pack(b"BB", 254, 0))[0], 506)
+		self.assertEqual(
+			unpack255UShort(struct.pack(b"BB", 255, 253))[0], 506)
+		self.assertEqual(
+			unpack255UShort(struct.pack(b"BBB", 253, 1, 250))[0], 506)
+
+		self.assertRaisesRegex(
+			ttLib.TTLibError,
+			"not enough data to unpack 255UInt16",
+			unpack255UShort, struct.pack(b"BB", 253, 0))
+
+		self.assertRaisesRegex(
+			ttLib.TTLibError,
+			"not enough data to unpack 255UInt16",
+			unpack255UShort, struct.pack(b"B", 254))
+
+		self.assertRaisesRegex(
+			ttLib.TTLibError,
+			"not enough data to unpack 255UInt16",
+			unpack255UShort, struct.pack(b"B", 255))
+
+	def test_pack255UShort(self):
+		self.assertEqual(pack255UShort(252), b'\xfc')
+		self.assertEqual(pack255UShort(505), b'\xff\xfc')
+		self.assertEqual(pack255UShort(506), b'\xfe\x00')
+		self.assertEqual(pack255UShort(762), b'\xfd\x02\xfa')
+
+		self.assertRaisesRegex(
+			ttLib.TTLibError,
+			"255UInt16 format requires 0 <= integer <= 65535",
+			pack255UShort, -1)
+
+		self.assertRaisesRegex(
+			ttLib.TTLibError,
+			"255UInt16 format requires 0 <= integer <= 65535",
+			pack255UShort, 0xFFFF+1)
+
+
+if __name__ == "__main__":
+	import sys
+	sys.exit(unittest.main())
diff --git a/Tests/ttx/data/TestBOM.ttx b/Tests/ttx/data/TestBOM.ttx
new file mode 100644
index 0000000..7597133
--- /dev/null
+++ b/Tests/ttx/data/TestBOM.ttx
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- file starts with BOM '\xef\xbb\xbf' -->
+<ttFont />
diff --git a/Tests/ttx/data/TestDFONT.dfont b/Tests/ttx/data/TestDFONT.dfont
new file mode 100644
index 0000000..a6ea700
--- /dev/null
+++ b/Tests/ttx/data/TestDFONT.dfont
Binary files differ
diff --git a/Tests/ttx/data/TestNoSFNT.ttx b/Tests/ttx/data/TestNoSFNT.ttx
new file mode 100644
index 0000000..c71198d
--- /dev/null
+++ b/Tests/ttx/data/TestNoSFNT.ttx
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont /> <!-- no sfntVersion -->
diff --git a/Tests/ttx/data/TestNoXML.ttx b/Tests/ttx/data/TestNoXML.ttx
new file mode 100644
index 0000000..8dac507
--- /dev/null
+++ b/Tests/ttx/data/TestNoXML.ttx
@@ -0,0 +1,2 @@
+<!-- no XML header -->
+<ttFont />
diff --git a/Tests/ttx/data/TestOTF.otf b/Tests/ttx/data/TestOTF.otf
new file mode 100644
index 0000000..cd0bdf6
--- /dev/null
+++ b/Tests/ttx/data/TestOTF.otf
Binary files differ
diff --git a/Tests/ttx/data/TestOTF.ttx b/Tests/ttx/data/TestOTF.ttx
new file mode 100644
index 0000000..852dacf
--- /dev/null
+++ b/Tests/ttx/data/TestOTF.ttx
@@ -0,0 +1,519 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name=".null"/>
+    <GlyphID id="2" name="CR"/>
+    <GlyphID id="3" name="space"/>
+    <GlyphID id="4" name="period"/>
+    <GlyphID id="5" name="ellipsis"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x34034793"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Jun  4 14:29:11 2015"/>
+    <modified value="Sat Aug  1 10:07:17 2015"/>
+    <xMin value="50"/>
+    <yMin value="0"/>
+    <xMax value="668"/>
+    <yMax value="750"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="9"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="900"/>
+    <descent value="-300"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="723"/>
+    <minLeftSideBearing value="50"/>
+    <minRightSideBearing value="50"/>
+    <xMaxExtent value="668"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="6"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="6"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="392"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="700"/>
+    <ySubscriptYSize value="650"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="140"/>
+    <ySuperscriptXSize value="700"/>
+    <ySuperscriptYSize value="650"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="477"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="250"/>
+    <sFamilyClass value="2050"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="6"/>
+      <bProportion value="4"/>
+      <bContrast value="4"/>
+      <bStrokeVariation value="2"/>
+      <bArmStyle value="7"/>
+      <bLetterForm value="8"/>
+      <bMidline value="1"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="10000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="NONE"/>
+    <fsSelection value="00000000 11000000"/>
+    <usFirstCharIndex value="0"/>
+    <usLastCharIndex value="8230"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="900"/>
+    <usWinDescent value="300"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="0" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Copyright (c) 2015 by FontTools. No rights reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Test OTF
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      FontTools: Test OTF: 2015
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Test OTF
+    </namerecord>
+    <namerecord nameID="5" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestOTF-Regular
+    </namerecord>
+    <namerecord nameID="7" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Test OTF is not a trademark of FontTools.
+    </namerecord>
+    <namerecord nameID="8" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      FontTools
+    </namerecord>
+    <namerecord nameID="9" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      FontTools
+    </namerecord>
+    <namerecord nameID="11" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      https://github.com/behdad/fonttools
+    </namerecord>
+    <namerecord nameID="12" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      https://github.com/behdad/fonttools
+    </namerecord>
+    <namerecord nameID="14" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+    </namerecord>
+    <namerecord nameID="18" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Test TTF
+    </namerecord>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (c) 2015 by FontTools. No rights reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test OTF
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      FontTools: Test OTF: 2015
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test OTF
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestOTF-Regular
+    </namerecord>
+    <namerecord nameID="7" platformID="3" platEncID="1" langID="0x409">
+      Test OTF is not a trademark of FontTools.
+    </namerecord>
+    <namerecord nameID="8" platformID="3" platEncID="1" langID="0x409">
+      FontTools
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      FontTools
+    </namerecord>
+    <namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
+      https://github.com/behdad/fonttools
+    </namerecord>
+    <namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
+      https://github.com/behdad/fonttools
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x0" name=".null"/><!-- ???? -->
+      <map code="0xd" name="CR"/><!-- ???? -->
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x2e" name="period"/><!-- FULL STOP -->
+      <map code="0x2026" name="ellipsis"/><!-- HORIZONTAL ELLIPSIS -->
+    </cmap_format_4>
+    <cmap_format_6 platformID="1" platEncID="0" language="0">
+      <map code="0x0" name=".null"/>
+      <map code="0x1" name=".notdef"/>
+      <map code="0x2" name=".notdef"/>
+      <map code="0x3" name=".notdef"/>
+      <map code="0x4" name=".notdef"/>
+      <map code="0x5" name=".notdef"/>
+      <map code="0x6" name=".notdef"/>
+      <map code="0x7" name=".notdef"/>
+      <map code="0x8" name=".notdef"/>
+      <map code="0x9" name=".notdef"/>
+      <map code="0xa" name=".notdef"/>
+      <map code="0xb" name=".notdef"/>
+      <map code="0xc" name=".notdef"/>
+      <map code="0xd" name="CR"/>
+      <map code="0xe" name=".notdef"/>
+      <map code="0xf" name=".notdef"/>
+      <map code="0x10" name=".notdef"/>
+      <map code="0x11" name=".notdef"/>
+      <map code="0x12" name=".notdef"/>
+      <map code="0x13" name=".notdef"/>
+      <map code="0x14" name=".notdef"/>
+      <map code="0x15" name=".notdef"/>
+      <map code="0x16" name=".notdef"/>
+      <map code="0x17" name=".notdef"/>
+      <map code="0x18" name=".notdef"/>
+      <map code="0x19" name=".notdef"/>
+      <map code="0x1a" name=".notdef"/>
+      <map code="0x1b" name=".notdef"/>
+      <map code="0x1c" name=".notdef"/>
+      <map code="0x1d" name=".notdef"/>
+      <map code="0x1e" name=".notdef"/>
+      <map code="0x1f" name=".notdef"/>
+      <map code="0x20" name="space"/>
+      <map code="0x21" name=".notdef"/>
+      <map code="0x22" name=".notdef"/>
+      <map code="0x23" name=".notdef"/>
+      <map code="0x24" name=".notdef"/>
+      <map code="0x25" name=".notdef"/>
+      <map code="0x26" name=".notdef"/>
+      <map code="0x27" name=".notdef"/>
+      <map code="0x28" name=".notdef"/>
+      <map code="0x29" name=".notdef"/>
+      <map code="0x2a" name=".notdef"/>
+      <map code="0x2b" name=".notdef"/>
+      <map code="0x2c" name=".notdef"/>
+      <map code="0x2d" name=".notdef"/>
+      <map code="0x2e" name="period"/>
+      <map code="0x2f" name=".notdef"/>
+      <map code="0x30" name=".notdef"/>
+      <map code="0x31" name=".notdef"/>
+      <map code="0x32" name=".notdef"/>
+      <map code="0x33" name=".notdef"/>
+      <map code="0x34" name=".notdef"/>
+      <map code="0x35" name=".notdef"/>
+      <map code="0x36" name=".notdef"/>
+      <map code="0x37" name=".notdef"/>
+      <map code="0x38" name=".notdef"/>
+      <map code="0x39" name=".notdef"/>
+      <map code="0x3a" name=".notdef"/>
+      <map code="0x3b" name=".notdef"/>
+      <map code="0x3c" name=".notdef"/>
+      <map code="0x3d" name=".notdef"/>
+      <map code="0x3e" name=".notdef"/>
+      <map code="0x3f" name=".notdef"/>
+      <map code="0x40" name=".notdef"/>
+      <map code="0x41" name=".notdef"/>
+      <map code="0x42" name=".notdef"/>
+      <map code="0x43" name=".notdef"/>
+      <map code="0x44" name=".notdef"/>
+      <map code="0x45" name=".notdef"/>
+      <map code="0x46" name=".notdef"/>
+      <map code="0x47" name=".notdef"/>
+      <map code="0x48" name=".notdef"/>
+      <map code="0x49" name=".notdef"/>
+      <map code="0x4a" name=".notdef"/>
+      <map code="0x4b" name=".notdef"/>
+      <map code="0x4c" name=".notdef"/>
+      <map code="0x4d" name=".notdef"/>
+      <map code="0x4e" name=".notdef"/>
+      <map code="0x4f" name=".notdef"/>
+      <map code="0x50" name=".notdef"/>
+      <map code="0x51" name=".notdef"/>
+      <map code="0x52" name=".notdef"/>
+      <map code="0x53" name=".notdef"/>
+      <map code="0x54" name=".notdef"/>
+      <map code="0x55" name=".notdef"/>
+      <map code="0x56" name=".notdef"/>
+      <map code="0x57" name=".notdef"/>
+      <map code="0x58" name=".notdef"/>
+      <map code="0x59" name=".notdef"/>
+      <map code="0x5a" name=".notdef"/>
+      <map code="0x5b" name=".notdef"/>
+      <map code="0x5c" name=".notdef"/>
+      <map code="0x5d" name=".notdef"/>
+      <map code="0x5e" name=".notdef"/>
+      <map code="0x5f" name=".notdef"/>
+      <map code="0x60" name=".notdef"/>
+      <map code="0x61" name=".notdef"/>
+      <map code="0x62" name=".notdef"/>
+      <map code="0x63" name=".notdef"/>
+      <map code="0x64" name=".notdef"/>
+      <map code="0x65" name=".notdef"/>
+      <map code="0x66" name=".notdef"/>
+      <map code="0x67" name=".notdef"/>
+      <map code="0x68" name=".notdef"/>
+      <map code="0x69" name=".notdef"/>
+      <map code="0x6a" name=".notdef"/>
+      <map code="0x6b" name=".notdef"/>
+      <map code="0x6c" name=".notdef"/>
+      <map code="0x6d" name=".notdef"/>
+      <map code="0x6e" name=".notdef"/>
+      <map code="0x6f" name=".notdef"/>
+      <map code="0x70" name=".notdef"/>
+      <map code="0x71" name=".notdef"/>
+      <map code="0x72" name=".notdef"/>
+      <map code="0x73" name=".notdef"/>
+      <map code="0x74" name=".notdef"/>
+      <map code="0x75" name=".notdef"/>
+      <map code="0x76" name=".notdef"/>
+      <map code="0x77" name=".notdef"/>
+      <map code="0x78" name=".notdef"/>
+      <map code="0x79" name=".notdef"/>
+      <map code="0x7a" name=".notdef"/>
+      <map code="0x7b" name=".notdef"/>
+      <map code="0x7c" name=".notdef"/>
+      <map code="0x7d" name=".notdef"/>
+      <map code="0x7e" name=".notdef"/>
+      <map code="0x7f" name=".notdef"/>
+      <map code="0x80" name=".notdef"/>
+      <map code="0x81" name=".notdef"/>
+      <map code="0x82" name=".notdef"/>
+      <map code="0x83" name=".notdef"/>
+      <map code="0x84" name=".notdef"/>
+      <map code="0x85" name=".notdef"/>
+      <map code="0x86" name=".notdef"/>
+      <map code="0x87" name=".notdef"/>
+      <map code="0x88" name=".notdef"/>
+      <map code="0x89" name=".notdef"/>
+      <map code="0x8a" name=".notdef"/>
+      <map code="0x8b" name=".notdef"/>
+      <map code="0x8c" name=".notdef"/>
+      <map code="0x8d" name=".notdef"/>
+      <map code="0x8e" name=".notdef"/>
+      <map code="0x8f" name=".notdef"/>
+      <map code="0x90" name=".notdef"/>
+      <map code="0x91" name=".notdef"/>
+      <map code="0x92" name=".notdef"/>
+      <map code="0x93" name=".notdef"/>
+      <map code="0x94" name=".notdef"/>
+      <map code="0x95" name=".notdef"/>
+      <map code="0x96" name=".notdef"/>
+      <map code="0x97" name=".notdef"/>
+      <map code="0x98" name=".notdef"/>
+      <map code="0x99" name=".notdef"/>
+      <map code="0x9a" name=".notdef"/>
+      <map code="0x9b" name=".notdef"/>
+      <map code="0x9c" name=".notdef"/>
+      <map code="0x9d" name=".notdef"/>
+      <map code="0x9e" name=".notdef"/>
+      <map code="0x9f" name=".notdef"/>
+      <map code="0xa0" name=".notdef"/>
+      <map code="0xa1" name=".notdef"/>
+      <map code="0xa2" name=".notdef"/>
+      <map code="0xa3" name=".notdef"/>
+      <map code="0xa4" name=".notdef"/>
+      <map code="0xa5" name=".notdef"/>
+      <map code="0xa6" name=".notdef"/>
+      <map code="0xa7" name=".notdef"/>
+      <map code="0xa8" name=".notdef"/>
+      <map code="0xa9" name=".notdef"/>
+      <map code="0xaa" name=".notdef"/>
+      <map code="0xab" name=".notdef"/>
+      <map code="0xac" name=".notdef"/>
+      <map code="0xad" name=".notdef"/>
+      <map code="0xae" name=".notdef"/>
+      <map code="0xaf" name=".notdef"/>
+      <map code="0xb0" name=".notdef"/>
+      <map code="0xb1" name=".notdef"/>
+      <map code="0xb2" name=".notdef"/>
+      <map code="0xb3" name=".notdef"/>
+      <map code="0xb4" name=".notdef"/>
+      <map code="0xb5" name=".notdef"/>
+      <map code="0xb6" name=".notdef"/>
+      <map code="0xb7" name=".notdef"/>
+      <map code="0xb8" name=".notdef"/>
+      <map code="0xb9" name=".notdef"/>
+      <map code="0xba" name=".notdef"/>
+      <map code="0xbb" name=".notdef"/>
+      <map code="0xbc" name=".notdef"/>
+      <map code="0xbd" name=".notdef"/>
+      <map code="0xbe" name=".notdef"/>
+      <map code="0xbf" name=".notdef"/>
+      <map code="0xc0" name=".notdef"/>
+      <map code="0xc1" name=".notdef"/>
+      <map code="0xc2" name=".notdef"/>
+      <map code="0xc3" name=".notdef"/>
+      <map code="0xc4" name=".notdef"/>
+      <map code="0xc5" name=".notdef"/>
+      <map code="0xc6" name=".notdef"/>
+      <map code="0xc7" name=".notdef"/>
+      <map code="0xc8" name=".notdef"/>
+      <map code="0xc9" name="ellipsis"/>
+    </cmap_format_6>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x0" name=".null"/><!-- ???? -->
+      <map code="0xd" name="CR"/><!-- ???? -->
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x2e" name="period"/><!-- FULL STOP -->
+      <map code="0x2026" name="ellipsis"/><!-- HORIZONTAL ELLIPSIS -->
+    </cmap_format_4>
+  </cmap>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <CFF>
+    <CFFFont name="TestOTF-Regular">
+      <version value="001.001"/>
+      <Notice value="Copyright \(c\) 2015 by FontTools. No rights reserved."/>
+      <FullName value="Test OTF"/>
+      <FamilyName value="Test OTF"/>
+      <Weight value="Regular"/>
+      <isFixedPitch value="0"/>
+      <ItalicAngle value="0"/>
+      <UnderlineThickness value="50"/>
+      <PaintType value="0"/>
+      <CharstringType value="2"/>
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FontBBox value="50 0 668 750"/>
+      <StrokeWidth value="0"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <Encoding name="StandardEncoding"/>
+      <Private>
+        <BlueScale value="0.039625"/>
+        <BlueShift value="7"/>
+        <BlueFuzz value="1"/>
+        <ForceBold value="0"/>
+        <LanguageGroup value="0"/>
+        <ExpansionFactor value="0.06"/>
+        <initialRandomSeed value="0"/>
+        <defaultWidthX value="0"/>
+        <nominalWidthX value="0"/>
+        <Subrs>
+          <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+          <CharString index="0">
+            131 122 -131 hlineto
+            return
+          </CharString>
+        </Subrs>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          500 450 hmoveto
+          750 -400 -750 vlineto
+          50 50 rmoveto
+          650 300 -650 vlineto
+          endchar
+        </CharString>
+        <CharString name=".null">
+          0 endchar
+        </CharString>
+        <CharString name="CR">
+          250 endchar
+        </CharString>
+        <CharString name="ellipsis">
+          723 55 hmoveto
+          -107 callsubr
+          241 -122 rmoveto
+          -107 callsubr
+          241 -122 rmoveto
+          -107 callsubr
+          endchar
+        </CharString>
+        <CharString name="period">
+          241 55 hmoveto
+          -107 callsubr
+          endchar
+        </CharString>
+        <CharString name="space">
+          250 endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name=".null" width="0" lsb="0"/>
+    <mtx name="CR" width="250" lsb="0"/>
+    <mtx name="ellipsis" width="723" lsb="55"/>
+    <mtx name="period" width="241" lsb="55"/>
+    <mtx name="space" width="250" lsb="0"/>
+  </hmtx>
+
+  <DSIG>
+    <!-- note that the Digital Signature will be invalid after recompilation! -->
+    <tableHeader flag="0x0" numSigs="0" version="1"/>
+  </DSIG>
+
+</ttFont>
diff --git a/Tests/ttx/data/TestTTC.ttc b/Tests/ttx/data/TestTTC.ttc
new file mode 100644
index 0000000..a21fe89
--- /dev/null
+++ b/Tests/ttx/data/TestTTC.ttc
Binary files differ
diff --git a/Tests/ttx/data/TestTTF.ttf b/Tests/ttx/data/TestTTF.ttf
new file mode 100644
index 0000000..e906d01
--- /dev/null
+++ b/Tests/ttx/data/TestTTF.ttf
Binary files differ
diff --git a/Tests/ttx/data/TestTTF.ttx b/Tests/ttx/data/TestTTF.ttx
new file mode 100644
index 0000000..c283a29
--- /dev/null
+++ b/Tests/ttx/data/TestTTF.ttx
@@ -0,0 +1,553 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name=".null"/>
+    <GlyphID id="2" name="CR"/>
+    <GlyphID id="3" name="space"/>
+    <GlyphID id="4" name="period"/>
+    <GlyphID id="5" name="ellipsis"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x2ee689e2"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Jun  4 14:29:11 2015"/>
+    <modified value="Mon Aug  3 13:04:43 2015"/>
+    <xMin value="50"/>
+    <yMin value="0"/>
+    <xMax value="668"/>
+    <yMax value="750"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="9"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="900"/>
+    <descent value="-300"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="723"/>
+    <minLeftSideBearing value="50"/>
+    <minRightSideBearing value="50"/>
+    <xMaxExtent value="668"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="6"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="8"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="12"/>
+    <maxCompositeContours value="3"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="3"/>
+    <maxComponentDepth value="1"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="392"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="700"/>
+    <ySubscriptYSize value="650"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="140"/>
+    <ySuperscriptXSize value="700"/>
+    <ySuperscriptYSize value="650"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="477"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="250"/>
+    <sFamilyClass value="2050"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="6"/>
+      <bProportion value="4"/>
+      <bContrast value="4"/>
+      <bStrokeVariation value="2"/>
+      <bArmStyle value="7"/>
+      <bLetterForm value="8"/>
+      <bMidline value="1"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="10000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="NONE"/>
+    <fsSelection value="00000000 11000000"/>
+    <usFirstCharIndex value="0"/>
+    <usLastCharIndex value="8230"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="900"/>
+    <usWinDescent value="300"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name=".null" width="0" lsb="0"/>
+    <mtx name="CR" width="250" lsb="0"/>
+    <mtx name="ellipsis" width="723" lsb="55"/>
+    <mtx name="period" width="241" lsb="55"/>
+    <mtx name="space" width="250" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x0" name=".null"/><!-- ???? -->
+      <map code="0xd" name="CR"/><!-- ???? -->
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x2e" name="period"/><!-- FULL STOP -->
+      <map code="0x2026" name="ellipsis"/><!-- HORIZONTAL ELLIPSIS -->
+    </cmap_format_4>
+    <cmap_format_6 platformID="1" platEncID="0" language="0">
+      <map code="0x0" name=".null"/>
+      <map code="0x1" name=".notdef"/>
+      <map code="0x2" name=".notdef"/>
+      <map code="0x3" name=".notdef"/>
+      <map code="0x4" name=".notdef"/>
+      <map code="0x5" name=".notdef"/>
+      <map code="0x6" name=".notdef"/>
+      <map code="0x7" name=".notdef"/>
+      <map code="0x8" name=".notdef"/>
+      <map code="0x9" name=".notdef"/>
+      <map code="0xa" name=".notdef"/>
+      <map code="0xb" name=".notdef"/>
+      <map code="0xc" name=".notdef"/>
+      <map code="0xd" name="CR"/>
+      <map code="0xe" name=".notdef"/>
+      <map code="0xf" name=".notdef"/>
+      <map code="0x10" name=".notdef"/>
+      <map code="0x11" name=".notdef"/>
+      <map code="0x12" name=".notdef"/>
+      <map code="0x13" name=".notdef"/>
+      <map code="0x14" name=".notdef"/>
+      <map code="0x15" name=".notdef"/>
+      <map code="0x16" name=".notdef"/>
+      <map code="0x17" name=".notdef"/>
+      <map code="0x18" name=".notdef"/>
+      <map code="0x19" name=".notdef"/>
+      <map code="0x1a" name=".notdef"/>
+      <map code="0x1b" name=".notdef"/>
+      <map code="0x1c" name=".notdef"/>
+      <map code="0x1d" name=".notdef"/>
+      <map code="0x1e" name=".notdef"/>
+      <map code="0x1f" name=".notdef"/>
+      <map code="0x20" name="space"/>
+      <map code="0x21" name=".notdef"/>
+      <map code="0x22" name=".notdef"/>
+      <map code="0x23" name=".notdef"/>
+      <map code="0x24" name=".notdef"/>
+      <map code="0x25" name=".notdef"/>
+      <map code="0x26" name=".notdef"/>
+      <map code="0x27" name=".notdef"/>
+      <map code="0x28" name=".notdef"/>
+      <map code="0x29" name=".notdef"/>
+      <map code="0x2a" name=".notdef"/>
+      <map code="0x2b" name=".notdef"/>
+      <map code="0x2c" name=".notdef"/>
+      <map code="0x2d" name=".notdef"/>
+      <map code="0x2e" name="period"/>
+      <map code="0x2f" name=".notdef"/>
+      <map code="0x30" name=".notdef"/>
+      <map code="0x31" name=".notdef"/>
+      <map code="0x32" name=".notdef"/>
+      <map code="0x33" name=".notdef"/>
+      <map code="0x34" name=".notdef"/>
+      <map code="0x35" name=".notdef"/>
+      <map code="0x36" name=".notdef"/>
+      <map code="0x37" name=".notdef"/>
+      <map code="0x38" name=".notdef"/>
+      <map code="0x39" name=".notdef"/>
+      <map code="0x3a" name=".notdef"/>
+      <map code="0x3b" name=".notdef"/>
+      <map code="0x3c" name=".notdef"/>
+      <map code="0x3d" name=".notdef"/>
+      <map code="0x3e" name=".notdef"/>
+      <map code="0x3f" name=".notdef"/>
+      <map code="0x40" name=".notdef"/>
+      <map code="0x41" name=".notdef"/>
+      <map code="0x42" name=".notdef"/>
+      <map code="0x43" name=".notdef"/>
+      <map code="0x44" name=".notdef"/>
+      <map code="0x45" name=".notdef"/>
+      <map code="0x46" name=".notdef"/>
+      <map code="0x47" name=".notdef"/>
+      <map code="0x48" name=".notdef"/>
+      <map code="0x49" name=".notdef"/>
+      <map code="0x4a" name=".notdef"/>
+      <map code="0x4b" name=".notdef"/>
+      <map code="0x4c" name=".notdef"/>
+      <map code="0x4d" name=".notdef"/>
+      <map code="0x4e" name=".notdef"/>
+      <map code="0x4f" name=".notdef"/>
+      <map code="0x50" name=".notdef"/>
+      <map code="0x51" name=".notdef"/>
+      <map code="0x52" name=".notdef"/>
+      <map code="0x53" name=".notdef"/>
+      <map code="0x54" name=".notdef"/>
+      <map code="0x55" name=".notdef"/>
+      <map code="0x56" name=".notdef"/>
+      <map code="0x57" name=".notdef"/>
+      <map code="0x58" name=".notdef"/>
+      <map code="0x59" name=".notdef"/>
+      <map code="0x5a" name=".notdef"/>
+      <map code="0x5b" name=".notdef"/>
+      <map code="0x5c" name=".notdef"/>
+      <map code="0x5d" name=".notdef"/>
+      <map code="0x5e" name=".notdef"/>
+      <map code="0x5f" name=".notdef"/>
+      <map code="0x60" name=".notdef"/>
+      <map code="0x61" name=".notdef"/>
+      <map code="0x62" name=".notdef"/>
+      <map code="0x63" name=".notdef"/>
+      <map code="0x64" name=".notdef"/>
+      <map code="0x65" name=".notdef"/>
+      <map code="0x66" name=".notdef"/>
+      <map code="0x67" name=".notdef"/>
+      <map code="0x68" name=".notdef"/>
+      <map code="0x69" name=".notdef"/>
+      <map code="0x6a" name=".notdef"/>
+      <map code="0x6b" name=".notdef"/>
+      <map code="0x6c" name=".notdef"/>
+      <map code="0x6d" name=".notdef"/>
+      <map code="0x6e" name=".notdef"/>
+      <map code="0x6f" name=".notdef"/>
+      <map code="0x70" name=".notdef"/>
+      <map code="0x71" name=".notdef"/>
+      <map code="0x72" name=".notdef"/>
+      <map code="0x73" name=".notdef"/>
+      <map code="0x74" name=".notdef"/>
+      <map code="0x75" name=".notdef"/>
+      <map code="0x76" name=".notdef"/>
+      <map code="0x77" name=".notdef"/>
+      <map code="0x78" name=".notdef"/>
+      <map code="0x79" name=".notdef"/>
+      <map code="0x7a" name=".notdef"/>
+      <map code="0x7b" name=".notdef"/>
+      <map code="0x7c" name=".notdef"/>
+      <map code="0x7d" name=".notdef"/>
+      <map code="0x7e" name=".notdef"/>
+      <map code="0x7f" name=".notdef"/>
+      <map code="0x80" name=".notdef"/>
+      <map code="0x81" name=".notdef"/>
+      <map code="0x82" name=".notdef"/>
+      <map code="0x83" name=".notdef"/>
+      <map code="0x84" name=".notdef"/>
+      <map code="0x85" name=".notdef"/>
+      <map code="0x86" name=".notdef"/>
+      <map code="0x87" name=".notdef"/>
+      <map code="0x88" name=".notdef"/>
+      <map code="0x89" name=".notdef"/>
+      <map code="0x8a" name=".notdef"/>
+      <map code="0x8b" name=".notdef"/>
+      <map code="0x8c" name=".notdef"/>
+      <map code="0x8d" name=".notdef"/>
+      <map code="0x8e" name=".notdef"/>
+      <map code="0x8f" name=".notdef"/>
+      <map code="0x90" name=".notdef"/>
+      <map code="0x91" name=".notdef"/>
+      <map code="0x92" name=".notdef"/>
+      <map code="0x93" name=".notdef"/>
+      <map code="0x94" name=".notdef"/>
+      <map code="0x95" name=".notdef"/>
+      <map code="0x96" name=".notdef"/>
+      <map code="0x97" name=".notdef"/>
+      <map code="0x98" name=".notdef"/>
+      <map code="0x99" name=".notdef"/>
+      <map code="0x9a" name=".notdef"/>
+      <map code="0x9b" name=".notdef"/>
+      <map code="0x9c" name=".notdef"/>
+      <map code="0x9d" name=".notdef"/>
+      <map code="0x9e" name=".notdef"/>
+      <map code="0x9f" name=".notdef"/>
+      <map code="0xa0" name=".notdef"/>
+      <map code="0xa1" name=".notdef"/>
+      <map code="0xa2" name=".notdef"/>
+      <map code="0xa3" name=".notdef"/>
+      <map code="0xa4" name=".notdef"/>
+      <map code="0xa5" name=".notdef"/>
+      <map code="0xa6" name=".notdef"/>
+      <map code="0xa7" name=".notdef"/>
+      <map code="0xa8" name=".notdef"/>
+      <map code="0xa9" name=".notdef"/>
+      <map code="0xaa" name=".notdef"/>
+      <map code="0xab" name=".notdef"/>
+      <map code="0xac" name=".notdef"/>
+      <map code="0xad" name=".notdef"/>
+      <map code="0xae" name=".notdef"/>
+      <map code="0xaf" name=".notdef"/>
+      <map code="0xb0" name=".notdef"/>
+      <map code="0xb1" name=".notdef"/>
+      <map code="0xb2" name=".notdef"/>
+      <map code="0xb3" name=".notdef"/>
+      <map code="0xb4" name=".notdef"/>
+      <map code="0xb5" name=".notdef"/>
+      <map code="0xb6" name=".notdef"/>
+      <map code="0xb7" name=".notdef"/>
+      <map code="0xb8" name=".notdef"/>
+      <map code="0xb9" name=".notdef"/>
+      <map code="0xba" name=".notdef"/>
+      <map code="0xbb" name=".notdef"/>
+      <map code="0xbc" name=".notdef"/>
+      <map code="0xbd" name=".notdef"/>
+      <map code="0xbe" name=".notdef"/>
+      <map code="0xbf" name=".notdef"/>
+      <map code="0xc0" name=".notdef"/>
+      <map code="0xc1" name=".notdef"/>
+      <map code="0xc2" name=".notdef"/>
+      <map code="0xc3" name=".notdef"/>
+      <map code="0xc4" name=".notdef"/>
+      <map code="0xc5" name=".notdef"/>
+      <map code="0xc6" name=".notdef"/>
+      <map code="0xc7" name=".notdef"/>
+      <map code="0xc8" name=".notdef"/>
+      <map code="0xc9" name="ellipsis"/>
+    </cmap_format_6>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x0" name=".null"/><!-- ???? -->
+      <map code="0xd" name="CR"/><!-- ???? -->
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x2e" name="period"/><!-- FULL STOP -->
+      <map code="0x2026" name="ellipsis"/><!-- HORIZONTAL ELLIPSIS -->
+    </cmap_format_4>
+  </cmap>
+
+  <fpgm>
+    <assembly>
+      SVTCA[0]  /* SetFPVectorToAxis */
+    </assembly>
+  </fpgm>
+
+  <prep>
+    <assembly>
+      SVTCA[0]  /* SetFPVectorToAxis */
+    </assembly>
+  </prep>
+
+  <cvt>
+    <cv index="0" value="0"/>
+  </cvt>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="50" yMin="0" xMax="450" yMax="750">
+      <contour>
+        <pt x="50" y="0" on="1"/>
+        <pt x="50" y="750" on="1"/>
+        <pt x="450" y="750" on="1"/>
+        <pt x="450" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="400" y="50" on="1"/>
+        <pt x="400" y="700" on="1"/>
+        <pt x="100" y="700" on="1"/>
+        <pt x="100" y="50" on="1"/>
+      </contour>
+      <instructions><assembly>
+          SVTCA[0]  /* SetFPVectorToAxis */
+          SVTCA[1]  /* SetFPVectorToAxis */
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name=".null"/><!-- contains no outline data -->
+
+    <TTGlyph name="CR"/><!-- contains no outline data -->
+
+    <TTGlyph name="ellipsis" xMin="55" yMin="0" xMax="668" yMax="122">
+      <component glyphName="period" x="0" y="0" flags="0x4"/>
+      <component glyphName="period" x="241" y="0" flags="0x4"/>
+      <component glyphName="period" x="482" y="0" flags="0x4"/>
+      <instructions><assembly>
+          SVTCA[0]  /* SetFPVectorToAxis */
+          SVTCA[1]  /* SetFPVectorToAxis */
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="period" xMin="55" yMin="0" xMax="186" yMax="122">
+      <contour>
+        <pt x="55" y="122" on="1"/>
+        <pt x="186" y="122" on="1"/>
+        <pt x="186" y="0" on="1"/>
+        <pt x="55" y="0" on="1"/>
+      </contour>
+      <instructions><assembly>
+          SVTCA[0]  /* SetFPVectorToAxis */
+          SVTCA[1]  /* SetFPVectorToAxis */
+        </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Copyright (c) 2015 by FontTools. No rights reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Test TTF
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      FontTools: Test TTF: 2015
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Test TTF
+    </namerecord>
+    <namerecord nameID="5" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestTTF-Regular
+    </namerecord>
+    <namerecord nameID="7" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Test TTF is not a trademark of FontTools.
+    </namerecord>
+    <namerecord nameID="8" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      FontTools
+    </namerecord>
+    <namerecord nameID="9" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      FontTools
+    </namerecord>
+    <namerecord nameID="11" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      https://github.com/behdad/fonttools
+    </namerecord>
+    <namerecord nameID="12" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      https://github.com/behdad/fonttools
+    </namerecord>
+    <namerecord nameID="14" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+    </namerecord>
+    <namerecord nameID="18" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Test TTF
+    </namerecord>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (c) 2015 by FontTools. No rights reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test TTF
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      FontTools: Test TTF: 2015
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test TTF
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestTTF-Regular
+    </namerecord>
+    <namerecord nameID="7" platformID="3" platEncID="1" langID="0x409">
+      Test TTF is not a trademark of FontTools.
+    </namerecord>
+    <namerecord nameID="8" platformID="3" platEncID="1" langID="0x409">
+      FontTools
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      FontTools
+    </namerecord>
+    <namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
+      https://github.com/behdad/fonttools
+    </namerecord>
+    <namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
+      https://github.com/behdad/fonttools
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name=".null"/>
+      <psName name="CR"/>
+    </extraNames>
+  </post>
+
+  <gasp>
+    <gaspRange rangeMaxPPEM="8" rangeGaspBehavior="10"/>
+    <gaspRange rangeMaxPPEM="65535" rangeGaspBehavior="15"/>
+  </gasp>
+
+  <DSIG>
+    <!-- note that the Digital Signature will be invalid after recompilation! -->
+    <tableHeader flag="0x0" numSigs="0" version="1"/>
+  </DSIG>
+
+</ttFont>
diff --git a/Tests/ttx/data/TestWOFF.woff b/Tests/ttx/data/TestWOFF.woff
new file mode 100644
index 0000000..64cd78d
--- /dev/null
+++ b/Tests/ttx/data/TestWOFF.woff
Binary files differ
diff --git a/Tests/ttx/data/TestWOFF2.woff2 b/Tests/ttx/data/TestWOFF2.woff2
new file mode 100644
index 0000000..a345573
--- /dev/null
+++ b/Tests/ttx/data/TestWOFF2.woff2
Binary files differ
diff --git a/Tests/ttx/ttx_test.py b/Tests/ttx/ttx_test.py
new file mode 100644
index 0000000..ceb7abd
--- /dev/null
+++ b/Tests/ttx/ttx_test.py
@@ -0,0 +1,1014 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.testTools import parseXML
+from fontTools.misc.timeTools import timestampSinceEpoch
+from fontTools.ttLib import TTFont, TTLibError
+from fontTools import ttx
+import getopt
+import logging
+import os
+import shutil
+import sys
+import tempfile
+import unittest
+
+import pytest
+
+try:
+    import zopfli
+except ImportError:
+    zopfli = None
+try:
+    import brotli
+except ImportError:
+    brotli = None
+
+
+class TTXTest(unittest.TestCase):
+
+    def __init__(self, methodName):
+        unittest.TestCase.__init__(self, methodName)
+        # Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
+        # and fires deprecation warnings if a program uses the old name.
+        if not hasattr(self, "assertRaisesRegex"):
+            self.assertRaisesRegex = self.assertRaisesRegexp
+
+    def setUp(self):
+        self.tempdir = None
+        self.num_tempfiles = 0
+
+    def tearDown(self):
+        if self.tempdir:
+            shutil.rmtree(self.tempdir)
+
+    @staticmethod
+    def getpath(testfile):
+        path, _ = os.path.split(__file__)
+        return os.path.join(path, "data", testfile)
+
+    def temp_dir(self):
+        if not self.tempdir:
+            self.tempdir = tempfile.mkdtemp()
+
+    def temp_font(self, font_path, file_name):
+        self.temp_dir()
+        temppath = os.path.join(self.tempdir, file_name)
+        shutil.copy2(font_path, temppath)
+        return temppath
+
+    @staticmethod
+    def read_file(file_path):
+        with open(file_path, "r", encoding="utf-8") as f:
+            return f.readlines()
+
+    # -----
+    # Tests
+    # -----
+
+    def test_parseOptions_no_args(self):
+        with self.assertRaises(getopt.GetoptError) as cm:
+            ttx.parseOptions([])
+        self.assertTrue(
+            "Must specify at least one input file" in str(cm.exception)
+        )
+
+    def test_parseOptions_invalid_path(self):
+        file_path = "invalid_font_path"
+        with self.assertRaises(getopt.GetoptError) as cm:
+            ttx.parseOptions([file_path])
+        self.assertTrue('File not found: "%s"' % file_path in str(cm.exception))
+
+    def test_parseOptions_font2ttx_1st_time(self):
+        file_name = "TestOTF.otf"
+        font_path = self.getpath(file_name)
+        temp_path = self.temp_font(font_path, file_name)
+        jobs, _ = ttx.parseOptions([temp_path])
+        self.assertEqual(jobs[0][0].__name__, "ttDump")
+        self.assertEqual(
+            jobs[0][1:],
+            (
+                os.path.join(self.tempdir, file_name),
+                os.path.join(self.tempdir, file_name.split(".")[0] + ".ttx"),
+            ),
+        )
+
+    def test_parseOptions_font2ttx_2nd_time(self):
+        file_name = "TestTTF.ttf"
+        font_path = self.getpath(file_name)
+        temp_path = self.temp_font(font_path, file_name)
+        _, _ = ttx.parseOptions([temp_path])  # this is NOT a mistake
+        jobs, _ = ttx.parseOptions([temp_path])
+        self.assertEqual(jobs[0][0].__name__, "ttDump")
+        self.assertEqual(
+            jobs[0][1:],
+            (
+                os.path.join(self.tempdir, file_name),
+                os.path.join(self.tempdir, file_name.split(".")[0] + "#1.ttx"),
+            ),
+        )
+
+    def test_parseOptions_ttx2font_1st_time(self):
+        file_name = "TestTTF.ttx"
+        font_path = self.getpath(file_name)
+        temp_path = self.temp_font(font_path, file_name)
+        jobs, _ = ttx.parseOptions([temp_path])
+        self.assertEqual(jobs[0][0].__name__, "ttCompile")
+        self.assertEqual(
+            jobs[0][1:],
+            (
+                os.path.join(self.tempdir, file_name),
+                os.path.join(self.tempdir, file_name.split(".")[0] + ".ttf"),
+            ),
+        )
+
+    def test_parseOptions_ttx2font_2nd_time(self):
+        file_name = "TestOTF.ttx"
+        font_path = self.getpath(file_name)
+        temp_path = self.temp_font(font_path, file_name)
+        _, _ = ttx.parseOptions([temp_path])  # this is NOT a mistake
+        jobs, _ = ttx.parseOptions([temp_path])
+        self.assertEqual(jobs[0][0].__name__, "ttCompile")
+        self.assertEqual(
+            jobs[0][1:],
+            (
+                os.path.join(self.tempdir, file_name),
+                os.path.join(self.tempdir, file_name.split(".")[0] + "#1.otf"),
+            ),
+        )
+
+    def test_parseOptions_multiple_fonts(self):
+        file_names = ["TestOTF.otf", "TestTTF.ttf"]
+        font_paths = [self.getpath(file_name) for file_name in file_names]
+        temp_paths = [
+            self.temp_font(font_path, file_name)
+            for font_path, file_name in zip(font_paths, file_names)
+        ]
+        jobs, _ = ttx.parseOptions(temp_paths)
+        for i in range(len(jobs)):
+            self.assertEqual(jobs[i][0].__name__, "ttDump")
+            self.assertEqual(
+                jobs[i][1:],
+                (
+                    os.path.join(self.tempdir, file_names[i]),
+                    os.path.join(
+                        self.tempdir, file_names[i].split(".")[0] + ".ttx"
+                    ),
+                ),
+            )
+
+    def test_parseOptions_mixed_files(self):
+        operations = ["ttDump", "ttCompile"]
+        extensions = [".ttx", ".ttf"]
+        file_names = ["TestOTF.otf", "TestTTF.ttx"]
+        font_paths = [self.getpath(file_name) for file_name in file_names]
+        temp_paths = [
+            self.temp_font(font_path, file_name)
+            for font_path, file_name in zip(font_paths, file_names)
+        ]
+        jobs, _ = ttx.parseOptions(temp_paths)
+        for i in range(len(jobs)):
+            self.assertEqual(jobs[i][0].__name__, operations[i])
+            self.assertEqual(
+                jobs[i][1:],
+                (
+                    os.path.join(self.tempdir, file_names[i]),
+                    os.path.join(
+                        self.tempdir,
+                        file_names[i].split(".")[0] + extensions[i],
+                    ),
+                ),
+            )
+
+    def test_parseOptions_splitTables(self):
+        file_name = "TestTTF.ttf"
+        font_path = self.getpath(file_name)
+        temp_path = self.temp_font(font_path, file_name)
+        args = ["-s", temp_path]
+
+        jobs, options = ttx.parseOptions(args)
+
+        ttx_file_path = jobs[0][2]
+        temp_folder = os.path.dirname(ttx_file_path)
+        self.assertTrue(options.splitTables)
+        self.assertTrue(os.path.exists(ttx_file_path))
+
+        ttx.process(jobs, options)
+
+        # Read the TTX file but strip the first two and the last lines:
+        # <?xml version="1.0" encoding="UTF-8"?>
+        # <ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.22">
+        # ...
+        # </ttFont>
+        parsed_xml = parseXML(self.read_file(ttx_file_path)[2:-1])
+        for item in parsed_xml:
+            if not isinstance(item, tuple):
+                continue
+            # the tuple looks like this:
+            # (u'head', {u'src': u'TestTTF._h_e_a_d.ttx'}, [])
+            table_file_name = item[1].get("src")
+            table_file_path = os.path.join(temp_folder, table_file_name)
+            self.assertTrue(os.path.exists(table_file_path))
+
+    def test_parseOptions_splitGlyphs(self):
+        file_name = "TestTTF.ttf"
+        font_path = self.getpath(file_name)
+        temp_path = self.temp_font(font_path, file_name)
+        args = ["-g", temp_path]
+
+        jobs, options = ttx.parseOptions(args)
+
+        ttx_file_path = jobs[0][2]
+        temp_folder = os.path.dirname(ttx_file_path)
+        self.assertTrue(options.splitGlyphs)
+        # splitGlyphs also forces splitTables
+        self.assertTrue(options.splitTables)
+        self.assertTrue(os.path.exists(ttx_file_path))
+
+        ttx.process(jobs, options)
+
+        # Read the TTX file but strip the first two and the last lines:
+        # <?xml version="1.0" encoding="UTF-8"?>
+        # <ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.22">
+        # ...
+        # </ttFont>
+        for item in parseXML(self.read_file(ttx_file_path)[2:-1]):
+            if not isinstance(item, tuple):
+                continue
+            # the tuple looks like this:
+            # (u'head', {u'src': u'TestTTF._h_e_a_d.ttx'}, [])
+            table_tag = item[0]
+            table_file_name = item[1].get("src")
+            table_file_path = os.path.join(temp_folder, table_file_name)
+            self.assertTrue(os.path.exists(table_file_path))
+            if table_tag != "glyf":
+                continue
+            # also strip the enclosing 'glyf' element
+            for item in parseXML(self.read_file(table_file_path)[4:-3]):
+                if not isinstance(item, tuple):
+                    continue
+                # glyphs without outline data only have 'name' attribute
+                glyph_file_name = item[1].get("src")
+                if glyph_file_name is not None:
+                    glyph_file_path = os.path.join(temp_folder, glyph_file_name)
+                    self.assertTrue(os.path.exists(glyph_file_path))
+
+    def test_guessFileType_ttf(self):
+        file_name = "TestTTF.ttf"
+        font_path = self.getpath(file_name)
+        self.assertEqual(ttx.guessFileType(font_path), "TTF")
+
+    def test_guessFileType_otf(self):
+        file_name = "TestOTF.otf"
+        font_path = self.getpath(file_name)
+        self.assertEqual(ttx.guessFileType(font_path), "OTF")
+
+    def test_guessFileType_woff(self):
+        file_name = "TestWOFF.woff"
+        font_path = self.getpath(file_name)
+        self.assertEqual(ttx.guessFileType(font_path), "WOFF")
+
+    def test_guessFileType_woff2(self):
+        file_name = "TestWOFF2.woff2"
+        font_path = self.getpath(file_name)
+        self.assertEqual(ttx.guessFileType(font_path), "WOFF2")
+
+    def test_guessFileType_ttc(self):
+        file_name = "TestTTC.ttc"
+        font_path = self.getpath(file_name)
+        self.assertEqual(ttx.guessFileType(font_path), "TTC")
+
+    def test_guessFileType_dfont(self):
+        file_name = "TestDFONT.dfont"
+        font_path = self.getpath(file_name)
+        self.assertEqual(ttx.guessFileType(font_path), "TTF")
+
+    def test_guessFileType_ttx_ttf(self):
+        file_name = "TestTTF.ttx"
+        font_path = self.getpath(file_name)
+        self.assertEqual(ttx.guessFileType(font_path), "TTX")
+
+    def test_guessFileType_ttx_otf(self):
+        file_name = "TestOTF.ttx"
+        font_path = self.getpath(file_name)
+        self.assertEqual(ttx.guessFileType(font_path), "OTX")
+
+    def test_guessFileType_ttx_bom(self):
+        file_name = "TestBOM.ttx"
+        font_path = self.getpath(file_name)
+        self.assertEqual(ttx.guessFileType(font_path), "TTX")
+
+    def test_guessFileType_ttx_no_sfntVersion(self):
+        file_name = "TestNoSFNT.ttx"
+        font_path = self.getpath(file_name)
+        self.assertEqual(ttx.guessFileType(font_path), "TTX")
+
+    def test_guessFileType_ttx_no_xml(self):
+        file_name = "TestNoXML.ttx"
+        font_path = self.getpath(file_name)
+        self.assertIsNone(ttx.guessFileType(font_path))
+
+    def test_guessFileType_invalid_path(self):
+        font_path = "invalid_font_path"
+        self.assertIsNone(ttx.guessFileType(font_path))
+
+
+# -----------------------
+# ttx.Options class tests
+# -----------------------
+
+
+def test_options_flag_h(capsys):
+    with pytest.raises(SystemExit):
+        ttx.Options([("-h", None)], 1)
+
+    out, err = capsys.readouterr()
+    assert "TTX -- From OpenType To XML And Back" in out
+
+
+def test_options_flag_version(capsys):
+    with pytest.raises(SystemExit):
+        ttx.Options([("--version", None)], 1)
+
+    out, err = capsys.readouterr()
+    version_list = out.split(".")
+    assert len(version_list) >= 3
+    assert version_list[0].isdigit()
+    assert version_list[1].isdigit()
+    assert version_list[2].strip().isdigit()
+
+
+def test_options_d_goodpath(tmpdir):
+    temp_dir_path = str(tmpdir)
+    tto = ttx.Options([("-d", temp_dir_path)], 1)
+    assert tto.outputDir == temp_dir_path
+
+
+def test_options_d_badpath():
+    with pytest.raises(getopt.GetoptError):
+        ttx.Options([("-d", "bogusdir")], 1)
+
+
+def test_options_o():
+    tto = ttx.Options([("-o", "testfile.ttx")], 1)
+    assert tto.outputFile == "testfile.ttx"
+
+
+def test_options_f():
+    tto = ttx.Options([("-f", "")], 1)
+    assert tto.overWrite is True
+
+
+def test_options_v():
+    tto = ttx.Options([("-v", "")], 1)
+    assert tto.verbose is True
+    assert tto.logLevel == logging.DEBUG
+
+
+def test_options_q():
+    tto = ttx.Options([("-q", "")], 1)
+    assert tto.quiet is True
+    assert tto.logLevel == logging.WARNING
+
+
+def test_options_l():
+    tto = ttx.Options([("-l", "")], 1)
+    assert tto.listTables is True
+
+
+def test_options_t_nopadding():
+    tto = ttx.Options([("-t", "CFF2")], 1)
+    assert len(tto.onlyTables) == 1
+    assert tto.onlyTables[0] == "CFF2"
+
+
+def test_options_t_withpadding():
+    tto = ttx.Options([("-t", "CFF")], 1)
+    assert len(tto.onlyTables) == 1
+    assert tto.onlyTables[0] == "CFF "
+
+
+def test_options_s():
+    tto = ttx.Options([("-s", "")], 1)
+    assert tto.splitTables is True
+    assert tto.splitGlyphs is False
+
+
+def test_options_g():
+    tto = ttx.Options([("-g", "")], 1)
+    assert tto.splitGlyphs is True
+    assert tto.splitTables is True
+
+
+def test_options_i():
+    tto = ttx.Options([("-i", "")], 1)
+    assert tto.disassembleInstructions is False
+
+
+def test_options_z_validoptions():
+    valid_options = ("raw", "row", "bitwise", "extfile")
+    for option in valid_options:
+        tto = ttx.Options([("-z", option)], 1)
+        assert tto.bitmapGlyphDataFormat == option
+
+
+def test_options_z_invalidoption():
+    with pytest.raises(getopt.GetoptError):
+        ttx.Options([("-z", "bogus")], 1)
+
+
+def test_options_y_validvalue():
+    tto = ttx.Options([("-y", "1")], 1)
+    assert tto.fontNumber == 1
+
+
+def test_options_y_invalidvalue():
+    with pytest.raises(ValueError):
+        ttx.Options([("-y", "A")], 1)
+
+
+def test_options_m():
+    tto = ttx.Options([("-m", "testfont.ttf")], 1)
+    assert tto.mergeFile == "testfont.ttf"
+
+
+def test_options_b():
+    tto = ttx.Options([("-b", "")], 1)
+    assert tto.recalcBBoxes is False
+
+
+def test_options_a():
+    tto = ttx.Options([("-a", "")], 1)
+    assert tto.allowVID is True
+
+
+def test_options_e():
+    tto = ttx.Options([("-e", "")], 1)
+    assert tto.ignoreDecompileErrors is False
+
+
+def test_options_unicodedata():
+    tto = ttx.Options([("--unicodedata", "UnicodeData.txt")], 1)
+    assert tto.unicodedata == "UnicodeData.txt"
+
+
+def test_options_newline_lf():
+    tto = ttx.Options([("--newline", "LF")], 1)
+    assert tto.newlinestr == "\n"
+
+
+def test_options_newline_cr():
+    tto = ttx.Options([("--newline", "CR")], 1)
+    assert tto.newlinestr == "\r"
+
+
+def test_options_newline_crlf():
+    tto = ttx.Options([("--newline", "CRLF")], 1)
+    assert tto.newlinestr == "\r\n"
+
+
+def test_options_newline_invalid():
+    with pytest.raises(getopt.GetoptError):
+        ttx.Options([("--newline", "BOGUS")], 1)
+
+
+def test_options_recalc_timestamp():
+    tto = ttx.Options([("--recalc-timestamp", "")], 1)
+    assert tto.recalcTimestamp is True
+
+
+def test_options_flavor():
+    tto = ttx.Options([("--flavor", "woff")], 1)
+    assert tto.flavor == "woff"
+
+
+def test_options_with_zopfli():
+    tto = ttx.Options([("--with-zopfli", ""), ("--flavor", "woff")], 1)
+    assert tto.useZopfli is True
+
+
+def test_options_with_zopfli_fails_without_woff_flavor():
+    with pytest.raises(getopt.GetoptError):
+        ttx.Options([("--with-zopfli", "")], 1)
+
+
+def test_options_quiet_and_verbose_shouldfail():
+    with pytest.raises(getopt.GetoptError):
+        ttx.Options([("-q", ""), ("-v", "")], 1)
+
+
+def test_options_mergefile_and_flavor_shouldfail():
+    with pytest.raises(getopt.GetoptError):
+        ttx.Options([("-m", "testfont.ttf"), ("--flavor", "woff")], 1)
+
+
+def test_options_onlytables_and_skiptables_shouldfail():
+    with pytest.raises(getopt.GetoptError):
+        ttx.Options([("-t", "CFF"), ("-x", "CFF2")], 1)
+
+
+def test_options_mergefile_and_multiplefiles_shouldfail():
+    with pytest.raises(getopt.GetoptError):
+        ttx.Options([("-m", "testfont.ttf")], 2)
+
+
+def test_options_woff2_and_zopfli_shouldfail():
+    with pytest.raises(getopt.GetoptError):
+        ttx.Options([("--with-zopfli", ""), ("--flavor", "woff2")], 1)
+
+
+# ----------------------------
+# ttx.ttCompile function tests
+# ----------------------------
+
+
+def test_ttcompile_otf_compile_default(tmpdir):
+    inttx = os.path.join("Tests", "ttx", "data", "TestOTF.ttx")
+    # outotf = os.path.join(str(tmpdir), "TestOTF.otf")
+    outotf = tmpdir.join("TestOTF.ttx")
+    default_options = ttx.Options([], 1)
+    ttx.ttCompile(inttx, str(outotf), default_options)
+    # confirm that font was built
+    assert outotf.check(file=True)
+    # confirm that it is valid OTF file, can instantiate a TTFont, has expected OpenType tables
+    ttf = TTFont(str(outotf))
+    expected_tables = (
+        "head",
+        "hhea",
+        "maxp",
+        "OS/2",
+        "name",
+        "cmap",
+        "post",
+        "CFF ",
+        "hmtx",
+        "DSIG",
+    )
+    for table in expected_tables:
+        assert table in ttf
+
+
+def test_ttcompile_otf_to_woff_without_zopfli(tmpdir):
+    inttx = os.path.join("Tests", "ttx", "data", "TestOTF.ttx")
+    outwoff = tmpdir.join("TestOTF.woff")
+    options = ttx.Options([], 1)
+    options.flavor = "woff"
+    ttx.ttCompile(inttx, str(outwoff), options)
+    # confirm that font was built
+    assert outwoff.check(file=True)
+    # confirm that it is valid TTF file, can instantiate a TTFont, has expected OpenType tables
+    ttf = TTFont(str(outwoff))
+    expected_tables = (
+        "head",
+        "hhea",
+        "maxp",
+        "OS/2",
+        "name",
+        "cmap",
+        "post",
+        "CFF ",
+        "hmtx",
+        "DSIG",
+    )
+    for table in expected_tables:
+        assert table in ttf
+
+
+@pytest.mark.skipif(zopfli is None, reason="zopfli not installed")
+def test_ttcompile_otf_to_woff_with_zopfli(tmpdir):
+    inttx = os.path.join("Tests", "ttx", "data", "TestOTF.ttx")
+    outwoff = tmpdir.join("TestOTF.woff")
+    options = ttx.Options([], 1)
+    options.flavor = "woff"
+    options.useZopfli = True
+    ttx.ttCompile(inttx, str(outwoff), options)
+    # confirm that font was built
+    assert outwoff.check(file=True)
+    # confirm that it is valid TTF file, can instantiate a TTFont, has expected OpenType tables
+    ttf = TTFont(str(outwoff))
+    expected_tables = (
+        "head",
+        "hhea",
+        "maxp",
+        "OS/2",
+        "name",
+        "cmap",
+        "post",
+        "CFF ",
+        "hmtx",
+        "DSIG",
+    )
+    for table in expected_tables:
+        assert table in ttf
+
+
+@pytest.mark.skipif(brotli is None, reason="brotli not installed")
+def test_ttcompile_otf_to_woff2(tmpdir):
+    inttx = os.path.join("Tests", "ttx", "data", "TestOTF.ttx")
+    outwoff2 = tmpdir.join("TestTTF.woff2")
+    options = ttx.Options([], 1)
+    options.flavor = "woff2"
+    ttx.ttCompile(inttx, str(outwoff2), options)
+    # confirm that font was built
+    assert outwoff2.check(file=True)
+    # confirm that it is valid TTF file, can instantiate a TTFont, has expected OpenType tables
+    ttf = TTFont(str(outwoff2))
+    # DSIG should not be included from original ttx as per woff2 spec (https://dev.w3.org/webfonts/WOFF2/spec/)
+    assert "DSIG" not in ttf
+    expected_tables = (
+        "head",
+        "hhea",
+        "maxp",
+        "OS/2",
+        "name",
+        "cmap",
+        "post",
+        "CFF ",
+        "hmtx",
+    )
+    for table in expected_tables:
+        assert table in ttf
+
+
+def test_ttcompile_ttf_compile_default(tmpdir):
+    inttx = os.path.join("Tests", "ttx", "data", "TestTTF.ttx")
+    outttf = tmpdir.join("TestTTF.ttf")
+    default_options = ttx.Options([], 1)
+    ttx.ttCompile(inttx, str(outttf), default_options)
+    # confirm that font was built
+    assert outttf.check(file=True)
+    # confirm that it is valid TTF file, can instantiate a TTFont, has expected OpenType tables
+    ttf = TTFont(str(outttf))
+    expected_tables = (
+        "head",
+        "hhea",
+        "maxp",
+        "OS/2",
+        "name",
+        "cmap",
+        "hmtx",
+        "fpgm",
+        "prep",
+        "cvt ",
+        "loca",
+        "glyf",
+        "post",
+        "gasp",
+        "DSIG",
+    )
+    for table in expected_tables:
+        assert table in ttf
+
+
+def test_ttcompile_ttf_to_woff_without_zopfli(tmpdir):
+    inttx = os.path.join("Tests", "ttx", "data", "TestTTF.ttx")
+    outwoff = tmpdir.join("TestTTF.woff")
+    options = ttx.Options([], 1)
+    options.flavor = "woff"
+    ttx.ttCompile(inttx, str(outwoff), options)
+    # confirm that font was built
+    assert outwoff.check(file=True)
+    # confirm that it is valid TTF file, can instantiate a TTFont, has expected OpenType tables
+    ttf = TTFont(str(outwoff))
+    expected_tables = (
+        "head",
+        "hhea",
+        "maxp",
+        "OS/2",
+        "name",
+        "cmap",
+        "hmtx",
+        "fpgm",
+        "prep",
+        "cvt ",
+        "loca",
+        "glyf",
+        "post",
+        "gasp",
+        "DSIG",
+    )
+    for table in expected_tables:
+        assert table in ttf
+
+
+@pytest.mark.skipif(zopfli is None, reason="zopfli not installed")
+def test_ttcompile_ttf_to_woff_with_zopfli(tmpdir):
+    inttx = os.path.join("Tests", "ttx", "data", "TestTTF.ttx")
+    outwoff = tmpdir.join("TestTTF.woff")
+    options = ttx.Options([], 1)
+    options.flavor = "woff"
+    options.useZopfli = True
+    ttx.ttCompile(inttx, str(outwoff), options)
+    # confirm that font was built
+    assert outwoff.check(file=True)
+    # confirm that it is valid TTF file, can instantiate a TTFont, has expected OpenType tables
+    ttf = TTFont(str(outwoff))
+    expected_tables = (
+        "head",
+        "hhea",
+        "maxp",
+        "OS/2",
+        "name",
+        "cmap",
+        "hmtx",
+        "fpgm",
+        "prep",
+        "cvt ",
+        "loca",
+        "glyf",
+        "post",
+        "gasp",
+        "DSIG",
+    )
+    for table in expected_tables:
+        assert table in ttf
+
+
+@pytest.mark.skipif(brotli is None, reason="brotli not installed")
+def test_ttcompile_ttf_to_woff2(tmpdir):
+    inttx = os.path.join("Tests", "ttx", "data", "TestTTF.ttx")
+    outwoff2 = tmpdir.join("TestTTF.woff2")
+    options = ttx.Options([], 1)
+    options.flavor = "woff2"
+    ttx.ttCompile(inttx, str(outwoff2), options)
+    # confirm that font was built
+    assert outwoff2.check(file=True)
+    # confirm that it is valid TTF file, can instantiate a TTFont, has expected OpenType tables
+    ttf = TTFont(str(outwoff2))
+    # DSIG should not be included from original ttx as per woff2 spec (https://dev.w3.org/webfonts/WOFF2/spec/)
+    assert "DSIG" not in ttf
+    expected_tables = (
+        "head",
+        "hhea",
+        "maxp",
+        "OS/2",
+        "name",
+        "cmap",
+        "hmtx",
+        "fpgm",
+        "prep",
+        "cvt ",
+        "loca",
+        "glyf",
+        "post",
+        "gasp",
+    )
+    for table in expected_tables:
+        assert table in ttf
+
+
+@pytest.mark.parametrize(
+    "inpath, outpath1, outpath2",
+    [
+        ("TestTTF.ttx", "TestTTF1.ttf", "TestTTF2.ttf"),
+        ("TestOTF.ttx", "TestOTF1.otf", "TestOTF2.otf"),
+    ],
+)
+def test_ttcompile_timestamp_calcs(inpath, outpath1, outpath2, tmpdir):
+    inttx = os.path.join("Tests", "ttx", "data", inpath)
+    outttf1 = tmpdir.join(outpath1)
+    outttf2 = tmpdir.join(outpath2)
+    options = ttx.Options([], 1)
+    # build with default options = do not recalculate timestamp
+    ttx.ttCompile(inttx, str(outttf1), options)
+    # confirm that font was built
+    assert outttf1.check(file=True)
+    # confirm that timestamp is same as modified time on ttx file
+    mtime = os.path.getmtime(inttx)
+    epochtime = timestampSinceEpoch(mtime)
+    ttf = TTFont(str(outttf1))
+    assert ttf["head"].modified == epochtime
+
+    # reset options to recalculate the timestamp and compile new font
+    options.recalcTimestamp = True
+    ttx.ttCompile(inttx, str(outttf2), options)
+    # confirm that font was built
+    assert outttf2.check(file=True)
+    # confirm that timestamp is more recent than modified time on ttx file
+    mtime = os.path.getmtime(inttx)
+    epochtime = timestampSinceEpoch(mtime)
+    ttf = TTFont(str(outttf2))
+    assert ttf["head"].modified > epochtime
+
+
+# -------------------------
+# ttx.ttList function tests
+# -------------------------
+
+
+def test_ttlist_ttf(capsys, tmpdir):
+    inpath = os.path.join("Tests", "ttx", "data", "TestTTF.ttf")
+    fakeoutpath = tmpdir.join("TestTTF.ttx")
+    options = ttx.Options([], 1)
+    options.listTables = True
+    ttx.ttList(inpath, str(fakeoutpath), options)
+    out, err = capsys.readouterr()
+    expected_tables = (
+        "head",
+        "hhea",
+        "maxp",
+        "OS/2",
+        "name",
+        "cmap",
+        "hmtx",
+        "fpgm",
+        "prep",
+        "cvt ",
+        "loca",
+        "glyf",
+        "post",
+        "gasp",
+        "DSIG",
+    )
+    # confirm that expected tables are printed to stdout
+    for table in expected_tables:
+        assert table in out
+    # test for one of the expected tag/checksum/length/offset strings
+    assert "OS/2  0x67230FF8        96       376" in out
+
+
+def test_ttlist_otf(capsys, tmpdir):
+    inpath = os.path.join("Tests", "ttx", "data", "TestOTF.otf")
+    fakeoutpath = tmpdir.join("TestOTF.ttx")
+    options = ttx.Options([], 1)
+    options.listTables = True
+    ttx.ttList(inpath, str(fakeoutpath), options)
+    out, err = capsys.readouterr()
+    expected_tables = (
+        "head",
+        "hhea",
+        "maxp",
+        "OS/2",
+        "name",
+        "cmap",
+        "post",
+        "CFF ",
+        "hmtx",
+        "DSIG",
+    )
+    # confirm that expected tables are printed to stdout
+    for table in expected_tables:
+        assert table in out
+    # test for one of the expected tag/checksum/length/offset strings
+    assert "OS/2  0x67230FF8        96       272" in out
+
+
+def test_ttlist_woff(capsys, tmpdir):
+    inpath = os.path.join("Tests", "ttx", "data", "TestWOFF.woff")
+    fakeoutpath = tmpdir.join("TestWOFF.ttx")
+    options = ttx.Options([], 1)
+    options.listTables = True
+    options.flavor = "woff"
+    ttx.ttList(inpath, str(fakeoutpath), options)
+    out, err = capsys.readouterr()
+    expected_tables = (
+        "head",
+        "hhea",
+        "maxp",
+        "OS/2",
+        "name",
+        "cmap",
+        "post",
+        "CFF ",
+        "hmtx",
+        "DSIG",
+    )
+    # confirm that expected tables are printed to stdout
+    for table in expected_tables:
+        assert table in out
+    # test for one of the expected tag/checksum/length/offset strings
+    assert "OS/2  0x67230FF8        84       340" in out
+
+
+@pytest.mark.skipif(brotli is None, reason="brotli not installed")
+def test_ttlist_woff2(capsys, tmpdir):
+    inpath = os.path.join("Tests", "ttx", "data", "TestWOFF2.woff2")
+    fakeoutpath = tmpdir.join("TestWOFF2.ttx")
+    options = ttx.Options([], 1)
+    options.listTables = True
+    options.flavor = "woff2"
+    ttx.ttList(inpath, str(fakeoutpath), options)
+    out, err = capsys.readouterr()
+    expected_tables = (
+        "head",
+        "hhea",
+        "maxp",
+        "OS/2",
+        "name",
+        "cmap",
+        "hmtx",
+        "fpgm",
+        "prep",
+        "cvt ",
+        "loca",
+        "glyf",
+        "post",
+        "gasp",
+    )
+    # confirm that expected tables are printed to stdout
+    for table in expected_tables:
+        assert table in out
+    # test for one of the expected tag/checksum/length/offset strings
+    assert "OS/2  0x67230FF8        96         0" in out
+
+
+# -------------------
+# main function tests
+# -------------------
+
+
+def test_main_default_ttf_dump_to_ttx(tmpdir):
+    inpath = os.path.join("Tests", "ttx", "data", "TestTTF.ttf")
+    outpath = tmpdir.join("TestTTF.ttx")
+    args = ["-o", str(outpath), inpath]
+    ttx.main(args)
+    assert outpath.check(file=True)
+
+
+def test_main_default_ttx_compile_to_ttf(tmpdir):
+    inpath = os.path.join("Tests", "ttx", "data", "TestTTF.ttx")
+    outpath = tmpdir.join("TestTTF.ttf")
+    args = ["-o", str(outpath), inpath]
+    ttx.main(args)
+    assert outpath.check(file=True)
+
+
+def test_main_getopterror_missing_directory():
+    with pytest.raises(SystemExit):
+        with pytest.raises(getopt.GetoptError):
+            inpath = os.path.join("Tests", "ttx", "data", "TestTTF.ttf")
+            args = ["-d", "bogusdir", inpath]
+            ttx.main(args)
+
+
+def test_main_keyboard_interrupt(tmpdir, monkeypatch, capsys):
+    with pytest.raises(SystemExit):
+        inpath = os.path.join("Tests", "ttx", "data", "TestTTF.ttx")
+        outpath = tmpdir.join("TestTTF.ttf")
+        args = ["-o", str(outpath), inpath]
+        monkeypatch.setattr(
+            ttx, "process", (lambda x, y: raise_exception(KeyboardInterrupt))
+        )
+        ttx.main(args)
+
+    out, err = capsys.readouterr()
+    assert "(Cancelled.)" in err
+
+
+@pytest.mark.skipif(
+    sys.platform == "win32",
+    reason="waitForKeyPress function causes test to hang on Windows platform",
+)
+def test_main_system_exit(tmpdir, monkeypatch):
+    with pytest.raises(SystemExit):
+        inpath = os.path.join("Tests", "ttx", "data", "TestTTF.ttx")
+        outpath = tmpdir.join("TestTTF.ttf")
+        args = ["-o", str(outpath), inpath]
+        monkeypatch.setattr(
+            ttx, "process", (lambda x, y: raise_exception(SystemExit))
+        )
+        ttx.main(args)
+
+
+def test_main_ttlib_error(tmpdir, monkeypatch, capsys):
+    with pytest.raises(SystemExit):
+        inpath = os.path.join("Tests", "ttx", "data", "TestTTF.ttx")
+        outpath = tmpdir.join("TestTTF.ttf")
+        args = ["-o", str(outpath), inpath]
+        monkeypatch.setattr(
+            ttx,
+            "process",
+            (lambda x, y: raise_exception(TTLibError("Test error"))),
+        )
+        ttx.main(args)
+
+    out, err = capsys.readouterr()
+    assert "Test error" in err
+
+
+@pytest.mark.skipif(
+    sys.platform == "win32",
+    reason="waitForKeyPress function causes test to hang on Windows platform",
+)
+def test_main_base_exception(tmpdir, monkeypatch, capsys):
+    with pytest.raises(SystemExit):
+        inpath = os.path.join("Tests", "ttx", "data", "TestTTF.ttx")
+        outpath = tmpdir.join("TestTTF.ttf")
+        args = ["-o", str(outpath), inpath]
+        monkeypatch.setattr(
+            ttx,
+            "process",
+            (lambda x, y: raise_exception(Exception("Test error"))),
+        )
+        ttx.main(args)
+
+    out, err = capsys.readouterr()
+    assert "Unhandled exception has occurred" in err
+
+
+# ---------------------------
+# support functions for tests
+# ---------------------------
+
+
+def raise_exception(exception):
+    raise exception
diff --git a/Tests/unicodedata_test.py b/Tests/unicodedata_test.py
new file mode 100644
index 0000000..96c0f01
--- /dev/null
+++ b/Tests/unicodedata_test.py
@@ -0,0 +1,254 @@
+from __future__ import (
+    print_function, division, absolute_import, unicode_literals)
+from fontTools.misc.py23 import *
+
+from fontTools import unicodedata
+
+import pytest
+
+
+def test_script():
+    assert unicodedata.script("a") == "Latn"
+    assert unicodedata.script(unichr(0)) == "Zyyy"
+    assert unicodedata.script(unichr(0x0378)) == "Zzzz"
+    assert unicodedata.script(unichr(0x10FFFF)) == "Zzzz"
+
+    # these were randomly sampled, one character per script
+    assert unicodedata.script(unichr(0x1E918)) == 'Adlm'
+    assert unicodedata.script(unichr(0x1170D)) == 'Ahom'
+    assert unicodedata.script(unichr(0x145A0)) == 'Hluw'
+    assert unicodedata.script(unichr(0x0607)) == 'Arab'
+    assert unicodedata.script(unichr(0x056C)) == 'Armn'
+    assert unicodedata.script(unichr(0x10B27)) == 'Avst'
+    assert unicodedata.script(unichr(0x1B41)) == 'Bali'
+    assert unicodedata.script(unichr(0x168AD)) == 'Bamu'
+    assert unicodedata.script(unichr(0x16ADD)) == 'Bass'
+    assert unicodedata.script(unichr(0x1BE5)) == 'Batk'
+    assert unicodedata.script(unichr(0x09F3)) == 'Beng'
+    assert unicodedata.script(unichr(0x11C5B)) == 'Bhks'
+    assert unicodedata.script(unichr(0x3126)) == 'Bopo'
+    assert unicodedata.script(unichr(0x1103B)) == 'Brah'
+    assert unicodedata.script(unichr(0x2849)) == 'Brai'
+    assert unicodedata.script(unichr(0x1A0A)) == 'Bugi'
+    assert unicodedata.script(unichr(0x174E)) == 'Buhd'
+    assert unicodedata.script(unichr(0x18EE)) == 'Cans'
+    assert unicodedata.script(unichr(0x102B7)) == 'Cari'
+    assert unicodedata.script(unichr(0x1053D)) == 'Aghb'
+    assert unicodedata.script(unichr(0x11123)) == 'Cakm'
+    assert unicodedata.script(unichr(0xAA1F)) == 'Cham'
+    assert unicodedata.script(unichr(0xAB95)) == 'Cher'
+    assert unicodedata.script(unichr(0x1F0C7)) == 'Zyyy'
+    assert unicodedata.script(unichr(0x2C85)) == 'Copt'
+    assert unicodedata.script(unichr(0x12014)) == 'Xsux'
+    assert unicodedata.script(unichr(0x1082E)) == 'Cprt'
+    assert unicodedata.script(unichr(0xA686)) == 'Cyrl'
+    assert unicodedata.script(unichr(0x10417)) == 'Dsrt'
+    assert unicodedata.script(unichr(0x093E)) == 'Deva'
+    assert unicodedata.script(unichr(0x1BC4B)) == 'Dupl'
+    assert unicodedata.script(unichr(0x1310C)) == 'Egyp'
+    assert unicodedata.script(unichr(0x1051C)) == 'Elba'
+    assert unicodedata.script(unichr(0x2DA6)) == 'Ethi'
+    assert unicodedata.script(unichr(0x10AD)) == 'Geor'
+    assert unicodedata.script(unichr(0x2C52)) == 'Glag'
+    assert unicodedata.script(unichr(0x10343)) == 'Goth'
+    assert unicodedata.script(unichr(0x11371)) == 'Gran'
+    assert unicodedata.script(unichr(0x03D0)) == 'Grek'
+    assert unicodedata.script(unichr(0x0AAA)) == 'Gujr'
+    assert unicodedata.script(unichr(0x0A4C)) == 'Guru'
+    assert unicodedata.script(unichr(0x23C9F)) == 'Hani'
+    assert unicodedata.script(unichr(0xC259)) == 'Hang'
+    assert unicodedata.script(unichr(0x1722)) == 'Hano'
+    assert unicodedata.script(unichr(0x108F5)) == 'Hatr'
+    assert unicodedata.script(unichr(0x05C2)) == 'Hebr'
+    assert unicodedata.script(unichr(0x1B072)) == 'Hira'
+    assert unicodedata.script(unichr(0x10847)) == 'Armi'
+    assert unicodedata.script(unichr(0x033A)) == 'Zinh'
+    assert unicodedata.script(unichr(0x10B66)) == 'Phli'
+    assert unicodedata.script(unichr(0x10B4B)) == 'Prti'
+    assert unicodedata.script(unichr(0xA98A)) == 'Java'
+    assert unicodedata.script(unichr(0x110B2)) == 'Kthi'
+    assert unicodedata.script(unichr(0x0CC6)) == 'Knda'
+    assert unicodedata.script(unichr(0x3337)) == 'Kana'
+    assert unicodedata.script(unichr(0xA915)) == 'Kali'
+    assert unicodedata.script(unichr(0x10A2E)) == 'Khar'
+    assert unicodedata.script(unichr(0x17AA)) == 'Khmr'
+    assert unicodedata.script(unichr(0x11225)) == 'Khoj'
+    assert unicodedata.script(unichr(0x112B6)) == 'Sind'
+    assert unicodedata.script(unichr(0x0ED7)) == 'Laoo'
+    assert unicodedata.script(unichr(0xAB3C)) == 'Latn'
+    assert unicodedata.script(unichr(0x1C48)) == 'Lepc'
+    assert unicodedata.script(unichr(0x1923)) == 'Limb'
+    assert unicodedata.script(unichr(0x1071D)) == 'Lina'
+    assert unicodedata.script(unichr(0x100EC)) == 'Linb'
+    assert unicodedata.script(unichr(0xA4E9)) == 'Lisu'
+    assert unicodedata.script(unichr(0x10284)) == 'Lyci'
+    assert unicodedata.script(unichr(0x10926)) == 'Lydi'
+    assert unicodedata.script(unichr(0x11161)) == 'Mahj'
+    assert unicodedata.script(unichr(0x0D56)) == 'Mlym'
+    assert unicodedata.script(unichr(0x0856)) == 'Mand'
+    assert unicodedata.script(unichr(0x10AF0)) == 'Mani'
+    assert unicodedata.script(unichr(0x11CB0)) == 'Marc'
+    assert unicodedata.script(unichr(0x11D28)) == 'Gonm'
+    assert unicodedata.script(unichr(0xABDD)) == 'Mtei'
+    assert unicodedata.script(unichr(0x1E897)) == 'Mend'
+    assert unicodedata.script(unichr(0x109B0)) == 'Merc'
+    assert unicodedata.script(unichr(0x10993)) == 'Mero'
+    assert unicodedata.script(unichr(0x16F5D)) == 'Plrd'
+    assert unicodedata.script(unichr(0x1160B)) == 'Modi'
+    assert unicodedata.script(unichr(0x18A8)) == 'Mong'
+    assert unicodedata.script(unichr(0x16A48)) == 'Mroo'
+    assert unicodedata.script(unichr(0x1128C)) == 'Mult'
+    assert unicodedata.script(unichr(0x105B)) == 'Mymr'
+    assert unicodedata.script(unichr(0x108AF)) == 'Nbat'
+    assert unicodedata.script(unichr(0x19B3)) == 'Talu'
+    assert unicodedata.script(unichr(0x1143D)) == 'Newa'
+    assert unicodedata.script(unichr(0x07F4)) == 'Nkoo'
+    assert unicodedata.script(unichr(0x1B192)) == 'Nshu'
+    assert unicodedata.script(unichr(0x169C)) == 'Ogam'
+    assert unicodedata.script(unichr(0x1C56)) == 'Olck'
+    assert unicodedata.script(unichr(0x10CE9)) == 'Hung'
+    assert unicodedata.script(unichr(0x10316)) == 'Ital'
+    assert unicodedata.script(unichr(0x10A93)) == 'Narb'
+    assert unicodedata.script(unichr(0x1035A)) == 'Perm'
+    assert unicodedata.script(unichr(0x103D5)) == 'Xpeo'
+    assert unicodedata.script(unichr(0x10A65)) == 'Sarb'
+    assert unicodedata.script(unichr(0x10C09)) == 'Orkh'
+    assert unicodedata.script(unichr(0x0B60)) == 'Orya'
+    assert unicodedata.script(unichr(0x104CF)) == 'Osge'
+    assert unicodedata.script(unichr(0x104A8)) == 'Osma'
+    assert unicodedata.script(unichr(0x16B12)) == 'Hmng'
+    assert unicodedata.script(unichr(0x10879)) == 'Palm'
+    assert unicodedata.script(unichr(0x11AF1)) == 'Pauc'
+    assert unicodedata.script(unichr(0xA869)) == 'Phag'
+    assert unicodedata.script(unichr(0x10909)) == 'Phnx'
+    assert unicodedata.script(unichr(0x10B81)) == 'Phlp'
+    assert unicodedata.script(unichr(0xA941)) == 'Rjng'
+    assert unicodedata.script(unichr(0x16C3)) == 'Runr'
+    assert unicodedata.script(unichr(0x0814)) == 'Samr'
+    assert unicodedata.script(unichr(0xA88C)) == 'Saur'
+    assert unicodedata.script(unichr(0x111C8)) == 'Shrd'
+    assert unicodedata.script(unichr(0x1045F)) == 'Shaw'
+    assert unicodedata.script(unichr(0x115AD)) == 'Sidd'
+    assert unicodedata.script(unichr(0x1D8C0)) == 'Sgnw'
+    assert unicodedata.script(unichr(0x0DB9)) == 'Sinh'
+    assert unicodedata.script(unichr(0x110F9)) == 'Sora'
+    assert unicodedata.script(unichr(0x11A60)) == 'Soyo'
+    assert unicodedata.script(unichr(0x1B94)) == 'Sund'
+    assert unicodedata.script(unichr(0xA81F)) == 'Sylo'
+    assert unicodedata.script(unichr(0x0740)) == 'Syrc'
+    assert unicodedata.script(unichr(0x1714)) == 'Tglg'
+    assert unicodedata.script(unichr(0x1761)) == 'Tagb'
+    assert unicodedata.script(unichr(0x1965)) == 'Tale'
+    assert unicodedata.script(unichr(0x1A32)) == 'Lana'
+    assert unicodedata.script(unichr(0xAA86)) == 'Tavt'
+    assert unicodedata.script(unichr(0x116A5)) == 'Takr'
+    assert unicodedata.script(unichr(0x0B8E)) == 'Taml'
+    assert unicodedata.script(unichr(0x1754D)) == 'Tang'
+    assert unicodedata.script(unichr(0x0C40)) == 'Telu'
+    assert unicodedata.script(unichr(0x07A4)) == 'Thaa'
+    assert unicodedata.script(unichr(0x0E42)) == 'Thai'
+    assert unicodedata.script(unichr(0x0F09)) == 'Tibt'
+    assert unicodedata.script(unichr(0x2D3A)) == 'Tfng'
+    assert unicodedata.script(unichr(0x114B0)) == 'Tirh'
+    assert unicodedata.script(unichr(0x1038B)) == 'Ugar'
+    assert unicodedata.script(unichr(0xA585)) == 'Vaii'
+    assert unicodedata.script(unichr(0x118CF)) == 'Wara'
+    assert unicodedata.script(unichr(0xA066)) == 'Yiii'
+    assert unicodedata.script(unichr(0x11A31)) == 'Zanb'
+
+
+def test_script_extension():
+    assert unicodedata.script_extension("a") == {"Latn"}
+    assert unicodedata.script_extension(unichr(0)) == {"Zyyy"}
+    assert unicodedata.script_extension(unichr(0x0378)) == {"Zzzz"}
+    assert unicodedata.script_extension(unichr(0x10FFFF)) == {"Zzzz"}
+
+    assert unicodedata.script_extension("\u0660") == {'Arab', 'Thaa'}
+    assert unicodedata.script_extension("\u0964") == {
+        'Beng', 'Deva', 'Gran', 'Gujr', 'Guru', 'Knda', 'Mahj', 'Mlym',
+        'Orya', 'Sind', 'Sinh', 'Sylo', 'Takr', 'Taml', 'Telu', 'Tirh'}
+
+
+def test_script_name():
+    assert unicodedata.script_name("Latn") == "Latin"
+    assert unicodedata.script_name("Zyyy") == "Common"
+    assert unicodedata.script_name("Zzzz") == "Unknown"
+    # underscores in long names are replaced by spaces
+    assert unicodedata.script_name("Egyp") == "Egyptian Hieroglyphs"
+
+    with pytest.raises(KeyError):
+        unicodedata.script_name("QQQQ")
+    assert unicodedata.script_name("QQQQ", default="Unknown")
+
+
+def test_script_code():
+    assert unicodedata.script_code("Latin") == "Latn"
+    assert unicodedata.script_code("Common") == "Zyyy"
+    assert unicodedata.script_code("Unknown") == "Zzzz"
+    # case, whitespace, underscores and hyphens are ignored
+    assert unicodedata.script_code("Egyptian Hieroglyphs") == "Egyp"
+    assert unicodedata.script_code("Egyptian_Hieroglyphs") == "Egyp"
+    assert unicodedata.script_code("egyptianhieroglyphs") == "Egyp"
+    assert unicodedata.script_code("Egyptian-Hieroglyphs") == "Egyp"
+
+    with pytest.raises(KeyError):
+        unicodedata.script_code("Does not exist")
+    assert unicodedata.script_code("Does not exist", default="Zzzz") == "Zzzz"
+
+
+def test_block():
+    assert unicodedata.block("\x00") == "Basic Latin"
+    assert unicodedata.block("\x7F") == "Basic Latin"
+    assert unicodedata.block("\x80") == "Latin-1 Supplement"
+    assert unicodedata.block("\u1c90") == "No_Block"
+
+
+def test_ot_tags_from_script():
+    # simple
+    assert unicodedata.ot_tags_from_script("Latn") == ["latn"]
+    # script mapped to multiple new and old script tags
+    assert unicodedata.ot_tags_from_script("Deva") == ["dev2", "deva"]
+    # exceptions
+    assert unicodedata.ot_tags_from_script("Hira") == ["kana"]
+    # special script codes map to DFLT
+    assert unicodedata.ot_tags_from_script("Zinh") == ["DFLT"]
+    assert unicodedata.ot_tags_from_script("Zyyy") == ["DFLT"]
+    assert unicodedata.ot_tags_from_script("Zzzz") == ["DFLT"]
+    # this is invalid or unknown
+    assert unicodedata.ot_tags_from_script("Aaaa") == ["DFLT"]
+
+
+def test_ot_tag_to_script():
+    assert unicodedata.ot_tag_to_script("latn") == "Latn"
+    assert unicodedata.ot_tag_to_script("kana") == "Kana"
+    assert unicodedata.ot_tag_to_script("DFLT") == None
+    assert unicodedata.ot_tag_to_script("aaaa") == None
+    assert unicodedata.ot_tag_to_script("beng") == "Beng"
+    assert unicodedata.ot_tag_to_script("bng2") == "Beng"
+    assert unicodedata.ot_tag_to_script("dev2") == "Deva"
+    assert unicodedata.ot_tag_to_script("gjr2") == "Gujr"
+    assert unicodedata.ot_tag_to_script("yi  ") == "Yiii"
+    assert unicodedata.ot_tag_to_script("nko ") == "Nkoo"
+    assert unicodedata.ot_tag_to_script("vai ") == "Vaii"
+    assert unicodedata.ot_tag_to_script("lao ") == "Laoo"
+    assert unicodedata.ot_tag_to_script("yi") == "Yiii"
+
+    for invalid_value in ("", " ", "z zz", "zzzzz"):
+        with pytest.raises(ValueError, match="invalid OpenType tag"):
+            unicodedata.ot_tag_to_script(invalid_value)
+
+
+def test_script_horizontal_direction():
+    assert unicodedata.script_horizontal_direction("Latn") == "LTR"
+    assert unicodedata.script_horizontal_direction("Arab") == "RTL"
+    assert unicodedata.script_horizontal_direction("Thaa") == "RTL"
+
+    with pytest.raises(KeyError):
+        unicodedata.script_horizontal_direction("Azzz")
+    assert unicodedata.script_horizontal_direction("Azzz",
+                                                   default="LTR") == "LTR"
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(pytest.main(sys.argv))
diff --git a/Tests/varLib/__init__.py b/Tests/varLib/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/varLib/__init__.py
diff --git a/Tests/varLib/builder_test.py b/Tests/varLib/builder_test.py
new file mode 100644
index 0000000..09fd290
--- /dev/null
+++ b/Tests/varLib/builder_test.py
@@ -0,0 +1,67 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.varLib.builder import buildVarData
+import pytest
+
+
+@pytest.mark.parametrize("region_indices, items, expected_num_shorts", [
+    ([], [], 0),
+    ([0], [[1]], 0),
+    ([0], [[128]], 1),
+    ([0, 1, 2], [[128, 1, 2], [3, -129, 5], [6, 7, 8]], 2),
+    ([0, 1, 2], [[0, 128, 2], [3, 4, 5], [6, 7, -129]], 3),
+], ids=[
+    "0_regions_0_deltas",
+    "1_region_1_uint8",
+    "1_region_1_short",
+    "3_regions_2_shorts_ordered",
+    "3_regions_2_shorts_unordered",
+])
+def test_buildVarData_no_optimize(region_indices, items, expected_num_shorts):
+    data = buildVarData(region_indices, items, optimize=False)
+
+    assert data.ItemCount == len(items)
+    assert data.NumShorts == expected_num_shorts
+    assert data.VarRegionCount == len(region_indices)
+    assert data.VarRegionIndex == region_indices
+    assert data.Item == items
+
+
+@pytest.mark.parametrize([
+    "region_indices", "items", "expected_num_shorts",
+    "expected_regions", "expected_items"
+], [
+    ([0, 1, 2], [[0, 1, 2], [3, 4, 5], [6, 7, 8]], 0,
+     [0, 1, 2], [[0, 1, 2], [3, 4, 5], [6, 7, 8]]),
+    ([0, 1, 2], [[0, 128, 2], [3, 4, 5], [6, 7, 8]], 1,
+     [1, 0, 2], [[128, 0, 2], [4, 3, 5], [7, 6, 8]]),
+    ([0, 1, 2], [[0, 1, 128], [3, 4, 5], [6, -129, 8]], 2,
+     [1, 2, 0], [[1, 128, 0], [4, 5, 3], [-129, 8, 6]]),
+    ([0, 1, 2], [[128, 1, -129], [3, 4, 5], [6, 7, 8]], 2,
+     [0, 2, 1], [[128, -129, 1], [3, 5, 4], [6, 8, 7]]),
+    ([0, 1, 2], [[0, 1, 128], [3, -129, 5], [256, 7, 8]], 3,
+     [0, 1, 2], [[0, 1, 128], [3, -129, 5], [256, 7, 8]]),
+    ([0, 1, 2], [[0, 128, 2], [0, 4, 5], [0, 7, 8]], 1,
+     [1, 2], [[128, 2], [4, 5], [7, 8]]),
+], ids=[
+    "0/3_shorts_no_reorder",
+    "1/3_shorts_reorder",
+    "2/3_shorts_reorder",
+    "2/3_shorts_same_row_reorder",
+    "3/3_shorts_no_reorder",
+    "1/3_shorts_1/3_zeroes",
+])
+def test_buildVarData_optimize(
+        region_indices, items, expected_num_shorts, expected_regions,
+        expected_items):
+    data = buildVarData(region_indices, items, optimize=True)
+
+    assert data.ItemCount == len(items)
+    assert data.NumShorts == expected_num_shorts
+    assert data.VarRegionCount == len(expected_regions)
+    assert data.VarRegionIndex == expected_regions
+    assert data.Item == expected_items
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(pytest.main(sys.argv))
diff --git a/Tests/varLib/data/Build.designspace b/Tests/varLib/data/Build.designspace
new file mode 100644
index 0000000..e0bf58d
--- /dev/null
+++ b/Tests/varLib/data/Build.designspace
@@ -0,0 +1,300 @@
+<?xml version='1.0' encoding='utf-8'?>
+<designspace format="3">
+    <axes>
+        <axis default="368.0" maximum="1000.0" minimum="0.0" name="weight" tag="wght" />
+        <axis default="0.0" maximum="100.0" minimum="0.0" name="contrast" tag="cntr">
+            <labelname xml:lang="en">Contrast</labelname>
+        </axis>
+    </axes>
+    <sources>
+        <source familyname="Test Family" filename="master_ufo/TestFamily-Master0.ufo" name="master_0" stylename="Master0">
+            <location>
+                <dimension name="weight" xvalue="0" />
+                <dimension name="contrast" xvalue="0" />
+            </location>
+        </source>
+        <source familyname="Test Family" filename="master_ufo/TestFamily-Master1.ufo" name="master_1" stylename="Master1">
+            <lib copy="1" />
+            <groups copy="1" />
+            <info copy="1" />
+            <location>
+                <dimension name="weight" xvalue="368" />
+                <dimension name="contrast" xvalue="0" />
+            </location>
+        </source>
+        <source familyname="Test Family" filename="master_ufo/TestFamily-Master2.ufo" name="master_2" stylename="Master2">
+            <location>
+                <dimension name="weight" xvalue="1000" />
+                <dimension name="contrast" xvalue="0" />
+            </location>
+        </source>
+        <source familyname="Test Family" filename="master_ufo/TestFamily-Master3.ufo" name="master_3" stylename="Master3">
+            <location>
+                <dimension name="weight" xvalue="1000" />
+                <dimension name="contrast" xvalue="100" />
+            </location>
+        </source>
+        <source familyname="Test Family" filename="master_ufo/TestFamily-Master0.ufo" name="master_0" stylename="Master0">
+            <location>
+                <dimension name="weight" xvalue="0" />
+                <dimension name="contrast" xvalue="100" />
+            </location>
+        </source>
+        <source familyname="Test Family" filename="master_ufo/TestFamily-Master4.ufo" name="master_4" stylename="Master4">
+            <location>
+                <dimension name="weight" xvalue="368" />
+                <dimension name="contrast" xvalue="100" />
+            </location>
+        </source>
+    </sources>
+    <instances>
+        <instance familyname="Test Family" filename="instances/TestFamily-ExtraLight.ufo" name="instance_ExtraLight" postscriptfontname="TestFamily-ExtraLight" stylename="ExtraLight">
+            <location>
+                <dimension name="weight" xvalue="0" />
+                <dimension name="contrast" xvalue="0" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test Family" filename="instances/TestFamily-Light.ufo" name="instance_Light" postscriptfontname="TestFamily-Light" stylename="Light">
+            <location>
+                <dimension name="weight" xvalue="150" />
+                <dimension name="contrast" xvalue="0" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test Family" filename="instances/TestFamily-Regular.ufo" name="instance_Regular" postscriptfontname="TestFamily-Regular" stylename="Regular">
+            <location>
+                <dimension name="weight" xvalue="394" />
+                <dimension name="contrast" xvalue="0" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test Family" filename="instances/TestFamily-Semibold.ufo" name="instance_Semibold" postscriptfontname="TestFamily-Semibold" stylename="Semibold">
+            <location>
+                <dimension name="weight" xvalue="600" />
+                <dimension name="contrast" xvalue="0" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test Family" filename="instances/TestFamily-Bold.ufo" name="instance_Bold" postscriptfontname="TestFamily-Bold" stylename="Bold">
+            <location>
+                <dimension name="weight" xvalue="824" />
+                <dimension name="contrast" xvalue="0" />
+            </location>
+            <glyphs>
+                <glyph name="dollar" unicode="0x24">
+                    <location>
+                        <dimension name="weight" xvalue="824" />
+                        <dimension name="contrast" xvalue="0" />
+                    </location>
+                    <masters>
+                        <master glyphname="dollar.nostroke" source="master_0">
+                            <location>
+                                <dimension name="weight" xvalue="0" />
+                                <dimension name="contrast" xvalue="0" />
+                            </location>
+                        </master>
+                        <master glyphname="dollar.nostroke" source="master_1">
+                            <location>
+                                <dimension name="weight" xvalue="368" />
+                                <dimension name="contrast" xvalue="0" />
+                            </location>
+                        </master>
+                        <master glyphname="dollar.nostroke" source="master_2">
+                            <location>
+                                <dimension name="weight" xvalue="1000" />
+                                <dimension name="contrast" xvalue="0" />
+                            </location>
+                        </master>
+                        <master glyphname="dollar.nostroke" source="master_3">
+                            <location>
+                                <dimension name="weight" xvalue="1000" />
+                                <dimension name="contrast" xvalue="0" />
+                            </location>
+                        </master>
+                        <master glyphname="dollar.nostroke" source="master_0">
+                            <location>
+                                <dimension name="weight" xvalue="0" />
+                                <dimension name="contrast" xvalue="0" />
+                            </location>
+                        </master>
+                        <master glyphname="dollar.nostroke" source="master_4">
+                            <location>
+                                <dimension name="weight" xvalue="368" />
+                                <dimension name="contrast" xvalue="0" />
+                            </location>
+                        </master>
+                    </masters>
+                </glyph>
+            </glyphs>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test Family" filename="instances/TestFamily-Black.ufo" name="instance_Black" postscriptfontname="TestFamily-Black" stylename="Black">
+            <location>
+                <dimension name="weight" xvalue="1000" />
+                <dimension name="contrast" xvalue="0" />
+            </location>
+            <glyphs>
+                <glyph name="dollar" unicode="0x24">
+                    <location>
+                        <dimension name="weight" xvalue="1000" />
+                        <dimension name="contrast" xvalue="0" />
+                    </location>
+                    <masters>
+                        <master glyphname="dollar.nostroke" source="master_0">
+                            <location>
+                                <dimension name="weight" xvalue="0" />
+                                <dimension name="contrast" xvalue="0" />
+                            </location>
+                        </master>
+                        <master glyphname="dollar.nostroke" source="master_1">
+                            <location>
+                                <dimension name="weight" xvalue="368" />
+                                <dimension name="contrast" xvalue="0" />
+                            </location>
+                        </master>
+                        <master glyphname="dollar.nostroke" source="master_2">
+                            <location>
+                                <dimension name="weight" xvalue="1000" />
+                                <dimension name="contrast" xvalue="0" />
+                            </location>
+                        </master>
+                        <master glyphname="dollar.nostroke" source="master_3">
+                            <location>
+                                <dimension name="weight" xvalue="1000" />
+                                <dimension name="contrast" xvalue="0" />
+                            </location>
+                        </master>
+                        <master glyphname="dollar.nostroke" source="master_0">
+                            <location>
+                                <dimension name="weight" xvalue="0" />
+                                <dimension name="contrast" xvalue="0" />
+                            </location>
+                        </master>
+                        <master glyphname="dollar.nostroke" source="master_4">
+                            <location>
+                                <dimension name="weight" xvalue="368" />
+                                <dimension name="contrast" xvalue="0" />
+                            </location>
+                        </master>
+                    </masters>
+                </glyph>
+            </glyphs>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test Family" filename="instances/TestFamily-BlackMediumContrast.ufo" name="instance_BlackMediumContrast" postscriptfontname="TestFamily-BlackMediumContrast" stylename="Black Medium Contrast">
+            <location>
+                <dimension name="weight" xvalue="1000" />
+                <dimension name="contrast" xvalue="50" />
+            </location>
+            <glyphs>
+                <glyph name="dollar" unicode="0x24">
+                    <location>
+                        <dimension name="weight" xvalue="1000" />
+                        <dimension name="contrast" xvalue="0" />
+                    </location>
+                    <masters>
+                        <master glyphname="dollar.nostroke" source="master_0">
+                            <location>
+                                <dimension name="weight" xvalue="0" />
+                                <dimension name="contrast" xvalue="0" />
+                            </location>
+                        </master>
+                        <master glyphname="dollar.nostroke" source="master_1">
+                            <location>
+                                <dimension name="weight" xvalue="368" />
+                                <dimension name="contrast" xvalue="0" />
+                            </location>
+                        </master>
+                        <master glyphname="dollar.nostroke" source="master_2">
+                            <location>
+                                <dimension name="weight" xvalue="1000" />
+                                <dimension name="contrast" xvalue="0" />
+                            </location>
+                        </master>
+                        <master glyphname="dollar.nostroke" source="master_3">
+                            <location>
+                                <dimension name="weight" xvalue="1000" />
+                                <dimension name="contrast" xvalue="0" />
+                            </location>
+                        </master>
+                        <master glyphname="dollar.nostroke" source="master_0">
+                            <location>
+                                <dimension name="weight" xvalue="0" />
+                                <dimension name="contrast" xvalue="0" />
+                            </location>
+                        </master>
+                        <master glyphname="dollar.nostroke" source="master_4">
+                            <location>
+                                <dimension name="weight" xvalue="368" />
+                                <dimension name="contrast" xvalue="0" />
+                            </location>
+                        </master>
+                    </masters>
+                </glyph>
+            </glyphs>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test Family" filename="instances/TestFamily-BlackHighContrast.ufo" name="instance_BlackHighContrast" postscriptfontname="TestFamily-BlackHighContrast" stylename="Black High Contrast">
+            <location>
+                <dimension name="weight" xvalue="1000" />
+                <dimension name="contrast" xvalue="100" />
+            </location>
+            <glyphs>
+                <glyph name="dollar" unicode="0x24">
+                    <location>
+                        <dimension name="weight" xvalue="1000" />
+                        <dimension name="contrast" xvalue="0" />
+                    </location>
+                    <masters>
+                        <master glyphname="dollar.nostroke" source="master_0">
+                            <location>
+                                <dimension name="weight" xvalue="0" />
+                                <dimension name="contrast" xvalue="0" />
+                            </location>
+                        </master>
+                        <master glyphname="dollar.nostroke" source="master_1">
+                            <location>
+                                <dimension name="weight" xvalue="368" />
+                                <dimension name="contrast" xvalue="0" />
+                            </location>
+                        </master>
+                        <master glyphname="dollar.nostroke" source="master_2">
+                            <location>
+                                <dimension name="weight" xvalue="1000" />
+                                <dimension name="contrast" xvalue="0" />
+                            </location>
+                        </master>
+                        <master glyphname="dollar.nostroke" source="master_3">
+                            <location>
+                                <dimension name="weight" xvalue="1000" />
+                                <dimension name="contrast" xvalue="0" />
+                            </location>
+                        </master>
+                        <master glyphname="dollar.nostroke" source="master_0">
+                            <location>
+                                <dimension name="weight" xvalue="0" />
+                                <dimension name="contrast" xvalue="0" />
+                            </location>
+                        </master>
+                        <master glyphname="dollar.nostroke" source="master_4">
+                            <location>
+                                <dimension name="weight" xvalue="368" />
+                                <dimension name="contrast" xvalue="0" />
+                            </location>
+                        </master>
+                    </masters>
+                </glyph>
+            </glyphs>
+            <kerning />
+            <info />
+        </instance>
+    </instances>
+</designspace>
diff --git a/Tests/varLib/data/BuildAvarEmptyAxis.designspace b/Tests/varLib/data/BuildAvarEmptyAxis.designspace
new file mode 100644
index 0000000..6f0d84e
--- /dev/null
+++ b/Tests/varLib/data/BuildAvarEmptyAxis.designspace
@@ -0,0 +1,59 @@
+<?xml version='1.0' encoding='utf-8'?>
+<designspace format="3">
+    <axes>
+        <axis default="400.0" maximum="900.0" minimum="100.0" name="weight" tag="wght">
+            <map input="100.0" output="26" />
+            <map input="400.0" output="90" />
+            <map input="700.0" output="151" />
+            <map input="900.0" output="190" />
+            <labelname xml:lang="en">Weight</labelname>
+        </axis>
+        <axis default="100" maximum="100" minimum="70" name="width" tag="wdth">
+            <labelname xml:lang="en">Width</labelname>
+        </axis>
+    </axes>
+    <sources>
+        <source familyname="Test Family 3" filename="master_ufo/TestFamily3-Light.ufo" name="Test Family 3 Light" stylename="Light">
+            <location>
+                <dimension name="weight" xvalue="26.000000" />
+                <dimension name="width" xvalue="100.000000" />
+            </location>
+        </source>
+        <source familyname="Test Family 3" filename="master_ufo/TestFamily3-Regular.ufo" name="Test Family 3 Regular" stylename="Regular">
+            <lib copy="1" />
+            <groups copy="1" />
+            <features copy="1" />
+            <info copy="1" />
+            <location>
+                <dimension name="weight" xvalue="90.000000" />
+                <dimension name="width" xvalue="100.000000" />
+            </location>
+        </source>
+        <source familyname="Test Family 3" filename="master_ufo/TestFamily3-Bold.ufo" name="Test Family 3 Bold" stylename="Bold">
+            <location>
+                <dimension name="weight" xvalue="190.000000" />
+                <dimension name="width" xvalue="100.000000" />
+            </location>
+        </source>
+        <source familyname="Test Family 3" filename="master_ufo/TestFamily3-CondensedLight.ufo" name="Test Family 3 Condensed Light" stylename="Condensed Light">
+            <location>
+                <dimension name="weight" xvalue="26.000000" />
+                <dimension name="width" xvalue="70.000000" />
+            </location>
+        </source>
+        <source familyname="Test Family 3" filename="master_ufo/TestFamily3-Condensed.ufo" name="Test Family 3 Condensed" stylename="Condensed">
+            <location>
+                <dimension name="weight" xvalue="90.000000" />
+                <dimension name="width" xvalue="70.000000" />
+            </location>
+        </source>
+        <source familyname="Test Family 3" filename="master_ufo/TestFamily3-CondensedBold.ufo" name="Test Family 3 Condensed Bold" stylename="Condensed Bold">
+            <location>
+                <dimension name="weight" xvalue="190.000000" />
+                <dimension name="width" xvalue="70.000000" />
+            </location>
+        </source>
+    </sources>
+    <instances>
+    </instances>
+</designspace>
diff --git a/Tests/varLib/data/BuildAvarIdentityMaps.designspace b/Tests/varLib/data/BuildAvarIdentityMaps.designspace
new file mode 100644
index 0000000..a9f30bf
--- /dev/null
+++ b/Tests/varLib/data/BuildAvarIdentityMaps.designspace
@@ -0,0 +1,80 @@
+<?xml version='1.0' encoding='utf-8'?>
+<designspace format="3">
+    <axes>
+        <axis default="400.0" maximum="900.0" minimum="100.0" name="weight" tag="wght">
+            <map input="100.0" output="26" />
+            <map input="200.0" output="39" />
+            <map input="300.0" output="58" />
+            <map input="400.0" output="90" />
+            <map input="500.0" output="108" />
+            <map input="600.0" output="128" />
+            <map input="700.0" output="151" />
+            <map input="800.0" output="169" />
+            <map input="900.0" output="190" />
+            <labelname xml:lang="en">Weight</labelname>
+        </axis>
+        <axis default="100" maximum="100" minimum="70" name="width" tag="wdth">
+            <map input="70" output="70" />
+            <map input="79" output="79" />
+            <map input="89" output="89" />
+            <map input="100" output="100" />
+            <labelname xml:lang="en">Width</labelname>
+        </axis>
+    </axes>
+    <sources>
+        <source familyname="Test Family 3" filename="master_ufo/TestFamily3-Light.ufo" name="Test Family 3 Light" stylename="Light">
+            <location>
+                <dimension name="weight" xvalue="26.000000" />
+                <dimension name="width" xvalue="100.000000" />
+            </location>
+        </source>
+        <source familyname="Test Family 3" filename="master_ufo/TestFamily3-Regular.ufo" name="Test Family 3 Regular" stylename="Regular">
+            <lib copy="1" />
+            <groups copy="1" />
+            <features copy="1" />
+            <info copy="1" />
+            <location>
+                <dimension name="weight" xvalue="90.000000" />
+                <dimension name="width" xvalue="100.000000" />
+            </location>
+        </source>
+        <source familyname="Test Family 3" filename="master_ufo/TestFamily3-SemiBold.ufo" name="Test Family 3 SemiBold" stylename="SemiBold">
+            <location>
+                <dimension name="weight" xvalue="151.000000" />
+                <dimension name="width" xvalue="100.000000" />
+            </location>
+        </source>
+        <source familyname="Test Family 3" filename="master_ufo/TestFamily3-Bold.ufo" name="Test Family 3 Bold" stylename="Bold">
+            <location>
+                <dimension name="weight" xvalue="190.000000" />
+                <dimension name="width" xvalue="100.000000" />
+            </location>
+        </source>
+        <source familyname="Test Family 3" filename="master_ufo/TestFamily3-CondensedLight.ufo" name="Test Family 3 Condensed Light" stylename="Condensed Light">
+            <location>
+                <dimension name="weight" xvalue="26.000000" />
+                <dimension name="width" xvalue="70.000000" />
+            </location>
+        </source>
+        <source familyname="Test Family 3" filename="master_ufo/TestFamily3-Condensed.ufo" name="Test Family 3 Condensed" stylename="Condensed">
+            <location>
+                <dimension name="weight" xvalue="90.000000" />
+                <dimension name="width" xvalue="70.000000" />
+            </location>
+        </source>
+        <source familyname="Test Family 3" filename="master_ufo/TestFamily3-CondensedSemiBold.ufo" name="Test Family 3 Condensed SemiBold" stylename="Condensed SemiBold">
+            <location>
+                <dimension name="weight" xvalue="151.000000" />
+                <dimension name="width" xvalue="70.000000" />
+            </location>
+        </source>
+        <source familyname="Test Family 3" filename="master_ufo/TestFamily3-CondensedBold.ufo" name="Test Family 3 Condensed Bold" stylename="Condensed Bold">
+            <location>
+                <dimension name="weight" xvalue="190.000000" />
+                <dimension name="width" xvalue="70.000000" />
+            </location>
+        </source>
+    </sources>
+    <instances>
+    </instances>
+</designspace>
diff --git a/Tests/varLib/data/BuildAvarSingleAxis.designspace b/Tests/varLib/data/BuildAvarSingleAxis.designspace
new file mode 100644
index 0000000..8895007
--- /dev/null
+++ b/Tests/varLib/data/BuildAvarSingleAxis.designspace
@@ -0,0 +1,45 @@
+<?xml version='1.0' encoding='utf-8'?>
+<designspace format="3">
+    <axes>
+        <axis default="400.0" maximum="900.0" minimum="100.0" name="weight" tag="wght">
+            <map input="100.0" output="26" />
+            <map input="200.0" output="39" />
+            <map input="300.0" output="58" />
+            <map input="400.0" output="90" />
+            <map input="500.0" output="108" />
+            <map input="600.0" output="128" />
+            <map input="700.0" output="151" />
+            <map input="800.0" output="169" />
+            <map input="900.0" output="190" />
+            <labelname xml:lang="en">Weight</labelname>
+        </axis>
+    </axes>
+    <sources>
+        <source familyname="Test Family 3" filename="master_ufo/TestFamily3-Light.ufo" name="Test Family 3 Light" stylename="Light">
+            <location>
+                <dimension name="weight" xvalue="26.000000" />
+            </location>
+        </source>
+        <source familyname="Test Family 3" filename="master_ufo/TestFamily3-Regular.ufo" name="Test Family 3 Regular" stylename="Regular">
+            <lib copy="1" />
+            <groups copy="1" />
+            <features copy="1" />
+            <info copy="1" />
+            <location>
+                <dimension name="weight" xvalue="90.000000" />
+            </location>
+        </source>
+        <source familyname="Test Family 3" filename="master_ufo/TestFamily3-SemiBold.ufo" name="Test Family 3 SemiBold" stylename="SemiBold">
+            <location>
+                <dimension name="weight" xvalue="151.000000" />
+            </location>
+        </source>
+        <source familyname="Test Family 3" filename="master_ufo/TestFamily3-Bold.ufo" name="Test Family 3 Bold" stylename="Bold">
+            <location>
+                <dimension name="weight" xvalue="190.000000" />
+            </location>
+        </source>
+    </sources>
+    <instances>
+    </instances>
+</designspace>
diff --git a/Tests/varLib/data/Designspace.designspace b/Tests/varLib/data/Designspace.designspace
new file mode 100644
index 0000000..df1036e
--- /dev/null
+++ b/Tests/varLib/data/Designspace.designspace
@@ -0,0 +1,39 @@
+<?xml version="1.0"?>
+<designspace format="3">
+    <axes>
+        <axis default="0.0" maximum="1000.0" minimum="0.0" name="weight" tag="wght">
+            <map input="0.0" output="10.0" />
+            <map input="401.0" output="66.0" />
+            <map input="1000.0" output="990.0" />
+        </axis>
+        <axis default="250.0" maximum="1000.0" minimum="0.0" name="width" tag="wdth" />
+        <axis default="0.0" maximum="100.0" minimum="0.0" name="contrast" tag="cntr">
+            <labelname xml:lang="en">Contrast</labelname>
+            <labelname xml:lang="de">Kontrast</labelname>
+        </axis>
+    </axes>
+    <sources>
+        <source filename="DesignspaceTest-Light.ufo" name="master_1">
+            <lib copy="1"/>
+            <groups copy="1"/>
+            <info copy="1"/>
+            <location>
+                <dimension name="weight" xvalue="0.0"/>
+            </location>
+        </source>
+        <source filename="DesignspaceTest-Bold.ufo" name="master_2">
+            <location>
+                <dimension name="weight" xvalue="1.0"/>
+            </location>
+        </source>
+    </sources>
+    <instances>
+        <instance familyname="DesignspaceTest" filename="instance/DesignspaceTest-Medium.ufo" stylename="Medium">
+            <location>
+                <dimension name="weight" xvalue="0.5"/>
+            </location>
+            <info/>
+            <kerning/>
+        </instance>
+    </instances>
+</designspace>
diff --git a/Tests/varLib/data/Designspace2.designspace b/Tests/varLib/data/Designspace2.designspace
new file mode 100644
index 0000000..ac7d403
--- /dev/null
+++ b/Tests/varLib/data/Designspace2.designspace
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<designspace format="3">
+    <!-- no <axes> element -->
+    <sources/><!-- empty <sources> element -->
+    <instances>
+        <instance/><!-- bare-bones <instance> element -->
+    </instances>
+</designspace>
diff --git a/Tests/varLib/data/InterpolateLayout.designspace b/Tests/varLib/data/InterpolateLayout.designspace
new file mode 100644
index 0000000..18f118d
--- /dev/null
+++ b/Tests/varLib/data/InterpolateLayout.designspace
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<designspace format="3">
+    <axes>
+        <axis default="0.0" maximum="1000.0" minimum="0.0" name="weight" tag="wght" />
+    </axes>
+    <sources>
+        <source familyname="Test Family 2" filename="master_ufo/TestFamily2-Master0.ufo" name="master_0" stylename="Master0">
+            <lib copy="1" />
+            <groups copy="1" />
+            <info copy="1" />
+            <location>
+                <dimension name="weight" xvalue="0" />
+            </location>
+        </source>
+        <source familyname="Test Family 2" filename="master_ufo/TestFamily2-Master1.ufo" name="master_1" stylename="Master1">
+            <location>
+                <dimension name="weight" xvalue="1000" />
+            </location>
+        </source>
+    </sources>
+    <instances>
+        <instance familyname="Test Family 2" filename="instances/TestFamily2-ExtraLight.ufo" name="instance_ExtraLight" postscriptfontname="TestFamily2-ExtraLight" stylename="ExtraLight">
+            <location>
+                <dimension name="weight" xvalue="0" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test Family 2" filename="instances/TestFamily2-Light.ufo" name="instance_Light" postscriptfontname="TestFamily2-Light" stylename="Light">
+            <location>
+                <dimension name="weight" xvalue="100" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test Family 2" filename="instances/TestFamily2-Regular.ufo" name="instance_Regular" postscriptfontname="TestFamily2-Regular" stylename="Regular">
+            <location>
+                <dimension name="weight" xvalue="368" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test Family 2" filename="instances/TestFamily2-Semibold.ufo" name="instance_Semibold" postscriptfontname="TestFamily2-Semibold" stylename="Semibold">
+            <location>
+                <dimension name="weight" xvalue="600" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test Family 2" filename="instances/TestFamily2-Bold.ufo" name="instance_Bold" postscriptfontname="TestFamily2-Bold" stylename="Bold">
+            <location>
+                <dimension name="weight" xvalue="824" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test Family 2" filename="instances/TestFamily2-Black.ufo" name="instance_Black" postscriptfontname="TestFamily2-Black" stylename="Black">
+            <location>
+                <dimension name="weight" xvalue="1000" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+    </instances>
+</designspace>
diff --git a/Tests/varLib/data/InterpolateLayout2.designspace b/Tests/varLib/data/InterpolateLayout2.designspace
new file mode 100644
index 0000000..b686c08
--- /dev/null
+++ b/Tests/varLib/data/InterpolateLayout2.designspace
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<designspace format="3">
+    <axes>
+        <axis default="1000.0" maximum="1000.0" minimum="0.0" name="weight" tag="wght" />
+    </axes>
+    <sources>
+        <source familyname="Test Family 2" filename="master_ufo/TestFamily2-Master0.ufo" name="master_0" stylename="Master0">
+            <location>
+                <dimension name="weight" xvalue="0" />
+            </location>
+        </source>
+        <source familyname="Test Family 2" filename="master_ufo/TestFamily2-Master1.ufo" name="master_1" stylename="Master1">
+            <lib copy="1" />
+            <groups copy="1" />
+            <info copy="1" />
+            <location>
+                <dimension name="weight" xvalue="1000" />
+            </location>
+        </source>
+    </sources>
+    <instances>
+        <instance familyname="Test Family 2" filename="instances/TestFamily2-ExtraLight.ufo" name="instance_ExtraLight" postscriptfontname="TestFamily2-ExtraLight" stylename="ExtraLight">
+            <location>
+                <dimension name="weight" xvalue="0" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test Family 2" filename="instances/TestFamily2-Light.ufo" name="instance_Light" postscriptfontname="TestFamily2-Light" stylename="Light">
+            <location>
+                <dimension name="weight" xvalue="100" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test Family 2" filename="instances/TestFamily2-Regular.ufo" name="instance_Regular" postscriptfontname="TestFamily2-Regular" stylename="Regular">
+            <location>
+                <dimension name="weight" xvalue="368" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test Family 2" filename="instances/TestFamily2-Semibold.ufo" name="instance_Semibold" postscriptfontname="TestFamily2-Semibold" stylename="Semibold">
+            <location>
+                <dimension name="weight" xvalue="600" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test Family 2" filename="instances/TestFamily2-Bold.ufo" name="instance_Bold" postscriptfontname="TestFamily2-Bold" stylename="Bold">
+            <location>
+                <dimension name="weight" xvalue="824" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test Family 2" filename="instances/TestFamily2-Black.ufo" name="instance_Black" postscriptfontname="TestFamily2-Black" stylename="Black">
+            <location>
+                <dimension name="weight" xvalue="1000" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+    </instances>
+</designspace>
diff --git a/Tests/varLib/data/InterpolateLayout3.designspace b/Tests/varLib/data/InterpolateLayout3.designspace
new file mode 100644
index 0000000..3764529
--- /dev/null
+++ b/Tests/varLib/data/InterpolateLayout3.designspace
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<designspace format="3">
+    <sources>
+        <source familyname="Test Family 2" filename="master_ufo/TestFamily2-Master0.ufo" name="master_0" stylename="Master0">
+            <lib copy="1" />
+            <groups copy="1" />
+            <info copy="1" />
+            <location>
+                <dimension name="weight" xvalue="0" />
+            </location>
+        </source>
+        <source familyname="Test Family 2" filename="master_ufo/TestFamily2-Master1.ufo" name="master_1" stylename="Master1">
+            <location>
+                <dimension name="weight" xvalue="1000" />
+            </location>
+        </source>
+    </sources>
+    <instances>
+        <instance familyname="Test Family 2" filename="instances/TestFamily2-ExtraLight.ufo" name="instance_ExtraLight" postscriptfontname="TestFamily2-ExtraLight" stylename="ExtraLight">
+            <location>
+                <dimension name="weight" xvalue="0" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test Family 2" filename="instances/TestFamily2-Light.ufo" name="instance_Light" postscriptfontname="TestFamily2-Light" stylename="Light">
+            <location>
+                <dimension name="weight" xvalue="100" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test Family 2" filename="instances/TestFamily2-Regular.ufo" name="instance_Regular" postscriptfontname="TestFamily2-Regular" stylename="Regular">
+            <location>
+                <dimension name="weight" xvalue="368" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test Family 2" filename="instances/TestFamily2-Semibold.ufo" name="instance_Semibold" postscriptfontname="TestFamily2-Semibold" stylename="Semibold">
+            <location>
+                <dimension name="weight" xvalue="600" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test Family 2" filename="instances/TestFamily2-Bold.ufo" name="instance_Bold" postscriptfontname="TestFamily2-Bold" stylename="Bold">
+            <location>
+                <dimension name="weight" xvalue="824" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test Family 2" filename="instances/TestFamily2-Black.ufo" name="instance_Black" postscriptfontname="TestFamily2-Black" stylename="Black">
+            <location>
+                <dimension name="weight" xvalue="1000" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+    </instances>
+</designspace>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_otf/TestFamily2-Master0.ttx b/Tests/varLib/data/master_ttx_interpolatable_otf/TestFamily2-Master0.ttx
new file mode 100644
index 0000000..a6a8e00
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_interpolatable_otf/TestFamily2-Master0.ttx
@@ -0,0 +1,855 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.9">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="space"/>
+    <GlyphID id="2" name="A"/>
+    <GlyphID id="3" name="a"/>
+    <GlyphID id="4" name="d"/>
+    <GlyphID id="5" name="f"/>
+    <GlyphID id="6" name="n"/>
+    <GlyphID id="7" name="t"/>
+    <GlyphID id="8" name="f_t"/>
+    <GlyphID id="9" name="a.alt"/>
+    <GlyphID id="10" name="A.sc"/>
+    <GlyphID id="11" name="atilde"/>
+    <GlyphID id="12" name="ampersand"/>
+    <GlyphID id="13" name="uni25CC"/>
+    <GlyphID id="14" name="uni0303"/>
+    <GlyphID id="15" name="uni0308"/>
+    <GlyphID id="16" name="uni0330"/>
+    <GlyphID id="17" name="uni0324"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="2.02"/>
+    <checkSumAdjustment value="0xa2f07d9e"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Mar 16 05:26:39 2017"/>
+    <modified value="Thu Mar 16 05:26:39 2017"/>
+    <xMin value="-160"/>
+    <yMin value="-220"/>
+    <xMax value="550"/>
+    <yMax value="734"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="984"/>
+    <descent value="-273"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="624"/>
+    <minLeftSideBearing value="-160"/>
+    <minRightSideBearing value="-160"/>
+    <xMaxExtent value="550"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="15"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="18"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="471"/>
+    <usWeightClass value="200"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="287"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="3"/>
+      <bProportion value="3"/>
+      <bContrast value="3"/>
+      <bStrokeVariation value="4"/>
+      <bArmStyle value="3"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000011"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBO"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="9676"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="984"/>
+    <usWinDescent value="273"/>
+    <ulCodePageRange1 value="00100000 00000000 00000000 00000011"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="478"/>
+    <sCapHeight value="660"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="2"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="9" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Paul D. Hunt
+    </namerecord>
+    <namerecord nameID="256" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Alternate a
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test Family 2
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      Version 2.020;ADBO;Test Family 2 Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test Family 2
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 2.020
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestFamily2-Master0
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Paul D. Hunt
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Master 0
+    </namerecord>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Alternate a
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x26" name="ampersand"/><!-- AMPERSAND -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x64" name="d"/><!-- LATIN SMALL LETTER D -->
+      <map code="0x66" name="f"/><!-- LATIN SMALL LETTER F -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x74" name="t"/><!-- LATIN SMALL LETTER T -->
+      <map code="0xe3" name="atilde"/><!-- LATIN SMALL LETTER A WITH TILDE -->
+      <map code="0x303" name="uni0303"/><!-- COMBINING TILDE -->
+      <map code="0x308" name="uni0308"/><!-- COMBINING DIAERESIS -->
+      <map code="0x324" name="uni0324"/><!-- COMBINING DIAERESIS BELOW -->
+      <map code="0x330" name="uni0330"/><!-- COMBINING TILDE BELOW -->
+      <map code="0x25cc" name="uni25CC"/><!-- DOTTED CIRCLE -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x26" name="ampersand"/><!-- AMPERSAND -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x64" name="d"/><!-- LATIN SMALL LETTER D -->
+      <map code="0x66" name="f"/><!-- LATIN SMALL LETTER F -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x74" name="t"/><!-- LATIN SMALL LETTER T -->
+      <map code="0xe3" name="atilde"/><!-- LATIN SMALL LETTER A WITH TILDE -->
+      <map code="0x303" name="uni0303"/><!-- COMBINING TILDE -->
+      <map code="0x308" name="uni0308"/><!-- COMBINING DIAERESIS -->
+      <map code="0x324" name="uni0324"/><!-- COMBINING DIAERESIS BELOW -->
+      <map code="0x330" name="uni0330"/><!-- COMBINING TILDE BELOW -->
+      <map code="0x25cc" name="uni25CC"/><!-- DOTTED CIRCLE -->
+    </cmap_format_4>
+  </cmap>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <CFF>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="TestFamily2-Master0">
+      <version value="2.20"/>
+      <Notice value=""/>
+      <Copyright value=""/>
+      <FullName value="Test Family 2 Master 0"/>
+      <FontName value="TestFamily2-Master0"/>
+      <FamilyName value="Test Family 2"/>
+      <Weight value="Extra-light"/>
+      <isFixedPitch value="0"/>
+      <ItalicAngle value="0"/>
+      <UnderlinePosition value="-75"/>
+      <UnderlineThickness value="50"/>
+      <PaintType value="0"/>
+      <CharstringType value="2"/>
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FontBBox value="-160 -220 550 734"/>
+      <StrokeWidth value="0"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <Encoding name="StandardEncoding"/>
+      <Private>
+        <BlueValues value="-12 0 478 490 510 522 570 582 640 652 660 672 722 734"/>
+        <OtherBlues value="-234 -222"/>
+        <FamilyBlues value="-12 0 486 498 518 530 574 586 638 650 656 668 712 724"/>
+        <FamilyOtherBlues value="-217 -205"/>
+        <BlueScale value="0.0625"/>
+        <BlueShift value="7"/>
+        <BlueFuzz value="0"/>
+        <StdHW value="28"/>
+        <StdVW value="32"/>
+        <StemSnapH value="28 40"/>
+        <StemSnapV value="32 48"/>
+        <ForceBold value="0"/>
+        <LanguageGroup value="0"/>
+        <ExpansionFactor value="0.06"/>
+        <initialRandomSeed value="0"/>
+        <defaultWidthX value="200"/>
+        <nominalWidthX value="0"/>
+        <Subrs>
+          <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+          <CharString index="0">
+            rmoveto
+            18 0 14 14 0 18 rrcurveto
+            0 18 -14 14 -18 0 rrcurveto
+            -18 0 -14 -14 0 -18 rrcurveto
+            0 -18 14 -14 18 0 rrcurveto
+            return
+          </CharString>
+          <CharString index="1">
+            rmoveto
+            64 0 60 36 50 40 rrcurveto
+            2 0 rlineto
+            4 -64 rlineto
+            26 0 rlineto
+            0 310 rlineto
+            0 96 -34 84 -112 0 rrcurveto
+            -78 0 -66 -40 -30 -22 rrcurveto
+            16 -24 rlineto
+            30 24 58 34 68 0 rrcurveto
+            100 0 20 -86 -2 -78 rrcurveto
+            -216 -24 -98 -50 0 -107 rrcurveto
+            0 -91 64 -38 74 0 rrcurveto
+            2 28 rmoveto
+            -58 0 -50 28 0 74 rrcurveto
+            0 82 72 48 210 24 rrcurveto
+            0 -174 rlineto
+            -64 -54 -52 -28 -58 0 rrcurveto
+            endchar
+          </CharString>
+          <CharString index="2">
+            -40 22 -58 0 rrcurveto
+            -116 0 -98 -98 0 -154 rrcurveto
+            0 -162 78 -88 120 0 rrcurveto
+            2 28 rmoveto
+            -108 0 -60 90 0 132 rrcurveto
+            0 124 78 100 102 0 rrcurveto
+            50 0 44 -18 54 -48 rrcurveto
+            0 -296 rlineto
+            -54 -54 -50 -30 -56 0 rrcurveto
+            endchar
+          </CharString>
+          <CharString index="3">
+            0 rlineto
+            0 114 rlineto
+            0 72 24 42 54 0 rrcurveto
+            18 0 20 -4 20 -10 rrcurveto
+            10 26 rlineto
+            -22 10 -24 6 -20 0 rrcurveto
+            -68 0 -42 -44 0 -94 rrcurveto
+            0 -118 rlineto
+            -66 -4 rlineto
+            0 -24 rlineto
+            66 0 rlineto
+            0 -450 rlineto
+            30 0 rlineto
+            0 450 rlineto
+            return
+          </CharString>
+          <CharString index="4">
+            580 rmoveto
+            63 0 16 66 4 56 rrcurveto
+            -26 2 rlineto
+            -2 -52 -16 -46 -37 0 rrcurveto
+            -59 0 -20 100 -76 0 rrcurveto
+            -64 0 -16 -65 -4 -57 rrcurveto
+            26 -2 rlineto
+            2 54 16 44 38 0 rrcurveto
+            58 0 20 -100 77 0 rrcurveto
+            return
+          </CharString>
+          <CharString index="5">
+            -10 26 rlineto
+            -18 -8 -26 -8 -18 0 rrcurveto
+            -70 0 -14 44 0 62 rrcurveto
+            0 328 rlineto
+            142 0 rlineto
+            0 28 rlineto
+            -142 0 rlineto
+            0 140 rlineto
+            -26 0 rlineto
+            -4 -140 rlineto
+            return
+          </CharString>
+          <CharString index="6">
+            540 252 -12 rmoveto
+            66 0 54 36 40 40 rrcurveto
+            2 0 rlineto
+            4 -64 rlineto
+            26 0 rlineto
+            0 return
+          </CharString>
+          <CharString index="7">
+            0 21 rrcurveto
+            0 20 -13 11 -18 0 rrcurveto
+            -16 0 -13 -11 0 -20 rrcurveto
+            0 -21 13 return
+          </CharString>
+        </Subrs>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          624 96 0 rmoveto
+          432 0 rlineto
+          0 660 rlineto
+          -432 0 rlineto
+          214 -294 rmoveto
+          -56 92 rlineto
+          -94 168 rlineto
+          302 0 rlineto
+          -94 -168 rlineto
+          -54 -92 rlineto
+          -180 -292 rmoveto
+          0 536 rlineto
+          154 -270 rlineto
+          200 -266 rmoveto
+          -152 266 rlineto
+          152 270 rlineto
+          -344 -578 rmoveto
+          102 176 rlineto
+          64 106 rlineto
+          4 0 rlineto
+          62 -106 rlineto
+          100 -176 rlineto
+          endchar
+        </CharString>
+        <CharString name="A">
+          520 476 0 rmoveto
+          34 0 rlineto
+          -236 660 rlineto
+          -28 0 rlineto
+          -236 -660 rlineto
+          32 0 rlineto
+          83 236 rlineto
+          269 0 rlineto
+          -212 160 rmoveto
+          28 80 24 68 24 82 rrcurveto
+          4 0 rlineto
+          24 -82 24 -68 28 -80 rrcurveto
+          46 -132 rlineto
+          -249 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="A.sc">
+          444 400 0 rmoveto
+          34 0 rlineto
+          -198 510 rlineto
+          -29 0 rlineto
+          -197 -510 rlineto
+          32 0 rlineto
+          67 176 rlineto
+          225 0 rlineto
+          -176 128 rmoveto
+          23 62 18 48 21 61 rrcurveto
+          4 0 rlineto
+          21 -60 18 -48 23 -63 rrcurveto
+          38 -100 rlineto
+          -204 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="a">
+          486 198 -12 -106 callsubr
+        </CharString>
+        <CharString name="a.alt">
+          -101 callsubr
+          478 rlineto
+          -28 0 rlineto
+          -2 -46 rlineto
+          -2 0 rlineto
+          -46 36 -105 callsubr
+        </CharString>
+        <CharString name="ampersand">
+          562 550 16 rmoveto
+          -39 15 -44 27 -47 39 rrcurveto
+          53 67 39 86 26 92 rrcurveto
+          -30 0 rlineto
+          -24 -88 -35 -77 -50 -62 rrcurveto
+          -70 64 -72 88 -47 90 rrcurveto
+          76 58 78 57 0 84 rrcurveto
+          0 66 -36 50 -68 0 rrcurveto
+          -76 0 -54 -60 0 -84 rrcurveto
+          0 -52 17 -57 28 -57 rrcurveto
+          -70 -53 -67 -58 0 -85 rrcurveto
+          0 -110 86 -68 100 0 rrcurveto
+          73 0 56 35 48 51 rrcurveto
+          51 -43 46 -28 40 -15 rrcurveto
+          -378 542 rmoveto
+          0 62 36 52 62 0 rrcurveto
+          56 0 20 -46 0 -44 rrcurveto
+          0 -72 -66 -50 -69 -52 rrcurveto
+          -24 52 -15 51 0 47 rrcurveto
+          -90 -362 rmoveto
+          0 71 54 51 63 49 rrcurveto
+          48 -91 73 -88 72 -67 rrcurveto
+          -43 -45 -53 -32 -58 0 rrcurveto
+          -84 0 -72 60 0 92 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="atilde">
+          486 319 -103 callsubr
+          -121 -592 -106 callsubr
+        </CharString>
+        <CharString name="d">
+          -101 callsubr
+          722 rlineto
+          -30 0 rlineto
+          0 -202 rlineto
+          2 -90 rlineto
+          -50 38 -105 callsubr
+        </CharString>
+        <CharString name="f">
+          252 244 450 rmoveto
+          0 28 rlineto
+          -114 -104 callsubr
+          endchar
+        </CharString>
+        <CharString name="f_t">
+          518 508 6 rmoveto
+          -102 callsubr
+          -192 -104 callsubr
+          192 0 rlineto
+          0 -324 rlineto
+          0 -82 24 -56 88 0 rrcurveto
+          16 0 30 8 28 10 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="n">
+          526 96 0 rmoveto
+          30 0 rlineto
+          0 366 rlineto
+          62 64 44 32 60 0 rrcurveto
+          82 0 34 -52 0 -106 rrcurveto
+          0 -304 rlineto
+          30 0 rlineto
+          0 308 rlineto
+          0 124 -46 58 -98 0 rrcurveto
+          -66 0 -50 -38 -50 -50 rrcurveto
+          -2 0 rlineto
+          -4 76 rlineto
+          -26 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="space">
+          200 endchar
+        </CharString>
+        <CharString name="t">
+          302 218 -12 rmoveto
+          16 0 30 8 28 10 rrcurveto
+          -102 callsubr
+          -76 -4 rlineto
+          0 -24 rlineto
+          76 0 rlineto
+          0 -324 rlineto
+          0 -82 24 -56 88 0 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uni0303">
+          0 77 -103 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0308">
+          0 -86 602 -107 callsubr
+          172 0 -107 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0324">
+          0 -86 -188 -107 callsubr
+          172 0 -107 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0330">
+          0 77 -220 rmoveto
+          63 0 16 66 4 56 rrcurveto
+          -26 2 rlineto
+          -2 -52 -16 -46 -37 0 rrcurveto
+          -59 0 -20 100 -76 0 rrcurveto
+          -64 0 -16 -65 -4 -57 rrcurveto
+          26 -2 rlineto
+          2 54 16 44 38 0 rrcurveto
+          58 0 20 -100 77 0 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uni25CC">
+          592 295 426 rmoveto
+          18 0 13 12 0 20 rrcurveto
+          0 20 -13 12 -18 0 rrcurveto
+          -16 0 -13 -12 0 -20 rrcurveto
+          0 -20 13 -12 16 0 rrcurveto
+          -106 -26 rmoveto
+          18 0 12 12 0 19 rrcurveto
+          0 22 -13 10 -17 0 rrcurveto
+          -16 0 -13 -10 0 -22 rrcurveto
+          0 -19 13 -12 16 0 rrcurveto
+          212 -1 rmoveto
+          19 0 11 13 0 19 rrcurveto
+          0 21 -13 10 -17 0 rrcurveto
+          -15 0 -13 -10 0 -21 rrcurveto
+          0 -19 13 -13 15 0 rrcurveto
+          -291 -81 rmoveto
+          19 0 12 12 -100 callsubr
+          -12 16 0 rrcurveto
+          370 -1 rmoveto
+          19 0 11 12 0 20 rrcurveto
+          0 20 -13 11 -17 0 rrcurveto
+          -15 0 -14 -11 0 -20 rrcurveto
+          0 -20 14 -12 15 0 rrcurveto
+          -398 -110 rmoveto
+          19 0 13 12 0 20 rrcurveto
+          0 21 -13 10 -19 0 rrcurveto
+          -15 0 -13 -10 0 -21 rrcurveto
+          0 -20 13 -12 15 0 rrcurveto
+          426 0 rmoveto
+          18 0 12 12 0 20 rrcurveto
+          0 21 -15 10 -15 0 rrcurveto
+          -17 0 -13 -10 0 -21 rrcurveto
+          0 -20 13 -12 17 0 rrcurveto
+          -398 -110 rmoveto
+          19 0 12 13 0 19 rrcurveto
+          0 21 -13 11 -18 0 rrcurveto
+          -16 0 -13 -11 0 -21 rrcurveto
+          0 -19 13 -13 16 0 rrcurveto
+          370 0 rmoveto
+          19 0 11 13 0 19 rrcurveto
+          0 21 -13 11 -17 0 rrcurveto
+          -15 0 -14 -11 0 -21 rrcurveto
+          0 -19 14 -13 15 0 rrcurveto
+          -291 -82 rmoveto
+          18 0 12 12 0 22 rrcurveto
+          0 19 -13 10 -17 0 rrcurveto
+          -16 0 -13 -10 0 -19 rrcurveto
+          0 -22 13 -12 16 0 rrcurveto
+          212 0 rmoveto
+          19 0 11 12 0 22 rrcurveto
+          0 19 -13 10 -17 0 rrcurveto
+          -15 0 -13 -10 0 -19 rrcurveto
+          0 -22 13 -12 15 0 rrcurveto
+          -106 -27 rmoveto
+          18 0 13 11 -100 callsubr
+          -11 16 0 rrcurveto
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+  <BASE>
+    <Version value="0x00010000"/>
+    <HorizAxis>
+      <BaseTagList>
+        <!-- BaseTagCount=2 -->
+        <BaselineTag index="0" value="ideo"/>
+        <BaselineTag index="1" value="romn"/>
+      </BaseTagList>
+      <BaseScriptList>
+        <!-- BaseScriptCount=4 -->
+        <BaseScriptRecord index="0">
+          <BaseScriptTag value="DFLT"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="1">
+          <BaseScriptTag value="cyrl"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="2">
+          <BaseScriptTag value="grek"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="3">
+          <BaseScriptTag value="latn"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+      </BaseScriptList>
+    </HorizAxis>
+  </BASE>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=2 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=6 -->
+            <FeatureIndex index="0" value="0"/>
+            <FeatureIndex index="1" value="1"/>
+            <FeatureIndex index="2" value="2"/>
+            <FeatureIndex index="3" value="3"/>
+            <FeatureIndex index="4" value="4"/>
+            <FeatureIndex index="5" value="5"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="1">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=6 -->
+            <FeatureIndex index="0" value="0"/>
+            <FeatureIndex index="1" value="1"/>
+            <FeatureIndex index="2" value="2"/>
+            <FeatureIndex index="3" value="3"/>
+            <FeatureIndex index="4" value="4"/>
+            <FeatureIndex index="5" value="5"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=6 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="c2sc"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="calt"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="5"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="2">
+        <FeatureTag value="ccmp"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="2"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="3">
+        <FeatureTag value="liga"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="4">
+        <FeatureTag value="salt"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="3"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="5">
+        <FeatureTag value="ss01"/>
+        <Feature>
+          <FeatureParamsStylisticSet>
+            <Version value="0"/>
+            <UINameID value="256"/>  <!-- Alternate a -->
+          </FeatureParamsStylisticSet>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=6 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="A" out="A.sc"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="a" out="a.alt"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="ampersand" out="a,n,d"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="3"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <AlternateSubst index="0" Format="1">
+          <AlternateSet glyph="a">
+            <Alternate glyph="a.alt"/>
+            <Alternate glyph="A.sc"/>
+          </AlternateSet>
+        </AlternateSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="f">
+            <Ligature components="t" glyph="f_t"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="5">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0" Format="1">
+            <Glyph value="a"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0" Format="1">
+            <Glyph value="t"/>
+          </LookAheadCoverage>
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+  <hmtx>
+    <mtx name=".notdef" width="624" lsb="96"/>
+    <mtx name="A" width="520" lsb="10"/>
+    <mtx name="A.sc" width="444" lsb="10"/>
+    <mtx name="a" width="486" lsb="60"/>
+    <mtx name="a.alt" width="540" lsb="54"/>
+    <mtx name="ampersand" width="562" lsb="38"/>
+    <mtx name="atilde" width="486" lsb="60"/>
+    <mtx name="d" width="540" lsb="54"/>
+    <mtx name="f" width="252" lsb="34"/>
+    <mtx name="f_t" width="518" lsb="34"/>
+    <mtx name="n" width="526" lsb="96"/>
+    <mtx name="space" width="200" lsb="0"/>
+    <mtx name="t" width="302" lsb="30"/>
+    <mtx name="uni0303" width="0" lsb="-160"/>
+    <mtx name="uni0308" width="0" lsb="-118"/>
+    <mtx name="uni0324" width="0" lsb="-118"/>
+    <mtx name="uni0330" width="0" lsb="-160"/>
+    <mtx name="uni25CC" width="592" lsb="54"/>
+  </hmtx>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_otf/TestFamily2-Master1.ttx b/Tests/varLib/data/master_ttx_interpolatable_otf/TestFamily2-Master1.ttx
new file mode 100644
index 0000000..b7ca3a9
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_interpolatable_otf/TestFamily2-Master1.ttx
@@ -0,0 +1,693 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.9">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="space"/>
+    <GlyphID id="2" name="A"/>
+    <GlyphID id="3" name="a"/>
+    <GlyphID id="4" name="d"/>
+    <GlyphID id="5" name="f"/>
+    <GlyphID id="6" name="n"/>
+    <GlyphID id="7" name="t"/>
+    <GlyphID id="8" name="f_t"/>
+    <GlyphID id="9" name="a.alt"/>
+    <GlyphID id="10" name="A.sc"/>
+    <GlyphID id="11" name="atilde"/>
+    <GlyphID id="12" name="ampersand"/>
+    <GlyphID id="13" name="uni25CC"/>
+    <GlyphID id="14" name="uni0303"/>
+    <GlyphID id="15" name="uni0308"/>
+    <GlyphID id="16" name="uni0330"/>
+    <GlyphID id="17" name="uni0324"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="2.02"/>
+    <checkSumAdjustment value="0x92a986cd"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Mar 16 05:26:39 2017"/>
+    <modified value="Thu Mar 16 05:26:39 2017"/>
+    <xMin value="-196"/>
+    <yMin value="-228"/>
+    <xMax value="706"/>
+    <yMax value="746"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="984"/>
+    <descent value="-273"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="724"/>
+    <minLeftSideBearing value="-196"/>
+    <minRightSideBearing value="-196"/>
+    <xMaxExtent value="706"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="15"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="18"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="540"/>
+    <usWeightClass value="900"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="8"/>
+      <bProportion value="3"/>
+      <bContrast value="3"/>
+      <bStrokeVariation value="4"/>
+      <bArmStyle value="3"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000011"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBO"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="9676"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="984"/>
+    <usWinDescent value="273"/>
+    <ulCodePageRange1 value="00100000 00000000 00000000 00000011"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="660"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="9" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Paul D. Hunt
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test Family 2
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      Version 2.020;ADBO;Test Family 2 Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test Family 2
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 2.020
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestFamily2-Master1
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Paul D. Hunt
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Master 1
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x26" name="ampersand"/><!-- AMPERSAND -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x64" name="d"/><!-- LATIN SMALL LETTER D -->
+      <map code="0x66" name="f"/><!-- LATIN SMALL LETTER F -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x74" name="t"/><!-- LATIN SMALL LETTER T -->
+      <map code="0xe3" name="atilde"/><!-- LATIN SMALL LETTER A WITH TILDE -->
+      <map code="0x303" name="uni0303"/><!-- COMBINING TILDE -->
+      <map code="0x308" name="uni0308"/><!-- COMBINING DIAERESIS -->
+      <map code="0x324" name="uni0324"/><!-- COMBINING DIAERESIS BELOW -->
+      <map code="0x330" name="uni0330"/><!-- COMBINING TILDE BELOW -->
+      <map code="0x25cc" name="uni25CC"/><!-- DOTTED CIRCLE -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x26" name="ampersand"/><!-- AMPERSAND -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x64" name="d"/><!-- LATIN SMALL LETTER D -->
+      <map code="0x66" name="f"/><!-- LATIN SMALL LETTER F -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x74" name="t"/><!-- LATIN SMALL LETTER T -->
+      <map code="0xe3" name="atilde"/><!-- LATIN SMALL LETTER A WITH TILDE -->
+      <map code="0x303" name="uni0303"/><!-- COMBINING TILDE -->
+      <map code="0x308" name="uni0308"/><!-- COMBINING DIAERESIS -->
+      <map code="0x324" name="uni0324"/><!-- COMBINING DIAERESIS BELOW -->
+      <map code="0x330" name="uni0330"/><!-- COMBINING TILDE BELOW -->
+      <map code="0x25cc" name="uni25CC"/><!-- DOTTED CIRCLE -->
+    </cmap_format_4>
+  </cmap>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <CFF>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="TestFamily2-Master1">
+      <version value="2.20"/>
+      <Notice value=""/>
+      <Copyright value=""/>
+      <FullName value="Test Family 2 Master 1"/>
+      <FontName value="TestFamily2-Master1"/>
+      <FamilyName value="Test Family 2"/>
+      <Weight value="Black"/>
+      <isFixedPitch value="0"/>
+      <ItalicAngle value="0"/>
+      <UnderlinePosition value="-75"/>
+      <UnderlineThickness value="50"/>
+      <PaintType value="0"/>
+      <CharstringType value="2"/>
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FontBBox value="-196 -228 706 746"/>
+      <StrokeWidth value="0"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <Encoding name="StandardEncoding"/>
+      <Private>
+        <BlueValues value="-12 0 500 512 532 544 580 592 634 646 650 662 696 708"/>
+        <OtherBlues value="-188 -176"/>
+        <FamilyBlues value="-12 0 486 498 518 530 574 586 638 650 656 668 712 724"/>
+        <FamilyOtherBlues value="-217 -205"/>
+        <BlueScale value="0.0625"/>
+        <BlueShift value="7"/>
+        <BlueFuzz value="0"/>
+        <StdHW value="134"/>
+        <StdVW value="172"/>
+        <StemSnapH value="134 144"/>
+        <StemSnapV value="172 176"/>
+        <ForceBold value="0"/>
+        <LanguageGroup value="0"/>
+        <ExpansionFactor value="0.06"/>
+        <initialRandomSeed value="0"/>
+        <defaultWidthX value="200"/>
+        <nominalWidthX value="0"/>
+        <Subrs>
+          <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+          <CharString index="0">
+            rmoveto
+            47 0 33 35 0 45 rrcurveto
+            0 45 -33 35 -47 0 rrcurveto
+            -47 0 -33 -35 0 -45 rrcurveto
+            0 -45 33 -35 47 0 rrcurveto
+            return
+          </CharString>
+          <CharString index="1">
+            rmoveto
+            54 0 44 24 40 36 rrcurveto
+            4 0 rlineto
+            12 -48 rlineto
+            140 0 rlineto
+            0 278 rlineto
+            0 164 -78 70 -130 0 rrcurveto
+            -78 0 -72 -24 -70 -42 rrcurveto
+            60 -112 rlineto
+            52 28 38 14 36 0 rrcurveto
+            44 0 22 -16 4 -36 rrcurveto
+            -192 -20 -80 -58 0 -104 rrcurveto
+            0 -82 56 -72 94 0 rrcurveto
+            60 132 rmoveto
+            -30 0 -16 13 0 23 rrcurveto
+            0 28 26 26 82 12 rrcurveto
+            0 -68 rlineto
+            -18 -20 -16 -14 -28 0 rrcurveto
+            endchar
+          </CharString>
+          <CharString index="2">
+            0 rlineto
+            0 12 rlineto
+            0 47 20 15 32 0 rrcurveto
+            16 0 18 -4 16 -6 rrcurveto
+            30 126 rlineto
+            -22 8 -38 10 -46 0 rrcurveto
+            -148 0 -50 -95 0 -107 rrcurveto
+            0 -7 rlineto
+            -66 -5 rlineto
+            0 -128 rlineto
+            66 0 rlineto
+            0 -366 rlineto
+            172 0 rlineto
+            0 366 rlineto
+            return
+          </CharString>
+          <CharString index="3">
+            -98 0 -164 rrcurveto
+            0 -162 80 -100 124 0 rrcurveto
+            46 140 rmoveto
+            -46 0 -28 34 0 90 rrcurveto
+            0 88 34 32 36 0 rrcurveto
+            22 0 26 -6 20 -18 rrcurveto
+            0 -184 rlineto
+            -18 -28 -20 -8 -26 0 rrcurveto
+            endchar
+          </CharString>
+          <CharString index="4">
+            -26 124 rlineto
+            -12 -4 -16 -4 -16 0 rrcurveto
+            -32 0 -28 18 0 55 rrcurveto
+            0 171 rlineto
+            114 0 rlineto
+            0 134 rlineto
+            -114 0 rlineto
+            0 130 rlineto
+            -142 0 rlineto
+            -20 -130 rlineto
+            return
+          </CharString>
+          <CharString index="5">
+            113 rrcurveto
+            -106 6 rlineto
+            -4 -36 -10 -10 -16 0 rrcurveto
+            -26 0 -38 56 -60 0 rrcurveto
+            -80 0 -50 -45 -2 -113 rrcurveto
+            106 -6 rlineto
+            4 36 10 10 16 0 rrcurveto
+            26 0 38 -56 60 0 rrcurveto
+            return
+          </CharString>
+          <CharString index="6">
+            580 240 -12 rmoveto
+            44 0 48 24 34 34 rrcurveto
+            4 0 rlineto
+            12 -46 rlineto
+            140 0 rlineto
+            0 return
+          </CharString>
+          <CharString index="7">
+            0 rrcurveto
+            -23 0 -21 -16 0 -28 rrcurveto
+            0 -30 21 -17 23 0 rrcurveto
+            return
+          </CharString>
+          <CharString index="8">
+            rrcurveto
+            0 28 -19 16 -26 0 rrcurveto
+            -23 0 -20 -16 0 -28 rrcurveto
+            0 return
+          </CharString>
+          <CharString index="9">
+            0 rlineto
+            0 -174 rlineto
+            0 -122 54 -82 130 0 rrcurveto
+            return
+          </CharString>
+          <CharString index="10">
+            rmoveto
+            26 0 19 17 0 30 rrcurveto
+            0 28 -21 16 -24 0 rrcurveto
+            return
+          </CharString>
+        </Subrs>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          704 76 0 rmoveto
+          552 0 rlineto
+          0 660 rlineto
+          -552 0 rlineto
+          274 -236 rmoveto
+          -40 96 rlineto
+          -18 36 rlineto
+          120 0 rlineto
+          -18 -36 rlineto
+          -40 -96 rlineto
+          -166 -252 rmoveto
+          0 336 rlineto
+          82 -168 rlineto
+          246 -168 rmoveto
+          -82 168 rlineto
+          82 168 rlineto
+          -228 -404 rmoveto
+          26 56 rlineto
+          36 96 rlineto
+          4 0 rlineto
+          36 -96 rlineto
+          26 -56 rlineto
+          endchar
+        </CharString>
+        <CharString name="A">
+          584 412 0 rmoveto
+          182 0 rlineto
+          -198 650 rlineto
+          -208 0 rlineto
+          -198 -650 rlineto
+          176 0 rlineto
+          32 138 rlineto
+          182 0 rlineto
+          -140 178 rmoveto
+          16 62 16 78 14 66 rrcurveto
+          4 0 rlineto
+          16 -65 16 -79 16 -62 rrcurveto
+          11 -45 rlineto
+          -120 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="A.sc">
+          516 346 0 rmoveto
+          180 0 rlineto
+          -165 532 rlineto
+          -206 0 rlineto
+          -165 -532 rlineto
+          174 0 rlineto
+          21 94 rlineto
+          140 0 rlineto
+          -106 150 rmoveto
+          11 48 11 66 11 51 rrcurveto
+          4 0 rlineto
+          13 -50 11 -67 11 -48 rrcurveto
+          6 -28 rlineto
+          -84 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="a">
+          536 188 -12 -106 callsubr
+        </CharString>
+        <CharString name="a.alt">
+          -101 callsubr
+          500 rlineto
+          -134 0 rlineto
+          -14 -50 rlineto
+          -4 0 rlineto
+          -38 44 -40 18 -48 0 rrcurveto
+          -102 0 -106 -104 callsubr
+        </CharString>
+        <CharString name="ampersand">
+          690 670 126 rmoveto
+          -31 4 -38 12 -39 19 rrcurveto
+          49 66 34 71 23 76 rrcurveto
+          -156 0 rlineto
+          -15 -56 -25 -48 -30 -40 rrcurveto
+          -41 29 -40 33 -33 35 rrcurveto
+          66 43 64 53 0 85 rrcurveto
+          0 94 -68 60 -104 0 rrcurveto
+          -116 0 -72 -82 0 -94 rrcurveto
+          0 -39 14 -45 25 -45 rrcurveto
+          -62 -38 -53 -52 0 -91 rrcurveto
+          0 -98 73 -90 151 0 rrcurveto
+          83 0 70 24 57 39 rrcurveto
+          58 -31 59 -22 57 -10 rrcurveto
+          -391 498 rmoveto
+          0 42 24 22 27 0 rrcurveto
+          25 0 13 -14 0 -28 rrcurveto
+          0 -38 -30 -25 -41 -24 rrcurveto
+          -12 23 -6 22 0 20 rrcurveto
+          -55 -300 rmoveto
+          0 23 12 19 18 19 rrcurveto
+          34 -40 40 -38 44 -35 rrcurveto
+          -22 -10 -21 -6 -21 0 rrcurveto
+          -52 0 -32 28 0 40 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="atilde">
+          536 330 572 rmoveto
+          80 0 50 45 2 -102 callsubr
+          -142 -584 -106 callsubr
+        </CharString>
+        <CharString name="d">
+          -101 callsubr
+          696 rlineto
+          -172 0 rlineto
+          0 -162 rlineto
+          6 -72 rlineto
+          -30 30 -32 20 -54 0 rrcurveto
+          -102 0 -102 -104 callsubr
+        </CharString>
+        <CharString name="f">
+          360 344 366 rmoveto
+          0 134 rlineto
+          -84 -105 callsubr
+          endchar
+        </CharString>
+        <CharString name="f_t">
+          724 706 6 rmoveto
+          -103 callsubr
+          -154 -105 callsubr
+          144 -98 callsubr
+          55 0 37 10 26 8 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="n">
+          582 58 0 rmoveto
+          172 0 rlineto
+          0 328 rlineto
+          26 24 18 14 32 0 rrcurveto
+          34 0 16 -16 0 -64 rrcurveto
+          0 -286 rlineto
+          172 0 rlineto
+          0 308 rlineto
+          0 124 -46 80 -110 0 rrcurveto
+          -68 0 -50 -34 -40 -38 rrcurveto
+          -4 0 rlineto
+          -12 60 rlineto
+          -140 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="space">
+          200 endchar
+        </CharString>
+        <CharString name="t">
+          400 264 -12 rmoveto
+          55 0 37 10 26 8 rrcurveto
+          -103 callsubr
+          -76 -6 rlineto
+          0 -128 rlineto
+          66 -98 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0303">
+          0 64 572 rmoveto
+          80 0 50 45 2 -102 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0308">
+          0 -114 562 -107 callsubr
+          228 0 -107 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0324">
+          0 -114 -224 -107 callsubr
+          228 0 -107 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni0330">
+          0 64 -228 rmoveto
+          80 0 50 45 2 -102 callsubr
+          endchar
+        </CharString>
+        <CharString name="uni25CC">
+          574 287 421 rmoveto
+          27 0 18 18 0 29 -99 callsubr
+          -29 20 -18 23 0 rrcurveto
+          -105 -26 rmoveto
+          26 0 19 17 0 28 rrcurveto
+          0 30 -21 16 -24 0 rrcurveto
+          -24 0 -21 -16 0 -30 rrcurveto
+          0 -28 21 -17 24 0 rrcurveto
+          210 -1 rmoveto
+          28 0 18 17 0 29 rrcurveto
+          0 29 -21 17 -25 0 rrcurveto
+          -23 0 -21 -17 0 -29 rrcurveto
+          0 -29 21 -17 23 0 rrcurveto
+          -288 -81 rmoveto
+          27 0 18 18 0 29 rrcurveto
+          0 30 -19 15 -26 0 rrcurveto
+          -22 0 -21 -15 0 -30 rrcurveto
+          0 -29 21 -18 22 0 rrcurveto
+          368 0 rmoveto
+          26 0 18 17 0 29 rrcurveto
+          0 29 -20 16 -24 0 rrcurveto
+          -25 0 -21 -16 0 -29 rrcurveto
+          0 -29 21 -17 25 0 rrcurveto
+          -396 -109 rmoveto
+          28 0 18 17 0 30 rrcurveto
+          0 28 -20 16 -26 -100 callsubr
+          422 0 -97 callsubr
+          -25 0 -20 -16 0 -28 rrcurveto
+          0 -30 20 -17 25 0 rrcurveto
+          -394 -108 rmoveto
+          27 0 18 16 0 29 rrcurveto
+          0 29 -19 17 -26 0 rrcurveto
+          -22 0 -21 -17 0 -29 rrcurveto
+          0 -29 21 -16 22 0 rrcurveto
+          368 0 rmoveto
+          26 0 18 16 0 29 rrcurveto
+          0 29 -20 17 -24 0 rrcurveto
+          -25 0 -21 -17 0 -29 rrcurveto
+          0 -29 21 -16 25 0 rrcurveto
+          -290 -82 -97 callsubr
+          -24 0 -21 -16 0 -28 rrcurveto
+          0 -30 21 -17 24 0 rrcurveto
+          210 0 rmoveto
+          28 0 18 17 0 30 rrcurveto
+          0 28 -21 16 -25 -100 callsubr
+          -105 -27 rmoveto
+          27 0 18 17 0 30 -99 callsubr
+          -30 20 -17 23 0 rrcurveto
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+  <BASE>
+    <Version value="0x00010000"/>
+    <HorizAxis>
+      <BaseTagList>
+        <!-- BaseTagCount=2 -->
+        <BaselineTag index="0" value="ideo"/>
+        <BaselineTag index="1" value="romn"/>
+      </BaseTagList>
+      <BaseScriptList>
+        <!-- BaseScriptCount=4 -->
+        <BaseScriptRecord index="0">
+          <BaseScriptTag value="DFLT"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="1">
+          <BaseScriptTag value="cyrl"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="2">
+          <BaseScriptTag value="grek"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="3">
+          <BaseScriptTag value="latn"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+      </BaseScriptList>
+    </HorizAxis>
+  </BASE>
+
+  <hmtx>
+    <mtx name=".notdef" width="704" lsb="76"/>
+    <mtx name="A" width="584" lsb="-10"/>
+    <mtx name="A.sc" width="516" lsb="-10"/>
+    <mtx name="a" width="536" lsb="38"/>
+    <mtx name="a.alt" width="580" lsb="36"/>
+    <mtx name="ampersand" width="690" lsb="22"/>
+    <mtx name="atilde" width="536" lsb="38"/>
+    <mtx name="d" width="580" lsb="36"/>
+    <mtx name="f" width="360" lsb="22"/>
+    <mtx name="f_t" width="724" lsb="22"/>
+    <mtx name="n" width="582" lsb="58"/>
+    <mtx name="space" width="200" lsb="0"/>
+    <mtx name="t" width="400" lsb="14"/>
+    <mtx name="uni0303" width="0" lsb="-196"/>
+    <mtx name="uni0308" width="0" lsb="-194"/>
+    <mtx name="uni0324" width="0" lsb="-194"/>
+    <mtx name="uni0330" width="0" lsb="-196"/>
+    <mtx name="uni25CC" width="574" lsb="32"/>
+  </hmtx>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master0.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master0.ttx
new file mode 100644
index 0000000..f14f89d
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master0.ttx
@@ -0,0 +1,520 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.7">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="uni0020"/>
+    <GlyphID id="2" name="uni0041"/>
+    <GlyphID id="3" name="uni0061"/>
+    <GlyphID id="4" name="uni0024"/>
+    <GlyphID id="5" name="uni0024.nostroke"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.001"/>
+    <checkSumAdjustment value="0x7789cbe0"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Feb 28 16:48:24 2017"/>
+    <modified value="Tue Feb 28 16:48:24 2017"/>
+    <xMin value="12"/>
+    <yMin value="-115"/>
+    <xMax value="651"/>
+    <yMax value="762"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="918"/>
+    <descent value="-335"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="653"/>
+    <minLeftSideBearing value="12"/>
+    <minRightSideBearing value="2"/>
+    <xMaxExtent value="651"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="5"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="62"/>
+    <maxContours value="4"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="1"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="1"/>
+    <maxSizeOfInstructions value="5"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="504"/>
+    <usWeightClass value="200"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000100"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="282"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="4"/>
+      <bWeight value="2"/>
+      <bProportion value="3"/>
+      <bContrast value="5"/>
+      <bStrokeVariation value="4"/>
+      <bArmStyle value="5"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000011"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBO"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="97"/>
+    <sTypoAscender value="730"/>
+    <sTypoDescender value="-270"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="918"/>
+    <usWinDescent value="335"/>
+    <ulCodePageRange1 value="00100000 00000000 00000000 00000011"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="470"/>
+    <sCapHeight value="677"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="640" lsb="80"/>
+    <mtx name="uni0020" width="248" lsb="0"/>
+    <mtx name="uni0024" width="490" lsb="53"/>
+    <mtx name="uni0024.nostroke" width="490" lsb="53"/>
+    <mtx name="uni0041" width="653" lsb="12"/>
+    <mtx name="uni0061" width="505" lsb="57"/>
+  </hmtx>
+
+  <fpgm>
+    <assembly>
+      PUSHB[ ]  /* 1 value pushed */
+      0
+      FDEF[ ]   /* FunctionDefinition */
+        POP[ ]  /* PopTopStack */
+      ENDF[ ]   /* EndFunctionDefinition */
+    </assembly>
+  </fpgm>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="uni0020"/><!-- SPACE -->
+      <map code="0x24" name="uni0024"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="uni0041"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="uni0061"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="uni0020"/><!-- SPACE -->
+      <map code="0x24" name="uni0024"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="uni0041"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="uni0061"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+  </cmap>
+
+  <cvt>
+    <cv index="0" value="470"/>
+    <cv index="1" value="677"/>
+  </cvt>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="80" yMin="0" xMax="560" yMax="677">
+      <contour>
+        <pt x="80" y="0" on="1"/>
+        <pt x="535" y="677" on="1"/>
+        <pt x="560" y="677" on="1"/>
+        <pt x="105" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="560" y="0" on="1"/>
+        <pt x="535" y="0" on="1"/>
+        <pt x="80" y="677" on="1"/>
+        <pt x="105" y="677" on="1"/>
+      </contour>
+      <contour>
+        <pt x="105" y="22" on="1"/>
+        <pt x="535" y="22" on="1"/>
+        <pt x="535" y="655" on="1"/>
+        <pt x="105" y="655" on="1"/>
+      </contour>
+      <contour>
+        <pt x="80" y="0" on="1"/>
+        <pt x="80" y="677" on="1"/>
+        <pt x="560" y="677" on="1"/>
+        <pt x="560" y="0" on="1"/>
+      </contour>
+      <instructions>
+        <assembly>
+          PUSHB[ ]  /* 1 value pushed */
+          0
+          MDAP[0]   /* MoveDirectAbsPt */
+        </assembly>
+      </instructions>
+    </TTGlyph>
+
+    <TTGlyph name="uni0020"/><!-- contains no outline data -->
+
+    <TTGlyph name="uni0024" xMin="53" yMin="-115" xMax="435" yMax="762">
+      <contour>
+        <pt x="245" y="7" on="1"/>
+        <pt x="312" y="7" on="0"/>
+        <pt x="400" y="84" on="0"/>
+        <pt x="400" y="153" on="1"/>
+        <pt x="400" y="202" on="0"/>
+        <pt x="346" y="277" on="0"/>
+        <pt x="260" y="316" on="1"/>
+        <pt x="223" y="333" on="1"/>
+        <pt x="154" y="364" on="0"/>
+        <pt x="86" y="448" on="0"/>
+        <pt x="86" y="508" on="1"/>
+        <pt x="86" y="589" on="0"/>
+        <pt x="191" y="667" on="0"/>
+        <pt x="260" y="667" on="1"/>
+        <pt x="323" y="667" on="0"/>
+        <pt x="417" y="597" on="0"/>
+        <pt x="435" y="531" on="1"/>
+        <pt x="434" y="520" on="0"/>
+        <pt x="422" y="510" on="0"/>
+        <pt x="412" y="510" on="1"/>
+        <pt x="404" y="510" on="0"/>
+        <pt x="390" y="519" on="0"/>
+        <pt x="385" y="530" on="1"/>
+        <pt x="357" y="630" on="1"/>
+        <pt x="395" y="583" on="1"/>
+        <pt x="362" y="620" on="0"/>
+        <pt x="302" y="647" on="0"/>
+        <pt x="260" y="647" on="1"/>
+        <pt x="194" y="647" on="0"/>
+        <pt x="112" y="576" on="0"/>
+        <pt x="112" y="508" on="1"/>
+        <pt x="112" y="457" on="0"/>
+        <pt x="177" y="381" on="0"/>
+        <pt x="247" y="352" on="1"/>
+        <pt x="266" y="344" on="1"/>
+        <pt x="366" y="301" on="0"/>
+        <pt x="426" y="204" on="0"/>
+        <pt x="426" y="151" on="1"/>
+        <pt x="426" y="68" on="0"/>
+        <pt x="313" y="-13" on="0"/>
+        <pt x="245" y="-13" on="1"/>
+        <pt x="165" y="-13" on="0"/>
+        <pt x="71" y="59" on="0"/>
+        <pt x="53" y="123" on="1"/>
+        <pt x="54" y="134" on="0"/>
+        <pt x="66" y="144" on="0"/>
+        <pt x="76" y="144" on="1"/>
+        <pt x="84" y="144" on="0"/>
+        <pt x="100" y="136" on="0"/>
+        <pt x="103" y="124" on="1"/>
+        <pt x="131" y="24" on="1"/>
+        <pt x="95" y="72" on="1"/>
+        <pt x="130" y="34" on="0"/>
+        <pt x="196" y="7" on="0"/>
+      </contour>
+      <contour>
+        <pt x="263" y="334" on="1"/>
+        <pt x="241" y="334" on="1"/>
+        <pt x="241" y="762" on="1"/>
+        <pt x="263" y="762" on="1"/>
+      </contour>
+      <contour>
+        <pt x="241" y="-115" on="1"/>
+        <pt x="241" y="334" on="1"/>
+        <pt x="263" y="334" on="1"/>
+        <pt x="263" y="-115" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0024.nostroke" xMin="53" yMin="-115" xMax="435" yMax="762">
+      <contour>
+        <pt x="245" y="7" on="1"/>
+        <pt x="312" y="7" on="0"/>
+        <pt x="400" y="84" on="0"/>
+        <pt x="400" y="153" on="1"/>
+        <pt x="400" y="202" on="0"/>
+        <pt x="346" y="277" on="0"/>
+        <pt x="260" y="316" on="1"/>
+        <pt x="223" y="333" on="1"/>
+        <pt x="154" y="364" on="0"/>
+        <pt x="86" y="448" on="0"/>
+        <pt x="86" y="508" on="1"/>
+        <pt x="86" y="589" on="0"/>
+        <pt x="191" y="667" on="0"/>
+        <pt x="260" y="667" on="1"/>
+        <pt x="323" y="667" on="0"/>
+        <pt x="417" y="597" on="0"/>
+        <pt x="435" y="531" on="1"/>
+        <pt x="434" y="520" on="0"/>
+        <pt x="422" y="510" on="0"/>
+        <pt x="412" y="510" on="1"/>
+        <pt x="404" y="510" on="0"/>
+        <pt x="390" y="519" on="0"/>
+        <pt x="385" y="530" on="1"/>
+        <pt x="357" y="630" on="1"/>
+        <pt x="395" y="583" on="1"/>
+        <pt x="362" y="620" on="0"/>
+        <pt x="302" y="647" on="0"/>
+        <pt x="260" y="647" on="1"/>
+        <pt x="194" y="647" on="0"/>
+        <pt x="112" y="576" on="0"/>
+        <pt x="112" y="508" on="1"/>
+        <pt x="112" y="457" on="0"/>
+        <pt x="177" y="381" on="0"/>
+        <pt x="247" y="352" on="1"/>
+        <pt x="266" y="344" on="1"/>
+        <pt x="366" y="301" on="0"/>
+        <pt x="426" y="204" on="0"/>
+        <pt x="426" y="151" on="1"/>
+        <pt x="426" y="68" on="0"/>
+        <pt x="313" y="-13" on="0"/>
+        <pt x="245" y="-13" on="1"/>
+        <pt x="165" y="-13" on="0"/>
+        <pt x="71" y="59" on="0"/>
+        <pt x="53" y="123" on="1"/>
+        <pt x="54" y="134" on="0"/>
+        <pt x="66" y="144" on="0"/>
+        <pt x="76" y="144" on="1"/>
+        <pt x="84" y="144" on="0"/>
+        <pt x="100" y="136" on="0"/>
+        <pt x="103" y="124" on="1"/>
+        <pt x="131" y="24" on="1"/>
+        <pt x="95" y="72" on="1"/>
+        <pt x="130" y="34" on="0"/>
+        <pt x="196" y="7" on="0"/>
+      </contour>
+      <contour>
+        <pt x="263" y="657" on="1"/>
+        <pt x="241" y="657" on="1"/>
+        <pt x="241" y="762" on="1"/>
+        <pt x="263" y="762" on="1"/>
+      </contour>
+      <contour>
+        <pt x="241" y="-115" on="1"/>
+        <pt x="241" y="-4" on="1"/>
+        <pt x="263" y="-4" on="1"/>
+        <pt x="263" y="-115" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0041" xMin="12" yMin="0" xMax="651" yMax="684">
+      <contour>
+        <pt x="12" y="0" on="1"/>
+        <pt x="12" y="20" on="1"/>
+        <pt x="99" y="26" on="1"/>
+        <pt x="113" y="26" on="1"/>
+        <pt x="210" y="20" on="1"/>
+        <pt x="210" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="85" y="0" on="1"/>
+        <pt x="307" y="684" on="1"/>
+        <pt x="327" y="684" on="1"/>
+        <pt x="556" y="0" on="1"/>
+        <pt x="524" y="0" on="1"/>
+        <pt x="308" y="657" on="1"/>
+        <pt x="325" y="667" on="1"/>
+        <pt x="114" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="173" y="247" on="1"/>
+        <pt x="182" y="267" on="1"/>
+        <pt x="460" y="267" on="1"/>
+        <pt x="468" y="247" on="1"/>
+      </contour>
+      <contour>
+        <pt x="403" y="0" on="1"/>
+        <pt x="403" y="20" on="1"/>
+        <pt x="523" y="26" on="1"/>
+        <pt x="537" y="26" on="1"/>
+        <pt x="651" y="20" on="1"/>
+        <pt x="651" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0061" xMin="57" yMin="-13" xMax="486" yMax="483">
+      <contour>
+        <pt x="57" y="104" on="1"/>
+        <pt x="57" y="158" on="0"/>
+        <pt x="132" y="230" on="0"/>
+        <pt x="206" y="254" on="1"/>
+        <pt x="248" y="268" on="0"/>
+        <pt x="331" y="286" on="0"/>
+        <pt x="369" y="295" on="1"/>
+        <pt x="369" y="277" on="1"/>
+        <pt x="331" y="268" on="0"/>
+        <pt x="245" y="245" on="0"/>
+        <pt x="207" y="231" on="1"/>
+        <pt x="146" y="208" on="0"/>
+        <pt x="87" y="149" on="0"/>
+        <pt x="87" y="110" on="1"/>
+        <pt x="87" y="64" on="0"/>
+        <pt x="142" y="13" on="0"/>
+        <pt x="188" y="13" on="1"/>
+        <pt x="231" y="13" on="0"/>
+        <pt x="309" y="61" on="0"/>
+        <pt x="356" y="113" on="1"/>
+        <pt x="367" y="125" on="1"/>
+        <pt x="367" y="100" on="1"/>
+        <pt x="358" y="90" on="1"/>
+        <pt x="312" y="38" on="0"/>
+        <pt x="232" y="-13" on="0"/>
+        <pt x="180" y="-13" on="1"/>
+        <pt x="127" y="-13" on="0"/>
+        <pt x="57" y="48" on="0"/>
+      </contour>
+      <contour>
+        <pt x="355" y="99" on="1"/>
+        <pt x="355" y="324" on="1"/>
+        <pt x="355" y="407" on="0"/>
+        <pt x="294" y="463" on="0"/>
+        <pt x="234" y="463" on="1"/>
+        <pt x="199" y="463" on="0"/>
+        <pt x="137" y="440" on="0"/>
+        <pt x="107" y="415" on="1"/>
+        <pt x="133" y="441" on="1"/>
+        <pt x="115" y="373" on="1"/>
+        <pt x="112" y="361" on="0"/>
+        <pt x="100" y="351" on="0"/>
+        <pt x="91" y="351" on="1"/>
+        <pt x="74" y="351" on="0"/>
+        <pt x="69" y="370" on="1"/>
+        <pt x="85" y="426" on="0"/>
+        <pt x="173" y="483" on="0"/>
+        <pt x="236" y="483" on="1"/>
+        <pt x="309" y="483" on="0"/>
+        <pt x="383" y="415" on="0"/>
+        <pt x="383" y="339" on="1"/>
+        <pt x="383" y="112" on="1"/>
+        <pt x="383" y="53" on="0"/>
+        <pt x="409" y="11" on="0"/>
+        <pt x="433" y="11" on="1"/>
+        <pt x="443" y="11" on="0"/>
+        <pt x="462" y="18" on="0"/>
+        <pt x="476" y="28" on="1"/>
+        <pt x="486" y="14" on="1"/>
+        <pt x="459" y="-13" on="0"/>
+        <pt x="425" y="-13" on="1"/>
+        <pt x="389" y="-13" on="0"/>
+        <pt x="358" y="45" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test Family
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      Version 1.001;ADBO;Test Family Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test Family
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.001
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestFamily-Master0
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Frank Grießhammer
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Master 0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name="dollar.nostroke"/>
+      <psName name="uni0020"/>
+      <psName name="uni0041"/>
+      <psName name="uni0061"/>
+      <psName name="uni0024"/>
+      <psName name="uni0024.nostroke"/>
+    </extraNames>
+  </post>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master1.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master1.ttx
new file mode 100644
index 0000000..8d5bace
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master1.ttx
@@ -0,0 +1,520 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.7">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="uni0020"/>
+    <GlyphID id="2" name="uni0041"/>
+    <GlyphID id="3" name="uni0061"/>
+    <GlyphID id="4" name="uni0024"/>
+    <GlyphID id="5" name="uni0024.nostroke"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.001"/>
+    <checkSumAdjustment value="0xd723fbc6"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Feb 28 16:48:24 2017"/>
+    <modified value="Tue Feb 28 16:48:24 2017"/>
+    <xMin value="5"/>
+    <yMin value="-115"/>
+    <xMax value="653"/>
+    <yMax value="750"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="918"/>
+    <descent value="-335"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="663"/>
+    <minLeftSideBearing value="5"/>
+    <minRightSideBearing value="7"/>
+    <xMaxExtent value="653"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="5"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="62"/>
+    <maxContours value="4"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="1"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="1"/>
+    <maxSizeOfInstructions value="5"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="506"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000100"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="284"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="4"/>
+      <bWeight value="6"/>
+      <bProportion value="3"/>
+      <bContrast value="5"/>
+      <bStrokeVariation value="4"/>
+      <bArmStyle value="5"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000011"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBO"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="97"/>
+    <sTypoAscender value="730"/>
+    <sTypoDescender value="-270"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="918"/>
+    <usWinDescent value="335"/>
+    <ulCodePageRange1 value="00100000 00000000 00000000 00000011"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="474"/>
+    <sCapHeight value="677"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="640" lsb="80"/>
+    <mtx name="uni0020" width="234" lsb="0"/>
+    <mtx name="uni0024" width="497" lsb="51"/>
+    <mtx name="uni0024.nostroke" width="497" lsb="51"/>
+    <mtx name="uni0041" width="663" lsb="5"/>
+    <mtx name="uni0061" width="508" lsb="46"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="uni0020"/><!-- SPACE -->
+      <map code="0x24" name="uni0024"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="uni0041"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="uni0061"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="uni0020"/><!-- SPACE -->
+      <map code="0x24" name="uni0024"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="uni0041"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="uni0061"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+  </cmap>
+
+  <fpgm>
+    <assembly>
+      PUSHB[ ]	/* 1 value pushed */
+      0
+      FDEF[ ]	/* FunctionDefinition */
+        POP[ ]	/* PopTopStack */
+      ENDF[ ]	/* EndFunctionDefinition */
+    </assembly>
+  </fpgm>
+
+  <cvt>
+    <cv index="0" value="474"/>
+    <cv index="1" value="677"/>
+  </cvt>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="80" yMin="0" xMax="560" yMax="670">
+      <contour>
+        <pt x="80" y="0" on="1"/>
+        <pt x="500" y="670" on="1"/>
+        <pt x="560" y="670" on="1"/>
+        <pt x="140" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="560" y="0" on="1"/>
+        <pt x="500" y="0" on="1"/>
+        <pt x="80" y="670" on="1"/>
+        <pt x="140" y="670" on="1"/>
+      </contour>
+      <contour>
+        <pt x="140" y="50" on="1"/>
+        <pt x="500" y="50" on="1"/>
+        <pt x="500" y="620" on="1"/>
+        <pt x="140" y="620" on="1"/>
+      </contour>
+      <contour>
+        <pt x="80" y="0" on="1"/>
+        <pt x="80" y="670" on="1"/>
+        <pt x="560" y="670" on="1"/>
+        <pt x="560" y="0" on="1"/>
+      </contour>
+      <instructions>
+        <assembly>
+          PUSHB[ ]	/* 1 value pushed */
+          0
+          MDAP[0]	/* MoveDirectAbsPt */
+        </assembly>
+      </instructions>
+    </TTGlyph>
+
+    <TTGlyph name="uni0020"/><!-- contains no outline data -->
+
+    <TTGlyph name="uni0024" xMin="51" yMin="-115" xMax="461" yMax="750">
+      <contour>
+        <pt x="248" y="35" on="1"/>
+        <pt x="310" y="35" on="0"/>
+        <pt x="383" y="96" on="0"/>
+        <pt x="383" y="151" on="1"/>
+        <pt x="383" y="198" on="0"/>
+        <pt x="326" y="252" on="0"/>
+        <pt x="262" y="274" on="1"/>
+        <pt x="225" y="287" on="1"/>
+        <pt x="146" y="315" on="0"/>
+        <pt x="58" y="403" on="0"/>
+        <pt x="58" y="478" on="1"/>
+        <pt x="58" y="554" on="0"/>
+        <pt x="170" y="660" on="0"/>
+        <pt x="256" y="660" on="1"/>
+        <pt x="332" y="660" on="0"/>
+        <pt x="438" y="578" on="0"/>
+        <pt x="443" y="514" on="1"/>
+        <pt x="437" y="498" on="0"/>
+        <pt x="413" y="483" on="0"/>
+        <pt x="399" y="483" on="1"/>
+        <pt x="382" y="483" on="0"/>
+        <pt x="349" y="510" on="0"/>
+        <pt x="340" y="549" on="1"/>
+        <pt x="323" y="625" on="1"/>
+        <pt x="375" y="589" on="1"/>
+        <pt x="344" y="602" on="0"/>
+        <pt x="290" y="611" on="0"/>
+        <pt x="267" y="611" on="1"/>
+        <pt x="212" y="611" on="0"/>
+        <pt x="136" y="555" on="0"/>
+        <pt x="136" y="499" on="1"/>
+        <pt x="136" y="451" on="0"/>
+        <pt x="194" y="394" on="0"/>
+        <pt x="247" y="377" on="1"/>
+        <pt x="285" y="364" on="1"/>
+        <pt x="386" y="329" on="0"/>
+        <pt x="461" y="232" on="0"/>
+        <pt x="461" y="170" on="1"/>
+        <pt x="461" y="90" on="0"/>
+        <pt x="345" y="-13" on="0"/>
+        <pt x="243" y="-13" on="1"/>
+        <pt x="163" y="-13" on="0"/>
+        <pt x="54" y="68" on="0"/>
+        <pt x="51" y="133" on="1"/>
+        <pt x="58" y="148" on="0"/>
+        <pt x="80" y="164" on="0"/>
+        <pt x="96" y="164" on="1"/>
+        <pt x="114" y="164" on="0"/>
+        <pt x="145" y="138" on="0"/>
+        <pt x="154" y="98" on="1"/>
+        <pt x="171" y="22" on="1"/>
+        <pt x="118" y="58" on="1"/>
+        <pt x="152" y="44" on="0"/>
+        <pt x="219" y="35" on="0"/>
+      </contour>
+      <contour>
+        <pt x="279" y="322" on="1"/>
+        <pt x="239" y="322" on="1"/>
+        <pt x="239" y="750" on="1"/>
+        <pt x="279" y="750" on="1"/>
+      </contour>
+      <contour>
+        <pt x="238" y="-115" on="1"/>
+        <pt x="238" y="322" on="1"/>
+        <pt x="278" y="322" on="1"/>
+        <pt x="278" y="-115" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0024.nostroke" xMin="51" yMin="-115" xMax="461" yMax="750">
+      <contour>
+        <pt x="248" y="35" on="1"/>
+        <pt x="310" y="35" on="0"/>
+        <pt x="383" y="96" on="0"/>
+        <pt x="383" y="151" on="1"/>
+        <pt x="383" y="198" on="0"/>
+        <pt x="326" y="252" on="0"/>
+        <pt x="262" y="274" on="1"/>
+        <pt x="225" y="287" on="1"/>
+        <pt x="146" y="315" on="0"/>
+        <pt x="58" y="403" on="0"/>
+        <pt x="58" y="478" on="1"/>
+        <pt x="58" y="554" on="0"/>
+        <pt x="170" y="660" on="0"/>
+        <pt x="256" y="660" on="1"/>
+        <pt x="332" y="660" on="0"/>
+        <pt x="438" y="578" on="0"/>
+        <pt x="443" y="514" on="1"/>
+        <pt x="437" y="498" on="0"/>
+        <pt x="413" y="483" on="0"/>
+        <pt x="399" y="483" on="1"/>
+        <pt x="382" y="483" on="0"/>
+        <pt x="349" y="510" on="0"/>
+        <pt x="340" y="549" on="1"/>
+        <pt x="323" y="625" on="1"/>
+        <pt x="375" y="589" on="1"/>
+        <pt x="344" y="602" on="0"/>
+        <pt x="290" y="611" on="0"/>
+        <pt x="267" y="611" on="1"/>
+        <pt x="212" y="611" on="0"/>
+        <pt x="136" y="555" on="0"/>
+        <pt x="136" y="499" on="1"/>
+        <pt x="136" y="451" on="0"/>
+        <pt x="194" y="394" on="0"/>
+        <pt x="247" y="377" on="1"/>
+        <pt x="285" y="364" on="1"/>
+        <pt x="386" y="329" on="0"/>
+        <pt x="461" y="232" on="0"/>
+        <pt x="461" y="170" on="1"/>
+        <pt x="461" y="90" on="0"/>
+        <pt x="345" y="-13" on="0"/>
+        <pt x="243" y="-13" on="1"/>
+        <pt x="163" y="-13" on="0"/>
+        <pt x="54" y="68" on="0"/>
+        <pt x="51" y="133" on="1"/>
+        <pt x="58" y="148" on="0"/>
+        <pt x="80" y="164" on="0"/>
+        <pt x="96" y="164" on="1"/>
+        <pt x="114" y="164" on="0"/>
+        <pt x="145" y="138" on="0"/>
+        <pt x="154" y="98" on="1"/>
+        <pt x="171" y="22" on="1"/>
+        <pt x="118" y="58" on="1"/>
+        <pt x="152" y="44" on="0"/>
+        <pt x="219" y="35" on="0"/>
+      </contour>
+      <contour>
+        <pt x="279" y="635" on="1"/>
+        <pt x="239" y="635" on="1"/>
+        <pt x="239" y="750" on="1"/>
+        <pt x="279" y="750" on="1"/>
+      </contour>
+      <contour>
+        <pt x="238" y="-115" on="1"/>
+        <pt x="238" y="12" on="1"/>
+        <pt x="278" y="12" on="1"/>
+        <pt x="278" y="-115" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0041" xMin="5" yMin="0" xMax="653" yMax="675">
+      <contour>
+        <pt x="5" y="0" on="1"/>
+        <pt x="5" y="40" on="1"/>
+        <pt x="105" y="55" on="1"/>
+        <pt x="125" y="55" on="1"/>
+        <pt x="235" y="40" on="1"/>
+        <pt x="235" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="71" y="0" on="1"/>
+        <pt x="303" y="675" on="1"/>
+        <pt x="363" y="675" on="1"/>
+        <pt x="593" y="0" on="1"/>
+        <pt x="500" y="0" on="1"/>
+        <pt x="299" y="599" on="1"/>
+        <pt x="322" y="599" on="1"/>
+        <pt x="118" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="170" y="219" on="1"/>
+        <pt x="186" y="265" on="1"/>
+        <pt x="456" y="265" on="1"/>
+        <pt x="472" y="219" on="1"/>
+      </contour>
+      <contour>
+        <pt x="383" y="0" on="1"/>
+        <pt x="383" y="40" on="1"/>
+        <pt x="509" y="55" on="1"/>
+        <pt x="529" y="55" on="1"/>
+        <pt x="653" y="40" on="1"/>
+        <pt x="653" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0061" xMin="46" yMin="-13" xMax="501" yMax="487">
+      <contour>
+        <pt x="46" y="112" on="1"/>
+        <pt x="46" y="154" on="0"/>
+        <pt x="110" y="225" on="0"/>
+        <pt x="210" y="262" on="1"/>
+        <pt x="242" y="273" on="0"/>
+        <pt x="328" y="297" on="0"/>
+        <pt x="365" y="304" on="1"/>
+        <pt x="365" y="268" on="1"/>
+        <pt x="331" y="261" on="0"/>
+        <pt x="254" y="237" on="0"/>
+        <pt x="231" y="228" on="1"/>
+        <pt x="164" y="202" on="0"/>
+        <pt x="131" y="148" on="0"/>
+        <pt x="131" y="126" on="1"/>
+        <pt x="131" y="86" on="0"/>
+        <pt x="178" y="52" on="0"/>
+        <pt x="212" y="52" on="1"/>
+        <pt x="238" y="52" on="0"/>
+        <pt x="283" y="76" on="0"/>
+        <pt x="330" y="110" on="1"/>
+        <pt x="350" y="125" on="1"/>
+        <pt x="364" y="104" on="1"/>
+        <pt x="335" y="75" on="1"/>
+        <pt x="290" y="30" on="0"/>
+        <pt x="226" y="-13" on="0"/>
+        <pt x="180" y="-13" on="1"/>
+        <pt x="125" y="-13" on="0"/>
+        <pt x="46" y="50" on="0"/>
+      </contour>
+      <contour>
+        <pt x="325" y="92" on="1"/>
+        <pt x="325" y="320" on="1"/>
+        <pt x="325" y="394" on="0"/>
+        <pt x="280" y="442" on="0"/>
+        <pt x="231" y="442" on="1"/>
+        <pt x="214" y="442" on="0"/>
+        <pt x="169" y="435" on="0"/>
+        <pt x="141" y="424" on="1"/>
+        <pt x="181" y="455" on="1"/>
+        <pt x="155" y="369" on="1"/>
+        <pt x="148" y="347" on="0"/>
+        <pt x="124" y="324" on="0"/>
+        <pt x="104" y="324" on="1"/>
+        <pt x="62" y="324" on="0"/>
+        <pt x="59" y="364" on="1"/>
+        <pt x="73" y="421" on="0"/>
+        <pt x="177" y="487" on="0"/>
+        <pt x="252" y="487" on="1"/>
+        <pt x="329" y="487" on="0"/>
+        <pt x="405" y="408" on="0"/>
+        <pt x="405" y="314" on="1"/>
+        <pt x="405" y="102" on="1"/>
+        <pt x="405" y="68" on="0"/>
+        <pt x="425" y="41" on="0"/>
+        <pt x="442" y="41" on="1"/>
+        <pt x="455" y="41" on="0"/>
+        <pt x="473" y="53" on="0"/>
+        <pt x="481" y="63" on="1"/>
+        <pt x="501" y="41" on="1"/>
+        <pt x="469" y="-10" on="0"/>
+        <pt x="416" y="-10" on="1"/>
+        <pt x="375" y="-10" on="0"/>
+        <pt x="325" y="46" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test Family
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      Version 1.001;ADBO;Test Family Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test Family
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.001
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestFamily-Master1
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Frank Grießhammer
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Master 1
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name="dollar.nostroke"/>
+      <psName name="uni0020"/>
+      <psName name="uni0041"/>
+      <psName name="uni0061"/>
+      <psName name="uni0024"/>
+      <psName name="uni0024.nostroke"/>
+    </extraNames>
+  </post>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master2.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master2.ttx
new file mode 100644
index 0000000..3e20fac
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master2.ttx
@@ -0,0 +1,504 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.7">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="uni0020"/>
+    <GlyphID id="2" name="uni0041"/>
+    <GlyphID id="3" name="uni0061"/>
+    <GlyphID id="4" name="uni0024"/>
+    <GlyphID id="5" name="uni0024.nostroke"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.001"/>
+    <checkSumAdjustment value="0x4b3253f0"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Feb 28 16:48:24 2017"/>
+    <modified value="Tue Feb 28 16:48:24 2017"/>
+    <xMin value="10"/>
+    <yMin value="-115"/>
+    <xMax value="665"/>
+    <yMax value="731"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="918"/>
+    <descent value="-335"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="680"/>
+    <minLeftSideBearing value="10"/>
+    <minRightSideBearing value="-8"/>
+    <xMaxExtent value="665"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="5"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="62"/>
+    <maxContours value="4"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="531"/>
+    <usWeightClass value="900"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000100"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="292"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="4"/>
+      <bWeight value="9"/>
+      <bProportion value="3"/>
+      <bContrast value="5"/>
+      <bStrokeVariation value="4"/>
+      <bArmStyle value="5"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000011"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBO"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="97"/>
+    <sTypoAscender value="730"/>
+    <sTypoDescender value="-270"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="918"/>
+    <usWinDescent value="335"/>
+    <ulCodePageRange1 value="00100000 00000000 00000000 00000011"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="487"/>
+    <sCapHeight value="677"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="640" lsb="80"/>
+    <mtx name="uni0020" width="206" lsb="0"/>
+    <mtx name="uni0024" width="560" lsb="51"/>
+    <mtx name="uni0024.nostroke" width="560" lsb="51"/>
+    <mtx name="uni0041" width="680" lsb="10"/>
+    <mtx name="uni0061" width="540" lsb="25"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="uni0020"/><!-- SPACE -->
+      <map code="0x24" name="uni0024"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="uni0041"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="uni0061"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="uni0020"/><!-- SPACE -->
+      <map code="0x24" name="uni0024"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="uni0041"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="uni0061"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+  </cmap>
+
+  <cvt>
+    <cv index="0" value="487"/>
+    <cv index="1" value="677"/>
+  </cvt>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="80" yMin="0" xMax="560" yMax="652">
+      <contour>
+        <pt x="80" y="0" on="1"/>
+        <pt x="480" y="652" on="1"/>
+        <pt x="560" y="652" on="1"/>
+        <pt x="160" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="560" y="0" on="1"/>
+        <pt x="480" y="0" on="1"/>
+        <pt x="80" y="652" on="1"/>
+        <pt x="160" y="652" on="1"/>
+      </contour>
+      <contour>
+        <pt x="150" y="60" on="1"/>
+        <pt x="490" y="60" on="1"/>
+        <pt x="490" y="592" on="1"/>
+        <pt x="150" y="592" on="1"/>
+      </contour>
+      <contour>
+        <pt x="80" y="0" on="1"/>
+        <pt x="80" y="652" on="1"/>
+        <pt x="560" y="652" on="1"/>
+        <pt x="560" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0020"/><!-- contains no outline data -->
+
+    <TTGlyph name="uni0024" xMin="51" yMin="-115" xMax="523" yMax="731">
+      <contour>
+        <pt x="260" y="39" on="1"/>
+        <pt x="310" y="39" on="0"/>
+        <pt x="365" y="86" on="0"/>
+        <pt x="365" y="123" on="1"/>
+        <pt x="365" y="170" on="0"/>
+        <pt x="319" y="212" on="0"/>
+        <pt x="265" y="235" on="1"/>
+        <pt x="234" y="248" on="1"/>
+        <pt x="146" y="285" on="0"/>
+        <pt x="53" y="378" on="0"/>
+        <pt x="53" y="457" on="1"/>
+        <pt x="53" y="534" on="0"/>
+        <pt x="178" y="640" on="0"/>
+        <pt x="296" y="640" on="1"/>
+        <pt x="387" y="640" on="0"/>
+        <pt x="501" y="567" on="0"/>
+        <pt x="501" y="504" on="1"/>
+        <pt x="497" y="473" on="0"/>
+        <pt x="460" y="448" on="0"/>
+        <pt x="441" y="448" on="1"/>
+        <pt x="412" y="448" on="0"/>
+        <pt x="362" y="481" on="0"/>
+        <pt x="354" y="537" on="1"/>
+        <pt x="344" y="604" on="1"/>
+        <pt x="429" y="557" on="1"/>
+        <pt x="390" y="575" on="0"/>
+        <pt x="340" y="585" on="0"/>
+        <pt x="308" y="585" on="1"/>
+        <pt x="262" y="585" on="0"/>
+        <pt x="204" y="544" on="0"/>
+        <pt x="204" y="497" on="1"/>
+        <pt x="204" y="461" on="0"/>
+        <pt x="250" y="425" on="0"/>
+        <pt x="297" y="406" on="1"/>
+        <pt x="329" y="393" on="1"/>
+        <pt x="432" y="351" on="0"/>
+        <pt x="523" y="256" on="0"/>
+        <pt x="523" y="176" on="1"/>
+        <pt x="523" y="95" on="0"/>
+        <pt x="383" y="-16" on="0"/>
+        <pt x="261" y="-16" on="1"/>
+        <pt x="157" y="-16" on="0"/>
+        <pt x="51" y="56" on="0"/>
+        <pt x="51" y="124" on="1"/>
+        <pt x="62" y="152" on="0"/>
+        <pt x="98" y="176" on="0"/>
+        <pt x="118" y="176" on="1"/>
+        <pt x="150" y="176" on="0"/>
+        <pt x="184" y="143" on="0"/>
+        <pt x="188" y="104" on="1"/>
+        <pt x="196" y="19" on="1"/>
+        <pt x="118" y="74" on="1"/>
+        <pt x="155" y="56" on="0"/>
+        <pt x="211" y="39" on="0"/>
+      </contour>
+      <contour>
+        <pt x="325" y="314" on="1"/>
+        <pt x="254" y="314" on="1"/>
+        <pt x="254" y="731" on="1"/>
+        <pt x="325" y="731" on="1"/>
+      </contour>
+      <contour>
+        <pt x="246" y="-115" on="1"/>
+        <pt x="246" y="314" on="1"/>
+        <pt x="317" y="314" on="1"/>
+        <pt x="317" y="-115" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0024.nostroke" xMin="51" yMin="-115" xMax="523" yMax="731">
+      <contour>
+        <pt x="260" y="39" on="1"/>
+        <pt x="304" y="39" on="0"/>
+        <pt x="359" y="89" on="0"/>
+        <pt x="359" y="126" on="1"/>
+        <pt x="359" y="167" on="0"/>
+        <pt x="314" y="211" on="0"/>
+        <pt x="265" y="235" on="1"/>
+        <pt x="234" y="250" on="1"/>
+        <pt x="150" y="290" on="0"/>
+        <pt x="57" y="379" on="0"/>
+        <pt x="57" y="457" on="1"/>
+        <pt x="57" y="534" on="0"/>
+        <pt x="180" y="640" on="0"/>
+        <pt x="296" y="640" on="1"/>
+        <pt x="387" y="640" on="0"/>
+        <pt x="501" y="567" on="0"/>
+        <pt x="501" y="504" on="1"/>
+        <pt x="497" y="473" on="0"/>
+        <pt x="460" y="448" on="0"/>
+        <pt x="441" y="448" on="1"/>
+        <pt x="412" y="448" on="0"/>
+        <pt x="362" y="481" on="0"/>
+        <pt x="354" y="537" on="1"/>
+        <pt x="344" y="604" on="1"/>
+        <pt x="429" y="557" on="1"/>
+        <pt x="390" y="575" on="0"/>
+        <pt x="339" y="585" on="0"/>
+        <pt x="307" y="585" on="1"/>
+        <pt x="267" y="585" on="0"/>
+        <pt x="216" y="545" on="0"/>
+        <pt x="216" y="500" on="1"/>
+        <pt x="216" y="466" on="0"/>
+        <pt x="256" y="428" on="0"/>
+        <pt x="297" y="408" on="1"/>
+        <pt x="329" y="393" on="1"/>
+        <pt x="430" y="346" on="0"/>
+        <pt x="523" y="256" on="0"/>
+        <pt x="523" y="176" on="1"/>
+        <pt x="523" y="95" on="0"/>
+        <pt x="383" y="-16" on="0"/>
+        <pt x="261" y="-16" on="1"/>
+        <pt x="157" y="-16" on="0"/>
+        <pt x="51" y="56" on="0"/>
+        <pt x="51" y="124" on="1"/>
+        <pt x="62" y="152" on="0"/>
+        <pt x="98" y="176" on="0"/>
+        <pt x="118" y="176" on="1"/>
+        <pt x="150" y="176" on="0"/>
+        <pt x="184" y="143" on="0"/>
+        <pt x="188" y="104" on="1"/>
+        <pt x="196" y="19" on="1"/>
+        <pt x="118" y="74" on="1"/>
+        <pt x="155" y="56" on="0"/>
+        <pt x="211" y="39" on="0"/>
+      </contour>
+      <contour>
+        <pt x="325" y="612" on="1"/>
+        <pt x="254" y="612" on="1"/>
+        <pt x="254" y="731" on="1"/>
+        <pt x="325" y="731" on="1"/>
+      </contour>
+      <contour>
+        <pt x="256" y="-115" on="1"/>
+        <pt x="256" y="14" on="1"/>
+        <pt x="327" y="14" on="1"/>
+        <pt x="327" y="-115" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0041" xMin="10" yMin="0" xMax="665" yMax="652">
+      <contour>
+        <pt x="10" y="0" on="1"/>
+        <pt x="10" y="59" on="1"/>
+        <pt x="114" y="74" on="1"/>
+        <pt x="131" y="74" on="1"/>
+        <pt x="220" y="59" on="1"/>
+        <pt x="220" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="65" y="0" on="1"/>
+        <pt x="289" y="652" on="1"/>
+        <pt x="409" y="652" on="1"/>
+        <pt x="632" y="0" on="1"/>
+        <pt x="431" y="0" on="1"/>
+        <pt x="272" y="513" on="1"/>
+        <pt x="315" y="583" on="1"/>
+        <pt x="129" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="168" y="180" on="1"/>
+        <pt x="185" y="243" on="1"/>
+        <pt x="455" y="243" on="1"/>
+        <pt x="480" y="180" on="1"/>
+      </contour>
+      <contour>
+        <pt x="342" y="0" on="1"/>
+        <pt x="342" y="56" on="1"/>
+        <pt x="450" y="71" on="1"/>
+        <pt x="535" y="71" on="1"/>
+        <pt x="665" y="56" on="1"/>
+        <pt x="665" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0061" xMin="25" yMin="-16" xMax="548" yMax="503">
+      <contour>
+        <pt x="25" y="113" on="1"/>
+        <pt x="25" y="171" on="0"/>
+        <pt x="108" y="253" on="0"/>
+        <pt x="230" y="285" on="1"/>
+        <pt x="261" y="293" on="0"/>
+        <pt x="356" y="318" on="0"/>
+        <pt x="391" y="327" on="1"/>
+        <pt x="391" y="283" on="1"/>
+        <pt x="355" y="273" on="0"/>
+        <pt x="284" y="254" on="0"/>
+        <pt x="262" y="243" on="1"/>
+        <pt x="241" y="233" on="0"/>
+        <pt x="197" y="184" on="0"/>
+        <pt x="197" y="144" on="1"/>
+        <pt x="197" y="107" on="0"/>
+        <pt x="227" y="71" on="0"/>
+        <pt x="249" y="71" on="1"/>
+        <pt x="259" y="71" on="0"/>
+        <pt x="281" y="81" on="0"/>
+        <pt x="296" y="92" on="1"/>
+        <pt x="344" y="128" on="1"/>
+        <pt x="353" y="116" on="1"/>
+        <pt x="306" y="64" on="1"/>
+        <pt x="273" y="28" on="0"/>
+        <pt x="213" y="-16" on="0"/>
+        <pt x="155" y="-16" on="1"/>
+        <pt x="96" y="-16" on="0"/>
+        <pt x="25" y="52" on="0"/>
+      </contour>
+      <contour>
+        <pt x="291" y="78" on="1"/>
+        <pt x="291" y="337" on="1"/>
+        <pt x="291" y="401" on="0"/>
+        <pt x="262" y="449" on="0"/>
+        <pt x="215" y="449" on="1"/>
+        <pt x="196" y="449" on="0"/>
+        <pt x="154" y="444" on="0"/>
+        <pt x="120" y="436" on="1"/>
+        <pt x="200" y="478" on="1"/>
+        <pt x="200" y="415" on="1"/>
+        <pt x="200" y="354" on="0"/>
+        <pt x="150" y="303" on="0"/>
+        <pt x="118" y="303" on="1"/>
+        <pt x="57" y="303" on="0"/>
+        <pt x="42" y="357" on="1"/>
+        <pt x="42" y="422" on="0"/>
+        <pt x="165" y="503" on="0"/>
+        <pt x="286" y="503" on="1"/>
+        <pt x="390" y="503" on="0"/>
+        <pt x="475" y="412" on="0"/>
+        <pt x="475" y="309" on="1"/>
+        <pt x="475" y="80" on="1"/>
+        <pt x="475" y="72" on="0"/>
+        <pt x="484" y="63" on="0"/>
+        <pt x="492" y="63" on="1"/>
+        <pt x="498" y="63" on="0"/>
+        <pt x="510" y="72" on="0"/>
+        <pt x="519" y="85" on="1"/>
+        <pt x="548" y="69" on="1"/>
+        <pt x="515" y="-16" on="0"/>
+        <pt x="414" y="-16" on="1"/>
+        <pt x="359" y="-16" on="0"/>
+        <pt x="300" y="33" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test Family
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      Version 1.001;ADBO;Test Family Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test Family
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.001
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestFamily-Master2
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Frank Grießhammer
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Master 2
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name="dollar.nostroke"/>
+      <psName name="uni0020"/>
+      <psName name="uni0041"/>
+      <psName name="uni0061"/>
+      <psName name="uni0024"/>
+      <psName name="uni0024.nostroke"/>
+    </extraNames>
+  </post>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master3.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master3.ttx
new file mode 100644
index 0000000..e1d0375
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master3.ttx
@@ -0,0 +1,504 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.7">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="uni0020"/>
+    <GlyphID id="2" name="uni0041"/>
+    <GlyphID id="3" name="uni0061"/>
+    <GlyphID id="4" name="uni0024"/>
+    <GlyphID id="5" name="uni0024.nostroke"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.001"/>
+    <checkSumAdjustment value="0x47b1cebe"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Feb 28 16:48:24 2017"/>
+    <modified value="Tue Feb 28 16:48:24 2017"/>
+    <xMin value="15"/>
+    <yMin value="-115"/>
+    <xMax value="665"/>
+    <yMax value="731"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="918"/>
+    <descent value="-335"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="680"/>
+    <minLeftSideBearing value="15"/>
+    <minRightSideBearing value="-8"/>
+    <xMaxExtent value="665"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="5"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="62"/>
+    <maxContours value="4"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="531"/>
+    <usWeightClass value="900"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000100"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="292"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="4"/>
+      <bWeight value="9"/>
+      <bProportion value="3"/>
+      <bContrast value="5"/>
+      <bStrokeVariation value="4"/>
+      <bArmStyle value="5"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000011"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBO"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="97"/>
+    <sTypoAscender value="730"/>
+    <sTypoDescender value="-270"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="918"/>
+    <usWinDescent value="335"/>
+    <ulCodePageRange1 value="00100000 00000000 00000000 00000011"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="487"/>
+    <sCapHeight value="677"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="640" lsb="80"/>
+    <mtx name="uni0020" width="206" lsb="0"/>
+    <mtx name="uni0024" width="560" lsb="51"/>
+    <mtx name="uni0024.nostroke" width="560" lsb="51"/>
+    <mtx name="uni0041" width="680" lsb="15"/>
+    <mtx name="uni0061" width="540" lsb="25"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="uni0020"/><!-- SPACE -->
+      <map code="0x24" name="uni0024"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="uni0041"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="uni0061"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="uni0020"/><!-- SPACE -->
+      <map code="0x24" name="uni0024"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="uni0041"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="uni0061"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+  </cmap>
+
+  <cvt>
+    <cv index="0" value="487"/>
+    <cv index="1" value="677"/>
+  </cvt>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="80" yMin="0" xMax="560" yMax="652">
+      <contour>
+        <pt x="90" y="0" on="1"/>
+        <pt x="510" y="652" on="1"/>
+        <pt x="550" y="652" on="1"/>
+        <pt x="130" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="550" y="0" on="1"/>
+        <pt x="510" y="0" on="1"/>
+        <pt x="90" y="652" on="1"/>
+        <pt x="130" y="652" on="1"/>
+      </contour>
+      <contour>
+        <pt x="150" y="60" on="1"/>
+        <pt x="490" y="60" on="1"/>
+        <pt x="490" y="592" on="1"/>
+        <pt x="150" y="592" on="1"/>
+      </contour>
+      <contour>
+        <pt x="80" y="0" on="1"/>
+        <pt x="80" y="652" on="1"/>
+        <pt x="560" y="652" on="1"/>
+        <pt x="560" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0020"/><!-- contains no outline data -->
+
+    <TTGlyph name="uni0024" xMin="51" yMin="-115" xMax="523" yMax="731">
+      <contour>
+        <pt x="260" y="19" on="1"/>
+        <pt x="308" y="19" on="0"/>
+        <pt x="365" y="80" on="0"/>
+        <pt x="365" y="118" on="1"/>
+        <pt x="365" y="169" on="0"/>
+        <pt x="319" y="212" on="0"/>
+        <pt x="265" y="235" on="1"/>
+        <pt x="234" y="248" on="1"/>
+        <pt x="146" y="285" on="0"/>
+        <pt x="53" y="378" on="0"/>
+        <pt x="53" y="457" on="1"/>
+        <pt x="53" y="534" on="0"/>
+        <pt x="178" y="640" on="0"/>
+        <pt x="296" y="640" on="1"/>
+        <pt x="393" y="640" on="0"/>
+        <pt x="501" y="567" on="0"/>
+        <pt x="501" y="504" on="1"/>
+        <pt x="496" y="474" on="0"/>
+        <pt x="460" y="448" on="0"/>
+        <pt x="440" y="448" on="1"/>
+        <pt x="412" y="448" on="0"/>
+        <pt x="362" y="482" on="0"/>
+        <pt x="354" y="537" on="1"/>
+        <pt x="341" y="629" on="1"/>
+        <pt x="429" y="577" on="1"/>
+        <pt x="390" y="595" on="0"/>
+        <pt x="340" y="605" on="0"/>
+        <pt x="308" y="605" on="1"/>
+        <pt x="261" y="605" on="0"/>
+        <pt x="204" y="548" on="0"/>
+        <pt x="204" y="505" on="1"/>
+        <pt x="204" y="463" on="0"/>
+        <pt x="250" y="425" on="0"/>
+        <pt x="297" y="406" on="1"/>
+        <pt x="329" y="393" on="1"/>
+        <pt x="432" y="351" on="0"/>
+        <pt x="523" y="258" on="0"/>
+        <pt x="523" y="178" on="1"/>
+        <pt x="523" y="97" on="0"/>
+        <pt x="383" y="-16" on="0"/>
+        <pt x="261" y="-16" on="1"/>
+        <pt x="157" y="-16" on="0"/>
+        <pt x="51" y="56" on="0"/>
+        <pt x="51" y="124" on="1"/>
+        <pt x="62" y="152" on="0"/>
+        <pt x="98" y="176" on="0"/>
+        <pt x="118" y="176" on="1"/>
+        <pt x="150" y="176" on="0"/>
+        <pt x="185" y="143" on="0"/>
+        <pt x="188" y="104" on="1"/>
+        <pt x="196" y="4" on="1"/>
+        <pt x="118" y="54" on="1"/>
+        <pt x="155" y="36" on="0"/>
+        <pt x="211" y="19" on="0"/>
+      </contour>
+      <contour>
+        <pt x="315" y="314" on="1"/>
+        <pt x="264" y="314" on="1"/>
+        <pt x="264" y="731" on="1"/>
+        <pt x="315" y="731" on="1"/>
+      </contour>
+      <contour>
+        <pt x="256" y="-115" on="1"/>
+        <pt x="256" y="314" on="1"/>
+        <pt x="307" y="314" on="1"/>
+        <pt x="307" y="-115" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0024.nostroke" xMin="51" yMin="-115" xMax="523" yMax="731">
+      <contour>
+        <pt x="260" y="19" on="1"/>
+        <pt x="301" y="19" on="0"/>
+        <pt x="355" y="73" on="0"/>
+        <pt x="355" y="112" on="1"/>
+        <pt x="355" y="156" on="0"/>
+        <pt x="314" y="211" on="0"/>
+        <pt x="265" y="235" on="1"/>
+        <pt x="234" y="250" on="1"/>
+        <pt x="150" y="290" on="0"/>
+        <pt x="57" y="379" on="0"/>
+        <pt x="57" y="457" on="1"/>
+        <pt x="57" y="534" on="0"/>
+        <pt x="180" y="640" on="0"/>
+        <pt x="296" y="640" on="1"/>
+        <pt x="393" y="640" on="0"/>
+        <pt x="501" y="567" on="0"/>
+        <pt x="501" y="504" on="1"/>
+        <pt x="496" y="474" on="0"/>
+        <pt x="460" y="448" on="0"/>
+        <pt x="440" y="448" on="1"/>
+        <pt x="412" y="448" on="0"/>
+        <pt x="362" y="482" on="0"/>
+        <pt x="354" y="537" on="1"/>
+        <pt x="341" y="629" on="1"/>
+        <pt x="429" y="577" on="1"/>
+        <pt x="390" y="595" on="0"/>
+        <pt x="340" y="605" on="0"/>
+        <pt x="308" y="605" on="1"/>
+        <pt x="267" y="605" on="0"/>
+        <pt x="218" y="550" on="0"/>
+        <pt x="218" y="510" on="1"/>
+        <pt x="218" y="470" on="0"/>
+        <pt x="259" y="426" on="0"/>
+        <pt x="297" y="408" on="1"/>
+        <pt x="329" y="393" on="1"/>
+        <pt x="430" y="346" on="0"/>
+        <pt x="523" y="258" on="0"/>
+        <pt x="523" y="178" on="1"/>
+        <pt x="523" y="97" on="0"/>
+        <pt x="383" y="-16" on="0"/>
+        <pt x="261" y="-16" on="1"/>
+        <pt x="157" y="-16" on="0"/>
+        <pt x="51" y="56" on="0"/>
+        <pt x="51" y="124" on="1"/>
+        <pt x="62" y="152" on="0"/>
+        <pt x="98" y="176" on="0"/>
+        <pt x="118" y="176" on="1"/>
+        <pt x="150" y="176" on="0"/>
+        <pt x="185" y="143" on="0"/>
+        <pt x="188" y="104" on="1"/>
+        <pt x="196" y="4" on="1"/>
+        <pt x="118" y="54" on="1"/>
+        <pt x="155" y="36" on="0"/>
+        <pt x="211" y="19" on="0"/>
+      </contour>
+      <contour>
+        <pt x="315" y="624" on="1"/>
+        <pt x="264" y="624" on="1"/>
+        <pt x="264" y="731" on="1"/>
+        <pt x="315" y="731" on="1"/>
+      </contour>
+      <contour>
+        <pt x="256" y="-115" on="1"/>
+        <pt x="256" y="4" on="1"/>
+        <pt x="307" y="4" on="1"/>
+        <pt x="307" y="-115" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0041" xMin="15" yMin="0" xMax="665" yMax="652">
+      <contour>
+        <pt x="15" y="0" on="1"/>
+        <pt x="15" y="35" on="1"/>
+        <pt x="104" y="50" on="1"/>
+        <pt x="121" y="50" on="1"/>
+        <pt x="215" y="35" on="1"/>
+        <pt x="215" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="70" y="0" on="1"/>
+        <pt x="279" y="652" on="1"/>
+        <pt x="399" y="652" on="1"/>
+        <pt x="622" y="0" on="1"/>
+        <pt x="421" y="0" on="1"/>
+        <pt x="255" y="534" on="1"/>
+        <pt x="288" y="583" on="1"/>
+        <pt x="124" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="167" y="193" on="1"/>
+        <pt x="177" y="229" on="1"/>
+        <pt x="468" y="229" on="1"/>
+        <pt x="478" y="193" on="1"/>
+      </contour>
+      <contour>
+        <pt x="342" y="0" on="1"/>
+        <pt x="342" y="35" on="1"/>
+        <pt x="460" y="50" on="1"/>
+        <pt x="535" y="50" on="1"/>
+        <pt x="665" y="35" on="1"/>
+        <pt x="665" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0061" xMin="25" yMin="-16" xMax="548" yMax="503">
+      <contour>
+        <pt x="25" y="105" on="1"/>
+        <pt x="25" y="170" on="0"/>
+        <pt x="111" y="246" on="0"/>
+        <pt x="230" y="278" on="1"/>
+        <pt x="261" y="286" on="0"/>
+        <pt x="356" y="311" on="0"/>
+        <pt x="391" y="320" on="1"/>
+        <pt x="391" y="295" on="1"/>
+        <pt x="355" y="285" on="0"/>
+        <pt x="291" y="268" on="0"/>
+        <pt x="279" y="263" on="1"/>
+        <pt x="244" y="248" on="0"/>
+        <pt x="197" y="183" on="0"/>
+        <pt x="197" y="129" on="1"/>
+        <pt x="197" y="92" on="0"/>
+        <pt x="225" y="56" on="0"/>
+        <pt x="249" y="56" on="1"/>
+        <pt x="259" y="56" on="0"/>
+        <pt x="281" y="66" on="0"/>
+        <pt x="296" y="80" on="1"/>
+        <pt x="344" y="125" on="1"/>
+        <pt x="353" y="117" on="1"/>
+        <pt x="306" y="64" on="1"/>
+        <pt x="274" y="27" on="0"/>
+        <pt x="213" y="-16" on="0"/>
+        <pt x="155" y="-16" on="1"/>
+        <pt x="96" y="-16" on="0"/>
+        <pt x="25" y="50" on="0"/>
+      </contour>
+      <contour>
+        <pt x="291" y="72" on="1"/>
+        <pt x="291" y="356" on="1"/>
+        <pt x="291" y="420" on="0"/>
+        <pt x="262" y="469" on="0"/>
+        <pt x="215" y="469" on="1"/>
+        <pt x="196" y="469" on="0"/>
+        <pt x="154" y="464" on="0"/>
+        <pt x="120" y="456" on="1"/>
+        <pt x="200" y="478" on="1"/>
+        <pt x="200" y="415" on="1"/>
+        <pt x="200" y="354" on="0"/>
+        <pt x="150" y="304" on="0"/>
+        <pt x="118" y="304" on="1"/>
+        <pt x="57" y="304" on="0"/>
+        <pt x="42" y="358" on="1"/>
+        <pt x="42" y="422" on="0"/>
+        <pt x="165" y="503" on="0"/>
+        <pt x="286" y="503" on="1"/>
+        <pt x="390" y="503" on="0"/>
+        <pt x="475" y="411" on="0"/>
+        <pt x="475" y="308" on="1"/>
+        <pt x="475" y="65" on="1"/>
+        <pt x="475" y="52" on="0"/>
+        <pt x="485" y="41" on="0"/>
+        <pt x="493" y="41" on="1"/>
+        <pt x="501" y="41" on="0"/>
+        <pt x="517" y="56" on="0"/>
+        <pt x="530" y="79" on="1"/>
+        <pt x="548" y="69" on="1"/>
+        <pt x="515" y="-16" on="0"/>
+        <pt x="414" y="-16" on="1"/>
+        <pt x="359" y="-16" on="0"/>
+        <pt x="301" y="32" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test Family
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      Version 1.001;ADBO;Test Family Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test Family
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.001
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestFamily-Master3
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Frank Grießhammer
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Master 3
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name="dollar.nostroke"/>
+      <psName name="uni0020"/>
+      <psName name="uni0041"/>
+      <psName name="uni0061"/>
+      <psName name="uni0024"/>
+      <psName name="uni0024.nostroke"/>
+    </extraNames>
+  </post>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master4.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master4.ttx
new file mode 100644
index 0000000..c0946fd
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master4.ttx
@@ -0,0 +1,504 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.7">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="uni0020"/>
+    <GlyphID id="2" name="uni0041"/>
+    <GlyphID id="3" name="uni0061"/>
+    <GlyphID id="4" name="uni0024"/>
+    <GlyphID id="5" name="uni0024.nostroke"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.001"/>
+    <checkSumAdjustment value="0x93d555b2"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Feb 28 16:48:24 2017"/>
+    <modified value="Tue Feb 28 16:48:24 2017"/>
+    <xMin value="7"/>
+    <yMin value="-115"/>
+    <xMax value="653"/>
+    <yMax value="750"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="918"/>
+    <descent value="-335"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="663"/>
+    <minLeftSideBearing value="7"/>
+    <minRightSideBearing value="7"/>
+    <xMaxExtent value="653"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="5"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="62"/>
+    <maxContours value="4"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="506"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000100"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="284"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="4"/>
+      <bWeight value="6"/>
+      <bProportion value="3"/>
+      <bContrast value="5"/>
+      <bStrokeVariation value="4"/>
+      <bArmStyle value="5"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000011"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBO"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="97"/>
+    <sTypoAscender value="730"/>
+    <sTypoDescender value="-270"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="918"/>
+    <usWinDescent value="335"/>
+    <ulCodePageRange1 value="00100000 00000000 00000000 00000011"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="474"/>
+    <sCapHeight value="677"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="640" lsb="80"/>
+    <mtx name="uni0020" width="234" lsb="0"/>
+    <mtx name="uni0024" width="497" lsb="51"/>
+    <mtx name="uni0024.nostroke" width="497" lsb="51"/>
+    <mtx name="uni0041" width="663" lsb="7"/>
+    <mtx name="uni0061" width="508" lsb="46"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="uni0020"/><!-- SPACE -->
+      <map code="0x24" name="uni0024"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="uni0041"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="uni0061"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="uni0020"/><!-- SPACE -->
+      <map code="0x24" name="uni0024"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="uni0041"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="uni0061"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+  </cmap>
+
+  <cvt>
+    <cv index="0" value="474"/>
+    <cv index="1" value="677"/>
+  </cvt>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="80" yMin="0" xMax="560" yMax="670">
+      <contour>
+        <pt x="84" y="0" on="1"/>
+        <pt x="511" y="670" on="1"/>
+        <pt x="556" y="670" on="1"/>
+        <pt x="129" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="556" y="0" on="1"/>
+        <pt x="511" y="0" on="1"/>
+        <pt x="84" y="670" on="1"/>
+        <pt x="129" y="670" on="1"/>
+      </contour>
+      <contour>
+        <pt x="140" y="50" on="1"/>
+        <pt x="500" y="50" on="1"/>
+        <pt x="500" y="620" on="1"/>
+        <pt x="140" y="620" on="1"/>
+      </contour>
+      <contour>
+        <pt x="80" y="0" on="1"/>
+        <pt x="80" y="670" on="1"/>
+        <pt x="560" y="670" on="1"/>
+        <pt x="560" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0020"/><!-- contains no outline data -->
+
+    <TTGlyph name="uni0024" xMin="51" yMin="-115" xMax="461" yMax="750">
+      <contour>
+        <pt x="248" y="28" on="1"/>
+        <pt x="309" y="28" on="0"/>
+        <pt x="383" y="93" on="0"/>
+        <pt x="383" y="149" on="1"/>
+        <pt x="383" y="198" on="0"/>
+        <pt x="326" y="252" on="0"/>
+        <pt x="262" y="274" on="1"/>
+        <pt x="225" y="287" on="1"/>
+        <pt x="146" y="315" on="0"/>
+        <pt x="58" y="403" on="0"/>
+        <pt x="58" y="478" on="1"/>
+        <pt x="58" y="554" on="0"/>
+        <pt x="170" y="660" on="0"/>
+        <pt x="256" y="660" on="1"/>
+        <pt x="334" y="660" on="0"/>
+        <pt x="438" y="578" on="0"/>
+        <pt x="443" y="514" on="1"/>
+        <pt x="436" y="498" on="0"/>
+        <pt x="413" y="483" on="0"/>
+        <pt x="399" y="483" on="1"/>
+        <pt x="382" y="483" on="0"/>
+        <pt x="349" y="510" on="0"/>
+        <pt x="340" y="549" on="1"/>
+        <pt x="322" y="634" on="1"/>
+        <pt x="375" y="596" on="1"/>
+        <pt x="344" y="609" on="0"/>
+        <pt x="290" y="618" on="0"/>
+        <pt x="267" y="618" on="1"/>
+        <pt x="211" y="618" on="0"/>
+        <pt x="136" y="557" on="0"/>
+        <pt x="136" y="502" on="1"/>
+        <pt x="136" y="452" on="0"/>
+        <pt x="194" y="394" on="0"/>
+        <pt x="247" y="377" on="1"/>
+        <pt x="285" y="364" on="1"/>
+        <pt x="386" y="329" on="0"/>
+        <pt x="461" y="232" on="0"/>
+        <pt x="461" y="171" on="1"/>
+        <pt x="461" y="91" on="0"/>
+        <pt x="345" y="-13" on="0"/>
+        <pt x="243" y="-13" on="1"/>
+        <pt x="163" y="-13" on="0"/>
+        <pt x="54" y="68" on="0"/>
+        <pt x="51" y="133" on="1"/>
+        <pt x="58" y="148" on="0"/>
+        <pt x="80" y="164" on="0"/>
+        <pt x="96" y="164" on="1"/>
+        <pt x="114" y="164" on="0"/>
+        <pt x="145" y="138" on="0"/>
+        <pt x="154" y="98" on="1"/>
+        <pt x="171" y="16" on="1"/>
+        <pt x="118" y="51" on="1"/>
+        <pt x="152" y="38" on="0"/>
+        <pt x="219" y="28" on="0"/>
+      </contour>
+      <contour>
+        <pt x="275" y="322" on="1"/>
+        <pt x="243" y="322" on="1"/>
+        <pt x="243" y="750" on="1"/>
+        <pt x="275" y="750" on="1"/>
+      </contour>
+      <contour>
+        <pt x="242" y="-115" on="1"/>
+        <pt x="242" y="322" on="1"/>
+        <pt x="274" y="322" on="1"/>
+        <pt x="274" y="-115" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0024.nostroke" xMin="51" yMin="-115" xMax="461" yMax="750">
+      <contour>
+        <pt x="248" y="28" on="1"/>
+        <pt x="309" y="28" on="0"/>
+        <pt x="382" y="90" on="0"/>
+        <pt x="382" y="146" on="1"/>
+        <pt x="382" y="194" on="0"/>
+        <pt x="326" y="252" on="0"/>
+        <pt x="262" y="274" on="1"/>
+        <pt x="225" y="287" on="1"/>
+        <pt x="146" y="315" on="0"/>
+        <pt x="58" y="403" on="0"/>
+        <pt x="58" y="478" on="1"/>
+        <pt x="58" y="554" on="0"/>
+        <pt x="170" y="660" on="0"/>
+        <pt x="256" y="660" on="1"/>
+        <pt x="334" y="660" on="0"/>
+        <pt x="438" y="578" on="0"/>
+        <pt x="443" y="514" on="1"/>
+        <pt x="436" y="498" on="0"/>
+        <pt x="413" y="483" on="0"/>
+        <pt x="399" y="483" on="1"/>
+        <pt x="382" y="483" on="0"/>
+        <pt x="349" y="510" on="0"/>
+        <pt x="340" y="549" on="1"/>
+        <pt x="322" y="634" on="1"/>
+        <pt x="375" y="596" on="1"/>
+        <pt x="344" y="609" on="0"/>
+        <pt x="290" y="618" on="0"/>
+        <pt x="267" y="618" on="1"/>
+        <pt x="212" y="618" on="0"/>
+        <pt x="137" y="557" on="0"/>
+        <pt x="137" y="503" on="1"/>
+        <pt x="137" y="453" on="0"/>
+        <pt x="194" y="394" on="0"/>
+        <pt x="247" y="377" on="1"/>
+        <pt x="285" y="364" on="1"/>
+        <pt x="386" y="329" on="0"/>
+        <pt x="461" y="232" on="0"/>
+        <pt x="461" y="171" on="1"/>
+        <pt x="461" y="91" on="0"/>
+        <pt x="345" y="-13" on="0"/>
+        <pt x="243" y="-13" on="1"/>
+        <pt x="163" y="-13" on="0"/>
+        <pt x="54" y="68" on="0"/>
+        <pt x="51" y="133" on="1"/>
+        <pt x="58" y="148" on="0"/>
+        <pt x="80" y="164" on="0"/>
+        <pt x="96" y="164" on="1"/>
+        <pt x="114" y="164" on="0"/>
+        <pt x="145" y="138" on="0"/>
+        <pt x="154" y="98" on="1"/>
+        <pt x="171" y="16" on="1"/>
+        <pt x="118" y="51" on="1"/>
+        <pt x="152" y="38" on="0"/>
+        <pt x="219" y="28" on="0"/>
+      </contour>
+      <contour>
+        <pt x="275" y="639" on="1"/>
+        <pt x="243" y="639" on="1"/>
+        <pt x="243" y="750" on="1"/>
+        <pt x="275" y="750" on="1"/>
+      </contour>
+      <contour>
+        <pt x="238" y="-115" on="1"/>
+        <pt x="238" y="8" on="1"/>
+        <pt x="271" y="8" on="1"/>
+        <pt x="271" y="-115" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0041" xMin="7" yMin="0" xMax="653" yMax="675">
+      <contour>
+        <pt x="7" y="0" on="1"/>
+        <pt x="7" y="31" on="1"/>
+        <pt x="101" y="46" on="1"/>
+        <pt x="121" y="46" on="1"/>
+        <pt x="233" y="31" on="1"/>
+        <pt x="233" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="73" y="0" on="1"/>
+        <pt x="299" y="675" on="1"/>
+        <pt x="359" y="675" on="1"/>
+        <pt x="589" y="0" on="1"/>
+        <pt x="496" y="0" on="1"/>
+        <pt x="293" y="607" on="1"/>
+        <pt x="312" y="599" on="1"/>
+        <pt x="116" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="170" y="224" on="1"/>
+        <pt x="183" y="260" on="1"/>
+        <pt x="461" y="260" on="1"/>
+        <pt x="471" y="224" on="1"/>
+      </contour>
+      <contour>
+        <pt x="383" y="0" on="1"/>
+        <pt x="383" y="32" on="1"/>
+        <pt x="513" y="47" on="1"/>
+        <pt x="529" y="47" on="1"/>
+        <pt x="653" y="32" on="1"/>
+        <pt x="653" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0061" xMin="46" yMin="-13" xMax="501" yMax="487">
+      <contour>
+        <pt x="46" y="109" on="1"/>
+        <pt x="46" y="153" on="0"/>
+        <pt x="110" y="222" on="0"/>
+        <pt x="210" y="259" on="1"/>
+        <pt x="242" y="270" on="0"/>
+        <pt x="328" y="294" on="0"/>
+        <pt x="365" y="301" on="1"/>
+        <pt x="365" y="272" on="1"/>
+        <pt x="331" y="265" on="0"/>
+        <pt x="256" y="242" on="0"/>
+        <pt x="237" y="235" on="1"/>
+        <pt x="165" y="207" on="0"/>
+        <pt x="131" y="147" on="0"/>
+        <pt x="131" y="120" on="1"/>
+        <pt x="131" y="80" on="0"/>
+        <pt x="177" y="46" on="0"/>
+        <pt x="212" y="46" on="1"/>
+        <pt x="238" y="46" on="0"/>
+        <pt x="283" y="71" on="0"/>
+        <pt x="330" y="106" on="1"/>
+        <pt x="350" y="124" on="1"/>
+        <pt x="364" y="104" on="1"/>
+        <pt x="335" y="75" on="1"/>
+        <pt x="290" y="30" on="0"/>
+        <pt x="226" y="-13" on="0"/>
+        <pt x="180" y="-13" on="1"/>
+        <pt x="125" y="-13" on="0"/>
+        <pt x="46" y="49" on="0"/>
+      </contour>
+      <contour>
+        <pt x="325" y="90" on="1"/>
+        <pt x="325" y="327" on="1"/>
+        <pt x="325" y="400" on="0"/>
+        <pt x="280" y="449" on="0"/>
+        <pt x="231" y="449" on="1"/>
+        <pt x="214" y="449" on="0"/>
+        <pt x="169" y="442" on="0"/>
+        <pt x="141" y="431" on="1"/>
+        <pt x="181" y="455" on="1"/>
+        <pt x="155" y="369" on="1"/>
+        <pt x="148" y="347" on="0"/>
+        <pt x="124" y="324" on="0"/>
+        <pt x="104" y="324" on="1"/>
+        <pt x="62" y="324" on="0"/>
+        <pt x="59" y="364" on="1"/>
+        <pt x="73" y="421" on="0"/>
+        <pt x="177" y="487" on="0"/>
+        <pt x="252" y="487" on="1"/>
+        <pt x="329" y="487" on="0"/>
+        <pt x="405" y="408" on="0"/>
+        <pt x="405" y="314" on="1"/>
+        <pt x="405" y="96" on="1"/>
+        <pt x="405" y="61" on="0"/>
+        <pt x="425" y="33" on="0"/>
+        <pt x="442" y="33" on="1"/>
+        <pt x="456" y="33" on="0"/>
+        <pt x="475" y="48" on="0"/>
+        <pt x="485" y="61" on="1"/>
+        <pt x="501" y="41" on="1"/>
+        <pt x="469" y="-10" on="0"/>
+        <pt x="416" y="-10" on="1"/>
+        <pt x="375" y="-10" on="0"/>
+        <pt x="325" y="45" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test Family
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      Version 1.001;ADBO;Test Family Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test Family
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.001
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestFamily-Master4
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Frank Grießhammer
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Master 4
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name="dollar.nostroke"/>
+      <psName name="uni0020"/>
+      <psName name="uni0041"/>
+      <psName name="uni0061"/>
+      <psName name="uni0024"/>
+      <psName name="uni0024.nostroke"/>
+    </extraNames>
+  </post>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily2-Master0.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily2-Master0.ttx
new file mode 100644
index 0000000..13d48e7
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily2-Master0.ttx
@@ -0,0 +1,1149 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.8">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="space"/>
+    <GlyphID id="2" name="A"/>
+    <GlyphID id="3" name="a"/>
+    <GlyphID id="4" name="d"/>
+    <GlyphID id="5" name="f"/>
+    <GlyphID id="6" name="n"/>
+    <GlyphID id="7" name="t"/>
+    <GlyphID id="8" name="f_t"/>
+    <GlyphID id="9" name="a.alt"/>
+    <GlyphID id="10" name="A.sc"/>
+    <GlyphID id="11" name="atilde"/>
+    <GlyphID id="12" name="ampersand"/>
+    <GlyphID id="13" name="uni25CC"/>
+    <GlyphID id="14" name="uni0303"/>
+    <GlyphID id="15" name="uni0308"/>
+    <GlyphID id="16" name="uni0330"/>
+    <GlyphID id="17" name="uni0324"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="2.02"/>
+    <checkSumAdjustment value="0xbc719815"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000001"/>
+    <unitsPerEm value="1000"/>
+    <created value="Mon Mar  6 09:06:52 2017"/>
+    <modified value="Mon Mar  6 09:06:52 2017"/>
+    <xMin value="-160"/>
+    <yMin value="-220"/>
+    <xMax value="550"/>
+    <yMax value="734"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="984"/>
+    <descent value="-273"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="624"/>
+    <minLeftSideBearing value="-160"/>
+    <minRightSideBearing value="-160"/>
+    <xMaxExtent value="549"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="15"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="18"/>
+    <maxPoints value="123"/>
+    <maxContours value="12"/>
+    <maxCompositePoints value="58"/>
+    <maxCompositeContours value="3"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="2"/>
+    <maxComponentDepth value="1"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="471"/>
+    <usWeightClass value="200"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="287"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="3"/>
+      <bProportion value="3"/>
+      <bContrast value="3"/>
+      <bStrokeVariation value="4"/>
+      <bArmStyle value="3"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000011"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBO"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="9676"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="984"/>
+    <usWinDescent value="273"/>
+    <ulCodePageRange1 value="00100000 00000000 00000000 00000011"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="478"/>
+    <sCapHeight value="660"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="2"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="624" lsb="96"/>
+    <mtx name="A" width="520" lsb="10"/>
+    <mtx name="A.sc" width="444" lsb="10"/>
+    <mtx name="a" width="486" lsb="60"/>
+    <mtx name="a.alt" width="540" lsb="54"/>
+    <mtx name="ampersand" width="562" lsb="37"/>
+    <mtx name="atilde" width="486" lsb="60"/>
+    <mtx name="d" width="540" lsb="54"/>
+    <mtx name="f" width="252" lsb="34"/>
+    <mtx name="f_t" width="518" lsb="34"/>
+    <mtx name="n" width="526" lsb="96"/>
+    <mtx name="space" width="200" lsb="0"/>
+    <mtx name="t" width="302" lsb="30"/>
+    <mtx name="uni0303" width="0" lsb="-160"/>
+    <mtx name="uni0308" width="0" lsb="-118"/>
+    <mtx name="uni0324" width="0" lsb="-118"/>
+    <mtx name="uni0330" width="0" lsb="-160"/>
+    <mtx name="uni25CC" width="592" lsb="54"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x26" name="ampersand"/><!-- AMPERSAND -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x64" name="d"/><!-- LATIN SMALL LETTER D -->
+      <map code="0x66" name="f"/><!-- LATIN SMALL LETTER F -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x74" name="t"/><!-- LATIN SMALL LETTER T -->
+      <map code="0xe3" name="atilde"/><!-- LATIN SMALL LETTER A WITH TILDE -->
+      <map code="0x303" name="uni0303"/><!-- COMBINING TILDE -->
+      <map code="0x308" name="uni0308"/><!-- COMBINING DIAERESIS -->
+      <map code="0x324" name="uni0324"/><!-- COMBINING DIAERESIS BELOW -->
+      <map code="0x330" name="uni0330"/><!-- COMBINING TILDE BELOW -->
+      <map code="0x25cc" name="uni25CC"/><!-- DOTTED CIRCLE -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x26" name="ampersand"/><!-- AMPERSAND -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x64" name="d"/><!-- LATIN SMALL LETTER D -->
+      <map code="0x66" name="f"/><!-- LATIN SMALL LETTER F -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x74" name="t"/><!-- LATIN SMALL LETTER T -->
+      <map code="0xe3" name="atilde"/><!-- LATIN SMALL LETTER A WITH TILDE -->
+      <map code="0x303" name="uni0303"/><!-- COMBINING TILDE -->
+      <map code="0x308" name="uni0308"/><!-- COMBINING DIAERESIS -->
+      <map code="0x324" name="uni0324"/><!-- COMBINING DIAERESIS BELOW -->
+      <map code="0x330" name="uni0330"/><!-- COMBINING TILDE BELOW -->
+      <map code="0x25cc" name="uni25CC"/><!-- DOTTED CIRCLE -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="96" yMin="0" xMax="528" yMax="660">
+      <contour>
+        <pt x="96" y="0" on="1"/>
+        <pt x="96" y="660" on="1"/>
+        <pt x="528" y="660" on="1"/>
+        <pt x="528" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="144" y="32" on="1"/>
+        <pt x="476" y="32" on="1"/>
+        <pt x="376" y="208" on="1"/>
+        <pt x="314" y="314" on="1"/>
+        <pt x="310" y="314" on="1"/>
+        <pt x="246" y="208" on="1"/>
+      </contour>
+      <contour>
+        <pt x="310" y="366" on="1"/>
+        <pt x="314" y="366" on="1"/>
+        <pt x="368" y="458" on="1"/>
+        <pt x="462" y="626" on="1"/>
+        <pt x="160" y="626" on="1"/>
+        <pt x="254" y="458" on="1"/>
+      </contour>
+      <contour>
+        <pt x="134" y="74" on="1"/>
+        <pt x="288" y="340" on="1"/>
+        <pt x="134" y="610" on="1"/>
+      </contour>
+      <contour>
+        <pt x="488" y="74" on="1"/>
+        <pt x="488" y="610" on="1"/>
+        <pt x="336" y="340" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="10" yMin="0" xMax="510" yMax="660">
+      <contour>
+        <pt x="10" y="0" on="1"/>
+        <pt x="246" y="660" on="1"/>
+        <pt x="274" y="660" on="1"/>
+        <pt x="510" y="0" on="1"/>
+        <pt x="476" y="0" on="1"/>
+        <pt x="338" y="396" on="1"/>
+        <pt x="317" y="456" on="0"/>
+        <pt x="280" y="564" on="0"/>
+        <pt x="262" y="626" on="1"/>
+        <pt x="258" y="626" on="1"/>
+        <pt x="240" y="564" on="0"/>
+        <pt x="203" y="456" on="0"/>
+        <pt x="182" y="396" on="1"/>
+        <pt x="42" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="112" y="236" on="1"/>
+        <pt x="112" y="264" on="1"/>
+        <pt x="405" y="264" on="1"/>
+        <pt x="405" y="236" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A.sc" xMin="10" yMin="0" xMax="434" yMax="510">
+      <contour>
+        <pt x="10" y="0" on="1"/>
+        <pt x="207" y="510" on="1"/>
+        <pt x="236" y="510" on="1"/>
+        <pt x="434" y="0" on="1"/>
+        <pt x="400" y="0" on="1"/>
+        <pt x="286" y="304" on="1"/>
+        <pt x="269" y="351" on="0"/>
+        <pt x="240" y="430" on="0"/>
+        <pt x="224" y="475" on="1"/>
+        <pt x="220" y="475" on="1"/>
+        <pt x="204" y="429" on="0"/>
+        <pt x="175" y="350" on="0"/>
+        <pt x="158" y="304" on="1"/>
+        <pt x="42" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="97" y="176" on="1"/>
+        <pt x="97" y="204" on="1"/>
+        <pt x="345" y="204" on="1"/>
+        <pt x="345" y="176" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="a" xMin="60" yMin="-12" xMax="404" yMax="490">
+      <contour>
+        <pt x="198" y="-12" on="1"/>
+        <pt x="142" y="-12" on="0"/>
+        <pt x="60" y="49" on="0"/>
+        <pt x="60" y="117" on="1"/>
+        <pt x="60" y="197" on="0"/>
+        <pt x="212" y="280" on="0"/>
+        <pt x="374" y="298" on="1"/>
+        <pt x="376" y="356" on="0"/>
+        <pt x="331" y="462" on="0"/>
+        <pt x="256" y="462" on="1"/>
+        <pt x="205" y="462" on="0"/>
+        <pt x="122" y="422" on="0"/>
+        <pt x="100" y="404" on="1"/>
+        <pt x="84" y="428" on="1"/>
+        <pt x="106" y="444" on="0"/>
+        <pt x="200" y="490" on="0"/>
+        <pt x="258" y="490" on="1"/>
+        <pt x="342" y="490" on="0"/>
+        <pt x="404" y="382" on="0"/>
+        <pt x="404" y="310" on="1"/>
+        <pt x="404" y="0" on="1"/>
+        <pt x="378" y="0" on="1"/>
+        <pt x="374" y="64" on="1"/>
+        <pt x="372" y="64" on="1"/>
+        <pt x="334" y="34" on="0"/>
+        <pt x="246" y="-12" on="0"/>
+      </contour>
+      <contour>
+        <pt x="200" y="16" on="1"/>
+        <pt x="244" y="16" on="0"/>
+        <pt x="326" y="58" on="0"/>
+        <pt x="374" y="98" on="1"/>
+        <pt x="374" y="272" on="1"/>
+        <pt x="216" y="254" on="0"/>
+        <pt x="92" y="180" on="0"/>
+        <pt x="92" y="118" on="1"/>
+        <pt x="92" y="62" on="0"/>
+        <pt x="156" y="16" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="a.alt" xMin="54" yMin="-12" xMax="444" yMax="490">
+      <contour>
+        <pt x="252" y="-12" on="1"/>
+        <pt x="162" y="-12" on="0"/>
+        <pt x="54" y="116" on="0"/>
+        <pt x="54" y="238" on="1"/>
+        <pt x="54" y="354" on="0"/>
+        <pt x="181" y="490" on="0"/>
+        <pt x="268" y="490" on="1"/>
+        <pt x="312" y="490" on="0"/>
+        <pt x="378" y="459" on="0"/>
+        <pt x="412" y="432" on="1"/>
+        <pt x="414" y="432" on="1"/>
+        <pt x="416" y="478" on="1"/>
+        <pt x="444" y="478" on="1"/>
+        <pt x="444" y="0" on="1"/>
+        <pt x="418" y="0" on="1"/>
+        <pt x="414" y="64" on="1"/>
+        <pt x="412" y="64" on="1"/>
+        <pt x="382" y="34" on="0"/>
+        <pt x="302" y="-12" on="0"/>
+      </contour>
+      <contour>
+        <pt x="254" y="16" on="1"/>
+        <pt x="296" y="16" on="0"/>
+        <pt x="374" y="60" on="0"/>
+        <pt x="414" y="100" on="1"/>
+        <pt x="414" y="396" on="1"/>
+        <pt x="374" y="432" on="0"/>
+        <pt x="304" y="462" on="0"/>
+        <pt x="266" y="462" on="1"/>
+        <pt x="190" y="462" on="0"/>
+        <pt x="86" y="331" on="0"/>
+        <pt x="86" y="238" on="1"/>
+        <pt x="86" y="139" on="0"/>
+        <pt x="173" y="16" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="ampersand" xMin="38" yMin="-12" xMax="550" yMax="672">
+      <contour>
+        <pt x="224" y="-12" on="1"/>
+        <pt x="149" y="-12" on="0"/>
+        <pt x="38" y="84" on="0"/>
+        <pt x="38" y="166" on="1"/>
+        <pt x="38" y="220" on="0"/>
+        <pt x="98" y="302" on="0"/>
+        <pt x="186" y="371" on="0"/>
+        <pt x="274" y="436" on="0"/>
+        <pt x="334" y="508" on="0"/>
+        <pt x="334" y="554" on="1"/>
+        <pt x="334" y="587" on="0"/>
+        <pt x="300" y="644" on="0"/>
+        <pt x="258" y="644" on="1"/>
+        <pt x="212" y="644" on="0"/>
+        <pt x="160" y="576" on="0"/>
+        <pt x="160" y="530" on="1"/>
+        <pt x="160" y="461" on="0"/>
+        <pt x="235" y="304" on="0"/>
+        <pt x="350" y="158" on="0"/>
+        <pt x="410" y="106" on="1"/>
+        <pt x="448" y="73" on="0"/>
+        <pt x="518" y="28" on="0"/>
+        <pt x="550" y="16" on="1"/>
+        <pt x="538" y="-12" on="1"/>
+        <pt x="506" y="0" on="0"/>
+        <pt x="432" y="46" on="0"/>
+        <pt x="392" y="82" on="1"/>
+        <pt x="328" y="139" on="0"/>
+        <pt x="208" y="292" on="0"/>
+        <pt x="130" y="455" on="0"/>
+        <pt x="130" y="528" on="1"/>
+        <pt x="130" y="591" on="0"/>
+        <pt x="203" y="672" on="0"/>
+        <pt x="260" y="672" on="1"/>
+        <pt x="311" y="672" on="0"/>
+        <pt x="364" y="606" on="0"/>
+        <pt x="364" y="556" on="1"/>
+        <pt x="364" y="494" on="0"/>
+        <pt x="275" y="403" on="0"/>
+        <pt x="159" y="321" on="0"/>
+        <pt x="70" y="230" on="0"/>
+        <pt x="70" y="168" on="1"/>
+        <pt x="70" y="99" on="0"/>
+        <pt x="163" y="16" on="0"/>
+        <pt x="226" y="16" on="1"/>
+        <pt x="272" y="16" on="0"/>
+        <pt x="355" y="64" on="0"/>
+        <pt x="388" y="102" on="1"/>
+        <pt x="430" y="150" on="0"/>
+        <pt x="488" y="272" on="0"/>
+        <pt x="508" y="342" on="1"/>
+        <pt x="538" y="342" on="1"/>
+        <pt x="517" y="268" on="0"/>
+        <pt x="454" y="136" on="0"/>
+        <pt x="410" y="84" on="1"/>
+        <pt x="372" y="42" on="0"/>
+        <pt x="282" y="-12" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="atilde" xMin="60" yMin="-12" xMax="404" yMax="706">
+      <component glyphName="a" x="0" y="0" flags="0x204"/>
+      <component glyphName="uni0303" x="242" y="0" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="d" xMin="54" yMin="-12" xMax="444" yMax="722">
+      <contour>
+        <pt x="252" y="-12" on="1"/>
+        <pt x="162" y="-12" on="0"/>
+        <pt x="54" y="116" on="0"/>
+        <pt x="54" y="238" on="1"/>
+        <pt x="54" y="354" on="0"/>
+        <pt x="181" y="490" on="0"/>
+        <pt x="268" y="490" on="1"/>
+        <pt x="312" y="490" on="0"/>
+        <pt x="378" y="458" on="0"/>
+        <pt x="416" y="430" on="1"/>
+        <pt x="414" y="520" on="1"/>
+        <pt x="414" y="722" on="1"/>
+        <pt x="444" y="722" on="1"/>
+        <pt x="444" y="0" on="1"/>
+        <pt x="418" y="0" on="1"/>
+        <pt x="414" y="64" on="1"/>
+        <pt x="412" y="64" on="1"/>
+        <pt x="382" y="34" on="0"/>
+        <pt x="302" y="-12" on="0"/>
+      </contour>
+      <contour>
+        <pt x="254" y="16" on="1"/>
+        <pt x="296" y="16" on="0"/>
+        <pt x="374" y="60" on="0"/>
+        <pt x="414" y="100" on="1"/>
+        <pt x="414" y="396" on="1"/>
+        <pt x="374" y="432" on="0"/>
+        <pt x="304" y="462" on="0"/>
+        <pt x="266" y="462" on="1"/>
+        <pt x="190" y="462" on="0"/>
+        <pt x="86" y="331" on="0"/>
+        <pt x="86" y="238" on="1"/>
+        <pt x="86" y="139" on="0"/>
+        <pt x="173" y="16" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="f" xMin="34" yMin="0" xMax="276" yMax="734">
+      <contour>
+        <pt x="100" y="0" on="1"/>
+        <pt x="100" y="596" on="1"/>
+        <pt x="100" y="666" on="0"/>
+        <pt x="159" y="734" on="0"/>
+        <pt x="210" y="734" on="1"/>
+        <pt x="225" y="734" on="0"/>
+        <pt x="260" y="726" on="0"/>
+        <pt x="276" y="718" on="1"/>
+        <pt x="266" y="692" on="1"/>
+        <pt x="238" y="706" on="0"/>
+        <pt x="208" y="706" on="1"/>
+        <pt x="168" y="706" on="0"/>
+        <pt x="130" y="646" on="0"/>
+        <pt x="130" y="592" on="1"/>
+        <pt x="130" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="34" y="450" on="1"/>
+        <pt x="34" y="474" on="1"/>
+        <pt x="100" y="478" on="1"/>
+        <pt x="244" y="478" on="1"/>
+        <pt x="244" y="450" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="f_t" xMin="34" yMin="-12" xMax="508" yMax="734">
+      <contour>
+        <pt x="100" y="0" on="1"/>
+        <pt x="100" y="596" on="1"/>
+        <pt x="100" y="666" on="0"/>
+        <pt x="159" y="734" on="0"/>
+        <pt x="210" y="734" on="1"/>
+        <pt x="225" y="734" on="0"/>
+        <pt x="260" y="726" on="0"/>
+        <pt x="276" y="718" on="1"/>
+        <pt x="266" y="692" on="1"/>
+        <pt x="238" y="706" on="0"/>
+        <pt x="208" y="706" on="1"/>
+        <pt x="168" y="706" on="0"/>
+        <pt x="130" y="646" on="0"/>
+        <pt x="130" y="592" on="1"/>
+        <pt x="130" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="434" y="-12" on="1"/>
+        <pt x="368" y="-12" on="0"/>
+        <pt x="322" y="64" on="0"/>
+        <pt x="322" y="126" on="1"/>
+        <pt x="322" y="450" on="1"/>
+        <pt x="34" y="450" on="1"/>
+        <pt x="34" y="474" on="1"/>
+        <pt x="100" y="478" on="1"/>
+        <pt x="322" y="478" on="1"/>
+        <pt x="326" y="618" on="1"/>
+        <pt x="352" y="618" on="1"/>
+        <pt x="352" y="478" on="1"/>
+        <pt x="494" y="478" on="1"/>
+        <pt x="494" y="450" on="1"/>
+        <pt x="352" y="450" on="1"/>
+        <pt x="352" y="122" on="1"/>
+        <pt x="352" y="76" on="0"/>
+        <pt x="384" y="16" on="0"/>
+        <pt x="436" y="16" on="1"/>
+        <pt x="450" y="16" on="0"/>
+        <pt x="484" y="26" on="0"/>
+        <pt x="498" y="32" on="1"/>
+        <pt x="508" y="6" on="1"/>
+        <pt x="487" y="-2" on="0"/>
+        <pt x="446" y="-12" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="n" xMin="96" yMin="0" xMax="438" yMax="490">
+      <contour>
+        <pt x="96" y="0" on="1"/>
+        <pt x="96" y="478" on="1"/>
+        <pt x="122" y="478" on="1"/>
+        <pt x="126" y="402" on="1"/>
+        <pt x="128" y="402" on="1"/>
+        <pt x="166" y="440" on="0"/>
+        <pt x="244" y="490" on="0"/>
+        <pt x="294" y="490" on="1"/>
+        <pt x="368" y="490" on="0"/>
+        <pt x="438" y="401" on="0"/>
+        <pt x="438" y="308" on="1"/>
+        <pt x="438" y="0" on="1"/>
+        <pt x="408" y="0" on="1"/>
+        <pt x="408" y="304" on="1"/>
+        <pt x="408" y="384" on="0"/>
+        <pt x="354" y="462" on="0"/>
+        <pt x="292" y="462" on="1"/>
+        <pt x="247" y="462" on="0"/>
+        <pt x="172" y="414" on="0"/>
+        <pt x="126" y="366" on="1"/>
+        <pt x="126" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+    <TTGlyph name="t" xMin="30" yMin="-12" xMax="292" yMax="618">
+      <contour>
+        <pt x="218" y="-12" on="1"/>
+        <pt x="152" y="-12" on="0"/>
+        <pt x="106" y="64" on="0"/>
+        <pt x="106" y="126" on="1"/>
+        <pt x="106" y="450" on="1"/>
+        <pt x="30" y="450" on="1"/>
+        <pt x="30" y="474" on="1"/>
+        <pt x="106" y="478" on="1"/>
+        <pt x="110" y="618" on="1"/>
+        <pt x="136" y="618" on="1"/>
+        <pt x="136" y="478" on="1"/>
+        <pt x="278" y="478" on="1"/>
+        <pt x="278" y="450" on="1"/>
+        <pt x="136" y="450" on="1"/>
+        <pt x="136" y="122" on="1"/>
+        <pt x="136" y="76" on="0"/>
+        <pt x="168" y="16" on="0"/>
+        <pt x="220" y="16" on="1"/>
+        <pt x="234" y="16" on="0"/>
+        <pt x="268" y="26" on="0"/>
+        <pt x="282" y="32" on="1"/>
+        <pt x="292" y="6" on="1"/>
+        <pt x="271" y="-2" on="0"/>
+        <pt x="230" y="-12" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0303" xMin="-160" yMin="580" xMax="160" yMax="706">
+      <contour>
+        <pt x="77" y="580" on="1"/>
+        <pt x="38" y="580" on="0"/>
+        <pt x="-8" y="630" on="0"/>
+        <pt x="-49" y="680" on="0"/>
+        <pt x="-78" y="680" on="1"/>
+        <pt x="-106" y="680" on="0"/>
+        <pt x="-132" y="622" on="0"/>
+        <pt x="-134" y="582" on="1"/>
+        <pt x="-160" y="584" on="1"/>
+        <pt x="-157" y="627" on="0"/>
+        <pt x="-124" y="706" on="0"/>
+        <pt x="-76" y="706" on="1"/>
+        <pt x="-38" y="706" on="0"/>
+        <pt x="9" y="656" on="0"/>
+        <pt x="50" y="606" on="0"/>
+        <pt x="79" y="606" on="1"/>
+        <pt x="107" y="606" on="0"/>
+        <pt x="132" y="665" on="0"/>
+        <pt x="134" y="704" on="1"/>
+        <pt x="160" y="702" on="1"/>
+        <pt x="157" y="660" on="0"/>
+        <pt x="124" y="580" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0308" xMin="-118" yMin="602" xMax="118" yMax="666">
+      <contour>
+        <pt x="-86" y="602" on="1"/>
+        <pt x="-100" y="602" on="0"/>
+        <pt x="-118" y="620" on="0"/>
+        <pt x="-118" y="634" on="1"/>
+        <pt x="-118" y="648" on="0"/>
+        <pt x="-100" y="666" on="0"/>
+        <pt x="-86" y="666" on="1"/>
+        <pt x="-72" y="666" on="0"/>
+        <pt x="-54" y="648" on="0"/>
+        <pt x="-54" y="634" on="1"/>
+        <pt x="-54" y="620" on="0"/>
+        <pt x="-72" y="602" on="0"/>
+      </contour>
+      <contour>
+        <pt x="86" y="602" on="1"/>
+        <pt x="72" y="602" on="0"/>
+        <pt x="54" y="620" on="0"/>
+        <pt x="54" y="634" on="1"/>
+        <pt x="54" y="648" on="0"/>
+        <pt x="72" y="666" on="0"/>
+        <pt x="86" y="666" on="1"/>
+        <pt x="100" y="666" on="0"/>
+        <pt x="118" y="648" on="0"/>
+        <pt x="118" y="634" on="1"/>
+        <pt x="118" y="620" on="0"/>
+        <pt x="100" y="602" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0324" xMin="-118" yMin="-188" xMax="118" yMax="-124">
+      <component glyphName="uni0308" x="0" y="-790" flags="0x204"/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0330" xMin="-160" yMin="-220" xMax="160" yMax="-94">
+      <component glyphName="uni0303" x="0" y="-800" flags="0x204"/>
+    </TTGlyph>
+
+    <TTGlyph name="uni25CC" xMin="54" yMin="-12" xMax="538" yMax="490">
+      <contour>
+        <pt x="110" y="97" on="1"/>
+        <pt x="98" y="97" on="0"/>
+        <pt x="81" y="115" on="0"/>
+        <pt x="81" y="129" on="1"/>
+        <pt x="81" y="145" on="0"/>
+        <pt x="98" y="161" on="0"/>
+        <pt x="110" y="161" on="1"/>
+        <pt x="141" y="161" on="0"/>
+        <pt x="141" y="129" on="1"/>
+        <pt x="141" y="97" on="0"/>
+      </contour>
+      <contour>
+        <pt x="82" y="207" on="1"/>
+        <pt x="71" y="207" on="0"/>
+        <pt x="54" y="224" on="0"/>
+        <pt x="54" y="239" on="1"/>
+        <pt x="54" y="255" on="0"/>
+        <pt x="71" y="270" on="0"/>
+        <pt x="82" y="270" on="1"/>
+        <pt x="114" y="270" on="0"/>
+        <pt x="114" y="239" on="1"/>
+        <pt x="114" y="207" on="0"/>
+      </contour>
+      <contour>
+        <pt x="110" y="318" on="1"/>
+        <pt x="98" y="318" on="0"/>
+        <pt x="81" y="335" on="0"/>
+        <pt x="81" y="351" on="1"/>
+        <pt x="81" y="366" on="0"/>
+        <pt x="98" y="382" on="0"/>
+        <pt x="110" y="382" on="1"/>
+        <pt x="141" y="382" on="0"/>
+        <pt x="141" y="351" on="1"/>
+        <pt x="141" y="318" on="0"/>
+      </contour>
+      <contour>
+        <pt x="189" y="15" on="1"/>
+        <pt x="177" y="15" on="0"/>
+        <pt x="160" y="32" on="0"/>
+        <pt x="160" y="49" on="1"/>
+        <pt x="160" y="63" on="0"/>
+        <pt x="177" y="78" on="0"/>
+        <pt x="189" y="78" on="1"/>
+        <pt x="202" y="78" on="0"/>
+        <pt x="219" y="63" on="0"/>
+        <pt x="219" y="49" on="1"/>
+        <pt x="219" y="15" on="0"/>
+      </contour>
+      <contour>
+        <pt x="189" y="400" on="1"/>
+        <pt x="177" y="400" on="0"/>
+        <pt x="160" y="417" on="0"/>
+        <pt x="160" y="431" on="1"/>
+        <pt x="160" y="448" on="0"/>
+        <pt x="177" y="463" on="0"/>
+        <pt x="189" y="463" on="1"/>
+        <pt x="202" y="463" on="0"/>
+        <pt x="219" y="448" on="0"/>
+        <pt x="219" y="431" on="1"/>
+        <pt x="219" y="400" on="0"/>
+      </contour>
+      <contour>
+        <pt x="295" y="-12" on="1"/>
+        <pt x="283" y="-12" on="0"/>
+        <pt x="266" y="4" on="0"/>
+        <pt x="266" y="20" on="1"/>
+        <pt x="266" y="35" on="0"/>
+        <pt x="283" y="51" on="0"/>
+        <pt x="295" y="51" on="1"/>
+        <pt x="326" y="51" on="0"/>
+        <pt x="326" y="20" on="1"/>
+        <pt x="326" y="-12" on="0"/>
+      </contour>
+      <contour>
+        <pt x="295" y="426" on="1"/>
+        <pt x="283" y="426" on="0"/>
+        <pt x="266" y="443" on="0"/>
+        <pt x="266" y="458" on="1"/>
+        <pt x="266" y="473" on="0"/>
+        <pt x="283" y="490" on="0"/>
+        <pt x="295" y="490" on="1"/>
+        <pt x="326" y="490" on="0"/>
+        <pt x="326" y="458" on="1"/>
+        <pt x="326" y="426" on="0"/>
+      </contour>
+      <contour>
+        <pt x="401" y="15" on="1"/>
+        <pt x="390" y="15" on="0"/>
+        <pt x="373" y="32" on="0"/>
+        <pt x="373" y="49" on="1"/>
+        <pt x="373" y="63" on="0"/>
+        <pt x="390" y="78" on="0"/>
+        <pt x="401" y="78" on="1"/>
+        <pt x="414" y="78" on="0"/>
+        <pt x="431" y="63" on="0"/>
+        <pt x="431" y="49" on="1"/>
+        <pt x="431" y="15" on="0"/>
+      </contour>
+      <contour>
+        <pt x="401" y="399" on="1"/>
+        <pt x="390" y="399" on="0"/>
+        <pt x="373" y="417" on="0"/>
+        <pt x="373" y="431" on="1"/>
+        <pt x="373" y="447" on="0"/>
+        <pt x="390" y="462" on="0"/>
+        <pt x="401" y="462" on="1"/>
+        <pt x="414" y="462" on="0"/>
+        <pt x="431" y="447" on="0"/>
+        <pt x="431" y="431" on="1"/>
+        <pt x="431" y="399" on="0"/>
+      </contour>
+      <contour>
+        <pt x="480" y="97" on="1"/>
+        <pt x="469" y="97" on="0"/>
+        <pt x="451" y="115" on="0"/>
+        <pt x="451" y="129" on="1"/>
+        <pt x="451" y="145" on="0"/>
+        <pt x="469" y="161" on="0"/>
+        <pt x="480" y="161" on="1"/>
+        <pt x="510" y="161" on="0"/>
+        <pt x="510" y="129" on="1"/>
+        <pt x="510" y="97" on="0"/>
+      </contour>
+      <contour>
+        <pt x="508" y="207" on="1"/>
+        <pt x="478" y="207" on="0"/>
+        <pt x="478" y="239" on="1"/>
+        <pt x="478" y="270" on="0"/>
+        <pt x="508" y="270" on="1"/>
+        <pt x="519" y="270" on="0"/>
+        <pt x="538" y="255" on="0"/>
+        <pt x="538" y="239" on="1"/>
+        <pt x="538" y="207" on="0"/>
+      </contour>
+      <contour>
+        <pt x="480" y="317" on="1"/>
+        <pt x="469" y="317" on="0"/>
+        <pt x="451" y="334" on="0"/>
+        <pt x="451" y="349" on="1"/>
+        <pt x="451" y="364" on="0"/>
+        <pt x="469" y="380" on="0"/>
+        <pt x="480" y="380" on="1"/>
+        <pt x="510" y="380" on="0"/>
+        <pt x="510" y="349" on="1"/>
+        <pt x="510" y="317" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="9" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Paul D. Hunt
+    </namerecord>
+    <namerecord nameID="256" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Alternate a
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test Family 2
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      Version 2.020;ADBO;Test Family 2 Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test Family 2
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 2.020
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestFamily2-Master0
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Paul D. Hunt
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Master 0
+    </namerecord>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Alternate a
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name="f_t"/>
+      <psName name="a.alt"/>
+      <psName name="A.sc"/>
+      <psName name="circledotted"/>
+      <psName name="tildecmb"/>
+      <psName name="dieresiscmb"/>
+      <psName name="tildebelowcmb"/>
+      <psName name="dieresisbelowcmb"/>
+      <psName name="uni25CC"/>
+      <psName name="uni0303"/>
+      <psName name="uni0308"/>
+      <psName name="uni0330"/>
+      <psName name="uni0324"/>
+    </extraNames>
+  </post>
+
+  <BASE>
+    <Version value="0x00010000"/>
+    <HorizAxis>
+      <BaseTagList>
+        <!-- BaseTagCount=2 -->
+        <BaselineTag index="0" value="ideo"/>
+        <BaselineTag index="1" value="romn"/>
+      </BaseTagList>
+      <BaseScriptList>
+        <!-- BaseScriptCount=4 -->
+        <BaseScriptRecord index="0">
+          <BaseScriptTag value="DFLT"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="1">
+          <BaseScriptTag value="cyrl"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="2">
+          <BaseScriptTag value="grek"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="3">
+          <BaseScriptTag value="latn"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+      </BaseScriptList>
+    </HorizAxis>
+  </BASE>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=2 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=6 -->
+            <FeatureIndex index="0" value="0"/>
+            <FeatureIndex index="1" value="1"/>
+            <FeatureIndex index="2" value="2"/>
+            <FeatureIndex index="3" value="3"/>
+            <FeatureIndex index="4" value="4"/>
+            <FeatureIndex index="5" value="5"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="1">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=6 -->
+            <FeatureIndex index="0" value="0"/>
+            <FeatureIndex index="1" value="1"/>
+            <FeatureIndex index="2" value="2"/>
+            <FeatureIndex index="3" value="3"/>
+            <FeatureIndex index="4" value="4"/>
+            <FeatureIndex index="5" value="5"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=6 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="c2sc"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="calt"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="5"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="2">
+        <FeatureTag value="ccmp"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="2"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="3">
+        <FeatureTag value="liga"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="4">
+        <FeatureTag value="salt"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="3"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="5">
+        <FeatureTag value="ss01"/>
+        <Feature>
+          <FeatureParamsStylisticSet>
+            <Version value="0"/>
+            <UINameID value="256"/>  <!-- Alternate a -->
+          </FeatureParamsStylisticSet>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=6 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="A" out="A.sc"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="a" out="a.alt"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="ampersand" out="a,n,d"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="3"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <AlternateSubst index="0" Format="1">
+          <AlternateSet glyph="a">
+            <Alternate glyph="a.alt"/>
+            <Alternate glyph="A.sc"/>
+          </AlternateSet>
+        </AlternateSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="f">
+            <Ligature components="t" glyph="f_t"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="5">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0" Format="1">
+            <Glyph value="a"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0" Format="1">
+            <Glyph value="t"/>
+          </LookAheadCoverage>
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily2-Master1.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily2-Master1.ttx
new file mode 100644
index 0000000..92157bb
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily2-Master1.ttx
@@ -0,0 +1,986 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.8">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="space"/>
+    <GlyphID id="2" name="A"/>
+    <GlyphID id="3" name="a"/>
+    <GlyphID id="4" name="d"/>
+    <GlyphID id="5" name="f"/>
+    <GlyphID id="6" name="n"/>
+    <GlyphID id="7" name="t"/>
+    <GlyphID id="8" name="f_t"/>
+    <GlyphID id="9" name="a.alt"/>
+    <GlyphID id="10" name="A.sc"/>
+    <GlyphID id="11" name="atilde"/>
+    <GlyphID id="12" name="ampersand"/>
+    <GlyphID id="13" name="uni25CC"/>
+    <GlyphID id="14" name="uni0303"/>
+    <GlyphID id="15" name="uni0308"/>
+    <GlyphID id="16" name="uni0330"/>
+    <GlyphID id="17" name="uni0324"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="2.02"/>
+    <checkSumAdjustment value="0xf545c03c"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000001"/>
+    <unitsPerEm value="1000"/>
+    <created value="Mon Mar  6 09:06:52 2017"/>
+    <modified value="Mon Mar  6 09:06:52 2017"/>
+    <xMin value="-196"/>
+    <yMin value="-228"/>
+    <xMax value="706"/>
+    <yMax value="746"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="984"/>
+    <descent value="-273"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="724"/>
+    <minLeftSideBearing value="-196"/>
+    <minRightSideBearing value="-196"/>
+    <xMaxExtent value="706"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="15"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="18"/>
+    <maxPoints value="123"/>
+    <maxContours value="12"/>
+    <maxCompositePoints value="58"/>
+    <maxCompositeContours value="3"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="2"/>
+    <maxComponentDepth value="1"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="540"/>
+    <usWeightClass value="900"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="8"/>
+      <bProportion value="3"/>
+      <bContrast value="3"/>
+      <bStrokeVariation value="4"/>
+      <bArmStyle value="3"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000011"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBO"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="9676"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="984"/>
+    <usWinDescent value="273"/>
+    <ulCodePageRange1 value="00100000 00000000 00000000 00000011"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="660"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="704" lsb="76"/>
+    <mtx name="A" width="584" lsb="-10"/>
+    <mtx name="A.sc" width="516" lsb="-10"/>
+    <mtx name="a" width="536" lsb="38"/>
+    <mtx name="a.alt" width="580" lsb="36"/>
+    <mtx name="ampersand" width="690" lsb="21"/>
+    <mtx name="atilde" width="536" lsb="38"/>
+    <mtx name="d" width="580" lsb="36"/>
+    <mtx name="f" width="360" lsb="22"/>
+    <mtx name="f_t" width="724" lsb="22"/>
+    <mtx name="n" width="582" lsb="58"/>
+    <mtx name="space" width="200" lsb="0"/>
+    <mtx name="t" width="400" lsb="14"/>
+    <mtx name="uni0303" width="0" lsb="-196"/>
+    <mtx name="uni0308" width="0" lsb="-194"/>
+    <mtx name="uni0324" width="0" lsb="-194"/>
+    <mtx name="uni0330" width="0" lsb="-196"/>
+    <mtx name="uni25CC" width="574" lsb="32"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x26" name="ampersand"/><!-- AMPERSAND -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x64" name="d"/><!-- LATIN SMALL LETTER D -->
+      <map code="0x66" name="f"/><!-- LATIN SMALL LETTER F -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x74" name="t"/><!-- LATIN SMALL LETTER T -->
+      <map code="0xe3" name="atilde"/><!-- LATIN SMALL LETTER A WITH TILDE -->
+      <map code="0x303" name="uni0303"/><!-- COMBINING TILDE -->
+      <map code="0x308" name="uni0308"/><!-- COMBINING DIAERESIS -->
+      <map code="0x324" name="uni0324"/><!-- COMBINING DIAERESIS BELOW -->
+      <map code="0x330" name="uni0330"/><!-- COMBINING TILDE BELOW -->
+      <map code="0x25cc" name="uni25CC"/><!-- DOTTED CIRCLE -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x26" name="ampersand"/><!-- AMPERSAND -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x64" name="d"/><!-- LATIN SMALL LETTER D -->
+      <map code="0x66" name="f"/><!-- LATIN SMALL LETTER F -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x74" name="t"/><!-- LATIN SMALL LETTER T -->
+      <map code="0xe3" name="atilde"/><!-- LATIN SMALL LETTER A WITH TILDE -->
+      <map code="0x303" name="uni0303"/><!-- COMBINING TILDE -->
+      <map code="0x308" name="uni0308"/><!-- COMBINING DIAERESIS -->
+      <map code="0x324" name="uni0324"/><!-- COMBINING DIAERESIS BELOW -->
+      <map code="0x330" name="uni0330"/><!-- COMBINING TILDE BELOW -->
+      <map code="0x25cc" name="uni25CC"/><!-- DOTTED CIRCLE -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="76" yMin="0" xMax="628" yMax="660">
+      <contour>
+        <pt x="76" y="0" on="1"/>
+        <pt x="76" y="660" on="1"/>
+        <pt x="628" y="660" on="1"/>
+        <pt x="628" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="288" y="104" on="1"/>
+        <pt x="416" y="104" on="1"/>
+        <pt x="390" y="160" on="1"/>
+        <pt x="354" y="256" on="1"/>
+        <pt x="350" y="256" on="1"/>
+        <pt x="314" y="160" on="1"/>
+      </contour>
+      <contour>
+        <pt x="350" y="424" on="1"/>
+        <pt x="354" y="424" on="1"/>
+        <pt x="394" y="520" on="1"/>
+        <pt x="412" y="556" on="1"/>
+        <pt x="292" y="556" on="1"/>
+        <pt x="310" y="520" on="1"/>
+      </contour>
+      <contour>
+        <pt x="188" y="172" on="1"/>
+        <pt x="270" y="340" on="1"/>
+        <pt x="188" y="508" on="1"/>
+      </contour>
+      <contour>
+        <pt x="516" y="172" on="1"/>
+        <pt x="516" y="508" on="1"/>
+        <pt x="434" y="340" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="-10" yMin="0" xMax="594" yMax="650">
+      <contour>
+        <pt x="-10" y="0" on="1"/>
+        <pt x="188" y="650" on="1"/>
+        <pt x="396" y="650" on="1"/>
+        <pt x="594" y="0" on="1"/>
+        <pt x="412" y="0" on="1"/>
+        <pt x="338" y="316" on="1"/>
+        <pt x="326" y="362" on="0"/>
+        <pt x="302" y="473" on="0"/>
+        <pt x="290" y="522" on="1"/>
+        <pt x="286" y="522" on="1"/>
+        <pt x="276" y="472" on="0"/>
+        <pt x="252" y="362" on="0"/>
+        <pt x="240" y="316" on="1"/>
+        <pt x="166" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="132" y="138" on="1"/>
+        <pt x="132" y="271" on="1"/>
+        <pt x="450" y="271" on="1"/>
+        <pt x="450" y="138" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A.sc" xMin="-10" yMin="0" xMax="526" yMax="532">
+      <contour>
+        <pt x="-10" y="0" on="1"/>
+        <pt x="155" y="532" on="1"/>
+        <pt x="361" y="532" on="1"/>
+        <pt x="526" y="0" on="1"/>
+        <pt x="346" y="0" on="1"/>
+        <pt x="291" y="244" on="1"/>
+        <pt x="283" y="280" on="0"/>
+        <pt x="266" y="372" on="0"/>
+        <pt x="256" y="409" on="1"/>
+        <pt x="252" y="409" on="1"/>
+        <pt x="244" y="371" on="0"/>
+        <pt x="227" y="280" on="0"/>
+        <pt x="219" y="244" on="1"/>
+        <pt x="164" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="118" y="94" on="1"/>
+        <pt x="118" y="216" on="1"/>
+        <pt x="397" y="216" on="1"/>
+        <pt x="397" y="94" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="a" xMin="38" yMin="-12" xMax="482" yMax="512">
+      <contour>
+        <pt x="188" y="-12" on="1"/>
+        <pt x="118" y="-12" on="0"/>
+        <pt x="38" y="80" on="0"/>
+        <pt x="38" y="142" on="1"/>
+        <pt x="38" y="220" on="0"/>
+        <pt x="166" y="309" on="0"/>
+        <pt x="310" y="324" on="1"/>
+        <pt x="307" y="351" on="0"/>
+        <pt x="273" y="376" on="0"/>
+        <pt x="240" y="376" on="1"/>
+        <pt x="213" y="376" on="0"/>
+        <pt x="153" y="355" on="0"/>
+        <pt x="114" y="334" on="1"/>
+        <pt x="54" y="446" on="1"/>
+        <pt x="106" y="478" on="0"/>
+        <pt x="216" y="512" on="0"/>
+        <pt x="274" y="512" on="1"/>
+        <pt x="372" y="512" on="0"/>
+        <pt x="482" y="401" on="0"/>
+        <pt x="482" y="278" on="1"/>
+        <pt x="482" y="0" on="1"/>
+        <pt x="342" y="0" on="1"/>
+        <pt x="330" y="48" on="1"/>
+        <pt x="326" y="48" on="1"/>
+        <pt x="296" y="21" on="0"/>
+        <pt x="228" y="-12" on="0"/>
+      </contour>
+      <contour>
+        <pt x="248" y="120" on="1"/>
+        <pt x="269" y="120" on="0"/>
+        <pt x="296" y="139" on="0"/>
+        <pt x="310" y="154" on="1"/>
+        <pt x="310" y="222" on="1"/>
+        <pt x="248" y="213" on="0"/>
+        <pt x="202" y="177" on="0"/>
+        <pt x="202" y="156" on="1"/>
+        <pt x="202" y="139" on="0"/>
+        <pt x="226" y="120" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="a.alt" xMin="36" yMin="-12" xMax="522" yMax="512">
+      <contour>
+        <pt x="240" y="-12" on="1"/>
+        <pt x="147" y="-12" on="0"/>
+        <pt x="36" y="128" on="0"/>
+        <pt x="36" y="250" on="1"/>
+        <pt x="36" y="373" on="0"/>
+        <pt x="168" y="512" on="0"/>
+        <pt x="244" y="512" on="1"/>
+        <pt x="280" y="512" on="0"/>
+        <pt x="342" y="483" on="0"/>
+        <pt x="370" y="450" on="1"/>
+        <pt x="374" y="450" on="1"/>
+        <pt x="388" y="500" on="1"/>
+        <pt x="522" y="500" on="1"/>
+        <pt x="522" y="0" on="1"/>
+        <pt x="382" y="0" on="1"/>
+        <pt x="370" y="46" on="1"/>
+        <pt x="366" y="46" on="1"/>
+        <pt x="340" y="20" on="0"/>
+        <pt x="273" y="-12" on="0"/>
+      </contour>
+      <contour>
+        <pt x="286" y="128" on="1"/>
+        <pt x="306" y="128" on="0"/>
+        <pt x="336" y="143" on="0"/>
+        <pt x="350" y="164" on="1"/>
+        <pt x="350" y="348" on="1"/>
+        <pt x="335" y="362" on="0"/>
+        <pt x="298" y="372" on="0"/>
+        <pt x="282" y="372" on="1"/>
+        <pt x="255" y="372" on="0"/>
+        <pt x="212" y="318" on="0"/>
+        <pt x="212" y="252" on="1"/>
+        <pt x="212" y="184" on="0"/>
+        <pt x="252" y="128" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="ampersand" xMin="22" yMin="-12" xMax="670" yMax="662">
+      <contour>
+        <pt x="246" y="-12" on="1"/>
+        <pt x="133" y="-12" on="0"/>
+        <pt x="22" y="102" on="0"/>
+        <pt x="22" y="176" on="1"/>
+        <pt x="22" y="242" on="0"/>
+        <pt x="84" y="326" on="0"/>
+        <pt x="175" y="380" on="0"/>
+        <pt x="266" y="423" on="0"/>
+        <pt x="328" y="471" on="0"/>
+        <pt x="328" y="508" on="1"/>
+        <pt x="328" y="529" on="0"/>
+        <pt x="309" y="550" on="0"/>
+        <pt x="290" y="550" on="1"/>
+        <pt x="270" y="550" on="0"/>
+        <pt x="239" y="518" on="0"/>
+        <pt x="239" y="486" on="1"/>
+        <pt x="239" y="438" on="0"/>
+        <pt x="322" y="326" on="0"/>
+        <pt x="450" y="220" on="0"/>
+        <pt x="518" y="183" on="1"/>
+        <pt x="559" y="160" on="0"/>
+        <pt x="638" y="130" on="0"/>
+        <pt x="670" y="126" on="1"/>
+        <pt x="630" y="-12" on="1"/>
+        <pt x="578" y="-3" on="0"/>
+        <pt x="468" y="42" on="0"/>
+        <pt x="415" y="75" on="1"/>
+        <pt x="330" y="128" on="0"/>
+        <pt x="185" y="272" on="0"/>
+        <pt x="98" y="422" on="0"/>
+        <pt x="98" y="486" on="1"/>
+        <pt x="98" y="556" on="0"/>
+        <pt x="199" y="662" on="0"/>
+        <pt x="286" y="662" on="1"/>
+        <pt x="364" y="662" on="0"/>
+        <pt x="458" y="578" on="0"/>
+        <pt x="458" y="508" on="1"/>
+        <pt x="458" y="442" on="0"/>
+        <pt x="375" y="355" on="0"/>
+        <pt x="267" y="290" on="0"/>
+        <pt x="184" y="227" on="0"/>
+        <pt x="184" y="186" on="1"/>
+        <pt x="184" y="156" on="0"/>
+        <pt x="229" y="118" on="0"/>
+        <pt x="268" y="118" on="1"/>
+        <pt x="300" y="118" on="0"/>
+        <pt x="366" y="152" on="0"/>
+        <pt x="398" y="180" on="1"/>
+        <pt x="436" y="216" on="0"/>
+        <pt x="496" y="312" on="0"/>
+        <pt x="512" y="374" on="1"/>
+        <pt x="668" y="374" on="1"/>
+        <pt x="646" y="300" on="0"/>
+        <pt x="572" y="166" on="0"/>
+        <pt x="516" y="104" on="1"/>
+        <pt x="468" y="52" on="0"/>
+        <pt x="333" y="-12" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="atilde" xMin="38" yMin="-12" xMax="482" yMax="746">
+      <component glyphName="a" x="0" y="0" flags="0x204"/>
+      <component glyphName="uni0303" x="266" y="0" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="d" xMin="36" yMin="-12" xMax="522" yMax="696">
+      <contour>
+        <pt x="240" y="-12" on="1"/>
+        <pt x="147" y="-12" on="0"/>
+        <pt x="36" y="128" on="0"/>
+        <pt x="36" y="250" on="1"/>
+        <pt x="36" y="373" on="0"/>
+        <pt x="164" y="512" on="0"/>
+        <pt x="240" y="512" on="1"/>
+        <pt x="280" y="512" on="0"/>
+        <pt x="334" y="484" on="0"/>
+        <pt x="356" y="462" on="1"/>
+        <pt x="350" y="534" on="1"/>
+        <pt x="350" y="696" on="1"/>
+        <pt x="522" y="696" on="1"/>
+        <pt x="522" y="0" on="1"/>
+        <pt x="382" y="0" on="1"/>
+        <pt x="370" y="46" on="1"/>
+        <pt x="366" y="46" on="1"/>
+        <pt x="340" y="20" on="0"/>
+        <pt x="273" y="-12" on="0"/>
+      </contour>
+      <contour>
+        <pt x="286" y="128" on="1"/>
+        <pt x="306" y="128" on="0"/>
+        <pt x="336" y="143" on="0"/>
+        <pt x="350" y="164" on="1"/>
+        <pt x="350" y="348" on="1"/>
+        <pt x="335" y="362" on="0"/>
+        <pt x="298" y="372" on="0"/>
+        <pt x="282" y="372" on="1"/>
+        <pt x="255" y="372" on="0"/>
+        <pt x="212" y="318" on="0"/>
+        <pt x="212" y="252" on="1"/>
+        <pt x="212" y="184" on="0"/>
+        <pt x="252" y="128" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="f" xMin="22" yMin="0" xMax="392" yMax="708">
+      <contour>
+        <pt x="88" y="0" on="1"/>
+        <pt x="88" y="506" on="1"/>
+        <pt x="88" y="586" on="0"/>
+        <pt x="175" y="708" on="0"/>
+        <pt x="286" y="708" on="1"/>
+        <pt x="320" y="708" on="0"/>
+        <pt x="376" y="696" on="0"/>
+        <pt x="392" y="690" on="1"/>
+        <pt x="362" y="564" on="1"/>
+        <pt x="335" y="574" on="0"/>
+        <pt x="312" y="574" on="1"/>
+        <pt x="288" y="574" on="0"/>
+        <pt x="260" y="547" on="0"/>
+        <pt x="260" y="512" on="1"/>
+        <pt x="260" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="22" y="366" on="1"/>
+        <pt x="22" y="494" on="1"/>
+        <pt x="98" y="500" on="1"/>
+        <pt x="344" y="500" on="1"/>
+        <pt x="344" y="366" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="f_t" xMin="22" yMin="-12" xMax="706" yMax="708">
+      <contour>
+        <pt x="88" y="0" on="1"/>
+        <pt x="88" y="506" on="1"/>
+        <pt x="88" y="586" on="0"/>
+        <pt x="175" y="708" on="0"/>
+        <pt x="286" y="708" on="1"/>
+        <pt x="320" y="708" on="0"/>
+        <pt x="376" y="696" on="0"/>
+        <pt x="392" y="690" on="1"/>
+        <pt x="362" y="564" on="1"/>
+        <pt x="335" y="574" on="0"/>
+        <pt x="312" y="574" on="1"/>
+        <pt x="288" y="574" on="0"/>
+        <pt x="260" y="547" on="0"/>
+        <pt x="260" y="512" on="1"/>
+        <pt x="260" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="588" y="-12" on="1"/>
+        <pt x="490" y="-12" on="0"/>
+        <pt x="404" y="100" on="0"/>
+        <pt x="404" y="192" on="1"/>
+        <pt x="404" y="366" on="1"/>
+        <pt x="22" y="366" on="1"/>
+        <pt x="22" y="494" on="1"/>
+        <pt x="98" y="500" on="1"/>
+        <pt x="414" y="500" on="1"/>
+        <pt x="434" y="630" on="1"/>
+        <pt x="576" y="630" on="1"/>
+        <pt x="576" y="500" on="1"/>
+        <pt x="690" y="500" on="1"/>
+        <pt x="690" y="366" on="1"/>
+        <pt x="576" y="366" on="1"/>
+        <pt x="576" y="195" on="1"/>
+        <pt x="576" y="154" on="0"/>
+        <pt x="612" y="122" on="0"/>
+        <pt x="636" y="122" on="1"/>
+        <pt x="648" y="122" on="0"/>
+        <pt x="671" y="127" on="0"/>
+        <pt x="680" y="130" on="1"/>
+        <pt x="706" y="6" on="1"/>
+        <pt x="686" y="0" on="0"/>
+        <pt x="629" y="-12" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="n" xMin="58" yMin="0" xMax="528" yMax="512">
+      <contour>
+        <pt x="58" y="0" on="1"/>
+        <pt x="58" y="500" on="1"/>
+        <pt x="198" y="500" on="1"/>
+        <pt x="210" y="440" on="1"/>
+        <pt x="214" y="440" on="1"/>
+        <pt x="244" y="468" on="0"/>
+        <pt x="321" y="512" on="0"/>
+        <pt x="372" y="512" on="1"/>
+        <pt x="454" y="512" on="0"/>
+        <pt x="528" y="401" on="0"/>
+        <pt x="528" y="308" on="1"/>
+        <pt x="528" y="0" on="1"/>
+        <pt x="356" y="0" on="1"/>
+        <pt x="356" y="286" on="1"/>
+        <pt x="356" y="334" on="0"/>
+        <pt x="332" y="366" on="0"/>
+        <pt x="306" y="366" on="1"/>
+        <pt x="282" y="366" on="0"/>
+        <pt x="250" y="346" on="0"/>
+        <pt x="230" y="328" on="1"/>
+        <pt x="230" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+    <TTGlyph name="t" xMin="14" yMin="-12" xMax="382" yMax="630">
+      <contour>
+        <pt x="264" y="-12" on="1"/>
+        <pt x="166" y="-12" on="0"/>
+        <pt x="80" y="100" on="0"/>
+        <pt x="80" y="192" on="1"/>
+        <pt x="80" y="366" on="1"/>
+        <pt x="14" y="366" on="1"/>
+        <pt x="14" y="494" on="1"/>
+        <pt x="90" y="500" on="1"/>
+        <pt x="110" y="630" on="1"/>
+        <pt x="252" y="630" on="1"/>
+        <pt x="252" y="500" on="1"/>
+        <pt x="366" y="500" on="1"/>
+        <pt x="366" y="366" on="1"/>
+        <pt x="252" y="366" on="1"/>
+        <pt x="252" y="195" on="1"/>
+        <pt x="252" y="154" on="0"/>
+        <pt x="288" y="122" on="0"/>
+        <pt x="312" y="122" on="1"/>
+        <pt x="324" y="122" on="0"/>
+        <pt x="347" y="127" on="0"/>
+        <pt x="356" y="130" on="1"/>
+        <pt x="382" y="6" on="1"/>
+        <pt x="362" y="0" on="0"/>
+        <pt x="305" y="-12" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0303" xMin="-196" yMin="572" xMax="196" yMax="746">
+      <contour>
+        <pt x="64" y="572" on="1"/>
+        <pt x="34" y="572" on="0"/>
+        <pt x="-12" y="600" on="0"/>
+        <pt x="-47" y="628" on="0"/>
+        <pt x="-60" y="628" on="1"/>
+        <pt x="-72" y="628" on="0"/>
+        <pt x="-87" y="609" on="0"/>
+        <pt x="-90" y="582" on="1"/>
+        <pt x="-196" y="588" on="1"/>
+        <pt x="-194" y="673" on="0"/>
+        <pt x="-124" y="746" on="0"/>
+        <pt x="-64" y="746" on="1"/>
+        <pt x="-34" y="746" on="0"/>
+        <pt x="12" y="718" on="0"/>
+        <pt x="47" y="690" on="0"/>
+        <pt x="60" y="690" on="1"/>
+        <pt x="72" y="690" on="0"/>
+        <pt x="87" y="709" on="0"/>
+        <pt x="90" y="736" on="1"/>
+        <pt x="196" y="730" on="1"/>
+        <pt x="194" y="645" on="0"/>
+        <pt x="124" y="572" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0308" xMin="-194" yMin="562" xMax="194" yMax="722">
+      <contour>
+        <pt x="-114" y="562" on="1"/>
+        <pt x="-149" y="562" on="0"/>
+        <pt x="-194" y="608" on="0"/>
+        <pt x="-194" y="642" on="1"/>
+        <pt x="-194" y="676" on="0"/>
+        <pt x="-149" y="722" on="0"/>
+        <pt x="-114" y="722" on="1"/>
+        <pt x="-79" y="722" on="0"/>
+        <pt x="-34" y="676" on="0"/>
+        <pt x="-34" y="642" on="1"/>
+        <pt x="-34" y="608" on="0"/>
+        <pt x="-79" y="562" on="0"/>
+      </contour>
+      <contour>
+        <pt x="114" y="562" on="1"/>
+        <pt x="79" y="562" on="0"/>
+        <pt x="34" y="608" on="0"/>
+        <pt x="34" y="642" on="1"/>
+        <pt x="34" y="676" on="0"/>
+        <pt x="79" y="722" on="0"/>
+        <pt x="114" y="722" on="1"/>
+        <pt x="149" y="722" on="0"/>
+        <pt x="194" y="676" on="0"/>
+        <pt x="194" y="642" on="1"/>
+        <pt x="194" y="608" on="0"/>
+        <pt x="149" y="562" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0324" xMin="-194" yMin="-224" xMax="194" yMax="-64">
+      <component glyphName="uni0308" x="0" y="-786" flags="0x204"/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0330" xMin="-196" yMin="-228" xMax="196" yMax="-54">
+      <component glyphName="uni0303" x="0" y="-800" flags="0x204"/>
+    </TTGlyph>
+
+    <TTGlyph name="uni25CC" xMin="32" yMin="-13" xMax="543" yMax="512">
+      <contour>
+        <pt x="104" y="96" on="1"/>
+        <pt x="88" y="96" on="0"/>
+        <pt x="61" y="119" on="0"/>
+        <pt x="61" y="141" on="1"/>
+        <pt x="61" y="163" on="0"/>
+        <pt x="88" y="187" on="0"/>
+        <pt x="104" y="187" on="1"/>
+        <pt x="149" y="187" on="0"/>
+        <pt x="149" y="141" on="1"/>
+        <pt x="149" y="96" on="0"/>
+      </contour>
+      <contour>
+        <pt x="76" y="204" on="1"/>
+        <pt x="59" y="204" on="0"/>
+        <pt x="32" y="228" on="0"/>
+        <pt x="32" y="251" on="1"/>
+        <pt x="32" y="272" on="0"/>
+        <pt x="59" y="295" on="0"/>
+        <pt x="76" y="295" on="1"/>
+        <pt x="122" y="295" on="0"/>
+        <pt x="122" y="251" on="1"/>
+        <pt x="122" y="204" on="0"/>
+      </contour>
+      <contour>
+        <pt x="104" y="313" on="1"/>
+        <pt x="88" y="313" on="0"/>
+        <pt x="61" y="338" on="0"/>
+        <pt x="61" y="360" on="1"/>
+        <pt x="61" y="382" on="0"/>
+        <pt x="88" y="405" on="0"/>
+        <pt x="104" y="405" on="1"/>
+        <pt x="149" y="405" on="0"/>
+        <pt x="149" y="360" on="1"/>
+        <pt x="149" y="313" on="0"/>
+      </contour>
+      <contour>
+        <pt x="182" y="14" on="1"/>
+        <pt x="164" y="14" on="0"/>
+        <pt x="137" y="38" on="0"/>
+        <pt x="137" y="61" on="1"/>
+        <pt x="137" y="82" on="0"/>
+        <pt x="164" y="105" on="0"/>
+        <pt x="182" y="105" on="1"/>
+        <pt x="200" y="105" on="0"/>
+        <pt x="227" y="82" on="0"/>
+        <pt x="227" y="61" on="1"/>
+        <pt x="227" y="14" on="0"/>
+      </contour>
+      <contour>
+        <pt x="182" y="395" on="1"/>
+        <pt x="164" y="395" on="0"/>
+        <pt x="137" y="419" on="0"/>
+        <pt x="137" y="440" on="1"/>
+        <pt x="137" y="462" on="0"/>
+        <pt x="164" y="486" on="0"/>
+        <pt x="182" y="486" on="1"/>
+        <pt x="200" y="486" on="0"/>
+        <pt x="227" y="462" on="0"/>
+        <pt x="227" y="440" on="1"/>
+        <pt x="227" y="395" on="0"/>
+      </contour>
+      <contour>
+        <pt x="287" y="-13" on="1"/>
+        <pt x="270" y="-13" on="0"/>
+        <pt x="244" y="12" on="0"/>
+        <pt x="244" y="34" on="1"/>
+        <pt x="244" y="55" on="0"/>
+        <pt x="270" y="78" on="0"/>
+        <pt x="287" y="78" on="1"/>
+        <pt x="332" y="78" on="0"/>
+        <pt x="332" y="34" on="1"/>
+        <pt x="332" y="-13" on="0"/>
+      </contour>
+      <contour>
+        <pt x="287" y="421" on="1"/>
+        <pt x="270" y="421" on="0"/>
+        <pt x="244" y="446" on="0"/>
+        <pt x="244" y="468" on="1"/>
+        <pt x="244" y="489" on="0"/>
+        <pt x="270" y="512" on="0"/>
+        <pt x="287" y="512" on="1"/>
+        <pt x="332" y="512" on="0"/>
+        <pt x="332" y="468" on="1"/>
+        <pt x="332" y="421" on="0"/>
+      </contour>
+      <contour>
+        <pt x="392" y="14" on="1"/>
+        <pt x="375" y="14" on="0"/>
+        <pt x="348" y="38" on="0"/>
+        <pt x="348" y="61" on="1"/>
+        <pt x="348" y="82" on="0"/>
+        <pt x="375" y="105" on="0"/>
+        <pt x="392" y="105" on="1"/>
+        <pt x="411" y="105" on="0"/>
+        <pt x="438" y="82" on="0"/>
+        <pt x="438" y="61" on="1"/>
+        <pt x="438" y="14" on="0"/>
+      </contour>
+      <contour>
+        <pt x="392" y="394" on="1"/>
+        <pt x="375" y="394" on="0"/>
+        <pt x="348" y="418" on="0"/>
+        <pt x="348" y="440" on="1"/>
+        <pt x="348" y="462" on="0"/>
+        <pt x="375" y="486" on="0"/>
+        <pt x="392" y="486" on="1"/>
+        <pt x="411" y="486" on="0"/>
+        <pt x="438" y="462" on="0"/>
+        <pt x="438" y="440" on="1"/>
+        <pt x="438" y="394" on="0"/>
+      </contour>
+      <contour>
+        <pt x="472" y="96" on="1"/>
+        <pt x="453" y="96" on="0"/>
+        <pt x="426" y="119" on="0"/>
+        <pt x="426" y="141" on="1"/>
+        <pt x="426" y="163" on="0"/>
+        <pt x="453" y="187" on="0"/>
+        <pt x="472" y="187" on="1"/>
+        <pt x="516" y="187" on="0"/>
+        <pt x="516" y="141" on="1"/>
+        <pt x="516" y="96" on="0"/>
+      </contour>
+      <contour>
+        <pt x="498" y="204" on="1"/>
+        <pt x="453" y="204" on="0"/>
+        <pt x="453" y="251" on="1"/>
+        <pt x="453" y="295" on="0"/>
+        <pt x="498" y="295" on="1"/>
+        <pt x="516" y="295" on="0"/>
+        <pt x="543" y="272" on="0"/>
+        <pt x="543" y="251" on="1"/>
+        <pt x="543" y="204" on="0"/>
+      </contour>
+      <contour>
+        <pt x="472" y="313" on="1"/>
+        <pt x="453" y="313" on="0"/>
+        <pt x="426" y="337" on="0"/>
+        <pt x="426" y="359" on="1"/>
+        <pt x="426" y="381" on="0"/>
+        <pt x="453" y="404" on="0"/>
+        <pt x="472" y="404" on="1"/>
+        <pt x="516" y="404" on="0"/>
+        <pt x="516" y="359" on="1"/>
+        <pt x="516" y="313" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="9" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Paul D. Hunt
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test Family 2
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      Version 2.020;ADBO;Test Family 2 Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test Family 2
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 2.020
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestFamily2-Master1
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Paul D. Hunt
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Master 1
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name="f_t"/>
+      <psName name="a.alt"/>
+      <psName name="A.sc"/>
+      <psName name="circledotted"/>
+      <psName name="tildecmb"/>
+      <psName name="dieresiscmb"/>
+      <psName name="tildebelowcmb"/>
+      <psName name="dieresisbelowcmb"/>
+      <psName name="uni25CC"/>
+      <psName name="uni0303"/>
+      <psName name="uni0308"/>
+      <psName name="uni0330"/>
+      <psName name="uni0324"/>
+    </extraNames>
+  </post>
+
+  <BASE>
+    <Version value="0x00010000"/>
+    <HorizAxis>
+      <BaseTagList>
+        <!-- BaseTagCount=2 -->
+        <BaselineTag index="0" value="ideo"/>
+        <BaselineTag index="1" value="romn"/>
+      </BaseTagList>
+      <BaseScriptList>
+        <!-- BaseScriptCount=4 -->
+        <BaseScriptRecord index="0">
+          <BaseScriptTag value="DFLT"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="1">
+          <BaseScriptTag value="cyrl"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="2">
+          <BaseScriptTag value="grek"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="3">
+          <BaseScriptTag value="latn"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+      </BaseScriptList>
+    </HorizAxis>
+  </BASE>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Bold.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Bold.ttx
new file mode 100644
index 0000000..a752bc1
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Bold.ttx
@@ -0,0 +1,520 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.14">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="F"/>
+    <GlyphID id="2" name="T"/>
+    <GlyphID id="3" name="l"/>
+    <GlyphID id="4" name="n"/>
+    <GlyphID id="5" name="o"/>
+    <GlyphID id="6" name="s"/>
+    <GlyphID id="7" name="t"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.902"/>
+    <checkSumAdjustment value="0xad920bcc"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Mar 15 19:50:39 2016"/>
+    <modified value="Wed Aug 16 14:03:23 2017"/>
+    <xMin value="25"/>
+    <yMin value="-240"/>
+    <xMax value="606"/>
+    <yMax value="760"/>
+    <macStyle value="00000000 00000001"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1069"/>
+    <descent value="-293"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="670"/>
+    <minLeftSideBearing value="25"/>
+    <minRightSideBearing value="25"/>
+    <xMaxExtent value="606"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="8"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="8"/>
+    <maxPoints value="43"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="530"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="332"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="10"/>
+      <bProportion value="2"/>
+      <bContrast value="4"/>
+      <bStrokeVariation value="5"/>
+      <bArmStyle value="4"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="11100000 00000000 00000010 11111111"/>
+    <ulUnicodeRange2 value="01000000 00000000 00000000 00011111"/>
+    <ulUnicodeRange3 value="00001000 00000000 00000000 00101001"/>
+    <ulUnicodeRange4 value="00000000 00010000 00000000 00000000"/>
+    <achVendID value="GOOG"/>
+    <fsSelection value="00000001 00100000"/>
+    <usFirstCharIndex value="70"/>
+    <usLastCharIndex value="116"/>
+    <sTypoAscender value="1069"/>
+    <sTypoDescender value="-293"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="1069"/>
+    <usWinDescent value="293"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000000"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="553"/>
+    <sCapHeight value="714"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="2"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name="F" width="539" lsb="77"/>
+    <mtx name="T" width="591" lsb="25"/>
+    <mtx name="l" width="323" lsb="66"/>
+    <mtx name="n" width="670" lsb="66"/>
+    <mtx name="o" width="637" lsb="42"/>
+    <mtx name="s" width="517" lsb="42"/>
+    <mtx name="t" width="460" lsb="26"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x46" name="F"/><!-- LATIN CAPITAL LETTER F -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0x6c" name="l"/><!-- LATIN SMALL LETTER L -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x6f" name="o"/><!-- LATIN SMALL LETTER O -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x74" name="t"/><!-- LATIN SMALL LETTER T -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x46" name="F"/><!-- LATIN CAPITAL LETTER F -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0x6c" name="l"/><!-- LATIN SMALL LETTER L -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x6f" name="o"/><!-- LATIN SMALL LETTER O -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x74" name="t"/><!-- LATIN SMALL LETTER T -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="50" yMin="-240" xMax="450" yMax="760">
+      <contour>
+        <pt x="50" y="-240" on="1"/>
+        <pt x="450" y="-240" on="1"/>
+        <pt x="450" y="760" on="1"/>
+        <pt x="50" y="760" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-190" on="1"/>
+        <pt x="100" y="710" on="1"/>
+        <pt x="400" y="710" on="1"/>
+        <pt x="400" y="-190" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="F" xMin="77" yMin="0" xMax="499" yMax="714">
+      <contour>
+        <pt x="267" y="0" on="1"/>
+        <pt x="77" y="0" on="1"/>
+        <pt x="77" y="714" on="1"/>
+        <pt x="499" y="714" on="1"/>
+        <pt x="499" y="559" on="1"/>
+        <pt x="267" y="559" on="1"/>
+        <pt x="267" y="423" on="1"/>
+        <pt x="481" y="423" on="1"/>
+        <pt x="481" y="268" on="1"/>
+        <pt x="267" y="268" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="T" xMin="25" yMin="0" xMax="566" yMax="714">
+      <contour>
+        <pt x="392" y="0" on="1"/>
+        <pt x="199" y="0" on="1"/>
+        <pt x="199" y="556" on="1"/>
+        <pt x="25" y="556" on="1"/>
+        <pt x="25" y="714" on="1"/>
+        <pt x="566" y="714" on="1"/>
+        <pt x="566" y="556" on="1"/>
+        <pt x="392" y="556" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="l" xMin="66" yMin="0" xMax="257" yMax="760">
+      <contour>
+        <pt x="257" y="0" on="1"/>
+        <pt x="66" y="0" on="1"/>
+        <pt x="66" y="760" on="1"/>
+        <pt x="257" y="760" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="n" xMin="66" yMin="0" xMax="606" yMax="563">
+      <contour>
+        <pt x="412" y="563" on="1"/>
+        <pt x="498" y="563" on="0"/>
+        <pt x="606" y="466" on="0"/>
+        <pt x="606" y="360" on="1"/>
+        <pt x="606" y="0" on="1"/>
+        <pt x="415" y="0" on="1"/>
+        <pt x="415" y="302" on="1"/>
+        <pt x="415" y="357" on="0"/>
+        <pt x="383" y="413" on="0"/>
+        <pt x="348" y="413" on="1"/>
+        <pt x="294" y="413" on="0"/>
+        <pt x="257" y="324" on="0"/>
+        <pt x="257" y="242" on="1"/>
+        <pt x="257" y="0" on="1"/>
+        <pt x="66" y="0" on="1"/>
+        <pt x="66" y="553" on="1"/>
+        <pt x="210" y="553" on="1"/>
+        <pt x="236" y="480" on="1"/>
+        <pt x="243" y="480" on="1"/>
+        <pt x="260" y="506" on="0"/>
+        <pt x="310" y="543" on="0"/>
+        <pt x="373" y="563" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="o" xMin="42" yMin="-10" xMax="594" yMax="563">
+      <contour>
+        <pt x="594" y="278" on="1"/>
+        <pt x="594" y="209" on="0"/>
+        <pt x="556" y="101" on="0"/>
+        <pt x="484" y="28" on="0"/>
+        <pt x="382" y="-10" on="0"/>
+        <pt x="317" y="-10" on="1"/>
+        <pt x="257" y="-10" on="0"/>
+        <pt x="156" y="28" on="0"/>
+        <pt x="83" y="101" on="0"/>
+        <pt x="42" y="209" on="0"/>
+        <pt x="42" y="278" on="1"/>
+        <pt x="42" y="370" on="0"/>
+        <pt x="109" y="497" on="0"/>
+        <pt x="233" y="563" on="0"/>
+        <pt x="320" y="563" on="1"/>
+        <pt x="400" y="563" on="0"/>
+        <pt x="523" y="497" on="0"/>
+        <pt x="594" y="370" on="0"/>
+      </contour>
+      <contour>
+        <pt x="236" y="278" on="1"/>
+        <pt x="236" y="230" on="0"/>
+        <pt x="252" y="165" on="0"/>
+        <pt x="289" y="132" on="0"/>
+        <pt x="319" y="132" on="1"/>
+        <pt x="348" y="132" on="0"/>
+        <pt x="384" y="165" on="0"/>
+        <pt x="400" y="230" on="0"/>
+        <pt x="400" y="278" on="1"/>
+        <pt x="400" y="325" on="0"/>
+        <pt x="384" y="389" on="0"/>
+        <pt x="348" y="421" on="0"/>
+        <pt x="318" y="421" on="1"/>
+        <pt x="274" y="421" on="0"/>
+        <pt x="236" y="348" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="s" xMin="42" yMin="-10" xMax="477" yMax="563">
+      <contour>
+        <pt x="477" y="170" on="1"/>
+        <pt x="477" y="118" on="0"/>
+        <pt x="430" y="36" on="0"/>
+        <pt x="327" y="-10" on="0"/>
+        <pt x="243" y="-10" on="1"/>
+        <pt x="184" y="-10" on="0"/>
+        <pt x="91" y="3" on="0"/>
+        <pt x="43" y="21" on="1"/>
+        <pt x="43" y="174" on="1"/>
+        <pt x="96" y="150" on="0"/>
+        <pt x="204" y="129" on="0"/>
+        <pt x="235" y="129" on="1"/>
+        <pt x="267" y="129" on="0"/>
+        <pt x="297" y="143" on="0"/>
+        <pt x="297" y="157" on="1"/>
+        <pt x="297" y="169" on="0"/>
+        <pt x="277" y="187" on="0"/>
+        <pt x="226" y="209" on="0"/>
+        <pt x="179" y="228" on="1"/>
+        <pt x="133" y="247" on="0"/>
+        <pt x="72" y="292" on="0"/>
+        <pt x="42" y="354" on="0"/>
+        <pt x="42" y="400" on="1"/>
+        <pt x="42" y="481" on="0"/>
+        <pt x="167" y="563" on="0"/>
+        <pt x="270" y="563" on="1"/>
+        <pt x="325" y="563" on="0"/>
+        <pt x="421" y="539" on="0"/>
+        <pt x="473" y="516" on="1"/>
+        <pt x="421" y="393" on="1"/>
+        <pt x="380" y="412" on="0"/>
+        <pt x="296" y="434" on="0"/>
+        <pt x="271" y="434" on="1"/>
+        <pt x="248" y="434" on="0"/>
+        <pt x="224" y="422" on="0"/>
+        <pt x="224" y="411" on="1"/>
+        <pt x="224" y="400" on="0"/>
+        <pt x="241" y="385" on="0"/>
+        <pt x="289" y="364" on="0"/>
+        <pt x="335" y="346" on="1"/>
+        <pt x="383" y="326" on="0"/>
+        <pt x="446" y="282" on="0"/>
+        <pt x="477" y="218" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="t" xMin="26" yMin="-10" xMax="429" yMax="664">
+      <contour>
+        <pt x="337" y="141" on="1"/>
+        <pt x="362" y="141" on="0"/>
+        <pt x="406" y="152" on="0"/>
+        <pt x="429" y="160" on="1"/>
+        <pt x="429" y="21" on="1"/>
+        <pt x="398" y="8" on="0"/>
+        <pt x="328" y="-10" on="0"/>
+        <pt x="274" y="-10" on="1"/>
+        <pt x="220" y="-10" on="0"/>
+        <pt x="138" y="24" on="0"/>
+        <pt x="93" y="107" on="0"/>
+        <pt x="93" y="182" on="1"/>
+        <pt x="93" y="410" on="1"/>
+        <pt x="26" y="410" on="1"/>
+        <pt x="26" y="488" on="1"/>
+        <pt x="111" y="548" on="1"/>
+        <pt x="160" y="664" on="1"/>
+        <pt x="285" y="664" on="1"/>
+        <pt x="285" y="553" on="1"/>
+        <pt x="421" y="553" on="1"/>
+        <pt x="421" y="410" on="1"/>
+        <pt x="285" y="410" on="1"/>
+        <pt x="285" y="195" on="1"/>
+        <pt x="285" y="168" on="0"/>
+        <pt x="312" y="141" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright 2015 Google Inc. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test Family 3
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Bold
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.902;GOOG;TestFamily3-Bold
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test Family 3 Bold
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.902
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestFamily3-Bold
+    </namerecord>
+    <namerecord nameID="7" platformID="3" platEncID="1" langID="0x409">
+      Noto is a trademark of Google Inc.
+    </namerecord>
+    <namerecord nameID="8" platformID="3" platEncID="1" langID="0x409">
+      Monotype Imaging Inc.
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Monotype Design Team
+    </namerecord>
+    <namerecord nameID="10" platformID="3" platEncID="1" langID="0x409">
+      Designed by Monotype design team.
+    </namerecord>
+    <namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
+      http://www.google.com/get/noto/
+    </namerecord>
+    <namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
+      http://www.monotype.com/studio
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      This Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License for the specific language, permissions and limitations governing your use of this Font Software.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://scripts.sil.org/OFL
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-100"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+    </extraNames>
+  </post>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="T"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1 Format="2">
+          </ClassDef1>
+          <ClassDef2 Format="1">
+            <ClassDef glyph="T" class="4"/>
+            <ClassDef glyph="n" class="3"/>
+            <ClassDef glyph="o" class="2"/>
+            <ClassDef glyph="s" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=1 -->
+          <!-- Class2Count=5 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="-60"/>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="-70"/>
+            </Class2Record>
+            <Class2Record index="3">
+              <Value1 XAdvance="-50"/>
+            </Class2Record>
+            <Class2Record index="4">
+              <Value1 XAdvance="20"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Condensed.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Condensed.ttx
new file mode 100644
index 0000000..db0372a
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Condensed.ttx
@@ -0,0 +1,526 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.14">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="F"/>
+    <GlyphID id="2" name="T"/>
+    <GlyphID id="3" name="l"/>
+    <GlyphID id="4" name="n"/>
+    <GlyphID id="5" name="o"/>
+    <GlyphID id="6" name="s"/>
+    <GlyphID id="7" name="t"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.902"/>
+    <checkSumAdjustment value="0x7c6d2210"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Mar 15 19:50:39 2016"/>
+    <modified value="Wed Aug 16 14:03:23 2017"/>
+    <xMin value="10"/>
+    <yMin value="-240"/>
+    <xMax value="450"/>
+    <yMax value="760"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1069"/>
+    <descent value="-293"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="500"/>
+    <minLeftSideBearing value="10"/>
+    <minRightSideBearing value="11"/>
+    <xMaxExtent value="450"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="8"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="8"/>
+    <maxPoints value="43"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="371"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="322"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="5"/>
+      <bProportion value="6"/>
+      <bContrast value="4"/>
+      <bStrokeVariation value="5"/>
+      <bArmStyle value="4"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="11100000 00000000 00000010 11111111"/>
+    <ulUnicodeRange2 value="01000000 00000000 00000000 00011111"/>
+    <ulUnicodeRange3 value="00001000 00000000 00000000 00101001"/>
+    <ulUnicodeRange4 value="00000000 00010000 00000000 00000000"/>
+    <achVendID value="GOOG"/>
+    <fsSelection value="00000001 01000000"/>
+    <usFirstCharIndex value="70"/>
+    <usLastCharIndex value="116"/>
+    <sTypoAscender value="1069"/>
+    <sTypoDescender value="-293"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="1069"/>
+    <usWinDescent value="293"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000000"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="537"/>
+    <sCapHeight value="714"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="2"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name="F" width="380" lsb="73"/>
+    <mtx name="T" width="381" lsb="10"/>
+    <mtx name="l" width="210" lsb="65"/>
+    <mtx name="n" width="456" lsb="65"/>
+    <mtx name="o" width="445" lsb="42"/>
+    <mtx name="s" width="339" lsb="33"/>
+    <mtx name="t" width="259" lsb="17"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x46" name="F"/><!-- LATIN CAPITAL LETTER F -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0x6c" name="l"/><!-- LATIN SMALL LETTER L -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x6f" name="o"/><!-- LATIN SMALL LETTER O -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x74" name="t"/><!-- LATIN SMALL LETTER T -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x46" name="F"/><!-- LATIN CAPITAL LETTER F -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0x6c" name="l"/><!-- LATIN SMALL LETTER L -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x6f" name="o"/><!-- LATIN SMALL LETTER O -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x74" name="t"/><!-- LATIN SMALL LETTER T -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="50" yMin="-240" xMax="450" yMax="760">
+      <contour>
+        <pt x="50" y="-240" on="1"/>
+        <pt x="450" y="-240" on="1"/>
+        <pt x="450" y="760" on="1"/>
+        <pt x="50" y="760" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-190" on="1"/>
+        <pt x="100" y="710" on="1"/>
+        <pt x="400" y="710" on="1"/>
+        <pt x="400" y="-190" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="F" xMin="73" yMin="0" xMax="357" yMax="714">
+      <contour>
+        <pt x="157" y="0" on="1"/>
+        <pt x="73" y="0" on="1"/>
+        <pt x="73" y="714" on="1"/>
+        <pt x="357" y="714" on="1"/>
+        <pt x="357" y="639" on="1"/>
+        <pt x="157" y="639" on="1"/>
+        <pt x="157" y="381" on="1"/>
+        <pt x="345" y="381" on="1"/>
+        <pt x="345" y="305" on="1"/>
+        <pt x="157" y="305" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="T" xMin="10" yMin="0" xMax="370" yMax="714">
+      <contour>
+        <pt x="232" y="0" on="1"/>
+        <pt x="148" y="0" on="1"/>
+        <pt x="148" y="638" on="1"/>
+        <pt x="10" y="638" on="1"/>
+        <pt x="10" y="714" on="1"/>
+        <pt x="370" y="714" on="1"/>
+        <pt x="370" y="638" on="1"/>
+        <pt x="232" y="638" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="l" xMin="65" yMin="0" xMax="146" yMax="760">
+      <contour>
+        <pt x="146" y="0" on="1"/>
+        <pt x="65" y="0" on="1"/>
+        <pt x="65" y="760" on="1"/>
+        <pt x="146" y="760" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="n" xMin="65" yMin="0" xMax="393" yMax="547">
+      <contour>
+        <pt x="262" y="547" on="1"/>
+        <pt x="326" y="547" on="0"/>
+        <pt x="393" y="457" on="0"/>
+        <pt x="393" y="364" on="1"/>
+        <pt x="393" y="0" on="1"/>
+        <pt x="312" y="0" on="1"/>
+        <pt x="312" y="348" on="1"/>
+        <pt x="312" y="411" on="0"/>
+        <pt x="280" y="475" on="0"/>
+        <pt x="244" y="475" on="1"/>
+        <pt x="192" y="475" on="0"/>
+        <pt x="146" y="382" on="0"/>
+        <pt x="146" y="279" on="1"/>
+        <pt x="146" y="0" on="1"/>
+        <pt x="65" y="0" on="1"/>
+        <pt x="65" y="537" on="1"/>
+        <pt x="130" y="537" on="1"/>
+        <pt x="139" y="464" on="1"/>
+        <pt x="144" y="464" on="1"/>
+        <pt x="156" y="490" on="0"/>
+        <pt x="191" y="528" on="0"/>
+        <pt x="236" y="547" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="o" xMin="42" yMin="-10" xMax="403" yMax="547">
+      <contour>
+        <pt x="403" y="269" on="1"/>
+        <pt x="403" y="206" on="0"/>
+        <pt x="381" y="104" on="0"/>
+        <pt x="337" y="30" on="0"/>
+        <pt x="268" y="-10" on="0"/>
+        <pt x="221" y="-10" on="1"/>
+        <pt x="176" y="-10" on="0"/>
+        <pt x="109" y="30" on="0"/>
+        <pt x="64" y="103" on="0"/>
+        <pt x="42" y="206" on="0"/>
+        <pt x="42" y="269" on="1"/>
+        <pt x="42" y="358" on="0"/>
+        <pt x="81" y="482" on="0"/>
+        <pt x="161" y="547" on="0"/>
+        <pt x="223" y="547" on="1"/>
+        <pt x="280" y="547" on="0"/>
+        <pt x="360" y="484" on="0"/>
+        <pt x="403" y="360" on="0"/>
+      </contour>
+      <contour>
+        <pt x="125" y="269" on="1"/>
+        <pt x="125" y="200" on="0"/>
+        <pt x="146" y="108" on="0"/>
+        <pt x="189" y="61" on="0"/>
+        <pt x="223" y="61" on="1"/>
+        <pt x="256" y="61" on="0"/>
+        <pt x="300" y="107" on="0"/>
+        <pt x="321" y="200" on="0"/>
+        <pt x="321" y="269" on="1"/>
+        <pt x="321" y="338" on="0"/>
+        <pt x="300" y="430" on="0"/>
+        <pt x="256" y="476" on="0"/>
+        <pt x="223" y="476" on="1"/>
+        <pt x="171" y="476" on="0"/>
+        <pt x="125" y="372" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="s" xMin="33" yMin="-10" xMax="307" yMax="547">
+      <contour>
+        <pt x="307" y="144" on="1"/>
+        <pt x="307" y="94" on="0"/>
+        <pt x="271" y="26" on="0"/>
+        <pt x="203" y="-10" on="0"/>
+        <pt x="155" y="-10" on="1"/>
+        <pt x="118" y="-10" on="0"/>
+        <pt x="56" y="7" on="0"/>
+        <pt x="33" y="20" on="1"/>
+        <pt x="33" y="104" on="1"/>
+        <pt x="55" y="86" on="0"/>
+        <pt x="119" y="63" on="0"/>
+        <pt x="152" y="63" on="1"/>
+        <pt x="187" y="63" on="0"/>
+        <pt x="227" y="104" on="0"/>
+        <pt x="227" y="141" on="1"/>
+        <pt x="227" y="162" on="0"/>
+        <pt x="211" y="195" on="0"/>
+        <pt x="175" y="225" on="0"/>
+        <pt x="145" y="242" on="1"/>
+        <pt x="112" y="262" on="0"/>
+        <pt x="61" y="306" on="0"/>
+        <pt x="33" y="364" on="0"/>
+        <pt x="33" y="406" on="1"/>
+        <pt x="33" y="470" on="0"/>
+        <pt x="116" y="547" on="0"/>
+        <pt x="183" y="547" on="1"/>
+        <pt x="218" y="547" on="0"/>
+        <pt x="278" y="529" on="0"/>
+        <pt x="306" y="512" on="1"/>
+        <pt x="276" y="447" on="1"/>
+        <pt x="255" y="461" on="0"/>
+        <pt x="209" y="478" on="0"/>
+        <pt x="184" y="478" on="1"/>
+        <pt x="150" y="478" on="0"/>
+        <pt x="111" y="440" on="0"/>
+        <pt x="111" y="408" on="1"/>
+        <pt x="111" y="386" on="0"/>
+        <pt x="127" y="355" on="0"/>
+        <pt x="164" y="326" on="0"/>
+        <pt x="196" y="307" on="1"/>
+        <pt x="229" y="286" on="0"/>
+        <pt x="279" y="242" on="0"/>
+        <pt x="307" y="185" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="t" xMin="17" yMin="-10" xMax="245" yMax="658">
+      <contour>
+        <pt x="197" y="62" on="1"/>
+        <pt x="209" y="62" on="0"/>
+        <pt x="233" y="68" on="0"/>
+        <pt x="245" y="72" on="1"/>
+        <pt x="245" y="6" on="1"/>
+        <pt x="229" y="-2" on="0"/>
+        <pt x="192" y="-10" on="0"/>
+        <pt x="170" y="-10" on="1"/>
+        <pt x="134" y="-10" on="0"/>
+        <pt x="89" y="22" on="0"/>
+        <pt x="67" y="85" on="0"/>
+        <pt x="67" y="133" on="1"/>
+        <pt x="67" y="469" on="1"/>
+        <pt x="17" y="469" on="1"/>
+        <pt x="17" y="513" on="1"/>
+        <pt x="71" y="535" on="1"/>
+        <pt x="93" y="658" on="1"/>
+        <pt x="148" y="658" on="1"/>
+        <pt x="148" y="537" on="1"/>
+        <pt x="238" y="537" on="1"/>
+        <pt x="238" y="469" on="1"/>
+        <pt x="148" y="469" on="1"/>
+        <pt x="148" y="143" on="1"/>
+        <pt x="148" y="102" on="0"/>
+        <pt x="168" y="62" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright 2015 Google Inc. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test Family 3 Condensed
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.902;GOOG;TestFamily3-Condensed
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test Family 3 Condensed
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.902
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestFamily3-Condensed
+    </namerecord>
+    <namerecord nameID="7" platformID="3" platEncID="1" langID="0x409">
+      Noto is a trademark of Google Inc.
+    </namerecord>
+    <namerecord nameID="8" platformID="3" platEncID="1" langID="0x409">
+      Monotype Imaging Inc.
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Monotype Design Team
+    </namerecord>
+    <namerecord nameID="10" platformID="3" platEncID="1" langID="0x409">
+      Designed by Monotype design team.
+    </namerecord>
+    <namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
+      http://www.google.com/get/noto/
+    </namerecord>
+    <namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
+      http://www.monotype.com/studio
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      This Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License for the specific language, permissions and limitations governing your use of this Font Software.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://scripts.sil.org/OFL
+    </namerecord>
+    <namerecord nameID="16" platformID="3" platEncID="1" langID="0x409">
+      Test Family 3
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Condensed
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-100"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+    </extraNames>
+  </post>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="T"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1 Format="2">
+          </ClassDef1>
+          <ClassDef2 Format="1">
+            <ClassDef glyph="T" class="4"/>
+            <ClassDef glyph="n" class="3"/>
+            <ClassDef glyph="o" class="2"/>
+            <ClassDef glyph="s" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=1 -->
+          <!-- Class2Count=5 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="-36"/>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="-42"/>
+            </Class2Record>
+            <Class2Record index="3">
+              <Value1 XAdvance="-30"/>
+            </Class2Record>
+            <Class2Record index="4">
+              <Value1 XAdvance="12"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-CondensedBold.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-CondensedBold.ttx
new file mode 100644
index 0000000..3313ce6
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-CondensedBold.ttx
@@ -0,0 +1,526 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.14">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="F"/>
+    <GlyphID id="2" name="T"/>
+    <GlyphID id="3" name="l"/>
+    <GlyphID id="4" name="n"/>
+    <GlyphID id="5" name="o"/>
+    <GlyphID id="6" name="s"/>
+    <GlyphID id="7" name="t"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.902"/>
+    <checkSumAdjustment value="0x7946b7f2"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Mar 15 19:50:39 2016"/>
+    <modified value="Wed Aug 16 14:03:23 2017"/>
+    <xMin value="7"/>
+    <yMin value="-240"/>
+    <xMax value="483"/>
+    <yMax value="760"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1069"/>
+    <descent value="-293"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="529"/>
+    <minLeftSideBearing value="7"/>
+    <minRightSideBearing value="17"/>
+    <xMaxExtent value="483"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="8"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="8"/>
+    <maxPoints value="43"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="432"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="332"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="10"/>
+      <bProportion value="6"/>
+      <bContrast value="4"/>
+      <bStrokeVariation value="5"/>
+      <bArmStyle value="4"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="11100000 00000000 00000010 11111111"/>
+    <ulUnicodeRange2 value="01000000 00000000 00000000 00011111"/>
+    <ulUnicodeRange3 value="00001000 00000000 00000000 00101001"/>
+    <ulUnicodeRange4 value="00000000 00010000 00000000 00000000"/>
+    <achVendID value="GOOG"/>
+    <fsSelection value="00000001 01000000"/>
+    <usFirstCharIndex value="70"/>
+    <usLastCharIndex value="116"/>
+    <sTypoAscender value="1069"/>
+    <sTypoDescender value="-293"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="1069"/>
+    <usWinDescent value="293"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000000"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="553"/>
+    <sCapHeight value="714"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="2"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name="F" width="436" lsb="59"/>
+    <mtx name="T" width="460" lsb="17"/>
+    <mtx name="l" width="263" lsb="50"/>
+    <mtx name="n" width="529" lsb="50"/>
+    <mtx name="o" width="516" lsb="32"/>
+    <mtx name="s" width="396" lsb="25"/>
+    <mtx name="t" width="354" lsb="7"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x46" name="F"/><!-- LATIN CAPITAL LETTER F -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0x6c" name="l"/><!-- LATIN SMALL LETTER L -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x6f" name="o"/><!-- LATIN SMALL LETTER O -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x74" name="t"/><!-- LATIN SMALL LETTER T -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x46" name="F"/><!-- LATIN CAPITAL LETTER F -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0x6c" name="l"/><!-- LATIN SMALL LETTER L -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x6f" name="o"/><!-- LATIN SMALL LETTER O -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x74" name="t"/><!-- LATIN SMALL LETTER T -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="50" yMin="-240" xMax="450" yMax="760">
+      <contour>
+        <pt x="50" y="-240" on="1"/>
+        <pt x="450" y="-240" on="1"/>
+        <pt x="450" y="760" on="1"/>
+        <pt x="50" y="760" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-190" on="1"/>
+        <pt x="100" y="710" on="1"/>
+        <pt x="400" y="710" on="1"/>
+        <pt x="400" y="-190" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="F" xMin="59" yMin="0" xMax="401" yMax="714">
+      <contour>
+        <pt x="229" y="0" on="1"/>
+        <pt x="59" y="0" on="1"/>
+        <pt x="59" y="714" on="1"/>
+        <pt x="401" y="714" on="1"/>
+        <pt x="401" y="570" on="1"/>
+        <pt x="229" y="570" on="1"/>
+        <pt x="229" y="417" on="1"/>
+        <pt x="388" y="417" on="1"/>
+        <pt x="388" y="273" on="1"/>
+        <pt x="229" y="273" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="T" xMin="17" yMin="0" xMax="443" yMax="714">
+      <contour>
+        <pt x="315" y="0" on="1"/>
+        <pt x="146" y="0" on="1"/>
+        <pt x="146" y="567" on="1"/>
+        <pt x="17" y="567" on="1"/>
+        <pt x="17" y="714" on="1"/>
+        <pt x="443" y="714" on="1"/>
+        <pt x="443" y="567" on="1"/>
+        <pt x="315" y="567" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="l" xMin="50" yMin="0" xMax="212" yMax="760">
+      <contour>
+        <pt x="212" y="0" on="1"/>
+        <pt x="50" y="0" on="1"/>
+        <pt x="50" y="760" on="1"/>
+        <pt x="212" y="760" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="n" xMin="50" yMin="0" xMax="480" yMax="563">
+      <contour>
+        <pt x="328" y="563" on="1"/>
+        <pt x="400" y="563" on="0"/>
+        <pt x="480" y="457" on="0"/>
+        <pt x="480" y="360" on="1"/>
+        <pt x="480" y="0" on="1"/>
+        <pt x="318" y="0" on="1"/>
+        <pt x="318" y="308" on="1"/>
+        <pt x="318" y="363" on="0"/>
+        <pt x="300" y="419" on="0"/>
+        <pt x="270" y="419" on="1"/>
+        <pt x="236" y="419" on="0"/>
+        <pt x="212" y="347" on="0"/>
+        <pt x="212" y="253" on="1"/>
+        <pt x="212" y="0" on="1"/>
+        <pt x="50" y="0" on="1"/>
+        <pt x="50" y="553" on="1"/>
+        <pt x="177" y="553" on="1"/>
+        <pt x="195" y="485" on="1"/>
+        <pt x="204" y="485" on="1"/>
+        <pt x="216" y="511" on="0"/>
+        <pt x="252" y="546" on="0"/>
+        <pt x="298" y="563" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="o" xMin="32" yMin="-10" xMax="483" yMax="563">
+      <contour>
+        <pt x="483" y="278" on="1"/>
+        <pt x="483" y="225" on="0"/>
+        <pt x="460" y="122" on="0"/>
+        <pt x="408" y="39" on="0"/>
+        <pt x="321" y="-10" on="0"/>
+        <pt x="257" y="-10" on="1"/>
+        <pt x="198" y="-10" on="0"/>
+        <pt x="113" y="38" on="0"/>
+        <pt x="58" y="120" on="0"/>
+        <pt x="32" y="223" on="0"/>
+        <pt x="32" y="278" on="1"/>
+        <pt x="32" y="358" on="0"/>
+        <pt x="78" y="487" on="0"/>
+        <pt x="178" y="563" on="0"/>
+        <pt x="259" y="563" on="1"/>
+        <pt x="326" y="563" on="0"/>
+        <pt x="427" y="496" on="0"/>
+        <pt x="483" y="369" on="0"/>
+      </contour>
+      <contour>
+        <pt x="196" y="276" on="1"/>
+        <pt x="196" y="226" on="0"/>
+        <pt x="209" y="159" on="0"/>
+        <pt x="236" y="125" on="0"/>
+        <pt x="258" y="125" on="1"/>
+        <pt x="280" y="125" on="0"/>
+        <pt x="307" y="159" on="0"/>
+        <pt x="319" y="227" on="0"/>
+        <pt x="319" y="278" on="1"/>
+        <pt x="319" y="328" on="0"/>
+        <pt x="307" y="395" on="0"/>
+        <pt x="280" y="428" on="0"/>
+        <pt x="258" y="428" on="1"/>
+        <pt x="226" y="428" on="0"/>
+        <pt x="196" y="354" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="s" xMin="25" yMin="-10" xMax="372" yMax="563">
+      <contour>
+        <pt x="372" y="170" on="1"/>
+        <pt x="372" y="111" on="0"/>
+        <pt x="327" y="30" on="0"/>
+        <pt x="242" y="-10" on="0"/>
+        <pt x="183" y="-10" on="1"/>
+        <pt x="142" y="-10" on="0"/>
+        <pt x="64" y="4" on="0"/>
+        <pt x="26" y="21" on="1"/>
+        <pt x="26" y="174" on="1"/>
+        <pt x="56" y="156" on="0"/>
+        <pt x="128" y="129" on="0"/>
+        <pt x="166" y="129" on="1"/>
+        <pt x="188" y="129" on="0"/>
+        <pt x="211" y="144" on="0"/>
+        <pt x="211" y="161" on="1"/>
+        <pt x="211" y="170" on="0"/>
+        <pt x="202" y="188" on="0"/>
+        <pt x="166" y="212" on="0"/>
+        <pt x="128" y="231" on="1"/>
+        <pt x="94" y="248" on="0"/>
+        <pt x="48" y="297" on="0"/>
+        <pt x="25" y="361" on="0"/>
+        <pt x="25" y="400" on="1"/>
+        <pt x="25" y="477" on="0"/>
+        <pt x="119" y="563" on="0"/>
+        <pt x="205" y="563" on="1"/>
+        <pt x="248" y="563" on="0"/>
+        <pt x="324" y="539" on="0"/>
+        <pt x="364" y="516" on="1"/>
+        <pt x="324" y="393" on="1"/>
+        <pt x="299" y="410" on="0"/>
+        <pt x="244" y="434" on="0"/>
+        <pt x="215" y="434" on="1"/>
+        <pt x="198" y="434" on="0"/>
+        <pt x="181" y="422" on="0"/>
+        <pt x="181" y="408" on="1"/>
+        <pt x="181" y="399" on="0"/>
+        <pt x="190" y="385" on="0"/>
+        <pt x="224" y="363" on="0"/>
+        <pt x="259" y="342" on="1"/>
+        <pt x="292" y="323" on="0"/>
+        <pt x="343" y="276" on="0"/>
+        <pt x="372" y="212" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="t" xMin="7" yMin="-10" xMax="336" yMax="664">
+      <contour>
+        <pt x="265" y="130" on="1"/>
+        <pt x="280" y="130" on="0"/>
+        <pt x="316" y="140" on="0"/>
+        <pt x="336" y="149" on="1"/>
+        <pt x="336" y="21" on="1"/>
+        <pt x="309" y="6" on="0"/>
+        <pt x="250" y="-10" on="0"/>
+        <pt x="216" y="-10" on="1"/>
+        <pt x="162" y="-10" on="0"/>
+        <pt x="94" y="32" on="0"/>
+        <pt x="63" y="117" on="0"/>
+        <pt x="63" y="182" on="1"/>
+        <pt x="63" y="420" on="1"/>
+        <pt x="7" y="420" on="1"/>
+        <pt x="7" y="506" on="1"/>
+        <pt x="77" y="548" on="1"/>
+        <pt x="115" y="664" on="1"/>
+        <pt x="225" y="664" on="1"/>
+        <pt x="225" y="553" on="1"/>
+        <pt x="329" y="553" on="1"/>
+        <pt x="329" y="420" on="1"/>
+        <pt x="225" y="420" on="1"/>
+        <pt x="225" y="184" on="1"/>
+        <pt x="225" y="157" on="0"/>
+        <pt x="245" y="130" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright 2015 Google Inc. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test Family 3 Condensed Bold
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.902;GOOG;TestFamily3-CondensedBold
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test Family 3 Condensed Bold
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.902
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestFamily3-CondensedBold
+    </namerecord>
+    <namerecord nameID="7" platformID="3" platEncID="1" langID="0x409">
+      Noto is a trademark of Google Inc.
+    </namerecord>
+    <namerecord nameID="8" platformID="3" platEncID="1" langID="0x409">
+      Monotype Imaging Inc.
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Monotype Design Team
+    </namerecord>
+    <namerecord nameID="10" platformID="3" platEncID="1" langID="0x409">
+      Designed by Monotype design team.
+    </namerecord>
+    <namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
+      http://www.google.com/get/noto/
+    </namerecord>
+    <namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
+      http://www.monotype.com/studio
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      This Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License for the specific language, permissions and limitations governing your use of this Font Software.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://scripts.sil.org/OFL
+    </namerecord>
+    <namerecord nameID="16" platformID="3" platEncID="1" langID="0x409">
+      Test Family 3
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Condensed Bold
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-100"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+    </extraNames>
+  </post>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="T"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1 Format="2">
+          </ClassDef1>
+          <ClassDef2 Format="1">
+            <ClassDef glyph="T" class="4"/>
+            <ClassDef glyph="n" class="3"/>
+            <ClassDef glyph="o" class="2"/>
+            <ClassDef glyph="s" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=1 -->
+          <!-- Class2Count=5 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="-36"/>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="-42"/>
+            </Class2Record>
+            <Class2Record index="3">
+              <Value1 XAdvance="-30"/>
+            </Class2Record>
+            <Class2Record index="4">
+              <Value1 XAdvance="12"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-CondensedLight.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-CondensedLight.ttx
new file mode 100644
index 0000000..c83912b
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-CondensedLight.ttx
@@ -0,0 +1,526 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.14">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="F"/>
+    <GlyphID id="2" name="T"/>
+    <GlyphID id="3" name="l"/>
+    <GlyphID id="4" name="n"/>
+    <GlyphID id="5" name="o"/>
+    <GlyphID id="6" name="s"/>
+    <GlyphID id="7" name="t"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.902"/>
+    <checkSumAdjustment value="0x52bdf18e"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Mar 15 19:50:39 2016"/>
+    <modified value="Wed Aug 16 14:03:23 2017"/>
+    <xMin value="7"/>
+    <yMin value="-232"/>
+    <xMax value="450"/>
+    <yMax value="760"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1069"/>
+    <descent value="-293"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="500"/>
+    <minLeftSideBearing value="7"/>
+    <minRightSideBearing value="8"/>
+    <xMaxExtent value="450"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="8"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="8"/>
+    <maxPoints value="43"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="336"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="316"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="2"/>
+      <bProportion value="6"/>
+      <bContrast value="4"/>
+      <bStrokeVariation value="5"/>
+      <bArmStyle value="4"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="11100000 00000000 00000010 11111111"/>
+    <ulUnicodeRange2 value="01000000 00000000 00000000 00011111"/>
+    <ulUnicodeRange3 value="00001000 00000000 00000000 00101001"/>
+    <ulUnicodeRange4 value="00000000 00010000 00000000 00000000"/>
+    <achVendID value="GOOG"/>
+    <fsSelection value="00000001 01000000"/>
+    <usFirstCharIndex value="70"/>
+    <usLastCharIndex value="116"/>
+    <sTypoAscender value="1069"/>
+    <sTypoDescender value="-293"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="1069"/>
+    <usWinDescent value="293"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000000"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="527"/>
+    <sCapHeight value="714"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="2"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name="F" width="352" lsb="78"/>
+    <mtx name="T" width="336" lsb="7"/>
+    <mtx name="l" width="167" lsb="71"/>
+    <mtx name="n" width="408" lsb="71"/>
+    <mtx name="o" width="403" lsb="46"/>
+    <mtx name="s" width="312" lsb="36"/>
+    <mtx name="t" width="209" lsb="21"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x46" name="F"/><!-- LATIN CAPITAL LETTER F -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0x6c" name="l"/><!-- LATIN SMALL LETTER L -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x6f" name="o"/><!-- LATIN SMALL LETTER O -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x74" name="t"/><!-- LATIN SMALL LETTER T -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x46" name="F"/><!-- LATIN CAPITAL LETTER F -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0x6c" name="l"/><!-- LATIN SMALL LETTER L -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x6f" name="o"/><!-- LATIN SMALL LETTER O -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x74" name="t"/><!-- LATIN SMALL LETTER T -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="50" yMin="-232" xMax="450" yMax="760">
+      <contour>
+        <pt x="50" y="-232" on="1"/>
+        <pt x="450" y="-232" on="1"/>
+        <pt x="450" y="760" on="1"/>
+        <pt x="50" y="760" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-182" on="1"/>
+        <pt x="100" y="710" on="1"/>
+        <pt x="400" y="710" on="1"/>
+        <pt x="400" y="-182" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="F" xMin="78" yMin="0" xMax="334" yMax="714">
+      <contour>
+        <pt x="104" y="0" on="1"/>
+        <pt x="78" y="0" on="1"/>
+        <pt x="78" y="714" on="1"/>
+        <pt x="334" y="714" on="1"/>
+        <pt x="334" y="689" on="1"/>
+        <pt x="104" y="689" on="1"/>
+        <pt x="104" y="354" on="1"/>
+        <pt x="322" y="354" on="1"/>
+        <pt x="322" y="329" on="1"/>
+        <pt x="104" y="329" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="T" xMin="7" yMin="0" xMax="328" yMax="714">
+      <contour>
+        <pt x="180" y="0" on="1"/>
+        <pt x="154" y="0" on="1"/>
+        <pt x="154" y="689" on="1"/>
+        <pt x="7" y="689" on="1"/>
+        <pt x="7" y="714" on="1"/>
+        <pt x="328" y="714" on="1"/>
+        <pt x="328" y="689" on="1"/>
+        <pt x="180" y="689" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="l" xMin="71" yMin="0" xMax="97" yMax="760">
+      <contour>
+        <pt x="97" y="0" on="1"/>
+        <pt x="71" y="0" on="1"/>
+        <pt x="71" y="760" on="1"/>
+        <pt x="97" y="760" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="n" xMin="71" yMin="0" xMax="339" yMax="537">
+      <contour>
+        <pt x="225" y="537" on="1"/>
+        <pt x="280" y="537" on="0"/>
+        <pt x="339" y="460" on="0"/>
+        <pt x="339" y="375" on="1"/>
+        <pt x="339" y="0" on="1"/>
+        <pt x="313" y="0" on="1"/>
+        <pt x="313" y="365" on="1"/>
+        <pt x="313" y="444" on="0"/>
+        <pt x="266" y="513" on="0"/>
+        <pt x="225" y="513" on="1"/>
+        <pt x="173" y="513" on="0"/>
+        <pt x="97" y="412" on="0"/>
+        <pt x="97" y="311" on="1"/>
+        <pt x="97" y="0" on="1"/>
+        <pt x="71" y="0" on="1"/>
+        <pt x="71" y="527" on="1"/>
+        <pt x="91" y="527" on="1"/>
+        <pt x="92" y="415" on="1"/>
+        <pt x="94" y="415" on="1"/>
+        <pt x="101" y="444" on="0"/>
+        <pt x="132" y="501" on="0"/>
+        <pt x="185" y="537" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="o" xMin="46" yMin="-10" xMax="357" yMax="537">
+      <contour>
+        <pt x="357" y="264" on="1"/>
+        <pt x="357" y="194" on="0"/>
+        <pt x="338" y="91" on="0"/>
+        <pt x="300" y="23" on="0"/>
+        <pt x="242" y="-10" on="0"/>
+        <pt x="204" y="-10" on="1"/>
+        <pt x="165" y="-10" on="0"/>
+        <pt x="106" y="24" on="0"/>
+        <pt x="66" y="93" on="0"/>
+        <pt x="46" y="196" on="0"/>
+        <pt x="46" y="266" on="1"/>
+        <pt x="46" y="355" on="0"/>
+        <pt x="80" y="476" on="0"/>
+        <pt x="149" y="537" on="0"/>
+        <pt x="202" y="537" on="1"/>
+        <pt x="258" y="537" on="0"/>
+        <pt x="327" y="471" on="0"/>
+        <pt x="357" y="348" on="0"/>
+      </contour>
+      <contour>
+        <pt x="72" y="266" on="1"/>
+        <pt x="72" y="184" on="0"/>
+        <pt x="100" y="72" on="0"/>
+        <pt x="158" y="15" on="0"/>
+        <pt x="203" y="15" on="1"/>
+        <pt x="247" y="15" on="0"/>
+        <pt x="304" y="69" on="0"/>
+        <pt x="331" y="180" on="0"/>
+        <pt x="331" y="265" on="1"/>
+        <pt x="331" y="342" on="0"/>
+        <pt x="306" y="452" on="0"/>
+        <pt x="250" y="512" on="0"/>
+        <pt x="202" y="512" on="1"/>
+        <pt x="133" y="512" on="0"/>
+        <pt x="72" y="384" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="s" xMin="36" yMin="-10" xMax="278" yMax="537">
+      <contour>
+        <pt x="278" y="123" on="1"/>
+        <pt x="278" y="83" on="0"/>
+        <pt x="249" y="23" on="0"/>
+        <pt x="191" y="-10" on="0"/>
+        <pt x="147" y="-10" on="1"/>
+        <pt x="110" y="-10" on="0"/>
+        <pt x="53" y="10" on="0"/>
+        <pt x="36" y="21" on="1"/>
+        <pt x="36" y="52" on="1"/>
+        <pt x="58" y="36" on="0"/>
+        <pt x="116" y="16" on="0"/>
+        <pt x="147" y="16" on="1"/>
+        <pt x="200" y="16" on="0"/>
+        <pt x="251" y="73" on="0"/>
+        <pt x="251" y="125" on="1"/>
+        <pt x="251" y="160" on="0"/>
+        <pt x="229" y="203" on="0"/>
+        <pt x="186" y="236" on="0"/>
+        <pt x="157" y="254" on="1"/>
+        <pt x="123" y="275" on="0"/>
+        <pt x="70" y="316" on="0"/>
+        <pt x="39" y="369" on="0"/>
+        <pt x="39" y="408" on="1"/>
+        <pt x="39" y="461" on="0"/>
+        <pt x="105" y="537" on="0"/>
+        <pt x="175" y="537" on="1"/>
+        <pt x="204" y="537" on="0"/>
+        <pt x="256" y="523" on="0"/>
+        <pt x="275" y="510" on="1"/>
+        <pt x="262" y="487" on="1"/>
+        <pt x="246" y="499" on="0"/>
+        <pt x="198" y="512" on="0"/>
+        <pt x="174" y="512" on="1"/>
+        <pt x="125" y="512" on="0"/>
+        <pt x="65" y="458" on="0"/>
+        <pt x="65" y="407" on="1"/>
+        <pt x="65" y="378" on="0"/>
+        <pt x="87" y="336" on="0"/>
+        <pt x="130" y="300" on="0"/>
+        <pt x="162" y="280" on="1"/>
+        <pt x="195" y="260" on="0"/>
+        <pt x="247" y="221" on="0"/>
+        <pt x="278" y="166" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="t" xMin="21" yMin="-10" xMax="196" yMax="656">
+      <contour>
+        <pt x="147" y="14" on="1"/>
+        <pt x="160" y="14" on="0"/>
+        <pt x="184" y="20" on="0"/>
+        <pt x="192" y="25" on="1"/>
+        <pt x="192" y="0" on="1"/>
+        <pt x="182" y="-4" on="0"/>
+        <pt x="160" y="-10" on="0"/>
+        <pt x="146" y="-10" on="1"/>
+        <pt x="116" y="-10" on="0"/>
+        <pt x="83" y="18" on="0"/>
+        <pt x="71" y="72" on="0"/>
+        <pt x="71" y="112" on="1"/>
+        <pt x="71" y="503" on="1"/>
+        <pt x="21" y="503" on="1"/>
+        <pt x="21" y="521" on="1"/>
+        <pt x="69" y="528" on="1"/>
+        <pt x="76" y="656" on="1"/>
+        <pt x="97" y="656" on="1"/>
+        <pt x="97" y="527" on="1"/>
+        <pt x="196" y="527" on="1"/>
+        <pt x="196" y="503" on="1"/>
+        <pt x="97" y="503" on="1"/>
+        <pt x="97" y="108" on="1"/>
+        <pt x="97" y="60" on="0"/>
+        <pt x="117" y="14" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright 2015 Google Inc. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test Family 3 Condensed Light
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.902;GOOG;TestFamily3-CondensedLight
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test Family 3 Condensed Light
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.902
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestFamily3-CondensedLight
+    </namerecord>
+    <namerecord nameID="7" platformID="3" platEncID="1" langID="0x409">
+      Noto is a trademark of Google Inc.
+    </namerecord>
+    <namerecord nameID="8" platformID="3" platEncID="1" langID="0x409">
+      Monotype Imaging Inc.
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Monotype Design Team
+    </namerecord>
+    <namerecord nameID="10" platformID="3" platEncID="1" langID="0x409">
+      Designed by Monotype design team.
+    </namerecord>
+    <namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
+      http://www.google.com/get/noto/
+    </namerecord>
+    <namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
+      http://www.monotype.com/studio
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      This Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License for the specific language, permissions and limitations governing your use of this Font Software.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://scripts.sil.org/OFL
+    </namerecord>
+    <namerecord nameID="16" platformID="3" platEncID="1" langID="0x409">
+      Test Family 3
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Condensed Light
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-100"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+    </extraNames>
+  </post>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="T"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1 Format="2">
+          </ClassDef1>
+          <ClassDef2 Format="1">
+            <ClassDef glyph="T" class="4"/>
+            <ClassDef glyph="n" class="3"/>
+            <ClassDef glyph="o" class="2"/>
+            <ClassDef glyph="s" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=1 -->
+          <!-- Class2Count=5 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="-36"/>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="-42"/>
+            </Class2Record>
+            <Class2Record index="3">
+              <Value1 XAdvance="-30"/>
+            </Class2Record>
+            <Class2Record index="4">
+              <Value1 XAdvance="12"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-CondensedSemiBold.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-CondensedSemiBold.ttx
new file mode 100644
index 0000000..7b0c88f
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-CondensedSemiBold.ttx
@@ -0,0 +1,526 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.14">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="F"/>
+    <GlyphID id="2" name="T"/>
+    <GlyphID id="3" name="l"/>
+    <GlyphID id="4" name="n"/>
+    <GlyphID id="5" name="o"/>
+    <GlyphID id="6" name="s"/>
+    <GlyphID id="7" name="t"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.902"/>
+    <checkSumAdjustment value="0x52e61490"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Mar 15 19:50:39 2016"/>
+    <modified value="Wed Aug 16 14:03:23 2017"/>
+    <xMin value="11"/>
+    <yMin value="-240"/>
+    <xMax value="452"/>
+    <yMax value="760"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1069"/>
+    <descent value="-293"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="501"/>
+    <minLeftSideBearing value="11"/>
+    <minRightSideBearing value="15"/>
+    <xMaxExtent value="452"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="8"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="8"/>
+    <maxPoints value="43"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="408"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="328"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="8"/>
+      <bProportion value="6"/>
+      <bContrast value="4"/>
+      <bStrokeVariation value="5"/>
+      <bArmStyle value="4"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="11100000 00000000 00000010 11111111"/>
+    <ulUnicodeRange2 value="01000000 00000000 00000000 00011111"/>
+    <ulUnicodeRange3 value="00001000 00000000 00000000 00101001"/>
+    <ulUnicodeRange4 value="00000000 00010000 00000000 00000000"/>
+    <achVendID value="GOOG"/>
+    <fsSelection value="00000001 01000000"/>
+    <usFirstCharIndex value="70"/>
+    <usLastCharIndex value="116"/>
+    <sTypoAscender value="1069"/>
+    <sTypoDescender value="-293"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="1069"/>
+    <usWinDescent value="293"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000000"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="547"/>
+    <sCapHeight value="714"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="2"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name="F" width="414" lsb="64"/>
+    <mtx name="T" width="429" lsb="14"/>
+    <mtx name="l" width="242" lsb="56"/>
+    <mtx name="n" width="501" lsb="56"/>
+    <mtx name="o" width="488" lsb="36"/>
+    <mtx name="s" width="374" lsb="28"/>
+    <mtx name="t" width="317" lsb="11"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x46" name="F"/><!-- LATIN CAPITAL LETTER F -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0x6c" name="l"/><!-- LATIN SMALL LETTER L -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x6f" name="o"/><!-- LATIN SMALL LETTER O -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x74" name="t"/><!-- LATIN SMALL LETTER T -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x46" name="F"/><!-- LATIN CAPITAL LETTER F -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0x6c" name="l"/><!-- LATIN SMALL LETTER L -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x6f" name="o"/><!-- LATIN SMALL LETTER O -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x74" name="t"/><!-- LATIN SMALL LETTER T -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="50" yMin="-240" xMax="450" yMax="760">
+      <contour>
+        <pt x="50" y="-240" on="1"/>
+        <pt x="450" y="-240" on="1"/>
+        <pt x="450" y="760" on="1"/>
+        <pt x="50" y="760" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-190" on="1"/>
+        <pt x="100" y="710" on="1"/>
+        <pt x="400" y="710" on="1"/>
+        <pt x="400" y="-190" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="F" xMin="64" yMin="0" xMax="383" yMax="714">
+      <contour>
+        <pt x="201" y="0" on="1"/>
+        <pt x="64" y="0" on="1"/>
+        <pt x="64" y="714" on="1"/>
+        <pt x="383" y="714" on="1"/>
+        <pt x="383" y="597" on="1"/>
+        <pt x="201" y="597" on="1"/>
+        <pt x="201" y="403" on="1"/>
+        <pt x="371" y="403" on="1"/>
+        <pt x="371" y="286" on="1"/>
+        <pt x="201" y="286" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="T" xMin="14" yMin="0" xMax="414" yMax="714">
+      <contour>
+        <pt x="282" y="0" on="1"/>
+        <pt x="147" y="0" on="1"/>
+        <pt x="147" y="595" on="1"/>
+        <pt x="14" y="595" on="1"/>
+        <pt x="14" y="714" on="1"/>
+        <pt x="414" y="714" on="1"/>
+        <pt x="414" y="595" on="1"/>
+        <pt x="282" y="595" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="l" xMin="56" yMin="0" xMax="186" yMax="760">
+      <contour>
+        <pt x="186" y="0" on="1"/>
+        <pt x="56" y="0" on="1"/>
+        <pt x="56" y="760" on="1"/>
+        <pt x="186" y="760" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="n" xMin="56" yMin="0" xMax="446" yMax="557">
+      <contour>
+        <pt x="302" y="557" on="1"/>
+        <pt x="371" y="557" on="0"/>
+        <pt x="446" y="457" on="0"/>
+        <pt x="446" y="362" on="1"/>
+        <pt x="446" y="0" on="1"/>
+        <pt x="316" y="0" on="1"/>
+        <pt x="316" y="324" on="1"/>
+        <pt x="316" y="382" on="0"/>
+        <pt x="292" y="441" on="0"/>
+        <pt x="260" y="441" on="1"/>
+        <pt x="219" y="441" on="0"/>
+        <pt x="186" y="360" on="0"/>
+        <pt x="186" y="263" on="1"/>
+        <pt x="186" y="0" on="1"/>
+        <pt x="56" y="0" on="1"/>
+        <pt x="56" y="547" on="1"/>
+        <pt x="159" y="547" on="1"/>
+        <pt x="173" y="477" on="1"/>
+        <pt x="181" y="477" on="1"/>
+        <pt x="193" y="503" on="0"/>
+        <pt x="229" y="539" on="0"/>
+        <pt x="274" y="557" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="o" xMin="36" yMin="-10" xMax="452" yMax="557">
+      <contour>
+        <pt x="452" y="275" on="1"/>
+        <pt x="452" y="218" on="0"/>
+        <pt x="429" y="115" on="0"/>
+        <pt x="380" y="36" on="0"/>
+        <pt x="300" y="-10" on="0"/>
+        <pt x="243" y="-10" on="1"/>
+        <pt x="190" y="-10" on="0"/>
+        <pt x="112" y="35" on="0"/>
+        <pt x="61" y="114" on="0"/>
+        <pt x="36" y="217" on="0"/>
+        <pt x="36" y="275" on="1"/>
+        <pt x="36" y="358" on="0"/>
+        <pt x="79" y="485" on="0"/>
+        <pt x="172" y="557" on="0"/>
+        <pt x="245" y="557" on="1"/>
+        <pt x="308" y="557" on="0"/>
+        <pt x="401" y="492" on="0"/>
+        <pt x="452" y="366" on="0"/>
+      </contour>
+      <contour>
+        <pt x="168" y="273" on="1"/>
+        <pt x="168" y="216" on="0"/>
+        <pt x="184" y="139" on="0"/>
+        <pt x="218" y="100" on="0"/>
+        <pt x="244" y="100" on="1"/>
+        <pt x="271" y="100" on="0"/>
+        <pt x="304" y="139" on="0"/>
+        <pt x="319" y="216" on="0"/>
+        <pt x="319" y="275" on="1"/>
+        <pt x="319" y="332" on="0"/>
+        <pt x="304" y="409" on="0"/>
+        <pt x="271" y="447" on="0"/>
+        <pt x="244" y="447" on="1"/>
+        <pt x="204" y="447" on="0"/>
+        <pt x="168" y="362" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="s" xMin="28" yMin="-10" xMax="347" yMax="557">
+      <contour>
+        <pt x="347" y="160" on="1"/>
+        <pt x="347" y="104" on="0"/>
+        <pt x="305" y="29" on="0"/>
+        <pt x="227" y="-10" on="0"/>
+        <pt x="172" y="-10" on="1"/>
+        <pt x="132" y="-10" on="0"/>
+        <pt x="60" y="5" on="0"/>
+        <pt x="29" y="21" on="1"/>
+        <pt x="29" y="147" on="1"/>
+        <pt x="55" y="129" on="0"/>
+        <pt x="125" y="103" on="0"/>
+        <pt x="160" y="103" on="1"/>
+        <pt x="188" y="103" on="0"/>
+        <pt x="217" y="128" on="0"/>
+        <pt x="217" y="153" on="1"/>
+        <pt x="217" y="166" on="0"/>
+        <pt x="205" y="190" on="0"/>
+        <pt x="170" y="217" on="0"/>
+        <pt x="135" y="235" on="1"/>
+        <pt x="101" y="254" on="0"/>
+        <pt x="53" y="301" on="0"/>
+        <pt x="28" y="362" on="0"/>
+        <pt x="28" y="402" on="1"/>
+        <pt x="28" y="474" on="0"/>
+        <pt x="118" y="557" on="0"/>
+        <pt x="196" y="557" on="1"/>
+        <pt x="236" y="557" on="0"/>
+        <pt x="306" y="535" on="0"/>
+        <pt x="341" y="514" on="1"/>
+        <pt x="305" y="414" on="1"/>
+        <pt x="282" y="430" on="0"/>
+        <pt x="230" y="451" on="0"/>
+        <pt x="203" y="451" on="1"/>
+        <pt x="179" y="451" on="0"/>
+        <pt x="154" y="428" on="0"/>
+        <pt x="154" y="408" on="1"/>
+        <pt x="154" y="394" on="0"/>
+        <pt x="166" y="373" on="0"/>
+        <pt x="200" y="348" on="0"/>
+        <pt x="234" y="328" on="1"/>
+        <pt x="268" y="308" on="0"/>
+        <pt x="318" y="263" on="0"/>
+        <pt x="347" y="202" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="t" xMin="11" yMin="-10" xMax="300" yMax="662">
+      <contour>
+        <pt x="239" y="104" on="1"/>
+        <pt x="252" y="104" on="0"/>
+        <pt x="284" y="112" on="0"/>
+        <pt x="300" y="119" on="1"/>
+        <pt x="300" y="15" on="1"/>
+        <pt x="278" y="3" on="0"/>
+        <pt x="227" y="-10" on="0"/>
+        <pt x="198" y="-10" on="1"/>
+        <pt x="151" y="-10" on="0"/>
+        <pt x="92" y="28" on="0"/>
+        <pt x="65" y="104" on="0"/>
+        <pt x="65" y="163" on="1"/>
+        <pt x="65" y="439" on="1"/>
+        <pt x="11" y="439" on="1"/>
+        <pt x="11" y="509" on="1"/>
+        <pt x="75" y="543" on="1"/>
+        <pt x="106" y="662" on="1"/>
+        <pt x="195" y="662" on="1"/>
+        <pt x="195" y="547" on="1"/>
+        <pt x="293" y="547" on="1"/>
+        <pt x="293" y="439" on="1"/>
+        <pt x="195" y="439" on="1"/>
+        <pt x="195" y="168" on="1"/>
+        <pt x="195" y="136" on="0"/>
+        <pt x="215" y="104" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright 2015 Google Inc. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test Family 3 Condensed SemiBold
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.902;GOOG;TestFamily3-CondensedSemiBold
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test Family 3 Condensed SemiBold
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.902
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestFamily3-CondensedSemiBold
+    </namerecord>
+    <namerecord nameID="7" platformID="3" platEncID="1" langID="0x409">
+      Noto is a trademark of Google Inc.
+    </namerecord>
+    <namerecord nameID="8" platformID="3" platEncID="1" langID="0x409">
+      Monotype Imaging Inc.
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Monotype Design Team
+    </namerecord>
+    <namerecord nameID="10" platformID="3" platEncID="1" langID="0x409">
+      Designed by Monotype design team.
+    </namerecord>
+    <namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
+      http://www.google.com/get/noto/
+    </namerecord>
+    <namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
+      http://www.monotype.com/studio
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      This Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License for the specific language, permissions and limitations governing your use of this Font Software.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://scripts.sil.org/OFL
+    </namerecord>
+    <namerecord nameID="16" platformID="3" platEncID="1" langID="0x409">
+      Test Family 3
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Condensed SemiBold
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-100"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+    </extraNames>
+  </post>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="T"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1 Format="2">
+          </ClassDef1>
+          <ClassDef2 Format="1">
+            <ClassDef glyph="T" class="4"/>
+            <ClassDef glyph="n" class="3"/>
+            <ClassDef glyph="o" class="2"/>
+            <ClassDef glyph="s" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=1 -->
+          <!-- Class2Count=5 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="-36"/>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="-42"/>
+            </Class2Record>
+            <Class2Record index="3">
+              <Value1 XAdvance="-30"/>
+            </Class2Record>
+            <Class2Record index="4">
+              <Value1 XAdvance="12"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Light.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Light.ttx
new file mode 100644
index 0000000..0e84719
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Light.ttx
@@ -0,0 +1,526 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.14">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="F"/>
+    <GlyphID id="2" name="T"/>
+    <GlyphID id="3" name="l"/>
+    <GlyphID id="4" name="n"/>
+    <GlyphID id="5" name="o"/>
+    <GlyphID id="6" name="s"/>
+    <GlyphID id="7" name="t"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.902"/>
+    <checkSumAdjustment value="0x4e9cc8d4"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Mar 15 19:50:39 2016"/>
+    <modified value="Wed Aug 16 14:03:23 2017"/>
+    <xMin value="2"/>
+    <yMin value="-240"/>
+    <xMax value="515"/>
+    <yMax value="760"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1069"/>
+    <descent value="-293"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="575"/>
+    <minLeftSideBearing value="2"/>
+    <minRightSideBearing value="2"/>
+    <xMaxExtent value="515"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="8"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="8"/>
+    <maxPoints value="43"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="454"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="317"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="2"/>
+      <bProportion value="2"/>
+      <bContrast value="4"/>
+      <bStrokeVariation value="5"/>
+      <bArmStyle value="4"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="11100000 00000000 00000010 11111111"/>
+    <ulUnicodeRange2 value="01000000 00000000 00000000 00011111"/>
+    <ulUnicodeRange3 value="00001000 00000000 00000000 00101001"/>
+    <ulUnicodeRange4 value="00000000 00010000 00000000 00000000"/>
+    <achVendID value="GOOG"/>
+    <fsSelection value="00000001 01000000"/>
+    <usFirstCharIndex value="70"/>
+    <usLastCharIndex value="116"/>
+    <sTypoAscender value="1069"/>
+    <sTypoDescender value="-293"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="1069"/>
+    <usWinDescent value="293"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000000"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="528"/>
+    <sCapHeight value="714"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="2"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name="F" width="492" lsb="103"/>
+    <mtx name="T" width="505" lsb="2"/>
+    <mtx name="l" width="207" lsb="91"/>
+    <mtx name="n" width="573" lsb="90"/>
+    <mtx name="o" width="575" lsb="59"/>
+    <mtx name="s" width="458" lsb="39"/>
+    <mtx name="t" width="319" lsb="10"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x46" name="F"/><!-- LATIN CAPITAL LETTER F -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0x6c" name="l"/><!-- LATIN SMALL LETTER L -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x6f" name="o"/><!-- LATIN SMALL LETTER O -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x74" name="t"/><!-- LATIN SMALL LETTER T -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x46" name="F"/><!-- LATIN CAPITAL LETTER F -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0x6c" name="l"/><!-- LATIN SMALL LETTER L -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x6f" name="o"/><!-- LATIN SMALL LETTER O -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x74" name="t"/><!-- LATIN SMALL LETTER T -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="50" yMin="-240" xMax="450" yMax="760">
+      <contour>
+        <pt x="50" y="-240" on="1"/>
+        <pt x="450" y="-240" on="1"/>
+        <pt x="450" y="760" on="1"/>
+        <pt x="50" y="760" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-190" on="1"/>
+        <pt x="100" y="710" on="1"/>
+        <pt x="400" y="710" on="1"/>
+        <pt x="400" y="-190" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="F" xMin="103" yMin="0" xMax="489" yMax="714">
+      <contour>
+        <pt x="129" y="0" on="1"/>
+        <pt x="103" y="0" on="1"/>
+        <pt x="103" y="714" on="1"/>
+        <pt x="489" y="714" on="1"/>
+        <pt x="489" y="689" on="1"/>
+        <pt x="129" y="689" on="1"/>
+        <pt x="129" y="354" on="1"/>
+        <pt x="470" y="354" on="1"/>
+        <pt x="470" y="329" on="1"/>
+        <pt x="129" y="329" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="T" xMin="2" yMin="0" xMax="503" yMax="714">
+      <contour>
+        <pt x="265" y="0" on="1"/>
+        <pt x="239" y="0" on="1"/>
+        <pt x="239" y="689" on="1"/>
+        <pt x="2" y="689" on="1"/>
+        <pt x="2" y="714" on="1"/>
+        <pt x="503" y="714" on="1"/>
+        <pt x="503" y="689" on="1"/>
+        <pt x="265" y="689" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="l" xMin="91" yMin="0" xMax="117" yMax="760">
+      <contour>
+        <pt x="117" y="0" on="1"/>
+        <pt x="91" y="0" on="1"/>
+        <pt x="91" y="760" on="1"/>
+        <pt x="117" y="760" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="n" xMin="90" yMin="0" xMax="489" yMax="538">
+      <contour>
+        <pt x="309" y="538" on="1"/>
+        <pt x="394" y="538" on="0"/>
+        <pt x="489" y="444" on="0"/>
+        <pt x="489" y="346" on="1"/>
+        <pt x="489" y="0" on="1"/>
+        <pt x="463" y="0" on="1"/>
+        <pt x="463" y="345" on="1"/>
+        <pt x="463" y="433" on="0"/>
+        <pt x="382" y="513" on="0"/>
+        <pt x="309" y="513" on="1"/>
+        <pt x="223" y="513" on="0"/>
+        <pt x="116" y="410" on="0"/>
+        <pt x="116" y="302" on="1"/>
+        <pt x="116" y="0" on="1"/>
+        <pt x="90" y="0" on="1"/>
+        <pt x="90" y="528" on="1"/>
+        <pt x="111" y="528" on="1"/>
+        <pt x="115" y="417" on="1"/>
+        <pt x="117" y="417" on="1"/>
+        <pt x="128" y="448" on="0"/>
+        <pt x="176" y="503" on="0"/>
+        <pt x="253" y="538" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="o" xMin="59" yMin="-10" xMax="515" yMax="538">
+      <contour>
+        <pt x="515" y="264" on="1"/>
+        <pt x="515" y="206" on="0"/>
+        <pt x="488" y="106" on="0"/>
+        <pt x="431" y="32" on="0"/>
+        <pt x="345" y="-10" on="0"/>
+        <pt x="286" y="-10" on="1"/>
+        <pt x="229" y="-10" on="0"/>
+        <pt x="144" y="31" on="0"/>
+        <pt x="87" y="105" on="0"/>
+        <pt x="59" y="205" on="0"/>
+        <pt x="59" y="264" on="1"/>
+        <pt x="59" y="348" on="0"/>
+        <pt x="114" y="471" on="0"/>
+        <pt x="218" y="538" on="0"/>
+        <pt x="292" y="538" on="1"/>
+        <pt x="372" y="538" on="0"/>
+        <pt x="470" y="465" on="0"/>
+        <pt x="515" y="340" on="0"/>
+      </contour>
+      <contour>
+        <pt x="86" y="264" on="1"/>
+        <pt x="86" y="190" on="0"/>
+        <pt x="130" y="78" on="0"/>
+        <pt x="218" y="15" on="0"/>
+        <pt x="286" y="15" on="1"/>
+        <pt x="356" y="15" on="0"/>
+        <pt x="445" y="79" on="0"/>
+        <pt x="488" y="192" on="0"/>
+        <pt x="488" y="264" on="1"/>
+        <pt x="488" y="333" on="0"/>
+        <pt x="449" y="446" on="0"/>
+        <pt x="362" y="513" on="0"/>
+        <pt x="292" y="513" on="1"/>
+        <pt x="193" y="513" on="0"/>
+        <pt x="86" y="381" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="s" xMin="39" yMin="-10" xMax="411" yMax="538">
+      <contour>
+        <pt x="411" y="134" on="1"/>
+        <pt x="411" y="90" on="0"/>
+        <pt x="367" y="25" on="0"/>
+        <pt x="279" y="-10" on="0"/>
+        <pt x="213" y="-10" on="1"/>
+        <pt x="158" y="-10" on="0"/>
+        <pt x="68" y="11" on="0"/>
+        <pt x="39" y="24" on="1"/>
+        <pt x="39" y="54" on="1"/>
+        <pt x="79" y="34" on="0"/>
+        <pt x="166" y="15" on="0"/>
+        <pt x="213" y="15" on="1"/>
+        <pt x="304" y="15" on="0"/>
+        <pt x="384" y="78" on="0"/>
+        <pt x="384" y="134" on="1"/>
+        <pt x="384" y="173" on="0"/>
+        <pt x="341" y="220" on="0"/>
+        <pt x="268" y="249" on="0"/>
+        <pt x="224" y="262" on="1"/>
+        <pt x="178" y="276" on="0"/>
+        <pt x="101" y="305" on="0"/>
+        <pt x="56" y="358" on="0"/>
+        <pt x="56" y="407" on="1"/>
+        <pt x="56" y="468" on="0"/>
+        <pt x="154" y="538" on="0"/>
+        <pt x="238" y="538" on="1"/>
+        <pt x="285" y="538" on="0"/>
+        <pt x="366" y="521" on="0"/>
+        <pt x="398" y="508" on="1"/>
+        <pt x="387" y="483" on="1"/>
+        <pt x="359" y="496" on="0"/>
+        <pt x="278" y="513" on="0"/>
+        <pt x="238" y="513" on="1"/>
+        <pt x="166" y="513" on="0"/>
+        <pt x="83" y="460" on="0"/>
+        <pt x="83" y="407" on="1"/>
+        <pt x="83" y="366" on="0"/>
+        <pt x="124" y="323" on="0"/>
+        <pt x="192" y="299" on="0"/>
+        <pt x="233" y="286" on="1"/>
+        <pt x="277" y="272" on="0"/>
+        <pt x="359" y="240" on="0"/>
+        <pt x="411" y="184" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="t" xMin="10" yMin="-10" xMax="291" yMax="659">
+      <contour>
+        <pt x="212" y="15" on="1"/>
+        <pt x="237" y="15" on="0"/>
+        <pt x="274" y="22" on="0"/>
+        <pt x="291" y="28" on="1"/>
+        <pt x="291" y="3" on="1"/>
+        <pt x="274" y="-2" on="0"/>
+        <pt x="238" y="-10" on="0"/>
+        <pt x="212" y="-10" on="1"/>
+        <pt x="166" y="-10" on="0"/>
+        <pt x="113" y="26" on="0"/>
+        <pt x="91" y="94" on="0"/>
+        <pt x="91" y="140" on="1"/>
+        <pt x="91" y="503" on="1"/>
+        <pt x="10" y="503" on="1"/>
+        <pt x="10" y="525" on="1"/>
+        <pt x="90" y="528" on="1"/>
+        <pt x="96" y="659" on="1"/>
+        <pt x="117" y="659" on="1"/>
+        <pt x="117" y="528" on="1"/>
+        <pt x="289" y="528" on="1"/>
+        <pt x="289" y="503" on="1"/>
+        <pt x="117" y="503" on="1"/>
+        <pt x="117" y="143" on="1"/>
+        <pt x="117" y="82" on="0"/>
+        <pt x="156" y="15" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright 2015 Google Inc. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test Family 3 Light
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.902;GOOG;TestFamily3-Light
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test Family 3 Light
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.902
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestFamily3-Light
+    </namerecord>
+    <namerecord nameID="7" platformID="3" platEncID="1" langID="0x409">
+      Noto is a trademark of Google Inc.
+    </namerecord>
+    <namerecord nameID="8" platformID="3" platEncID="1" langID="0x409">
+      Monotype Imaging Inc.
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Monotype Design Team
+    </namerecord>
+    <namerecord nameID="10" platformID="3" platEncID="1" langID="0x409">
+      Designed by Monotype design team.
+    </namerecord>
+    <namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
+      http://www.google.com/get/noto/
+    </namerecord>
+    <namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
+      http://www.monotype.com/studio
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      This Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License for the specific language, permissions and limitations governing your use of this Font Software.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://scripts.sil.org/OFL
+    </namerecord>
+    <namerecord nameID="16" platformID="3" platEncID="1" langID="0x409">
+      Test Family 3
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Light
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-100"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+    </extraNames>
+  </post>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="T"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1 Format="2">
+          </ClassDef1>
+          <ClassDef2 Format="1">
+            <ClassDef glyph="T" class="4"/>
+            <ClassDef glyph="n" class="3"/>
+            <ClassDef glyph="o" class="2"/>
+            <ClassDef glyph="s" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=1 -->
+          <!-- Class2Count=5 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="-60"/>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="-70"/>
+            </Class2Record>
+            <Class2Record index="3">
+              <Value1 XAdvance="-50"/>
+            </Class2Record>
+            <Class2Record index="4">
+              <Value1 XAdvance="20"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Regular.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Regular.ttx
new file mode 100644
index 0000000..44848db
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Regular.ttx
@@ -0,0 +1,520 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.14">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="F"/>
+    <GlyphID id="2" name="T"/>
+    <GlyphID id="3" name="l"/>
+    <GlyphID id="4" name="n"/>
+    <GlyphID id="5" name="o"/>
+    <GlyphID id="6" name="s"/>
+    <GlyphID id="7" name="t"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.902"/>
+    <checkSumAdjustment value="0x216f5a4a"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Mar 15 19:50:39 2016"/>
+    <modified value="Wed Aug 16 14:03:23 2017"/>
+    <xMin value="10"/>
+    <yMin value="-240"/>
+    <xMax value="551"/>
+    <yMax value="760"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1069"/>
+    <descent value="-293"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="618"/>
+    <minLeftSideBearing value="10"/>
+    <minRightSideBearing value="11"/>
+    <xMaxExtent value="551"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="8"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="8"/>
+    <maxPoints value="43"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="487"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="322"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="5"/>
+      <bProportion value="2"/>
+      <bContrast value="4"/>
+      <bStrokeVariation value="5"/>
+      <bArmStyle value="4"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="11100000 00000000 00000010 11111111"/>
+    <ulUnicodeRange2 value="01000000 00000000 00000000 00011111"/>
+    <ulUnicodeRange3 value="00001000 00000000 00000000 00101001"/>
+    <ulUnicodeRange4 value="00000000 00010000 00000000 00000000"/>
+    <achVendID value="GOOG"/>
+    <fsSelection value="00000001 01000000"/>
+    <usFirstCharIndex value="70"/>
+    <usLastCharIndex value="116"/>
+    <sTypoAscender value="1069"/>
+    <sTypoDescender value="-293"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="1069"/>
+    <usWinDescent value="293"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000000"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="536"/>
+    <sCapHeight value="714"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="2"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name="F" width="519" lsb="97"/>
+    <mtx name="T" width="556" lsb="10"/>
+    <mtx name="l" width="258" lsb="85"/>
+    <mtx name="n" width="618" lsb="85"/>
+    <mtx name="o" width="605" lsb="55"/>
+    <mtx name="s" width="479" lsb="51"/>
+    <mtx name="t" width="361" lsb="16"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x46" name="F"/><!-- LATIN CAPITAL LETTER F -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0x6c" name="l"/><!-- LATIN SMALL LETTER L -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x6f" name="o"/><!-- LATIN SMALL LETTER O -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x74" name="t"/><!-- LATIN SMALL LETTER T -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x46" name="F"/><!-- LATIN CAPITAL LETTER F -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0x6c" name="l"/><!-- LATIN SMALL LETTER L -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x6f" name="o"/><!-- LATIN SMALL LETTER O -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x74" name="t"/><!-- LATIN SMALL LETTER T -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="50" yMin="-240" xMax="450" yMax="760">
+      <contour>
+        <pt x="50" y="-240" on="1"/>
+        <pt x="450" y="-240" on="1"/>
+        <pt x="450" y="760" on="1"/>
+        <pt x="50" y="760" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-190" on="1"/>
+        <pt x="100" y="710" on="1"/>
+        <pt x="400" y="710" on="1"/>
+        <pt x="400" y="-190" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="F" xMin="97" yMin="0" xMax="496" yMax="714">
+      <contour>
+        <pt x="187" y="0" on="1"/>
+        <pt x="97" y="0" on="1"/>
+        <pt x="97" y="714" on="1"/>
+        <pt x="496" y="714" on="1"/>
+        <pt x="496" y="635" on="1"/>
+        <pt x="187" y="635" on="1"/>
+        <pt x="187" y="382" on="1"/>
+        <pt x="477" y="382" on="1"/>
+        <pt x="477" y="303" on="1"/>
+        <pt x="187" y="303" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="T" xMin="10" yMin="0" xMax="545" yMax="714">
+      <contour>
+        <pt x="323" y="0" on="1"/>
+        <pt x="233" y="0" on="1"/>
+        <pt x="233" y="635" on="1"/>
+        <pt x="10" y="635" on="1"/>
+        <pt x="10" y="714" on="1"/>
+        <pt x="545" y="714" on="1"/>
+        <pt x="545" y="635" on="1"/>
+        <pt x="323" y="635" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="l" xMin="85" yMin="0" xMax="173" yMax="760">
+      <contour>
+        <pt x="173" y="0" on="1"/>
+        <pt x="85" y="0" on="1"/>
+        <pt x="85" y="760" on="1"/>
+        <pt x="173" y="760" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="n" xMin="85" yMin="0" xMax="537" yMax="546">
+      <contour>
+        <pt x="343" y="546" on="1"/>
+        <pt x="439" y="546" on="0"/>
+        <pt x="537" y="452" on="0"/>
+        <pt x="537" y="349" on="1"/>
+        <pt x="537" y="0" on="1"/>
+        <pt x="450" y="0" on="1"/>
+        <pt x="450" y="343" on="1"/>
+        <pt x="450" y="408" on="0"/>
+        <pt x="392" y="472" on="0"/>
+        <pt x="330" y="472" on="1"/>
+        <pt x="241" y="472" on="0"/>
+        <pt x="173" y="372" on="0"/>
+        <pt x="173" y="278" on="1"/>
+        <pt x="173" y="0" on="1"/>
+        <pt x="85" y="0" on="1"/>
+        <pt x="85" y="536" on="1"/>
+        <pt x="156" y="536" on="1"/>
+        <pt x="169" y="463" on="1"/>
+        <pt x="174" y="463" on="1"/>
+        <pt x="192" y="491" on="0"/>
+        <pt x="245" y="528" on="0"/>
+        <pt x="309" y="546" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="o" xMin="55" yMin="-10" xMax="551" yMax="546">
+      <contour>
+        <pt x="551" y="269" on="1"/>
+        <pt x="551" y="202" on="0"/>
+        <pt x="516" y="98" on="0"/>
+        <pt x="451" y="27" on="0"/>
+        <pt x="358" y="-10" on="0"/>
+        <pt x="301" y="-10" on="1"/>
+        <pt x="248" y="-10" on="0"/>
+        <pt x="158" y="27" on="0"/>
+        <pt x="92" y="98" on="0"/>
+        <pt x="55" y="202" on="0"/>
+        <pt x="55" y="269" on="1"/>
+        <pt x="55" y="358" on="0"/>
+        <pt x="115" y="481" on="0"/>
+        <pt x="227" y="546" on="0"/>
+        <pt x="304" y="546" on="1"/>
+        <pt x="376" y="546" on="0"/>
+        <pt x="488" y="481" on="0"/>
+        <pt x="551" y="358" on="0"/>
+      </contour>
+      <contour>
+        <pt x="146" y="269" on="1"/>
+        <pt x="146" y="206" on="0"/>
+        <pt x="179" y="113" on="0"/>
+        <pt x="249" y="63" on="0"/>
+        <pt x="303" y="63" on="1"/>
+        <pt x="357" y="63" on="0"/>
+        <pt x="426" y="113" on="0"/>
+        <pt x="460" y="206" on="0"/>
+        <pt x="460" y="269" on="1"/>
+        <pt x="460" y="332" on="0"/>
+        <pt x="426" y="423" on="0"/>
+        <pt x="356" y="472" on="0"/>
+        <pt x="302" y="472" on="1"/>
+        <pt x="220" y="472" on="0"/>
+        <pt x="146" y="364" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="s" xMin="51" yMin="-10" xMax="434" yMax="546">
+      <contour>
+        <pt x="434" y="148" on="1"/>
+        <pt x="434" y="96" on="0"/>
+        <pt x="382" y="26" on="0"/>
+        <pt x="286" y="-10" on="0"/>
+        <pt x="220" y="-10" on="1"/>
+        <pt x="164" y="-10" on="0"/>
+        <pt x="83" y="8" on="0"/>
+        <pt x="52" y="24" on="1"/>
+        <pt x="52" y="104" on="1"/>
+        <pt x="84" y="88" on="0"/>
+        <pt x="175" y="61" on="0"/>
+        <pt x="222" y="61" on="1"/>
+        <pt x="289" y="61" on="0"/>
+        <pt x="349" y="104" on="0"/>
+        <pt x="349" y="140" on="1"/>
+        <pt x="349" y="160" on="0"/>
+        <pt x="327" y="192" on="0"/>
+        <pt x="270" y="224" on="0"/>
+        <pt x="217" y="244" on="1"/>
+        <pt x="165" y="264" on="0"/>
+        <pt x="91" y="304" on="0"/>
+        <pt x="51" y="360" on="0"/>
+        <pt x="51" y="404" on="1"/>
+        <pt x="51" y="472" on="0"/>
+        <pt x="162" y="546" on="0"/>
+        <pt x="252" y="546" on="1"/>
+        <pt x="301" y="546" on="0"/>
+        <pt x="386" y="526" on="0"/>
+        <pt x="423" y="510" on="1"/>
+        <pt x="393" y="440" on="1"/>
+        <pt x="359" y="454" on="0"/>
+        <pt x="285" y="474" on="0"/>
+        <pt x="246" y="474" on="1"/>
+        <pt x="192" y="474" on="0"/>
+        <pt x="135" y="439" on="0"/>
+        <pt x="135" y="409" on="1"/>
+        <pt x="135" y="386" on="0"/>
+        <pt x="161" y="356" on="0"/>
+        <pt x="222" y="326" on="0"/>
+        <pt x="273" y="307" on="1"/>
+        <pt x="324" y="288" on="0"/>
+        <pt x="396" y="248" on="0"/>
+        <pt x="434" y="191" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="t" xMin="16" yMin="-10" xMax="339" yMax="659">
+      <contour>
+        <pt x="264" y="62" on="1"/>
+        <pt x="284" y="62" on="0"/>
+        <pt x="326" y="68" on="0"/>
+        <pt x="339" y="73" on="1"/>
+        <pt x="339" y="6" on="1"/>
+        <pt x="325" y="-1" on="0"/>
+        <pt x="273" y="-10" on="0"/>
+        <pt x="249" y="-10" on="1"/>
+        <pt x="207" y="-10" on="0"/>
+        <pt x="136" y="19" on="0"/>
+        <pt x="92" y="91" on="0"/>
+        <pt x="92" y="156" on="1"/>
+        <pt x="92" y="468" on="1"/>
+        <pt x="16" y="468" on="1"/>
+        <pt x="16" y="510" on="1"/>
+        <pt x="93" y="545" on="1"/>
+        <pt x="128" y="659" on="1"/>
+        <pt x="180" y="659" on="1"/>
+        <pt x="180" y="536" on="1"/>
+        <pt x="335" y="536" on="1"/>
+        <pt x="335" y="468" on="1"/>
+        <pt x="180" y="468" on="1"/>
+        <pt x="180" y="158" on="1"/>
+        <pt x="180" y="109" on="0"/>
+        <pt x="227" y="62" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright 2015 Google Inc. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test Family 3
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.902;GOOG;TestFamily3-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test Family 3 Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.902
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestFamily3-Regular
+    </namerecord>
+    <namerecord nameID="7" platformID="3" platEncID="1" langID="0x409">
+      Noto is a trademark of Google Inc.
+    </namerecord>
+    <namerecord nameID="8" platformID="3" platEncID="1" langID="0x409">
+      Monotype Imaging Inc.
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Monotype Design Team
+    </namerecord>
+    <namerecord nameID="10" platformID="3" platEncID="1" langID="0x409">
+      Designed by Monotype design team.
+    </namerecord>
+    <namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
+      http://www.google.com/get/noto/
+    </namerecord>
+    <namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
+      http://www.monotype.com/studio
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      This Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License for the specific language, permissions and limitations governing your use of this Font Software.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://scripts.sil.org/OFL
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-100"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+    </extraNames>
+  </post>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="T"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1 Format="2">
+          </ClassDef1>
+          <ClassDef2 Format="1">
+            <ClassDef glyph="T" class="4"/>
+            <ClassDef glyph="n" class="3"/>
+            <ClassDef glyph="o" class="2"/>
+            <ClassDef glyph="s" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=1 -->
+          <!-- Class2Count=5 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="-60"/>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="-70"/>
+            </Class2Record>
+            <Class2Record index="3">
+              <Value1 XAdvance="-50"/>
+            </Class2Record>
+            <Class2Record index="4">
+              <Value1 XAdvance="20"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-SemiBold.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-SemiBold.ttx
new file mode 100644
index 0000000..7e6a0b3
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-SemiBold.ttx
@@ -0,0 +1,526 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.14">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="F"/>
+    <GlyphID id="2" name="T"/>
+    <GlyphID id="3" name="l"/>
+    <GlyphID id="4" name="n"/>
+    <GlyphID id="5" name="o"/>
+    <GlyphID id="6" name="s"/>
+    <GlyphID id="7" name="t"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.902"/>
+    <checkSumAdjustment value="0x9fcb5370"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Mar 15 19:50:39 2016"/>
+    <modified value="Wed Aug 16 14:03:23 2017"/>
+    <xMin value="20"/>
+    <yMin value="-240"/>
+    <xMax value="582"/>
+    <yMax value="760"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1069"/>
+    <descent value="-293"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="657"/>
+    <minLeftSideBearing value="20"/>
+    <minRightSideBearing value="20"/>
+    <xMaxExtent value="582"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="8"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="8"/>
+    <maxPoints value="43"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="518"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="328"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="8"/>
+      <bProportion value="2"/>
+      <bContrast value="4"/>
+      <bStrokeVariation value="5"/>
+      <bArmStyle value="4"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="11100000 00000000 00000010 11111111"/>
+    <ulUnicodeRange2 value="01000000 00000000 00000000 00011111"/>
+    <ulUnicodeRange3 value="00001000 00000000 00000000 00101001"/>
+    <ulUnicodeRange4 value="00000000 00010000 00000000 00000000"/>
+    <achVendID value="GOOG"/>
+    <fsSelection value="00000001 01000000"/>
+    <usFirstCharIndex value="70"/>
+    <usLastCharIndex value="116"/>
+    <sTypoAscender value="1069"/>
+    <sTypoDescender value="-293"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="1069"/>
+    <usWinDescent value="293"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000000"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="546"/>
+    <sCapHeight value="714"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="2"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name="F" width="549" lsb="90"/>
+    <mtx name="T" width="579" lsb="20"/>
+    <mtx name="l" width="305" lsb="78"/>
+    <mtx name="n" width="657" lsb="78"/>
+    <mtx name="o" width="619" lsb="45"/>
+    <mtx name="s" width="497" lsb="45"/>
+    <mtx name="t" width="434" lsb="23"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x46" name="F"/><!-- LATIN CAPITAL LETTER F -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0x6c" name="l"/><!-- LATIN SMALL LETTER L -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x6f" name="o"/><!-- LATIN SMALL LETTER O -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x74" name="t"/><!-- LATIN SMALL LETTER T -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x46" name="F"/><!-- LATIN CAPITAL LETTER F -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0x6c" name="l"/><!-- LATIN SMALL LETTER L -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x6f" name="o"/><!-- LATIN SMALL LETTER O -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x74" name="t"/><!-- LATIN SMALL LETTER T -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="50" yMin="-240" xMax="450" yMax="760">
+      <contour>
+        <pt x="50" y="-240" on="1"/>
+        <pt x="450" y="-240" on="1"/>
+        <pt x="450" y="760" on="1"/>
+        <pt x="50" y="760" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-190" on="1"/>
+        <pt x="100" y="710" on="1"/>
+        <pt x="400" y="710" on="1"/>
+        <pt x="400" y="-190" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="F" xMin="90" yMin="0" xMax="499" yMax="714">
+      <contour>
+        <pt x="239" y="0" on="1"/>
+        <pt x="90" y="0" on="1"/>
+        <pt x="90" y="714" on="1"/>
+        <pt x="499" y="714" on="1"/>
+        <pt x="499" y="590" on="1"/>
+        <pt x="239" y="590" on="1"/>
+        <pt x="239" y="406" on="1"/>
+        <pt x="481" y="406" on="1"/>
+        <pt x="481" y="282" on="1"/>
+        <pt x="239" y="282" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="T" xMin="20" yMin="0" xMax="559" yMax="714">
+      <contour>
+        <pt x="365" y="0" on="1"/>
+        <pt x="214" y="0" on="1"/>
+        <pt x="214" y="588" on="1"/>
+        <pt x="20" y="588" on="1"/>
+        <pt x="20" y="714" on="1"/>
+        <pt x="559" y="714" on="1"/>
+        <pt x="559" y="588" on="1"/>
+        <pt x="365" y="588" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="l" xMin="78" yMin="0" xMax="227" yMax="760">
+      <contour>
+        <pt x="227" y="0" on="1"/>
+        <pt x="78" y="0" on="1"/>
+        <pt x="78" y="760" on="1"/>
+        <pt x="227" y="760" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="n" xMin="78" yMin="0" xMax="582" yMax="556">
+      <contour>
+        <pt x="388" y="556" on="1"/>
+        <pt x="476" y="556" on="0"/>
+        <pt x="582" y="461" on="0"/>
+        <pt x="582" y="356" on="1"/>
+        <pt x="582" y="0" on="1"/>
+        <pt x="433" y="0" on="1"/>
+        <pt x="433" y="319" on="1"/>
+        <pt x="433" y="378" on="0"/>
+        <pt x="391" y="437" on="0"/>
+        <pt x="345" y="437" on="1"/>
+        <pt x="277" y="437" on="0"/>
+        <pt x="227" y="344" on="0"/>
+        <pt x="227" y="257" on="1"/>
+        <pt x="227" y="0" on="1"/>
+        <pt x="78" y="0" on="1"/>
+        <pt x="78" y="546" on="1"/>
+        <pt x="192" y="546" on="1"/>
+        <pt x="212" y="476" on="1"/>
+        <pt x="220" y="476" on="1"/>
+        <pt x="238" y="504" on="0"/>
+        <pt x="290" y="539" on="0"/>
+        <pt x="354" y="556" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="o" xMin="45" yMin="-10" xMax="574" yMax="556">
+      <contour>
+        <pt x="574" y="274" on="1"/>
+        <pt x="574" y="206" on="0"/>
+        <pt x="537" y="100" on="0"/>
+        <pt x="468" y="27" on="0"/>
+        <pt x="370" y="-10" on="0"/>
+        <pt x="308" y="-10" on="1"/>
+        <pt x="251" y="-10" on="0"/>
+        <pt x="154" y="27" on="0"/>
+        <pt x="84" y="100" on="0"/>
+        <pt x="45" y="206" on="0"/>
+        <pt x="45" y="274" on="1"/>
+        <pt x="45" y="364" on="0"/>
+        <pt x="109" y="490" on="0"/>
+        <pt x="228" y="556" on="0"/>
+        <pt x="311" y="556" on="1"/>
+        <pt x="388" y="556" on="0"/>
+        <pt x="506" y="490" on="0"/>
+        <pt x="574" y="364" on="0"/>
+      </contour>
+      <contour>
+        <pt x="197" y="274" on="1"/>
+        <pt x="197" y="220" on="0"/>
+        <pt x="220" y="147" on="0"/>
+        <pt x="270" y="110" on="0"/>
+        <pt x="310" y="110" on="1"/>
+        <pt x="350" y="110" on="0"/>
+        <pt x="399" y="147" on="0"/>
+        <pt x="422" y="220" on="0"/>
+        <pt x="422" y="274" on="1"/>
+        <pt x="422" y="328" on="0"/>
+        <pt x="399" y="400" on="0"/>
+        <pt x="349" y="436" on="0"/>
+        <pt x="309" y="436" on="1"/>
+        <pt x="250" y="436" on="0"/>
+        <pt x="197" y="355" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="s" xMin="45" yMin="-10" xMax="459" yMax="556">
+      <contour>
+        <pt x="459" y="162" on="1"/>
+        <pt x="459" y="106" on="0"/>
+        <pt x="407" y="30" on="0"/>
+        <pt x="303" y="-10" on="0"/>
+        <pt x="226" y="-10" on="1"/>
+        <pt x="169" y="-10" on="0"/>
+        <pt x="87" y="5" on="0"/>
+        <pt x="46" y="22" on="1"/>
+        <pt x="46" y="145" on="1"/>
+        <pt x="90" y="125" on="0"/>
+        <pt x="192" y="99" on="0"/>
+        <pt x="231" y="99" on="1"/>
+        <pt x="274" y="99" on="0"/>
+        <pt x="312" y="125" on="0"/>
+        <pt x="312" y="146" on="1"/>
+        <pt x="312" y="160" on="0"/>
+        <pt x="297" y="182" on="0"/>
+        <pt x="247" y="210" on="0"/>
+        <pt x="194" y="232" on="1"/>
+        <pt x="142" y="254" on="0"/>
+        <pt x="77" y="297" on="0"/>
+        <pt x="45" y="358" on="0"/>
+        <pt x="45" y="404" on="1"/>
+        <pt x="45" y="480" on="0"/>
+        <pt x="163" y="556" on="0"/>
+        <pt x="261" y="556" on="1"/>
+        <pt x="312" y="556" on="0"/>
+        <pt x="404" y="536" on="0"/>
+        <pt x="453" y="513" on="1"/>
+        <pt x="408" y="406" on="1"/>
+        <pt x="368" y="423" on="0"/>
+        <pt x="296" y="446" on="0"/>
+        <pt x="259" y="446" on="1"/>
+        <pt x="226" y="446" on="0"/>
+        <pt x="193" y="428" on="0"/>
+        <pt x="193" y="410" on="1"/>
+        <pt x="193" y="397" on="0"/>
+        <pt x="210" y="376" on="0"/>
+        <pt x="259" y="352" on="0"/>
+        <pt x="307" y="332" on="1"/>
+        <pt x="354" y="313" on="0"/>
+        <pt x="422" y="272" on="0"/>
+        <pt x="459" y="210" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="t" xMin="23" yMin="-10" xMax="402" yMax="662">
+      <contour>
+        <pt x="308" y="109" on="1"/>
+        <pt x="333" y="109" on="0"/>
+        <pt x="379" y="118" on="0"/>
+        <pt x="402" y="126" on="1"/>
+        <pt x="402" y="15" on="1"/>
+        <pt x="378" y="4" on="0"/>
+        <pt x="307" y="-10" on="0"/>
+        <pt x="265" y="-10" on="1"/>
+        <pt x="216" y="-10" on="0"/>
+        <pt x="139" y="22" on="0"/>
+        <pt x="94" y="100" on="0"/>
+        <pt x="94" y="171" on="1"/>
+        <pt x="94" y="434" on="1"/>
+        <pt x="23" y="434" on="1"/>
+        <pt x="23" y="497" on="1"/>
+        <pt x="105" y="547" on="1"/>
+        <pt x="148" y="662" on="1"/>
+        <pt x="243" y="662" on="1"/>
+        <pt x="243" y="546" on="1"/>
+        <pt x="396" y="546" on="1"/>
+        <pt x="396" y="434" on="1"/>
+        <pt x="243" y="434" on="1"/>
+        <pt x="243" y="171" on="1"/>
+        <pt x="243" y="140" on="0"/>
+        <pt x="279" y="109" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright 2015 Google Inc. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test Family 3 SemiBold
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.902;GOOG;TestFamily3-SemiBold
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test Family 3 SemiBold
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.902
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestFamily3-SemiBold
+    </namerecord>
+    <namerecord nameID="7" platformID="3" platEncID="1" langID="0x409">
+      Noto is a trademark of Google Inc.
+    </namerecord>
+    <namerecord nameID="8" platformID="3" platEncID="1" langID="0x409">
+      Monotype Imaging Inc.
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Monotype Design Team
+    </namerecord>
+    <namerecord nameID="10" platformID="3" platEncID="1" langID="0x409">
+      Designed by Monotype design team.
+    </namerecord>
+    <namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
+      http://www.google.com/get/noto/
+    </namerecord>
+    <namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
+      http://www.monotype.com/studio
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      This Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License for the specific language, permissions and limitations governing your use of this Font Software.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://scripts.sil.org/OFL
+    </namerecord>
+    <namerecord nameID="16" platformID="3" platEncID="1" langID="0x409">
+      Test Family 3
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      SemiBold
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-100"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+    </extraNames>
+  </post>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="T"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1 Format="2">
+          </ClassDef1>
+          <ClassDef2 Format="1">
+            <ClassDef glyph="T" class="4"/>
+            <ClassDef glyph="n" class="3"/>
+            <ClassDef glyph="o" class="2"/>
+            <ClassDef glyph="s" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=1 -->
+          <!-- Class2Count=5 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="-60"/>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="-70"/>
+            </Class2Record>
+            <Class2Record index="3">
+              <Value1 XAdvance="-50"/>
+            </Class2Record>
+            <Class2Record index="4">
+              <Value1 XAdvance="20"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_ttx_varfont_ttf/Mutator_IUP.ttx b/Tests/varLib/data/master_ttx_varfont_ttf/Mutator_IUP.ttx
new file mode 100755
index 0000000..23c240e
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_varfont_ttf/Mutator_IUP.ttx
@@ -0,0 +1,551 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.10">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="NULL"/>
+    <GlyphID id="2" name="nonmarkingreturn"/>
+    <GlyphID id="3" name="space"/>
+    <GlyphID id="4" name="b"/>
+    <GlyphID id="5" name="q"/>
+    <GlyphID id="6" name="a"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0xe59c28a1"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00001011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Apr 27 12:41:42 2017"/>
+    <modified value="Tue May  2 16:43:12 2017"/>
+    <xMin value="38"/>
+    <yMin value="-152"/>
+    <xMax value="456"/>
+    <yMax value="608"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="9"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="750"/>
+    <descent value="-250"/>
+    <lineGap value="9"/>
+    <advanceWidthMax value="494"/>
+    <minLeftSideBearing value="38"/>
+    <minRightSideBearing value="38"/>
+    <xMaxExtent value="456"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="5"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="7"/>
+    <maxPoints value="20"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="20"/>
+    <maxCompositeContours value="2"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="1"/>
+    <maxComponentDepth value="1"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="347"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000100"/>
+    <ySubscriptXSize value="700"/>
+    <ySubscriptYSize value="650"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="140"/>
+    <ySuperscriptXSize value="700"/>
+    <ySuperscriptYSize value="650"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="477"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="250"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="0"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="LuFo"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="0"/>
+    <usLastCharIndex value="113"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="608"/>
+    <usWinDescent value="152"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="456"/>
+    <sCapHeight value="608"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="200" lsb="0"/>
+    <mtx name="NULL" width="0" lsb="0"/>
+    <mtx name="a" width="494" lsb="38"/>
+    <mtx name="b" width="494" lsb="76"/>
+    <mtx name="nonmarkingreturn" width="200" lsb="0"/>
+    <mtx name="q" width="494" lsb="38"/>
+    <mtx name="space" width="200" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x0" name="NULL"/><!-- ???? -->
+      <map code="0xd" name="nonmarkingreturn"/><!-- ???? -->
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x62" name="b"/><!-- LATIN SMALL LETTER B -->
+      <map code="0x71" name="q"/><!-- LATIN SMALL LETTER Q -->
+    </cmap_format_4>
+    <cmap_format_6 platformID="1" platEncID="0" language="0">
+      <map code="0x0" name="NULL"/>
+      <map code="0xd" name="nonmarkingreturn"/>
+      <map code="0x20" name="space"/>
+      <map code="0x61" name="a"/>
+      <map code="0x62" name="b"/>
+      <map code="0x71" name="q"/>
+    </cmap_format_6>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x0" name="NULL"/><!-- ???? -->
+      <map code="0xd" name="nonmarkingreturn"/><!-- ???? -->
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x62" name="b"/><!-- LATIN SMALL LETTER B -->
+      <map code="0x71" name="q"/><!-- LATIN SMALL LETTER Q -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef"/><!-- contains no outline data -->
+
+    <TTGlyph name="NULL"/><!-- contains no outline data -->
+
+    <TTGlyph name="a" xMin="38" yMin="-12" xMax="418" yMax="468">
+      <contour>
+        <pt x="342" y="0" on="1"/>
+        <pt x="342" y="64" on="1"/>
+        <pt x="264" y="-12" on="1"/>
+        <pt x="190" y="-12" on="1"/>
+        <pt x="38" y="140" on="1"/>
+        <pt x="38" y="316" on="1"/>
+        <pt x="190" y="468" on="1"/>
+        <pt x="266" y="468" on="1"/>
+        <pt x="342" y="392" on="1"/>
+        <pt x="342" y="456" on="1"/>
+        <pt x="418" y="456" on="1"/>
+        <pt x="418" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="266" y="64" on="1"/>
+        <pt x="342" y="140" on="1"/>
+        <pt x="342" y="316" on="1"/>
+        <pt x="266" y="392" on="1"/>
+        <pt x="190" y="392" on="1"/>
+        <pt x="114" y="316" on="1"/>
+        <pt x="114" y="140" on="1"/>
+        <pt x="190" y="64" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="b" xMin="76" yMin="-12" xMax="456" yMax="608">
+      <contour>
+        <pt x="228" y="468" on="1"/>
+        <pt x="304" y="468" on="1"/>
+        <pt x="456" y="316" on="1"/>
+        <pt x="456" y="140" on="1"/>
+        <pt x="304" y="-12" on="1"/>
+        <pt x="230" y="-12" on="1"/>
+        <pt x="152" y="64" on="1"/>
+        <pt x="152" y="0" on="1"/>
+        <pt x="76" y="0" on="1"/>
+        <pt x="76" y="608" on="1"/>
+        <pt x="152" y="608" on="1"/>
+        <pt x="152" y="392" on="1"/>
+      </contour>
+      <contour>
+        <pt x="152" y="316" on="1"/>
+        <pt x="152" y="140" on="1"/>
+        <pt x="228" y="64" on="1"/>
+        <pt x="304" y="64" on="1"/>
+        <pt x="380" y="140" on="1"/>
+        <pt x="380" y="316" on="1"/>
+        <pt x="304" y="392" on="1"/>
+        <pt x="228" y="392" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="nonmarkingreturn"/><!-- contains no outline data -->
+
+    <TTGlyph name="q" xMin="38" yMin="-152" xMax="418" yMax="468">
+      <component glyphName="b" x="494" y="456" scale="-0.99994" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      VarFont
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      VarFont Regular: 2017
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      VarFont Regular
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      VarFont-Regular
+    </namerecord>
+    <namerecord nameID="256" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Width
+    </namerecord>
+    <namerecord nameID="257" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Ascender
+    </namerecord>
+    <namerecord nameID="258" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      VarFont
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      VarFont Regular: 2017
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      VarFont Regular
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      VarFont-Regular
+    </namerecord>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Width
+    </namerecord>
+    <namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
+      Ascender
+    </namerecord>
+    <namerecord nameID="258" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name="NULL"/>
+    </extraNames>
+  </post>
+
+  <GDEF>
+    <Version value="0x00010003"/>
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=2 -->
+        <!-- RegionCount=2 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="1">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=1 -->
+      <VarData index="0">
+        <!-- ItemCount=0 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=2 -->
+        <VarRegionIndex index="0" value="0"/>
+        <VarRegionIndex index="1" value="1"/>
+      </VarData>
+    </VarStore>
+  </GDEF>
+
+  <HVAR>
+    <Version value="0x00010000"/>
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=2 -->
+        <!-- RegionCount=2 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="1">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=1 -->
+      <VarData index="0">
+        <!-- ItemCount=2 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=2 -->
+        <VarRegionIndex index="0" value="0"/>
+        <VarRegionIndex index="1" value="1"/>
+        <Item index="0" value="[0, -60]"/>
+        <Item index="1" value="[0, 0]"/>
+      </VarData>
+    </VarStore>
+    <AdvWidthMap>
+      <Map glyph=".notdef" outer="0" inner="1"/>
+      <Map glyph="NULL" outer="0" inner="1"/>
+      <Map glyph="a" outer="0" inner="0"/>
+      <Map glyph="b" outer="0" inner="0"/>
+      <Map glyph="q" outer="0" inner="0"/>
+      <Map glyph="nonmarkingreturn" outer="0" inner="1"/>
+      <Map glyph="space" outer="0" inner="1"/>
+    </AdvWidthMap>
+  </HVAR>
+
+  <MVAR>
+    <Version value="0x00010000"/>
+    <Reserved value="0"/>
+    <ValueRecordSize value="0"/>
+    <!-- ValueRecordCount=0 -->
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=2 -->
+        <!-- RegionCount=2 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="1">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=1 -->
+      <VarData index="0">
+        <!-- ItemCount=0 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=2 -->
+        <VarRegionIndex index="0" value="0"/>
+        <VarRegionIndex index="1" value="1"/>
+      </VarData>
+    </VarStore>
+  </MVAR>
+
+  <fvar>
+
+    <!-- Width -->
+    <Axis>
+      <AxisTag>wdth</AxisTag>
+      <MinValue>60.0</MinValue>
+      <DefaultValue>100.0</DefaultValue>
+      <MaxValue>100.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+
+    <!-- Ascender -->
+    <Axis>
+      <AxisTag>ASCN</AxisTag>
+      <MinValue>608.0</MinValue>
+      <DefaultValue>608.0</DefaultValue>
+      <MaxValue>648.0</MaxValue>
+      <AxisNameID>257</AxisNameID>
+    </Axis>
+
+    <!-- Regular -->
+    <NamedInstance subfamilyNameID="258">
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="ASCN" value="608.0"/>
+    </NamedInstance>
+  </fvar>
+
+  <gvar>
+    <version value="1"/>
+    <reserved value="0"/>
+    <glyphVariations glyph="b">
+      <tuple>
+        <coord axis="ASCN" value="1.0"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="0" y="40"/>
+        <delta pt="10" x="0" y="40"/>
+        <delta pt="11" x="0" y="0"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="22" x="0" y="40"/>
+      </tuple>
+      <tuple>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="0" x="-20" y="0"/>
+        <delta pt="1" x="-40" y="0"/>
+        <delta pt="2" x="-60" y="0"/>
+        <delta pt="3" x="-60" y="0"/>
+        <delta pt="4" x="-40" y="0"/>
+        <delta pt="5" x="-20" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="11" x="0" y="0"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="17" x="-60" y="0"/>
+        <delta pt="21" x="-60" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="q">
+      <tuple>
+        <coord axis="ASCN" value="1.0"/>
+        <delta pt="4" x="0" y="40"/>
+      </tuple>
+      <tuple>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="0" x="-60" y="0"/>
+        <delta pt="2" x="-60" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="a">
+      <tuple>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="0" x="-60" y="0"/>
+        <delta pt="1" x="-60" y="0"/>
+        <delta pt="2" x="-40" y="0"/>
+        <delta pt="3" x="-20" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="-20" y="0"/>
+        <delta pt="7" x="-40" y="0"/>
+        <delta pt="8" x="-60" y="0"/>
+        <delta pt="14" x="-60" y="0"/>
+        <delta pt="20" x="0" y="0"/>
+        <delta pt="21" x="-60" y="0"/>
+        <delta pt="22" x="0" y="0"/>
+        <delta pt="23" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+  </gvar>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/features.fea b/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/features.fea
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/features.fea
@@ -0,0 +1 @@
+
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/fontinfo.plist b/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/fontinfo.plist
new file mode 100644
index 0000000..15db2ee
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/fontinfo.plist
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>ascender</key>
+		<integer>738</integer>
+		<key>capHeight</key>
+		<integer>677</integer>
+		<key>descender</key>
+		<integer>-245</integer>
+		<key>familyName</key>
+		<string>Test Family</string>
+		<key>openTypeHheaAscender</key>
+		<integer>918</integer>
+		<key>openTypeHheaDescender</key>
+		<integer>-335</integer>
+		<key>openTypeHheaLineGap</key>
+		<integer>0</integer>
+		<key>openTypeNameDesigner</key>
+		<string>Frank Grießhammer</string>
+		<key>openTypeOS2CodePageRanges</key>
+		<array>
+			<integer>0</integer>
+			<integer>1</integer>
+			<integer>29</integer>
+		</array>
+		<key>openTypeOS2Panose</key>
+		<array>
+			<integer>2</integer>
+			<integer>4</integer>
+			<integer>2</integer>
+			<integer>3</integer>
+			<integer>5</integer>
+			<integer>4</integer>
+			<integer>5</integer>
+			<integer>2</integer>
+			<integer>2</integer>
+			<integer>4</integer>
+		</array>
+		<key>openTypeOS2TypoAscender</key>
+		<integer>730</integer>
+		<key>openTypeOS2TypoDescender</key>
+		<integer>-270</integer>
+		<key>openTypeOS2TypoLineGap</key>
+		<integer>0</integer>
+		<key>openTypeOS2UnicodeRanges</key>
+		<array>
+			<integer>0</integer>
+			<integer>1</integer>
+		</array>
+		<key>openTypeOS2VendorID</key>
+		<string>ADBO</string>
+		<key>openTypeOS2WeightClass</key>
+		<integer>200</integer>
+		<key>openTypeOS2WinAscent</key>
+		<integer>918</integer>
+		<key>openTypeOS2WinDescent</key>
+		<integer>335</integer>
+		<key>postscriptBlueFuzz</key>
+		<integer>0</integer>
+		<key>postscriptBlueScale</key>
+		<real>0.0375</real>
+		<key>postscriptBlueShift</key>
+		<integer>7</integer>
+		<key>postscriptBlueValues</key>
+		<array>
+			<integer>-13</integer>
+			<integer>0</integer>
+			<integer>470</integer>
+			<integer>483</integer>
+			<integer>534</integer>
+			<integer>547</integer>
+			<integer>556</integer>
+			<integer>569</integer>
+			<integer>654</integer>
+			<integer>667</integer>
+			<integer>677</integer>
+			<integer>690</integer>
+			<integer>738</integer>
+			<integer>758</integer>
+		</array>
+		<key>postscriptFamilyBlues</key>
+		<array>
+			<integer>-20</integer>
+			<integer>0</integer>
+			<integer>473</integer>
+			<integer>491</integer>
+			<integer>525</integer>
+			<integer>540</integer>
+			<integer>549</integer>
+			<integer>562</integer>
+			<integer>644</integer>
+			<integer>659</integer>
+			<integer>669</integer>
+			<integer>689</integer>
+			<integer>729</integer>
+			<integer>749</integer>
+		</array>
+		<key>postscriptFamilyOtherBlues</key>
+		<array>
+			<integer>-249</integer>
+			<integer>-239</integer>
+		</array>
+		<key>postscriptFontName</key>
+		<string>TestFamily-Master0</string>
+		<key>postscriptForceBold</key>
+		<false/>
+		<key>postscriptOtherBlues</key>
+		<array>
+			<integer>-255</integer>
+			<integer>-245</integer>
+		</array>
+		<key>postscriptStemSnapH</key>
+		<array>
+			<integer>26</integer>
+			<integer>20</integer>
+		</array>
+		<key>postscriptStemSnapV</key>
+		<array>
+			<integer>28</integer>
+			<integer>32</integer>
+		</array>
+		<key>postscriptUnderlinePosition</key>
+		<integer>-75</integer>
+		<key>postscriptUnderlineThickness</key>
+		<integer>50</integer>
+		<key>styleMapFamilyName</key>
+		<string>Test Family</string>
+		<key>styleName</key>
+		<string>Master 0</string>
+		<key>unitsPerEm</key>
+		<integer>1000</integer>
+		<key>versionMajor</key>
+		<integer>1</integer>
+		<key>versionMinor</key>
+		<integer>1</integer>
+		<key>xHeight</key>
+		<integer>470</integer>
+	</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/A_.glif b/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/A_.glif
new file mode 100644
index 0000000..0861ae7
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/A_.glif
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="A" format="1">
+	<unicode hex="0041"/>
+	<advance width="653"/>
+	<outline>
+		<contour>
+			<point x="12" y="0" type="line"/>
+			<point x="210" y="0" type="line"/>
+			<point x="210" y="20" type="line"/>
+			<point x="113" y="26" type="line"/>
+			<point x="99" y="26" type="line"/>
+			<point x="12" y="20" type="line"/>
+		</contour>
+		<contour>
+			<point x="85" y="0" type="line"/>
+			<point x="114" y="0" type="line"/>
+			<point x="325" y="667" type="line"/>
+			<point x="308" y="657" type="line"/>
+			<point x="524" y="0" type="line"/>
+			<point x="556" y="0" type="line"/>
+			<point x="327" y="684" type="line"/>
+			<point x="307" y="684" type="line"/>
+		</contour>
+		<contour>
+			<point x="173" y="247" type="line"/>
+			<point x="468" y="247" type="line"/>
+			<point x="460" y="267" type="line"/>
+			<point x="182" y="267" type="line"/>
+		</contour>
+		<contour>
+			<point x="403" y="0" type="line"/>
+			<point x="651" y="0" type="line"/>
+			<point x="651" y="20" type="line"/>
+			<point x="537" y="26" type="line"/>
+			<point x="523" y="26" type="line"/>
+			<point x="403" y="20" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/_notdef.glif b/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/_notdef.glif
new file mode 100644
index 0000000..0c5a60d
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/_notdef.glif
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name=".notdef" format="1">
+	<advance width="640"/>
+	<outline>
+		<contour>
+			<point x="80" y="0" type="line"/>
+			<point x="105" y="0" type="line"/>
+			<point x="560" y="677" type="line"/>
+			<point x="535" y="677" type="line"/>
+		</contour>
+		<contour>
+			<point x="560" y="0" type="line"/>
+			<point x="105" y="677" type="line"/>
+			<point x="80" y="677" type="line"/>
+			<point x="535" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="105" y="22" type="line"/>
+			<point x="105" y="655" type="line"/>
+			<point x="535" y="655" type="line"/>
+			<point x="535" y="22" type="line"/>
+		</contour>
+		<contour>
+			<point x="80" y="0" type="line"/>
+			<point x="560" y="0" type="line"/>
+			<point x="560" y="677" type="line"/>
+			<point x="80" y="677" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/a.glif b/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/a.glif
new file mode 100644
index 0000000..ed47bff
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/a.glif
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="a" format="1">
+	<unicode hex="0061"/>
+	<advance width="505"/>
+	<outline>
+		<contour>
+			<point x="57" y="104" type="curve" smooth="yes"/>
+			<point x="57" y="29"/>
+			<point x="109" y="-13"/>
+			<point x="180" y="-13" type="curve" smooth="yes"/>
+			<point x="249" y="-13"/>
+			<point x="296" y="21"/>
+			<point x="358" y="90" type="curve" smooth="yes"/>
+			<point x="367" y="100" type="line"/>
+			<point x="367" y="125" type="line"/>
+			<point x="356" y="113" type="line" smooth="yes"/>
+			<point x="293" y="44"/>
+			<point x="245" y="13"/>
+			<point x="188" y="13" type="curve" smooth="yes"/>
+			<point x="126" y="13"/>
+			<point x="87" y="49"/>
+			<point x="87" y="110" type="curve" smooth="yes"/>
+			<point x="87" y="162"/>
+			<point x="126" y="201"/>
+			<point x="207" y="231" type="curve" smooth="yes"/>
+			<point x="258" y="250"/>
+			<point x="318" y="265"/>
+			<point x="369" y="277" type="curve"/>
+			<point x="369" y="295" type="line"/>
+			<point x="318" y="283"/>
+			<point x="262" y="272"/>
+			<point x="206" y="254" type="curve" smooth="yes"/>
+			<point x="108" y="222"/>
+			<point x="57" y="176"/>
+		</contour>
+		<contour>
+			<point x="355" y="99" type="line"/>
+			<point x="359" y="27"/>
+			<point x="377" y="-13"/>
+			<point x="425" y="-13" type="curve" smooth="yes"/>
+			<point x="448" y="-13"/>
+			<point x="470" y="-2"/>
+			<point x="486" y="14" type="curve"/>
+			<point x="476" y="28" type="line"/>
+			<point x="458" y="15"/>
+			<point x="446" y="11"/>
+			<point x="433" y="11" type="curve" smooth="yes"/>
+			<point x="401" y="11"/>
+			<point x="383" y="33"/>
+			<point x="383" y="112" type="curve" smooth="yes"/>
+			<point x="383" y="339" type="line" smooth="yes"/>
+			<point x="383" y="440"/>
+			<point x="333" y="483"/>
+			<point x="236" y="483" type="curve" smooth="yes"/>
+			<point x="152" y="483"/>
+			<point x="90" y="444"/>
+			<point x="69" y="370" type="curve"/>
+			<point x="72" y="358"/>
+			<point x="79" y="351"/>
+			<point x="91" y="351" type="curve" smooth="yes"/>
+			<point x="103" y="351"/>
+			<point x="111" y="357"/>
+			<point x="115" y="373" type="curve" smooth="yes"/>
+			<point x="133" y="441" type="line"/>
+			<point x="107" y="415" type="line"/>
+			<point x="147" y="449"/>
+			<point x="187" y="463"/>
+			<point x="234" y="463" type="curve" smooth="yes"/>
+			<point x="314" y="463"/>
+			<point x="355" y="435"/>
+			<point x="355" y="324" type="curve" smooth="yes"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/contents.plist b/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/contents.plist
new file mode 100644
index 0000000..ec975ca
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/contents.plist
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>.notdef</key>
+		<string>_notdef.glif</string>
+		<key>A</key>
+		<string>A_.glif</string>
+		<key>a</key>
+		<string>a.glif</string>
+		<key>dollar</key>
+		<string>dollar.glif</string>
+		<key>dollar.nostroke</key>
+		<string>dollar.nostroke.glif</string>
+		<key>space</key>
+		<string>space.glif</string>
+	</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/dollar.glif b/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/dollar.glif
new file mode 100644
index 0000000..fd3d67f
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/dollar.glif
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="dollar" format="1">
+	<unicode hex="0024"/>
+	<advance width="490"/>
+	<outline>
+		<contour>
+			<point x="245" y="7" type="curve" smooth="yes"/>
+			<point x="180" y="7"/>
+			<point x="141" y="22"/>
+			<point x="95" y="72" type="curve"/>
+			<point x="131" y="24" type="line"/>
+			<point x="103" y="124" type="line" smooth="yes"/>
+			<point x="99" y="140"/>
+			<point x="87" y="144"/>
+			<point x="76" y="144" type="curve" smooth="yes"/>
+			<point x="62" y="144"/>
+			<point x="54" y="137"/>
+			<point x="53" y="123" type="curve"/>
+			<point x="77" y="38"/>
+			<point x="138" y="-13"/>
+			<point x="245" y="-13" type="curve" smooth="yes"/>
+			<point x="336" y="-13"/>
+			<point x="426" y="40"/>
+			<point x="426" y="151" type="curve" smooth="yes"/>
+			<point x="426" y="221"/>
+			<point x="400" y="287"/>
+			<point x="266" y="344" type="curve" smooth="yes"/>
+			<point x="247" y="352" type="line" smooth="yes"/>
+			<point x="154" y="391"/>
+			<point x="112" y="440"/>
+			<point x="112" y="508" type="curve" smooth="yes"/>
+			<point x="112" y="599"/>
+			<point x="172" y="647"/>
+			<point x="260" y="647" type="curve" smooth="yes"/>
+			<point x="316" y="647"/>
+			<point x="351" y="633"/>
+			<point x="395" y="583" type="curve"/>
+			<point x="357" y="630" type="line"/>
+			<point x="385" y="530" type="line" smooth="yes"/>
+			<point x="391" y="515"/>
+			<point x="401" y="510"/>
+			<point x="412" y="510" type="curve" smooth="yes"/>
+			<point x="426" y="510"/>
+			<point x="434" y="517"/>
+			<point x="435" y="531" type="curve"/>
+			<point x="411" y="619"/>
+			<point x="344" y="667"/>
+			<point x="260" y="667" type="curve" smooth="yes"/>
+			<point x="168" y="667"/>
+			<point x="86" y="616"/>
+			<point x="86" y="508" type="curve" smooth="yes"/>
+			<point x="86" y="428"/>
+			<point x="131" y="375"/>
+			<point x="223" y="333" type="curve" smooth="yes"/>
+			<point x="260" y="316" type="line" smooth="yes"/>
+			<point x="374" y="264"/>
+			<point x="400" y="218"/>
+			<point x="400" y="153" type="curve" smooth="yes"/>
+			<point x="400" y="61"/>
+			<point x="335" y="7"/>
+		</contour>
+		<contour>
+			<point x="263" y="334" type="line"/>
+			<point x="263" y="762" type="line"/>
+			<point x="241" y="762" type="line"/>
+			<point x="241" y="334" type="line"/>
+		</contour>
+		<contour>
+			<point x="241" y="-115" type="line"/>
+			<point x="263" y="-115" type="line"/>
+			<point x="263" y="334" type="line"/>
+			<point x="241" y="334" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/dollar.nostroke.glif b/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/dollar.nostroke.glif
new file mode 100644
index 0000000..9703f85
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/dollar.nostroke.glif
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="dollar.nostroke" format="1">
+	<advance width="490"/>
+	<outline>
+		<contour>
+			<point x="245" y="7" type="curve" smooth="yes"/>
+			<point x="180" y="7"/>
+			<point x="141" y="22"/>
+			<point x="95" y="72" type="curve"/>
+			<point x="131" y="24" type="line"/>
+			<point x="103" y="124" type="line" smooth="yes"/>
+			<point x="99" y="140"/>
+			<point x="87" y="144"/>
+			<point x="76" y="144" type="curve" smooth="yes"/>
+			<point x="62" y="144"/>
+			<point x="54" y="137"/>
+			<point x="53" y="123" type="curve"/>
+			<point x="77" y="38"/>
+			<point x="138" y="-13"/>
+			<point x="245" y="-13" type="curve" smooth="yes"/>
+			<point x="336" y="-13"/>
+			<point x="426" y="40"/>
+			<point x="426" y="151" type="curve" smooth="yes"/>
+			<point x="426" y="221"/>
+			<point x="400" y="287"/>
+			<point x="266" y="344" type="curve" smooth="yes"/>
+			<point x="247" y="352" type="line" smooth="yes"/>
+			<point x="154" y="391"/>
+			<point x="112" y="440"/>
+			<point x="112" y="508" type="curve" smooth="yes"/>
+			<point x="112" y="599"/>
+			<point x="172" y="647"/>
+			<point x="260" y="647" type="curve" smooth="yes"/>
+			<point x="316" y="647"/>
+			<point x="351" y="633"/>
+			<point x="395" y="583" type="curve"/>
+			<point x="357" y="630" type="line"/>
+			<point x="385" y="530" type="line" smooth="yes"/>
+			<point x="391" y="515"/>
+			<point x="401" y="510"/>
+			<point x="412" y="510" type="curve" smooth="yes"/>
+			<point x="426" y="510"/>
+			<point x="434" y="517"/>
+			<point x="435" y="531" type="curve"/>
+			<point x="411" y="619"/>
+			<point x="344" y="667"/>
+			<point x="260" y="667" type="curve" smooth="yes"/>
+			<point x="168" y="667"/>
+			<point x="86" y="616"/>
+			<point x="86" y="508" type="curve" smooth="yes"/>
+			<point x="86" y="428"/>
+			<point x="131" y="375"/>
+			<point x="223" y="333" type="curve" smooth="yes"/>
+			<point x="260" y="316" type="line" smooth="yes"/>
+			<point x="374" y="264"/>
+			<point x="400" y="218"/>
+			<point x="400" y="153" type="curve" smooth="yes"/>
+			<point x="400" y="61"/>
+			<point x="335" y="7"/>
+		</contour>
+		<contour>
+			<point x="263" y="657" type="line"/>
+			<point x="263" y="762" type="line"/>
+			<point x="241" y="762" type="line"/>
+			<point x="241" y="657" type="line"/>
+		</contour>
+		<contour>
+			<point x="241" y="-115" type="line"/>
+			<point x="263" y="-115" type="line"/>
+			<point x="263" y="-4" type="line"/>
+			<point x="241" y="-4" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/space.glif b/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/space.glif
new file mode 100644
index 0000000..58d78af
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/glyphs/space.glif
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="space" format="1">
+	<unicode hex="0020"/>
+	<advance width="248"/>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/lib.plist b/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/lib.plist
new file mode 100644
index 0000000..a379c4b
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/lib.plist
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.glyphOrder</key>
+	<array>
+		<string>.notdef</string>
+		<string>space</string>
+		<string>A</string>
+		<string>a</string>
+		<string>dollar</string>
+		<string>dollar.nostroke</string>
+	</array>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/metainfo.plist b/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/metainfo.plist
new file mode 100644
index 0000000..9c654d4
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/metainfo.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>creator</key>
+		<string>org.robofab.ufoLib</string>
+		<key>formatVersion</key>
+		<integer>2</integer>
+	</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/features.fea b/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/features.fea
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/features.fea
@@ -0,0 +1 @@
+
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/fontinfo.plist b/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/fontinfo.plist
new file mode 100644
index 0000000..24c1bbf
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/fontinfo.plist
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>ascender</key>
+		<integer>738</integer>
+		<key>capHeight</key>
+		<integer>677</integer>
+		<key>descender</key>
+		<integer>-245</integer>
+		<key>familyName</key>
+		<string>Test Family</string>
+		<key>openTypeHheaAscender</key>
+		<integer>918</integer>
+		<key>openTypeHheaDescender</key>
+		<integer>-335</integer>
+		<key>openTypeHheaLineGap</key>
+		<integer>0</integer>
+		<key>openTypeNameDesigner</key>
+		<string>Frank Grießhammer</string>
+		<key>openTypeOS2CodePageRanges</key>
+		<array>
+			<integer>0</integer>
+			<integer>1</integer>
+			<integer>29</integer>
+		</array>
+		<key>openTypeOS2Panose</key>
+		<array>
+			<integer>2</integer>
+			<integer>4</integer>
+			<integer>6</integer>
+			<integer>3</integer>
+			<integer>5</integer>
+			<integer>4</integer>
+			<integer>5</integer>
+			<integer>2</integer>
+			<integer>2</integer>
+			<integer>4</integer>
+		</array>
+		<key>openTypeOS2TypoAscender</key>
+		<integer>730</integer>
+		<key>openTypeOS2TypoDescender</key>
+		<integer>-270</integer>
+		<key>openTypeOS2TypoLineGap</key>
+		<integer>0</integer>
+		<key>openTypeOS2UnicodeRanges</key>
+		<array>
+			<integer>0</integer>
+			<integer>1</integer>
+		</array>
+		<key>openTypeOS2VendorID</key>
+		<string>ADBO</string>
+		<key>openTypeOS2WeightClass</key>
+		<integer>400</integer>
+		<key>openTypeOS2WinAscent</key>
+		<integer>918</integer>
+		<key>openTypeOS2WinDescent</key>
+		<integer>335</integer>
+		<key>postscriptBlueFuzz</key>
+		<integer>0</integer>
+		<key>postscriptBlueScale</key>
+		<real>0.0375</real>
+		<key>postscriptBlueShift</key>
+		<integer>7</integer>
+		<key>postscriptBlueValues</key>
+		<array>
+			<integer>-15</integer>
+			<integer>0</integer>
+			<integer>474</integer>
+			<integer>487</integer>
+			<integer>527</integer>
+			<integer>540</integer>
+			<integer>550</integer>
+			<integer>563</integer>
+			<integer>647</integer>
+			<integer>660</integer>
+			<integer>670</integer>
+			<integer>685</integer>
+			<integer>730</integer>
+			<integer>750</integer>
+		</array>
+		<key>postscriptFamilyBlues</key>
+		<array>
+			<integer>-20</integer>
+			<integer>0</integer>
+			<integer>473</integer>
+			<integer>491</integer>
+			<integer>525</integer>
+			<integer>540</integer>
+			<integer>549</integer>
+			<integer>562</integer>
+			<integer>644</integer>
+			<integer>659</integer>
+			<integer>669</integer>
+			<integer>689</integer>
+			<integer>729</integer>
+			<integer>749</integer>
+		</array>
+		<key>postscriptFamilyOtherBlues</key>
+		<array>
+			<integer>-249</integer>
+			<integer>-239</integer>
+		</array>
+		<key>postscriptFontName</key>
+		<string>TestFamily-Master1</string>
+		<key>postscriptForceBold</key>
+		<false/>
+		<key>postscriptOtherBlues</key>
+		<array>
+			<integer>-250</integer>
+			<integer>-240</integer>
+		</array>
+		<key>postscriptStemSnapH</key>
+		<array>
+			<integer>55</integer>
+			<integer>40</integer>
+		</array>
+		<key>postscriptStemSnapV</key>
+		<array>
+			<integer>80</integer>
+			<integer>90</integer>
+		</array>
+		<key>postscriptUnderlinePosition</key>
+		<integer>-75</integer>
+		<key>postscriptUnderlineThickness</key>
+		<integer>50</integer>
+		<key>styleMapFamilyName</key>
+		<string>Test Family</string>
+		<key>styleName</key>
+		<string>Master 1</string>
+		<key>unitsPerEm</key>
+		<integer>1000</integer>
+		<key>versionMajor</key>
+		<integer>1</integer>
+		<key>versionMinor</key>
+		<integer>1</integer>
+		<key>xHeight</key>
+		<integer>474</integer>
+	</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/A_.glif b/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/A_.glif
new file mode 100644
index 0000000..1cf8995
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/A_.glif
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="A" format="1">
+	<unicode hex="0041"/>
+	<advance width="663"/>
+	<outline>
+		<contour>
+			<point x="5" y="0" type="line"/>
+			<point x="235" y="0" type="line"/>
+			<point x="235" y="40" type="line"/>
+			<point x="125" y="55" type="line"/>
+			<point x="105" y="55" type="line"/>
+			<point x="5" y="40" type="line"/>
+		</contour>
+		<contour>
+			<point x="71" y="0" type="line"/>
+			<point x="118" y="0" type="line"/>
+			<point x="322" y="599" type="line"/>
+			<point x="299" y="599" type="line"/>
+			<point x="500" y="0" type="line"/>
+			<point x="593" y="0" type="line"/>
+			<point x="363" y="675" type="line"/>
+			<point x="303" y="675" type="line"/>
+		</contour>
+		<contour>
+			<point x="170" y="219" type="line"/>
+			<point x="472" y="219" type="line"/>
+			<point x="456" y="265" type="line"/>
+			<point x="186" y="265" type="line"/>
+		</contour>
+		<contour>
+			<point x="383" y="0" type="line"/>
+			<point x="653" y="0" type="line"/>
+			<point x="653" y="40" type="line"/>
+			<point x="529" y="55" type="line"/>
+			<point x="509" y="55" type="line"/>
+			<point x="383" y="40" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/_notdef.glif b/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/_notdef.glif
new file mode 100644
index 0000000..27591b4
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/_notdef.glif
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name=".notdef" format="1">
+	<advance width="640"/>
+	<outline>
+		<contour>
+			<point x="80" y="0" type="line"/>
+			<point x="140" y="0" type="line"/>
+			<point x="560" y="670" type="line"/>
+			<point x="500" y="670" type="line"/>
+		</contour>
+		<contour>
+			<point x="560" y="0" type="line"/>
+			<point x="140" y="670" type="line"/>
+			<point x="80" y="670" type="line"/>
+			<point x="500" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="140" y="50" type="line"/>
+			<point x="140" y="620" type="line"/>
+			<point x="500" y="620" type="line"/>
+			<point x="500" y="50" type="line"/>
+		</contour>
+		<contour>
+			<point x="80" y="0" type="line"/>
+			<point x="560" y="0" type="line"/>
+			<point x="560" y="670" type="line"/>
+			<point x="80" y="670" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/a.glif b/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/a.glif
new file mode 100644
index 0000000..b9ed8a5
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/a.glif
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="a" format="1">
+	<unicode hex="0061"/>
+	<advance width="508"/>
+	<outline>
+		<contour>
+			<point x="46" y="112" type="curve" smooth="yes"/>
+			<point x="46" y="29"/>
+			<point x="107" y="-13"/>
+			<point x="180" y="-13" type="curve" smooth="yes"/>
+			<point x="241" y="-13"/>
+			<point x="275" y="15"/>
+			<point x="335" y="75" type="curve" smooth="yes"/>
+			<point x="364" y="104" type="line"/>
+			<point x="350" y="125" type="line"/>
+			<point x="330" y="110" type="line" smooth="yes"/>
+			<point x="267" y="65"/>
+			<point x="247" y="52"/>
+			<point x="212" y="52" type="curve" smooth="yes"/>
+			<point x="166" y="52"/>
+			<point x="131" y="73"/>
+			<point x="131" y="126" type="curve" smooth="yes"/>
+			<point x="131" y="155"/>
+			<point x="141" y="193"/>
+			<point x="231" y="228" type="curve" smooth="yes"/>
+			<point x="261" y="240"/>
+			<point x="320" y="259"/>
+			<point x="365" y="268" type="curve"/>
+			<point x="365" y="304" type="line"/>
+			<point x="315" y="295"/>
+			<point x="252" y="277"/>
+			<point x="210" y="262" type="curve" smooth="yes"/>
+			<point x="76" y="213"/>
+			<point x="46" y="168"/>
+		</contour>
+		<contour>
+			<point x="325" y="92" type="line"/>
+			<point x="325" y="30"/>
+			<point x="361" y="-10"/>
+			<point x="416" y="-10" type="curve" smooth="yes"/>
+			<point x="450" y="-10"/>
+			<point x="477" y="3"/>
+			<point x="501" y="41" type="curve"/>
+			<point x="481" y="63" type="line"/>
+			<point x="470" y="50"/>
+			<point x="459" y="41"/>
+			<point x="442" y="41" type="curve" smooth="yes"/>
+			<point x="419" y="41"/>
+			<point x="405" y="57"/>
+			<point x="405" y="102" type="curve" smooth="yes"/>
+			<point x="405" y="314" type="line" smooth="yes"/>
+			<point x="405" y="440"/>
+			<point x="355" y="487"/>
+			<point x="252" y="487" type="curve" smooth="yes"/>
+			<point x="152" y="487"/>
+			<point x="78" y="440"/>
+			<point x="59" y="364" type="curve"/>
+			<point x="61" y="339"/>
+			<point x="77" y="324"/>
+			<point x="104" y="324" type="curve" smooth="yes"/>
+			<point x="131" y="324"/>
+			<point x="146" y="340"/>
+			<point x="155" y="369" type="curve" smooth="yes"/>
+			<point x="181" y="455" type="line"/>
+			<point x="141" y="424" type="line"/>
+			<point x="178" y="439"/>
+			<point x="208" y="442"/>
+			<point x="231" y="442" type="curve" smooth="yes"/>
+			<point x="297" y="442"/>
+			<point x="325" y="418"/>
+			<point x="325" y="320" type="curve" smooth="yes"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/contents.plist b/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/contents.plist
new file mode 100644
index 0000000..ec975ca
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/contents.plist
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>.notdef</key>
+		<string>_notdef.glif</string>
+		<key>A</key>
+		<string>A_.glif</string>
+		<key>a</key>
+		<string>a.glif</string>
+		<key>dollar</key>
+		<string>dollar.glif</string>
+		<key>dollar.nostroke</key>
+		<string>dollar.nostroke.glif</string>
+		<key>space</key>
+		<string>space.glif</string>
+	</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/dollar.glif b/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/dollar.glif
new file mode 100644
index 0000000..3ce19bf
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/dollar.glif
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="dollar" format="1">
+	<unicode hex="0024"/>
+	<advance width="497"/>
+	<outline>
+		<contour>
+			<point x="248" y="35" type="curve" smooth="yes"/>
+			<point x="209" y="35"/>
+			<point x="164" y="40"/>
+			<point x="118" y="58" type="curve"/>
+			<point x="171" y="22" type="line"/>
+			<point x="154" y="98" type="line" smooth="yes"/>
+			<point x="142" y="151"/>
+			<point x="120" y="164"/>
+			<point x="96" y="164" type="curve" smooth="yes"/>
+			<point x="74" y="164"/>
+			<point x="60" y="153"/>
+			<point x="51" y="133" type="curve"/>
+			<point x="55" y="46"/>
+			<point x="136" y="-13"/>
+			<point x="243" y="-13" type="curve" smooth="yes"/>
+			<point x="379" y="-13"/>
+			<point x="461" y="63"/>
+			<point x="461" y="170" type="curve" smooth="yes"/>
+			<point x="461" y="252"/>
+			<point x="420" y="317"/>
+			<point x="285" y="364" type="curve" smooth="yes"/>
+			<point x="247" y="377" type="line" smooth="yes"/>
+			<point x="176" y="400"/>
+			<point x="136" y="435"/>
+			<point x="136" y="499" type="curve" smooth="yes"/>
+			<point x="136" y="574"/>
+			<point x="193" y="611"/>
+			<point x="267" y="611" type="curve" smooth="yes"/>
+			<point x="297" y="611"/>
+			<point x="333" y="606"/>
+			<point x="375" y="589" type="curve"/>
+			<point x="323" y="625" type="line"/>
+			<point x="340" y="549" type="line" smooth="yes"/>
+			<point x="352" y="497"/>
+			<point x="377" y="483"/>
+			<point x="399" y="483" type="curve" smooth="yes"/>
+			<point x="418" y="483"/>
+			<point x="435" y="493"/>
+			<point x="443" y="514" type="curve"/>
+			<point x="437" y="600"/>
+			<point x="357" y="660"/>
+			<point x="256" y="660" type="curve" smooth="yes"/>
+			<point x="141" y="660"/>
+			<point x="58" y="580"/>
+			<point x="58" y="478" type="curve" smooth="yes"/>
+			<point x="58" y="378"/>
+			<point x="120" y="324"/>
+			<point x="225" y="287" type="curve" smooth="yes"/>
+			<point x="262" y="274" type="line" smooth="yes"/>
+			<point x="347" y="244"/>
+			<point x="383" y="214"/>
+			<point x="383" y="151" type="curve" smooth="yes"/>
+			<point x="383" y="77"/>
+			<point x="330" y="35"/>
+		</contour>
+		<contour>
+			<point x="279" y="322" type="line"/>
+			<point x="279" y="750" type="line"/>
+			<point x="239" y="750" type="line"/>
+			<point x="239" y="322" type="line"/>
+		</contour>
+		<contour>
+			<point x="238" y="-115" type="line"/>
+			<point x="278" y="-115" type="line"/>
+			<point x="278" y="322" type="line"/>
+			<point x="238" y="322" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/dollar.nostroke.glif b/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/dollar.nostroke.glif
new file mode 100644
index 0000000..fb86b06
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/dollar.nostroke.glif
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="dollar.nostroke" format="1">
+	<advance width="497"/>
+	<outline>
+		<contour>
+			<point x="248" y="35" type="curve" smooth="yes"/>
+			<point x="209" y="35"/>
+			<point x="164" y="40"/>
+			<point x="118" y="58" type="curve"/>
+			<point x="171" y="22" type="line"/>
+			<point x="154" y="98" type="line" smooth="yes"/>
+			<point x="142" y="151"/>
+			<point x="120" y="164"/>
+			<point x="96" y="164" type="curve" smooth="yes"/>
+			<point x="74" y="164"/>
+			<point x="60" y="153"/>
+			<point x="51" y="133" type="curve"/>
+			<point x="55" y="46"/>
+			<point x="136" y="-13"/>
+			<point x="243" y="-13" type="curve" smooth="yes"/>
+			<point x="379" y="-13"/>
+			<point x="461" y="63"/>
+			<point x="461" y="170" type="curve" smooth="yes"/>
+			<point x="461" y="252"/>
+			<point x="420" y="317"/>
+			<point x="285" y="364" type="curve" smooth="yes"/>
+			<point x="247" y="377" type="line" smooth="yes"/>
+			<point x="176" y="400"/>
+			<point x="136" y="435"/>
+			<point x="136" y="499" type="curve" smooth="yes"/>
+			<point x="136" y="574"/>
+			<point x="193" y="611"/>
+			<point x="267" y="611" type="curve" smooth="yes"/>
+			<point x="297" y="611"/>
+			<point x="333" y="606"/>
+			<point x="375" y="589" type="curve"/>
+			<point x="323" y="625" type="line"/>
+			<point x="340" y="549" type="line" smooth="yes"/>
+			<point x="352" y="497"/>
+			<point x="377" y="483"/>
+			<point x="399" y="483" type="curve" smooth="yes"/>
+			<point x="418" y="483"/>
+			<point x="435" y="493"/>
+			<point x="443" y="514" type="curve"/>
+			<point x="437" y="600"/>
+			<point x="357" y="660"/>
+			<point x="256" y="660" type="curve" smooth="yes"/>
+			<point x="141" y="660"/>
+			<point x="58" y="580"/>
+			<point x="58" y="478" type="curve" smooth="yes"/>
+			<point x="58" y="378"/>
+			<point x="120" y="324"/>
+			<point x="225" y="287" type="curve" smooth="yes"/>
+			<point x="262" y="274" type="line" smooth="yes"/>
+			<point x="347" y="244"/>
+			<point x="383" y="214"/>
+			<point x="383" y="151" type="curve" smooth="yes"/>
+			<point x="383" y="77"/>
+			<point x="330" y="35"/>
+		</contour>
+		<contour>
+			<point x="279" y="635" type="line"/>
+			<point x="279" y="750" type="line"/>
+			<point x="239" y="750" type="line"/>
+			<point x="239" y="635" type="line"/>
+		</contour>
+		<contour>
+			<point x="238" y="-115" type="line"/>
+			<point x="278" y="-115" type="line"/>
+			<point x="278" y="12" type="line"/>
+			<point x="238" y="12" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/space.glif b/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/space.glif
new file mode 100644
index 0000000..d1fb461
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/glyphs/space.glif
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="space" format="1">
+	<unicode hex="0020"/>
+	<advance width="234"/>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/lib.plist b/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/lib.plist
new file mode 100644
index 0000000..a379c4b
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/lib.plist
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.glyphOrder</key>
+	<array>
+		<string>.notdef</string>
+		<string>space</string>
+		<string>A</string>
+		<string>a</string>
+		<string>dollar</string>
+		<string>dollar.nostroke</string>
+	</array>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/metainfo.plist b/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/metainfo.plist
new file mode 100644
index 0000000..9c654d4
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master1.ufo/metainfo.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>creator</key>
+		<string>org.robofab.ufoLib</string>
+		<key>formatVersion</key>
+		<integer>2</integer>
+	</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/features.fea b/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/features.fea
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/features.fea
@@ -0,0 +1 @@
+
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/fontinfo.plist b/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/fontinfo.plist
new file mode 100644
index 0000000..dab014f
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/fontinfo.plist
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>ascender</key>
+		<integer>738</integer>
+		<key>capHeight</key>
+		<integer>677</integer>
+		<key>descender</key>
+		<integer>-245</integer>
+		<key>familyName</key>
+		<string>Test Family</string>
+		<key>openTypeHheaAscender</key>
+		<integer>918</integer>
+		<key>openTypeHheaDescender</key>
+		<integer>-335</integer>
+		<key>openTypeHheaLineGap</key>
+		<integer>0</integer>
+		<key>openTypeNameDesigner</key>
+		<string>Frank Grießhammer</string>
+		<key>openTypeOS2CodePageRanges</key>
+		<array>
+			<integer>0</integer>
+			<integer>1</integer>
+			<integer>29</integer>
+		</array>
+		<key>openTypeOS2Panose</key>
+		<array>
+			<integer>2</integer>
+			<integer>4</integer>
+			<integer>9</integer>
+			<integer>3</integer>
+			<integer>5</integer>
+			<integer>4</integer>
+			<integer>5</integer>
+			<integer>2</integer>
+			<integer>2</integer>
+			<integer>4</integer>
+		</array>
+		<key>openTypeOS2TypoAscender</key>
+		<integer>730</integer>
+		<key>openTypeOS2TypoDescender</key>
+		<integer>-270</integer>
+		<key>openTypeOS2TypoLineGap</key>
+		<integer>0</integer>
+		<key>openTypeOS2UnicodeRanges</key>
+		<array>
+			<integer>0</integer>
+			<integer>1</integer>
+		</array>
+		<key>openTypeOS2VendorID</key>
+		<string>ADBO</string>
+		<key>openTypeOS2WeightClass</key>
+		<integer>900</integer>
+		<key>openTypeOS2WinAscent</key>
+		<integer>918</integer>
+		<key>openTypeOS2WinDescent</key>
+		<integer>335</integer>
+		<key>postscriptBlueFuzz</key>
+		<integer>0</integer>
+		<key>postscriptBlueScale</key>
+		<real>0.0375</real>
+		<key>postscriptBlueShift</key>
+		<integer>7</integer>
+		<key>postscriptBlueValues</key>
+		<array>
+			<integer>-20</integer>
+			<integer>0</integer>
+			<integer>487</integer>
+			<integer>503</integer>
+			<integer>515</integer>
+			<integer>531</integer>
+			<integer>536</integer>
+			<integer>552</integer>
+			<integer>624</integer>
+			<integer>640</integer>
+			<integer>652</integer>
+			<integer>672</integer>
+			<integer>711</integer>
+			<integer>731</integer>
+		</array>
+		<key>postscriptFamilyBlues</key>
+		<array>
+			<integer>-20</integer>
+			<integer>0</integer>
+			<integer>473</integer>
+			<integer>491</integer>
+			<integer>525</integer>
+			<integer>540</integer>
+			<integer>549</integer>
+			<integer>562</integer>
+			<integer>644</integer>
+			<integer>659</integer>
+			<integer>669</integer>
+			<integer>689</integer>
+			<integer>729</integer>
+			<integer>749</integer>
+		</array>
+		<key>postscriptFamilyOtherBlues</key>
+		<array>
+			<integer>-249</integer>
+			<integer>-239</integer>
+		</array>
+		<key>postscriptFontName</key>
+		<string>TestFamily-Master2</string>
+		<key>postscriptForceBold</key>
+		<false/>
+		<key>postscriptOtherBlues</key>
+		<array>
+			<integer>-232</integer>
+			<integer>-222</integer>
+		</array>
+		<key>postscriptStemSnapH</key>
+		<array>
+			<integer>74</integer>
+			<integer>60</integer>
+		</array>
+		<key>postscriptStemSnapV</key>
+		<array>
+			<integer>190</integer>
+			<integer>200</integer>
+		</array>
+		<key>postscriptUnderlinePosition</key>
+		<integer>-75</integer>
+		<key>postscriptUnderlineThickness</key>
+		<integer>50</integer>
+		<key>styleMapFamilyName</key>
+		<string>Test Family</string>
+		<key>styleName</key>
+		<string>Master 2</string>
+		<key>unitsPerEm</key>
+		<integer>1000</integer>
+		<key>versionMajor</key>
+		<integer>1</integer>
+		<key>versionMinor</key>
+		<integer>1</integer>
+		<key>xHeight</key>
+		<integer>487</integer>
+	</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/A_.glif b/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/A_.glif
new file mode 100644
index 0000000..792544c
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/A_.glif
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="A" format="1">
+	<unicode hex="0041"/>
+	<advance width="680"/>
+	<outline>
+		<contour>
+			<point x="10" y="0" type="line"/>
+			<point x="220" y="0" type="line"/>
+			<point x="220" y="59" type="line"/>
+			<point x="131" y="74" type="line"/>
+			<point x="114" y="74" type="line"/>
+			<point x="10" y="59" type="line"/>
+		</contour>
+		<contour>
+			<point x="65" y="0" type="line"/>
+			<point x="129" y="0" type="line"/>
+			<point x="315" y="583" type="line"/>
+			<point x="272" y="513" type="line"/>
+			<point x="431" y="0" type="line"/>
+			<point x="632" y="0" type="line"/>
+			<point x="409" y="652" type="line"/>
+			<point x="289" y="652" type="line"/>
+		</contour>
+		<contour>
+			<point x="168" y="180" type="line"/>
+			<point x="480" y="180" type="line"/>
+			<point x="455" y="243" type="line"/>
+			<point x="185" y="243" type="line"/>
+		</contour>
+		<contour>
+			<point x="342" y="0" type="line"/>
+			<point x="665" y="0" type="line"/>
+			<point x="665" y="56" type="line"/>
+			<point x="535" y="71" type="line"/>
+			<point x="450" y="71" type="line"/>
+			<point x="342" y="56" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/_notdef.glif b/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/_notdef.glif
new file mode 100644
index 0000000..3aeffb1
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/_notdef.glif
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name=".notdef" format="1">
+	<advance width="640"/>
+	<outline>
+		<contour>
+			<point x="80" y="0" type="line"/>
+			<point x="160" y="0" type="line"/>
+			<point x="560" y="652" type="line"/>
+			<point x="480" y="652" type="line"/>
+		</contour>
+		<contour>
+			<point x="560" y="0" type="line"/>
+			<point x="160" y="652" type="line"/>
+			<point x="80" y="652" type="line"/>
+			<point x="480" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="150" y="60" type="line"/>
+			<point x="150" y="592" type="line"/>
+			<point x="490" y="592" type="line"/>
+			<point x="490" y="60" type="line"/>
+		</contour>
+		<contour>
+			<point x="80" y="0" type="line"/>
+			<point x="560" y="0" type="line"/>
+			<point x="560" y="652" type="line"/>
+			<point x="80" y="652" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/a.glif b/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/a.glif
new file mode 100644
index 0000000..458dfea
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/a.glif
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="a" format="1">
+	<unicode hex="0061"/>
+	<advance width="540"/>
+	<outline>
+		<contour>
+			<point x="25" y="113" type="curve" smooth="yes"/>
+			<point x="25" y="31"/>
+			<point x="76" y="-16"/>
+			<point x="155" y="-16" type="curve" smooth="yes"/>
+			<point x="232" y="-16"/>
+			<point x="262" y="16"/>
+			<point x="306" y="64" type="curve" smooth="yes"/>
+			<point x="353" y="116" type="line"/>
+			<point x="344" y="128" type="line"/>
+			<point x="296" y="92" type="line" smooth="yes"/>
+			<point x="276" y="77"/>
+			<point x="262" y="71"/>
+			<point x="249" y="71" type="curve" smooth="yes"/>
+			<point x="220" y="71"/>
+			<point x="197" y="95"/>
+			<point x="197" y="144" type="curve" smooth="yes"/>
+			<point x="197" y="198"/>
+			<point x="234" y="230"/>
+			<point x="262" y="243" type="curve" smooth="yes"/>
+			<point x="291" y="257"/>
+			<point x="343" y="270"/>
+			<point x="391" y="283" type="curve"/>
+			<point x="391" y="327" type="line"/>
+			<point x="345" y="315"/>
+			<point x="271" y="296"/>
+			<point x="230" y="285" type="curve" smooth="yes"/>
+			<point x="68" y="242"/>
+			<point x="25" y="190"/>
+		</contour>
+		<contour>
+			<point x="291" y="78" type="line"/>
+			<point x="303" y="18"/>
+			<point x="341" y="-16"/>
+			<point x="414" y="-16" type="curve" smooth="yes"/>
+			<point x="485" y="-16"/>
+			<point x="524" y="8"/>
+			<point x="548" y="69" type="curve"/>
+			<point x="519" y="85" type="line"/>
+			<point x="507" y="67"/>
+			<point x="500" y="63"/>
+			<point x="492" y="63" type="curve" smooth="yes"/>
+			<point x="481" y="63"/>
+			<point x="475" y="70"/>
+			<point x="475" y="80" type="curve" smooth="yes"/>
+			<point x="475" y="309" type="line" smooth="yes"/>
+			<point x="475" y="446"/>
+			<point x="424" y="503"/>
+			<point x="286" y="503" type="curve" smooth="yes"/>
+			<point x="125" y="503"/>
+			<point x="42" y="443"/>
+			<point x="42" y="357" type="curve"/>
+			<point x="52" y="321"/>
+			<point x="79" y="303"/>
+			<point x="118" y="303" type="curve" smooth="yes"/>
+			<point x="161" y="303"/>
+			<point x="200" y="334"/>
+			<point x="200" y="415" type="curve" smooth="yes"/>
+			<point x="200" y="478" type="line"/>
+			<point x="120" y="436" type="line"/>
+			<point x="166" y="446"/>
+			<point x="190" y="449"/>
+			<point x="215" y="449" type="curve" smooth="yes"/>
+			<point x="278" y="449"/>
+			<point x="291" y="422"/>
+			<point x="291" y="337" type="curve" smooth="yes"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/contents.plist b/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/contents.plist
new file mode 100644
index 0000000..ec975ca
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/contents.plist
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>.notdef</key>
+		<string>_notdef.glif</string>
+		<key>A</key>
+		<string>A_.glif</string>
+		<key>a</key>
+		<string>a.glif</string>
+		<key>dollar</key>
+		<string>dollar.glif</string>
+		<key>dollar.nostroke</key>
+		<string>dollar.nostroke.glif</string>
+		<key>space</key>
+		<string>space.glif</string>
+	</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/dollar.glif b/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/dollar.glif
new file mode 100644
index 0000000..a15b1d7
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/dollar.glif
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="dollar" format="1">
+	<unicode hex="0024"/>
+	<advance width="560"/>
+	<outline>
+		<contour>
+			<point x="260" y="39" type="curve" smooth="yes"/>
+			<point x="195" y="39"/>
+			<point x="167" y="50"/>
+			<point x="118" y="74" type="curve"/>
+			<point x="196" y="19" type="line"/>
+			<point x="188" y="104" type="line" smooth="yes"/>
+			<point x="183" y="156"/>
+			<point x="161" y="176"/>
+			<point x="118" y="176" type="curve" smooth="yes"/>
+			<point x="92" y="176"/>
+			<point x="65" y="162"/>
+			<point x="51" y="124" type="curve"/>
+			<point x="51" y="34"/>
+			<point x="122" y="-16"/>
+			<point x="261" y="-16" type="curve" smooth="yes"/>
+			<point x="424" y="-16"/>
+			<point x="523" y="68"/>
+			<point x="523" y="176" type="curve" smooth="yes"/>
+			<point x="523" y="283"/>
+			<point x="467" y="337"/>
+			<point x="329" y="393" type="curve" smooth="yes"/>
+			<point x="297" y="406" type="line" smooth="yes"/>
+			<point x="234" y="431"/>
+			<point x="204" y="449"/>
+			<point x="204" y="497" type="curve" smooth="yes"/>
+			<point x="204" y="560"/>
+			<point x="247" y="585"/>
+			<point x="308" y="585" type="curve" smooth="yes"/>
+			<point x="350" y="585"/>
+			<point x="377" y="581"/>
+			<point x="429" y="557" type="curve"/>
+			<point x="344" y="604" type="line"/>
+			<point x="354" y="537" type="line" smooth="yes"/>
+			<point x="365" y="462"/>
+			<point x="402" y="448"/>
+			<point x="441" y="448" type="curve" smooth="yes"/>
+			<point x="467" y="448"/>
+			<point x="496" y="463"/>
+			<point x="501" y="504" type="curve"/>
+			<point x="501" y="588"/>
+			<point x="417" y="640"/>
+			<point x="296" y="640" type="curve" smooth="yes"/>
+			<point x="138" y="640"/>
+			<point x="53" y="560"/>
+			<point x="53" y="457" type="curve" smooth="yes"/>
+			<point x="53" y="352"/>
+			<point x="117" y="297"/>
+			<point x="234" y="248" type="curve" smooth="yes"/>
+			<point x="265" y="235" type="line" smooth="yes"/>
+			<point x="337" y="205"/>
+			<point x="365" y="186"/>
+			<point x="365" y="123" type="curve" smooth="yes"/>
+			<point x="365" y="74"/>
+			<point x="326" y="39"/>
+		</contour>
+		<contour>
+			<point x="325" y="314" type="line"/>
+			<point x="325" y="731" type="line"/>
+			<point x="254" y="731" type="line"/>
+			<point x="254" y="314" type="line"/>
+		</contour>
+		<contour>
+			<point x="246" y="-115" type="line"/>
+			<point x="317" y="-115" type="line"/>
+			<point x="317" y="314" type="line"/>
+			<point x="246" y="314" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/dollar.nostroke.glif b/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/dollar.nostroke.glif
new file mode 100644
index 0000000..6bf5b54
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/dollar.nostroke.glif
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="dollar.nostroke" format="1">
+	<advance width="560"/>
+	<outline>
+		<contour>
+			<point x="260" y="39" type="curve" smooth="yes"/>
+			<point x="195" y="39"/>
+			<point x="167" y="50"/>
+			<point x="118" y="74" type="curve"/>
+			<point x="196" y="19" type="line"/>
+			<point x="188" y="104" type="line" smooth="yes"/>
+			<point x="183" y="156"/>
+			<point x="161" y="176"/>
+			<point x="118" y="176" type="curve" smooth="yes"/>
+			<point x="92" y="176"/>
+			<point x="65" y="162"/>
+			<point x="51" y="124" type="curve"/>
+			<point x="51" y="34"/>
+			<point x="122" y="-16"/>
+			<point x="261" y="-16" type="curve" smooth="yes"/>
+			<point x="424" y="-16"/>
+			<point x="523" y="68"/>
+			<point x="523" y="176" type="curve" smooth="yes"/>
+			<point x="523" y="283"/>
+			<point x="464" y="330"/>
+			<point x="329" y="393" type="curve" smooth="yes"/>
+			<point x="297" y="408" type="line" smooth="yes"/>
+			<point x="242" y="434"/>
+			<point x="216" y="455"/>
+			<point x="216" y="500" type="curve" smooth="yes"/>
+			<point x="216" y="560"/>
+			<point x="254" y="585"/>
+			<point x="307" y="585" type="curve" smooth="yes"/>
+			<point x="350" y="585"/>
+			<point x="377" y="581"/>
+			<point x="429" y="557" type="curve"/>
+			<point x="344" y="604" type="line"/>
+			<point x="354" y="537" type="line" smooth="yes"/>
+			<point x="365" y="462"/>
+			<point x="402" y="448"/>
+			<point x="441" y="448" type="curve" smooth="yes"/>
+			<point x="467" y="448"/>
+			<point x="496" y="463"/>
+			<point x="501" y="504" type="curve"/>
+			<point x="501" y="588"/>
+			<point x="417" y="640"/>
+			<point x="296" y="640" type="curve" smooth="yes"/>
+			<point x="141" y="640"/>
+			<point x="57" y="560"/>
+			<point x="57" y="457" type="curve" smooth="yes"/>
+			<point x="57" y="353"/>
+			<point x="122" y="304"/>
+			<point x="234" y="250" type="curve" smooth="yes"/>
+			<point x="265" y="235" type="line" smooth="yes"/>
+			<point x="331" y="203"/>
+			<point x="359" y="181"/>
+			<point x="359" y="126" type="curve" smooth="yes"/>
+			<point x="359" y="77"/>
+			<point x="318" y="39"/>
+		</contour>
+		<contour>
+			<point x="325" y="612" type="line"/>
+			<point x="325" y="731" type="line"/>
+			<point x="254" y="731" type="line"/>
+			<point x="254" y="612" type="line"/>
+		</contour>
+		<contour>
+			<point x="256" y="-115" type="line"/>
+			<point x="327" y="-115" type="line"/>
+			<point x="327" y="14" type="line"/>
+			<point x="256" y="14" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/space.glif b/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/space.glif
new file mode 100644
index 0000000..32dd00c
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/glyphs/space.glif
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="space" format="1">
+	<unicode hex="0020"/>
+	<advance width="206"/>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/lib.plist b/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/lib.plist
new file mode 100644
index 0000000..a379c4b
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/lib.plist
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.glyphOrder</key>
+	<array>
+		<string>.notdef</string>
+		<string>space</string>
+		<string>A</string>
+		<string>a</string>
+		<string>dollar</string>
+		<string>dollar.nostroke</string>
+	</array>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/metainfo.plist b/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/metainfo.plist
new file mode 100644
index 0000000..9c654d4
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master2.ufo/metainfo.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>creator</key>
+		<string>org.robofab.ufoLib</string>
+		<key>formatVersion</key>
+		<integer>2</integer>
+	</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/features.fea b/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/features.fea
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/features.fea
@@ -0,0 +1 @@
+
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/fontinfo.plist b/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/fontinfo.plist
new file mode 100644
index 0000000..6a94c39
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/fontinfo.plist
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>ascender</key>
+		<integer>738</integer>
+		<key>capHeight</key>
+		<integer>677</integer>
+		<key>descender</key>
+		<integer>-245</integer>
+		<key>familyName</key>
+		<string>Test Family</string>
+		<key>openTypeHheaAscender</key>
+		<integer>918</integer>
+		<key>openTypeHheaDescender</key>
+		<integer>-335</integer>
+		<key>openTypeHheaLineGap</key>
+		<integer>0</integer>
+		<key>openTypeNameDesigner</key>
+		<string>Frank Grießhammer</string>
+		<key>openTypeOS2CodePageRanges</key>
+		<array>
+			<integer>0</integer>
+			<integer>1</integer>
+			<integer>29</integer>
+		</array>
+		<key>openTypeOS2Panose</key>
+		<array>
+			<integer>2</integer>
+			<integer>4</integer>
+			<integer>9</integer>
+			<integer>3</integer>
+			<integer>5</integer>
+			<integer>4</integer>
+			<integer>5</integer>
+			<integer>2</integer>
+			<integer>2</integer>
+			<integer>4</integer>
+		</array>
+		<key>openTypeOS2TypoAscender</key>
+		<integer>730</integer>
+		<key>openTypeOS2TypoDescender</key>
+		<integer>-270</integer>
+		<key>openTypeOS2TypoLineGap</key>
+		<integer>0</integer>
+		<key>openTypeOS2UnicodeRanges</key>
+		<array>
+			<integer>0</integer>
+			<integer>1</integer>
+		</array>
+		<key>openTypeOS2VendorID</key>
+		<string>ADBO</string>
+		<key>openTypeOS2WeightClass</key>
+		<integer>900</integer>
+		<key>openTypeOS2WinAscent</key>
+		<integer>918</integer>
+		<key>openTypeOS2WinDescent</key>
+		<integer>335</integer>
+		<key>postscriptBlueFuzz</key>
+		<integer>0</integer>
+		<key>postscriptBlueScale</key>
+		<real>0.0375</real>
+		<key>postscriptBlueShift</key>
+		<integer>7</integer>
+		<key>postscriptBlueValues</key>
+		<array>
+			<integer>-20</integer>
+			<integer>0</integer>
+			<integer>487</integer>
+			<integer>503</integer>
+			<integer>515</integer>
+			<integer>531</integer>
+			<integer>536</integer>
+			<integer>552</integer>
+			<integer>624</integer>
+			<integer>640</integer>
+			<integer>652</integer>
+			<integer>672</integer>
+			<integer>711</integer>
+			<integer>731</integer>
+		</array>
+		<key>postscriptFamilyBlues</key>
+		<array>
+			<integer>-20</integer>
+			<integer>0</integer>
+			<integer>473</integer>
+			<integer>491</integer>
+			<integer>525</integer>
+			<integer>540</integer>
+			<integer>549</integer>
+			<integer>562</integer>
+			<integer>644</integer>
+			<integer>659</integer>
+			<integer>669</integer>
+			<integer>689</integer>
+			<integer>729</integer>
+			<integer>749</integer>
+		</array>
+		<key>postscriptFamilyOtherBlues</key>
+		<array>
+			<integer>-249</integer>
+			<integer>-239</integer>
+		</array>
+		<key>postscriptFontName</key>
+		<string>TestFamily-Master3</string>
+		<key>postscriptForceBold</key>
+		<false/>
+		<key>postscriptOtherBlues</key>
+		<array>
+			<integer>-232</integer>
+			<integer>-222</integer>
+		</array>
+		<key>postscriptStemSnapH</key>
+		<array>
+			<integer>50</integer>
+			<integer>38</integer>
+		</array>
+		<key>postscriptStemSnapV</key>
+		<array>
+			<integer>190</integer>
+			<integer>200</integer>
+		</array>
+		<key>postscriptUnderlinePosition</key>
+		<integer>-75</integer>
+		<key>postscriptUnderlineThickness</key>
+		<integer>50</integer>
+		<key>styleMapFamilyName</key>
+		<string>Test Family</string>
+		<key>styleName</key>
+		<string>Master 3</string>
+		<key>unitsPerEm</key>
+		<integer>1000</integer>
+		<key>versionMajor</key>
+		<integer>1</integer>
+		<key>versionMinor</key>
+		<integer>1</integer>
+		<key>xHeight</key>
+		<integer>487</integer>
+	</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/A_.glif b/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/A_.glif
new file mode 100644
index 0000000..0422ab9
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/A_.glif
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="A" format="1">
+	<unicode hex="0041"/>
+	<advance width="680"/>
+	<outline>
+		<contour>
+			<point x="15" y="0" type="line"/>
+			<point x="215" y="0" type="line"/>
+			<point x="215" y="35" type="line"/>
+			<point x="121" y="50" type="line"/>
+			<point x="104" y="50" type="line"/>
+			<point x="15" y="35" type="line"/>
+		</contour>
+		<contour>
+			<point x="70" y="0" type="line"/>
+			<point x="124" y="0" type="line"/>
+			<point x="288" y="583" type="line"/>
+			<point x="255" y="534" type="line"/>
+			<point x="421" y="0" type="line"/>
+			<point x="622" y="0" type="line"/>
+			<point x="399" y="652" type="line"/>
+			<point x="279" y="652" type="line"/>
+		</contour>
+		<contour>
+			<point x="167" y="193" type="line"/>
+			<point x="478" y="193" type="line"/>
+			<point x="468" y="229" type="line"/>
+			<point x="177" y="229" type="line"/>
+		</contour>
+		<contour>
+			<point x="342" y="0" type="line"/>
+			<point x="665" y="0" type="line"/>
+			<point x="665" y="35" type="line"/>
+			<point x="535" y="50" type="line"/>
+			<point x="460" y="50" type="line"/>
+			<point x="342" y="35" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/_notdef.glif b/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/_notdef.glif
new file mode 100644
index 0000000..c6f960c
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/_notdef.glif
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name=".notdef" format="1">
+	<advance width="640"/>
+	<outline>
+		<contour>
+			<point x="90" y="0" type="line"/>
+			<point x="130" y="0" type="line"/>
+			<point x="550" y="652" type="line"/>
+			<point x="510" y="652" type="line"/>
+		</contour>
+		<contour>
+			<point x="550" y="0" type="line"/>
+			<point x="130" y="652" type="line"/>
+			<point x="90" y="652" type="line"/>
+			<point x="510" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="150" y="60" type="line"/>
+			<point x="150" y="592" type="line"/>
+			<point x="490" y="592" type="line"/>
+			<point x="490" y="60" type="line"/>
+		</contour>
+		<contour>
+			<point x="80" y="0" type="line"/>
+			<point x="560" y="0" type="line"/>
+			<point x="560" y="652" type="line"/>
+			<point x="80" y="652" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/a.glif b/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/a.glif
new file mode 100644
index 0000000..0cdd98b
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/a.glif
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="a" format="1">
+	<unicode hex="0061"/>
+	<advance width="540"/>
+	<outline>
+		<contour>
+			<point x="25" y="105" type="curve" smooth="yes"/>
+			<point x="25" y="32"/>
+			<point x="76" y="-16"/>
+			<point x="155" y="-16" type="curve" smooth="yes"/>
+			<point x="232" y="-16"/>
+			<point x="263" y="15"/>
+			<point x="306" y="64" type="curve" smooth="yes"/>
+			<point x="353" y="117" type="line"/>
+			<point x="344" y="125" type="line"/>
+			<point x="296" y="80" type="line" smooth="yes"/>
+			<point x="276" y="62"/>
+			<point x="262" y="56"/>
+			<point x="249" y="56" type="curve" smooth="yes"/>
+			<point x="217" y="56"/>
+			<point x="197" y="79"/>
+			<point x="197" y="129" type="curve" smooth="yes"/>
+			<point x="197" y="201"/>
+			<point x="233" y="243"/>
+			<point x="279" y="263" type="curve" smooth="yes"/>
+			<point x="295" y="270"/>
+			<point x="343" y="282"/>
+			<point x="391" y="295" type="curve"/>
+			<point x="391" y="320" type="line"/>
+			<point x="345" y="308"/>
+			<point x="271" y="289"/>
+			<point x="230" y="278" type="curve" smooth="yes"/>
+			<point x="71" y="235"/>
+			<point x="25" y="191"/>
+		</contour>
+		<contour>
+			<point x="291" y="72" type="line"/>
+			<point x="304" y="19"/>
+			<point x="341" y="-16"/>
+			<point x="414" y="-16" type="curve" smooth="yes"/>
+			<point x="485" y="-16"/>
+			<point x="524" y="8"/>
+			<point x="548" y="69" type="curve"/>
+			<point x="530" y="79" type="line"/>
+			<point x="513" y="48"/>
+			<point x="504" y="41"/>
+			<point x="493" y="41" type="curve" smooth="yes"/>
+			<point x="482" y="41"/>
+			<point x="475" y="48"/>
+			<point x="475" y="65" type="curve" smooth="yes"/>
+			<point x="475" y="308" type="line" smooth="yes"/>
+			<point x="475" y="445"/>
+			<point x="424" y="503"/>
+			<point x="286" y="503" type="curve" smooth="yes"/>
+			<point x="125" y="503"/>
+			<point x="42" y="444"/>
+			<point x="42" y="358" type="curve"/>
+			<point x="52" y="322"/>
+			<point x="79" y="304"/>
+			<point x="118" y="304" type="curve" smooth="yes"/>
+			<point x="161" y="304"/>
+			<point x="200" y="334"/>
+			<point x="200" y="415" type="curve" smooth="yes"/>
+			<point x="200" y="478" type="line"/>
+			<point x="120" y="456" type="line"/>
+			<point x="166" y="466"/>
+			<point x="190" y="469"/>
+			<point x="215" y="469" type="curve" smooth="yes"/>
+			<point x="278" y="469"/>
+			<point x="291" y="441"/>
+			<point x="291" y="356" type="curve" smooth="yes"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/contents.plist b/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/contents.plist
new file mode 100644
index 0000000..ec975ca
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/contents.plist
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>.notdef</key>
+		<string>_notdef.glif</string>
+		<key>A</key>
+		<string>A_.glif</string>
+		<key>a</key>
+		<string>a.glif</string>
+		<key>dollar</key>
+		<string>dollar.glif</string>
+		<key>dollar.nostroke</key>
+		<string>dollar.nostroke.glif</string>
+		<key>space</key>
+		<string>space.glif</string>
+	</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/dollar.glif b/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/dollar.glif
new file mode 100644
index 0000000..9f75ced
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/dollar.glif
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="dollar" format="1">
+	<unicode hex="0024"/>
+	<advance width="560"/>
+	<outline>
+		<contour>
+			<point x="260" y="19" type="curve" smooth="yes"/>
+			<point x="195" y="19"/>
+			<point x="167" y="30"/>
+			<point x="118" y="54" type="curve"/>
+			<point x="196" y="4" type="line"/>
+			<point x="188" y="104" type="line" smooth="yes"/>
+			<point x="184" y="156"/>
+			<point x="161" y="176"/>
+			<point x="118" y="176" type="curve" smooth="yes"/>
+			<point x="92" y="176"/>
+			<point x="65" y="162"/>
+			<point x="51" y="124" type="curve"/>
+			<point x="51" y="34"/>
+			<point x="122" y="-16"/>
+			<point x="261" y="-16" type="curve" smooth="yes"/>
+			<point x="424" y="-16"/>
+			<point x="523" y="70"/>
+			<point x="523" y="178" type="curve" smooth="yes"/>
+			<point x="523" y="285"/>
+			<point x="467" y="337"/>
+			<point x="329" y="393" type="curve" smooth="yes"/>
+			<point x="297" y="406" type="line" smooth="yes"/>
+			<point x="234" y="431"/>
+			<point x="204" y="449"/>
+			<point x="204" y="505" type="curve" smooth="yes"/>
+			<point x="204" y="562"/>
+			<point x="245" y="605"/>
+			<point x="308" y="605" type="curve" smooth="yes"/>
+			<point x="350" y="605"/>
+			<point x="377" y="601"/>
+			<point x="429" y="577" type="curve"/>
+			<point x="341" y="629" type="line"/>
+			<point x="354" y="537" type="line" smooth="yes"/>
+			<point x="364" y="463"/>
+			<point x="402" y="448"/>
+			<point x="440" y="448" type="curve" smooth="yes"/>
+			<point x="467" y="448"/>
+			<point x="494" y="464"/>
+			<point x="501" y="504" type="curve"/>
+			<point x="501" y="588"/>
+			<point x="425" y="640"/>
+			<point x="296" y="640" type="curve" smooth="yes"/>
+			<point x="138" y="640"/>
+			<point x="53" y="560"/>
+			<point x="53" y="457" type="curve" smooth="yes"/>
+			<point x="53" y="352"/>
+			<point x="117" y="297"/>
+			<point x="234" y="248" type="curve" smooth="yes"/>
+			<point x="265" y="235" type="line" smooth="yes"/>
+			<point x="337" y="205"/>
+			<point x="365" y="186"/>
+			<point x="365" y="118" type="curve" smooth="yes"/>
+			<point x="365" y="67"/>
+			<point x="324" y="19"/>
+		</contour>
+		<contour>
+			<point x="315" y="314" type="line"/>
+			<point x="315" y="731" type="line"/>
+			<point x="264" y="731" type="line"/>
+			<point x="264" y="314" type="line"/>
+		</contour>
+		<contour>
+			<point x="256" y="-115" type="line"/>
+			<point x="307" y="-115" type="line"/>
+			<point x="307" y="314" type="line"/>
+			<point x="256" y="314" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/dollar.nostroke.glif b/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/dollar.nostroke.glif
new file mode 100644
index 0000000..d009a05
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/dollar.nostroke.glif
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="dollar.nostroke" format="1">
+	<advance width="560"/>
+	<outline>
+		<contour>
+			<point x="260" y="19" type="curve" smooth="yes"/>
+			<point x="195" y="19"/>
+			<point x="167" y="30"/>
+			<point x="118" y="54" type="curve"/>
+			<point x="196" y="4" type="line"/>
+			<point x="188" y="104" type="line" smooth="yes"/>
+			<point x="184" y="156"/>
+			<point x="161" y="176"/>
+			<point x="118" y="176" type="curve" smooth="yes"/>
+			<point x="92" y="176"/>
+			<point x="65" y="162"/>
+			<point x="51" y="124" type="curve"/>
+			<point x="51" y="34"/>
+			<point x="122" y="-16"/>
+			<point x="261" y="-16" type="curve" smooth="yes"/>
+			<point x="424" y="-16"/>
+			<point x="523" y="70"/>
+			<point x="523" y="178" type="curve" smooth="yes"/>
+			<point x="523" y="285"/>
+			<point x="464" y="330"/>
+			<point x="329" y="393" type="curve" smooth="yes"/>
+			<point x="297" y="408" type="line" smooth="yes"/>
+			<point x="246" y="432"/>
+			<point x="218" y="457"/>
+			<point x="218" y="510" type="curve" smooth="yes"/>
+			<point x="218" y="564"/>
+			<point x="253" y="605"/>
+			<point x="308" y="605" type="curve" smooth="yes"/>
+			<point x="350" y="605"/>
+			<point x="377" y="601"/>
+			<point x="429" y="577" type="curve"/>
+			<point x="341" y="629" type="line"/>
+			<point x="354" y="537" type="line" smooth="yes"/>
+			<point x="364" y="463"/>
+			<point x="402" y="448"/>
+			<point x="440" y="448" type="curve" smooth="yes"/>
+			<point x="467" y="448"/>
+			<point x="494" y="464"/>
+			<point x="501" y="504" type="curve"/>
+			<point x="501" y="588"/>
+			<point x="425" y="640"/>
+			<point x="296" y="640" type="curve" smooth="yes"/>
+			<point x="141" y="640"/>
+			<point x="57" y="560"/>
+			<point x="57" y="457" type="curve" smooth="yes"/>
+			<point x="57" y="353"/>
+			<point x="122" y="304"/>
+			<point x="234" y="250" type="curve" smooth="yes"/>
+			<point x="265" y="235" type="line" smooth="yes"/>
+			<point x="331" y="203"/>
+			<point x="355" y="171"/>
+			<point x="355" y="112" type="curve" smooth="yes"/>
+			<point x="355" y="60"/>
+			<point x="315" y="19"/>
+		</contour>
+		<contour>
+			<point x="315" y="624" type="line"/>
+			<point x="315" y="731" type="line"/>
+			<point x="264" y="731" type="line"/>
+			<point x="264" y="624" type="line"/>
+		</contour>
+		<contour>
+			<point x="256" y="-115" type="line"/>
+			<point x="307" y="-115" type="line"/>
+			<point x="307" y="4" type="line"/>
+			<point x="256" y="4" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/space.glif b/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/space.glif
new file mode 100644
index 0000000..32dd00c
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/glyphs/space.glif
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="space" format="1">
+	<unicode hex="0020"/>
+	<advance width="206"/>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/lib.plist b/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/lib.plist
new file mode 100644
index 0000000..a379c4b
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/lib.plist
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.glyphOrder</key>
+	<array>
+		<string>.notdef</string>
+		<string>space</string>
+		<string>A</string>
+		<string>a</string>
+		<string>dollar</string>
+		<string>dollar.nostroke</string>
+	</array>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/metainfo.plist b/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/metainfo.plist
new file mode 100644
index 0000000..9c654d4
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master3.ufo/metainfo.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>creator</key>
+		<string>org.robofab.ufoLib</string>
+		<key>formatVersion</key>
+		<integer>2</integer>
+	</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/features.fea b/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/features.fea
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/features.fea
@@ -0,0 +1 @@
+
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/fontinfo.plist b/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/fontinfo.plist
new file mode 100644
index 0000000..f8b307d
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/fontinfo.plist
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>ascender</key>
+		<integer>738</integer>
+		<key>capHeight</key>
+		<integer>677</integer>
+		<key>descender</key>
+		<integer>-245</integer>
+		<key>familyName</key>
+		<string>Test Family</string>
+		<key>openTypeHheaAscender</key>
+		<integer>918</integer>
+		<key>openTypeHheaDescender</key>
+		<integer>-335</integer>
+		<key>openTypeHheaLineGap</key>
+		<integer>0</integer>
+		<key>openTypeNameDesigner</key>
+		<string>Frank Grießhammer</string>
+		<key>openTypeOS2CodePageRanges</key>
+		<array>
+			<integer>0</integer>
+			<integer>1</integer>
+			<integer>29</integer>
+		</array>
+		<key>openTypeOS2Panose</key>
+		<array>
+			<integer>2</integer>
+			<integer>4</integer>
+			<integer>6</integer>
+			<integer>3</integer>
+			<integer>5</integer>
+			<integer>4</integer>
+			<integer>5</integer>
+			<integer>2</integer>
+			<integer>2</integer>
+			<integer>4</integer>
+		</array>
+		<key>openTypeOS2TypoAscender</key>
+		<integer>730</integer>
+		<key>openTypeOS2TypoDescender</key>
+		<integer>-270</integer>
+		<key>openTypeOS2TypoLineGap</key>
+		<integer>0</integer>
+		<key>openTypeOS2UnicodeRanges</key>
+		<array>
+			<integer>0</integer>
+			<integer>1</integer>
+		</array>
+		<key>openTypeOS2VendorID</key>
+		<string>ADBO</string>
+		<key>openTypeOS2WeightClass</key>
+		<integer>400</integer>
+		<key>openTypeOS2WinAscent</key>
+		<integer>918</integer>
+		<key>openTypeOS2WinDescent</key>
+		<integer>335</integer>
+		<key>postscriptBlueFuzz</key>
+		<integer>0</integer>
+		<key>postscriptBlueScale</key>
+		<real>0.0375</real>
+		<key>postscriptBlueShift</key>
+		<integer>7</integer>
+		<key>postscriptBlueValues</key>
+		<array>
+			<integer>-15</integer>
+			<integer>0</integer>
+			<integer>474</integer>
+			<integer>487</integer>
+			<integer>527</integer>
+			<integer>540</integer>
+			<integer>550</integer>
+			<integer>563</integer>
+			<integer>647</integer>
+			<integer>660</integer>
+			<integer>670</integer>
+			<integer>685</integer>
+			<integer>730</integer>
+			<integer>750</integer>
+		</array>
+		<key>postscriptFamilyBlues</key>
+		<array>
+			<integer>-20</integer>
+			<integer>0</integer>
+			<integer>473</integer>
+			<integer>491</integer>
+			<integer>525</integer>
+			<integer>540</integer>
+			<integer>549</integer>
+			<integer>562</integer>
+			<integer>644</integer>
+			<integer>659</integer>
+			<integer>669</integer>
+			<integer>689</integer>
+			<integer>729</integer>
+			<integer>749</integer>
+		</array>
+		<key>postscriptFamilyOtherBlues</key>
+		<array>
+			<integer>-249</integer>
+			<integer>-239</integer>
+		</array>
+		<key>postscriptFontName</key>
+		<string>TestFamily-Master4</string>
+		<key>postscriptForceBold</key>
+		<false/>
+		<key>postscriptOtherBlues</key>
+		<array>
+			<integer>-250</integer>
+			<integer>-240</integer>
+		</array>
+		<key>postscriptStemSnapH</key>
+		<array>
+			<integer>55</integer>
+			<integer>40</integer>
+		</array>
+		<key>postscriptStemSnapV</key>
+		<array>
+			<integer>80</integer>
+			<integer>90</integer>
+		</array>
+		<key>postscriptUnderlinePosition</key>
+		<integer>-75</integer>
+		<key>postscriptUnderlineThickness</key>
+		<integer>50</integer>
+		<key>styleMapFamilyName</key>
+		<string>Test Family</string>
+		<key>styleName</key>
+		<string>Master 4</string>
+		<key>unitsPerEm</key>
+		<integer>1000</integer>
+		<key>versionMajor</key>
+		<integer>1</integer>
+		<key>versionMinor</key>
+		<integer>1</integer>
+		<key>xHeight</key>
+		<integer>474</integer>
+	</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/A_.glif b/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/A_.glif
new file mode 100644
index 0000000..1061198
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/A_.glif
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="A" format="1">
+	<unicode hex="0041"/>
+	<advance width="663"/>
+	<outline>
+		<contour>
+			<point x="7" y="0" type="line"/>
+			<point x="233" y="0" type="line"/>
+			<point x="233" y="31" type="line"/>
+			<point x="121" y="46" type="line"/>
+			<point x="101" y="46" type="line"/>
+			<point x="7" y="31" type="line"/>
+		</contour>
+		<contour>
+			<point x="73" y="0" type="line"/>
+			<point x="116" y="0" type="line"/>
+			<point x="312" y="599" type="line"/>
+			<point x="293" y="607" type="line"/>
+			<point x="496" y="0" type="line"/>
+			<point x="589" y="0" type="line"/>
+			<point x="359" y="675" type="line"/>
+			<point x="299" y="675" type="line"/>
+		</contour>
+		<contour>
+			<point x="170" y="224" type="line"/>
+			<point x="471" y="224" type="line"/>
+			<point x="461" y="260" type="line"/>
+			<point x="183" y="260" type="line"/>
+		</contour>
+		<contour>
+			<point x="383" y="0" type="line"/>
+			<point x="653" y="0" type="line"/>
+			<point x="653" y="32" type="line"/>
+			<point x="529" y="47" type="line"/>
+			<point x="513" y="47" type="line"/>
+			<point x="383" y="32" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/_notdef.glif b/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/_notdef.glif
new file mode 100644
index 0000000..fe1f506
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/_notdef.glif
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name=".notdef" format="1">
+	<advance width="640"/>
+	<outline>
+		<contour>
+			<point x="84" y="0" type="line"/>
+			<point x="129" y="0" type="line"/>
+			<point x="556" y="670" type="line"/>
+			<point x="511" y="670" type="line"/>
+		</contour>
+		<contour>
+			<point x="556" y="0" type="line"/>
+			<point x="129" y="670" type="line"/>
+			<point x="84" y="670" type="line"/>
+			<point x="511" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="140" y="50" type="line"/>
+			<point x="140" y="620" type="line"/>
+			<point x="500" y="620" type="line"/>
+			<point x="500" y="50" type="line"/>
+		</contour>
+		<contour>
+			<point x="80" y="0" type="line"/>
+			<point x="560" y="0" type="line"/>
+			<point x="560" y="670" type="line"/>
+			<point x="80" y="670" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/a.glif b/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/a.glif
new file mode 100644
index 0000000..b62fb75
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/a.glif
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="a" format="1">
+	<unicode hex="0061"/>
+	<advance width="508"/>
+	<outline>
+		<contour>
+			<point x="46" y="109" type="curve" smooth="yes"/>
+			<point x="46" y="29"/>
+			<point x="107" y="-13"/>
+			<point x="180" y="-13" type="curve" smooth="yes"/>
+			<point x="241" y="-13"/>
+			<point x="275" y="15"/>
+			<point x="335" y="75" type="curve" smooth="yes"/>
+			<point x="364" y="104" type="line"/>
+			<point x="350" y="124" type="line"/>
+			<point x="330" y="106" type="line" smooth="yes"/>
+			<point x="267" y="59"/>
+			<point x="247" y="46"/>
+			<point x="212" y="46" type="curve" smooth="yes"/>
+			<point x="165" y="46"/>
+			<point x="131" y="67"/>
+			<point x="131" y="120" type="curve" smooth="yes"/>
+			<point x="131" y="156"/>
+			<point x="141" y="198"/>
+			<point x="237" y="235" type="curve" smooth="yes"/>
+			<point x="262" y="245"/>
+			<point x="320" y="263"/>
+			<point x="365" y="272" type="curve"/>
+			<point x="365" y="301" type="line"/>
+			<point x="315" y="292"/>
+			<point x="252" y="274"/>
+			<point x="210" y="259" type="curve" smooth="yes"/>
+			<point x="77" y="210"/>
+			<point x="46" y="168"/>
+		</contour>
+		<contour>
+			<point x="325" y="90" type="line"/>
+			<point x="325" y="30"/>
+			<point x="361" y="-10"/>
+			<point x="416" y="-10" type="curve" smooth="yes"/>
+			<point x="450" y="-10"/>
+			<point x="477" y="3"/>
+			<point x="501" y="41" type="curve"/>
+			<point x="485" y="61" type="line"/>
+			<point x="472" y="43"/>
+			<point x="460" y="33"/>
+			<point x="442" y="33" type="curve" smooth="yes"/>
+			<point x="419" y="33"/>
+			<point x="405" y="49"/>
+			<point x="405" y="96" type="curve" smooth="yes"/>
+			<point x="405" y="314" type="line" smooth="yes"/>
+			<point x="405" y="440"/>
+			<point x="355" y="487"/>
+			<point x="252" y="487" type="curve" smooth="yes"/>
+			<point x="152" y="487"/>
+			<point x="78" y="440"/>
+			<point x="59" y="364" type="curve"/>
+			<point x="61" y="339"/>
+			<point x="77" y="324"/>
+			<point x="104" y="324" type="curve" smooth="yes"/>
+			<point x="131" y="324"/>
+			<point x="146" y="340"/>
+			<point x="155" y="369" type="curve" smooth="yes"/>
+			<point x="181" y="455" type="line"/>
+			<point x="141" y="431" type="line"/>
+			<point x="178" y="446"/>
+			<point x="208" y="449"/>
+			<point x="231" y="449" type="curve" smooth="yes"/>
+			<point x="297" y="449"/>
+			<point x="325" y="425"/>
+			<point x="325" y="327" type="curve" smooth="yes"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/contents.plist b/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/contents.plist
new file mode 100644
index 0000000..ec975ca
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/contents.plist
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>.notdef</key>
+		<string>_notdef.glif</string>
+		<key>A</key>
+		<string>A_.glif</string>
+		<key>a</key>
+		<string>a.glif</string>
+		<key>dollar</key>
+		<string>dollar.glif</string>
+		<key>dollar.nostroke</key>
+		<string>dollar.nostroke.glif</string>
+		<key>space</key>
+		<string>space.glif</string>
+	</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/dollar.glif b/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/dollar.glif
new file mode 100644
index 0000000..75c00e4
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/dollar.glif
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="dollar" format="1">
+	<unicode hex="0024"/>
+	<advance width="497"/>
+	<outline>
+		<contour>
+			<point x="248" y="28" type="curve" smooth="yes"/>
+			<point x="209" y="28"/>
+			<point x="164" y="33"/>
+			<point x="118" y="51" type="curve"/>
+			<point x="171" y="16" type="line"/>
+			<point x="154" y="98" type="line" smooth="yes"/>
+			<point x="142" y="151"/>
+			<point x="120" y="164"/>
+			<point x="96" y="164" type="curve" smooth="yes"/>
+			<point x="74" y="164"/>
+			<point x="60" y="153"/>
+			<point x="51" y="133" type="curve"/>
+			<point x="55" y="46"/>
+			<point x="136" y="-13"/>
+			<point x="243" y="-13" type="curve" smooth="yes"/>
+			<point x="379" y="-13"/>
+			<point x="461" y="64"/>
+			<point x="461" y="171" type="curve" smooth="yes"/>
+			<point x="461" y="253"/>
+			<point x="420" y="317"/>
+			<point x="285" y="364" type="curve" smooth="yes"/>
+			<point x="247" y="377" type="line" smooth="yes"/>
+			<point x="176" y="400"/>
+			<point x="136" y="435"/>
+			<point x="136" y="502" type="curve" smooth="yes"/>
+			<point x="136" y="575"/>
+			<point x="192" y="618"/>
+			<point x="267" y="618" type="curve" smooth="yes"/>
+			<point x="297" y="618"/>
+			<point x="333" y="613"/>
+			<point x="375" y="596" type="curve"/>
+			<point x="322" y="634" type="line"/>
+			<point x="340" y="549" type="line" smooth="yes"/>
+			<point x="352" y="497"/>
+			<point x="377" y="483"/>
+			<point x="399" y="483" type="curve" smooth="yes"/>
+			<point x="418" y="483"/>
+			<point x="434" y="493"/>
+			<point x="443" y="514" type="curve"/>
+			<point x="437" y="600"/>
+			<point x="360" y="660"/>
+			<point x="256" y="660" type="curve" smooth="yes"/>
+			<point x="141" y="660"/>
+			<point x="58" y="580"/>
+			<point x="58" y="478" type="curve" smooth="yes"/>
+			<point x="58" y="378"/>
+			<point x="120" y="324"/>
+			<point x="225" y="287" type="curve" smooth="yes"/>
+			<point x="262" y="274" type="line" smooth="yes"/>
+			<point x="347" y="244"/>
+			<point x="383" y="214"/>
+			<point x="383" y="149" type="curve" smooth="yes"/>
+			<point x="383" y="74"/>
+			<point x="329" y="28"/>
+		</contour>
+		<contour>
+			<point x="275" y="322" type="line"/>
+			<point x="275" y="750" type="line"/>
+			<point x="243" y="750" type="line"/>
+			<point x="243" y="322" type="line"/>
+		</contour>
+		<contour>
+			<point x="242" y="-115" type="line"/>
+			<point x="274" y="-115" type="line"/>
+			<point x="274" y="322" type="line"/>
+			<point x="242" y="322" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/dollar.nostroke.glif b/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/dollar.nostroke.glif
new file mode 100644
index 0000000..53dd438
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/dollar.nostroke.glif
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="dollar.nostroke" format="1">
+	<advance width="497"/>
+	<outline>
+		<contour>
+			<point x="248" y="28" type="curve" smooth="yes"/>
+			<point x="209" y="28"/>
+			<point x="164" y="33"/>
+			<point x="118" y="51" type="curve"/>
+			<point x="171" y="16" type="line"/>
+			<point x="154" y="98" type="line" smooth="yes"/>
+			<point x="142" y="151"/>
+			<point x="120" y="164"/>
+			<point x="96" y="164" type="curve" smooth="yes"/>
+			<point x="74" y="164"/>
+			<point x="60" y="153"/>
+			<point x="51" y="133" type="curve"/>
+			<point x="55" y="46"/>
+			<point x="136" y="-13"/>
+			<point x="243" y="-13" type="curve" smooth="yes"/>
+			<point x="379" y="-13"/>
+			<point x="461" y="64"/>
+			<point x="461" y="171" type="curve" smooth="yes"/>
+			<point x="461" y="253"/>
+			<point x="420" y="317"/>
+			<point x="285" y="364" type="curve" smooth="yes"/>
+			<point x="247" y="377" type="line" smooth="yes"/>
+			<point x="177" y="399"/>
+			<point x="137" y="436"/>
+			<point x="137" y="503" type="curve" smooth="yes"/>
+			<point x="137" y="575"/>
+			<point x="193" y="618"/>
+			<point x="267" y="618" type="curve" smooth="yes"/>
+			<point x="297" y="618"/>
+			<point x="333" y="613"/>
+			<point x="375" y="596" type="curve"/>
+			<point x="322" y="634" type="line"/>
+			<point x="340" y="549" type="line" smooth="yes"/>
+			<point x="352" y="497"/>
+			<point x="377" y="483"/>
+			<point x="399" y="483" type="curve" smooth="yes"/>
+			<point x="418" y="483"/>
+			<point x="434" y="493"/>
+			<point x="443" y="514" type="curve"/>
+			<point x="437" y="600"/>
+			<point x="360" y="660"/>
+			<point x="256" y="660" type="curve" smooth="yes"/>
+			<point x="141" y="660"/>
+			<point x="58" y="580"/>
+			<point x="58" y="478" type="curve" smooth="yes"/>
+			<point x="58" y="378"/>
+			<point x="120" y="324"/>
+			<point x="225" y="287" type="curve" smooth="yes"/>
+			<point x="262" y="274" type="line" smooth="yes"/>
+			<point x="347" y="244"/>
+			<point x="382" y="210"/>
+			<point x="382" y="146" type="curve" smooth="yes"/>
+			<point x="382" y="71"/>
+			<point x="329" y="28"/>
+		</contour>
+		<contour>
+			<point x="275" y="639" type="line"/>
+			<point x="275" y="750" type="line"/>
+			<point x="243" y="750" type="line"/>
+			<point x="243" y="639" type="line"/>
+		</contour>
+		<contour>
+			<point x="238" y="-115" type="line"/>
+			<point x="271" y="-115" type="line"/>
+			<point x="271" y="8" type="line"/>
+			<point x="238" y="8" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/space.glif b/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/space.glif
new file mode 100644
index 0000000..d1fb461
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/glyphs/space.glif
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="space" format="1">
+	<unicode hex="0020"/>
+	<advance width="234"/>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/lib.plist b/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/lib.plist
new file mode 100644
index 0000000..a379c4b
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/lib.plist
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.glyphOrder</key>
+	<array>
+		<string>.notdef</string>
+		<string>space</string>
+		<string>A</string>
+		<string>a</string>
+		<string>dollar</string>
+		<string>dollar.nostroke</string>
+	</array>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/metainfo.plist b/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/metainfo.plist
new file mode 100644
index 0000000..9c654d4
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily-Master4.ufo/metainfo.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>creator</key>
+		<string>org.robofab.ufoLib</string>
+		<key>formatVersion</key>
+		<integer>2</integer>
+	</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/features.fea b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/features.fea
new file mode 100644
index 0000000..5d84635
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/features.fea
@@ -0,0 +1,81 @@
+table head {
+    FontRevision 2.020;
+} head;
+
+
+table name {
+    nameid 9 "Paul D. Hunt";
+    nameid 9 1 "Paul D. Hunt";
+} name;
+
+
+table hhea {
+    Ascender 984;
+    Descender -273;
+    LineGap 0;
+} hhea;
+
+
+table BASE {
+    HorizAxis.BaseTagList ideo romn;
+    HorizAxis.BaseScriptList
+    latn romn -170 0,
+    grek romn -170 0,
+    cyrl romn -170 0,
+    DFLT romn -170 0;
+} BASE;
+
+
+table OS/2 {
+    Panose 2 11 3 3 3 4 3 2 2 4;
+    XHeight 478;
+    WeightClass 200;
+
+    TypoAscender 750;
+    TypoDescender -250;
+    TypoLineGap 0;
+    winAscent 984;
+    winDescent 273;
+
+    CapHeight 660;
+    WidthClass 5;
+    Vendor "ADBO";
+    FSType 0;
+} OS/2;
+
+
+languagesystem DFLT dflt;
+languagesystem latn dflt;
+
+# GSUB =========================================
+# Merging of GSUB is not performed. The variable
+# font will inherit the GSUB table from the
+# base master.
+
+feature c2sc {
+    sub A by A.sc; # GSUB LookupType 1
+} c2sc;
+
+feature ss01 {
+    featureNames {
+        name "Alternate a";
+        name 1 0 0 "Alternate a";};
+    sub a by a.alt;
+} ss01;
+
+feature ccmp {
+    sub ampersand by a n d; # GSUB LookupType 2
+} ccmp;
+
+feature salt {
+    sub a from [a.alt A.sc]; # GSUB LookupType 3
+} salt;
+
+feature liga {
+    sub f t by f_t; # GSUB LookupType 4
+} liga;
+
+feature calt {
+    sub a' t by a.alt; # GSUB LookupType 6
+} calt;
+
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/fontinfo.plist b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/fontinfo.plist
new file mode 100644
index 0000000..2775576
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/fontinfo.plist
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>ascender</key>
+		<integer>722</integer>
+		<key>capHeight</key>
+		<integer>660</integer>
+		<key>descender</key>
+		<integer>-222</integer>
+		<key>familyName</key>
+		<string>Test Family 2</string>
+		<key>italicAngle</key>
+		<integer>0</integer>
+		<key>openTypeHheaAscender</key>
+		<integer>984</integer>
+		<key>openTypeHheaDescender</key>
+		<integer>-273</integer>
+		<key>openTypeHheaLineGap</key>
+		<integer>0</integer>
+		<key>openTypeNameDesigner</key>
+		<string>Paul D. Hunt</string>
+		<key>openTypeOS2CodePageRanges</key>
+		<array>
+			<integer>0</integer>
+			<integer>1</integer>
+			<integer>29</integer>
+		</array>
+		<key>openTypeOS2Panose</key>
+		<array>
+			<integer>2</integer>
+			<integer>11</integer>
+			<integer>5</integer>
+			<integer>3</integer>
+			<integer>3</integer>
+			<integer>4</integer>
+			<integer>3</integer>
+			<integer>2</integer>
+			<integer>2</integer>
+			<integer>4</integer>
+		</array>
+		<key>openTypeOS2TypoAscender</key>
+		<integer>750</integer>
+		<key>openTypeOS2TypoDescender</key>
+		<integer>-250</integer>
+		<key>openTypeOS2TypoLineGap</key>
+		<integer>0</integer>
+		<key>openTypeOS2UnicodeRanges</key>
+		<array>
+			<integer>0</integer>
+			<integer>1</integer>
+		</array>
+		<key>openTypeOS2VendorID</key>
+		<string>ADBO</string>
+		<key>openTypeOS2WeightClass</key>
+		<integer>200</integer>
+		<key>openTypeOS2WinAscent</key>
+		<integer>984</integer>
+		<key>openTypeOS2WinDescent</key>
+		<integer>273</integer>
+		<key>postscriptBlueFuzz</key>
+		<integer>0</integer>
+		<key>postscriptBlueScale</key>
+		<real>0.0625</real>
+		<key>postscriptBlueValues</key>
+		<array>
+			<integer>-12</integer>
+			<integer>0</integer>
+			<integer>478</integer>
+			<integer>490</integer>
+			<integer>510</integer>
+			<integer>522</integer>
+			<integer>570</integer>
+			<integer>582</integer>
+			<integer>640</integer>
+			<integer>652</integer>
+			<integer>660</integer>
+			<integer>672</integer>
+			<integer>722</integer>
+			<integer>734</integer>
+		</array>
+		<key>postscriptFamilyBlues</key>
+		<array>
+			<integer>-12</integer>
+			<integer>0</integer>
+			<integer>486</integer>
+			<integer>498</integer>
+			<integer>518</integer>
+			<integer>530</integer>
+			<integer>574</integer>
+			<integer>586</integer>
+			<integer>638</integer>
+			<integer>650</integer>
+			<integer>656</integer>
+			<integer>668</integer>
+			<integer>712</integer>
+			<integer>724</integer>
+		</array>
+		<key>postscriptFamilyOtherBlues</key>
+		<array>
+			<integer>-217</integer>
+			<integer>-205</integer>
+		</array>
+		<key>postscriptFontName</key>
+		<string>TestFamily2-Master0</string>
+		<key>postscriptForceBold</key>
+		<false/>
+		<key>postscriptOtherBlues</key>
+		<array>
+			<integer>-234</integer>
+			<integer>-222</integer>
+		</array>
+		<key>postscriptStemSnapH</key>
+		<array>
+			<integer>28</integer>
+			<integer>40</integer>
+		</array>
+		<key>postscriptStemSnapV</key>
+		<array>
+			<integer>32</integer>
+			<integer>48</integer>
+		</array>
+		<key>postscriptUnderlinePosition</key>
+		<integer>-75</integer>
+		<key>postscriptUnderlineThickness</key>
+		<integer>50</integer>
+		<key>styleMapFamilyName</key>
+		<string>Test Family 2</string>
+		<key>styleName</key>
+		<string>Master 0</string>
+		<key>unitsPerEm</key>
+		<integer>1000</integer>
+		<key>versionMajor</key>
+		<integer>2</integer>
+		<key>versionMinor</key>
+		<integer>20</integer>
+		<key>xHeight</key>
+		<integer>478</integer>
+	</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/A_.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/A_.glif
new file mode 100644
index 0000000..6712970
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/A_.glif
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="A" format="1">
+	<unicode hex="0041"/>
+	<advance width="520"/>
+	<outline>
+		<contour>
+			<point x="10" y="0" type="line"/>
+			<point x="42" y="0" type="line"/>
+			<point x="182" y="396" type="line" smooth="yes"/>
+			<point x="210" y="476"/>
+			<point x="234" y="544"/>
+			<point x="258" y="626" type="curve"/>
+			<point x="262" y="626" type="line"/>
+			<point x="286" y="544"/>
+			<point x="310" y="476"/>
+			<point x="338" y="396" type="curve" smooth="yes"/>
+			<point x="476" y="0" type="line"/>
+			<point x="510" y="0" type="line"/>
+			<point x="274" y="660" type="line"/>
+			<point x="246" y="660" type="line"/>
+		</contour>
+		<contour>
+			<point x="112" y="236" type="line"/>
+			<point x="405" y="236" type="line"/>
+			<point x="405" y="264" type="line"/>
+			<point x="112" y="264" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/A_.sc.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/A_.sc.glif
new file mode 100644
index 0000000..7152360
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/A_.sc.glif
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="A.sc" format="1">
+	<advance width="444"/>
+	<outline>
+		<contour>
+			<point x="10" y="0" type="line"/>
+			<point x="42" y="0" type="line"/>
+			<point x="158" y="304" type="line" smooth="yes"/>
+			<point x="181" y="366"/>
+			<point x="199" y="414"/>
+			<point x="220" y="475" type="curve"/>
+			<point x="224" y="475" type="line"/>
+			<point x="245" y="415"/>
+			<point x="263" y="367"/>
+			<point x="286" y="304" type="curve" smooth="yes"/>
+			<point x="400" y="0" type="line"/>
+			<point x="434" y="0" type="line"/>
+			<point x="236" y="510" type="line"/>
+			<point x="207" y="510" type="line"/>
+		</contour>
+		<contour>
+			<point x="97" y="176" type="line"/>
+			<point x="345" y="176" type="line"/>
+			<point x="345" y="204" type="line"/>
+			<point x="97" y="204" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/_notdef.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/_notdef.glif
new file mode 100644
index 0000000..5f6c8ec
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/_notdef.glif
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name=".notdef" format="1">
+	<advance width="624"/>
+	<outline>
+		<contour>
+			<point x="96" y="0" type="line"/>
+			<point x="528" y="0" type="line"/>
+			<point x="528" y="660" type="line"/>
+			<point x="96" y="660" type="line"/>
+		</contour>
+		<contour>
+			<point x="144" y="32" type="line"/>
+			<point x="246" y="208" type="line"/>
+			<point x="310" y="314" type="line"/>
+			<point x="314" y="314" type="line"/>
+			<point x="376" y="208" type="line"/>
+			<point x="476" y="32" type="line"/>
+		</contour>
+		<contour>
+			<point x="310" y="366" type="line"/>
+			<point x="254" y="458" type="line"/>
+			<point x="160" y="626" type="line"/>
+			<point x="462" y="626" type="line"/>
+			<point x="368" y="458" type="line"/>
+			<point x="314" y="366" type="line"/>
+		</contour>
+		<contour>
+			<point x="134" y="74" type="line"/>
+			<point x="134" y="610" type="line"/>
+			<point x="288" y="340" type="line"/>
+		</contour>
+		<contour>
+			<point x="488" y="74" type="line"/>
+			<point x="336" y="340" type="line"/>
+			<point x="488" y="610" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/a.alt.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/a.alt.glif
new file mode 100644
index 0000000..ecc308e
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/a.alt.glif
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="a.alt" format="1">
+	<advance width="540"/>
+	<outline>
+		<contour>
+			<point x="252" y="-12" type="curve" smooth="yes"/>
+			<point x="318" y="-12"/>
+			<point x="372" y="24"/>
+			<point x="412" y="64" type="curve"/>
+			<point x="414" y="64" type="line"/>
+			<point x="418" y="0" type="line"/>
+			<point x="444" y="0" type="line"/>
+			<point x="444" y="478" type="line"/>
+			<point x="416" y="478" type="line"/>
+			<point x="414" y="432" type="line"/>
+			<point x="412" y="432" type="line"/>
+			<point x="366" y="468"/>
+			<point x="326" y="490"/>
+			<point x="268" y="490" type="curve" smooth="yes"/>
+			<point x="152" y="490"/>
+			<point x="54" y="392"/>
+			<point x="54" y="238" type="curve" smooth="yes"/>
+			<point x="54" y="76"/>
+			<point x="132" y="-12"/>
+		</contour>
+		<contour>
+			<point x="254" y="16" type="curve" smooth="yes"/>
+			<point x="146" y="16"/>
+			<point x="86" y="106"/>
+			<point x="86" y="238" type="curve" smooth="yes"/>
+			<point x="86" y="362"/>
+			<point x="164" y="462"/>
+			<point x="266" y="462" type="curve" smooth="yes"/>
+			<point x="316" y="462"/>
+			<point x="360" y="444"/>
+			<point x="414" y="396" type="curve"/>
+			<point x="414" y="100" type="line"/>
+			<point x="360" y="46"/>
+			<point x="310" y="16"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/a.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/a.glif
new file mode 100644
index 0000000..7a91021
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/a.glif
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="a" format="1">
+	<unicode hex="0061"/>
+	<advance width="486"/>
+	<outline>
+		<contour>
+			<point x="198" y="-12" type="curve" smooth="yes"/>
+			<point x="262" y="-12"/>
+			<point x="322" y="24"/>
+			<point x="372" y="64" type="curve"/>
+			<point x="374" y="64" type="line"/>
+			<point x="378" y="0" type="line"/>
+			<point x="404" y="0" type="line"/>
+			<point x="404" y="310" type="line" smooth="yes"/>
+			<point x="404" y="406"/>
+			<point x="370" y="490"/>
+			<point x="258" y="490" type="curve" smooth="yes"/>
+			<point x="180" y="490"/>
+			<point x="114" y="450"/>
+			<point x="84" y="428" type="curve"/>
+			<point x="100" y="404" type="line"/>
+			<point x="130" y="428"/>
+			<point x="188" y="462"/>
+			<point x="256" y="462" type="curve" smooth="yes"/>
+			<point x="356" y="462"/>
+			<point x="376" y="376"/>
+			<point x="374" y="298" type="curve"/>
+			<point x="158" y="274"/>
+			<point x="60" y="224"/>
+			<point x="60" y="117" type="curve" smooth="yes"/>
+			<point x="60" y="26"/>
+			<point x="124" y="-12"/>
+		</contour>
+		<contour>
+			<point x="200" y="16" type="curve" smooth="yes"/>
+			<point x="142" y="16"/>
+			<point x="92" y="44"/>
+			<point x="92" y="118" type="curve" smooth="yes"/>
+			<point x="92" y="200"/>
+			<point x="164" y="248"/>
+			<point x="374" y="272" type="curve"/>
+			<point x="374" y="98" type="line"/>
+			<point x="310" y="44"/>
+			<point x="258" y="16"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/ampersand.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/ampersand.glif
new file mode 100644
index 0000000..8844902
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/ampersand.glif
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="ampersand" format="1">
+	<unicode hex="0026"/>
+	<advance width="562"/>
+	<outline>
+		<contour>
+			<point x="224" y="-12" type="curve" smooth="yes"/>
+			<point x="302" y="-12"/>
+			<point x="360" y="28"/>
+			<point x="410" y="84" type="curve" smooth="yes"/>
+			<point x="468" y="153"/>
+			<point x="510" y="244"/>
+			<point x="538" y="342" type="curve"/>
+			<point x="508" y="342" type="line"/>
+			<point x="482" y="248"/>
+			<point x="444" y="166"/>
+			<point x="388" y="102" type="curve" smooth="yes"/>
+			<point x="344" y="52"/>
+			<point x="288" y="16"/>
+			<point x="226" y="16" type="curve" smooth="yes"/>
+			<point x="142" y="16"/>
+			<point x="70" y="76"/>
+			<point x="70" y="168" type="curve" smooth="yes"/>
+			<point x="70" y="332"/>
+			<point x="364" y="392"/>
+			<point x="364" y="556" type="curve" smooth="yes"/>
+			<point x="364" y="622"/>
+			<point x="328" y="672"/>
+			<point x="260" y="672" type="curve" smooth="yes"/>
+			<point x="184" y="672"/>
+			<point x="130" y="612"/>
+			<point x="130" y="528" type="curve" smooth="yes"/>
+			<point x="130" y="382"/>
+			<point x="264" y="196"/>
+			<point x="392" y="82" type="curve" smooth="yes"/>
+			<point x="446" y="34"/>
+			<point x="496" y="4"/>
+			<point x="538" y="-12" type="curve"/>
+			<point x="550" y="16" type="line"/>
+			<point x="508" y="32"/>
+			<point x="460" y="62"/>
+			<point x="410" y="106" type="curve" smooth="yes"/>
+			<point x="290" y="210"/>
+			<point x="160" y="392"/>
+			<point x="160" y="530" type="curve" smooth="yes"/>
+			<point x="160" y="592"/>
+			<point x="196" y="644"/>
+			<point x="258" y="644" type="curve" smooth="yes"/>
+			<point x="314" y="644"/>
+			<point x="334" y="598"/>
+			<point x="334" y="554" type="curve" smooth="yes"/>
+			<point x="334" y="402"/>
+			<point x="38" y="346"/>
+			<point x="38" y="166" type="curve" smooth="yes"/>
+			<point x="38" y="56"/>
+			<point x="124" y="-12"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/atilde.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/atilde.glif
new file mode 100644
index 0000000..37411a1
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/atilde.glif
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="atilde" format="1">
+	<unicode hex="00E3"/>
+	<advance width="486"/>
+	<outline>
+		<component base="a"/>
+		<component base="tildecmb" xOffset="242"/>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/circledotted.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/circledotted.glif
new file mode 100644
index 0000000..c6337ce
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/circledotted.glif
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="circledotted" format="1">
+	<unicode hex="25CC"/>
+	<advance width="592"/>
+	<outline>
+		<contour>
+			<point x="110" y="97" type="curve" smooth="yes"/>
+			<point x="129" y="97"/>
+			<point x="141" y="110"/>
+			<point x="141" y="129" type="curve" smooth="yes"/>
+			<point x="141" y="150"/>
+			<point x="128" y="161"/>
+			<point x="110" y="161" type="curve" smooth="yes"/>
+			<point x="94" y="161"/>
+			<point x="81" y="150"/>
+			<point x="81" y="129" type="curve" smooth="yes"/>
+			<point x="81" y="110"/>
+			<point x="94" y="97"/>
+		</contour>
+		<contour>
+			<point x="82" y="207" type="curve" smooth="yes"/>
+			<point x="101" y="207"/>
+			<point x="114" y="219"/>
+			<point x="114" y="239" type="curve" smooth="yes"/>
+			<point x="114" y="260"/>
+			<point x="101" y="270"/>
+			<point x="82" y="270" type="curve" smooth="yes"/>
+			<point x="67" y="270"/>
+			<point x="54" y="260"/>
+			<point x="54" y="239" type="curve" smooth="yes"/>
+			<point x="54" y="219"/>
+			<point x="67" y="207"/>
+		</contour>
+		<contour>
+			<point x="110" y="318" type="curve" smooth="yes"/>
+			<point x="129" y="318"/>
+			<point x="141" y="330"/>
+			<point x="141" y="351" type="curve" smooth="yes"/>
+			<point x="141" y="371"/>
+			<point x="128" y="382"/>
+			<point x="110" y="382" type="curve" smooth="yes"/>
+			<point x="94" y="382"/>
+			<point x="81" y="371"/>
+			<point x="81" y="351" type="curve" smooth="yes"/>
+			<point x="81" y="330"/>
+			<point x="94" y="318"/>
+		</contour>
+		<contour>
+			<point x="189" y="15" type="curve" smooth="yes"/>
+			<point x="207" y="15"/>
+			<point x="219" y="27"/>
+			<point x="219" y="49" type="curve" smooth="yes"/>
+			<point x="219" y="68"/>
+			<point x="206" y="78"/>
+			<point x="189" y="78" type="curve" smooth="yes"/>
+			<point x="173" y="78"/>
+			<point x="160" y="68"/>
+			<point x="160" y="49" type="curve" smooth="yes"/>
+			<point x="160" y="27"/>
+			<point x="173" y="15"/>
+		</contour>
+		<contour>
+			<point x="189" y="400" type="curve" smooth="yes"/>
+			<point x="207" y="400"/>
+			<point x="219" y="412"/>
+			<point x="219" y="431" type="curve" smooth="yes"/>
+			<point x="219" y="453"/>
+			<point x="206" y="463"/>
+			<point x="189" y="463" type="curve" smooth="yes"/>
+			<point x="173" y="463"/>
+			<point x="160" y="453"/>
+			<point x="160" y="431" type="curve" smooth="yes"/>
+			<point x="160" y="412"/>
+			<point x="173" y="400"/>
+		</contour>
+		<contour>
+			<point x="295" y="-12" type="curve" smooth="yes"/>
+			<point x="313" y="-12"/>
+			<point x="326" y="-1"/>
+			<point x="326" y="20" type="curve" smooth="yes"/>
+			<point x="326" y="40"/>
+			<point x="313" y="51"/>
+			<point x="295" y="51" type="curve" smooth="yes"/>
+			<point x="279" y="51"/>
+			<point x="266" y="40"/>
+			<point x="266" y="20" type="curve" smooth="yes"/>
+			<point x="266" y="-1"/>
+			<point x="279" y="-12"/>
+		</contour>
+		<contour>
+			<point x="295" y="426" type="curve" smooth="yes"/>
+			<point x="313" y="426"/>
+			<point x="326" y="438"/>
+			<point x="326" y="458" type="curve" smooth="yes"/>
+			<point x="326" y="478"/>
+			<point x="313" y="490"/>
+			<point x="295" y="490" type="curve" smooth="yes"/>
+			<point x="279" y="490"/>
+			<point x="266" y="478"/>
+			<point x="266" y="458" type="curve" smooth="yes"/>
+			<point x="266" y="438"/>
+			<point x="279" y="426"/>
+		</contour>
+		<contour>
+			<point x="401" y="15" type="curve" smooth="yes"/>
+			<point x="420" y="15"/>
+			<point x="431" y="27"/>
+			<point x="431" y="49" type="curve" smooth="yes"/>
+			<point x="431" y="68"/>
+			<point x="418" y="78"/>
+			<point x="401" y="78" type="curve" smooth="yes"/>
+			<point x="386" y="78"/>
+			<point x="373" y="68"/>
+			<point x="373" y="49" type="curve" smooth="yes"/>
+			<point x="373" y="27"/>
+			<point x="386" y="15"/>
+		</contour>
+		<contour>
+			<point x="401" y="399" type="curve" smooth="yes"/>
+			<point x="420" y="399"/>
+			<point x="431" y="412"/>
+			<point x="431" y="431" type="curve" smooth="yes"/>
+			<point x="431" y="452"/>
+			<point x="418" y="462"/>
+			<point x="401" y="462" type="curve" smooth="yes"/>
+			<point x="386" y="462"/>
+			<point x="373" y="452"/>
+			<point x="373" y="431" type="curve" smooth="yes"/>
+			<point x="373" y="412"/>
+			<point x="386" y="399"/>
+		</contour>
+		<contour>
+			<point x="480" y="97" type="curve" smooth="yes"/>
+			<point x="499" y="97"/>
+			<point x="510" y="110"/>
+			<point x="510" y="129" type="curve" smooth="yes"/>
+			<point x="510" y="150"/>
+			<point x="497" y="161"/>
+			<point x="480" y="161" type="curve" smooth="yes"/>
+			<point x="465" y="161"/>
+			<point x="451" y="150"/>
+			<point x="451" y="129" type="curve" smooth="yes"/>
+			<point x="451" y="110"/>
+			<point x="465" y="97"/>
+		</contour>
+		<contour>
+			<point x="508" y="207" type="curve" smooth="yes"/>
+			<point x="526" y="207"/>
+			<point x="538" y="219"/>
+			<point x="538" y="239" type="curve" smooth="yes"/>
+			<point x="538" y="260"/>
+			<point x="523" y="270"/>
+			<point x="508" y="270" type="curve" smooth="yes"/>
+			<point x="491" y="270"/>
+			<point x="478" y="260"/>
+			<point x="478" y="239" type="curve" smooth="yes"/>
+			<point x="478" y="219"/>
+			<point x="491" y="207"/>
+		</contour>
+		<contour>
+			<point x="480" y="317" type="curve" smooth="yes"/>
+			<point x="499" y="317"/>
+			<point x="510" y="329"/>
+			<point x="510" y="349" type="curve" smooth="yes"/>
+			<point x="510" y="369"/>
+			<point x="497" y="380"/>
+			<point x="480" y="380" type="curve" smooth="yes"/>
+			<point x="465" y="380"/>
+			<point x="451" y="369"/>
+			<point x="451" y="349" type="curve" smooth="yes"/>
+			<point x="451" y="329"/>
+			<point x="465" y="317"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/contents.plist b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/contents.plist
new file mode 100644
index 0000000..2c5ee6b
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/contents.plist
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>.notdef</key>
+		<string>_notdef.glif</string>
+		<key>A</key>
+		<string>A_.glif</string>
+		<key>A.sc</key>
+		<string>A_.sc.glif</string>
+		<key>a</key>
+		<string>a.glif</string>
+		<key>a.alt</key>
+		<string>a.alt.glif</string>
+		<key>ampersand</key>
+		<string>ampersand.glif</string>
+		<key>atilde</key>
+		<string>atilde.glif</string>
+		<key>circledotted</key>
+		<string>circledotted.glif</string>
+		<key>d</key>
+		<string>d.glif</string>
+		<key>dieresisbelowcmb</key>
+		<string>dieresisbelowcmb.glif</string>
+		<key>dieresiscmb</key>
+		<string>dieresiscmb.glif</string>
+		<key>f</key>
+		<string>f.glif</string>
+		<key>f_t</key>
+		<string>f_t.glif</string>
+		<key>n</key>
+		<string>n.glif</string>
+		<key>space</key>
+		<string>space.glif</string>
+		<key>t</key>
+		<string>t.glif</string>
+		<key>tildebelowcmb</key>
+		<string>tildebelowcmb.glif</string>
+		<key>tildecmb</key>
+		<string>tildecmb.glif</string>
+	</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/d.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/d.glif
new file mode 100644
index 0000000..1dc211e
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/d.glif
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="d" format="1">
+	<unicode hex="0064"/>
+	<advance width="540"/>
+	<outline>
+		<contour>
+			<point x="252" y="-12" type="curve" smooth="yes"/>
+			<point x="318" y="-12"/>
+			<point x="372" y="24"/>
+			<point x="412" y="64" type="curve"/>
+			<point x="414" y="64" type="line"/>
+			<point x="418" y="0" type="line"/>
+			<point x="444" y="0" type="line"/>
+			<point x="444" y="722" type="line"/>
+			<point x="414" y="722" type="line"/>
+			<point x="414" y="520" type="line"/>
+			<point x="416" y="430" type="line"/>
+			<point x="366" y="468"/>
+			<point x="326" y="490"/>
+			<point x="268" y="490" type="curve" smooth="yes"/>
+			<point x="152" y="490"/>
+			<point x="54" y="392"/>
+			<point x="54" y="238" type="curve" smooth="yes"/>
+			<point x="54" y="76"/>
+			<point x="132" y="-12"/>
+		</contour>
+		<contour>
+			<point x="254" y="16" type="curve" smooth="yes"/>
+			<point x="146" y="16"/>
+			<point x="86" y="106"/>
+			<point x="86" y="238" type="curve" smooth="yes"/>
+			<point x="86" y="362"/>
+			<point x="164" y="462"/>
+			<point x="266" y="462" type="curve" smooth="yes"/>
+			<point x="316" y="462"/>
+			<point x="360" y="444"/>
+			<point x="414" y="396" type="curve"/>
+			<point x="414" y="100" type="line"/>
+			<point x="360" y="46"/>
+			<point x="310" y="16"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/dieresisbelowcmb.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/dieresisbelowcmb.glif
new file mode 100644
index 0000000..c63f156
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/dieresisbelowcmb.glif
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="dieresisbelowcmb" format="1">
+	<unicode hex="0324"/>
+	<outline>
+		<component base="dieresiscmb" yOffset="-790"/>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/dieresiscmb.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/dieresiscmb.glif
new file mode 100644
index 0000000..33f264f
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/dieresiscmb.glif
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="dieresiscmb" format="1">
+	<unicode hex="0308"/>
+	<outline>
+		<contour>
+			<point x="-86" y="602" type="curve" smooth="yes"/>
+			<point x="-68" y="602"/>
+			<point x="-54" y="616"/>
+			<point x="-54" y="634" type="curve" smooth="yes"/>
+			<point x="-54" y="652"/>
+			<point x="-68" y="666"/>
+			<point x="-86" y="666" type="curve" smooth="yes"/>
+			<point x="-104" y="666"/>
+			<point x="-118" y="652"/>
+			<point x="-118" y="634" type="curve" smooth="yes"/>
+			<point x="-118" y="616"/>
+			<point x="-104" y="602"/>
+		</contour>
+		<contour>
+			<point x="86" y="602" type="curve" smooth="yes"/>
+			<point x="104" y="602"/>
+			<point x="118" y="616"/>
+			<point x="118" y="634" type="curve" smooth="yes"/>
+			<point x="118" y="652"/>
+			<point x="104" y="666"/>
+			<point x="86" y="666" type="curve" smooth="yes"/>
+			<point x="68" y="666"/>
+			<point x="54" y="652"/>
+			<point x="54" y="634" type="curve" smooth="yes"/>
+			<point x="54" y="616"/>
+			<point x="68" y="602"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/f.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/f.glif
new file mode 100644
index 0000000..b87de41
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/f.glif
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="f" format="1">
+	<unicode hex="0066"/>
+	<advance width="252"/>
+	<outline>
+		<contour>
+			<point x="100" y="0" type="line"/>
+			<point x="130" y="0" type="line"/>
+			<point x="130" y="592" type="line" smooth="yes"/>
+			<point x="130" y="664"/>
+			<point x="154" y="706"/>
+			<point x="208" y="706" type="curve" smooth="yes"/>
+			<point x="226" y="706"/>
+			<point x="246" y="702"/>
+			<point x="266" y="692" type="curve"/>
+			<point x="276" y="718" type="line"/>
+			<point x="254" y="728"/>
+			<point x="230" y="734"/>
+			<point x="210" y="734" type="curve" smooth="yes"/>
+			<point x="142" y="734"/>
+			<point x="100" y="690"/>
+			<point x="100" y="596" type="curve" smooth="yes"/>
+		</contour>
+		<contour>
+			<point x="34" y="450" type="line"/>
+			<point x="244" y="450" type="line"/>
+			<point x="244" y="478" type="line"/>
+			<point x="100" y="478" type="line"/>
+			<point x="34" y="474" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/f_t.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/f_t.glif
new file mode 100644
index 0000000..7c1f3d3
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/f_t.glif
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="f_t" format="1">
+	<advance width="518"/>
+	<outline>
+		<contour>
+			<point x="100" y="0" type="line"/>
+			<point x="130" y="0" type="line"/>
+			<point x="130" y="592" type="line" smooth="yes"/>
+			<point x="130" y="664"/>
+			<point x="154" y="706"/>
+			<point x="208" y="706" type="curve" smooth="yes"/>
+			<point x="226" y="706"/>
+			<point x="246" y="702"/>
+			<point x="266" y="692" type="curve"/>
+			<point x="276" y="718" type="line"/>
+			<point x="254" y="728"/>
+			<point x="230" y="734"/>
+			<point x="210" y="734" type="curve" smooth="yes"/>
+			<point x="142" y="734"/>
+			<point x="100" y="690"/>
+			<point x="100" y="596" type="curve" smooth="yes"/>
+		</contour>
+		<contour>
+			<point x="434" y="-12" type="curve" smooth="yes"/>
+			<point x="450" y="-12"/>
+			<point x="480" y="-4"/>
+			<point x="508" y="6" type="curve"/>
+			<point x="498" y="32" type="line"/>
+			<point x="480" y="24"/>
+			<point x="454" y="16"/>
+			<point x="436" y="16" type="curve" smooth="yes"/>
+			<point x="366" y="16"/>
+			<point x="352" y="60"/>
+			<point x="352" y="122" type="curve" smooth="yes"/>
+			<point x="352" y="450" type="line"/>
+			<point x="494" y="450" type="line"/>
+			<point x="494" y="478" type="line"/>
+			<point x="352" y="478" type="line"/>
+			<point x="352" y="618" type="line"/>
+			<point x="326" y="618" type="line"/>
+			<point x="322" y="478" type="line"/>
+			<point x="100" y="478" type="line"/>
+			<point x="34" y="474" type="line"/>
+			<point x="34" y="450" type="line"/>
+			<point x="322" y="450" type="line"/>
+			<point x="322" y="126" type="line" smooth="yes"/>
+			<point x="322" y="44"/>
+			<point x="346" y="-12"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/n.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/n.glif
new file mode 100644
index 0000000..d2486e8
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/n.glif
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="n" format="1">
+	<unicode hex="006E"/>
+	<advance width="526"/>
+	<outline>
+		<contour>
+			<point x="96" y="0" type="line"/>
+			<point x="126" y="0" type="line"/>
+			<point x="126" y="366" type="line"/>
+			<point x="188" y="430"/>
+			<point x="232" y="462"/>
+			<point x="292" y="462" type="curve" smooth="yes"/>
+			<point x="374" y="462"/>
+			<point x="408" y="410"/>
+			<point x="408" y="304" type="curve" smooth="yes"/>
+			<point x="408" y="0" type="line"/>
+			<point x="438" y="0" type="line"/>
+			<point x="438" y="308" type="line" smooth="yes"/>
+			<point x="438" y="432"/>
+			<point x="392" y="490"/>
+			<point x="294" y="490" type="curve" smooth="yes"/>
+			<point x="228" y="490"/>
+			<point x="178" y="452"/>
+			<point x="128" y="402" type="curve"/>
+			<point x="126" y="402" type="line"/>
+			<point x="122" y="478" type="line"/>
+			<point x="96" y="478" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/space.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/space.glif
new file mode 100644
index 0000000..eea7af4
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/space.glif
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="space" format="1">
+	<unicode hex="0020"/>
+	<advance width="200"/>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/t.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/t.glif
new file mode 100644
index 0000000..1a6d330
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/t.glif
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="t" format="1">
+	<unicode hex="0074"/>
+	<advance width="302"/>
+	<outline>
+		<contour>
+			<point x="218" y="-12" type="curve" smooth="yes"/>
+			<point x="234" y="-12"/>
+			<point x="264" y="-4"/>
+			<point x="292" y="6" type="curve"/>
+			<point x="282" y="32" type="line"/>
+			<point x="264" y="24"/>
+			<point x="238" y="16"/>
+			<point x="220" y="16" type="curve" smooth="yes"/>
+			<point x="150" y="16"/>
+			<point x="136" y="60"/>
+			<point x="136" y="122" type="curve" smooth="yes"/>
+			<point x="136" y="450" type="line"/>
+			<point x="278" y="450" type="line"/>
+			<point x="278" y="478" type="line"/>
+			<point x="136" y="478" type="line"/>
+			<point x="136" y="618" type="line"/>
+			<point x="110" y="618" type="line"/>
+			<point x="106" y="478" type="line"/>
+			<point x="30" y="474" type="line"/>
+			<point x="30" y="450" type="line"/>
+			<point x="106" y="450" type="line"/>
+			<point x="106" y="126" type="line" smooth="yes"/>
+			<point x="106" y="44"/>
+			<point x="130" y="-12"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/tildebelowcmb.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/tildebelowcmb.glif
new file mode 100644
index 0000000..29589ac
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/tildebelowcmb.glif
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="tildebelowcmb" format="1">
+	<unicode hex="0330"/>
+	<outline>
+		<component base="tildecmb" yOffset="-800"/>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/tildecmb.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/tildecmb.glif
new file mode 100644
index 0000000..966bd5c
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/glyphs/tildecmb.glif
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="tildecmb" format="1">
+	<unicode hex="0303"/>
+	<outline>
+		<contour>
+			<point x="77" y="580" type="curve" smooth="yes"/>
+			<point x="140" y="580"/>
+			<point x="156" y="646"/>
+			<point x="160" y="702" type="curve"/>
+			<point x="134" y="704" type="line"/>
+			<point x="132" y="652"/>
+			<point x="116" y="606"/>
+			<point x="79" y="606" type="curve" smooth="yes"/>
+			<point x="20" y="606"/>
+			<point x="0" y="706"/>
+			<point x="-76" y="706" type="curve" smooth="yes"/>
+			<point x="-140" y="706"/>
+			<point x="-156" y="641"/>
+			<point x="-160" y="584" type="curve"/>
+			<point x="-134" y="582" type="line"/>
+			<point x="-132" y="636"/>
+			<point x="-116" y="680"/>
+			<point x="-78" y="680" type="curve" smooth="yes"/>
+			<point x="-20" y="680"/>
+			<point x="0" y="580"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/lib.plist b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/lib.plist
new file mode 100644
index 0000000..54eab2b
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/lib.plist
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.glyphOrder</key>
+	<array>
+		<string>.notdef</string>
+		<string>space</string>
+		<string>A</string>
+		<string>a</string>
+		<string>d</string>
+		<string>f</string>
+		<string>n</string>
+		<string>t</string>
+		<string>f_t</string>
+		<string>a.alt</string>
+		<string>A.sc</string>
+		<string>atilde</string>
+		<string>ampersand</string>
+		<string>circledotted</string>
+		<string>tildecmb</string>
+		<string>dieresiscmb</string>
+		<string>tildebelowcmb</string>
+		<string>dieresisbelowcmb</string>
+	</array>
+	<key>public.postscriptNames</key>
+	<dict>
+		<key>circledotted</key>
+		<string>uni25CC</string>
+		<key>dieresisbelowcmb</key>
+		<string>uni0324</string>
+		<key>dieresiscmb</key>
+		<string>uni0308</string>
+		<key>tildebelowcmb</key>
+		<string>uni0330</string>
+		<key>tildecmb</key>
+		<string>uni0303</string>
+	</dict>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/metainfo.plist b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/metainfo.plist
new file mode 100644
index 0000000..9c654d4
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master0.ufo/metainfo.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>creator</key>
+		<string>org.robofab.ufoLib</string>
+		<key>formatVersion</key>
+		<integer>2</integer>
+	</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/features.fea b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/features.fea
new file mode 100644
index 0000000..487b2ec
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/features.fea
@@ -0,0 +1,54 @@
+table head {
+    FontRevision 2.020;
+} head;
+
+
+table name {
+    nameid 9 "Paul D. Hunt";
+    nameid 9 1 "Paul D. Hunt";
+} name;
+
+
+table hhea {
+    Ascender 984;
+    Descender -273;
+    LineGap 0;
+} hhea;
+
+
+table BASE {
+    HorizAxis.BaseTagList ideo romn;
+    HorizAxis.BaseScriptList
+    latn romn -170 0,
+    grek romn -170 0,
+    cyrl romn -170 0,
+    DFLT romn -170 0;
+} BASE;
+
+
+table OS/2 {
+    Panose 2 11 8 3 3 4 3 2 2 4;
+    XHeight 500;
+    WeightClass 900;
+
+    TypoAscender 750;
+    TypoDescender -250;
+    TypoLineGap 0;
+    winAscent 984;
+    winDescent 273;
+
+    CapHeight 660;
+    WidthClass 5;
+    Vendor "ADBO";
+    FSType 0;
+} OS/2;
+
+
+languagesystem DFLT dflt;
+languagesystem latn dflt;
+
+# GSUB =========================================
+# No merging of GSUB is performed. The variable
+# font will inherit the GSUB table from the
+# base master.
+
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/fontinfo.plist b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/fontinfo.plist
new file mode 100644
index 0000000..43f7e75
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/fontinfo.plist
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>ascender</key>
+		<integer>696</integer>
+		<key>capHeight</key>
+		<integer>650</integer>
+		<key>descender</key>
+		<integer>-176</integer>
+		<key>familyName</key>
+		<string>Test Family 2</string>
+		<key>italicAngle</key>
+		<integer>0</integer>
+		<key>openTypeHheaAscender</key>
+		<integer>984</integer>
+		<key>openTypeHheaDescender</key>
+		<integer>-273</integer>
+		<key>openTypeHheaLineGap</key>
+		<integer>0</integer>
+		<key>openTypeNameDesigner</key>
+		<string>Paul D. Hunt</string>
+		<key>openTypeOS2CodePageRanges</key>
+		<array>
+			<integer>0</integer>
+			<integer>1</integer>
+			<integer>29</integer>
+		</array>
+		<key>openTypeOS2Panose</key>
+		<array>
+			<integer>2</integer>
+			<integer>11</integer>
+			<integer>5</integer>
+			<integer>3</integer>
+			<integer>3</integer>
+			<integer>4</integer>
+			<integer>3</integer>
+			<integer>2</integer>
+			<integer>2</integer>
+			<integer>4</integer>
+		</array>
+		<key>openTypeOS2TypoAscender</key>
+		<integer>750</integer>
+		<key>openTypeOS2TypoDescender</key>
+		<integer>-250</integer>
+		<key>openTypeOS2TypoLineGap</key>
+		<integer>0</integer>
+		<key>openTypeOS2UnicodeRanges</key>
+		<array>
+			<integer>0</integer>
+			<integer>1</integer>
+		</array>
+		<key>openTypeOS2VendorID</key>
+		<string>ADBO</string>
+		<key>openTypeOS2WeightClass</key>
+		<integer>900</integer>
+		<key>openTypeOS2WinAscent</key>
+		<integer>984</integer>
+		<key>openTypeOS2WinDescent</key>
+		<integer>273</integer>
+		<key>postscriptBlueFuzz</key>
+		<integer>0</integer>
+		<key>postscriptBlueScale</key>
+		<real>0.0625</real>
+		<key>postscriptBlueValues</key>
+		<array>
+			<integer>-12</integer>
+			<integer>0</integer>
+			<integer>500</integer>
+			<integer>512</integer>
+			<integer>532</integer>
+			<integer>544</integer>
+			<integer>580</integer>
+			<integer>592</integer>
+			<integer>634</integer>
+			<integer>646</integer>
+			<integer>650</integer>
+			<integer>662</integer>
+			<integer>696</integer>
+			<integer>708</integer>
+		</array>
+		<key>postscriptFamilyBlues</key>
+		<array>
+			<integer>-12</integer>
+			<integer>0</integer>
+			<integer>486</integer>
+			<integer>498</integer>
+			<integer>518</integer>
+			<integer>530</integer>
+			<integer>574</integer>
+			<integer>586</integer>
+			<integer>638</integer>
+			<integer>650</integer>
+			<integer>656</integer>
+			<integer>668</integer>
+			<integer>712</integer>
+			<integer>724</integer>
+		</array>
+		<key>postscriptFamilyOtherBlues</key>
+		<array>
+			<integer>-217</integer>
+			<integer>-205</integer>
+		</array>
+		<key>postscriptFontName</key>
+		<string>TestFamily2-Master1</string>
+		<key>postscriptForceBold</key>
+		<false/>
+		<key>postscriptOtherBlues</key>
+		<array>
+			<integer>-188</integer>
+			<integer>-176</integer>
+		</array>
+		<key>postscriptStemSnapH</key>
+		<array>
+			<integer>134</integer>
+			<integer>144</integer>
+		</array>
+		<key>postscriptStemSnapV</key>
+		<array>
+			<integer>172</integer>
+			<integer>176</integer>
+		</array>
+		<key>postscriptUnderlinePosition</key>
+		<integer>-75</integer>
+		<key>postscriptUnderlineThickness</key>
+		<integer>50</integer>
+		<key>styleMapFamilyName</key>
+		<string>Test Family 2</string>
+		<key>styleName</key>
+		<string>Master 1</string>
+		<key>unitsPerEm</key>
+		<integer>1000</integer>
+		<key>versionMajor</key>
+		<integer>2</integer>
+		<key>versionMinor</key>
+		<integer>20</integer>
+		<key>xHeight</key>
+		<integer>500</integer>
+	</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/A_.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/A_.glif
new file mode 100644
index 0000000..e5afc57
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/A_.glif
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="A" format="1">
+	<unicode hex="0041"/>
+	<advance width="584"/>
+	<outline>
+		<contour>
+			<point x="-10" y="0" type="line"/>
+			<point x="166" y="0" type="line"/>
+			<point x="240" y="316" type="line" smooth="yes"/>
+			<point x="256" y="378"/>
+			<point x="272" y="456"/>
+			<point x="286" y="522" type="curve"/>
+			<point x="290" y="522" type="line"/>
+			<point x="306" y="457"/>
+			<point x="322" y="378"/>
+			<point x="338" y="316" type="curve" smooth="yes"/>
+			<point x="412" y="0" type="line"/>
+			<point x="594" y="0" type="line"/>
+			<point x="396" y="650" type="line"/>
+			<point x="188" y="650" type="line"/>
+		</contour>
+		<contour>
+			<point x="132" y="138" type="line"/>
+			<point x="450" y="138" type="line"/>
+			<point x="450" y="271" type="line"/>
+			<point x="132" y="271" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/A_.sc.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/A_.sc.glif
new file mode 100644
index 0000000..4b4ac31
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/A_.sc.glif
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="A.sc" format="1">
+	<advance width="516"/>
+	<outline>
+		<contour>
+			<point x="-10" y="0" type="line"/>
+			<point x="164" y="0" type="line"/>
+			<point x="219" y="244" type="line" smooth="yes"/>
+			<point x="230" y="292"/>
+			<point x="241" y="358"/>
+			<point x="252" y="409" type="curve"/>
+			<point x="256" y="409" type="line"/>
+			<point x="269" y="359"/>
+			<point x="280" y="292"/>
+			<point x="291" y="244" type="curve" smooth="yes"/>
+			<point x="346" y="0" type="line"/>
+			<point x="526" y="0" type="line"/>
+			<point x="361" y="532" type="line"/>
+			<point x="155" y="532" type="line"/>
+		</contour>
+		<contour>
+			<point x="118" y="94" type="line"/>
+			<point x="397" y="94" type="line"/>
+			<point x="397" y="216" type="line"/>
+			<point x="118" y="216" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/_notdef.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/_notdef.glif
new file mode 100644
index 0000000..930ed6d
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/_notdef.glif
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name=".notdef" format="1">
+	<advance width="704"/>
+	<outline>
+		<contour>
+			<point x="76" y="0" type="line"/>
+			<point x="628" y="0" type="line"/>
+			<point x="628" y="660" type="line"/>
+			<point x="76" y="660" type="line"/>
+		</contour>
+		<contour>
+			<point x="288" y="104" type="line"/>
+			<point x="314" y="160" type="line"/>
+			<point x="350" y="256" type="line"/>
+			<point x="354" y="256" type="line"/>
+			<point x="390" y="160" type="line"/>
+			<point x="416" y="104" type="line"/>
+		</contour>
+		<contour>
+			<point x="350" y="424" type="line"/>
+			<point x="310" y="520" type="line"/>
+			<point x="292" y="556" type="line"/>
+			<point x="412" y="556" type="line"/>
+			<point x="394" y="520" type="line"/>
+			<point x="354" y="424" type="line"/>
+		</contour>
+		<contour>
+			<point x="188" y="172" type="line"/>
+			<point x="188" y="508" type="line"/>
+			<point x="270" y="340" type="line"/>
+		</contour>
+		<contour>
+			<point x="516" y="172" type="line"/>
+			<point x="434" y="340" type="line"/>
+			<point x="516" y="508" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/a.alt.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/a.alt.glif
new file mode 100644
index 0000000..db719a0
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/a.alt.glif
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="a.alt" format="1">
+	<advance width="580"/>
+	<outline>
+		<contour>
+			<point x="240" y="-12" type="curve" smooth="yes"/>
+			<point x="284" y="-12"/>
+			<point x="332" y="12"/>
+			<point x="366" y="46" type="curve"/>
+			<point x="370" y="46" type="line"/>
+			<point x="382" y="0" type="line"/>
+			<point x="522" y="0" type="line"/>
+			<point x="522" y="500" type="line"/>
+			<point x="388" y="500" type="line"/>
+			<point x="374" y="450" type="line"/>
+			<point x="370" y="450" type="line"/>
+			<point x="332" y="494"/>
+			<point x="292" y="512"/>
+			<point x="244" y="512" type="curve" smooth="yes"/>
+			<point x="142" y="512"/>
+			<point x="36" y="414"/>
+			<point x="36" y="250" type="curve" smooth="yes"/>
+			<point x="36" y="88"/>
+			<point x="116" y="-12"/>
+		</contour>
+		<contour>
+			<point x="286" y="128" type="curve" smooth="yes"/>
+			<point x="240" y="128"/>
+			<point x="212" y="162"/>
+			<point x="212" y="252" type="curve" smooth="yes"/>
+			<point x="212" y="340"/>
+			<point x="246" y="372"/>
+			<point x="282" y="372" type="curve" smooth="yes"/>
+			<point x="304" y="372"/>
+			<point x="330" y="366"/>
+			<point x="350" y="348" type="curve"/>
+			<point x="350" y="164" type="line"/>
+			<point x="332" y="136"/>
+			<point x="312" y="128"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/a.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/a.glif
new file mode 100644
index 0000000..e49d9ac
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/a.glif
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="a" format="1">
+	<unicode hex="0061"/>
+	<advance width="536"/>
+	<outline>
+		<contour>
+			<point x="188" y="-12" type="curve" smooth="yes"/>
+			<point x="242" y="-12"/>
+			<point x="286" y="12"/>
+			<point x="326" y="48" type="curve"/>
+			<point x="330" y="48" type="line"/>
+			<point x="342" y="0" type="line"/>
+			<point x="482" y="0" type="line"/>
+			<point x="482" y="278" type="line" smooth="yes"/>
+			<point x="482" y="442"/>
+			<point x="404" y="512"/>
+			<point x="274" y="512" type="curve" smooth="yes"/>
+			<point x="196" y="512"/>
+			<point x="124" y="488"/>
+			<point x="54" y="446" type="curve"/>
+			<point x="114" y="334" type="line"/>
+			<point x="166" y="362"/>
+			<point x="204" y="376"/>
+			<point x="240" y="376" type="curve" smooth="yes"/>
+			<point x="284" y="376"/>
+			<point x="306" y="360"/>
+			<point x="310" y="324" type="curve"/>
+			<point x="118" y="304"/>
+			<point x="38" y="246"/>
+			<point x="38" y="142" type="curve" smooth="yes"/>
+			<point x="38" y="60"/>
+			<point x="94" y="-12"/>
+		</contour>
+		<contour>
+			<point x="248" y="120" type="curve" smooth="yes"/>
+			<point x="218" y="120"/>
+			<point x="202" y="133"/>
+			<point x="202" y="156" type="curve" smooth="yes"/>
+			<point x="202" y="184"/>
+			<point x="228" y="210"/>
+			<point x="310" y="222" type="curve"/>
+			<point x="310" y="154" type="line"/>
+			<point x="292" y="134"/>
+			<point x="276" y="120"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/ampersand.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/ampersand.glif
new file mode 100644
index 0000000..622075f
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/ampersand.glif
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="ampersand" format="1">
+	<unicode hex="0026"/>
+	<advance width="690"/>
+	<outline>
+		<contour>
+			<point x="246" y="-12" type="curve" smooth="yes"/>
+			<point x="362" y="-12"/>
+			<point x="452" y="34"/>
+			<point x="516" y="104" type="curve" smooth="yes"/>
+			<point x="590" y="187"/>
+			<point x="638" y="276"/>
+			<point x="668" y="374" type="curve"/>
+			<point x="512" y="374" type="line"/>
+			<point x="490" y="292"/>
+			<point x="448" y="228"/>
+			<point x="398" y="180" type="curve" smooth="yes"/>
+			<point x="356" y="142"/>
+			<point x="310" y="118"/>
+			<point x="268" y="118" type="curve" smooth="yes"/>
+			<point x="216" y="118"/>
+			<point x="184" y="146"/>
+			<point x="184" y="186" type="curve" smooth="yes"/>
+			<point x="184" y="296"/>
+			<point x="458" y="332"/>
+			<point x="458" y="508" type="curve" smooth="yes"/>
+			<point x="458" y="602"/>
+			<point x="390" y="662"/>
+			<point x="286" y="662" type="curve" smooth="yes"/>
+			<point x="170" y="662"/>
+			<point x="98" y="580"/>
+			<point x="98" y="486" type="curve" smooth="yes"/>
+			<point x="98" y="359"/>
+			<point x="244" y="182"/>
+			<point x="415" y="75" type="curve" smooth="yes"/>
+			<point x="485" y="31"/>
+			<point x="560" y="0"/>
+			<point x="630" y="-12" type="curve"/>
+			<point x="670" y="126" type="line"/>
+			<point x="627" y="131"/>
+			<point x="573" y="153"/>
+			<point x="518" y="183" type="curve" smooth="yes"/>
+			<point x="382" y="258"/>
+			<point x="239" y="390"/>
+			<point x="239" y="486" type="curve" smooth="yes"/>
+			<point x="239" y="528"/>
+			<point x="263" y="550"/>
+			<point x="290" y="550" type="curve" smooth="yes"/>
+			<point x="315" y="550"/>
+			<point x="328" y="536"/>
+			<point x="328" y="508" type="curve" smooth="yes"/>
+			<point x="328" y="386"/>
+			<point x="22" y="396"/>
+			<point x="22" y="176" type="curve" smooth="yes"/>
+			<point x="22" y="78"/>
+			<point x="95" y="-12"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/atilde.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/atilde.glif
new file mode 100644
index 0000000..b53079d
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/atilde.glif
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="atilde" format="1">
+	<unicode hex="00E3"/>
+	<advance width="536"/>
+	<outline>
+		<component base="a"/>
+		<component base="tildecmb" xOffset="266"/>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/circledotted.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/circledotted.glif
new file mode 100644
index 0000000..9a24018
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/circledotted.glif
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="circledotted" format="1">
+	<unicode hex="25CC"/>
+	<advance width="574"/>
+	<outline>
+		<contour>
+			<point x="104" y="96" type="curve" smooth="yes"/>
+			<point x="131" y="96"/>
+			<point x="149" y="112"/>
+			<point x="149" y="141" type="curve" smooth="yes"/>
+			<point x="149" y="170"/>
+			<point x="130" y="187"/>
+			<point x="104" y="187" type="curve" smooth="yes"/>
+			<point x="82" y="187"/>
+			<point x="61" y="170"/>
+			<point x="61" y="141" type="curve" smooth="yes"/>
+			<point x="61" y="112"/>
+			<point x="82" y="96"/>
+		</contour>
+		<contour>
+			<point x="76" y="204" type="curve" smooth="yes"/>
+			<point x="104" y="204"/>
+			<point x="122" y="221"/>
+			<point x="122" y="251" type="curve" smooth="yes"/>
+			<point x="122" y="279"/>
+			<point x="102" y="295"/>
+			<point x="76" y="295" type="curve" smooth="yes"/>
+			<point x="53" y="295"/>
+			<point x="32" y="279"/>
+			<point x="32" y="251" type="curve" smooth="yes"/>
+			<point x="32" y="221"/>
+			<point x="53" y="204"/>
+		</contour>
+		<contour>
+			<point x="104" y="313" type="curve" smooth="yes"/>
+			<point x="131" y="313"/>
+			<point x="149" y="331"/>
+			<point x="149" y="360" type="curve" smooth="yes"/>
+			<point x="149" y="390"/>
+			<point x="130" y="405"/>
+			<point x="104" y="405" type="curve" smooth="yes"/>
+			<point x="82" y="405"/>
+			<point x="61" y="390"/>
+			<point x="61" y="360" type="curve" smooth="yes"/>
+			<point x="61" y="331"/>
+			<point x="82" y="313"/>
+		</contour>
+		<contour>
+			<point x="182" y="14" type="curve" smooth="yes"/>
+			<point x="208" y="14"/>
+			<point x="227" y="31"/>
+			<point x="227" y="61" type="curve" smooth="yes"/>
+			<point x="227" y="89"/>
+			<point x="206" y="105"/>
+			<point x="182" y="105" type="curve" smooth="yes"/>
+			<point x="158" y="105"/>
+			<point x="137" y="89"/>
+			<point x="137" y="61" type="curve" smooth="yes"/>
+			<point x="137" y="31"/>
+			<point x="158" y="14"/>
+		</contour>
+		<contour>
+			<point x="182" y="395" type="curve" smooth="yes"/>
+			<point x="208" y="395"/>
+			<point x="227" y="412"/>
+			<point x="227" y="440" type="curve" smooth="yes"/>
+			<point x="227" y="470"/>
+			<point x="206" y="486"/>
+			<point x="182" y="486" type="curve" smooth="yes"/>
+			<point x="158" y="486"/>
+			<point x="137" y="470"/>
+			<point x="137" y="440" type="curve" smooth="yes"/>
+			<point x="137" y="412"/>
+			<point x="158" y="395"/>
+		</contour>
+		<contour>
+			<point x="287" y="-13" type="curve" smooth="yes"/>
+			<point x="314" y="-13"/>
+			<point x="332" y="4"/>
+			<point x="332" y="34" type="curve" smooth="yes"/>
+			<point x="332" y="62"/>
+			<point x="313" y="78"/>
+			<point x="287" y="78" type="curve" smooth="yes"/>
+			<point x="264" y="78"/>
+			<point x="244" y="62"/>
+			<point x="244" y="34" type="curve" smooth="yes"/>
+			<point x="244" y="4"/>
+			<point x="264" y="-13"/>
+		</contour>
+		<contour>
+			<point x="287" y="421" type="curve" smooth="yes"/>
+			<point x="314" y="421"/>
+			<point x="332" y="439"/>
+			<point x="332" y="468" type="curve" smooth="yes"/>
+			<point x="332" y="496"/>
+			<point x="313" y="512"/>
+			<point x="287" y="512" type="curve" smooth="yes"/>
+			<point x="264" y="512"/>
+			<point x="244" y="496"/>
+			<point x="244" y="468" type="curve" smooth="yes"/>
+			<point x="244" y="439"/>
+			<point x="264" y="421"/>
+		</contour>
+		<contour>
+			<point x="392" y="14" type="curve" smooth="yes"/>
+			<point x="420" y="14"/>
+			<point x="438" y="31"/>
+			<point x="438" y="61" type="curve" smooth="yes"/>
+			<point x="438" y="89"/>
+			<point x="417" y="105"/>
+			<point x="392" y="105" type="curve" smooth="yes"/>
+			<point x="369" y="105"/>
+			<point x="348" y="89"/>
+			<point x="348" y="61" type="curve" smooth="yes"/>
+			<point x="348" y="31"/>
+			<point x="369" y="14"/>
+		</contour>
+		<contour>
+			<point x="392" y="394" type="curve" smooth="yes"/>
+			<point x="420" y="394"/>
+			<point x="438" y="411"/>
+			<point x="438" y="440" type="curve" smooth="yes"/>
+			<point x="438" y="469"/>
+			<point x="417" y="486"/>
+			<point x="392" y="486" type="curve" smooth="yes"/>
+			<point x="369" y="486"/>
+			<point x="348" y="469"/>
+			<point x="348" y="440" type="curve" smooth="yes"/>
+			<point x="348" y="411"/>
+			<point x="369" y="394"/>
+		</contour>
+		<contour>
+			<point x="472" y="96" type="curve" smooth="yes"/>
+			<point x="498" y="96"/>
+			<point x="516" y="112"/>
+			<point x="516" y="141" type="curve" smooth="yes"/>
+			<point x="516" y="170"/>
+			<point x="496" y="187"/>
+			<point x="472" y="187" type="curve" smooth="yes"/>
+			<point x="447" y="187"/>
+			<point x="426" y="170"/>
+			<point x="426" y="141" type="curve" smooth="yes"/>
+			<point x="426" y="112"/>
+			<point x="447" y="96"/>
+		</contour>
+		<contour>
+			<point x="498" y="204" type="curve" smooth="yes"/>
+			<point x="524" y="204"/>
+			<point x="543" y="221"/>
+			<point x="543" y="251" type="curve" smooth="yes"/>
+			<point x="543" y="279"/>
+			<point x="522" y="295"/>
+			<point x="498" y="295" type="curve" smooth="yes"/>
+			<point x="473" y="295"/>
+			<point x="453" y="279"/>
+			<point x="453" y="251" type="curve" smooth="yes"/>
+			<point x="453" y="221"/>
+			<point x="473" y="204"/>
+		</contour>
+		<contour>
+			<point x="472" y="313" type="curve" smooth="yes"/>
+			<point x="498" y="313"/>
+			<point x="516" y="330"/>
+			<point x="516" y="359" type="curve" smooth="yes"/>
+			<point x="516" y="388"/>
+			<point x="496" y="404"/>
+			<point x="472" y="404" type="curve" smooth="yes"/>
+			<point x="447" y="404"/>
+			<point x="426" y="388"/>
+			<point x="426" y="359" type="curve" smooth="yes"/>
+			<point x="426" y="330"/>
+			<point x="447" y="313"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/contents.plist b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/contents.plist
new file mode 100644
index 0000000..2c5ee6b
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/contents.plist
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>.notdef</key>
+		<string>_notdef.glif</string>
+		<key>A</key>
+		<string>A_.glif</string>
+		<key>A.sc</key>
+		<string>A_.sc.glif</string>
+		<key>a</key>
+		<string>a.glif</string>
+		<key>a.alt</key>
+		<string>a.alt.glif</string>
+		<key>ampersand</key>
+		<string>ampersand.glif</string>
+		<key>atilde</key>
+		<string>atilde.glif</string>
+		<key>circledotted</key>
+		<string>circledotted.glif</string>
+		<key>d</key>
+		<string>d.glif</string>
+		<key>dieresisbelowcmb</key>
+		<string>dieresisbelowcmb.glif</string>
+		<key>dieresiscmb</key>
+		<string>dieresiscmb.glif</string>
+		<key>f</key>
+		<string>f.glif</string>
+		<key>f_t</key>
+		<string>f_t.glif</string>
+		<key>n</key>
+		<string>n.glif</string>
+		<key>space</key>
+		<string>space.glif</string>
+		<key>t</key>
+		<string>t.glif</string>
+		<key>tildebelowcmb</key>
+		<string>tildebelowcmb.glif</string>
+		<key>tildecmb</key>
+		<string>tildecmb.glif</string>
+	</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/d.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/d.glif
new file mode 100644
index 0000000..9caa625
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/d.glif
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="d" format="1">
+	<unicode hex="0064"/>
+	<advance width="580"/>
+	<outline>
+		<contour>
+			<point x="240" y="-12" type="curve" smooth="yes"/>
+			<point x="284" y="-12"/>
+			<point x="332" y="12"/>
+			<point x="366" y="46" type="curve"/>
+			<point x="370" y="46" type="line"/>
+			<point x="382" y="0" type="line"/>
+			<point x="522" y="0" type="line"/>
+			<point x="522" y="696" type="line"/>
+			<point x="350" y="696" type="line"/>
+			<point x="350" y="534" type="line"/>
+			<point x="356" y="462" type="line"/>
+			<point x="326" y="492"/>
+			<point x="294" y="512"/>
+			<point x="240" y="512" type="curve" smooth="yes"/>
+			<point x="138" y="512"/>
+			<point x="36" y="414"/>
+			<point x="36" y="250" type="curve" smooth="yes"/>
+			<point x="36" y="88"/>
+			<point x="116" y="-12"/>
+		</contour>
+		<contour>
+			<point x="286" y="128" type="curve" smooth="yes"/>
+			<point x="240" y="128"/>
+			<point x="212" y="162"/>
+			<point x="212" y="252" type="curve" smooth="yes"/>
+			<point x="212" y="340"/>
+			<point x="246" y="372"/>
+			<point x="282" y="372" type="curve" smooth="yes"/>
+			<point x="304" y="372"/>
+			<point x="330" y="366"/>
+			<point x="350" y="348" type="curve"/>
+			<point x="350" y="164" type="line"/>
+			<point x="332" y="136"/>
+			<point x="312" y="128"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/dieresisbelowcmb.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/dieresisbelowcmb.glif
new file mode 100644
index 0000000..a8b69d5
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/dieresisbelowcmb.glif
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="dieresisbelowcmb" format="1">
+	<unicode hex="0324"/>
+	<outline>
+		<component base="dieresiscmb" yOffset="-786"/>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/dieresiscmb.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/dieresiscmb.glif
new file mode 100644
index 0000000..8c78d78
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/dieresiscmb.glif
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="dieresiscmb" format="1">
+	<unicode hex="0308"/>
+	<outline>
+		<contour>
+			<point x="-114" y="562" type="curve" smooth="yes"/>
+			<point x="-67" y="562"/>
+			<point x="-34" y="597"/>
+			<point x="-34" y="642" type="curve" smooth="yes"/>
+			<point x="-34" y="687"/>
+			<point x="-67" y="722"/>
+			<point x="-114" y="722" type="curve" smooth="yes"/>
+			<point x="-161" y="722"/>
+			<point x="-194" y="687"/>
+			<point x="-194" y="642" type="curve" smooth="yes"/>
+			<point x="-194" y="597"/>
+			<point x="-161" y="562"/>
+		</contour>
+		<contour>
+			<point x="114" y="562" type="curve" smooth="yes"/>
+			<point x="161" y="562"/>
+			<point x="194" y="597"/>
+			<point x="194" y="642" type="curve" smooth="yes"/>
+			<point x="194" y="687"/>
+			<point x="161" y="722"/>
+			<point x="114" y="722" type="curve" smooth="yes"/>
+			<point x="67" y="722"/>
+			<point x="34" y="687"/>
+			<point x="34" y="642" type="curve" smooth="yes"/>
+			<point x="34" y="597"/>
+			<point x="67" y="562"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/f.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/f.glif
new file mode 100644
index 0000000..35765a2
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/f.glif
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="f" format="1">
+	<unicode hex="0066"/>
+	<advance width="360"/>
+	<outline>
+		<contour>
+			<point x="88" y="0" type="line"/>
+			<point x="260" y="0" type="line"/>
+			<point x="260" y="512" type="line" smooth="yes"/>
+			<point x="260" y="559"/>
+			<point x="280" y="574"/>
+			<point x="312" y="574" type="curve" smooth="yes"/>
+			<point x="328" y="574"/>
+			<point x="346" y="570"/>
+			<point x="362" y="564" type="curve"/>
+			<point x="392" y="690" type="line"/>
+			<point x="370" y="698"/>
+			<point x="332" y="708"/>
+			<point x="286" y="708" type="curve" smooth="yes"/>
+			<point x="138" y="708"/>
+			<point x="88" y="613"/>
+			<point x="88" y="506" type="curve" smooth="yes"/>
+		</contour>
+		<contour>
+			<point x="22" y="366" type="line"/>
+			<point x="344" y="366" type="line"/>
+			<point x="344" y="500" type="line"/>
+			<point x="98" y="500" type="line"/>
+			<point x="22" y="494" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/f_t.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/f_t.glif
new file mode 100644
index 0000000..58378a8
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/f_t.glif
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="f_t" format="1">
+	<advance width="724"/>
+	<outline>
+		<contour>
+			<point x="88" y="0" type="line"/>
+			<point x="260" y="0" type="line"/>
+			<point x="260" y="512" type="line" smooth="yes"/>
+			<point x="260" y="559"/>
+			<point x="280" y="574"/>
+			<point x="312" y="574" type="curve" smooth="yes"/>
+			<point x="328" y="574"/>
+			<point x="346" y="570"/>
+			<point x="362" y="564" type="curve"/>
+			<point x="392" y="690" type="line"/>
+			<point x="370" y="698"/>
+			<point x="332" y="708"/>
+			<point x="286" y="708" type="curve" smooth="yes"/>
+			<point x="138" y="708"/>
+			<point x="88" y="613"/>
+			<point x="88" y="506" type="curve" smooth="yes"/>
+		</contour>
+		<contour>
+			<point x="588" y="-12" type="curve" smooth="yes"/>
+			<point x="643" y="-12"/>
+			<point x="680" y="-2"/>
+			<point x="706" y="6" type="curve"/>
+			<point x="680" y="130" type="line"/>
+			<point x="668" y="126"/>
+			<point x="652" y="122"/>
+			<point x="636" y="122" type="curve" smooth="yes"/>
+			<point x="604" y="122"/>
+			<point x="576" y="140"/>
+			<point x="576" y="195" type="curve"/>
+			<point x="576" y="366" type="line"/>
+			<point x="690" y="366" type="line"/>
+			<point x="690" y="500" type="line"/>
+			<point x="576" y="500" type="line"/>
+			<point x="576" y="630" type="line"/>
+			<point x="434" y="630" type="line"/>
+			<point x="414" y="500" type="line"/>
+			<point x="98" y="500" type="line"/>
+			<point x="22" y="494" type="line"/>
+			<point x="22" y="366" type="line"/>
+			<point x="404" y="366" type="line"/>
+			<point x="404" y="192" type="line" smooth="yes"/>
+			<point x="404" y="70"/>
+			<point x="458" y="-12"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/n.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/n.glif
new file mode 100644
index 0000000..ccf3277
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/n.glif
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="n" format="1">
+	<unicode hex="006E"/>
+	<advance width="582"/>
+	<outline>
+		<contour>
+			<point x="58" y="0" type="line"/>
+			<point x="230" y="0" type="line"/>
+			<point x="230" y="328" type="line"/>
+			<point x="256" y="352"/>
+			<point x="274" y="366"/>
+			<point x="306" y="366" type="curve" smooth="yes"/>
+			<point x="340" y="366"/>
+			<point x="356" y="350"/>
+			<point x="356" y="286" type="curve" smooth="yes"/>
+			<point x="356" y="0" type="line"/>
+			<point x="528" y="0" type="line"/>
+			<point x="528" y="308" type="line" smooth="yes"/>
+			<point x="528" y="432"/>
+			<point x="482" y="512"/>
+			<point x="372" y="512" type="curve" smooth="yes"/>
+			<point x="304" y="512"/>
+			<point x="254" y="478"/>
+			<point x="214" y="440" type="curve"/>
+			<point x="210" y="440" type="line"/>
+			<point x="198" y="500" type="line"/>
+			<point x="58" y="500" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/space.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/space.glif
new file mode 100644
index 0000000..eea7af4
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/space.glif
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="space" format="1">
+	<unicode hex="0020"/>
+	<advance width="200"/>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/t.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/t.glif
new file mode 100644
index 0000000..e5c75c2
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/t.glif
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="t" format="1">
+	<unicode hex="0074"/>
+	<advance width="400"/>
+	<outline>
+		<contour>
+			<point x="264" y="-12" type="curve" smooth="yes"/>
+			<point x="319" y="-12"/>
+			<point x="356" y="-2"/>
+			<point x="382" y="6" type="curve"/>
+			<point x="356" y="130" type="line"/>
+			<point x="344" y="126"/>
+			<point x="328" y="122"/>
+			<point x="312" y="122" type="curve" smooth="yes"/>
+			<point x="280" y="122"/>
+			<point x="252" y="140"/>
+			<point x="252" y="195" type="curve" smooth="yes"/>
+			<point x="252" y="366" type="line"/>
+			<point x="366" y="366" type="line"/>
+			<point x="366" y="500" type="line"/>
+			<point x="252" y="500" type="line"/>
+			<point x="252" y="630" type="line"/>
+			<point x="110" y="630" type="line"/>
+			<point x="90" y="500" type="line"/>
+			<point x="14" y="494" type="line"/>
+			<point x="14" y="366" type="line"/>
+			<point x="80" y="366" type="line"/>
+			<point x="80" y="192" type="line" smooth="yes"/>
+			<point x="80" y="70"/>
+			<point x="134" y="-12"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/tildebelowcmb.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/tildebelowcmb.glif
new file mode 100644
index 0000000..29589ac
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/tildebelowcmb.glif
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="tildebelowcmb" format="1">
+	<unicode hex="0330"/>
+	<outline>
+		<component base="tildecmb" yOffset="-800"/>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/tildecmb.glif b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/tildecmb.glif
new file mode 100644
index 0000000..e5d36b2
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/glyphs/tildecmb.glif
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="tildecmb" format="1">
+	<unicode hex="0303"/>
+	<outline>
+		<contour>
+			<point x="64" y="572" type="curve" smooth="yes"/>
+			<point x="144" y="572"/>
+			<point x="194" y="617"/>
+			<point x="196" y="730" type="curve"/>
+			<point x="90" y="736" type="line"/>
+			<point x="86" y="700"/>
+			<point x="76" y="690"/>
+			<point x="60" y="690" type="curve" smooth="yes"/>
+			<point x="34" y="690"/>
+			<point x="-4" y="746"/>
+			<point x="-64" y="746" type="curve" smooth="yes"/>
+			<point x="-144" y="746"/>
+			<point x="-194" y="701"/>
+			<point x="-196" y="588" type="curve"/>
+			<point x="-90" y="582" type="line"/>
+			<point x="-86" y="618"/>
+			<point x="-76" y="628"/>
+			<point x="-60" y="628" type="curve" smooth="yes"/>
+			<point x="-34" y="628"/>
+			<point x="4" y="572"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/lib.plist b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/lib.plist
new file mode 100644
index 0000000..54eab2b
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/lib.plist
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.glyphOrder</key>
+	<array>
+		<string>.notdef</string>
+		<string>space</string>
+		<string>A</string>
+		<string>a</string>
+		<string>d</string>
+		<string>f</string>
+		<string>n</string>
+		<string>t</string>
+		<string>f_t</string>
+		<string>a.alt</string>
+		<string>A.sc</string>
+		<string>atilde</string>
+		<string>ampersand</string>
+		<string>circledotted</string>
+		<string>tildecmb</string>
+		<string>dieresiscmb</string>
+		<string>tildebelowcmb</string>
+		<string>dieresisbelowcmb</string>
+	</array>
+	<key>public.postscriptNames</key>
+	<dict>
+		<key>circledotted</key>
+		<string>uni25CC</string>
+		<key>dieresisbelowcmb</key>
+		<string>uni0324</string>
+		<key>dieresiscmb</key>
+		<string>uni0308</string>
+		<key>tildebelowcmb</key>
+		<string>uni0330</string>
+		<key>tildecmb</key>
+		<string>uni0303</string>
+	</dict>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/metainfo.plist b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/metainfo.plist
new file mode 100644
index 0000000..9c654d4
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily2-Master1.ufo/metainfo.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>creator</key>
+		<string>org.robofab.ufoLib</string>
+		<key>formatVersion</key>
+		<integer>2</integer>
+	</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/fontinfo.plist b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/fontinfo.plist
new file mode 100644
index 0000000..003488d
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/fontinfo.plist
@@ -0,0 +1,153 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>ascender</key>
+	<integer>760</integer>
+	<key>capHeight</key>
+	<integer>714</integer>
+	<key>copyright</key>
+	<string>Copyright 2015 Google Inc. All Rights Reserved.</string>
+	<key>descender</key>
+	<integer>-240</integer>
+	<key>familyName</key>
+	<string>Test Family 3</string>
+	<key>guidelines</key>
+	<array/>
+	<key>openTypeHeadCreated</key>
+	<string>2016/03/15 19:50:39</string>
+	<key>openTypeHheaAscender</key>
+	<integer>1069</integer>
+	<key>openTypeHheaDescender</key>
+	<integer>-293</integer>
+	<key>openTypeHheaLineGap</key>
+	<integer>0</integer>
+	<key>openTypeNameDescription</key>
+	<string>Designed by Monotype design team.</string>
+	<key>openTypeNameDesigner</key>
+	<string>Monotype Design Team</string>
+	<key>openTypeNameDesignerURL</key>
+	<string>http://www.monotype.com/studio</string>
+	<key>openTypeNameLicense</key>
+	<string>This Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License for the specific language, permissions and limitations governing your use of this Font Software.</string>
+	<key>openTypeNameLicenseURL</key>
+	<string>http://scripts.sil.org/OFL</string>
+	<key>openTypeNameManufacturer</key>
+	<string>Monotype Imaging Inc.</string>
+	<key>openTypeNameManufacturerURL</key>
+	<string>http://www.google.com/get/noto/</string>
+	<key>openTypeNameVersion</key>
+	<string>Version 1.902</string>
+	<key>openTypeOS2Panose</key>
+	<array>
+		<integer>2</integer>
+		<integer>11</integer>
+		<integer>10</integer>
+		<integer>2</integer>
+		<integer>4</integer>
+		<integer>5</integer>
+		<integer>4</integer>
+		<integer>2</integer>
+		<integer>2</integer>
+		<integer>4</integer>
+	</array>
+	<key>openTypeOS2Selection</key>
+	<array>
+		<integer>8</integer>
+	</array>
+	<key>openTypeOS2Type</key>
+	<array/>
+	<key>openTypeOS2TypoAscender</key>
+	<integer>1069</integer>
+	<key>openTypeOS2TypoDescender</key>
+	<integer>-293</integer>
+	<key>openTypeOS2TypoLineGap</key>
+	<integer>0</integer>
+	<key>openTypeOS2UnicodeRanges</key>
+	<array>
+		<integer>0</integer>
+		<integer>1</integer>
+		<integer>2</integer>
+		<integer>3</integer>
+		<integer>4</integer>
+		<integer>5</integer>
+		<integer>6</integer>
+		<integer>7</integer>
+		<integer>9</integer>
+		<integer>29</integer>
+		<integer>30</integer>
+		<integer>31</integer>
+		<integer>32</integer>
+		<integer>33</integer>
+		<integer>34</integer>
+		<integer>35</integer>
+		<integer>36</integer>
+		<integer>62</integer>
+		<integer>64</integer>
+		<integer>67</integer>
+		<integer>69</integer>
+		<integer>91</integer>
+		<integer>116</integer>
+	</array>
+	<key>openTypeOS2VendorID</key>
+	<string>GOOG</string>
+	<key>openTypeOS2WinAscent</key>
+	<integer>1069</integer>
+	<key>openTypeOS2WinDescent</key>
+	<integer>293</integer>
+	<key>postscriptBlueValues</key>
+	<array>
+		<integer>-15</integer>
+		<integer>0</integer>
+		<integer>553</integer>
+		<integer>563</integer>
+		<integer>591</integer>
+		<integer>601</integer>
+		<integer>714</integer>
+		<integer>725</integer>
+		<integer>760</integer>
+		<integer>766</integer>
+	</array>
+	<key>postscriptFamilyBlues</key>
+	<array/>
+	<key>postscriptFamilyOtherBlues</key>
+	<array/>
+	<key>postscriptOtherBlues</key>
+	<array>
+		<integer>-256</integer>
+		<integer>-240</integer>
+	</array>
+	<key>postscriptStemSnapH</key>
+	<array>
+		<integer>115</integer>
+		<integer>143</integer>
+		<integer>158</integer>
+	</array>
+	<key>postscriptStemSnapV</key>
+	<array>
+		<integer>132</integer>
+		<integer>176</integer>
+		<integer>194</integer>
+	</array>
+	<key>postscriptUnderlinePosition</key>
+	<integer>-100</integer>
+	<key>postscriptUnderlineThickness</key>
+	<integer>50</integer>
+	<key>styleMapFamilyName</key>
+	<string>Test Family 3</string>
+	<key>styleMapStyleName</key>
+	<string>bold</string>
+	<key>styleName</key>
+	<string>Bold</string>
+	<key>trademark</key>
+	<string>Noto is a trademark of Google Inc.</string>
+	<key>unitsPerEm</key>
+	<integer>1000</integer>
+	<key>versionMajor</key>
+	<integer>1</integer>
+	<key>versionMinor</key>
+	<integer>902</integer>
+	<key>xHeight</key>
+	<integer>553</integer>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/F_.glif b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/F_.glif
new file mode 100644
index 0000000..11b34cc
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/F_.glif
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="F" format="2">
+  <advance width="539"/>
+  <unicode hex="0046"/>
+  <anchor x="270" y="0" name="bottom"/>
+  <anchor x="288" y="714" name="top"/>
+  <anchor x="519" y="714" name="topright"/>
+  <outline>
+    <contour>
+      <point x="267" y="0" type="line"/>
+      <point x="267" y="268" type="line"/>
+      <point x="481" y="268" type="line"/>
+      <point x="481" y="423" type="line"/>
+      <point x="267" y="423" type="line"/>
+      <point x="267" y="559" type="line"/>
+      <point x="499" y="559" type="line"/>
+      <point x="499" y="714" type="line"/>
+      <point x="77" y="714" type="line"/>
+      <point x="77" y="0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/T_.glif b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/T_.glif
new file mode 100644
index 0000000..80de0d1
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/T_.glif
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="T" format="2">
+  <advance width="591"/>
+  <unicode hex="0054"/>
+  <anchor x="296" y="0" name="bottom"/>
+  <anchor x="296" y="357" name="center"/>
+  <anchor x="296" y="714" name="top"/>
+  <anchor x="571" y="714" name="topright"/>
+  <outline>
+    <contour>
+      <point x="392" y="0" type="line"/>
+      <point x="392" y="556" type="line"/>
+      <point x="566" y="556" type="line"/>
+      <point x="566" y="714" type="line"/>
+      <point x="25" y="714" type="line"/>
+      <point x="25" y="556" type="line"/>
+      <point x="199" y="556" type="line"/>
+      <point x="199" y="0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/contents.plist b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/contents.plist
new file mode 100644
index 0000000..d50a055
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/contents.plist
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>F</key>
+	<string>F_.glif</string>
+	<key>T</key>
+	<string>T_.glif</string>
+	<key>l</key>
+	<string>l.glif</string>
+	<key>n</key>
+	<string>n.glif</string>
+	<key>o</key>
+	<string>o.glif</string>
+	<key>s</key>
+	<string>s.glif</string>
+	<key>t</key>
+	<string>t.glif</string>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/l.glif b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/l.glif
new file mode 100644
index 0000000..7c63850
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/l.glif
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="l" format="2">
+  <advance width="323"/>
+  <unicode hex="006C"/>
+  <anchor x="162" y="0" name="bottom"/>
+  <anchor x="162" y="277" name="center"/>
+  <anchor x="162" y="760" name="top"/>
+  <anchor x="278" y="760" name="topright"/>
+  <outline>
+    <contour>
+      <point x="257" y="0" type="line"/>
+      <point x="257" y="760" type="line"/>
+      <point x="66" y="760" type="line"/>
+      <point x="66" y="0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/layerinfo.plist b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/layerinfo.plist
new file mode 100644
index 0000000..0c67376
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/layerinfo.plist
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict/>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/n.glif b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/n.glif
new file mode 100644
index 0000000..cdce9c2
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/n.glif
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="n" format="2">
+  <advance width="670"/>
+  <unicode hex="006E"/>
+  <anchor x="335" y="0" name="bottom"/>
+  <anchor x="335" y="553" name="top"/>
+  <anchor x="636" y="553" name="topright"/>
+  <outline>
+    <contour>
+      <point x="412" y="563" type="curve" smooth="yes"/>
+      <point x="334" y="563"/>
+      <point x="278" y="532"/>
+      <point x="243" y="480" type="curve"/>
+      <point x="236" y="480" type="line"/>
+      <point x="210" y="553" type="line"/>
+      <point x="66" y="553" type="line"/>
+      <point x="66" y="0" type="line"/>
+      <point x="257" y="0" type="line"/>
+      <point x="257" y="242" type="line" smooth="yes"/>
+      <point x="257" y="352"/>
+      <point x="276" y="413"/>
+      <point x="348" y="413" type="curve" smooth="yes"/>
+      <point x="395" y="413"/>
+      <point x="415" y="375"/>
+      <point x="415" y="302" type="curve" smooth="yes"/>
+      <point x="415" y="0" type="line"/>
+      <point x="606" y="0" type="line"/>
+      <point x="606" y="360" type="line" smooth="yes"/>
+      <point x="606" y="502"/>
+      <point x="526" y="563"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/o.glif b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/o.glif
new file mode 100644
index 0000000..1a5405b
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/o.glif
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="o" format="2">
+  <advance width="637"/>
+  <unicode hex="006F"/>
+  <anchor x="317" y="0" name="bottom"/>
+  <anchor x="319" y="277" name="center"/>
+  <anchor x="319" y="553" name="top"/>
+  <anchor x="617" y="553" name="topright"/>
+  <outline>
+    <contour>
+      <point x="594" y="278" type="curve" smooth="yes"/>
+      <point x="594" y="461"/>
+      <point x="479" y="563"/>
+      <point x="320" y="563" type="curve" smooth="yes"/>
+      <point x="147" y="563"/>
+      <point x="42" y="461"/>
+      <point x="42" y="278" type="curve" smooth="yes"/>
+      <point x="42" y="93"/>
+      <point x="157" y="-10"/>
+      <point x="317" y="-10" type="curve" smooth="yes"/>
+      <point x="489" y="-10"/>
+      <point x="594" y="93"/>
+    </contour>
+    <contour>
+      <point x="236" y="278" type="curve" smooth="yes"/>
+      <point x="236" y="372"/>
+      <point x="260" y="421"/>
+      <point x="318" y="421" type="curve" smooth="yes"/>
+      <point x="378" y="421"/>
+      <point x="400" y="372"/>
+      <point x="400" y="278" type="curve" smooth="yes"/>
+      <point x="400" y="183"/>
+      <point x="378" y="132"/>
+      <point x="319" y="132" type="curve" smooth="yes"/>
+      <point x="259" y="132"/>
+      <point x="236" y="183"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/s.glif b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/s.glif
new file mode 100644
index 0000000..019e3d9
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/s.glif
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="s" format="2">
+  <advance width="517"/>
+  <unicode hex="0073"/>
+  <anchor x="251" y="0" name="bottom"/>
+  <anchor x="251" y="553" name="top"/>
+  <anchor x="497" y="553" name="topright"/>
+  <outline>
+    <contour>
+      <point x="477" y="170" type="curve" smooth="yes"/>
+      <point x="477" y="267"/>
+      <point x="431" y="307"/>
+      <point x="335" y="346" type="curve" smooth="yes"/>
+      <point x="243" y="383"/>
+      <point x="224" y="390"/>
+      <point x="224" y="411" type="curve" smooth="yes"/>
+      <point x="224" y="426"/>
+      <point x="241" y="434"/>
+      <point x="271" y="434" type="curve" smooth="yes"/>
+      <point x="304" y="434"/>
+      <point x="366" y="418"/>
+      <point x="421" y="393" type="curve"/>
+      <point x="473" y="516" type="line"/>
+      <point x="404" y="547"/>
+      <point x="343" y="563"/>
+      <point x="270" y="563" type="curve" smooth="yes"/>
+      <point x="133" y="563"/>
+      <point x="42" y="508"/>
+      <point x="42" y="400" type="curve" smooth="yes"/>
+      <point x="42" y="309"/>
+      <point x="87" y="266"/>
+      <point x="179" y="228" type="curve" smooth="yes"/>
+      <point x="272" y="190"/>
+      <point x="297" y="181"/>
+      <point x="297" y="157" type="curve" smooth="yes"/>
+      <point x="297" y="138"/>
+      <point x="278" y="129"/>
+      <point x="235" y="129" type="curve" smooth="yes"/>
+      <point x="193" y="129"/>
+      <point x="113" y="142"/>
+      <point x="43" y="174" type="curve"/>
+      <point x="43" y="21" type="line"/>
+      <point x="107" y="-3"/>
+      <point x="164" y="-10"/>
+      <point x="243" y="-10" type="curve" smooth="yes"/>
+      <point x="411" y="-10"/>
+      <point x="477" y="65"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/t.glif b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/t.glif
new file mode 100644
index 0000000..54dde26
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/glyphs/t.glif
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="t" format="2">
+  <advance width="460"/>
+  <unicode hex="0074"/>
+  <anchor x="264" y="0" name="bottom"/>
+  <anchor x="230" y="277" name="center"/>
+  <anchor x="219" y="664" name="top"/>
+  <anchor x="421" y="664" name="topright"/>
+  <outline>
+    <contour>
+      <point x="337" y="141" type="curve" smooth="yes"/>
+      <point x="304" y="141"/>
+      <point x="285" y="159"/>
+      <point x="285" y="195" type="curve" smooth="yes"/>
+      <point x="285" y="410" type="line"/>
+      <point x="421" y="410" type="line"/>
+      <point x="421" y="553" type="line"/>
+      <point x="285" y="553" type="line"/>
+      <point x="285" y="664" type="line"/>
+      <point x="160" y="664" type="line"/>
+      <point x="111" y="548" type="line"/>
+      <point x="26" y="488" type="line"/>
+      <point x="26" y="410" type="line"/>
+      <point x="93" y="410" type="line"/>
+      <point x="93" y="182" type="line" smooth="yes"/>
+      <point x="93" y="32"/>
+      <point x="166" y="-10"/>
+      <point x="274" y="-10" type="curve" smooth="yes"/>
+      <point x="346" y="-10"/>
+      <point x="387" y="3"/>
+      <point x="429" y="21" type="curve"/>
+      <point x="429" y="160" type="line"/>
+      <point x="398" y="149"/>
+      <point x="371" y="141"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/groups.plist b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/groups.plist
new file mode 100644
index 0000000..02bf8c6
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/groups.plist
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.kern1.T.left</key>
+	<array>
+		<string>T</string>
+	</array>
+	<key>public.kern1.d</key>
+	<array>
+		<string>l</string>
+	</array>
+	<key>public.kern1.n.left</key>
+	<array>
+		<string>n</string>
+	</array>
+	<key>public.kern1.o.left</key>
+	<array>
+		<string>o</string>
+	</array>
+	<key>public.kern1.t.left</key>
+	<array>
+		<string>t</string>
+	</array>
+	<key>public.kern2.T.right</key>
+	<array>
+		<string>T</string>
+	</array>
+	<key>public.kern2.b</key>
+	<array>
+		<string>l</string>
+	</array>
+	<key>public.kern2.n.right</key>
+	<array>
+		<string>n</string>
+	</array>
+	<key>public.kern2.o.right</key>
+	<array>
+		<string>o</string>
+	</array>
+	<key>public.kern2.s.right</key>
+	<array>
+		<string>s</string>
+	</array>
+	<key>public.kern2.t.right</key>
+	<array>
+		<string>t</string>
+	</array>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/kerning.plist b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/kerning.plist
new file mode 100644
index 0000000..2000fcc
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/kerning.plist
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.kern1.T.left</key>
+	<dict>
+		<key>public.kern2.T.right</key>
+		<integer>20</integer>
+		<key>public.kern2.n.right</key>
+		<integer>-50</integer>
+		<key>public.kern2.o.right</key>
+		<integer>-70</integer>
+		<key>public.kern2.s.right</key>
+		<integer>-60</integer>
+	</dict>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/layercontents.plist b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/layercontents.plist
new file mode 100644
index 0000000..cf95d35
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/layercontents.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<array>
+	<array>
+		<string>public.default</string>
+		<string>glyphs</string>
+	</array>
+</array>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/lib.plist b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/lib.plist
new file mode 100644
index 0000000..42d19d9
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/lib.plist
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.glyphOrder</key>
+	<array>
+		<string>F</string>
+		<string>T</string>
+		<string>l</string>
+		<string>n</string>
+		<string>o</string>
+		<string>s</string>
+		<string>t</string>
+	</array>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/metainfo.plist b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/metainfo.plist
new file mode 100644
index 0000000..632695b
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Bold.ufo/metainfo.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>creator</key>
+	<string>org.robofab.ufoLib</string>
+	<key>formatVersion</key>
+	<integer>3</integer>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/fontinfo.plist b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/fontinfo.plist
new file mode 100644
index 0000000..52de30a
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/fontinfo.plist
@@ -0,0 +1,153 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>ascender</key>
+	<integer>760</integer>
+	<key>capHeight</key>
+	<integer>714</integer>
+	<key>copyright</key>
+	<string>Copyright 2015 Google Inc. All Rights Reserved.</string>
+	<key>descender</key>
+	<integer>-240</integer>
+	<key>familyName</key>
+	<string>Test Family 3</string>
+	<key>guidelines</key>
+	<array/>
+	<key>openTypeHeadCreated</key>
+	<string>2016/03/15 19:50:39</string>
+	<key>openTypeHheaAscender</key>
+	<integer>1069</integer>
+	<key>openTypeHheaDescender</key>
+	<integer>-293</integer>
+	<key>openTypeHheaLineGap</key>
+	<integer>0</integer>
+	<key>openTypeNameDescription</key>
+	<string>Designed by Monotype design team.</string>
+	<key>openTypeNameDesigner</key>
+	<string>Monotype Design Team</string>
+	<key>openTypeNameDesignerURL</key>
+	<string>http://www.monotype.com/studio</string>
+	<key>openTypeNameLicense</key>
+	<string>This Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License for the specific language, permissions and limitations governing your use of this Font Software.</string>
+	<key>openTypeNameLicenseURL</key>
+	<string>http://scripts.sil.org/OFL</string>
+	<key>openTypeNameManufacturer</key>
+	<string>Monotype Imaging Inc.</string>
+	<key>openTypeNameManufacturerURL</key>
+	<string>http://www.google.com/get/noto/</string>
+	<key>openTypeNameVersion</key>
+	<string>Version 1.902</string>
+	<key>openTypeOS2Panose</key>
+	<array>
+		<integer>2</integer>
+		<integer>11</integer>
+		<integer>5</integer>
+		<integer>6</integer>
+		<integer>4</integer>
+		<integer>5</integer>
+		<integer>4</integer>
+		<integer>2</integer>
+		<integer>2</integer>
+		<integer>4</integer>
+	</array>
+	<key>openTypeOS2Selection</key>
+	<array>
+		<integer>8</integer>
+	</array>
+	<key>openTypeOS2Type</key>
+	<array/>
+	<key>openTypeOS2TypoAscender</key>
+	<integer>1069</integer>
+	<key>openTypeOS2TypoDescender</key>
+	<integer>-293</integer>
+	<key>openTypeOS2TypoLineGap</key>
+	<integer>0</integer>
+	<key>openTypeOS2UnicodeRanges</key>
+	<array>
+		<integer>0</integer>
+		<integer>1</integer>
+		<integer>2</integer>
+		<integer>3</integer>
+		<integer>4</integer>
+		<integer>5</integer>
+		<integer>6</integer>
+		<integer>7</integer>
+		<integer>9</integer>
+		<integer>29</integer>
+		<integer>30</integer>
+		<integer>31</integer>
+		<integer>32</integer>
+		<integer>33</integer>
+		<integer>34</integer>
+		<integer>35</integer>
+		<integer>36</integer>
+		<integer>62</integer>
+		<integer>64</integer>
+		<integer>67</integer>
+		<integer>69</integer>
+		<integer>91</integer>
+		<integer>116</integer>
+	</array>
+	<key>openTypeOS2VendorID</key>
+	<string>GOOG</string>
+	<key>openTypeOS2WinAscent</key>
+	<integer>1069</integer>
+	<key>openTypeOS2WinDescent</key>
+	<integer>293</integer>
+	<key>postscriptBlueValues</key>
+	<array>
+		<integer>-15</integer>
+		<integer>0</integer>
+		<integer>537</integer>
+		<integer>548</integer>
+		<integer>571</integer>
+		<integer>581</integer>
+		<integer>714</integer>
+		<integer>725</integer>
+		<integer>760</integer>
+		<integer>766</integer>
+	</array>
+	<key>postscriptFamilyBlues</key>
+	<array/>
+	<key>postscriptFamilyOtherBlues</key>
+	<array/>
+	<key>postscriptOtherBlues</key>
+	<array>
+		<integer>-256</integer>
+		<integer>-240</integer>
+	</array>
+	<key>postscriptStemSnapH</key>
+	<array>
+		<integer>60</integer>
+		<integer>68</integer>
+		<integer>76</integer>
+	</array>
+	<key>postscriptStemSnapV</key>
+	<array>
+		<integer>64</integer>
+		<integer>75</integer>
+		<integer>84</integer>
+	</array>
+	<key>postscriptUnderlinePosition</key>
+	<integer>-100</integer>
+	<key>postscriptUnderlineThickness</key>
+	<integer>50</integer>
+	<key>styleMapFamilyName</key>
+	<string>Test Family 3 Condensed</string>
+	<key>styleMapStyleName</key>
+	<string>regular</string>
+	<key>styleName</key>
+	<string>Condensed</string>
+	<key>trademark</key>
+	<string>Noto is a trademark of Google Inc.</string>
+	<key>unitsPerEm</key>
+	<integer>1000</integer>
+	<key>versionMajor</key>
+	<integer>1</integer>
+	<key>versionMinor</key>
+	<integer>902</integer>
+	<key>xHeight</key>
+	<integer>537</integer>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/F_.glif b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/F_.glif
new file mode 100644
index 0000000..aa27dad
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/F_.glif
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="F" format="2">
+  <advance width="380"/>
+  <unicode hex="0046"/>
+  <anchor x="190" y="0" name="bottom"/>
+  <anchor x="220" y="714" name="top"/>
+  <anchor x="360" y="714" name="topright"/>
+  <outline>
+    <contour>
+      <point x="157" y="0" type="line"/>
+      <point x="157" y="305" type="line"/>
+      <point x="345" y="305" type="line"/>
+      <point x="345" y="381" type="line"/>
+      <point x="157" y="381" type="line"/>
+      <point x="157" y="639" type="line"/>
+      <point x="357" y="639" type="line"/>
+      <point x="357" y="714" type="line"/>
+      <point x="73" y="714" type="line"/>
+      <point x="73" y="0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/T_.glif b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/T_.glif
new file mode 100644
index 0000000..fe0bff7
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/T_.glif
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="T" format="2">
+  <advance width="381"/>
+  <unicode hex="0054"/>
+  <anchor x="191" y="0" name="bottom"/>
+  <anchor x="191" y="357" name="center"/>
+  <anchor x="191" y="714" name="top"/>
+  <anchor x="361" y="714" name="topright"/>
+  <outline>
+    <contour>
+      <point x="232" y="0" type="line"/>
+      <point x="232" y="638" type="line"/>
+      <point x="370" y="638" type="line"/>
+      <point x="370" y="714" type="line"/>
+      <point x="10" y="714" type="line"/>
+      <point x="10" y="638" type="line"/>
+      <point x="148" y="638" type="line"/>
+      <point x="148" y="0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/contents.plist b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/contents.plist
new file mode 100644
index 0000000..d50a055
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/contents.plist
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>F</key>
+	<string>F_.glif</string>
+	<key>T</key>
+	<string>T_.glif</string>
+	<key>l</key>
+	<string>l.glif</string>
+	<key>n</key>
+	<string>n.glif</string>
+	<key>o</key>
+	<string>o.glif</string>
+	<key>s</key>
+	<string>s.glif</string>
+	<key>t</key>
+	<string>t.glif</string>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/l.glif b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/l.glif
new file mode 100644
index 0000000..4a3fdac
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/l.glif
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="l" format="2">
+  <advance width="210"/>
+  <unicode hex="006C"/>
+  <anchor x="106" y="0" name="bottom"/>
+  <anchor x="105" y="269" name="center"/>
+  <anchor x="106" y="760" name="top"/>
+  <anchor x="167" y="760" name="topright"/>
+  <outline>
+    <contour>
+      <point x="146" y="0" type="line"/>
+      <point x="146" y="760" type="line"/>
+      <point x="65" y="760" type="line"/>
+      <point x="65" y="0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/layerinfo.plist b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/layerinfo.plist
new file mode 100644
index 0000000..0c67376
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/layerinfo.plist
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict/>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/n.glif b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/n.glif
new file mode 100644
index 0000000..e1e638b
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/n.glif
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="n" format="2">
+  <advance width="456"/>
+  <unicode hex="006E"/>
+  <anchor x="229" y="0" name="bottom"/>
+  <anchor x="236" y="537" name="top"/>
+  <anchor x="418" y="537" name="topright"/>
+  <outline>
+    <contour>
+      <point x="262" y="547" type="curve" smooth="yes"/>
+      <point x="211" y="547"/>
+      <point x="167" y="517"/>
+      <point x="144" y="464" type="curve"/>
+      <point x="139" y="464" type="line"/>
+      <point x="130" y="537" type="line"/>
+      <point x="65" y="537" type="line"/>
+      <point x="65" y="0" type="line"/>
+      <point x="146" y="0" type="line"/>
+      <point x="146" y="279" type="line" smooth="yes"/>
+      <point x="146" y="417"/>
+      <point x="174" y="475"/>
+      <point x="244" y="475" type="curve" smooth="yes"/>
+      <point x="292" y="475"/>
+      <point x="312" y="432"/>
+      <point x="312" y="348" type="curve" smooth="yes"/>
+      <point x="312" y="0" type="line"/>
+      <point x="393" y="0" type="line"/>
+      <point x="393" y="364" type="line" smooth="yes"/>
+      <point x="393" y="488"/>
+      <point x="348" y="547"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/o.glif b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/o.glif
new file mode 100644
index 0000000..aa7f5c6
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/o.glif
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="o" format="2">
+  <advance width="445"/>
+  <unicode hex="006F"/>
+  <anchor x="223" y="0" name="bottom"/>
+  <anchor x="223" y="269" name="center"/>
+  <anchor x="222" y="537" name="top"/>
+  <anchor x="425" y="537" name="topright"/>
+  <outline>
+    <contour>
+      <point x="403" y="269" type="curve" smooth="yes"/>
+      <point x="403" y="452"/>
+      <point x="337" y="547"/>
+      <point x="223" y="547" type="curve" smooth="yes"/>
+      <point x="99" y="547"/>
+      <point x="42" y="446"/>
+      <point x="42" y="269" type="curve" smooth="yes"/>
+      <point x="42" y="101"/>
+      <point x="102" y="-10"/>
+      <point x="221" y="-10" type="curve" smooth="yes"/>
+      <point x="346" y="-10"/>
+      <point x="403" y="102"/>
+    </contour>
+    <contour>
+      <point x="125" y="269" type="curve" smooth="yes"/>
+      <point x="125" y="407"/>
+      <point x="154" y="476"/>
+      <point x="223" y="476" type="curve" smooth="yes"/>
+      <point x="290" y="476"/>
+      <point x="321" y="407"/>
+      <point x="321" y="269" type="curve" smooth="yes"/>
+      <point x="321" y="130"/>
+      <point x="290" y="61"/>
+      <point x="223" y="61" type="curve" smooth="yes"/>
+      <point x="155" y="61"/>
+      <point x="125" y="132"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/s.glif b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/s.glif
new file mode 100644
index 0000000..6f2a82f
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/s.glif
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="s" format="2">
+  <advance width="339"/>
+  <unicode hex="0073"/>
+  <anchor x="158" y="0" name="bottom"/>
+  <anchor x="179" y="537" name="top"/>
+  <anchor x="309" y="537" name="topright"/>
+  <outline>
+    <contour>
+      <point x="307" y="144" type="curve" smooth="yes"/>
+      <point x="307" y="226"/>
+      <point x="262" y="265"/>
+      <point x="196" y="307" type="curve" smooth="yes"/>
+      <point x="133" y="345"/>
+      <point x="111" y="365"/>
+      <point x="111" y="408" type="curve" smooth="yes"/>
+      <point x="111" y="450"/>
+      <point x="139" y="478"/>
+      <point x="184" y="478" type="curve" smooth="yes"/>
+      <point x="217" y="478"/>
+      <point x="248" y="466"/>
+      <point x="276" y="447" type="curve"/>
+      <point x="306" y="512" type="line"/>
+      <point x="269" y="535"/>
+      <point x="229" y="547"/>
+      <point x="183" y="547" type="curve" smooth="yes"/>
+      <point x="94" y="547"/>
+      <point x="33" y="491"/>
+      <point x="33" y="406" type="curve" smooth="yes"/>
+      <point x="33" y="323"/>
+      <point x="78" y="283"/>
+      <point x="145" y="242" type="curve" smooth="yes"/>
+      <point x="205" y="208"/>
+      <point x="227" y="183"/>
+      <point x="227" y="141" type="curve" smooth="yes"/>
+      <point x="227" y="92"/>
+      <point x="199" y="63"/>
+      <point x="152" y="63" type="curve" smooth="yes"/>
+      <point x="108" y="63"/>
+      <point x="62" y="80"/>
+      <point x="33" y="104" type="curve"/>
+      <point x="33" y="20" type="line"/>
+      <point x="63" y="3"/>
+      <point x="105" y="-10"/>
+      <point x="155" y="-10" type="curve" smooth="yes"/>
+      <point x="251" y="-10"/>
+      <point x="307" y="45"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/t.glif b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/t.glif
new file mode 100644
index 0000000..42e05ce
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/glyphs/t.glif
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="t" format="2">
+  <advance width="259"/>
+  <unicode hex="0074"/>
+  <anchor x="159" y="0" name="bottom"/>
+  <anchor x="130" y="268" name="center"/>
+  <anchor x="119" y="658" name="top"/>
+  <anchor x="238" y="658" name="topright"/>
+  <outline>
+    <contour>
+      <point x="197" y="62" type="curve" smooth="yes"/>
+      <point x="158" y="62"/>
+      <point x="148" y="89"/>
+      <point x="148" y="143" type="curve" smooth="yes"/>
+      <point x="148" y="469" type="line"/>
+      <point x="238" y="469" type="line"/>
+      <point x="238" y="537" type="line"/>
+      <point x="148" y="537" type="line"/>
+      <point x="148" y="658" type="line"/>
+      <point x="93" y="658" type="line"/>
+      <point x="71" y="535" type="line"/>
+      <point x="17" y="513" type="line"/>
+      <point x="17" y="469" type="line"/>
+      <point x="67" y="469" type="line"/>
+      <point x="67" y="133" type="line" smooth="yes"/>
+      <point x="67" y="37"/>
+      <point x="99" y="-10"/>
+      <point x="170" y="-10" type="curve" smooth="yes"/>
+      <point x="200" y="-10"/>
+      <point x="224" y="-4"/>
+      <point x="245" y="6" type="curve"/>
+      <point x="245" y="72" type="line"/>
+      <point x="229" y="66"/>
+      <point x="213" y="62"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/groups.plist b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/groups.plist
new file mode 100644
index 0000000..02bf8c6
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/groups.plist
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.kern1.T.left</key>
+	<array>
+		<string>T</string>
+	</array>
+	<key>public.kern1.d</key>
+	<array>
+		<string>l</string>
+	</array>
+	<key>public.kern1.n.left</key>
+	<array>
+		<string>n</string>
+	</array>
+	<key>public.kern1.o.left</key>
+	<array>
+		<string>o</string>
+	</array>
+	<key>public.kern1.t.left</key>
+	<array>
+		<string>t</string>
+	</array>
+	<key>public.kern2.T.right</key>
+	<array>
+		<string>T</string>
+	</array>
+	<key>public.kern2.b</key>
+	<array>
+		<string>l</string>
+	</array>
+	<key>public.kern2.n.right</key>
+	<array>
+		<string>n</string>
+	</array>
+	<key>public.kern2.o.right</key>
+	<array>
+		<string>o</string>
+	</array>
+	<key>public.kern2.s.right</key>
+	<array>
+		<string>s</string>
+	</array>
+	<key>public.kern2.t.right</key>
+	<array>
+		<string>t</string>
+	</array>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/kerning.plist b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/kerning.plist
new file mode 100644
index 0000000..0548061
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/kerning.plist
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.kern1.T.left</key>
+	<dict>
+		<key>public.kern2.T.right</key>
+		<integer>12</integer>
+		<key>public.kern2.n.right</key>
+		<integer>-30</integer>
+		<key>public.kern2.o.right</key>
+		<integer>-42</integer>
+		<key>public.kern2.s.right</key>
+		<integer>-36</integer>
+	</dict>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/layercontents.plist b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/layercontents.plist
new file mode 100644
index 0000000..cf95d35
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/layercontents.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<array>
+	<array>
+		<string>public.default</string>
+		<string>glyphs</string>
+	</array>
+</array>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/lib.plist b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/lib.plist
new file mode 100644
index 0000000..42d19d9
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/lib.plist
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.glyphOrder</key>
+	<array>
+		<string>F</string>
+		<string>T</string>
+		<string>l</string>
+		<string>n</string>
+		<string>o</string>
+		<string>s</string>
+		<string>t</string>
+	</array>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/metainfo.plist b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/metainfo.plist
new file mode 100644
index 0000000..632695b
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Condensed.ufo/metainfo.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>creator</key>
+	<string>org.robofab.ufoLib</string>
+	<key>formatVersion</key>
+	<integer>3</integer>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/fontinfo.plist b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/fontinfo.plist
new file mode 100644
index 0000000..d74fc13
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/fontinfo.plist
@@ -0,0 +1,153 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>ascender</key>
+	<integer>760</integer>
+	<key>capHeight</key>
+	<integer>714</integer>
+	<key>copyright</key>
+	<string>Copyright 2015 Google Inc. All Rights Reserved.</string>
+	<key>descender</key>
+	<integer>-240</integer>
+	<key>familyName</key>
+	<string>Test Family 3</string>
+	<key>guidelines</key>
+	<array/>
+	<key>openTypeHeadCreated</key>
+	<string>2016/03/15 19:50:39</string>
+	<key>openTypeHheaAscender</key>
+	<integer>1069</integer>
+	<key>openTypeHheaDescender</key>
+	<integer>-293</integer>
+	<key>openTypeHheaLineGap</key>
+	<integer>0</integer>
+	<key>openTypeNameDescription</key>
+	<string>Designed by Monotype design team.</string>
+	<key>openTypeNameDesigner</key>
+	<string>Monotype Design Team</string>
+	<key>openTypeNameDesignerURL</key>
+	<string>http://www.monotype.com/studio</string>
+	<key>openTypeNameLicense</key>
+	<string>This Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License for the specific language, permissions and limitations governing your use of this Font Software.</string>
+	<key>openTypeNameLicenseURL</key>
+	<string>http://scripts.sil.org/OFL</string>
+	<key>openTypeNameManufacturer</key>
+	<string>Monotype Imaging Inc.</string>
+	<key>openTypeNameManufacturerURL</key>
+	<string>http://www.google.com/get/noto/</string>
+	<key>openTypeNameVersion</key>
+	<string>Version 1.902</string>
+	<key>openTypeOS2Panose</key>
+	<array>
+		<integer>2</integer>
+		<integer>11</integer>
+		<integer>10</integer>
+		<integer>6</integer>
+		<integer>4</integer>
+		<integer>5</integer>
+		<integer>4</integer>
+		<integer>2</integer>
+		<integer>2</integer>
+		<integer>4</integer>
+	</array>
+	<key>openTypeOS2Selection</key>
+	<array>
+		<integer>8</integer>
+	</array>
+	<key>openTypeOS2Type</key>
+	<array/>
+	<key>openTypeOS2TypoAscender</key>
+	<integer>1069</integer>
+	<key>openTypeOS2TypoDescender</key>
+	<integer>-293</integer>
+	<key>openTypeOS2TypoLineGap</key>
+	<integer>0</integer>
+	<key>openTypeOS2UnicodeRanges</key>
+	<array>
+		<integer>0</integer>
+		<integer>1</integer>
+		<integer>2</integer>
+		<integer>3</integer>
+		<integer>4</integer>
+		<integer>5</integer>
+		<integer>6</integer>
+		<integer>7</integer>
+		<integer>9</integer>
+		<integer>29</integer>
+		<integer>30</integer>
+		<integer>31</integer>
+		<integer>32</integer>
+		<integer>33</integer>
+		<integer>34</integer>
+		<integer>35</integer>
+		<integer>36</integer>
+		<integer>62</integer>
+		<integer>64</integer>
+		<integer>67</integer>
+		<integer>69</integer>
+		<integer>91</integer>
+		<integer>116</integer>
+	</array>
+	<key>openTypeOS2VendorID</key>
+	<string>GOOG</string>
+	<key>openTypeOS2WinAscent</key>
+	<integer>1069</integer>
+	<key>openTypeOS2WinDescent</key>
+	<integer>293</integer>
+	<key>postscriptBlueValues</key>
+	<array>
+		<integer>-15</integer>
+		<integer>0</integer>
+		<integer>553</integer>
+		<integer>564</integer>
+		<integer>589</integer>
+		<integer>599</integer>
+		<integer>714</integer>
+		<integer>726</integer>
+		<integer>760</integer>
+		<integer>766</integer>
+	</array>
+	<key>postscriptFamilyBlues</key>
+	<array/>
+	<key>postscriptFamilyOtherBlues</key>
+	<array/>
+	<key>postscriptOtherBlues</key>
+	<array>
+		<integer>-256</integer>
+		<integer>-240</integer>
+	</array>
+	<key>postscriptStemSnapH</key>
+	<array>
+		<integer>115</integer>
+		<integer>133</integer>
+		<integer>146</integer>
+	</array>
+	<key>postscriptStemSnapV</key>
+	<array>
+		<integer>117</integer>
+		<integer>150</integer>
+		<integer>170</integer>
+	</array>
+	<key>postscriptUnderlinePosition</key>
+	<integer>-100</integer>
+	<key>postscriptUnderlineThickness</key>
+	<integer>50</integer>
+	<key>styleMapFamilyName</key>
+	<string>Test Family 3 Condensed Bold</string>
+	<key>styleMapStyleName</key>
+	<string>regular</string>
+	<key>styleName</key>
+	<string>Condensed Bold</string>
+	<key>trademark</key>
+	<string>Noto is a trademark of Google Inc.</string>
+	<key>unitsPerEm</key>
+	<integer>1000</integer>
+	<key>versionMajor</key>
+	<integer>1</integer>
+	<key>versionMinor</key>
+	<integer>902</integer>
+	<key>xHeight</key>
+	<integer>553</integer>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/F_.glif b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/F_.glif
new file mode 100644
index 0000000..f3c1c89
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/F_.glif
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="F" format="2">
+  <advance width="436"/>
+  <unicode hex="0046"/>
+  <anchor x="218" y="0" name="bottom"/>
+  <anchor x="231" y="714" name="top"/>
+  <anchor x="416" y="714" name="topright"/>
+  <outline>
+    <contour>
+      <point x="229" y="0" type="line"/>
+      <point x="229" y="273" type="line"/>
+      <point x="388" y="273" type="line"/>
+      <point x="388" y="417" type="line"/>
+      <point x="229" y="417" type="line"/>
+      <point x="229" y="570" type="line"/>
+      <point x="401" y="570" type="line"/>
+      <point x="401" y="714" type="line"/>
+      <point x="59" y="714" type="line"/>
+      <point x="59" y="0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/T_.glif b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/T_.glif
new file mode 100644
index 0000000..603c147
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/T_.glif
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="T" format="2">
+  <advance width="460"/>
+  <unicode hex="0054"/>
+  <anchor x="230" y="0" name="bottom"/>
+  <anchor x="230" y="357" name="center"/>
+  <anchor x="230" y="714" name="top"/>
+  <anchor x="440" y="714" name="topright"/>
+  <outline>
+    <contour>
+      <point x="315" y="0" type="line"/>
+      <point x="315" y="567" type="line"/>
+      <point x="443" y="567" type="line"/>
+      <point x="443" y="714" type="line"/>
+      <point x="17" y="714" type="line"/>
+      <point x="17" y="567" type="line"/>
+      <point x="146" y="567" type="line"/>
+      <point x="146" y="0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/contents.plist b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/contents.plist
new file mode 100644
index 0000000..d50a055
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/contents.plist
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>F</key>
+	<string>F_.glif</string>
+	<key>T</key>
+	<string>T_.glif</string>
+	<key>l</key>
+	<string>l.glif</string>
+	<key>n</key>
+	<string>n.glif</string>
+	<key>o</key>
+	<string>o.glif</string>
+	<key>s</key>
+	<string>s.glif</string>
+	<key>t</key>
+	<string>t.glif</string>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/l.glif b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/l.glif
new file mode 100644
index 0000000..9beb251
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/l.glif
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="l" format="2">
+  <advance width="263"/>
+  <unicode hex="006C"/>
+  <anchor x="132" y="0" name="bottom"/>
+  <anchor x="132" y="277" name="center"/>
+  <anchor x="132" y="760" name="top"/>
+  <anchor x="232" y="760" name="topright"/>
+  <outline>
+    <contour>
+      <point x="212" y="0" type="line"/>
+      <point x="212" y="760" type="line"/>
+      <point x="50" y="760" type="line"/>
+      <point x="50" y="0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/layerinfo.plist b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/layerinfo.plist
new file mode 100644
index 0000000..0c67376
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/layerinfo.plist
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict/>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/n.glif b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/n.glif
new file mode 100644
index 0000000..d914b64
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/n.glif
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="n" format="2">
+  <advance width="529"/>
+  <unicode hex="006E"/>
+  <anchor x="265" y="0" name="bottom"/>
+  <anchor x="275" y="553" name="top"/>
+  <anchor x="500" y="553" name="topright"/>
+  <outline>
+    <contour>
+      <point x="328" y="563" type="curve" smooth="yes"/>
+      <point x="269" y="563"/>
+      <point x="229" y="537"/>
+      <point x="204" y="485" type="curve"/>
+      <point x="195" y="485" type="line"/>
+      <point x="177" y="553" type="line"/>
+      <point x="50" y="553" type="line"/>
+      <point x="50" y="0" type="line"/>
+      <point x="212" y="0" type="line"/>
+      <point x="212" y="253" type="line" smooth="yes"/>
+      <point x="212" y="378"/>
+      <point x="224" y="419"/>
+      <point x="270" y="419" type="curve" smooth="yes"/>
+      <point x="310" y="419"/>
+      <point x="318" y="381"/>
+      <point x="318" y="308" type="curve" smooth="yes"/>
+      <point x="318" y="0" type="line"/>
+      <point x="480" y="0" type="line"/>
+      <point x="480" y="360" type="line" smooth="yes"/>
+      <point x="480" y="489"/>
+      <point x="424" y="563"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/o.glif b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/o.glif
new file mode 100644
index 0000000..78ebeae
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/o.glif
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="o" format="2">
+  <advance width="516"/>
+  <unicode hex="006F"/>
+  <anchor x="257" y="0" name="bottom"/>
+  <anchor x="258" y="277" name="center"/>
+  <anchor x="258" y="553" name="top"/>
+  <anchor x="496" y="553" name="topright"/>
+  <outline>
+    <contour>
+      <point x="483" y="278" type="curve" smooth="yes"/>
+      <point x="483" y="460"/>
+      <point x="393" y="563"/>
+      <point x="259" y="563" type="curve" smooth="yes"/>
+      <point x="97" y="563"/>
+      <point x="32" y="437"/>
+      <point x="32" y="278" type="curve" smooth="yes"/>
+      <point x="32" y="132"/>
+      <point x="100" y="-10"/>
+      <point x="257" y="-10" type="curve" smooth="yes"/>
+      <point x="427" y="-10"/>
+      <point x="483" y="136"/>
+    </contour>
+    <contour>
+      <point x="196" y="276" type="curve" smooth="yes"/>
+      <point x="196" y="380"/>
+      <point x="215" y="428"/>
+      <point x="258" y="428" type="curve" smooth="yes"/>
+      <point x="303" y="428"/>
+      <point x="319" y="379"/>
+      <point x="319" y="278" type="curve" smooth="yes"/>
+      <point x="319" y="176"/>
+      <point x="303" y="125"/>
+      <point x="258" y="125" type="curve" smooth="yes"/>
+      <point x="215" y="125"/>
+      <point x="196" y="177"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/s.glif b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/s.glif
new file mode 100644
index 0000000..2bc0153
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/s.glif
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="s" format="2">
+  <advance width="396"/>
+  <unicode hex="0073"/>
+  <anchor x="184" y="0" name="bottom"/>
+  <anchor x="204" y="553" name="top"/>
+  <anchor x="376" y="553" name="topright"/>
+  <outline>
+    <contour>
+      <point x="372" y="170" type="curve" smooth="yes"/>
+      <point x="372" y="255"/>
+      <point x="326" y="304"/>
+      <point x="259" y="342" type="curve" smooth="yes"/>
+      <point x="188" y="384"/>
+      <point x="181" y="390"/>
+      <point x="181" y="408" type="curve" smooth="yes"/>
+      <point x="181" y="426"/>
+      <point x="192" y="434"/>
+      <point x="215" y="434" type="curve" smooth="yes"/>
+      <point x="253" y="434"/>
+      <point x="291" y="415"/>
+      <point x="324" y="393" type="curve"/>
+      <point x="364" y="516" type="line"/>
+      <point x="311" y="547"/>
+      <point x="262" y="563"/>
+      <point x="205" y="563" type="curve" smooth="yes"/>
+      <point x="90" y="563"/>
+      <point x="25" y="503"/>
+      <point x="25" y="400" type="curve" smooth="yes"/>
+      <point x="25" y="322"/>
+      <point x="60" y="265"/>
+      <point x="128" y="231" type="curve" smooth="yes"/>
+      <point x="205" y="193"/>
+      <point x="211" y="179"/>
+      <point x="211" y="161" type="curve" smooth="yes"/>
+      <point x="211" y="138"/>
+      <point x="196" y="129"/>
+      <point x="166" y="129" type="curve" smooth="yes"/>
+      <point x="116" y="129"/>
+      <point x="66" y="150"/>
+      <point x="26" y="174" type="curve"/>
+      <point x="26" y="21" type="line"/>
+      <point x="77" y="-2"/>
+      <point x="129" y="-10"/>
+      <point x="183" y="-10" type="curve" smooth="yes"/>
+      <point x="302" y="-10"/>
+      <point x="372" y="51"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/t.glif b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/t.glif
new file mode 100644
index 0000000..0c0765c
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/glyphs/t.glif
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="t" format="2">
+  <advance width="354"/>
+  <unicode hex="0074"/>
+  <anchor x="208" y="0" name="bottom"/>
+  <anchor x="177" y="277" name="center"/>
+  <anchor x="163" y="664" name="top"/>
+  <anchor x="329" y="664" name="topright"/>
+  <outline>
+    <contour>
+      <point x="265" y="130" type="curve" smooth="yes"/>
+      <point x="238" y="130"/>
+      <point x="225" y="148"/>
+      <point x="225" y="184" type="curve" smooth="yes"/>
+      <point x="225" y="420" type="line"/>
+      <point x="329" y="420" type="line"/>
+      <point x="329" y="553" type="line"/>
+      <point x="225" y="553" type="line"/>
+      <point x="225" y="664" type="line"/>
+      <point x="115" y="664" type="line"/>
+      <point x="77" y="548" type="line"/>
+      <point x="7" y="506" type="line"/>
+      <point x="7" y="420" type="line"/>
+      <point x="63" y="420" type="line"/>
+      <point x="63" y="182" type="line" smooth="yes"/>
+      <point x="63" y="52"/>
+      <point x="107" y="-10"/>
+      <point x="216" y="-10" type="curve" smooth="yes"/>
+      <point x="261" y="-10"/>
+      <point x="300" y="1"/>
+      <point x="336" y="21" type="curve"/>
+      <point x="336" y="149" type="line"/>
+      <point x="309" y="137"/>
+      <point x="285" y="130"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/groups.plist b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/groups.plist
new file mode 100644
index 0000000..02bf8c6
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/groups.plist
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.kern1.T.left</key>
+	<array>
+		<string>T</string>
+	</array>
+	<key>public.kern1.d</key>
+	<array>
+		<string>l</string>
+	</array>
+	<key>public.kern1.n.left</key>
+	<array>
+		<string>n</string>
+	</array>
+	<key>public.kern1.o.left</key>
+	<array>
+		<string>o</string>
+	</array>
+	<key>public.kern1.t.left</key>
+	<array>
+		<string>t</string>
+	</array>
+	<key>public.kern2.T.right</key>
+	<array>
+		<string>T</string>
+	</array>
+	<key>public.kern2.b</key>
+	<array>
+		<string>l</string>
+	</array>
+	<key>public.kern2.n.right</key>
+	<array>
+		<string>n</string>
+	</array>
+	<key>public.kern2.o.right</key>
+	<array>
+		<string>o</string>
+	</array>
+	<key>public.kern2.s.right</key>
+	<array>
+		<string>s</string>
+	</array>
+	<key>public.kern2.t.right</key>
+	<array>
+		<string>t</string>
+	</array>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/kerning.plist b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/kerning.plist
new file mode 100644
index 0000000..0548061
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/kerning.plist
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.kern1.T.left</key>
+	<dict>
+		<key>public.kern2.T.right</key>
+		<integer>12</integer>
+		<key>public.kern2.n.right</key>
+		<integer>-30</integer>
+		<key>public.kern2.o.right</key>
+		<integer>-42</integer>
+		<key>public.kern2.s.right</key>
+		<integer>-36</integer>
+	</dict>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/layercontents.plist b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/layercontents.plist
new file mode 100644
index 0000000..cf95d35
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/layercontents.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<array>
+	<array>
+		<string>public.default</string>
+		<string>glyphs</string>
+	</array>
+</array>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/lib.plist b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/lib.plist
new file mode 100644
index 0000000..42d19d9
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/lib.plist
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.glyphOrder</key>
+	<array>
+		<string>F</string>
+		<string>T</string>
+		<string>l</string>
+		<string>n</string>
+		<string>o</string>
+		<string>s</string>
+		<string>t</string>
+	</array>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/metainfo.plist b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/metainfo.plist
new file mode 100644
index 0000000..632695b
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedBold.ufo/metainfo.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>creator</key>
+	<string>org.robofab.ufoLib</string>
+	<key>formatVersion</key>
+	<integer>3</integer>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/fontinfo.plist b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/fontinfo.plist
new file mode 100644
index 0000000..268b885
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/fontinfo.plist
@@ -0,0 +1,149 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>ascender</key>
+	<integer>760</integer>
+	<key>capHeight</key>
+	<integer>714</integer>
+	<key>copyright</key>
+	<string>Copyright 2015 Google Inc. All Rights Reserved.</string>
+	<key>descender</key>
+	<integer>-232</integer>
+	<key>familyName</key>
+	<string>Test Family 3</string>
+	<key>guidelines</key>
+	<array/>
+	<key>openTypeHeadCreated</key>
+	<string>2016/03/15 19:50:39</string>
+	<key>openTypeHheaAscender</key>
+	<integer>1069</integer>
+	<key>openTypeHheaDescender</key>
+	<integer>-293</integer>
+	<key>openTypeHheaLineGap</key>
+	<integer>0</integer>
+	<key>openTypeNameDescription</key>
+	<string>Designed by Monotype design team.</string>
+	<key>openTypeNameDesigner</key>
+	<string>Monotype Design Team</string>
+	<key>openTypeNameDesignerURL</key>
+	<string>http://www.monotype.com/studio</string>
+	<key>openTypeNameLicense</key>
+	<string>This Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License for the specific language, permissions and limitations governing your use of this Font Software.</string>
+	<key>openTypeNameLicenseURL</key>
+	<string>http://scripts.sil.org/OFL</string>
+	<key>openTypeNameManufacturer</key>
+	<string>Monotype Imaging Inc.</string>
+	<key>openTypeNameManufacturerURL</key>
+	<string>http://www.google.com/get/noto/</string>
+	<key>openTypeNameVersion</key>
+	<string>Version 1.902</string>
+	<key>openTypeOS2Panose</key>
+	<array>
+		<integer>2</integer>
+		<integer>11</integer>
+		<integer>2</integer>
+		<integer>6</integer>
+		<integer>4</integer>
+		<integer>5</integer>
+		<integer>4</integer>
+		<integer>2</integer>
+		<integer>2</integer>
+		<integer>4</integer>
+	</array>
+	<key>openTypeOS2Selection</key>
+	<array>
+		<integer>8</integer>
+	</array>
+	<key>openTypeOS2Type</key>
+	<array/>
+	<key>openTypeOS2TypoAscender</key>
+	<integer>1069</integer>
+	<key>openTypeOS2TypoDescender</key>
+	<integer>-293</integer>
+	<key>openTypeOS2TypoLineGap</key>
+	<integer>0</integer>
+	<key>openTypeOS2UnicodeRanges</key>
+	<array>
+		<integer>0</integer>
+		<integer>1</integer>
+		<integer>2</integer>
+		<integer>3</integer>
+		<integer>4</integer>
+		<integer>5</integer>
+		<integer>6</integer>
+		<integer>7</integer>
+		<integer>9</integer>
+		<integer>29</integer>
+		<integer>30</integer>
+		<integer>31</integer>
+		<integer>32</integer>
+		<integer>33</integer>
+		<integer>34</integer>
+		<integer>35</integer>
+		<integer>36</integer>
+		<integer>62</integer>
+		<integer>64</integer>
+		<integer>67</integer>
+		<integer>69</integer>
+		<integer>91</integer>
+		<integer>116</integer>
+	</array>
+	<key>openTypeOS2VendorID</key>
+	<string>GOOG</string>
+	<key>openTypeOS2WinAscent</key>
+	<integer>1069</integer>
+	<key>openTypeOS2WinDescent</key>
+	<integer>293</integer>
+	<key>postscriptBlueValues</key>
+	<array>
+		<integer>-15</integer>
+		<integer>0</integer>
+		<integer>527</integer>
+		<integer>537</integer>
+		<integer>559</integer>
+		<integer>569</integer>
+		<integer>714</integer>
+		<integer>724</integer>
+		<integer>760</integer>
+		<integer>765</integer>
+	</array>
+	<key>postscriptFamilyBlues</key>
+	<array/>
+	<key>postscriptFamilyOtherBlues</key>
+	<array/>
+	<key>postscriptOtherBlues</key>
+	<array>
+		<integer>-242</integer>
+		<integer>-232</integer>
+	</array>
+	<key>postscriptStemSnapH</key>
+	<array>
+		<integer>26</integer>
+	</array>
+	<key>postscriptStemSnapV</key>
+	<array>
+		<integer>26</integer>
+	</array>
+	<key>postscriptUnderlinePosition</key>
+	<integer>-100</integer>
+	<key>postscriptUnderlineThickness</key>
+	<integer>50</integer>
+	<key>styleMapFamilyName</key>
+	<string>Test Family 3 Condensed Light</string>
+	<key>styleMapStyleName</key>
+	<string>regular</string>
+	<key>styleName</key>
+	<string>Condensed Light</string>
+	<key>trademark</key>
+	<string>Noto is a trademark of Google Inc.</string>
+	<key>unitsPerEm</key>
+	<integer>1000</integer>
+	<key>versionMajor</key>
+	<integer>1</integer>
+	<key>versionMinor</key>
+	<integer>902</integer>
+	<key>xHeight</key>
+	<integer>527</integer>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/F_.glif b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/F_.glif
new file mode 100644
index 0000000..43c930e
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/F_.glif
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="F" format="2">
+  <advance width="352"/>
+  <unicode hex="0046"/>
+  <anchor x="176" y="0" name="bottom"/>
+  <anchor x="206" y="714" name="top"/>
+  <anchor x="332" y="714" name="topright"/>
+  <outline>
+    <contour>
+      <point x="104" y="0" type="line"/>
+      <point x="104" y="329" type="line"/>
+      <point x="322" y="329" type="line"/>
+      <point x="322" y="354" type="line"/>
+      <point x="104" y="354" type="line"/>
+      <point x="104" y="689" type="line"/>
+      <point x="334" y="689" type="line"/>
+      <point x="334" y="714" type="line"/>
+      <point x="78" y="714" type="line"/>
+      <point x="78" y="0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/T_.glif b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/T_.glif
new file mode 100644
index 0000000..cc943a3
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/T_.glif
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="T" format="2">
+  <advance width="336"/>
+  <unicode hex="0054"/>
+  <anchor x="168" y="0" name="bottom"/>
+  <anchor x="168" y="357" name="center"/>
+  <anchor x="168" y="714" name="top"/>
+  <anchor x="316" y="714" name="topright"/>
+  <outline>
+    <contour>
+      <point x="180" y="0" type="line"/>
+      <point x="180" y="689" type="line"/>
+      <point x="328" y="689" type="line"/>
+      <point x="328" y="714" type="line"/>
+      <point x="7" y="714" type="line"/>
+      <point x="7" y="689" type="line"/>
+      <point x="154" y="689" type="line"/>
+      <point x="154" y="0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/contents.plist b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/contents.plist
new file mode 100644
index 0000000..d50a055
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/contents.plist
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>F</key>
+	<string>F_.glif</string>
+	<key>T</key>
+	<string>T_.glif</string>
+	<key>l</key>
+	<string>l.glif</string>
+	<key>n</key>
+	<string>n.glif</string>
+	<key>o</key>
+	<string>o.glif</string>
+	<key>s</key>
+	<string>s.glif</string>
+	<key>t</key>
+	<string>t.glif</string>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/l.glif b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/l.glif
new file mode 100644
index 0000000..6689b25
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/l.glif
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="l" format="2">
+  <advance width="167"/>
+  <unicode hex="006C"/>
+  <anchor x="84" y="0" name="bottom"/>
+  <anchor x="84" y="264" name="center"/>
+  <anchor x="84" y="760" name="top"/>
+  <anchor x="118" y="760" name="topright"/>
+  <outline>
+    <contour>
+      <point x="97" y="0" type="line"/>
+      <point x="97" y="760" type="line"/>
+      <point x="71" y="760" type="line"/>
+      <point x="71" y="0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/layerinfo.plist b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/layerinfo.plist
new file mode 100644
index 0000000..0c67376
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/layerinfo.plist
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict/>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/n.glif b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/n.glif
new file mode 100644
index 0000000..b8b3116
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/n.glif
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="n" format="2">
+  <advance width="408"/>
+  <unicode hex="006E"/>
+  <anchor x="204" y="0" name="bottom"/>
+  <anchor x="204" y="527" name="top"/>
+  <anchor x="388" y="527" name="topright"/>
+  <outline>
+    <contour>
+      <point x="225" y="537" type="curve" smooth="yes"/>
+      <point x="145" y="537"/>
+      <point x="108" y="474"/>
+      <point x="94" y="415" type="curve"/>
+      <point x="92" y="415" type="line"/>
+      <point x="91" y="527" type="line"/>
+      <point x="71" y="527" type="line"/>
+      <point x="71" y="0" type="line"/>
+      <point x="97" y="0" type="line"/>
+      <point x="97" y="311" type="line" smooth="yes"/>
+      <point x="97" y="445"/>
+      <point x="156" y="513"/>
+      <point x="225" y="513" type="curve" smooth="yes"/>
+      <point x="279" y="513"/>
+      <point x="313" y="471"/>
+      <point x="313" y="365" type="curve" smooth="yes"/>
+      <point x="313" y="0" type="line"/>
+      <point x="339" y="0" type="line"/>
+      <point x="339" y="375" type="line" smooth="yes"/>
+      <point x="339" y="488"/>
+      <point x="298" y="537"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/o.glif b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/o.glif
new file mode 100644
index 0000000..a58d0c0
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/o.glif
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="o" format="2">
+  <advance width="403"/>
+  <unicode hex="006F"/>
+  <anchor x="202" y="0" name="bottom"/>
+  <anchor x="202" y="264" name="center"/>
+  <anchor x="202" y="527" name="top"/>
+  <anchor x="383" y="527" name="topright"/>
+  <outline>
+    <contour>
+      <point x="357" y="264" type="curve" smooth="yes"/>
+      <point x="357" y="433"/>
+      <point x="315" y="537"/>
+      <point x="202" y="537" type="curve" smooth="yes"/>
+      <point x="96" y="537"/>
+      <point x="46" y="444"/>
+      <point x="46" y="266" type="curve" smooth="yes"/>
+      <point x="46" y="80"/>
+      <point x="100" y="-10"/>
+      <point x="204" y="-10" type="curve" smooth="yes"/>
+      <point x="306" y="-10"/>
+      <point x="357" y="77"/>
+    </contour>
+    <contour>
+      <point x="72" y="266" type="curve" smooth="yes"/>
+      <point x="72" y="424"/>
+      <point x="110" y="512"/>
+      <point x="202" y="512" type="curve" smooth="yes"/>
+      <point x="298" y="512"/>
+      <point x="331" y="418"/>
+      <point x="331" y="265" type="curve" smooth="yes"/>
+      <point x="331" y="94"/>
+      <point x="291" y="15"/>
+      <point x="203" y="15" type="curve" smooth="yes"/>
+      <point x="113" y="15"/>
+      <point x="72" y="102"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/s.glif b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/s.glif
new file mode 100644
index 0000000..de8071b
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/s.glif
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="s" format="2">
+  <advance width="312"/>
+  <unicode hex="0073"/>
+  <anchor x="156" y="0" name="bottom"/>
+  <anchor x="156" y="527" name="top"/>
+  <anchor x="292" y="527" name="topright"/>
+  <outline>
+    <contour>
+      <point x="278" y="123" type="curve" smooth="yes"/>
+      <point x="278" y="210"/>
+      <point x="228" y="239"/>
+      <point x="162" y="280" type="curve" smooth="yes"/>
+      <point x="98" y="320"/>
+      <point x="65" y="349"/>
+      <point x="65" y="407" type="curve" smooth="yes"/>
+      <point x="65" y="475"/>
+      <point x="109" y="512"/>
+      <point x="174" y="512" type="curve" smooth="yes"/>
+      <point x="206" y="512"/>
+      <point x="240" y="503"/>
+      <point x="262" y="487" type="curve"/>
+      <point x="275" y="510" type="line"/>
+      <point x="249" y="527"/>
+      <point x="213" y="537"/>
+      <point x="175" y="537" type="curve" smooth="yes"/>
+      <point x="82" y="537"/>
+      <point x="39" y="479"/>
+      <point x="39" y="408" type="curve" smooth="yes"/>
+      <point x="39" y="330"/>
+      <point x="90" y="296"/>
+      <point x="157" y="254" type="curve" smooth="yes"/>
+      <point x="216" y="217"/>
+      <point x="251" y="195"/>
+      <point x="251" y="125" type="curve" smooth="yes"/>
+      <point x="251" y="56"/>
+      <point x="217" y="16"/>
+      <point x="147" y="16" type="curve" smooth="yes"/>
+      <point x="105" y="16"/>
+      <point x="65" y="31"/>
+      <point x="36" y="52" type="curve"/>
+      <point x="36" y="21" type="line"/>
+      <point x="59" y="6"/>
+      <point x="98" y="-10"/>
+      <point x="147" y="-10" type="curve" smooth="yes"/>
+      <point x="235" y="-10"/>
+      <point x="278" y="43"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/t.glif b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/t.glif
new file mode 100644
index 0000000..ac82427
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/glyphs/t.glif
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="t" format="2">
+  <advance width="209"/>
+  <unicode hex="0074"/>
+  <anchor x="134" y="0" name="bottom"/>
+  <anchor x="105" y="264" name="center"/>
+  <anchor x="85" y="656" name="top"/>
+  <anchor x="189" y="656" name="topright"/>
+  <outline>
+    <contour>
+      <point x="147" y="14" type="curve" smooth="yes"/>
+      <point x="107" y="14"/>
+      <point x="97" y="44"/>
+      <point x="97" y="108" type="curve" smooth="yes"/>
+      <point x="97" y="503" type="line"/>
+      <point x="196" y="503" type="line"/>
+      <point x="196" y="527" type="line"/>
+      <point x="97" y="527" type="line"/>
+      <point x="97" y="656" type="line"/>
+      <point x="76" y="656" type="line"/>
+      <point x="69" y="528" type="line"/>
+      <point x="21" y="521" type="line"/>
+      <point x="21" y="503" type="line"/>
+      <point x="71" y="503" type="line"/>
+      <point x="71" y="112" type="line" smooth="yes"/>
+      <point x="71" y="32"/>
+      <point x="85" y="-10"/>
+      <point x="146" y="-10" type="curve" smooth="yes"/>
+      <point x="165" y="-10"/>
+      <point x="179" y="-6"/>
+      <point x="192" y="0" type="curve"/>
+      <point x="192" y="25" type="line"/>
+      <point x="182" y="19"/>
+      <point x="164" y="14"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/groups.plist b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/groups.plist
new file mode 100644
index 0000000..02bf8c6
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/groups.plist
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.kern1.T.left</key>
+	<array>
+		<string>T</string>
+	</array>
+	<key>public.kern1.d</key>
+	<array>
+		<string>l</string>
+	</array>
+	<key>public.kern1.n.left</key>
+	<array>
+		<string>n</string>
+	</array>
+	<key>public.kern1.o.left</key>
+	<array>
+		<string>o</string>
+	</array>
+	<key>public.kern1.t.left</key>
+	<array>
+		<string>t</string>
+	</array>
+	<key>public.kern2.T.right</key>
+	<array>
+		<string>T</string>
+	</array>
+	<key>public.kern2.b</key>
+	<array>
+		<string>l</string>
+	</array>
+	<key>public.kern2.n.right</key>
+	<array>
+		<string>n</string>
+	</array>
+	<key>public.kern2.o.right</key>
+	<array>
+		<string>o</string>
+	</array>
+	<key>public.kern2.s.right</key>
+	<array>
+		<string>s</string>
+	</array>
+	<key>public.kern2.t.right</key>
+	<array>
+		<string>t</string>
+	</array>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/kerning.plist b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/kerning.plist
new file mode 100644
index 0000000..0548061
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/kerning.plist
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.kern1.T.left</key>
+	<dict>
+		<key>public.kern2.T.right</key>
+		<integer>12</integer>
+		<key>public.kern2.n.right</key>
+		<integer>-30</integer>
+		<key>public.kern2.o.right</key>
+		<integer>-42</integer>
+		<key>public.kern2.s.right</key>
+		<integer>-36</integer>
+	</dict>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/layercontents.plist b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/layercontents.plist
new file mode 100644
index 0000000..cf95d35
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/layercontents.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<array>
+	<array>
+		<string>public.default</string>
+		<string>glyphs</string>
+	</array>
+</array>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/lib.plist b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/lib.plist
new file mode 100644
index 0000000..42d19d9
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/lib.plist
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.glyphOrder</key>
+	<array>
+		<string>F</string>
+		<string>T</string>
+		<string>l</string>
+		<string>n</string>
+		<string>o</string>
+		<string>s</string>
+		<string>t</string>
+	</array>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/metainfo.plist b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/metainfo.plist
new file mode 100644
index 0000000..632695b
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedLight.ufo/metainfo.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>creator</key>
+	<string>org.robofab.ufoLib</string>
+	<key>formatVersion</key>
+	<integer>3</integer>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/fontinfo.plist b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/fontinfo.plist
new file mode 100644
index 0000000..4f8649f
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/fontinfo.plist
@@ -0,0 +1,153 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>ascender</key>
+	<integer>760</integer>
+	<key>capHeight</key>
+	<integer>714</integer>
+	<key>copyright</key>
+	<string>Copyright 2015 Google Inc. All Rights Reserved.</string>
+	<key>descender</key>
+	<integer>-240</integer>
+	<key>familyName</key>
+	<string>Test Family 3</string>
+	<key>guidelines</key>
+	<array/>
+	<key>openTypeHeadCreated</key>
+	<string>2016/03/15 19:50:39</string>
+	<key>openTypeHheaAscender</key>
+	<integer>1069</integer>
+	<key>openTypeHheaDescender</key>
+	<integer>-293</integer>
+	<key>openTypeHheaLineGap</key>
+	<integer>0</integer>
+	<key>openTypeNameDescription</key>
+	<string>Designed by Monotype design team.</string>
+	<key>openTypeNameDesigner</key>
+	<string>Monotype Design Team</string>
+	<key>openTypeNameDesignerURL</key>
+	<string>http://www.monotype.com/studio</string>
+	<key>openTypeNameLicense</key>
+	<string>This Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License for the specific language, permissions and limitations governing your use of this Font Software.</string>
+	<key>openTypeNameLicenseURL</key>
+	<string>http://scripts.sil.org/OFL</string>
+	<key>openTypeNameManufacturer</key>
+	<string>Monotype Imaging Inc.</string>
+	<key>openTypeNameManufacturerURL</key>
+	<string>http://www.google.com/get/noto/</string>
+	<key>openTypeNameVersion</key>
+	<string>Version 1.902</string>
+	<key>openTypeOS2Panose</key>
+	<array>
+		<integer>2</integer>
+		<integer>11</integer>
+		<integer>8</integer>
+		<integer>6</integer>
+		<integer>4</integer>
+		<integer>5</integer>
+		<integer>4</integer>
+		<integer>2</integer>
+		<integer>2</integer>
+		<integer>4</integer>
+	</array>
+	<key>openTypeOS2Selection</key>
+	<array>
+		<integer>8</integer>
+	</array>
+	<key>openTypeOS2Type</key>
+	<array/>
+	<key>openTypeOS2TypoAscender</key>
+	<integer>1069</integer>
+	<key>openTypeOS2TypoDescender</key>
+	<integer>-293</integer>
+	<key>openTypeOS2TypoLineGap</key>
+	<integer>0</integer>
+	<key>openTypeOS2UnicodeRanges</key>
+	<array>
+		<integer>0</integer>
+		<integer>1</integer>
+		<integer>2</integer>
+		<integer>3</integer>
+		<integer>4</integer>
+		<integer>5</integer>
+		<integer>6</integer>
+		<integer>7</integer>
+		<integer>9</integer>
+		<integer>29</integer>
+		<integer>30</integer>
+		<integer>31</integer>
+		<integer>32</integer>
+		<integer>33</integer>
+		<integer>34</integer>
+		<integer>35</integer>
+		<integer>36</integer>
+		<integer>62</integer>
+		<integer>64</integer>
+		<integer>67</integer>
+		<integer>69</integer>
+		<integer>91</integer>
+		<integer>116</integer>
+	</array>
+	<key>openTypeOS2VendorID</key>
+	<string>GOOG</string>
+	<key>openTypeOS2WinAscent</key>
+	<integer>1069</integer>
+	<key>openTypeOS2WinDescent</key>
+	<integer>293</integer>
+	<key>postscriptBlueValues</key>
+	<array>
+		<integer>-15</integer>
+		<integer>0</integer>
+		<integer>547</integer>
+		<integer>558</integer>
+		<integer>582</integer>
+		<integer>592</integer>
+		<integer>714</integer>
+		<integer>726</integer>
+		<integer>760</integer>
+		<integer>766</integer>
+	</array>
+	<key>postscriptFamilyBlues</key>
+	<array/>
+	<key>postscriptFamilyOtherBlues</key>
+	<array/>
+	<key>postscriptOtherBlues</key>
+	<array>
+		<integer>-256</integer>
+		<integer>-240</integer>
+	</array>
+	<key>postscriptStemSnapH</key>
+	<array>
+		<integer>95</integer>
+		<integer>108</integer>
+		<integer>119</integer>
+	</array>
+	<key>postscriptStemSnapV</key>
+	<array>
+		<integer>97</integer>
+		<integer>121</integer>
+		<integer>137</integer>
+	</array>
+	<key>postscriptUnderlinePosition</key>
+	<integer>-100</integer>
+	<key>postscriptUnderlineThickness</key>
+	<integer>50</integer>
+	<key>styleMapFamilyName</key>
+	<string>Test Family 3 Condensed SemiBold</string>
+	<key>styleMapStyleName</key>
+	<string>regular</string>
+	<key>styleName</key>
+	<string>Condensed SemiBold</string>
+	<key>trademark</key>
+	<string>Noto is a trademark of Google Inc.</string>
+	<key>unitsPerEm</key>
+	<integer>1000</integer>
+	<key>versionMajor</key>
+	<integer>1</integer>
+	<key>versionMinor</key>
+	<integer>902</integer>
+	<key>xHeight</key>
+	<integer>547</integer>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/F_.glif b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/F_.glif
new file mode 100644
index 0000000..c3578a3
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/F_.glif
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="F" format="2">
+  <advance width="414"/>
+  <unicode hex="0046"/>
+  <anchor x="207" y="0" name="bottom"/>
+  <anchor x="227" y="714" name="top"/>
+  <anchor x="394" y="714" name="topright"/>
+  <outline>
+    <contour>
+      <point x="201" y="0" type="line"/>
+      <point x="201" y="286" type="line"/>
+      <point x="371" y="286" type="line"/>
+      <point x="371" y="403" type="line"/>
+      <point x="201" y="403" type="line"/>
+      <point x="201" y="597" type="line"/>
+      <point x="383" y="597" type="line"/>
+      <point x="383" y="714" type="line"/>
+      <point x="64" y="714" type="line"/>
+      <point x="64" y="0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/T_.glif b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/T_.glif
new file mode 100644
index 0000000..32e1ec3
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/T_.glif
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="T" format="2">
+  <advance width="429"/>
+  <unicode hex="0054"/>
+  <anchor x="215" y="0" name="bottom"/>
+  <anchor x="215" y="357" name="center"/>
+  <anchor x="215" y="714" name="top"/>
+  <anchor x="409" y="714" name="topright"/>
+  <outline>
+    <contour>
+      <point x="282" y="0" type="line"/>
+      <point x="282" y="595" type="line"/>
+      <point x="414" y="595" type="line"/>
+      <point x="414" y="714" type="line"/>
+      <point x="14" y="714" type="line"/>
+      <point x="14" y="595" type="line"/>
+      <point x="147" y="595" type="line"/>
+      <point x="147" y="0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/contents.plist b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/contents.plist
new file mode 100644
index 0000000..d50a055
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/contents.plist
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>F</key>
+	<string>F_.glif</string>
+	<key>T</key>
+	<string>T_.glif</string>
+	<key>l</key>
+	<string>l.glif</string>
+	<key>n</key>
+	<string>n.glif</string>
+	<key>o</key>
+	<string>o.glif</string>
+	<key>s</key>
+	<string>s.glif</string>
+	<key>t</key>
+	<string>t.glif</string>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/l.glif b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/l.glif
new file mode 100644
index 0000000..02b0e36
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/l.glif
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="l" format="2">
+  <advance width="242"/>
+  <unicode hex="006C"/>
+  <anchor x="122" y="0" name="bottom"/>
+  <anchor x="121" y="274" name="center"/>
+  <anchor x="122" y="760" name="top"/>
+  <anchor x="210" y="760" name="topright"/>
+  <outline>
+    <contour>
+      <point x="186" y="0" type="line"/>
+      <point x="186" y="760" type="line"/>
+      <point x="56" y="760" type="line"/>
+      <point x="56" y="0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/layerinfo.plist b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/layerinfo.plist
new file mode 100644
index 0000000..0c67376
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/layerinfo.plist
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict/>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/n.glif b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/n.glif
new file mode 100644
index 0000000..10c02f2
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/n.glif
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="n" format="2">
+  <advance width="501"/>
+  <unicode hex="006E"/>
+  <anchor x="251" y="0" name="bottom"/>
+  <anchor x="260" y="547" name="top"/>
+  <anchor x="468" y="547" name="topright"/>
+  <outline>
+    <contour>
+      <point x="302" y="557" type="curve" smooth="yes"/>
+      <point x="247" y="557"/>
+      <point x="205" y="529"/>
+      <point x="181" y="477" type="curve"/>
+      <point x="173" y="477" type="line"/>
+      <point x="159" y="547" type="line"/>
+      <point x="56" y="547" type="line"/>
+      <point x="56" y="0" type="line"/>
+      <point x="186" y="0" type="line"/>
+      <point x="186" y="263" type="line" smooth="yes"/>
+      <point x="186" y="393"/>
+      <point x="205" y="441"/>
+      <point x="260" y="441" type="curve" smooth="yes"/>
+      <point x="303" y="441"/>
+      <point x="316" y="401"/>
+      <point x="316" y="324" type="curve" smooth="yes"/>
+      <point x="316" y="0" type="line"/>
+      <point x="446" y="0" type="line"/>
+      <point x="446" y="362" type="line" smooth="yes"/>
+      <point x="446" y="489"/>
+      <point x="394" y="557"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/o.glif b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/o.glif
new file mode 100644
index 0000000..c4b03fc
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/o.glif
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="o" format="2">
+  <advance width="488"/>
+  <unicode hex="006F"/>
+  <anchor x="244" y="0" name="bottom"/>
+  <anchor x="244" y="274" name="center"/>
+  <anchor x="244" y="547" name="top"/>
+  <anchor x="468" y="547" name="topright"/>
+  <outline>
+    <contour>
+      <point x="452" y="275" type="curve" smooth="yes"/>
+      <point x="452" y="457"/>
+      <point x="371" y="557"/>
+      <point x="245" y="557" type="curve" smooth="yes"/>
+      <point x="98" y="557"/>
+      <point x="36" y="440"/>
+      <point x="36" y="275" type="curve" smooth="yes"/>
+      <point x="36" y="120"/>
+      <point x="101" y="-10"/>
+      <point x="243" y="-10" type="curve" smooth="yes"/>
+      <point x="395" y="-10"/>
+      <point x="452" y="123"/>
+    </contour>
+    <contour>
+      <point x="168" y="273" type="curve" smooth="yes"/>
+      <point x="168" y="391"/>
+      <point x="191" y="447"/>
+      <point x="244" y="447" type="curve" smooth="yes"/>
+      <point x="298" y="447"/>
+      <point x="319" y="390"/>
+      <point x="319" y="275" type="curve" smooth="yes"/>
+      <point x="319" y="158"/>
+      <point x="298" y="100"/>
+      <point x="244" y="100" type="curve" smooth="yes"/>
+      <point x="191" y="100"/>
+      <point x="168" y="159"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/s.glif b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/s.glif
new file mode 100644
index 0000000..9ab6232
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/s.glif
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="s" format="2">
+  <advance width="374"/>
+  <unicode hex="0073"/>
+  <anchor x="174" y="0" name="bottom"/>
+  <anchor x="194" y="547" name="top"/>
+  <anchor x="336" y="547" name="topright"/>
+  <outline>
+    <contour>
+      <point x="347" y="160" type="curve" smooth="yes"/>
+      <point x="347" y="244"/>
+      <point x="301" y="289"/>
+      <point x="234" y="328" type="curve" smooth="yes"/>
+      <point x="166" y="369"/>
+      <point x="154" y="380"/>
+      <point x="154" y="408" type="curve" smooth="yes"/>
+      <point x="154" y="435"/>
+      <point x="171" y="451"/>
+      <point x="203" y="451" type="curve" smooth="yes"/>
+      <point x="239" y="451"/>
+      <point x="274" y="435"/>
+      <point x="305" y="414" type="curve"/>
+      <point x="341" y="514" type="line"/>
+      <point x="295" y="542"/>
+      <point x="249" y="557"/>
+      <point x="196" y="557" type="curve" smooth="yes"/>
+      <point x="92" y="557"/>
+      <point x="28" y="498"/>
+      <point x="28" y="402" type="curve" smooth="yes"/>
+      <point x="28" y="323"/>
+      <point x="67" y="272"/>
+      <point x="135" y="235" type="curve" smooth="yes"/>
+      <point x="205" y="199"/>
+      <point x="217" y="180"/>
+      <point x="217" y="153" type="curve" smooth="yes"/>
+      <point x="217" y="120"/>
+      <point x="197" y="103"/>
+      <point x="160" y="103" type="curve" smooth="yes"/>
+      <point x="113" y="103"/>
+      <point x="64" y="123"/>
+      <point x="29" y="147" type="curve"/>
+      <point x="29" y="21" type="line"/>
+      <point x="71" y="0"/>
+      <point x="119" y="-10"/>
+      <point x="172" y="-10" type="curve" smooth="yes"/>
+      <point x="282" y="-10"/>
+      <point x="347" y="49"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/t.glif b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/t.glif
new file mode 100644
index 0000000..d43759a
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/glyphs/t.glif
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="t" format="2">
+  <advance width="317"/>
+  <unicode hex="0074"/>
+  <anchor x="189" y="0" name="bottom"/>
+  <anchor x="159" y="274" name="center"/>
+  <anchor x="138" y="662" name="top"/>
+  <anchor x="293" y="662" name="topright"/>
+  <outline>
+    <contour>
+      <point x="239" y="104" type="curve" smooth="yes"/>
+      <point x="207" y="104"/>
+      <point x="195" y="125"/>
+      <point x="195" y="168" type="curve" smooth="yes"/>
+      <point x="195" y="439" type="line"/>
+      <point x="293" y="439" type="line"/>
+      <point x="293" y="547" type="line"/>
+      <point x="195" y="547" type="line"/>
+      <point x="195" y="662" type="line"/>
+      <point x="106" y="662" type="line"/>
+      <point x="75" y="543" type="line"/>
+      <point x="11" y="509" type="line"/>
+      <point x="11" y="439" type="line"/>
+      <point x="65" y="439" type="line"/>
+      <point x="65" y="163" type="line" smooth="yes"/>
+      <point x="65" y="46"/>
+      <point x="104" y="-10"/>
+      <point x="198" y="-10" type="curve" smooth="yes"/>
+      <point x="237" y="-10"/>
+      <point x="271" y="-1"/>
+      <point x="300" y="15" type="curve"/>
+      <point x="300" y="119" type="line"/>
+      <point x="278" y="109"/>
+      <point x="257" y="104"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/groups.plist b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/groups.plist
new file mode 100644
index 0000000..02bf8c6
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/groups.plist
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.kern1.T.left</key>
+	<array>
+		<string>T</string>
+	</array>
+	<key>public.kern1.d</key>
+	<array>
+		<string>l</string>
+	</array>
+	<key>public.kern1.n.left</key>
+	<array>
+		<string>n</string>
+	</array>
+	<key>public.kern1.o.left</key>
+	<array>
+		<string>o</string>
+	</array>
+	<key>public.kern1.t.left</key>
+	<array>
+		<string>t</string>
+	</array>
+	<key>public.kern2.T.right</key>
+	<array>
+		<string>T</string>
+	</array>
+	<key>public.kern2.b</key>
+	<array>
+		<string>l</string>
+	</array>
+	<key>public.kern2.n.right</key>
+	<array>
+		<string>n</string>
+	</array>
+	<key>public.kern2.o.right</key>
+	<array>
+		<string>o</string>
+	</array>
+	<key>public.kern2.s.right</key>
+	<array>
+		<string>s</string>
+	</array>
+	<key>public.kern2.t.right</key>
+	<array>
+		<string>t</string>
+	</array>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/kerning.plist b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/kerning.plist
new file mode 100644
index 0000000..0548061
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/kerning.plist
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.kern1.T.left</key>
+	<dict>
+		<key>public.kern2.T.right</key>
+		<integer>12</integer>
+		<key>public.kern2.n.right</key>
+		<integer>-30</integer>
+		<key>public.kern2.o.right</key>
+		<integer>-42</integer>
+		<key>public.kern2.s.right</key>
+		<integer>-36</integer>
+	</dict>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/layercontents.plist b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/layercontents.plist
new file mode 100644
index 0000000..cf95d35
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/layercontents.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<array>
+	<array>
+		<string>public.default</string>
+		<string>glyphs</string>
+	</array>
+</array>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/lib.plist b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/lib.plist
new file mode 100644
index 0000000..42d19d9
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/lib.plist
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.glyphOrder</key>
+	<array>
+		<string>F</string>
+		<string>T</string>
+		<string>l</string>
+		<string>n</string>
+		<string>o</string>
+		<string>s</string>
+		<string>t</string>
+	</array>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/metainfo.plist b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/metainfo.plist
new file mode 100644
index 0000000..632695b
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-CondensedSemiBold.ufo/metainfo.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>creator</key>
+	<string>org.robofab.ufoLib</string>
+	<key>formatVersion</key>
+	<integer>3</integer>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/fontinfo.plist b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/fontinfo.plist
new file mode 100644
index 0000000..575f306
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/fontinfo.plist
@@ -0,0 +1,149 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>ascender</key>
+	<integer>760</integer>
+	<key>capHeight</key>
+	<integer>714</integer>
+	<key>copyright</key>
+	<string>Copyright 2015 Google Inc. All Rights Reserved.</string>
+	<key>descender</key>
+	<integer>-240</integer>
+	<key>familyName</key>
+	<string>Test Family 3</string>
+	<key>guidelines</key>
+	<array/>
+	<key>openTypeHeadCreated</key>
+	<string>2016/03/15 19:50:39</string>
+	<key>openTypeHheaAscender</key>
+	<integer>1069</integer>
+	<key>openTypeHheaDescender</key>
+	<integer>-293</integer>
+	<key>openTypeHheaLineGap</key>
+	<integer>0</integer>
+	<key>openTypeNameDescription</key>
+	<string>Designed by Monotype design team.</string>
+	<key>openTypeNameDesigner</key>
+	<string>Monotype Design Team</string>
+	<key>openTypeNameDesignerURL</key>
+	<string>http://www.monotype.com/studio</string>
+	<key>openTypeNameLicense</key>
+	<string>This Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License for the specific language, permissions and limitations governing your use of this Font Software.</string>
+	<key>openTypeNameLicenseURL</key>
+	<string>http://scripts.sil.org/OFL</string>
+	<key>openTypeNameManufacturer</key>
+	<string>Monotype Imaging Inc.</string>
+	<key>openTypeNameManufacturerURL</key>
+	<string>http://www.google.com/get/noto/</string>
+	<key>openTypeNameVersion</key>
+	<string>Version 1.902</string>
+	<key>openTypeOS2Panose</key>
+	<array>
+		<integer>2</integer>
+		<integer>11</integer>
+		<integer>2</integer>
+		<integer>2</integer>
+		<integer>4</integer>
+		<integer>5</integer>
+		<integer>4</integer>
+		<integer>2</integer>
+		<integer>2</integer>
+		<integer>4</integer>
+	</array>
+	<key>openTypeOS2Selection</key>
+	<array>
+		<integer>8</integer>
+	</array>
+	<key>openTypeOS2Type</key>
+	<array/>
+	<key>openTypeOS2TypoAscender</key>
+	<integer>1069</integer>
+	<key>openTypeOS2TypoDescender</key>
+	<integer>-293</integer>
+	<key>openTypeOS2TypoLineGap</key>
+	<integer>0</integer>
+	<key>openTypeOS2UnicodeRanges</key>
+	<array>
+		<integer>0</integer>
+		<integer>1</integer>
+		<integer>2</integer>
+		<integer>3</integer>
+		<integer>4</integer>
+		<integer>5</integer>
+		<integer>6</integer>
+		<integer>7</integer>
+		<integer>9</integer>
+		<integer>29</integer>
+		<integer>30</integer>
+		<integer>31</integer>
+		<integer>32</integer>
+		<integer>33</integer>
+		<integer>34</integer>
+		<integer>35</integer>
+		<integer>36</integer>
+		<integer>62</integer>
+		<integer>64</integer>
+		<integer>67</integer>
+		<integer>69</integer>
+		<integer>91</integer>
+		<integer>116</integer>
+	</array>
+	<key>openTypeOS2VendorID</key>
+	<string>GOOG</string>
+	<key>openTypeOS2WinAscent</key>
+	<integer>1069</integer>
+	<key>openTypeOS2WinDescent</key>
+	<integer>293</integer>
+	<key>postscriptBlueValues</key>
+	<array>
+		<integer>-15</integer>
+		<integer>0</integer>
+		<integer>528</integer>
+		<integer>537</integer>
+		<integer>559</integer>
+		<integer>569</integer>
+		<integer>714</integer>
+		<integer>724</integer>
+		<integer>760</integer>
+		<integer>766</integer>
+	</array>
+	<key>postscriptFamilyBlues</key>
+	<array/>
+	<key>postscriptFamilyOtherBlues</key>
+	<array/>
+	<key>postscriptOtherBlues</key>
+	<array>
+		<integer>-256</integer>
+		<integer>-240</integer>
+	</array>
+	<key>postscriptStemSnapH</key>
+	<array>
+		<integer>25</integer>
+	</array>
+	<key>postscriptStemSnapV</key>
+	<array>
+		<integer>26</integer>
+	</array>
+	<key>postscriptUnderlinePosition</key>
+	<integer>-100</integer>
+	<key>postscriptUnderlineThickness</key>
+	<integer>50</integer>
+	<key>styleMapFamilyName</key>
+	<string>Test Family 3 Light</string>
+	<key>styleMapStyleName</key>
+	<string>regular</string>
+	<key>styleName</key>
+	<string>Light</string>
+	<key>trademark</key>
+	<string>Noto is a trademark of Google Inc.</string>
+	<key>unitsPerEm</key>
+	<integer>1000</integer>
+	<key>versionMajor</key>
+	<integer>1</integer>
+	<key>versionMinor</key>
+	<integer>902</integer>
+	<key>xHeight</key>
+	<integer>528</integer>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/F_.glif b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/F_.glif
new file mode 100644
index 0000000..6cc64b0
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/F_.glif
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="F" format="2">
+  <advance width="492"/>
+  <unicode hex="0046"/>
+  <anchor x="246" y="0" name="bottom"/>
+  <anchor x="296" y="714" name="top"/>
+  <anchor x="472" y="714" name="topright"/>
+  <outline>
+    <contour>
+      <point x="129" y="0" type="line"/>
+      <point x="129" y="329" type="line"/>
+      <point x="470" y="329" type="line"/>
+      <point x="470" y="354" type="line"/>
+      <point x="129" y="354" type="line"/>
+      <point x="129" y="689" type="line"/>
+      <point x="489" y="689" type="line"/>
+      <point x="489" y="714" type="line"/>
+      <point x="103" y="714" type="line"/>
+      <point x="103" y="0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/T_.glif b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/T_.glif
new file mode 100644
index 0000000..2d18271
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/T_.glif
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="T" format="2">
+  <advance width="505"/>
+  <unicode hex="0054"/>
+  <anchor x="253" y="0" name="bottom"/>
+  <anchor x="253" y="357" name="center"/>
+  <anchor x="253" y="714" name="top"/>
+  <anchor x="485" y="714" name="topright"/>
+  <outline>
+    <contour>
+      <point x="265" y="0" type="line"/>
+      <point x="265" y="689" type="line"/>
+      <point x="503" y="689" type="line"/>
+      <point x="503" y="714" type="line"/>
+      <point x="2" y="714" type="line"/>
+      <point x="2" y="689" type="line"/>
+      <point x="239" y="689" type="line"/>
+      <point x="239" y="0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/contents.plist b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/contents.plist
new file mode 100644
index 0000000..d50a055
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/contents.plist
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>F</key>
+	<string>F_.glif</string>
+	<key>T</key>
+	<string>T_.glif</string>
+	<key>l</key>
+	<string>l.glif</string>
+	<key>n</key>
+	<string>n.glif</string>
+	<key>o</key>
+	<string>o.glif</string>
+	<key>s</key>
+	<string>s.glif</string>
+	<key>t</key>
+	<string>t.glif</string>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/l.glif b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/l.glif
new file mode 100644
index 0000000..a7c1e80
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/l.glif
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="l" format="2">
+  <advance width="207"/>
+  <unicode hex="006C"/>
+  <anchor x="104" y="0" name="bottom"/>
+  <anchor x="104" y="264" name="center"/>
+  <anchor x="104" y="760" name="top"/>
+  <anchor x="187" y="760" name="topright"/>
+  <outline>
+    <contour>
+      <point x="117" y="0" type="line"/>
+      <point x="117" y="760" type="line"/>
+      <point x="91" y="760" type="line"/>
+      <point x="91" y="0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/layerinfo.plist b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/layerinfo.plist
new file mode 100644
index 0000000..0c67376
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/layerinfo.plist
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict/>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/n.glif b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/n.glif
new file mode 100644
index 0000000..65ea31d
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/n.glif
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="n" format="2">
+  <advance width="573"/>
+  <unicode hex="006E"/>
+  <anchor x="287" y="0" name="bottom"/>
+  <anchor x="287" y="528" name="top"/>
+  <anchor x="553" y="528" name="topright"/>
+  <outline>
+    <contour>
+      <point x="309" y="538" type="curve" smooth="yes"/>
+      <point x="197" y="538"/>
+      <point x="140" y="478"/>
+      <point x="117" y="417" type="curve"/>
+      <point x="115" y="417" type="line"/>
+      <point x="111" y="528" type="line"/>
+      <point x="90" y="528" type="line"/>
+      <point x="90" y="0" type="line"/>
+      <point x="116" y="0" type="line"/>
+      <point x="116" y="302" type="line" smooth="yes"/>
+      <point x="116" y="446"/>
+      <point x="194" y="513"/>
+      <point x="309" y="513" type="curve" smooth="yes"/>
+      <point x="406" y="513"/>
+      <point x="463" y="462"/>
+      <point x="463" y="345" type="curve" smooth="yes"/>
+      <point x="463" y="0" type="line"/>
+      <point x="489" y="0" type="line"/>
+      <point x="489" y="346" type="line" smooth="yes"/>
+      <point x="489" y="477"/>
+      <point x="423" y="538"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/o.glif b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/o.glif
new file mode 100644
index 0000000..410754c
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/o.glif
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="o" format="2">
+  <advance width="575"/>
+  <unicode hex="006F"/>
+  <anchor x="288" y="0" name="bottom"/>
+  <anchor x="288" y="264" name="center"/>
+  <anchor x="288" y="528" name="top"/>
+  <anchor x="555" y="528" name="topright"/>
+  <outline>
+    <contour>
+      <point x="515" y="264" type="curve" smooth="yes"/>
+      <point x="515" y="417"/>
+      <point x="451" y="538"/>
+      <point x="292" y="538" type="curve" smooth="yes"/>
+      <point x="145" y="538"/>
+      <point x="59" y="432"/>
+      <point x="59" y="264" type="curve" smooth="yes"/>
+      <point x="59" y="107"/>
+      <point x="134" y="-10"/>
+      <point x="286" y="-10" type="curve" smooth="yes"/>
+      <point x="443" y="-10"/>
+      <point x="515" y="109"/>
+    </contour>
+    <contour>
+      <point x="86" y="264" type="curve" smooth="yes"/>
+      <point x="86" y="420"/>
+      <point x="160" y="513"/>
+      <point x="292" y="513" type="curve" smooth="yes"/>
+      <point x="433" y="513"/>
+      <point x="488" y="402"/>
+      <point x="488" y="264" type="curve" smooth="yes"/>
+      <point x="488" y="119"/>
+      <point x="426" y="15"/>
+      <point x="286" y="15" type="curve" smooth="yes"/>
+      <point x="151" y="15"/>
+      <point x="86" y="117"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/s.glif b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/s.glif
new file mode 100644
index 0000000..34cd9d4
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/s.glif
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="s" format="2">
+  <advance width="458"/>
+  <unicode hex="0073"/>
+  <anchor x="229" y="0" name="bottom"/>
+  <anchor x="229" y="528" name="top"/>
+  <anchor x="438" y="528" name="topright"/>
+  <outline>
+    <contour>
+      <point x="411" y="134" type="curve" smooth="yes"/>
+      <point x="411" y="235"/>
+      <point x="321" y="258"/>
+      <point x="233" y="286" type="curve" smooth="yes"/>
+      <point x="152" y="312"/>
+      <point x="83" y="325"/>
+      <point x="83" y="407" type="curve" smooth="yes"/>
+      <point x="83" y="478"/>
+      <point x="142" y="513"/>
+      <point x="238" y="513" type="curve" smooth="yes"/>
+      <point x="291" y="513"/>
+      <point x="350" y="501"/>
+      <point x="387" y="483" type="curve"/>
+      <point x="398" y="508" type="line"/>
+      <point x="356" y="525"/>
+      <point x="301" y="538"/>
+      <point x="238" y="538" type="curve" smooth="yes"/>
+      <point x="126" y="538"/>
+      <point x="56" y="489"/>
+      <point x="56" y="407" type="curve" smooth="yes"/>
+      <point x="56" y="309"/>
+      <point x="131" y="290"/>
+      <point x="224" y="262" type="curve" smooth="yes"/>
+      <point x="312" y="236"/>
+      <point x="384" y="212"/>
+      <point x="384" y="134" type="curve" smooth="yes"/>
+      <point x="384" y="60"/>
+      <point x="334" y="15"/>
+      <point x="213" y="15" type="curve" smooth="yes"/>
+      <point x="151" y="15"/>
+      <point x="92" y="28"/>
+      <point x="39" y="54" type="curve"/>
+      <point x="39" y="24" type="line"/>
+      <point x="77" y="7"/>
+      <point x="140" y="-10"/>
+      <point x="213" y="-10" type="curve" smooth="yes"/>
+      <point x="345" y="-10"/>
+      <point x="411" y="45"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/t.glif b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/t.glif
new file mode 100644
index 0000000..9874dc9
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/glyphs/t.glif
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="t" format="2">
+  <advance width="319"/>
+  <unicode hex="0074"/>
+  <anchor x="191" y="0" name="bottom"/>
+  <anchor x="160" y="264" name="center"/>
+  <anchor x="109" y="659" name="top"/>
+  <anchor x="299" y="659" name="topright"/>
+  <outline>
+    <contour>
+      <point x="212" y="15" type="curve" smooth="yes"/>
+      <point x="138" y="15"/>
+      <point x="117" y="61"/>
+      <point x="117" y="143" type="curve" smooth="yes"/>
+      <point x="117" y="503" type="line"/>
+      <point x="289" y="503" type="line"/>
+      <point x="289" y="528" type="line"/>
+      <point x="117" y="528" type="line"/>
+      <point x="117" y="659" type="line"/>
+      <point x="96" y="659" type="line"/>
+      <point x="90" y="528" type="line"/>
+      <point x="10" y="525" type="line"/>
+      <point x="10" y="503" type="line"/>
+      <point x="91" y="503" type="line"/>
+      <point x="91" y="140" type="line" smooth="yes"/>
+      <point x="91" y="47"/>
+      <point x="119" y="-10"/>
+      <point x="212" y="-10" type="curve" smooth="yes"/>
+      <point x="247" y="-10"/>
+      <point x="269" y="-4"/>
+      <point x="291" y="3" type="curve"/>
+      <point x="291" y="28" type="line"/>
+      <point x="269" y="20"/>
+      <point x="245" y="15"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/groups.plist b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/groups.plist
new file mode 100644
index 0000000..02bf8c6
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/groups.plist
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.kern1.T.left</key>
+	<array>
+		<string>T</string>
+	</array>
+	<key>public.kern1.d</key>
+	<array>
+		<string>l</string>
+	</array>
+	<key>public.kern1.n.left</key>
+	<array>
+		<string>n</string>
+	</array>
+	<key>public.kern1.o.left</key>
+	<array>
+		<string>o</string>
+	</array>
+	<key>public.kern1.t.left</key>
+	<array>
+		<string>t</string>
+	</array>
+	<key>public.kern2.T.right</key>
+	<array>
+		<string>T</string>
+	</array>
+	<key>public.kern2.b</key>
+	<array>
+		<string>l</string>
+	</array>
+	<key>public.kern2.n.right</key>
+	<array>
+		<string>n</string>
+	</array>
+	<key>public.kern2.o.right</key>
+	<array>
+		<string>o</string>
+	</array>
+	<key>public.kern2.s.right</key>
+	<array>
+		<string>s</string>
+	</array>
+	<key>public.kern2.t.right</key>
+	<array>
+		<string>t</string>
+	</array>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/kerning.plist b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/kerning.plist
new file mode 100644
index 0000000..2000fcc
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/kerning.plist
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.kern1.T.left</key>
+	<dict>
+		<key>public.kern2.T.right</key>
+		<integer>20</integer>
+		<key>public.kern2.n.right</key>
+		<integer>-50</integer>
+		<key>public.kern2.o.right</key>
+		<integer>-70</integer>
+		<key>public.kern2.s.right</key>
+		<integer>-60</integer>
+	</dict>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/layercontents.plist b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/layercontents.plist
new file mode 100644
index 0000000..cf95d35
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/layercontents.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<array>
+	<array>
+		<string>public.default</string>
+		<string>glyphs</string>
+	</array>
+</array>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/lib.plist b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/lib.plist
new file mode 100644
index 0000000..42d19d9
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/lib.plist
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.glyphOrder</key>
+	<array>
+		<string>F</string>
+		<string>T</string>
+		<string>l</string>
+		<string>n</string>
+		<string>o</string>
+		<string>s</string>
+		<string>t</string>
+	</array>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/metainfo.plist b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/metainfo.plist
new file mode 100644
index 0000000..632695b
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Light.ufo/metainfo.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>creator</key>
+	<string>org.robofab.ufoLib</string>
+	<key>formatVersion</key>
+	<integer>3</integer>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/fontinfo.plist b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/fontinfo.plist
new file mode 100644
index 0000000..c843436
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/fontinfo.plist
@@ -0,0 +1,153 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>ascender</key>
+	<integer>760</integer>
+	<key>capHeight</key>
+	<integer>714</integer>
+	<key>copyright</key>
+	<string>Copyright 2015 Google Inc. All Rights Reserved.</string>
+	<key>descender</key>
+	<integer>-240</integer>
+	<key>familyName</key>
+	<string>Test Family 3</string>
+	<key>guidelines</key>
+	<array/>
+	<key>openTypeHeadCreated</key>
+	<string>2016/03/15 19:50:39</string>
+	<key>openTypeHheaAscender</key>
+	<integer>1069</integer>
+	<key>openTypeHheaDescender</key>
+	<integer>-293</integer>
+	<key>openTypeHheaLineGap</key>
+	<integer>0</integer>
+	<key>openTypeNameDescription</key>
+	<string>Designed by Monotype design team.</string>
+	<key>openTypeNameDesigner</key>
+	<string>Monotype Design Team</string>
+	<key>openTypeNameDesignerURL</key>
+	<string>http://www.monotype.com/studio</string>
+	<key>openTypeNameLicense</key>
+	<string>This Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License for the specific language, permissions and limitations governing your use of this Font Software.</string>
+	<key>openTypeNameLicenseURL</key>
+	<string>http://scripts.sil.org/OFL</string>
+	<key>openTypeNameManufacturer</key>
+	<string>Monotype Imaging Inc.</string>
+	<key>openTypeNameManufacturerURL</key>
+	<string>http://www.google.com/get/noto/</string>
+	<key>openTypeNameVersion</key>
+	<string>Version 1.902</string>
+	<key>openTypeOS2Panose</key>
+	<array>
+		<integer>2</integer>
+		<integer>11</integer>
+		<integer>5</integer>
+		<integer>2</integer>
+		<integer>4</integer>
+		<integer>5</integer>
+		<integer>4</integer>
+		<integer>2</integer>
+		<integer>2</integer>
+		<integer>4</integer>
+	</array>
+	<key>openTypeOS2Selection</key>
+	<array>
+		<integer>8</integer>
+	</array>
+	<key>openTypeOS2Type</key>
+	<array/>
+	<key>openTypeOS2TypoAscender</key>
+	<integer>1069</integer>
+	<key>openTypeOS2TypoDescender</key>
+	<integer>-293</integer>
+	<key>openTypeOS2TypoLineGap</key>
+	<integer>0</integer>
+	<key>openTypeOS2UnicodeRanges</key>
+	<array>
+		<integer>0</integer>
+		<integer>1</integer>
+		<integer>2</integer>
+		<integer>3</integer>
+		<integer>4</integer>
+		<integer>5</integer>
+		<integer>6</integer>
+		<integer>7</integer>
+		<integer>9</integer>
+		<integer>29</integer>
+		<integer>30</integer>
+		<integer>31</integer>
+		<integer>32</integer>
+		<integer>33</integer>
+		<integer>34</integer>
+		<integer>35</integer>
+		<integer>36</integer>
+		<integer>62</integer>
+		<integer>64</integer>
+		<integer>67</integer>
+		<integer>69</integer>
+		<integer>91</integer>
+		<integer>116</integer>
+	</array>
+	<key>openTypeOS2VendorID</key>
+	<string>GOOG</string>
+	<key>openTypeOS2WinAscent</key>
+	<integer>1069</integer>
+	<key>openTypeOS2WinDescent</key>
+	<integer>293</integer>
+	<key>postscriptBlueValues</key>
+	<array>
+		<integer>-15</integer>
+		<integer>0</integer>
+		<integer>536</integer>
+		<integer>547</integer>
+		<integer>572</integer>
+		<integer>582</integer>
+		<integer>714</integer>
+		<integer>726</integer>
+		<integer>760</integer>
+		<integer>766</integer>
+	</array>
+	<key>postscriptFamilyBlues</key>
+	<array/>
+	<key>postscriptFamilyOtherBlues</key>
+	<array/>
+	<key>postscriptOtherBlues</key>
+	<array>
+		<integer>-256</integer>
+		<integer>-240</integer>
+	</array>
+	<key>postscriptStemSnapH</key>
+	<array>
+		<integer>60</integer>
+		<integer>68</integer>
+		<integer>79</integer>
+	</array>
+	<key>postscriptStemSnapV</key>
+	<array>
+		<integer>64</integer>
+		<integer>75</integer>
+		<integer>90</integer>
+	</array>
+	<key>postscriptUnderlinePosition</key>
+	<integer>-100</integer>
+	<key>postscriptUnderlineThickness</key>
+	<integer>50</integer>
+	<key>styleMapFamilyName</key>
+	<string>Test Family 3</string>
+	<key>styleMapStyleName</key>
+	<string>regular</string>
+	<key>styleName</key>
+	<string>Regular</string>
+	<key>trademark</key>
+	<string>Noto is a trademark of Google Inc.</string>
+	<key>unitsPerEm</key>
+	<integer>1000</integer>
+	<key>versionMajor</key>
+	<integer>1</integer>
+	<key>versionMinor</key>
+	<integer>902</integer>
+	<key>xHeight</key>
+	<integer>536</integer>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/F_.glif b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/F_.glif
new file mode 100644
index 0000000..1dfb60b
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/F_.glif
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="F" format="2">
+  <advance width="519"/>
+  <unicode hex="0046"/>
+  <anchor x="260" y="0" name="bottom"/>
+  <anchor x="298" y="714" name="top"/>
+  <anchor x="499" y="714" name="topright"/>
+  <outline>
+    <contour>
+      <point x="187" y="0" type="line"/>
+      <point x="187" y="303" type="line"/>
+      <point x="477" y="303" type="line"/>
+      <point x="477" y="382" type="line"/>
+      <point x="187" y="382" type="line"/>
+      <point x="187" y="635" type="line"/>
+      <point x="496" y="635" type="line"/>
+      <point x="496" y="714" type="line"/>
+      <point x="97" y="714" type="line"/>
+      <point x="97" y="0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/T_.glif b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/T_.glif
new file mode 100644
index 0000000..a92aaba
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/T_.glif
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="T" format="2">
+  <advance width="556"/>
+  <unicode hex="0054"/>
+  <anchor x="278" y="0" name="bottom"/>
+  <anchor x="278" y="357" name="center"/>
+  <anchor x="278" y="714" name="top"/>
+  <anchor x="536" y="714" name="topright"/>
+  <outline>
+    <contour>
+      <point x="323" y="0" type="line"/>
+      <point x="323" y="635" type="line"/>
+      <point x="545" y="635" type="line"/>
+      <point x="545" y="714" type="line"/>
+      <point x="10" y="714" type="line"/>
+      <point x="10" y="635" type="line"/>
+      <point x="233" y="635" type="line"/>
+      <point x="233" y="0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/contents.plist b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/contents.plist
new file mode 100644
index 0000000..d50a055
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/contents.plist
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>F</key>
+	<string>F_.glif</string>
+	<key>T</key>
+	<string>T_.glif</string>
+	<key>l</key>
+	<string>l.glif</string>
+	<key>n</key>
+	<string>n.glif</string>
+	<key>o</key>
+	<string>o.glif</string>
+	<key>s</key>
+	<string>s.glif</string>
+	<key>t</key>
+	<string>t.glif</string>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/l.glif b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/l.glif
new file mode 100644
index 0000000..c8cf0fd
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/l.glif
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="l" format="2">
+  <advance width="258"/>
+  <unicode hex="006C"/>
+  <anchor x="129" y="0" name="bottom"/>
+  <anchor x="129" y="268" name="center"/>
+  <anchor x="129" y="760" name="top"/>
+  <anchor x="202" y="760" name="topright"/>
+  <outline>
+    <contour>
+      <point x="173" y="0" type="line"/>
+      <point x="173" y="760" type="line"/>
+      <point x="85" y="760" type="line"/>
+      <point x="85" y="0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/layerinfo.plist b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/layerinfo.plist
new file mode 100644
index 0000000..0c67376
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/layerinfo.plist
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict/>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/n.glif b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/n.glif
new file mode 100644
index 0000000..018de2b
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/n.glif
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="n" format="2">
+  <advance width="618"/>
+  <unicode hex="006E"/>
+  <anchor x="309" y="0" name="bottom"/>
+  <anchor x="309" y="536" name="top"/>
+  <anchor x="562" y="536" name="topright"/>
+  <outline>
+    <contour>
+      <point x="343" y="546" type="curve" smooth="yes"/>
+      <point x="275" y="546"/>
+      <point x="209" y="519"/>
+      <point x="174" y="463" type="curve"/>
+      <point x="169" y="463" type="line"/>
+      <point x="156" y="536" type="line"/>
+      <point x="85" y="536" type="line"/>
+      <point x="85" y="0" type="line"/>
+      <point x="173" y="0" type="line"/>
+      <point x="173" y="278" type="line" smooth="yes"/>
+      <point x="173" y="403"/>
+      <point x="211" y="472"/>
+      <point x="330" y="472" type="curve" smooth="yes"/>
+      <point x="412" y="472"/>
+      <point x="450" y="429"/>
+      <point x="450" y="343" type="curve" smooth="yes"/>
+      <point x="450" y="0" type="line"/>
+      <point x="537" y="0" type="line"/>
+      <point x="537" y="349" type="line" smooth="yes"/>
+      <point x="537" y="487"/>
+      <point x="471" y="546"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/o.glif b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/o.glif
new file mode 100644
index 0000000..13b0675
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/o.glif
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="o" format="2">
+  <advance width="605"/>
+  <unicode hex="006F"/>
+  <anchor x="301" y="0" name="bottom"/>
+  <anchor x="303" y="268" name="center"/>
+  <anchor x="303" y="536" name="top"/>
+  <anchor x="575" y="536" name="topright"/>
+  <outline>
+    <contour>
+      <point x="551" y="269" type="curve" smooth="yes"/>
+      <point x="551" y="446"/>
+      <point x="449" y="546"/>
+      <point x="304" y="546" type="curve" smooth="yes"/>
+      <point x="150" y="546"/>
+      <point x="55" y="446"/>
+      <point x="55" y="269" type="curve" smooth="yes"/>
+      <point x="55" y="91"/>
+      <point x="159" y="-10"/>
+      <point x="301" y="-10" type="curve" smooth="yes"/>
+      <point x="454" y="-10"/>
+      <point x="551" y="91"/>
+    </contour>
+    <contour>
+      <point x="146" y="269" type="curve" smooth="yes"/>
+      <point x="146" y="396"/>
+      <point x="193" y="472"/>
+      <point x="302" y="472" type="curve" smooth="yes"/>
+      <point x="411" y="472"/>
+      <point x="460" y="396"/>
+      <point x="460" y="269" type="curve" smooth="yes"/>
+      <point x="460" y="142"/>
+      <point x="411" y="63"/>
+      <point x="303" y="63" type="curve" smooth="yes"/>
+      <point x="194" y="63"/>
+      <point x="146" y="142"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/s.glif b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/s.glif
new file mode 100644
index 0000000..3a58fb8
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/s.glif
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="s" format="2">
+  <advance width="479"/>
+  <unicode hex="0073"/>
+  <anchor x="240" y="0" name="bottom"/>
+  <anchor x="240" y="536" name="top"/>
+  <anchor x="439" y="536" name="topright"/>
+  <outline>
+    <contour>
+      <point x="434" y="148" type="curve" smooth="yes"/>
+      <point x="434" y="234"/>
+      <point x="375" y="269"/>
+      <point x="273" y="307" type="curve" smooth="yes"/>
+      <point x="170" y="346"/>
+      <point x="135" y="364"/>
+      <point x="135" y="409" type="curve" smooth="yes"/>
+      <point x="135" y="449"/>
+      <point x="174" y="474"/>
+      <point x="246" y="474" type="curve" smooth="yes"/>
+      <point x="298" y="474"/>
+      <point x="348" y="459"/>
+      <point x="393" y="440" type="curve"/>
+      <point x="423" y="510" type="line"/>
+      <point x="373" y="532"/>
+      <point x="317" y="546"/>
+      <point x="252" y="546" type="curve" smooth="yes"/>
+      <point x="132" y="546"/>
+      <point x="51" y="495"/>
+      <point x="51" y="404" type="curve" smooth="yes"/>
+      <point x="51" y="316"/>
+      <point x="113" y="284"/>
+      <point x="217" y="244" type="curve" smooth="yes"/>
+      <point x="322" y="204"/>
+      <point x="349" y="180"/>
+      <point x="349" y="140" type="curve" smooth="yes"/>
+      <point x="349" y="92"/>
+      <point x="311" y="61"/>
+      <point x="222" y="61" type="curve" smooth="yes"/>
+      <point x="159" y="61"/>
+      <point x="94" y="83"/>
+      <point x="52" y="104" type="curve"/>
+      <point x="52" y="24" type="line"/>
+      <point x="93" y="2"/>
+      <point x="145" y="-10"/>
+      <point x="220" y="-10" type="curve" smooth="yes"/>
+      <point x="351" y="-10"/>
+      <point x="434" y="44"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/t.glif b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/t.glif
new file mode 100644
index 0000000..2e74d20
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/glyphs/t.glif
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="t" format="2">
+  <advance width="361"/>
+  <unicode hex="0074"/>
+  <anchor x="214" y="0" name="bottom"/>
+  <anchor x="181" y="268" name="center"/>
+  <anchor x="154" y="659" name="top"/>
+  <anchor x="335" y="659" name="topright"/>
+  <outline>
+    <contour>
+      <point x="264" y="62" type="curve" smooth="yes"/>
+      <point x="215" y="62"/>
+      <point x="180" y="93"/>
+      <point x="180" y="158" type="curve" smooth="yes"/>
+      <point x="180" y="468" type="line"/>
+      <point x="335" y="468" type="line"/>
+      <point x="335" y="536" type="line"/>
+      <point x="180" y="536" type="line"/>
+      <point x="180" y="659" type="line"/>
+      <point x="128" y="659" type="line"/>
+      <point x="93" y="545" type="line"/>
+      <point x="16" y="510" type="line"/>
+      <point x="16" y="468" type="line"/>
+      <point x="92" y="468" type="line"/>
+      <point x="92" y="156" type="line" smooth="yes"/>
+      <point x="92" y="26"/>
+      <point x="165" y="-10"/>
+      <point x="249" y="-10" type="curve" smooth="yes"/>
+      <point x="281" y="-10"/>
+      <point x="320" y="-3"/>
+      <point x="339" y="6" type="curve"/>
+      <point x="339" y="73" type="line"/>
+      <point x="322" y="67"/>
+      <point x="290" y="62"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/groups.plist b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/groups.plist
new file mode 100644
index 0000000..02bf8c6
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/groups.plist
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.kern1.T.left</key>
+	<array>
+		<string>T</string>
+	</array>
+	<key>public.kern1.d</key>
+	<array>
+		<string>l</string>
+	</array>
+	<key>public.kern1.n.left</key>
+	<array>
+		<string>n</string>
+	</array>
+	<key>public.kern1.o.left</key>
+	<array>
+		<string>o</string>
+	</array>
+	<key>public.kern1.t.left</key>
+	<array>
+		<string>t</string>
+	</array>
+	<key>public.kern2.T.right</key>
+	<array>
+		<string>T</string>
+	</array>
+	<key>public.kern2.b</key>
+	<array>
+		<string>l</string>
+	</array>
+	<key>public.kern2.n.right</key>
+	<array>
+		<string>n</string>
+	</array>
+	<key>public.kern2.o.right</key>
+	<array>
+		<string>o</string>
+	</array>
+	<key>public.kern2.s.right</key>
+	<array>
+		<string>s</string>
+	</array>
+	<key>public.kern2.t.right</key>
+	<array>
+		<string>t</string>
+	</array>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/kerning.plist b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/kerning.plist
new file mode 100644
index 0000000..2000fcc
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/kerning.plist
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.kern1.T.left</key>
+	<dict>
+		<key>public.kern2.T.right</key>
+		<integer>20</integer>
+		<key>public.kern2.n.right</key>
+		<integer>-50</integer>
+		<key>public.kern2.o.right</key>
+		<integer>-70</integer>
+		<key>public.kern2.s.right</key>
+		<integer>-60</integer>
+	</dict>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/layercontents.plist b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/layercontents.plist
new file mode 100644
index 0000000..cf95d35
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/layercontents.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<array>
+	<array>
+		<string>public.default</string>
+		<string>glyphs</string>
+	</array>
+</array>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/lib.plist b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/lib.plist
new file mode 100644
index 0000000..42d19d9
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/lib.plist
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.glyphOrder</key>
+	<array>
+		<string>F</string>
+		<string>T</string>
+		<string>l</string>
+		<string>n</string>
+		<string>o</string>
+		<string>s</string>
+		<string>t</string>
+	</array>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/metainfo.plist b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/metainfo.plist
new file mode 100644
index 0000000..632695b
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-Regular.ufo/metainfo.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>creator</key>
+	<string>org.robofab.ufoLib</string>
+	<key>formatVersion</key>
+	<integer>3</integer>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/fontinfo.plist b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/fontinfo.plist
new file mode 100644
index 0000000..b819589
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/fontinfo.plist
@@ -0,0 +1,153 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>ascender</key>
+	<integer>760</integer>
+	<key>capHeight</key>
+	<integer>714</integer>
+	<key>copyright</key>
+	<string>Copyright 2015 Google Inc. All Rights Reserved.</string>
+	<key>descender</key>
+	<integer>-240</integer>
+	<key>familyName</key>
+	<string>Test Family 3</string>
+	<key>guidelines</key>
+	<array/>
+	<key>openTypeHeadCreated</key>
+	<string>2016/03/15 19:50:39</string>
+	<key>openTypeHheaAscender</key>
+	<integer>1069</integer>
+	<key>openTypeHheaDescender</key>
+	<integer>-293</integer>
+	<key>openTypeHheaLineGap</key>
+	<integer>0</integer>
+	<key>openTypeNameDescription</key>
+	<string>Designed by Monotype design team.</string>
+	<key>openTypeNameDesigner</key>
+	<string>Monotype Design Team</string>
+	<key>openTypeNameDesignerURL</key>
+	<string>http://www.monotype.com/studio</string>
+	<key>openTypeNameLicense</key>
+	<string>This Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License for the specific language, permissions and limitations governing your use of this Font Software.</string>
+	<key>openTypeNameLicenseURL</key>
+	<string>http://scripts.sil.org/OFL</string>
+	<key>openTypeNameManufacturer</key>
+	<string>Monotype Imaging Inc.</string>
+	<key>openTypeNameManufacturerURL</key>
+	<string>http://www.google.com/get/noto/</string>
+	<key>openTypeNameVersion</key>
+	<string>Version 1.902</string>
+	<key>openTypeOS2Panose</key>
+	<array>
+		<integer>2</integer>
+		<integer>11</integer>
+		<integer>8</integer>
+		<integer>2</integer>
+		<integer>4</integer>
+		<integer>5</integer>
+		<integer>4</integer>
+		<integer>2</integer>
+		<integer>2</integer>
+		<integer>4</integer>
+	</array>
+	<key>openTypeOS2Selection</key>
+	<array>
+		<integer>8</integer>
+	</array>
+	<key>openTypeOS2Type</key>
+	<array/>
+	<key>openTypeOS2TypoAscender</key>
+	<integer>1069</integer>
+	<key>openTypeOS2TypoDescender</key>
+	<integer>-293</integer>
+	<key>openTypeOS2TypoLineGap</key>
+	<integer>0</integer>
+	<key>openTypeOS2UnicodeRanges</key>
+	<array>
+		<integer>0</integer>
+		<integer>1</integer>
+		<integer>2</integer>
+		<integer>3</integer>
+		<integer>4</integer>
+		<integer>5</integer>
+		<integer>6</integer>
+		<integer>7</integer>
+		<integer>9</integer>
+		<integer>29</integer>
+		<integer>30</integer>
+		<integer>31</integer>
+		<integer>32</integer>
+		<integer>33</integer>
+		<integer>34</integer>
+		<integer>35</integer>
+		<integer>36</integer>
+		<integer>62</integer>
+		<integer>64</integer>
+		<integer>67</integer>
+		<integer>69</integer>
+		<integer>91</integer>
+		<integer>116</integer>
+	</array>
+	<key>openTypeOS2VendorID</key>
+	<string>GOOG</string>
+	<key>openTypeOS2WinAscent</key>
+	<integer>1069</integer>
+	<key>openTypeOS2WinDescent</key>
+	<integer>293</integer>
+	<key>postscriptBlueValues</key>
+	<array>
+		<integer>-15</integer>
+		<integer>0</integer>
+		<integer>546</integer>
+		<integer>557</integer>
+		<integer>582</integer>
+		<integer>593</integer>
+		<integer>714</integer>
+		<integer>726</integer>
+		<integer>760</integer>
+		<integer>766</integer>
+	</array>
+	<key>postscriptFamilyBlues</key>
+	<array/>
+	<key>postscriptFamilyOtherBlues</key>
+	<array/>
+	<key>postscriptOtherBlues</key>
+	<array>
+		<integer>-256</integer>
+		<integer>-240</integer>
+	</array>
+	<key>postscriptStemSnapH</key>
+	<array>
+		<integer>103</integer>
+		<integer>112</integer>
+		<integer>124</integer>
+	</array>
+	<key>postscriptStemSnapV</key>
+	<array>
+		<integer>117</integer>
+		<integer>144</integer>
+		<integer>151</integer>
+	</array>
+	<key>postscriptUnderlinePosition</key>
+	<integer>-100</integer>
+	<key>postscriptUnderlineThickness</key>
+	<integer>50</integer>
+	<key>styleMapFamilyName</key>
+	<string>Test Family 3 SemiBold</string>
+	<key>styleMapStyleName</key>
+	<string>regular</string>
+	<key>styleName</key>
+	<string>SemiBold</string>
+	<key>trademark</key>
+	<string>Noto is a trademark of Google Inc.</string>
+	<key>unitsPerEm</key>
+	<integer>1000</integer>
+	<key>versionMajor</key>
+	<integer>1</integer>
+	<key>versionMinor</key>
+	<integer>902</integer>
+	<key>xHeight</key>
+	<integer>546</integer>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/F_.glif b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/F_.glif
new file mode 100644
index 0000000..6a2b861
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/F_.glif
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="F" format="2">
+  <advance width="549"/>
+  <unicode hex="0046"/>
+  <anchor x="275" y="0" name="bottom"/>
+  <anchor x="298" y="714" name="top"/>
+  <anchor x="529" y="714" name="topright"/>
+  <outline>
+    <contour>
+      <point x="239" y="0" type="line"/>
+      <point x="239" y="282" type="line"/>
+      <point x="481" y="282" type="line"/>
+      <point x="481" y="406" type="line"/>
+      <point x="239" y="406" type="line"/>
+      <point x="239" y="590" type="line"/>
+      <point x="499" y="590" type="line"/>
+      <point x="499" y="714" type="line"/>
+      <point x="90" y="714" type="line"/>
+      <point x="90" y="0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/T_.glif b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/T_.glif
new file mode 100644
index 0000000..93aed48
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/T_.glif
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="T" format="2">
+  <advance width="579"/>
+  <unicode hex="0054"/>
+  <anchor x="290" y="0" name="bottom"/>
+  <anchor x="290" y="357" name="center"/>
+  <anchor x="290" y="714" name="top"/>
+  <anchor x="559" y="714" name="topright"/>
+  <outline>
+    <contour>
+      <point x="365" y="0" type="line"/>
+      <point x="365" y="588" type="line"/>
+      <point x="559" y="588" type="line"/>
+      <point x="559" y="714" type="line"/>
+      <point x="20" y="714" type="line"/>
+      <point x="20" y="588" type="line"/>
+      <point x="214" y="588" type="line"/>
+      <point x="214" y="0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/contents.plist b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/contents.plist
new file mode 100644
index 0000000..d50a055
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/contents.plist
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>F</key>
+	<string>F_.glif</string>
+	<key>T</key>
+	<string>T_.glif</string>
+	<key>l</key>
+	<string>l.glif</string>
+	<key>n</key>
+	<string>n.glif</string>
+	<key>o</key>
+	<string>o.glif</string>
+	<key>s</key>
+	<string>s.glif</string>
+	<key>t</key>
+	<string>t.glif</string>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/l.glif b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/l.glif
new file mode 100644
index 0000000..7e6cdad
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/l.glif
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="l" format="2">
+  <advance width="305"/>
+  <unicode hex="006C"/>
+  <anchor x="153" y="0" name="bottom"/>
+  <anchor x="153" y="273" name="center"/>
+  <anchor x="153" y="760" name="top"/>
+  <anchor x="248" y="760" name="topright"/>
+  <outline>
+    <contour>
+      <point x="227" y="0" type="line"/>
+      <point x="227" y="760" type="line"/>
+      <point x="78" y="760" type="line"/>
+      <point x="78" y="0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/layerinfo.plist b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/layerinfo.plist
new file mode 100644
index 0000000..0c67376
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/layerinfo.plist
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict/>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/n.glif b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/n.glif
new file mode 100644
index 0000000..74a3878
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/n.glif
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="n" format="2">
+  <advance width="657"/>
+  <unicode hex="006E"/>
+  <anchor x="329" y="0" name="bottom"/>
+  <anchor x="329" y="546" name="top"/>
+  <anchor x="607" y="546" name="topright"/>
+  <outline>
+    <contour>
+      <point x="388" y="556" type="curve" smooth="yes"/>
+      <point x="320" y="556"/>
+      <point x="255" y="532"/>
+      <point x="220" y="476" type="curve"/>
+      <point x="212" y="476" type="line"/>
+      <point x="192" y="546" type="line"/>
+      <point x="78" y="546" type="line"/>
+      <point x="78" y="0" type="line"/>
+      <point x="227" y="0" type="line"/>
+      <point x="227" y="257" type="line" smooth="yes"/>
+      <point x="227" y="373"/>
+      <point x="254" y="437"/>
+      <point x="345" y="437" type="curve" smooth="yes"/>
+      <point x="406" y="437"/>
+      <point x="433" y="397"/>
+      <point x="433" y="319" type="curve" smooth="yes"/>
+      <point x="433" y="0" type="line"/>
+      <point x="582" y="0" type="line"/>
+      <point x="582" y="356" type="line"/>
+      <point x="582" y="496"/>
+      <point x="505" y="556"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/o.glif b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/o.glif
new file mode 100644
index 0000000..e78de65
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/o.glif
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="o" format="2">
+  <advance width="619"/>
+  <unicode hex="006F"/>
+  <anchor x="310" y="0" name="bottom"/>
+  <anchor x="310" y="273" name="center"/>
+  <anchor x="310" y="546" name="top"/>
+  <anchor x="599" y="546" name="topright"/>
+  <outline>
+    <contour>
+      <point x="574" y="274" type="curve"/>
+      <point x="574" y="455"/>
+      <point x="464" y="556"/>
+      <point x="311" y="556" type="curve" smooth="yes"/>
+      <point x="146" y="556"/>
+      <point x="45" y="455"/>
+      <point x="45" y="274" type="curve" smooth="yes"/>
+      <point x="45" y="92"/>
+      <point x="155" y="-10"/>
+      <point x="308" y="-10" type="curve" smooth="yes"/>
+      <point x="472" y="-10"/>
+      <point x="574" y="92"/>
+    </contour>
+    <contour>
+      <point x="197" y="274" type="curve"/>
+      <point x="197" y="382"/>
+      <point x="230" y="436"/>
+      <point x="309" y="436" type="curve" smooth="yes"/>
+      <point x="389" y="436"/>
+      <point x="422" y="382"/>
+      <point x="422" y="274" type="curve" smooth="yes"/>
+      <point x="422" y="166"/>
+      <point x="389" y="110"/>
+      <point x="310" y="110" type="curve" smooth="yes"/>
+      <point x="230" y="110"/>
+      <point x="197" y="166"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/s.glif b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/s.glif
new file mode 100644
index 0000000..e027b48
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/s.glif
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="s" format="2">
+  <advance width="497"/>
+  <unicode hex="0073"/>
+  <anchor x="249" y="0" name="bottom"/>
+  <anchor x="249" y="546" name="top"/>
+  <anchor x="457" y="546" name="topright"/>
+  <outline>
+    <contour>
+      <point x="459" y="162" type="curve" smooth="yes"/>
+      <point x="459" y="259"/>
+      <point x="400" y="294"/>
+      <point x="307" y="332" type="curve" smooth="yes"/>
+      <point x="211" y="371"/>
+      <point x="193" y="384"/>
+      <point x="193" y="410" type="curve" smooth="yes"/>
+      <point x="193" y="434"/>
+      <point x="215" y="446"/>
+      <point x="259" y="446" type="curve" smooth="yes"/>
+      <point x="308" y="446"/>
+      <point x="354" y="429"/>
+      <point x="408" y="406" type="curve"/>
+      <point x="453" y="513" type="line"/>
+      <point x="388" y="543"/>
+      <point x="329" y="556"/>
+      <point x="261" y="556" type="curve" smooth="yes"/>
+      <point x="130" y="556"/>
+      <point x="45" y="505"/>
+      <point x="45" y="404" type="curve" smooth="yes"/>
+      <point x="45" y="311"/>
+      <point x="91" y="275"/>
+      <point x="194" y="232" type="curve" smooth="yes"/>
+      <point x="300" y="187"/>
+      <point x="312" y="173"/>
+      <point x="312" y="146" type="curve" smooth="yes"/>
+      <point x="312" y="118"/>
+      <point x="289" y="99"/>
+      <point x="231" y="99" type="curve" smooth="yes"/>
+      <point x="179" y="99"/>
+      <point x="105" y="118"/>
+      <point x="46" y="145" type="curve"/>
+      <point x="46" y="22" type="line"/>
+      <point x="101" y="-1"/>
+      <point x="150" y="-10"/>
+      <point x="226" y="-10" type="curve" smooth="yes"/>
+      <point x="380" y="-10"/>
+      <point x="459" y="51"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/t.glif b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/t.glif
new file mode 100644
index 0000000..da81c00
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/t.glif
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="t" format="2">
+  <advance width="434"/>
+  <unicode hex="0074"/>
+  <anchor x="241" y="0" name="bottom"/>
+  <anchor x="217" y="273" name="center"/>
+  <anchor x="198" y="662" name="top"/>
+  <anchor x="396" y="662" name="topright"/>
+  <outline>
+    <contour>
+      <point x="308" y="109" type="curve" smooth="yes"/>
+      <point x="269" y="109"/>
+      <point x="243" y="129"/>
+      <point x="243" y="171" type="curve" smooth="yes"/>
+      <point x="243" y="434" type="line"/>
+      <point x="396" y="434" type="line"/>
+      <point x="396" y="546" type="line"/>
+      <point x="243" y="546" type="line"/>
+      <point x="243" y="662" type="line"/>
+      <point x="148" y="662" type="line"/>
+      <point x="105" y="547" type="line"/>
+      <point x="23" y="497" type="line"/>
+      <point x="23" y="434" type="line"/>
+      <point x="94" y="434" type="line"/>
+      <point x="94" y="171" type="line" smooth="yes"/>
+      <point x="94" y="30"/>
+      <point x="167" y="-10"/>
+      <point x="265" y="-10" type="curve" smooth="yes"/>
+      <point x="321" y="-10"/>
+      <point x="370" y="1"/>
+      <point x="402" y="15" type="curve"/>
+      <point x="402" y="126" type="line"/>
+      <point x="371" y="116"/>
+      <point x="341" y="109"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/groups.plist b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/groups.plist
new file mode 100644
index 0000000..02bf8c6
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/groups.plist
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.kern1.T.left</key>
+	<array>
+		<string>T</string>
+	</array>
+	<key>public.kern1.d</key>
+	<array>
+		<string>l</string>
+	</array>
+	<key>public.kern1.n.left</key>
+	<array>
+		<string>n</string>
+	</array>
+	<key>public.kern1.o.left</key>
+	<array>
+		<string>o</string>
+	</array>
+	<key>public.kern1.t.left</key>
+	<array>
+		<string>t</string>
+	</array>
+	<key>public.kern2.T.right</key>
+	<array>
+		<string>T</string>
+	</array>
+	<key>public.kern2.b</key>
+	<array>
+		<string>l</string>
+	</array>
+	<key>public.kern2.n.right</key>
+	<array>
+		<string>n</string>
+	</array>
+	<key>public.kern2.o.right</key>
+	<array>
+		<string>o</string>
+	</array>
+	<key>public.kern2.s.right</key>
+	<array>
+		<string>s</string>
+	</array>
+	<key>public.kern2.t.right</key>
+	<array>
+		<string>t</string>
+	</array>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/kerning.plist b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/kerning.plist
new file mode 100644
index 0000000..2000fcc
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/kerning.plist
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.kern1.T.left</key>
+	<dict>
+		<key>public.kern2.T.right</key>
+		<integer>20</integer>
+		<key>public.kern2.n.right</key>
+		<integer>-50</integer>
+		<key>public.kern2.o.right</key>
+		<integer>-70</integer>
+		<key>public.kern2.s.right</key>
+		<integer>-60</integer>
+	</dict>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/layercontents.plist b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/layercontents.plist
new file mode 100644
index 0000000..cf95d35
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/layercontents.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<array>
+	<array>
+		<string>public.default</string>
+		<string>glyphs</string>
+	</array>
+</array>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/lib.plist b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/lib.plist
new file mode 100644
index 0000000..42d19d9
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/lib.plist
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>public.glyphOrder</key>
+	<array>
+		<string>F</string>
+		<string>T</string>
+		<string>l</string>
+		<string>n</string>
+		<string>o</string>
+		<string>s</string>
+		<string>t</string>
+	</array>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/metainfo.plist b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/metainfo.plist
new file mode 100644
index 0000000..632695b
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/metainfo.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>creator</key>
+	<string>org.robofab.ufoLib</string>
+	<key>formatVersion</key>
+	<integer>3</integer>
+</dict>
+</plist>
diff --git a/Tests/varLib/data/test_results/Build.ttx b/Tests/varLib/data/test_results/Build.ttx
new file mode 100644
index 0000000..5d665e3
--- /dev/null
+++ b/Tests/varLib/data/test_results/Build.ttx
@@ -0,0 +1,1601 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.17">
+
+  <HVAR>
+    <Version value="0x00010000"/>
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=2 -->
+        <!-- RegionCount=5 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="1">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="2">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="3">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="4">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=1 -->
+      <VarData index="0">
+        <!-- ItemCount=6 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=2 -->
+        <VarRegionIndex index="0" value="0"/>
+        <VarRegionIndex index="1" value="1"/>
+        <Item index="0" value="[0, 0]"/>
+        <Item index="1" value="[14, -28]"/>
+        <Item index="2" value="[-10, 17]"/>
+        <Item index="3" value="[-3, 32]"/>
+        <Item index="4" value="[-7, 63]"/>
+        <Item index="5" value="[-7, 63]"/>
+      </VarData>
+    </VarStore>
+  </HVAR>
+
+  <MVAR>
+    <Version value="0x00010000"/>
+    <Reserved value="0"/>
+    <ValueRecordSize value="8"/>
+    <!-- ValueRecordCount=2 -->
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=2 -->
+        <!-- RegionCount=5 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="1">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="2">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="3">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="4">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=1 -->
+      <VarData index="0">
+        <!-- ItemCount=2 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=2 -->
+        <VarRegionIndex index="0" value="0"/>
+        <VarRegionIndex index="1" value="1"/>
+        <Item index="0" value="[-4, 13]"/>
+        <Item index="1" value="[-2, 8]"/>
+      </VarData>
+    </VarStore>
+    <ValueRecord index="0">
+      <ValueTag value="stro"/>
+      <VarIdx value="1"/>
+    </ValueRecord>
+    <ValueRecord index="1">
+      <ValueTag value="xhgt"/>
+      <VarIdx value="0"/>
+    </ValueRecord>
+  </MVAR>
+
+  <fvar>
+
+    <!-- Weight -->
+    <Axis>
+      <AxisTag>wght</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>368.0</DefaultValue>
+      <MaxValue>1000.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+
+    <!-- Contrast -->
+    <Axis>
+      <AxisTag>cntr</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>100.0</MaxValue>
+      <AxisNameID>257</AxisNameID>
+    </Axis>
+
+    <!-- ExtraLight -->
+    <!-- PostScript: TestFamily-ExtraLight -->
+    <NamedInstance flags="0x0" postscriptNameID="259" subfamilyNameID="258">
+      <coord axis="wght" value="0.0"/>
+      <coord axis="cntr" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Light -->
+    <!-- PostScript: TestFamily-Light -->
+    <NamedInstance flags="0x0" postscriptNameID="261" subfamilyNameID="260">
+      <coord axis="wght" value="150.0"/>
+      <coord axis="cntr" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Regular -->
+    <!-- PostScript: TestFamily-Regular -->
+    <NamedInstance flags="0x0" postscriptNameID="263" subfamilyNameID="262">
+      <coord axis="wght" value="394.0"/>
+      <coord axis="cntr" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Semibold -->
+    <!-- PostScript: TestFamily-Semibold -->
+    <NamedInstance flags="0x0" postscriptNameID="265" subfamilyNameID="264">
+      <coord axis="wght" value="600.0"/>
+      <coord axis="cntr" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Bold -->
+    <!-- PostScript: TestFamily-Bold -->
+    <NamedInstance flags="0x0" postscriptNameID="267" subfamilyNameID="266">
+      <coord axis="wght" value="824.0"/>
+      <coord axis="cntr" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Black -->
+    <!-- PostScript: TestFamily-Black -->
+    <NamedInstance flags="0x0" postscriptNameID="269" subfamilyNameID="268">
+      <coord axis="wght" value="1000.0"/>
+      <coord axis="cntr" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Black Medium Contrast -->
+    <!-- PostScript: TestFamily-BlackMediumContrast -->
+    <NamedInstance flags="0x0" postscriptNameID="271" subfamilyNameID="270">
+      <coord axis="wght" value="1000.0"/>
+      <coord axis="cntr" value="50.0"/>
+    </NamedInstance>
+
+    <!-- Black High Contrast -->
+    <!-- PostScript: TestFamily-BlackHighContrast -->
+    <NamedInstance flags="0x0" postscriptNameID="273" subfamilyNameID="272">
+      <coord axis="wght" value="1000.0"/>
+      <coord axis="cntr" value="100.0"/>
+    </NamedInstance>
+  </fvar>
+
+  <gvar>
+    <version value="1"/>
+    <reserved value="0"/>
+    <glyphVariations glyph=".notdef">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="35" y="7"/>
+        <delta pt="2" x="0" y="7"/>
+        <delta pt="3" x="-35" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="35" y="0"/>
+        <delta pt="6" x="0" y="7"/>
+        <delta pt="7" x="-35" y="7"/>
+        <delta pt="8" x="-35" y="-28"/>
+        <delta pt="9" x="35" y="-28"/>
+        <delta pt="10" x="35" y="35"/>
+        <delta pt="11" x="-35" y="35"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="7"/>
+        <delta pt="14" x="0" y="7"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+        <delta pt="17" x="0" y="0"/>
+        <delta pt="18" x="0" y="7"/>
+        <delta pt="19" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="-20" y="-18"/>
+        <delta pt="2" x="0" y="-18"/>
+        <delta pt="3" x="20" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="-20" y="0"/>
+        <delta pt="6" x="0" y="-18"/>
+        <delta pt="7" x="20" y="-18"/>
+        <delta pt="8" x="10" y="10"/>
+        <delta pt="9" x="-10" y="10"/>
+        <delta pt="10" x="-10" y="-28"/>
+        <delta pt="11" x="10" y="-28"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="-18"/>
+        <delta pt="14" x="0" y="-18"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+        <delta pt="17" x="0" y="0"/>
+        <delta pt="18" x="0" y="-18"/>
+        <delta pt="19" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="4" y="0"/>
+        <delta pt="1" x="11" y="0"/>
+        <delta pt="2" x="-4" y="0"/>
+        <delta pt="3" x="-11" y="0"/>
+        <delta pt="4" x="-4" y="0"/>
+        <delta pt="5" x="11" y="0"/>
+        <delta pt="6" x="4" y="0"/>
+        <delta pt="7" x="-11" y="0"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="0" y="0"/>
+        <delta pt="10" x="0" y="0"/>
+        <delta pt="11" x="0" y="0"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="0"/>
+        <delta pt="14" x="0" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+        <delta pt="17" x="0" y="0"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="-4" y="0"/>
+        <delta pt="1" x="-11" y="0"/>
+        <delta pt="2" x="4" y="0"/>
+        <delta pt="3" x="11" y="0"/>
+        <delta pt="4" x="4" y="0"/>
+        <delta pt="5" x="-11" y="0"/>
+        <delta pt="6" x="-4" y="0"/>
+        <delta pt="7" x="11" y="0"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="0" y="0"/>
+        <delta pt="10" x="0" y="0"/>
+        <delta pt="11" x="0" y="0"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="0"/>
+        <delta pt="14" x="0" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+        <delta pt="17" x="0" y="0"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="6" y="0"/>
+        <delta pt="1" x="19" y="0"/>
+        <delta pt="2" x="-6" y="0"/>
+        <delta pt="3" x="-19" y="0"/>
+        <delta pt="4" x="-6" y="0"/>
+        <delta pt="5" x="19" y="0"/>
+        <delta pt="6" x="6" y="0"/>
+        <delta pt="7" x="-19" y="0"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="0" y="0"/>
+        <delta pt="10" x="0" y="0"/>
+        <delta pt="11" x="0" y="0"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="0"/>
+        <delta pt="14" x="0" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+        <delta pt="17" x="0" y="0"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="uni0020">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="14" y="0"/>
+        <delta pt="2" x="0" y="0"/>
+        <delta pt="3" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="-28" y="0"/>
+        <delta pt="2" x="0" y="0"/>
+        <delta pt="3" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="uni0041">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="0" x="7" y="0"/>
+        <delta pt="1" x="7" y="-20"/>
+        <delta pt="2" x="-6" y="-29"/>
+        <delta pt="3" x="-12" y="-29"/>
+        <delta pt="4" x="-25" y="-20"/>
+        <delta pt="5" x="-25" y="0"/>
+        <delta pt="6" x="14" y="0"/>
+        <delta pt="7" x="4" y="9"/>
+        <delta pt="8" x="-36" y="9"/>
+        <delta pt="9" x="-37" y="0"/>
+        <delta pt="10" x="24" y="0"/>
+        <delta pt="11" x="9" y="58"/>
+        <delta pt="12" x="3" y="68"/>
+        <delta pt="13" x="-4" y="0"/>
+        <delta pt="14" x="3" y="28"/>
+        <delta pt="15" x="-4" y="2"/>
+        <delta pt="16" x="4" y="2"/>
+        <delta pt="17" x="-4" y="28"/>
+        <delta pt="18" x="20" y="0"/>
+        <delta pt="19" x="20" y="-20"/>
+        <delta pt="20" x="14" y="-29"/>
+        <delta pt="21" x="8" y="-29"/>
+        <delta pt="22" x="-2" y="-20"/>
+        <delta pt="23" x="-2" y="0"/>
+        <delta pt="24" x="0" y="0"/>
+        <delta pt="25" x="-10" y="0"/>
+        <delta pt="26" x="0" y="9"/>
+        <delta pt="27" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="5" y="0"/>
+        <delta pt="1" x="5" y="19"/>
+        <delta pt="2" x="9" y="19"/>
+        <delta pt="3" x="6" y="19"/>
+        <delta pt="4" x="-15" y="19"/>
+        <delta pt="5" x="-15" y="0"/>
+        <delta pt="6" x="-6" y="0"/>
+        <delta pt="7" x="-14" y="-23"/>
+        <delta pt="8" x="46" y="-23"/>
+        <delta pt="9" x="39" y="0"/>
+        <delta pt="10" x="-69" y="0"/>
+        <delta pt="11" x="-27" y="-86"/>
+        <delta pt="12" x="-7" y="-16"/>
+        <delta pt="13" x="11" y="0"/>
+        <delta pt="14" x="-2" y="-39"/>
+        <delta pt="15" x="-1" y="-22"/>
+        <delta pt="16" x="-1" y="-22"/>
+        <delta pt="17" x="8" y="-39"/>
+        <delta pt="18" x="-41" y="0"/>
+        <delta pt="19" x="-41" y="16"/>
+        <delta pt="20" x="-59" y="16"/>
+        <delta pt="21" x="6" y="16"/>
+        <delta pt="22" x="12" y="16"/>
+        <delta pt="23" x="12" y="0"/>
+        <delta pt="24" x="0" y="0"/>
+        <delta pt="25" x="17" y="0"/>
+        <delta pt="26" x="0" y="-23"/>
+        <delta pt="27" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="2" y="0"/>
+        <delta pt="1" x="2" y="-9"/>
+        <delta pt="2" x="-4" y="-9"/>
+        <delta pt="3" x="-4" y="-9"/>
+        <delta pt="4" x="-2" y="-9"/>
+        <delta pt="5" x="-2" y="0"/>
+        <delta pt="6" x="2" y="0"/>
+        <delta pt="7" x="-4" y="0"/>
+        <delta pt="8" x="-4" y="0"/>
+        <delta pt="9" x="-4" y="0"/>
+        <delta pt="10" x="-4" y="0"/>
+        <delta pt="11" x="-6" y="8"/>
+        <delta pt="12" x="-10" y="0"/>
+        <delta pt="13" x="-2" y="0"/>
+        <delta pt="14" x="0" y="5"/>
+        <delta pt="15" x="-3" y="-5"/>
+        <delta pt="16" x="5" y="-5"/>
+        <delta pt="17" x="-1" y="5"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="0" y="-8"/>
+        <delta pt="20" x="4" y="-8"/>
+        <delta pt="21" x="0" y="-8"/>
+        <delta pt="22" x="0" y="-8"/>
+        <delta pt="23" x="0" y="0"/>
+        <delta pt="24" x="0" y="0"/>
+        <delta pt="25" x="0" y="0"/>
+        <delta pt="26" x="0" y="0"/>
+        <delta pt="27" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="-2" y="0"/>
+        <delta pt="1" x="-2" y="9"/>
+        <delta pt="2" x="4" y="9"/>
+        <delta pt="3" x="4" y="9"/>
+        <delta pt="4" x="2" y="9"/>
+        <delta pt="5" x="2" y="0"/>
+        <delta pt="6" x="-2" y="0"/>
+        <delta pt="7" x="4" y="0"/>
+        <delta pt="8" x="4" y="0"/>
+        <delta pt="9" x="4" y="0"/>
+        <delta pt="10" x="4" y="0"/>
+        <delta pt="11" x="6" y="-8"/>
+        <delta pt="12" x="10" y="0"/>
+        <delta pt="13" x="2" y="0"/>
+        <delta pt="14" x="0" y="-5"/>
+        <delta pt="15" x="3" y="5"/>
+        <delta pt="16" x="-5" y="5"/>
+        <delta pt="17" x="1" y="-5"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="0" y="8"/>
+        <delta pt="20" x="-4" y="8"/>
+        <delta pt="21" x="0" y="8"/>
+        <delta pt="22" x="0" y="8"/>
+        <delta pt="23" x="0" y="0"/>
+        <delta pt="24" x="0" y="0"/>
+        <delta pt="25" x="0" y="0"/>
+        <delta pt="26" x="0" y="0"/>
+        <delta pt="27" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="3" y="0"/>
+        <delta pt="1" x="3" y="-15"/>
+        <delta pt="2" x="-6" y="-15"/>
+        <delta pt="3" x="-6" y="-15"/>
+        <delta pt="4" x="-3" y="-15"/>
+        <delta pt="5" x="-3" y="0"/>
+        <delta pt="6" x="3" y="0"/>
+        <delta pt="7" x="-6" y="0"/>
+        <delta pt="8" x="-6" y="0"/>
+        <delta pt="9" x="-6" y="0"/>
+        <delta pt="10" x="-6" y="0"/>
+        <delta pt="11" x="-11" y="13"/>
+        <delta pt="12" x="-17" y="0"/>
+        <delta pt="13" x="-3" y="0"/>
+        <delta pt="14" x="-1" y="8"/>
+        <delta pt="15" x="-5" y="-9"/>
+        <delta pt="16" x="8" y="-9"/>
+        <delta pt="17" x="-1" y="8"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="0" y="-13"/>
+        <delta pt="20" x="6" y="-13"/>
+        <delta pt="21" x="0" y="-13"/>
+        <delta pt="22" x="0" y="-13"/>
+        <delta pt="23" x="0" y="0"/>
+        <delta pt="24" x="0" y="0"/>
+        <delta pt="25" x="0" y="0"/>
+        <delta pt="26" x="0" y="0"/>
+        <delta pt="27" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="uni0061">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="0" x="11" y="-8"/>
+        <delta pt="1" x="11" y="4"/>
+        <delta pt="2" x="22" y="5"/>
+        <delta pt="3" x="-4" y="-8"/>
+        <delta pt="4" x="6" y="-5"/>
+        <delta pt="5" x="3" y="-11"/>
+        <delta pt="6" x="4" y="-9"/>
+        <delta pt="7" x="4" y="9"/>
+        <delta pt="8" x="0" y="7"/>
+        <delta pt="9" x="-9" y="8"/>
+        <delta pt="10" x="-24" y="3"/>
+        <delta pt="11" x="-18" y="6"/>
+        <delta pt="12" x="-44" y="1"/>
+        <delta pt="13" x="-44" y="-16"/>
+        <delta pt="14" x="-44" y="-22"/>
+        <delta pt="15" x="-36" y="-39"/>
+        <delta pt="16" x="-24" y="-39"/>
+        <delta pt="17" x="-7" y="-39"/>
+        <delta pt="18" x="26" y="-15"/>
+        <delta pt="19" x="26" y="3"/>
+        <delta pt="20" x="17" y="0"/>
+        <delta pt="21" x="3" y="-4"/>
+        <delta pt="22" x="23" y="15"/>
+        <delta pt="23" x="22" y="8"/>
+        <delta pt="24" x="6" y="0"/>
+        <delta pt="25" x="0" y="0"/>
+        <delta pt="26" x="2" y="0"/>
+        <delta pt="27" x="11" y="-2"/>
+        <delta pt="28" x="30" y="7"/>
+        <delta pt="29" x="30" y="4"/>
+        <delta pt="30" x="30" y="13"/>
+        <delta pt="31" x="14" y="21"/>
+        <delta pt="32" x="3" y="21"/>
+        <delta pt="33" x="-15" y="21"/>
+        <delta pt="34" x="-32" y="5"/>
+        <delta pt="35" x="-34" y="-9"/>
+        <delta pt="36" x="-48" y="-14"/>
+        <delta pt="37" x="-40" y="4"/>
+        <delta pt="38" x="-36" y="14"/>
+        <delta pt="39" x="-24" y="27"/>
+        <delta pt="40" x="-13" y="27"/>
+        <delta pt="41" x="12" y="27"/>
+        <delta pt="42" x="10" y="6"/>
+        <delta pt="43" x="12" y="5"/>
+        <delta pt="44" x="-4" y="-4"/>
+        <delta pt="45" x="-16" y="-4"/>
+        <delta pt="46" x="-20" y="-4"/>
+        <delta pt="47" x="-22" y="7"/>
+        <delta pt="48" x="-22" y="25"/>
+        <delta pt="49" x="-22" y="10"/>
+        <delta pt="50" x="-22" y="-15"/>
+        <delta pt="51" x="-16" y="-30"/>
+        <delta pt="52" x="-9" y="-30"/>
+        <delta pt="53" x="-12" y="-30"/>
+        <delta pt="54" x="-11" y="-35"/>
+        <delta pt="55" x="-5" y="-35"/>
+        <delta pt="56" x="-15" y="-27"/>
+        <delta pt="57" x="-10" y="-3"/>
+        <delta pt="58" x="9" y="-3"/>
+        <delta pt="59" x="14" y="-3"/>
+        <delta pt="60" x="33" y="-1"/>
+        <delta pt="61" x="0" y="0"/>
+        <delta pt="62" x="-3" y="0"/>
+        <delta pt="63" x="0" y="-4"/>
+        <delta pt="64" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="-21" y="1"/>
+        <delta pt="1" x="-21" y="17"/>
+        <delta pt="2" x="-2" y="28"/>
+        <delta pt="3" x="20" y="23"/>
+        <delta pt="4" x="19" y="20"/>
+        <delta pt="5" x="28" y="21"/>
+        <delta pt="6" x="26" y="23"/>
+        <delta pt="7" x="26" y="15"/>
+        <delta pt="8" x="24" y="12"/>
+        <delta pt="9" x="30" y="17"/>
+        <delta pt="10" x="31" y="15"/>
+        <delta pt="11" x="77" y="31"/>
+        <delta pt="12" x="66" y="36"/>
+        <delta pt="13" x="66" y="18"/>
+        <delta pt="14" x="66" y="21"/>
+        <delta pt="15" x="49" y="19"/>
+        <delta pt="16" x="37" y="19"/>
+        <delta pt="17" x="21" y="19"/>
+        <delta pt="18" x="-2" y="5"/>
+        <delta pt="19" x="-34" y="-18"/>
+        <delta pt="20" x="-6" y="3"/>
+        <delta pt="21" x="-11" y="12"/>
+        <delta pt="22" x="-29" y="-11"/>
+        <delta pt="23" x="-17" y="-2"/>
+        <delta pt="24" x="-13" y="-3"/>
+        <delta pt="25" x="-25" y="-3"/>
+        <delta pt="26" x="-29" y="-3"/>
+        <delta pt="27" x="-21" y="2"/>
+        <delta pt="28" x="-34" y="-14"/>
+        <delta pt="29" x="-34" y="17"/>
+        <delta pt="30" x="-34" y="7"/>
+        <delta pt="31" x="-18" y="7"/>
+        <delta pt="32" x="-16" y="7"/>
+        <delta pt="33" x="-18" y="7"/>
+        <delta pt="34" x="-15" y="9"/>
+        <delta pt="35" x="-21" y="12"/>
+        <delta pt="36" x="19" y="23"/>
+        <delta pt="37" x="45" y="46"/>
+        <delta pt="38" x="52" y="7"/>
+        <delta pt="39" x="26" y="-21"/>
+        <delta pt="40" x="14" y="-21"/>
+        <delta pt="41" x="-5" y="-21"/>
+        <delta pt="42" x="-17" y="-7"/>
+        <delta pt="43" x="-31" y="1"/>
+        <delta pt="44" x="-12" y="16"/>
+        <delta pt="45" x="34" y="16"/>
+        <delta pt="46" x="61" y="16"/>
+        <delta pt="47" x="70" y="4"/>
+        <delta pt="48" x="70" y="-5"/>
+        <delta pt="49" x="70" y="-22"/>
+        <delta pt="50" x="70" y="4"/>
+        <delta pt="51" x="59" y="22"/>
+        <delta pt="52" x="50" y="22"/>
+        <delta pt="53" x="43" y="22"/>
+        <delta pt="54" x="37" y="19"/>
+        <delta pt="55" x="38" y="22"/>
+        <delta pt="56" x="47" y="28"/>
+        <delta pt="57" x="46" y="-6"/>
+        <delta pt="58" x="-2" y="-6"/>
+        <delta pt="59" x="-16" y="-6"/>
+        <delta pt="60" x="-25" y="-13"/>
+        <delta pt="61" x="0" y="0"/>
+        <delta pt="62" x="32" y="0"/>
+        <delta pt="63" x="0" y="16"/>
+        <delta pt="64" x="0" y="3"/>
+      </tuple>
+      <tuple>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="0" y="-3"/>
+        <delta pt="1" x="0" y="-1"/>
+        <delta pt="2" x="0" y="-3"/>
+        <delta pt="3" x="0" y="-3"/>
+        <delta pt="4" x="0" y="-3"/>
+        <delta pt="5" x="0" y="-3"/>
+        <delta pt="6" x="0" y="-3"/>
+        <delta pt="7" x="0" y="4"/>
+        <delta pt="8" x="0" y="4"/>
+        <delta pt="9" x="2" y="5"/>
+        <delta pt="10" x="6" y="7"/>
+        <delta pt="11" x="1" y="5"/>
+        <delta pt="12" x="0" y="-1"/>
+        <delta pt="13" x="0" y="-6"/>
+        <delta pt="14" x="0" y="-6"/>
+        <delta pt="15" x="-1" y="-6"/>
+        <delta pt="16" x="0" y="-6"/>
+        <delta pt="17" x="0" y="-6"/>
+        <delta pt="18" x="0" y="-5"/>
+        <delta pt="19" x="0" y="-4"/>
+        <delta pt="20" x="0" y="-1"/>
+        <delta pt="21" x="0" y="0"/>
+        <delta pt="22" x="0" y="0"/>
+        <delta pt="23" x="0" y="0"/>
+        <delta pt="24" x="0" y="0"/>
+        <delta pt="25" x="0" y="0"/>
+        <delta pt="26" x="0" y="0"/>
+        <delta pt="27" x="0" y="-1"/>
+        <delta pt="28" x="0" y="-2"/>
+        <delta pt="29" x="0" y="7"/>
+        <delta pt="30" x="0" y="6"/>
+        <delta pt="31" x="0" y="7"/>
+        <delta pt="32" x="0" y="7"/>
+        <delta pt="33" x="0" y="7"/>
+        <delta pt="34" x="0" y="7"/>
+        <delta pt="35" x="0" y="7"/>
+        <delta pt="36" x="0" y="0"/>
+        <delta pt="37" x="0" y="0"/>
+        <delta pt="38" x="0" y="0"/>
+        <delta pt="39" x="0" y="0"/>
+        <delta pt="40" x="0" y="0"/>
+        <delta pt="41" x="0" y="0"/>
+        <delta pt="42" x="0" y="0"/>
+        <delta pt="43" x="0" y="0"/>
+        <delta pt="44" x="0" y="0"/>
+        <delta pt="45" x="0" y="0"/>
+        <delta pt="46" x="0" y="0"/>
+        <delta pt="47" x="0" y="0"/>
+        <delta pt="48" x="0" y="0"/>
+        <delta pt="49" x="0" y="-6"/>
+        <delta pt="50" x="0" y="-7"/>
+        <delta pt="51" x="0" y="-8"/>
+        <delta pt="52" x="0" y="-8"/>
+        <delta pt="53" x="1" y="-8"/>
+        <delta pt="54" x="2" y="-5"/>
+        <delta pt="55" x="4" y="-2"/>
+        <delta pt="56" x="0" y="0"/>
+        <delta pt="57" x="0" y="0"/>
+        <delta pt="58" x="0" y="0"/>
+        <delta pt="59" x="0" y="0"/>
+        <delta pt="60" x="0" y="-1"/>
+        <delta pt="61" x="0" y="0"/>
+        <delta pt="62" x="0" y="0"/>
+        <delta pt="63" x="0" y="0"/>
+        <delta pt="64" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="0" y="3"/>
+        <delta pt="1" x="0" y="1"/>
+        <delta pt="2" x="0" y="3"/>
+        <delta pt="3" x="0" y="3"/>
+        <delta pt="4" x="0" y="3"/>
+        <delta pt="5" x="0" y="3"/>
+        <delta pt="6" x="0" y="3"/>
+        <delta pt="7" x="0" y="-4"/>
+        <delta pt="8" x="0" y="-4"/>
+        <delta pt="9" x="-2" y="-5"/>
+        <delta pt="10" x="-6" y="-7"/>
+        <delta pt="11" x="-1" y="-5"/>
+        <delta pt="12" x="0" y="1"/>
+        <delta pt="13" x="0" y="6"/>
+        <delta pt="14" x="0" y="6"/>
+        <delta pt="15" x="1" y="6"/>
+        <delta pt="16" x="0" y="6"/>
+        <delta pt="17" x="0" y="6"/>
+        <delta pt="18" x="0" y="5"/>
+        <delta pt="19" x="0" y="4"/>
+        <delta pt="20" x="0" y="1"/>
+        <delta pt="21" x="0" y="0"/>
+        <delta pt="22" x="0" y="0"/>
+        <delta pt="23" x="0" y="0"/>
+        <delta pt="24" x="0" y="0"/>
+        <delta pt="25" x="0" y="0"/>
+        <delta pt="26" x="0" y="0"/>
+        <delta pt="27" x="0" y="1"/>
+        <delta pt="28" x="0" y="2"/>
+        <delta pt="29" x="0" y="-7"/>
+        <delta pt="30" x="0" y="-6"/>
+        <delta pt="31" x="0" y="-7"/>
+        <delta pt="32" x="0" y="-7"/>
+        <delta pt="33" x="0" y="-7"/>
+        <delta pt="34" x="0" y="-7"/>
+        <delta pt="35" x="0" y="-7"/>
+        <delta pt="36" x="0" y="0"/>
+        <delta pt="37" x="0" y="0"/>
+        <delta pt="38" x="0" y="0"/>
+        <delta pt="39" x="0" y="0"/>
+        <delta pt="40" x="0" y="0"/>
+        <delta pt="41" x="0" y="0"/>
+        <delta pt="42" x="0" y="0"/>
+        <delta pt="43" x="0" y="0"/>
+        <delta pt="44" x="0" y="0"/>
+        <delta pt="45" x="0" y="0"/>
+        <delta pt="46" x="0" y="0"/>
+        <delta pt="47" x="0" y="0"/>
+        <delta pt="48" x="0" y="0"/>
+        <delta pt="49" x="0" y="6"/>
+        <delta pt="50" x="0" y="7"/>
+        <delta pt="51" x="0" y="8"/>
+        <delta pt="52" x="0" y="8"/>
+        <delta pt="53" x="-1" y="8"/>
+        <delta pt="54" x="-2" y="5"/>
+        <delta pt="55" x="-4" y="2"/>
+        <delta pt="56" x="0" y="0"/>
+        <delta pt="57" x="0" y="0"/>
+        <delta pt="58" x="0" y="0"/>
+        <delta pt="59" x="0" y="0"/>
+        <delta pt="60" x="0" y="1"/>
+        <delta pt="61" x="0" y="0"/>
+        <delta pt="62" x="0" y="0"/>
+        <delta pt="63" x="0" y="0"/>
+        <delta pt="64" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="0" y="-5"/>
+        <delta pt="1" x="0" y="0"/>
+        <delta pt="2" x="3" y="-4"/>
+        <delta pt="3" x="0" y="-4"/>
+        <delta pt="4" x="0" y="-4"/>
+        <delta pt="5" x="0" y="-4"/>
+        <delta pt="6" x="0" y="-4"/>
+        <delta pt="7" x="0" y="8"/>
+        <delta pt="8" x="0" y="8"/>
+        <delta pt="9" x="5" y="9"/>
+        <delta pt="10" x="11" y="13"/>
+        <delta pt="11" x="2" y="10"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="-9"/>
+        <delta pt="14" x="0" y="-9"/>
+        <delta pt="15" x="-1" y="-9"/>
+        <delta pt="16" x="0" y="-9"/>
+        <delta pt="17" x="0" y="-9"/>
+        <delta pt="18" x="0" y="-10"/>
+        <delta pt="19" x="0" y="-8"/>
+        <delta pt="20" x="0" y="-2"/>
+        <delta pt="21" x="0" y="1"/>
+        <delta pt="22" x="0" y="0"/>
+        <delta pt="23" x="1" y="-1"/>
+        <delta pt="24" x="0" y="0"/>
+        <delta pt="25" x="0" y="0"/>
+        <delta pt="26" x="0" y="0"/>
+        <delta pt="27" x="0" y="-1"/>
+        <delta pt="28" x="0" y="-4"/>
+        <delta pt="29" x="0" y="12"/>
+        <delta pt="30" x="0" y="13"/>
+        <delta pt="31" x="0" y="13"/>
+        <delta pt="32" x="0" y="13"/>
+        <delta pt="33" x="0" y="13"/>
+        <delta pt="34" x="0" y="13"/>
+        <delta pt="35" x="0" y="13"/>
+        <delta pt="36" x="0" y="0"/>
+        <delta pt="37" x="0" y="0"/>
+        <delta pt="38" x="0" y="0"/>
+        <delta pt="39" x="0" y="1"/>
+        <delta pt="40" x="0" y="1"/>
+        <delta pt="41" x="0" y="1"/>
+        <delta pt="42" x="0" y="1"/>
+        <delta pt="43" x="0" y="0"/>
+        <delta pt="44" x="0" y="0"/>
+        <delta pt="45" x="0" y="0"/>
+        <delta pt="46" x="0" y="0"/>
+        <delta pt="47" x="0" y="-1"/>
+        <delta pt="48" x="0" y="-1"/>
+        <delta pt="49" x="0" y="-9"/>
+        <delta pt="50" x="0" y="-13"/>
+        <delta pt="51" x="1" y="-14"/>
+        <delta pt="52" x="1" y="-14"/>
+        <delta pt="53" x="2" y="-14"/>
+        <delta pt="54" x="5" y="-11"/>
+        <delta pt="55" x="7" y="-4"/>
+        <delta pt="56" x="0" y="0"/>
+        <delta pt="57" x="0" y="0"/>
+        <delta pt="58" x="0" y="0"/>
+        <delta pt="59" x="0" y="0"/>
+        <delta pt="60" x="1" y="0"/>
+        <delta pt="61" x="0" y="0"/>
+        <delta pt="62" x="0" y="0"/>
+        <delta pt="63" x="0" y="0"/>
+        <delta pt="64" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="uni0024">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="0" x="-3" y="-28"/>
+        <delta pt="1" x="2" y="-28"/>
+        <delta pt="2" x="17" y="-12"/>
+        <delta pt="3" x="17" y="2"/>
+        <delta pt="4" x="17" y="4"/>
+        <delta pt="5" x="20" y="25"/>
+        <delta pt="6" x="-2" y="42"/>
+        <delta pt="7" x="-2" y="46"/>
+        <delta pt="8" x="8" y="49"/>
+        <delta pt="9" x="28" y="45"/>
+        <delta pt="10" x="28" y="30"/>
+        <delta pt="11" x="28" y="35"/>
+        <delta pt="12" x="21" y="7"/>
+        <delta pt="13" x="4" y="7"/>
+        <delta pt="14" x="-9" y="7"/>
+        <delta pt="15" x="-21" y="19"/>
+        <delta pt="16" x="-8" y="17"/>
+        <delta pt="17" x="-3" y="22"/>
+        <delta pt="18" x="9" y="27"/>
+        <delta pt="19" x="13" y="27"/>
+        <delta pt="20" x="22" y="27"/>
+        <delta pt="21" x="41" y="9"/>
+        <delta pt="22" x="45" y="-19"/>
+        <delta pt="23" x="34" y="5"/>
+        <delta pt="24" x="20" y="-6"/>
+        <delta pt="25" x="18" y="18"/>
+        <delta pt="26" x="12" y="36"/>
+        <delta pt="27" x="-7" y="36"/>
+        <delta pt="28" x="-18" y="36"/>
+        <delta pt="29" x="-24" y="21"/>
+        <delta pt="30" x="-24" y="9"/>
+        <delta pt="31" x="-24" y="6"/>
+        <delta pt="32" x="-17" y="-13"/>
+        <delta pt="33" x="0" y="-25"/>
+        <delta pt="34" x="-19" y="-20"/>
+        <delta pt="35" x="-20" y="-28"/>
+        <delta pt="36" x="-35" y="-28"/>
+        <delta pt="37" x="-35" y="-19"/>
+        <delta pt="38" x="-35" y="-22"/>
+        <delta pt="39" x="-32" y="0"/>
+        <delta pt="40" x="2" y="0"/>
+        <delta pt="41" x="2" y="0"/>
+        <delta pt="42" x="17" y="-9"/>
+        <delta pt="43" x="2" y="-10"/>
+        <delta pt="44" x="-4" y="-14"/>
+        <delta pt="45" x="-14" y="-20"/>
+        <delta pt="46" x="-20" y="-20"/>
+        <delta pt="47" x="-30" y="-20"/>
+        <delta pt="48" x="-45" y="-2"/>
+        <delta pt="49" x="-51" y="26"/>
+        <delta pt="50" x="-40" y="2"/>
+        <delta pt="51" x="-23" y="14"/>
+        <delta pt="52" x="-22" y="-10"/>
+        <delta pt="53" x="-23" y="-28"/>
+        <delta pt="54" x="-16" y="12"/>
+        <delta pt="55" x="2" y="12"/>
+        <delta pt="56" x="2" y="12"/>
+        <delta pt="57" x="-16" y="12"/>
+        <delta pt="58" x="3" y="0"/>
+        <delta pt="59" x="3" y="12"/>
+        <delta pt="60" x="-15" y="12"/>
+        <delta pt="61" x="-15" y="0"/>
+        <delta pt="62" x="0" y="0"/>
+        <delta pt="63" x="-7" y="0"/>
+        <delta pt="64" x="0" y="12"/>
+        <delta pt="65" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="12" y="4"/>
+        <delta pt="1" x="0" y="4"/>
+        <delta pt="2" x="-18" y="-10"/>
+        <delta pt="3" x="-18" y="-28"/>
+        <delta pt="4" x="-18" y="-28"/>
+        <delta pt="5" x="-7" y="-40"/>
+        <delta pt="6" x="3" y="-39"/>
+        <delta pt="7" x="9" y="-39"/>
+        <delta pt="8" x="0" y="-30"/>
+        <delta pt="9" x="-5" y="-25"/>
+        <delta pt="10" x="-5" y="-21"/>
+        <delta pt="11" x="-5" y="-20"/>
+        <delta pt="12" x="8" y="-20"/>
+        <delta pt="13" x="40" y="-20"/>
+        <delta pt="14" x="55" y="-20"/>
+        <delta pt="15" x="63" y="-11"/>
+        <delta pt="16" x="58" y="-10"/>
+        <delta pt="17" x="60" y="-25"/>
+        <delta pt="18" x="47" y="-35"/>
+        <delta pt="19" x="42" y="-35"/>
+        <delta pt="20" x="30" y="-35"/>
+        <delta pt="21" x="13" y="-29"/>
+        <delta pt="22" x="14" y="-12"/>
+        <delta pt="23" x="21" y="-21"/>
+        <delta pt="24" x="54" y="-32"/>
+        <delta pt="25" x="46" y="-27"/>
+        <delta pt="26" x="50" y="-26"/>
+        <delta pt="27" x="41" y="-26"/>
+        <delta pt="28" x="50" y="-26"/>
+        <delta pt="29" x="68" y="-11"/>
+        <delta pt="30" x="68" y="-2"/>
+        <delta pt="31" x="68" y="10"/>
+        <delta pt="32" x="56" y="31"/>
+        <delta pt="33" x="50" y="29"/>
+        <delta pt="34" x="44" y="29"/>
+        <delta pt="35" x="46" y="22"/>
+        <delta pt="36" x="62" y="24"/>
+        <delta pt="37" x="62" y="6"/>
+        <delta pt="38" x="62" y="5"/>
+        <delta pt="39" x="38" y="-3"/>
+        <delta pt="40" x="18" y="-3"/>
+        <delta pt="41" x="-6" y="-3"/>
+        <delta pt="42" x="-3" y="-12"/>
+        <delta pt="43" x="0" y="-9"/>
+        <delta pt="44" x="4" y="4"/>
+        <delta pt="45" x="18" y="12"/>
+        <delta pt="46" x="22" y="12"/>
+        <delta pt="47" x="36" y="12"/>
+        <delta pt="48" x="39" y="5"/>
+        <delta pt="49" x="34" y="6"/>
+        <delta pt="50" x="25" y="-3"/>
+        <delta pt="51" x="0" y="16"/>
+        <delta pt="52" x="3" y="12"/>
+        <delta pt="53" x="-8" y="4"/>
+        <delta pt="54" x="46" y="-8"/>
+        <delta pt="55" x="15" y="-8"/>
+        <delta pt="56" x="15" y="-19"/>
+        <delta pt="57" x="46" y="-19"/>
+        <delta pt="58" x="8" y="0"/>
+        <delta pt="59" x="8" y="-8"/>
+        <delta pt="60" x="39" y="-8"/>
+        <delta pt="61" x="39" y="0"/>
+        <delta pt="62" x="0" y="0"/>
+        <delta pt="63" x="63" y="0"/>
+        <delta pt="64" x="0" y="-19"/>
+        <delta pt="65" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="0" y="-7"/>
+        <delta pt="1" x="-1" y="-7"/>
+        <delta pt="2" x="0" y="-3"/>
+        <delta pt="3" x="0" y="-2"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="0" y="0"/>
+        <delta pt="10" x="0" y="0"/>
+        <delta pt="11" x="0" y="0"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="0"/>
+        <delta pt="14" x="2" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+        <delta pt="17" x="-1" y="0"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="0" y="0"/>
+        <delta pt="20" x="0" y="0"/>
+        <delta pt="21" x="0" y="0"/>
+        <delta pt="22" x="0" y="0"/>
+        <delta pt="23" x="-1" y="9"/>
+        <delta pt="24" x="0" y="7"/>
+        <delta pt="25" x="0" y="7"/>
+        <delta pt="26" x="0" y="7"/>
+        <delta pt="27" x="0" y="7"/>
+        <delta pt="28" x="-1" y="7"/>
+        <delta pt="29" x="0" y="2"/>
+        <delta pt="30" x="0" y="3"/>
+        <delta pt="31" x="0" y="1"/>
+        <delta pt="32" x="0" y="0"/>
+        <delta pt="33" x="0" y="0"/>
+        <delta pt="34" x="0" y="0"/>
+        <delta pt="35" x="0" y="0"/>
+        <delta pt="36" x="0" y="0"/>
+        <delta pt="37" x="0" y="1"/>
+        <delta pt="38" x="0" y="1"/>
+        <delta pt="39" x="0" y="0"/>
+        <delta pt="40" x="0" y="0"/>
+        <delta pt="41" x="0" y="0"/>
+        <delta pt="42" x="0" y="0"/>
+        <delta pt="43" x="0" y="0"/>
+        <delta pt="44" x="0" y="0"/>
+        <delta pt="45" x="0" y="0"/>
+        <delta pt="46" x="0" y="0"/>
+        <delta pt="47" x="0" y="0"/>
+        <delta pt="48" x="0" y="0"/>
+        <delta pt="49" x="0" y="0"/>
+        <delta pt="50" x="0" y="-6"/>
+        <delta pt="51" x="0" y="-7"/>
+        <delta pt="52" x="0" y="-6"/>
+        <delta pt="53" x="0" y="-7"/>
+        <delta pt="54" x="-4" y="0"/>
+        <delta pt="55" x="4" y="0"/>
+        <delta pt="56" x="4" y="0"/>
+        <delta pt="57" x="-4" y="0"/>
+        <delta pt="58" x="4" y="0"/>
+        <delta pt="59" x="4" y="0"/>
+        <delta pt="60" x="-4" y="0"/>
+        <delta pt="61" x="-4" y="0"/>
+        <delta pt="62" x="0" y="0"/>
+        <delta pt="63" x="0" y="0"/>
+        <delta pt="64" x="0" y="0"/>
+        <delta pt="65" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="0" y="7"/>
+        <delta pt="1" x="1" y="7"/>
+        <delta pt="2" x="0" y="3"/>
+        <delta pt="3" x="0" y="2"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="0" y="0"/>
+        <delta pt="10" x="0" y="0"/>
+        <delta pt="11" x="0" y="0"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="0"/>
+        <delta pt="14" x="-2" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+        <delta pt="17" x="1" y="0"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="0" y="0"/>
+        <delta pt="20" x="0" y="0"/>
+        <delta pt="21" x="0" y="0"/>
+        <delta pt="22" x="0" y="0"/>
+        <delta pt="23" x="1" y="-9"/>
+        <delta pt="24" x="0" y="-7"/>
+        <delta pt="25" x="0" y="-7"/>
+        <delta pt="26" x="0" y="-7"/>
+        <delta pt="27" x="0" y="-7"/>
+        <delta pt="28" x="1" y="-7"/>
+        <delta pt="29" x="0" y="-2"/>
+        <delta pt="30" x="0" y="-3"/>
+        <delta pt="31" x="0" y="-1"/>
+        <delta pt="32" x="0" y="0"/>
+        <delta pt="33" x="0" y="0"/>
+        <delta pt="34" x="0" y="0"/>
+        <delta pt="35" x="0" y="0"/>
+        <delta pt="36" x="0" y="0"/>
+        <delta pt="37" x="0" y="-1"/>
+        <delta pt="38" x="0" y="-1"/>
+        <delta pt="39" x="0" y="0"/>
+        <delta pt="40" x="0" y="0"/>
+        <delta pt="41" x="0" y="0"/>
+        <delta pt="42" x="0" y="0"/>
+        <delta pt="43" x="0" y="0"/>
+        <delta pt="44" x="0" y="0"/>
+        <delta pt="45" x="0" y="0"/>
+        <delta pt="46" x="0" y="0"/>
+        <delta pt="47" x="0" y="0"/>
+        <delta pt="48" x="0" y="0"/>
+        <delta pt="49" x="0" y="0"/>
+        <delta pt="50" x="0" y="6"/>
+        <delta pt="51" x="0" y="7"/>
+        <delta pt="52" x="0" y="6"/>
+        <delta pt="53" x="0" y="7"/>
+        <delta pt="54" x="4" y="0"/>
+        <delta pt="55" x="-4" y="0"/>
+        <delta pt="56" x="-4" y="0"/>
+        <delta pt="57" x="4" y="0"/>
+        <delta pt="58" x="-4" y="0"/>
+        <delta pt="59" x="-4" y="0"/>
+        <delta pt="60" x="4" y="0"/>
+        <delta pt="61" x="4" y="0"/>
+        <delta pt="62" x="0" y="0"/>
+        <delta pt="63" x="0" y="0"/>
+        <delta pt="64" x="0" y="0"/>
+        <delta pt="65" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="0" y="-13"/>
+        <delta pt="1" x="-1" y="-13"/>
+        <delta pt="2" x="0" y="-3"/>
+        <delta pt="3" x="0" y="-3"/>
+        <delta pt="4" x="0" y="-1"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="0" y="0"/>
+        <delta pt="10" x="0" y="0"/>
+        <delta pt="11" x="0" y="0"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="0"/>
+        <delta pt="14" x="4" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+        <delta pt="17" x="0" y="1"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="-1" y="0"/>
+        <delta pt="20" x="0" y="0"/>
+        <delta pt="21" x="0" y="1"/>
+        <delta pt="22" x="0" y="0"/>
+        <delta pt="23" x="-2" y="16"/>
+        <delta pt="24" x="0" y="13"/>
+        <delta pt="25" x="0" y="13"/>
+        <delta pt="26" x="0" y="13"/>
+        <delta pt="27" x="0" y="13"/>
+        <delta pt="28" x="0" y="13"/>
+        <delta pt="29" x="0" y="2"/>
+        <delta pt="30" x="0" y="5"/>
+        <delta pt="31" x="0" y="1"/>
+        <delta pt="32" x="0" y="0"/>
+        <delta pt="33" x="0" y="0"/>
+        <delta pt="34" x="0" y="0"/>
+        <delta pt="35" x="0" y="0"/>
+        <delta pt="36" x="0" y="2"/>
+        <delta pt="37" x="0" y="1"/>
+        <delta pt="38" x="0" y="1"/>
+        <delta pt="39" x="0" y="0"/>
+        <delta pt="40" x="0" y="0"/>
+        <delta pt="41" x="0" y="0"/>
+        <delta pt="42" x="0" y="0"/>
+        <delta pt="43" x="0" y="0"/>
+        <delta pt="44" x="0" y="0"/>
+        <delta pt="45" x="0" y="0"/>
+        <delta pt="46" x="0" y="0"/>
+        <delta pt="47" x="0" y="0"/>
+        <delta pt="48" x="1" y="0"/>
+        <delta pt="49" x="0" y="0"/>
+        <delta pt="50" x="0" y="-9"/>
+        <delta pt="51" x="0" y="-13"/>
+        <delta pt="52" x="0" y="-14"/>
+        <delta pt="53" x="0" y="-13"/>
+        <delta pt="54" x="-6" y="0"/>
+        <delta pt="55" x="6" y="0"/>
+        <delta pt="56" x="6" y="0"/>
+        <delta pt="57" x="-6" y="0"/>
+        <delta pt="58" x="6" y="0"/>
+        <delta pt="59" x="6" y="0"/>
+        <delta pt="60" x="-6" y="0"/>
+        <delta pt="61" x="-6" y="0"/>
+        <delta pt="62" x="0" y="0"/>
+        <delta pt="63" x="0" y="0"/>
+        <delta pt="64" x="0" y="0"/>
+        <delta pt="65" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="uni0024.nostroke">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="0" x="-3" y="-28"/>
+        <delta pt="1" x="2" y="-28"/>
+        <delta pt="2" x="17" y="-12"/>
+        <delta pt="3" x="17" y="2"/>
+        <delta pt="4" x="17" y="4"/>
+        <delta pt="5" x="20" y="25"/>
+        <delta pt="6" x="-2" y="42"/>
+        <delta pt="7" x="-2" y="46"/>
+        <delta pt="8" x="8" y="49"/>
+        <delta pt="9" x="28" y="45"/>
+        <delta pt="10" x="28" y="30"/>
+        <delta pt="11" x="28" y="35"/>
+        <delta pt="12" x="21" y="7"/>
+        <delta pt="13" x="4" y="7"/>
+        <delta pt="14" x="-9" y="7"/>
+        <delta pt="15" x="-21" y="19"/>
+        <delta pt="16" x="-8" y="17"/>
+        <delta pt="17" x="-3" y="22"/>
+        <delta pt="18" x="9" y="27"/>
+        <delta pt="19" x="13" y="27"/>
+        <delta pt="20" x="22" y="27"/>
+        <delta pt="21" x="41" y="9"/>
+        <delta pt="22" x="45" y="-19"/>
+        <delta pt="23" x="34" y="5"/>
+        <delta pt="24" x="20" y="-6"/>
+        <delta pt="25" x="18" y="18"/>
+        <delta pt="26" x="12" y="36"/>
+        <delta pt="27" x="-7" y="36"/>
+        <delta pt="28" x="-18" y="36"/>
+        <delta pt="29" x="-24" y="21"/>
+        <delta pt="30" x="-24" y="9"/>
+        <delta pt="31" x="-24" y="6"/>
+        <delta pt="32" x="-17" y="-13"/>
+        <delta pt="33" x="0" y="-25"/>
+        <delta pt="34" x="-19" y="-20"/>
+        <delta pt="35" x="-20" y="-28"/>
+        <delta pt="36" x="-35" y="-28"/>
+        <delta pt="37" x="-35" y="-19"/>
+        <delta pt="38" x="-35" y="-22"/>
+        <delta pt="39" x="-32" y="0"/>
+        <delta pt="40" x="2" y="0"/>
+        <delta pt="41" x="2" y="0"/>
+        <delta pt="42" x="17" y="-9"/>
+        <delta pt="43" x="2" y="-10"/>
+        <delta pt="44" x="-4" y="-14"/>
+        <delta pt="45" x="-14" y="-20"/>
+        <delta pt="46" x="-20" y="-20"/>
+        <delta pt="47" x="-30" y="-20"/>
+        <delta pt="48" x="-45" y="-2"/>
+        <delta pt="49" x="-51" y="26"/>
+        <delta pt="50" x="-40" y="2"/>
+        <delta pt="51" x="-23" y="14"/>
+        <delta pt="52" x="-22" y="-10"/>
+        <delta pt="53" x="-23" y="-28"/>
+        <delta pt="54" x="-16" y="22"/>
+        <delta pt="55" x="2" y="22"/>
+        <delta pt="56" x="2" y="12"/>
+        <delta pt="57" x="-16" y="12"/>
+        <delta pt="58" x="3" y="0"/>
+        <delta pt="59" x="3" y="-16"/>
+        <delta pt="60" x="-15" y="-16"/>
+        <delta pt="61" x="-15" y="0"/>
+        <delta pt="62" x="0" y="0"/>
+        <delta pt="63" x="-7" y="0"/>
+        <delta pt="64" x="0" y="12"/>
+        <delta pt="65" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="12" y="4"/>
+        <delta pt="1" x="-6" y="4"/>
+        <delta pt="2" x="-24" y="-7"/>
+        <delta pt="3" x="-24" y="-25"/>
+        <delta pt="4" x="-24" y="-31"/>
+        <delta pt="5" x="-12" y="-41"/>
+        <delta pt="6" x="3" y="-39"/>
+        <delta pt="7" x="9" y="-37"/>
+        <delta pt="8" x="4" y="-25"/>
+        <delta pt="9" x="-1" y="-24"/>
+        <delta pt="10" x="-1" y="-21"/>
+        <delta pt="11" x="-1" y="-20"/>
+        <delta pt="12" x="10" y="-20"/>
+        <delta pt="13" x="40" y="-20"/>
+        <delta pt="14" x="55" y="-20"/>
+        <delta pt="15" x="63" y="-11"/>
+        <delta pt="16" x="58" y="-10"/>
+        <delta pt="17" x="60" y="-25"/>
+        <delta pt="18" x="47" y="-35"/>
+        <delta pt="19" x="42" y="-35"/>
+        <delta pt="20" x="30" y="-35"/>
+        <delta pt="21" x="13" y="-29"/>
+        <delta pt="22" x="14" y="-12"/>
+        <delta pt="23" x="21" y="-21"/>
+        <delta pt="24" x="54" y="-32"/>
+        <delta pt="25" x="46" y="-27"/>
+        <delta pt="26" x="49" y="-26"/>
+        <delta pt="27" x="40" y="-26"/>
+        <delta pt="28" x="55" y="-26"/>
+        <delta pt="29" x="80" y="-10"/>
+        <delta pt="30" x="80" y="1"/>
+        <delta pt="31" x="80" y="15"/>
+        <delta pt="32" x="62" y="34"/>
+        <delta pt="33" x="50" y="31"/>
+        <delta pt="34" x="44" y="29"/>
+        <delta pt="35" x="44" y="17"/>
+        <delta pt="36" x="62" y="24"/>
+        <delta pt="37" x="62" y="6"/>
+        <delta pt="38" x="62" y="5"/>
+        <delta pt="39" x="38" y="-3"/>
+        <delta pt="40" x="18" y="-3"/>
+        <delta pt="41" x="-6" y="-3"/>
+        <delta pt="42" x="-3" y="-12"/>
+        <delta pt="43" x="0" y="-9"/>
+        <delta pt="44" x="4" y="4"/>
+        <delta pt="45" x="18" y="12"/>
+        <delta pt="46" x="22" y="12"/>
+        <delta pt="47" x="36" y="12"/>
+        <delta pt="48" x="39" y="5"/>
+        <delta pt="49" x="34" y="6"/>
+        <delta pt="50" x="25" y="-3"/>
+        <delta pt="51" x="0" y="16"/>
+        <delta pt="52" x="3" y="12"/>
+        <delta pt="53" x="-8" y="4"/>
+        <delta pt="54" x="46" y="-23"/>
+        <delta pt="55" x="15" y="-23"/>
+        <delta pt="56" x="15" y="-19"/>
+        <delta pt="57" x="46" y="-19"/>
+        <delta pt="58" x="18" y="0"/>
+        <delta pt="59" x="18" y="2"/>
+        <delta pt="60" x="49" y="2"/>
+        <delta pt="61" x="49" y="0"/>
+        <delta pt="62" x="0" y="0"/>
+        <delta pt="63" x="63" y="0"/>
+        <delta pt="64" x="0" y="-19"/>
+        <delta pt="65" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="0" y="-7"/>
+        <delta pt="1" x="-1" y="-7"/>
+        <delta pt="2" x="-1" y="-6"/>
+        <delta pt="3" x="-1" y="-5"/>
+        <delta pt="4" x="-1" y="-4"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="0" y="0"/>
+        <delta pt="10" x="0" y="0"/>
+        <delta pt="11" x="0" y="0"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="0"/>
+        <delta pt="14" x="2" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+        <delta pt="17" x="-1" y="0"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="0" y="0"/>
+        <delta pt="20" x="0" y="0"/>
+        <delta pt="21" x="0" y="0"/>
+        <delta pt="22" x="0" y="0"/>
+        <delta pt="23" x="-1" y="9"/>
+        <delta pt="24" x="0" y="7"/>
+        <delta pt="25" x="0" y="7"/>
+        <delta pt="26" x="0" y="7"/>
+        <delta pt="27" x="0" y="7"/>
+        <delta pt="28" x="0" y="7"/>
+        <delta pt="29" x="1" y="2"/>
+        <delta pt="30" x="1" y="4"/>
+        <delta pt="31" x="1" y="2"/>
+        <delta pt="32" x="0" y="0"/>
+        <delta pt="33" x="0" y="0"/>
+        <delta pt="34" x="0" y="0"/>
+        <delta pt="35" x="0" y="0"/>
+        <delta pt="36" x="0" y="0"/>
+        <delta pt="37" x="0" y="1"/>
+        <delta pt="38" x="0" y="1"/>
+        <delta pt="39" x="0" y="0"/>
+        <delta pt="40" x="0" y="0"/>
+        <delta pt="41" x="0" y="0"/>
+        <delta pt="42" x="0" y="0"/>
+        <delta pt="43" x="0" y="0"/>
+        <delta pt="44" x="0" y="0"/>
+        <delta pt="45" x="0" y="0"/>
+        <delta pt="46" x="0" y="0"/>
+        <delta pt="47" x="0" y="0"/>
+        <delta pt="48" x="0" y="0"/>
+        <delta pt="49" x="0" y="0"/>
+        <delta pt="50" x="0" y="-6"/>
+        <delta pt="51" x="0" y="-7"/>
+        <delta pt="52" x="0" y="-6"/>
+        <delta pt="53" x="0" y="-7"/>
+        <delta pt="54" x="-4" y="4"/>
+        <delta pt="55" x="4" y="4"/>
+        <delta pt="56" x="4" y="0"/>
+        <delta pt="57" x="-4" y="0"/>
+        <delta pt="58" x="0" y="0"/>
+        <delta pt="59" x="0" y="-4"/>
+        <delta pt="60" x="-7" y="-4"/>
+        <delta pt="61" x="-7" y="0"/>
+        <delta pt="62" x="0" y="0"/>
+        <delta pt="63" x="0" y="0"/>
+        <delta pt="64" x="0" y="0"/>
+        <delta pt="65" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="0" y="7"/>
+        <delta pt="1" x="1" y="7"/>
+        <delta pt="2" x="1" y="6"/>
+        <delta pt="3" x="1" y="5"/>
+        <delta pt="4" x="1" y="4"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="0" y="0"/>
+        <delta pt="10" x="0" y="0"/>
+        <delta pt="11" x="0" y="0"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="0"/>
+        <delta pt="14" x="-2" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+        <delta pt="17" x="1" y="0"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="0" y="0"/>
+        <delta pt="20" x="0" y="0"/>
+        <delta pt="21" x="0" y="0"/>
+        <delta pt="22" x="0" y="0"/>
+        <delta pt="23" x="1" y="-9"/>
+        <delta pt="24" x="0" y="-7"/>
+        <delta pt="25" x="0" y="-7"/>
+        <delta pt="26" x="0" y="-7"/>
+        <delta pt="27" x="0" y="-7"/>
+        <delta pt="28" x="0" y="-7"/>
+        <delta pt="29" x="-1" y="-2"/>
+        <delta pt="30" x="-1" y="-4"/>
+        <delta pt="31" x="-1" y="-2"/>
+        <delta pt="32" x="0" y="0"/>
+        <delta pt="33" x="0" y="0"/>
+        <delta pt="34" x="0" y="0"/>
+        <delta pt="35" x="0" y="0"/>
+        <delta pt="36" x="0" y="0"/>
+        <delta pt="37" x="0" y="-1"/>
+        <delta pt="38" x="0" y="-1"/>
+        <delta pt="39" x="0" y="0"/>
+        <delta pt="40" x="0" y="0"/>
+        <delta pt="41" x="0" y="0"/>
+        <delta pt="42" x="0" y="0"/>
+        <delta pt="43" x="0" y="0"/>
+        <delta pt="44" x="0" y="0"/>
+        <delta pt="45" x="0" y="0"/>
+        <delta pt="46" x="0" y="0"/>
+        <delta pt="47" x="0" y="0"/>
+        <delta pt="48" x="0" y="0"/>
+        <delta pt="49" x="0" y="0"/>
+        <delta pt="50" x="0" y="6"/>
+        <delta pt="51" x="0" y="7"/>
+        <delta pt="52" x="0" y="6"/>
+        <delta pt="53" x="0" y="7"/>
+        <delta pt="54" x="4" y="-4"/>
+        <delta pt="55" x="-4" y="-4"/>
+        <delta pt="56" x="-4" y="0"/>
+        <delta pt="57" x="4" y="0"/>
+        <delta pt="58" x="0" y="0"/>
+        <delta pt="59" x="0" y="4"/>
+        <delta pt="60" x="7" y="4"/>
+        <delta pt="61" x="7" y="0"/>
+        <delta pt="62" x="0" y="0"/>
+        <delta pt="63" x="0" y="0"/>
+        <delta pt="64" x="0" y="0"/>
+        <delta pt="65" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="0" y="-13"/>
+        <delta pt="1" x="-2" y="-13"/>
+        <delta pt="2" x="-3" y="-10"/>
+        <delta pt="3" x="-3" y="-9"/>
+        <delta pt="4" x="-3" y="-7"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="0" y="0"/>
+        <delta pt="10" x="0" y="0"/>
+        <delta pt="11" x="0" y="0"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="0"/>
+        <delta pt="14" x="4" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+        <delta pt="17" x="0" y="1"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="-1" y="0"/>
+        <delta pt="20" x="0" y="0"/>
+        <delta pt="21" x="0" y="1"/>
+        <delta pt="22" x="0" y="0"/>
+        <delta pt="23" x="-2" y="16"/>
+        <delta pt="24" x="0" y="13"/>
+        <delta pt="25" x="0" y="13"/>
+        <delta pt="26" x="1" y="13"/>
+        <delta pt="27" x="1" y="13"/>
+        <delta pt="28" x="0" y="13"/>
+        <delta pt="29" x="1" y="3"/>
+        <delta pt="30" x="1" y="6"/>
+        <delta pt="31" x="1" y="2"/>
+        <delta pt="32" x="3" y="-2"/>
+        <delta pt="33" x="0" y="0"/>
+        <delta pt="34" x="0" y="0"/>
+        <delta pt="35" x="0" y="0"/>
+        <delta pt="36" x="0" y="2"/>
+        <delta pt="37" x="0" y="1"/>
+        <delta pt="38" x="0" y="1"/>
+        <delta pt="39" x="0" y="0"/>
+        <delta pt="40" x="0" y="0"/>
+        <delta pt="41" x="0" y="0"/>
+        <delta pt="42" x="0" y="0"/>
+        <delta pt="43" x="0" y="0"/>
+        <delta pt="44" x="0" y="0"/>
+        <delta pt="45" x="0" y="0"/>
+        <delta pt="46" x="0" y="0"/>
+        <delta pt="47" x="0" y="0"/>
+        <delta pt="48" x="1" y="0"/>
+        <delta pt="49" x="0" y="0"/>
+        <delta pt="50" x="0" y="-9"/>
+        <delta pt="51" x="0" y="-13"/>
+        <delta pt="52" x="0" y="-14"/>
+        <delta pt="53" x="0" y="-13"/>
+        <delta pt="54" x="-6" y="8"/>
+        <delta pt="55" x="6" y="8"/>
+        <delta pt="56" x="6" y="0"/>
+        <delta pt="57" x="-6" y="0"/>
+        <delta pt="58" x="0" y="0"/>
+        <delta pt="59" x="0" y="-6"/>
+        <delta pt="60" x="-13" y="-6"/>
+        <delta pt="61" x="-13" y="0"/>
+        <delta pt="62" x="0" y="0"/>
+        <delta pt="63" x="0" y="0"/>
+        <delta pt="64" x="0" y="0"/>
+        <delta pt="65" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+  </gvar>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/Build3.ttx b/Tests/varLib/data/test_results/Build3.ttx
new file mode 100644
index 0000000..a6ae23e
--- /dev/null
+++ b/Tests/varLib/data/test_results/Build3.ttx
@@ -0,0 +1,725 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.14">
+
+  <HVAR>
+    <Version value="0x00010000"/>
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=1 -->
+        <!-- RegionCount=1 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=1 -->
+      <VarData index="0">
+        <!-- ItemCount=18 -->
+        <NumShorts value="1"/>
+        <!-- VarRegionCount=1 -->
+        <VarRegionIndex index="0" value="0"/>
+        <Item index="0" value="[80]"/>
+        <Item index="1" value="[0]"/>
+        <Item index="2" value="[64]"/>
+        <Item index="3" value="[50]"/>
+        <Item index="4" value="[40]"/>
+        <Item index="5" value="[108]"/>
+        <Item index="6" value="[56]"/>
+        <Item index="7" value="[98]"/>
+        <Item index="8" value="[206]"/>
+        <Item index="9" value="[40]"/>
+        <Item index="10" value="[72]"/>
+        <Item index="11" value="[50]"/>
+        <Item index="12" value="[128]"/>
+        <Item index="13" value="[-18]"/>
+        <Item index="14" value="[0]"/>
+        <Item index="15" value="[0]"/>
+        <Item index="16" value="[0]"/>
+        <Item index="17" value="[0]"/>
+      </VarData>
+    </VarStore>
+  </HVAR>
+
+  <MVAR>
+    <Version value="0x00010000"/>
+    <Reserved value="0"/>
+    <ValueRecordSize value="8"/>
+    <!-- ValueRecordCount=2 -->
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=1 -->
+        <!-- RegionCount=1 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=1 -->
+      <VarData index="0">
+        <!-- ItemCount=2 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=1 -->
+        <VarRegionIndex index="0" value="0"/>
+        <Item index="0" value="[13]"/>
+        <Item index="1" value="[22]"/>
+      </VarData>
+    </VarStore>
+    <ValueRecord index="0">
+      <ValueTag value="stro"/>
+      <VarIdx value="0"/>
+    </ValueRecord>
+    <ValueRecord index="1">
+      <ValueTag value="xhgt"/>
+      <VarIdx value="1"/>
+    </ValueRecord>
+  </MVAR>
+
+  <fvar>
+
+    <!-- Weight -->
+    <Axis>
+      <AxisTag>wght</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1000.0</MaxValue>
+      <AxisNameID>257</AxisNameID>
+    </Axis>
+
+    <!-- ExtraLight -->
+    <!-- PostScript: TestFamily2-ExtraLight -->
+    <NamedInstance flags="0x0" postscriptNameID="259" subfamilyNameID="258">
+      <coord axis="wght" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Light -->
+    <!-- PostScript: TestFamily2-Light -->
+    <NamedInstance flags="0x0" postscriptNameID="261" subfamilyNameID="260">
+      <coord axis="wght" value="100.0"/>
+    </NamedInstance>
+
+    <!-- Regular -->
+    <!-- PostScript: TestFamily2-Regular -->
+    <NamedInstance flags="0x0" postscriptNameID="263" subfamilyNameID="262">
+      <coord axis="wght" value="368.0"/>
+    </NamedInstance>
+
+    <!-- Semibold -->
+    <!-- PostScript: TestFamily2-Semibold -->
+    <NamedInstance flags="0x0" postscriptNameID="265" subfamilyNameID="264">
+      <coord axis="wght" value="600.0"/>
+    </NamedInstance>
+
+    <!-- Bold -->
+    <!-- PostScript: TestFamily2-Bold -->
+    <NamedInstance flags="0x0" postscriptNameID="267" subfamilyNameID="266">
+      <coord axis="wght" value="824.0"/>
+    </NamedInstance>
+
+    <!-- Black -->
+    <!-- PostScript: TestFamily2-Black -->
+    <NamedInstance flags="0x0" postscriptNameID="269" subfamilyNameID="268">
+      <coord axis="wght" value="1000.0"/>
+    </NamedInstance>
+  </fvar>
+
+  <gvar>
+    <version value="1"/>
+    <reserved value="0"/>
+    <glyphVariations glyph=".notdef">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="-20" y="0"/>
+        <delta pt="1" x="-20" y="0"/>
+        <delta pt="2" x="100" y="0"/>
+        <delta pt="3" x="100" y="0"/>
+        <delta pt="4" x="144" y="72"/>
+        <delta pt="5" x="-60" y="72"/>
+        <delta pt="6" x="14" y="-48"/>
+        <delta pt="7" x="40" y="-58"/>
+        <delta pt="8" x="40" y="-58"/>
+        <delta pt="9" x="68" y="-48"/>
+        <delta pt="10" x="40" y="58"/>
+        <delta pt="11" x="40" y="58"/>
+        <delta pt="12" x="26" y="62"/>
+        <delta pt="13" x="-50" y="-70"/>
+        <delta pt="14" x="132" y="-70"/>
+        <delta pt="15" x="56" y="62"/>
+        <delta pt="16" x="54" y="98"/>
+        <delta pt="17" x="-18" y="0"/>
+        <delta pt="18" x="54" y="-102"/>
+        <delta pt="19" x="28" y="98"/>
+        <delta pt="20" x="28" y="-102"/>
+        <delta pt="21" x="98" y="0"/>
+        <delta pt="22" x="0" y="0"/>
+        <delta pt="23" x="80" y="0"/>
+        <delta pt="24" x="0" y="0"/>
+        <delta pt="25" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="A">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="-20" y="0"/>
+        <delta pt="1" x="-58" y="-10"/>
+        <delta pt="2" x="122" y="-10"/>
+        <delta pt="3" x="84" y="0"/>
+        <delta pt="4" x="-64" y="0"/>
+        <delta pt="5" x="0" y="-80"/>
+        <delta pt="6" x="9" y="-94"/>
+        <delta pt="7" x="22" y="-91"/>
+        <delta pt="8" x="28" y="-104"/>
+        <delta pt="9" x="28" y="-104"/>
+        <delta pt="10" x="36" y="-92"/>
+        <delta pt="11" x="49" y="-94"/>
+        <delta pt="12" x="58" y="-80"/>
+        <delta pt="13" x="124" y="0"/>
+        <delta pt="14" x="20" y="-98"/>
+        <delta pt="15" x="20" y="7"/>
+        <delta pt="16" x="45" y="7"/>
+        <delta pt="17" x="45" y="-98"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="64" y="0"/>
+        <delta pt="20" x="0" y="-10"/>
+        <delta pt="21" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="a">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="-10" y="0"/>
+        <delta pt="1" x="-24" y="0"/>
+        <delta pt="2" x="-22" y="31"/>
+        <delta pt="3" x="-22" y="25"/>
+        <delta pt="4" x="-22" y="23"/>
+        <delta pt="5" x="-46" y="29"/>
+        <delta pt="6" x="-64" y="26"/>
+        <delta pt="7" x="-69" y="-5"/>
+        <delta pt="8" x="-58" y="-86"/>
+        <delta pt="9" x="-16" y="-86"/>
+        <delta pt="10" x="8" y="-86"/>
+        <delta pt="11" x="31" y="-67"/>
+        <delta pt="12" x="14" y="-70"/>
+        <delta pt="13" x="-30" y="18"/>
+        <delta pt="14" x="0" y="34"/>
+        <delta pt="15" x="16" y="22"/>
+        <delta pt="16" x="16" y="22"/>
+        <delta pt="17" x="30" y="22"/>
+        <delta pt="18" x="78" y="19"/>
+        <delta pt="19" x="78" y="-32"/>
+        <delta pt="20" x="78" y="0"/>
+        <delta pt="21" x="-36" y="0"/>
+        <delta pt="22" x="-44" y="-16"/>
+        <delta pt="23" x="-46" y="-16"/>
+        <delta pt="24" x="-38" y="-13"/>
+        <delta pt="25" x="-18" y="0"/>
+        <delta pt="26" x="48" y="104"/>
+        <delta pt="27" x="25" y="104"/>
+        <delta pt="28" x="-30" y="81"/>
+        <delta pt="29" x="-64" y="56"/>
+        <delta pt="30" x="-64" y="-50"/>
+        <delta pt="31" x="32" y="-41"/>
+        <delta pt="32" x="110" y="-3"/>
+        <delta pt="33" x="110" y="38"/>
+        <delta pt="34" x="110" y="77"/>
+        <delta pt="35" x="70" y="104"/>
+        <delta pt="36" x="0" y="0"/>
+        <delta pt="37" x="50" y="0"/>
+        <delta pt="38" x="0" y="22"/>
+        <delta pt="39" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="d">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="-12" y="0"/>
+        <delta pt="1" x="-15" y="0"/>
+        <delta pt="2" x="-18" y="12"/>
+        <delta pt="3" x="-18" y="12"/>
+        <delta pt="4" x="-18" y="19"/>
+        <delta pt="5" x="-17" y="22"/>
+        <delta pt="6" x="-28" y="22"/>
+        <delta pt="7" x="-32" y="22"/>
+        <delta pt="8" x="-44" y="26"/>
+        <delta pt="9" x="-60" y="32"/>
+        <delta pt="10" x="-64" y="14"/>
+        <delta pt="11" x="-64" y="-26"/>
+        <delta pt="12" x="78" y="-26"/>
+        <delta pt="13" x="78" y="0"/>
+        <delta pt="14" x="-36" y="0"/>
+        <delta pt="15" x="-44" y="-18"/>
+        <delta pt="16" x="-46" y="-18"/>
+        <delta pt="17" x="-42" y="-14"/>
+        <delta pt="18" x="-29" y="0"/>
+        <delta pt="19" x="32" y="112"/>
+        <delta pt="20" x="10" y="112"/>
+        <delta pt="21" x="-38" y="83"/>
+        <delta pt="22" x="-64" y="64"/>
+        <delta pt="23" x="-64" y="-48"/>
+        <delta pt="24" x="-39" y="-70"/>
+        <delta pt="25" x="-6" y="-90"/>
+        <delta pt="26" x="16" y="-90"/>
+        <delta pt="27" x="65" y="-90"/>
+        <delta pt="28" x="126" y="-13"/>
+        <delta pt="29" x="126" y="14"/>
+        <delta pt="30" x="126" y="45"/>
+        <delta pt="31" x="79" y="112"/>
+        <delta pt="32" x="0" y="0"/>
+        <delta pt="33" x="40" y="0"/>
+        <delta pt="34" x="0" y="-26"/>
+        <delta pt="35" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="f">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="-12" y="0"/>
+        <delta pt="1" x="-12" y="-90"/>
+        <delta pt="2" x="-12" y="-80"/>
+        <delta pt="3" x="16" y="-26"/>
+        <delta pt="4" x="76" y="-26"/>
+        <delta pt="5" x="95" y="-26"/>
+        <delta pt="6" x="116" y="-30"/>
+        <delta pt="7" x="116" y="-28"/>
+        <delta pt="8" x="96" y="-128"/>
+        <delta pt="9" x="97" y="-132"/>
+        <delta pt="10" x="104" y="-132"/>
+        <delta pt="11" x="120" y="-132"/>
+        <delta pt="12" x="130" y="-99"/>
+        <delta pt="13" x="130" y="-80"/>
+        <delta pt="14" x="130" y="0"/>
+        <delta pt="15" x="-12" y="-84"/>
+        <delta pt="16" x="-12" y="20"/>
+        <delta pt="17" x="-2" y="22"/>
+        <delta pt="18" x="100" y="22"/>
+        <delta pt="19" x="100" y="-84"/>
+        <delta pt="20" x="0" y="0"/>
+        <delta pt="21" x="108" y="0"/>
+        <delta pt="22" x="0" y="-26"/>
+        <delta pt="23" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="n">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="-38" y="0"/>
+        <delta pt="1" x="-38" y="22"/>
+        <delta pt="2" x="76" y="22"/>
+        <delta pt="3" x="84" y="38"/>
+        <delta pt="4" x="86" y="38"/>
+        <delta pt="5" x="78" y="28"/>
+        <delta pt="6" x="77" y="22"/>
+        <delta pt="7" x="78" y="22"/>
+        <delta pt="8" x="86" y="22"/>
+        <delta pt="9" x="90" y="0"/>
+        <delta pt="10" x="90" y="0"/>
+        <delta pt="11" x="90" y="0"/>
+        <delta pt="12" x="-52" y="0"/>
+        <delta pt="13" x="-52" y="-18"/>
+        <delta pt="14" x="-52" y="-50"/>
+        <delta pt="15" x="-22" y="-96"/>
+        <delta pt="16" x="14" y="-96"/>
+        <delta pt="17" x="35" y="-96"/>
+        <delta pt="18" x="78" y="-68"/>
+        <delta pt="19" x="104" y="-38"/>
+        <delta pt="20" x="104" y="0"/>
+        <delta pt="21" x="0" y="0"/>
+        <delta pt="22" x="56" y="0"/>
+        <delta pt="23" x="0" y="22"/>
+        <delta pt="24" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="t">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="46" y="0"/>
+        <delta pt="1" x="14" y="0"/>
+        <delta pt="2" x="-26" y="36"/>
+        <delta pt="3" x="-26" y="66"/>
+        <delta pt="4" x="-26" y="-84"/>
+        <delta pt="5" x="-16" y="-84"/>
+        <delta pt="6" x="-16" y="20"/>
+        <delta pt="7" x="-16" y="22"/>
+        <delta pt="8" x="0" y="12"/>
+        <delta pt="9" x="116" y="12"/>
+        <delta pt="10" x="116" y="22"/>
+        <delta pt="11" x="88" y="22"/>
+        <delta pt="12" x="88" y="-84"/>
+        <delta pt="13" x="116" y="-84"/>
+        <delta pt="14" x="116" y="73"/>
+        <delta pt="15" x="116" y="78"/>
+        <delta pt="16" x="120" y="106"/>
+        <delta pt="17" x="92" y="106"/>
+        <delta pt="18" x="90" y="106"/>
+        <delta pt="19" x="79" y="101"/>
+        <delta pt="20" x="74" y="98"/>
+        <delta pt="21" x="90" y="0"/>
+        <delta pt="22" x="91" y="2"/>
+        <delta pt="23" x="75" y="0"/>
+        <delta pt="24" x="0" y="0"/>
+        <delta pt="25" x="98" y="0"/>
+        <delta pt="26" x="0" y="12"/>
+        <delta pt="27" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="f_t">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="-12" y="0"/>
+        <delta pt="1" x="-12" y="-90"/>
+        <delta pt="2" x="-12" y="-80"/>
+        <delta pt="3" x="16" y="-26"/>
+        <delta pt="4" x="76" y="-26"/>
+        <delta pt="5" x="95" y="-26"/>
+        <delta pt="6" x="116" y="-30"/>
+        <delta pt="7" x="116" y="-28"/>
+        <delta pt="8" x="96" y="-128"/>
+        <delta pt="9" x="97" y="-132"/>
+        <delta pt="10" x="104" y="-132"/>
+        <delta pt="11" x="120" y="-132"/>
+        <delta pt="12" x="130" y="-99"/>
+        <delta pt="13" x="130" y="-80"/>
+        <delta pt="14" x="130" y="0"/>
+        <delta pt="15" x="154" y="0"/>
+        <delta pt="16" x="122" y="0"/>
+        <delta pt="17" x="82" y="36"/>
+        <delta pt="18" x="82" y="66"/>
+        <delta pt="19" x="82" y="-84"/>
+        <delta pt="20" x="-12" y="-84"/>
+        <delta pt="21" x="-12" y="20"/>
+        <delta pt="22" x="-2" y="22"/>
+        <delta pt="23" x="92" y="22"/>
+        <delta pt="24" x="108" y="12"/>
+        <delta pt="25" x="224" y="12"/>
+        <delta pt="26" x="224" y="22"/>
+        <delta pt="27" x="196" y="22"/>
+        <delta pt="28" x="196" y="-84"/>
+        <delta pt="29" x="224" y="-84"/>
+        <delta pt="30" x="224" y="73"/>
+        <delta pt="31" x="224" y="78"/>
+        <delta pt="32" x="228" y="106"/>
+        <delta pt="33" x="200" y="106"/>
+        <delta pt="34" x="198" y="106"/>
+        <delta pt="35" x="187" y="101"/>
+        <delta pt="36" x="182" y="98"/>
+        <delta pt="37" x="198" y="0"/>
+        <delta pt="38" x="199" y="2"/>
+        <delta pt="39" x="183" y="0"/>
+        <delta pt="40" x="0" y="0"/>
+        <delta pt="41" x="206" y="0"/>
+        <delta pt="42" x="0" y="-26"/>
+        <delta pt="43" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="a.alt">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="-12" y="0"/>
+        <delta pt="1" x="-15" y="0"/>
+        <delta pt="2" x="-18" y="12"/>
+        <delta pt="3" x="-18" y="12"/>
+        <delta pt="4" x="-18" y="19"/>
+        <delta pt="5" x="-13" y="22"/>
+        <delta pt="6" x="-24" y="22"/>
+        <delta pt="7" x="-32" y="22"/>
+        <delta pt="8" x="-36" y="24"/>
+        <delta pt="9" x="-42" y="18"/>
+        <delta pt="10" x="-40" y="18"/>
+        <delta pt="11" x="-28" y="22"/>
+        <delta pt="12" x="78" y="22"/>
+        <delta pt="13" x="78" y="0"/>
+        <delta pt="14" x="-36" y="0"/>
+        <delta pt="15" x="-44" y="-18"/>
+        <delta pt="16" x="-46" y="-18"/>
+        <delta pt="17" x="-42" y="-14"/>
+        <delta pt="18" x="-29" y="0"/>
+        <delta pt="19" x="32" y="112"/>
+        <delta pt="20" x="10" y="112"/>
+        <delta pt="21" x="-38" y="83"/>
+        <delta pt="22" x="-64" y="64"/>
+        <delta pt="23" x="-64" y="-48"/>
+        <delta pt="24" x="-39" y="-70"/>
+        <delta pt="25" x="-6" y="-90"/>
+        <delta pt="26" x="16" y="-90"/>
+        <delta pt="27" x="65" y="-90"/>
+        <delta pt="28" x="126" y="-13"/>
+        <delta pt="29" x="126" y="14"/>
+        <delta pt="30" x="126" y="45"/>
+        <delta pt="31" x="79" y="112"/>
+        <delta pt="32" x="0" y="0"/>
+        <delta pt="33" x="40" y="0"/>
+        <delta pt="34" x="0" y="22"/>
+        <delta pt="35" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="A.sc">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="-20" y="0"/>
+        <delta pt="1" x="-52" y="22"/>
+        <delta pt="2" x="125" y="22"/>
+        <delta pt="3" x="92" y="0"/>
+        <delta pt="4" x="-54" y="0"/>
+        <delta pt="5" x="5" y="-60"/>
+        <delta pt="6" x="14" y="-71"/>
+        <delta pt="7" x="26" y="-58"/>
+        <delta pt="8" x="32" y="-66"/>
+        <delta pt="9" x="32" y="-66"/>
+        <delta pt="10" x="40" y="-58"/>
+        <delta pt="11" x="52" y="-70"/>
+        <delta pt="12" x="61" y="-60"/>
+        <delta pt="13" x="122" y="0"/>
+        <delta pt="14" x="21" y="-82"/>
+        <delta pt="15" x="21" y="12"/>
+        <delta pt="16" x="52" y="12"/>
+        <delta pt="17" x="52" y="-82"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="72" y="0"/>
+        <delta pt="20" x="0" y="22"/>
+        <delta pt="21" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="atilde">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="24" y="0"/>
+        <delta pt="2" x="0" y="0"/>
+        <delta pt="3" x="50" y="0"/>
+        <delta pt="4" x="0" y="40"/>
+        <delta pt="5" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="ampersand">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="22" y="0"/>
+        <delta pt="1" x="-16" y="0"/>
+        <delta pt="2" x="-16" y="18"/>
+        <delta pt="3" x="-16" y="10"/>
+        <delta pt="4" x="-16" y="22"/>
+        <delta pt="5" x="-14" y="24"/>
+        <delta pt="6" x="-11" y="9"/>
+        <delta pt="7" x="-8" y="-13"/>
+        <delta pt="8" x="-6" y="-37"/>
+        <delta pt="9" x="-6" y="-46"/>
+        <delta pt="10" x="-6" y="-58"/>
+        <delta pt="11" x="9" y="-94"/>
+        <delta pt="12" x="32" y="-94"/>
+        <delta pt="13" x="58" y="-94"/>
+        <delta pt="14" x="79" y="-58"/>
+        <delta pt="15" x="79" y="-44"/>
+        <delta pt="16" x="79" y="-23"/>
+        <delta pt="17" x="87" y="22"/>
+        <delta pt="18" x="100" y="62"/>
+        <delta pt="19" x="108" y="77"/>
+        <delta pt="20" x="111" y="87"/>
+        <delta pt="21" x="120" y="102"/>
+        <delta pt="22" x="120" y="110"/>
+        <delta pt="23" x="92" y="0"/>
+        <delta pt="24" x="72" y="-3"/>
+        <delta pt="25" x="36" y="-4"/>
+        <delta pt="26" x="23" y="-7"/>
+        <delta pt="27" x="2" y="-11"/>
+        <delta pt="28" x="-23" y="-20"/>
+        <delta pt="29" x="-32" y="-33"/>
+        <delta pt="30" x="-32" y="-42"/>
+        <delta pt="31" x="-32" y="-35"/>
+        <delta pt="32" x="-4" y="-10"/>
+        <delta pt="33" x="26" y="-10"/>
+        <delta pt="34" x="53" y="-10"/>
+        <delta pt="35" x="94" y="-28"/>
+        <delta pt="36" x="94" y="-48"/>
+        <delta pt="37" x="94" y="-52"/>
+        <delta pt="38" x="100" y="-48"/>
+        <delta pt="39" x="108" y="-31"/>
+        <delta pt="40" x="114" y="-3"/>
+        <delta pt="41" x="114" y="18"/>
+        <delta pt="42" x="114" y="57"/>
+        <delta pt="43" x="66" y="102"/>
+        <delta pt="44" x="42" y="102"/>
+        <delta pt="45" x="28" y="102"/>
+        <delta pt="46" x="11" y="88"/>
+        <delta pt="47" x="10" y="78"/>
+        <delta pt="48" x="6" y="66"/>
+        <delta pt="49" x="8" y="40"/>
+        <delta pt="50" x="4" y="32"/>
+        <delta pt="51" x="130" y="32"/>
+        <delta pt="52" x="129" y="32"/>
+        <delta pt="53" x="118" y="30"/>
+        <delta pt="54" x="106" y="20"/>
+        <delta pt="55" x="96" y="10"/>
+        <delta pt="56" x="51" y="0"/>
+        <delta pt="57" x="0" y="0"/>
+        <delta pt="58" x="128" y="0"/>
+        <delta pt="59" x="0" y="-10"/>
+        <delta pt="60" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="uni25CC">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="-6" y="-1"/>
+        <delta pt="1" x="-10" y="-1"/>
+        <delta pt="2" x="-20" y="4"/>
+        <delta pt="3" x="-20" y="12"/>
+        <delta pt="4" x="-20" y="18"/>
+        <delta pt="5" x="-10" y="26"/>
+        <delta pt="6" x="-6" y="26"/>
+        <delta pt="9" x="8" y="-1"/>
+        <delta pt="10" x="-6" y="-3"/>
+        <delta pt="12" x="-22" y="4"/>
+        <delta pt="13" x="-22" y="12"/>
+        <delta pt="14" x="-22" y="17"/>
+        <delta pt="16" x="-6" y="25"/>
+        <delta pt="18" x="8" y="12"/>
+        <delta pt="20" x="-6" y="-5"/>
+        <delta pt="21" x="-10" y="-5"/>
+        <delta pt="22" x="-20" y="3"/>
+        <delta pt="23" x="-20" y="9"/>
+        <delta pt="25" x="-10" y="23"/>
+        <delta pt="26" x="-6" y="23"/>
+        <delta pt="29" x="8" y="-5"/>
+        <delta pt="31" x="-13" y="-1"/>
+        <delta pt="33" x="-23" y="12"/>
+        <delta pt="35" x="-13" y="27"/>
+        <delta pt="36" x="-7" y="27"/>
+        <delta pt="37" x="-2" y="27"/>
+        <delta pt="39" x="8" y="12"/>
+        <delta pt="42" x="-13" y="-5"/>
+        <delta pt="43" x="-23" y="2"/>
+        <delta pt="44" x="-23" y="9"/>
+        <delta pt="45" x="-23" y="14"/>
+        <delta pt="46" x="-13" y="23"/>
+        <delta pt="47" x="-7" y="23"/>
+        <delta pt="48" x="-2" y="23"/>
+        <delta pt="49" x="8" y="14"/>
+        <delta pt="50" x="8" y="9"/>
+        <delta pt="53" x="-13" y="-1"/>
+        <delta pt="54" x="-22" y="8"/>
+        <delta pt="56" x="-22" y="20"/>
+        <delta pt="57" x="-13" y="27"/>
+        <delta pt="60" x="6" y="14"/>
+        <delta pt="63" x="-13" y="-5"/>
+        <delta pt="65" x="-22" y="10"/>
+        <delta pt="67" x="-13" y="22"/>
+        <delta pt="70" x="6" y="10"/>
+        <delta pt="73" x="-15" y="-1"/>
+        <delta pt="75" x="-25" y="12"/>
+        <delta pt="78" x="-9" y="27"/>
+        <delta pt="79" x="-3" y="27"/>
+        <delta pt="81" x="7" y="12"/>
+        <delta pt="84" x="-15" y="-5"/>
+        <delta pt="85" x="-25" y="1"/>
+        <delta pt="86" x="-25" y="9"/>
+        <delta pt="87" x="-25" y="15"/>
+        <delta pt="89" x="-9" y="24"/>
+        <delta pt="90" x="-3" y="24"/>
+        <delta pt="91" x="7" y="15"/>
+        <delta pt="92" x="7" y="9"/>
+        <delta pt="94" x="-8" y="-1"/>
+        <delta pt="95" x="-16" y="-1"/>
+        <delta pt="96" x="-25" y="4"/>
+        <delta pt="97" x="-25" y="12"/>
+        <delta pt="98" x="-25" y="18"/>
+        <delta pt="99" x="-16" y="26"/>
+        <delta pt="100" x="-8" y="26"/>
+        <delta pt="103" x="6" y="-1"/>
+        <delta pt="105" x="-25" y="-3"/>
+        <delta pt="106" x="-25" y="12"/>
+        <delta pt="108" x="-10" y="25"/>
+        <delta pt="109" x="-3" y="25"/>
+        <delta pt="110" x="5" y="17"/>
+        <delta pt="111" x="5" y="12"/>
+        <delta pt="113" x="-8" y="-4"/>
+        <delta pt="114" x="-16" y="-4"/>
+        <delta pt="116" x="-25" y="10"/>
+        <delta pt="118" x="-16" y="24"/>
+        <delta pt="119" x="-8" y="24"/>
+        <delta pt="122" x="6" y="-4"/>
+        <delta pt="124" x="-18" y="0"/>
+        <delta pt="125" x="0" y="22"/>
+        <delta pt="126" x="0" y="1"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="uni0303">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="-13" y="-8"/>
+        <delta pt="1" x="-4" y="-8"/>
+        <delta pt="2" x="-4" y="-30"/>
+        <delta pt="3" x="2" y="-52"/>
+        <delta pt="4" x="18" y="-52"/>
+        <delta pt="5" x="34" y="-52"/>
+        <delta pt="6" x="45" y="-13"/>
+        <delta pt="7" x="44" y="0"/>
+        <delta pt="8" x="-36" y="4"/>
+        <delta pt="9" x="-37" y="46"/>
+        <delta pt="10" x="0" y="40"/>
+        <delta pt="11" x="12" y="40"/>
+        <delta pt="12" x="4" y="40"/>
+        <delta pt="13" x="3" y="62"/>
+        <delta pt="14" x="-3" y="84"/>
+        <delta pt="15" x="-19" y="84"/>
+        <delta pt="16" x="-35" y="84"/>
+        <delta pt="17" x="-45" y="44"/>
+        <delta pt="18" x="-44" y="32"/>
+        <delta pt="19" x="36" y="28"/>
+        <delta pt="20" x="37" y="-15"/>
+        <delta pt="21" x="0" y="-8"/>
+        <delta pt="22" x="0" y="0"/>
+        <delta pt="23" x="0" y="0"/>
+        <delta pt="24" x="0" y="40"/>
+        <delta pt="25" x="0" y="8"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="uni0308">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="1" x="-49" y="-40"/>
+        <delta pt="2" x="-76" y="-12"/>
+        <delta pt="4" x="-76" y="28"/>
+        <delta pt="7" x="-7" y="56"/>
+        <delta pt="8" x="20" y="28"/>
+        <delta pt="10" x="20" y="-12"/>
+        <delta pt="13" x="7" y="-40"/>
+        <delta pt="14" x="-20" y="-12"/>
+        <delta pt="16" x="-20" y="28"/>
+        <delta pt="19" x="49" y="56"/>
+        <delta pt="20" x="76" y="28"/>
+        <delta pt="22" x="76" y="-12"/>
+        <delta pt="26" x="0" y="56"/>
+        <delta pt="27" x="0" y="40"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="uni0330">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="0" y="0"/>
+        <delta pt="2" x="0" y="0"/>
+        <delta pt="3" x="0" y="40"/>
+        <delta pt="4" x="0" y="8"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="uni0324">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="0" y="4"/>
+        <delta pt="1" x="0" y="0"/>
+        <delta pt="2" x="0" y="0"/>
+        <delta pt="3" x="0" y="60"/>
+        <delta pt="4" x="0" y="36"/>
+      </tuple>
+    </glyphVariations>
+  </gvar>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/BuildAvarEmptyAxis.ttx b/Tests/varLib/data/test_results/BuildAvarEmptyAxis.ttx
new file mode 100644
index 0000000..aee6f5a
--- /dev/null
+++ b/Tests/varLib/data/test_results/BuildAvarEmptyAxis.ttx
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.14">
+
+  <avar>
+    <segment axis="wght">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="0.6" to="0.61"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+    <segment axis="wdth">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+  </avar>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/BuildAvarIdentityMaps.ttx b/Tests/varLib/data/test_results/BuildAvarIdentityMaps.ttx
new file mode 100644
index 0000000..799d68f
--- /dev/null
+++ b/Tests/varLib/data/test_results/BuildAvarIdentityMaps.ttx
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.14">
+
+  <avar>
+    <segment axis="wght">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="-0.6667" to="-0.7969"/>
+      <mapping from="-0.3333" to="-0.5"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="0.2" to="0.18"/>
+      <mapping from="0.4" to="0.38"/>
+      <mapping from="0.6" to="0.61"/>
+      <mapping from="0.8" to="0.79"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+    <segment axis="wdth">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+  </avar>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/BuildAvarSingleAxis.ttx b/Tests/varLib/data/test_results/BuildAvarSingleAxis.ttx
new file mode 100644
index 0000000..9daa330
--- /dev/null
+++ b/Tests/varLib/data/test_results/BuildAvarSingleAxis.ttx
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.14">
+
+  <avar>
+    <segment axis="wght">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="-0.6667" to="-0.7969"/>
+      <mapping from="-0.3333" to="-0.5"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="0.2" to="0.18"/>
+      <mapping from="0.4" to="0.38"/>
+      <mapping from="0.6" to="0.61"/>
+      <mapping from="0.8" to="0.79"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+  </avar>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/BuildMain.ttx b/Tests/varLib/data/test_results/BuildMain.ttx
new file mode 100644
index 0000000..33ebbd4
--- /dev/null
+++ b/Tests/varLib/data/test_results/BuildMain.ttx
@@ -0,0 +1,2238 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.19">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="uni0020"/>
+    <GlyphID id="2" name="uni0041"/>
+    <GlyphID id="3" name="uni0061"/>
+    <GlyphID id="4" name="uni0024"/>
+    <GlyphID id="5" name="uni0024.nostroke"/>
+  </GlyphOrder>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="918"/>
+    <descent value="-335"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="663"/>
+    <minLeftSideBearing value="5"/>
+    <minRightSideBearing value="7"/>
+    <xMaxExtent value="653"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="5"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="62"/>
+    <maxContours value="4"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="1"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="1"/>
+    <maxSizeOfInstructions value="5"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="506"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000100"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="284"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="4"/>
+      <bWeight value="6"/>
+      <bProportion value="3"/>
+      <bContrast value="5"/>
+      <bStrokeVariation value="4"/>
+      <bArmStyle value="5"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000011"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBO"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="97"/>
+    <sTypoAscender value="730"/>
+    <sTypoDescender value="-270"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="918"/>
+    <usWinDescent value="335"/>
+    <ulCodePageRange1 value="00100000 00000000 00000000 00000011"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="474"/>
+    <sCapHeight value="677"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="640" lsb="80"/>
+    <mtx name="uni0020" width="234" lsb="0"/>
+    <mtx name="uni0024" width="497" lsb="51"/>
+    <mtx name="uni0024.nostroke" width="497" lsb="51"/>
+    <mtx name="uni0041" width="663" lsb="5"/>
+    <mtx name="uni0061" width="508" lsb="46"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="uni0020"/><!-- SPACE -->
+      <map code="0x24" name="uni0024"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="uni0041"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="uni0061"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="uni0020"/><!-- SPACE -->
+      <map code="0x24" name="uni0024"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="uni0041"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="uni0061"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+  </cmap>
+
+  <fpgm>
+    <assembly>
+      PUSHB[ ]	/* 1 value pushed */
+      0
+      FDEF[ ]	/* FunctionDefinition */
+        POP[ ]	/* PopTopStack */
+      ENDF[ ]	/* EndFunctionDefinition */
+    </assembly>
+  </fpgm>
+
+  <cvt>
+    <cv index="0" value="474"/>
+    <cv index="1" value="677"/>
+  </cvt>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="80" yMin="0" xMax="560" yMax="670">
+      <contour>
+        <pt x="80" y="0" on="1"/>
+        <pt x="500" y="670" on="1"/>
+        <pt x="560" y="670" on="1"/>
+        <pt x="140" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="560" y="0" on="1"/>
+        <pt x="500" y="0" on="1"/>
+        <pt x="80" y="670" on="1"/>
+        <pt x="140" y="670" on="1"/>
+      </contour>
+      <contour>
+        <pt x="140" y="50" on="1"/>
+        <pt x="500" y="50" on="1"/>
+        <pt x="500" y="620" on="1"/>
+        <pt x="140" y="620" on="1"/>
+      </contour>
+      <contour>
+        <pt x="80" y="0" on="1"/>
+        <pt x="80" y="670" on="1"/>
+        <pt x="560" y="670" on="1"/>
+        <pt x="560" y="0" on="1"/>
+      </contour>
+      <instructions>
+        <assembly>
+          PUSHB[ ]	/* 1 value pushed */
+          0
+          MDAP[0]	/* MoveDirectAbsPt */
+        </assembly>
+      </instructions>
+    </TTGlyph>
+
+    <TTGlyph name="uni0020"/><!-- contains no outline data -->
+
+    <TTGlyph name="uni0024" xMin="51" yMin="-115" xMax="461" yMax="750">
+      <contour>
+        <pt x="248" y="35" on="1"/>
+        <pt x="310" y="35" on="0"/>
+        <pt x="383" y="96" on="0"/>
+        <pt x="383" y="151" on="1"/>
+        <pt x="383" y="198" on="0"/>
+        <pt x="326" y="252" on="0"/>
+        <pt x="262" y="274" on="1"/>
+        <pt x="225" y="287" on="1"/>
+        <pt x="146" y="315" on="0"/>
+        <pt x="58" y="403" on="0"/>
+        <pt x="58" y="478" on="1"/>
+        <pt x="58" y="554" on="0"/>
+        <pt x="170" y="660" on="0"/>
+        <pt x="256" y="660" on="1"/>
+        <pt x="332" y="660" on="0"/>
+        <pt x="438" y="578" on="0"/>
+        <pt x="443" y="514" on="1"/>
+        <pt x="437" y="498" on="0"/>
+        <pt x="413" y="483" on="0"/>
+        <pt x="399" y="483" on="1"/>
+        <pt x="382" y="483" on="0"/>
+        <pt x="349" y="510" on="0"/>
+        <pt x="340" y="549" on="1"/>
+        <pt x="323" y="625" on="1"/>
+        <pt x="375" y="589" on="1"/>
+        <pt x="344" y="602" on="0"/>
+        <pt x="290" y="611" on="0"/>
+        <pt x="267" y="611" on="1"/>
+        <pt x="212" y="611" on="0"/>
+        <pt x="136" y="555" on="0"/>
+        <pt x="136" y="499" on="1"/>
+        <pt x="136" y="451" on="0"/>
+        <pt x="194" y="394" on="0"/>
+        <pt x="247" y="377" on="1"/>
+        <pt x="285" y="364" on="1"/>
+        <pt x="386" y="329" on="0"/>
+        <pt x="461" y="232" on="0"/>
+        <pt x="461" y="170" on="1"/>
+        <pt x="461" y="90" on="0"/>
+        <pt x="345" y="-13" on="0"/>
+        <pt x="243" y="-13" on="1"/>
+        <pt x="163" y="-13" on="0"/>
+        <pt x="54" y="68" on="0"/>
+        <pt x="51" y="133" on="1"/>
+        <pt x="58" y="148" on="0"/>
+        <pt x="80" y="164" on="0"/>
+        <pt x="96" y="164" on="1"/>
+        <pt x="114" y="164" on="0"/>
+        <pt x="145" y="138" on="0"/>
+        <pt x="154" y="98" on="1"/>
+        <pt x="171" y="22" on="1"/>
+        <pt x="118" y="58" on="1"/>
+        <pt x="152" y="44" on="0"/>
+        <pt x="219" y="35" on="0"/>
+      </contour>
+      <contour>
+        <pt x="279" y="322" on="1"/>
+        <pt x="239" y="322" on="1"/>
+        <pt x="239" y="750" on="1"/>
+        <pt x="279" y="750" on="1"/>
+      </contour>
+      <contour>
+        <pt x="238" y="-115" on="1"/>
+        <pt x="238" y="322" on="1"/>
+        <pt x="278" y="322" on="1"/>
+        <pt x="278" y="-115" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0024.nostroke" xMin="51" yMin="-115" xMax="461" yMax="750">
+      <contour>
+        <pt x="248" y="35" on="1"/>
+        <pt x="310" y="35" on="0"/>
+        <pt x="383" y="96" on="0"/>
+        <pt x="383" y="151" on="1"/>
+        <pt x="383" y="198" on="0"/>
+        <pt x="326" y="252" on="0"/>
+        <pt x="262" y="274" on="1"/>
+        <pt x="225" y="287" on="1"/>
+        <pt x="146" y="315" on="0"/>
+        <pt x="58" y="403" on="0"/>
+        <pt x="58" y="478" on="1"/>
+        <pt x="58" y="554" on="0"/>
+        <pt x="170" y="660" on="0"/>
+        <pt x="256" y="660" on="1"/>
+        <pt x="332" y="660" on="0"/>
+        <pt x="438" y="578" on="0"/>
+        <pt x="443" y="514" on="1"/>
+        <pt x="437" y="498" on="0"/>
+        <pt x="413" y="483" on="0"/>
+        <pt x="399" y="483" on="1"/>
+        <pt x="382" y="483" on="0"/>
+        <pt x="349" y="510" on="0"/>
+        <pt x="340" y="549" on="1"/>
+        <pt x="323" y="625" on="1"/>
+        <pt x="375" y="589" on="1"/>
+        <pt x="344" y="602" on="0"/>
+        <pt x="290" y="611" on="0"/>
+        <pt x="267" y="611" on="1"/>
+        <pt x="212" y="611" on="0"/>
+        <pt x="136" y="555" on="0"/>
+        <pt x="136" y="499" on="1"/>
+        <pt x="136" y="451" on="0"/>
+        <pt x="194" y="394" on="0"/>
+        <pt x="247" y="377" on="1"/>
+        <pt x="285" y="364" on="1"/>
+        <pt x="386" y="329" on="0"/>
+        <pt x="461" y="232" on="0"/>
+        <pt x="461" y="170" on="1"/>
+        <pt x="461" y="90" on="0"/>
+        <pt x="345" y="-13" on="0"/>
+        <pt x="243" y="-13" on="1"/>
+        <pt x="163" y="-13" on="0"/>
+        <pt x="54" y="68" on="0"/>
+        <pt x="51" y="133" on="1"/>
+        <pt x="58" y="148" on="0"/>
+        <pt x="80" y="164" on="0"/>
+        <pt x="96" y="164" on="1"/>
+        <pt x="114" y="164" on="0"/>
+        <pt x="145" y="138" on="0"/>
+        <pt x="154" y="98" on="1"/>
+        <pt x="171" y="22" on="1"/>
+        <pt x="118" y="58" on="1"/>
+        <pt x="152" y="44" on="0"/>
+        <pt x="219" y="35" on="0"/>
+      </contour>
+      <contour>
+        <pt x="279" y="635" on="1"/>
+        <pt x="239" y="635" on="1"/>
+        <pt x="239" y="750" on="1"/>
+        <pt x="279" y="750" on="1"/>
+      </contour>
+      <contour>
+        <pt x="238" y="-115" on="1"/>
+        <pt x="238" y="12" on="1"/>
+        <pt x="278" y="12" on="1"/>
+        <pt x="278" y="-115" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0041" xMin="5" yMin="0" xMax="653" yMax="675">
+      <contour>
+        <pt x="5" y="0" on="1"/>
+        <pt x="5" y="40" on="1"/>
+        <pt x="105" y="55" on="1"/>
+        <pt x="125" y="55" on="1"/>
+        <pt x="235" y="40" on="1"/>
+        <pt x="235" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="71" y="0" on="1"/>
+        <pt x="303" y="675" on="1"/>
+        <pt x="363" y="675" on="1"/>
+        <pt x="593" y="0" on="1"/>
+        <pt x="500" y="0" on="1"/>
+        <pt x="299" y="599" on="1"/>
+        <pt x="322" y="599" on="1"/>
+        <pt x="118" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="170" y="219" on="1"/>
+        <pt x="186" y="265" on="1"/>
+        <pt x="456" y="265" on="1"/>
+        <pt x="472" y="219" on="1"/>
+      </contour>
+      <contour>
+        <pt x="383" y="0" on="1"/>
+        <pt x="383" y="40" on="1"/>
+        <pt x="509" y="55" on="1"/>
+        <pt x="529" y="55" on="1"/>
+        <pt x="653" y="40" on="1"/>
+        <pt x="653" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0061" xMin="46" yMin="-13" xMax="501" yMax="487">
+      <contour>
+        <pt x="46" y="112" on="1"/>
+        <pt x="46" y="154" on="0"/>
+        <pt x="110" y="225" on="0"/>
+        <pt x="210" y="262" on="1"/>
+        <pt x="242" y="273" on="0"/>
+        <pt x="328" y="297" on="0"/>
+        <pt x="365" y="304" on="1"/>
+        <pt x="365" y="268" on="1"/>
+        <pt x="331" y="261" on="0"/>
+        <pt x="254" y="237" on="0"/>
+        <pt x="231" y="228" on="1"/>
+        <pt x="164" y="202" on="0"/>
+        <pt x="131" y="148" on="0"/>
+        <pt x="131" y="126" on="1"/>
+        <pt x="131" y="86" on="0"/>
+        <pt x="178" y="52" on="0"/>
+        <pt x="212" y="52" on="1"/>
+        <pt x="238" y="52" on="0"/>
+        <pt x="283" y="76" on="0"/>
+        <pt x="330" y="110" on="1"/>
+        <pt x="350" y="125" on="1"/>
+        <pt x="364" y="104" on="1"/>
+        <pt x="335" y="75" on="1"/>
+        <pt x="290" y="30" on="0"/>
+        <pt x="226" y="-13" on="0"/>
+        <pt x="180" y="-13" on="1"/>
+        <pt x="125" y="-13" on="0"/>
+        <pt x="46" y="50" on="0"/>
+      </contour>
+      <contour>
+        <pt x="325" y="92" on="1"/>
+        <pt x="325" y="320" on="1"/>
+        <pt x="325" y="394" on="0"/>
+        <pt x="280" y="442" on="0"/>
+        <pt x="231" y="442" on="1"/>
+        <pt x="214" y="442" on="0"/>
+        <pt x="169" y="435" on="0"/>
+        <pt x="141" y="424" on="1"/>
+        <pt x="181" y="455" on="1"/>
+        <pt x="155" y="369" on="1"/>
+        <pt x="148" y="347" on="0"/>
+        <pt x="124" y="324" on="0"/>
+        <pt x="104" y="324" on="1"/>
+        <pt x="62" y="324" on="0"/>
+        <pt x="59" y="364" on="1"/>
+        <pt x="73" y="421" on="0"/>
+        <pt x="177" y="487" on="0"/>
+        <pt x="252" y="487" on="1"/>
+        <pt x="329" y="487" on="0"/>
+        <pt x="405" y="408" on="0"/>
+        <pt x="405" y="314" on="1"/>
+        <pt x="405" y="102" on="1"/>
+        <pt x="405" y="68" on="0"/>
+        <pt x="425" y="41" on="0"/>
+        <pt x="442" y="41" on="1"/>
+        <pt x="455" y="41" on="0"/>
+        <pt x="473" y="53" on="0"/>
+        <pt x="481" y="63" on="1"/>
+        <pt x="501" y="41" on="1"/>
+        <pt x="469" y="-10" on="0"/>
+        <pt x="416" y="-10" on="1"/>
+        <pt x="375" y="-10" on="0"/>
+        <pt x="325" y="46" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="256" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Weight
+    </namerecord>
+    <namerecord nameID="257" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Contrast
+    </namerecord>
+    <namerecord nameID="258" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      ExtraLight
+    </namerecord>
+    <namerecord nameID="259" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestFamily-ExtraLight
+    </namerecord>
+    <namerecord nameID="260" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Light
+    </namerecord>
+    <namerecord nameID="261" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestFamily-Light
+    </namerecord>
+    <namerecord nameID="262" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="263" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestFamily-Regular
+    </namerecord>
+    <namerecord nameID="264" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Semibold
+    </namerecord>
+    <namerecord nameID="265" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestFamily-Semibold
+    </namerecord>
+    <namerecord nameID="266" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Bold
+    </namerecord>
+    <namerecord nameID="267" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestFamily-Bold
+    </namerecord>
+    <namerecord nameID="268" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Black
+    </namerecord>
+    <namerecord nameID="269" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestFamily-Black
+    </namerecord>
+    <namerecord nameID="270" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Black Medium Contrast
+    </namerecord>
+    <namerecord nameID="271" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestFamily-BlackMediumContrast
+    </namerecord>
+    <namerecord nameID="272" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Black High Contrast
+    </namerecord>
+    <namerecord nameID="273" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestFamily-BlackHighContrast
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test Family
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      Version 1.001;ADBO;Test Family Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test Family
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.001
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestFamily-Master1
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Frank Grießhammer
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Master 1
+    </namerecord>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </namerecord>
+    <namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
+      Contrast
+    </namerecord>
+    <namerecord nameID="258" platformID="3" platEncID="1" langID="0x409">
+      ExtraLight
+    </namerecord>
+    <namerecord nameID="259" platformID="3" platEncID="1" langID="0x409">
+      TestFamily-ExtraLight
+    </namerecord>
+    <namerecord nameID="260" platformID="3" platEncID="1" langID="0x409">
+      Light
+    </namerecord>
+    <namerecord nameID="261" platformID="3" platEncID="1" langID="0x409">
+      TestFamily-Light
+    </namerecord>
+    <namerecord nameID="262" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="263" platformID="3" platEncID="1" langID="0x409">
+      TestFamily-Regular
+    </namerecord>
+    <namerecord nameID="264" platformID="3" platEncID="1" langID="0x409">
+      Semibold
+    </namerecord>
+    <namerecord nameID="265" platformID="3" platEncID="1" langID="0x409">
+      TestFamily-Semibold
+    </namerecord>
+    <namerecord nameID="266" platformID="3" platEncID="1" langID="0x409">
+      Bold
+    </namerecord>
+    <namerecord nameID="267" platformID="3" platEncID="1" langID="0x409">
+      TestFamily-Bold
+    </namerecord>
+    <namerecord nameID="268" platformID="3" platEncID="1" langID="0x409">
+      Black
+    </namerecord>
+    <namerecord nameID="269" platformID="3" platEncID="1" langID="0x409">
+      TestFamily-Black
+    </namerecord>
+    <namerecord nameID="270" platformID="3" platEncID="1" langID="0x409">
+      Black Medium Contrast
+    </namerecord>
+    <namerecord nameID="271" platformID="3" platEncID="1" langID="0x409">
+      TestFamily-BlackMediumContrast
+    </namerecord>
+    <namerecord nameID="272" platformID="3" platEncID="1" langID="0x409">
+      Black High Contrast
+    </namerecord>
+    <namerecord nameID="273" platformID="3" platEncID="1" langID="0x409">
+      TestFamily-BlackHighContrast
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name="dollar.nostroke"/>
+      <psName name="uni0020"/>
+      <psName name="uni0041"/>
+      <psName name="uni0061"/>
+      <psName name="uni0024"/>
+      <psName name="uni0024.nostroke"/>
+    </extraNames>
+  </post>
+
+  <HVAR>
+    <Version value="0x00010000"/>
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=2 -->
+        <!-- RegionCount=5 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="1">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="2">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="3">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="4">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=1 -->
+      <VarData index="0">
+        <!-- ItemCount=6 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=2 -->
+        <VarRegionIndex index="0" value="0"/>
+        <VarRegionIndex index="1" value="1"/>
+        <Item index="0" value="[0, 0]"/>
+        <Item index="1" value="[14, -28]"/>
+        <Item index="2" value="[-10, 17]"/>
+        <Item index="3" value="[-3, 32]"/>
+        <Item index="4" value="[-7, 63]"/>
+        <Item index="5" value="[-7, 63]"/>
+      </VarData>
+    </VarStore>
+  </HVAR>
+
+  <MVAR>
+    <Version value="0x00010000"/>
+    <Reserved value="0"/>
+    <ValueRecordSize value="8"/>
+    <!-- ValueRecordCount=2 -->
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=2 -->
+        <!-- RegionCount=5 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="1">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="2">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="3">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="4">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=1 -->
+      <VarData index="0">
+        <!-- ItemCount=2 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=2 -->
+        <VarRegionIndex index="0" value="0"/>
+        <VarRegionIndex index="1" value="1"/>
+        <Item index="0" value="[-4, 13]"/>
+        <Item index="1" value="[-2, 8]"/>
+      </VarData>
+    </VarStore>
+    <ValueRecord index="0">
+      <ValueTag value="stro"/>
+      <VarIdx value="1"/>
+    </ValueRecord>
+    <ValueRecord index="1">
+      <ValueTag value="xhgt"/>
+      <VarIdx value="0"/>
+    </ValueRecord>
+  </MVAR>
+
+  <STAT>
+    <Version value="0x00010002"/>
+    <DesignAxisRecordSize value="8"/>
+    <!-- DesignAxisCount=2 -->
+    <DesignAxisRecord>
+      <Axis index="0">
+        <AxisTag value="wght"/>
+        <AxisNameID value="256"/>  <!-- Weight -->
+        <AxisOrdering value="0"/>
+      </Axis>
+      <Axis index="1">
+        <AxisTag value="cntr"/>
+        <AxisNameID value="257"/>  <!-- Contrast -->
+        <AxisOrdering value="1"/>
+      </Axis>
+    </DesignAxisRecord>
+    <!-- AxisValueCount=0 -->
+    <ElidedFallbackNameID value="2"/>  <!-- Regular -->
+  </STAT>
+
+  <cvar>
+    <version major="1" minor="0"/>
+    <tuple>
+      <coord axis="wght" value="-1.0"/>
+      <delta cvt="0" value="-4"/>
+      <delta cvt="1" value="0"/>
+    </tuple>
+    <tuple>
+      <coord axis="wght" value="1.0"/>
+      <delta cvt="0" value="13"/>
+      <delta cvt="1" value="0"/>
+    </tuple>
+  </cvar>
+
+  <fvar>
+
+    <!-- Weight -->
+    <Axis>
+      <AxisTag>wght</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>368.0</DefaultValue>
+      <MaxValue>1000.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+
+    <!-- Contrast -->
+    <Axis>
+      <AxisTag>cntr</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>100.0</MaxValue>
+      <AxisNameID>257</AxisNameID>
+    </Axis>
+
+    <!-- ExtraLight -->
+    <!-- PostScript: TestFamily-ExtraLight -->
+    <NamedInstance flags="0x0" postscriptNameID="259" subfamilyNameID="258">
+      <coord axis="wght" value="0.0"/>
+      <coord axis="cntr" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Light -->
+    <!-- PostScript: TestFamily-Light -->
+    <NamedInstance flags="0x0" postscriptNameID="261" subfamilyNameID="260">
+      <coord axis="wght" value="150.0"/>
+      <coord axis="cntr" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Regular -->
+    <!-- PostScript: TestFamily-Regular -->
+    <NamedInstance flags="0x0" postscriptNameID="263" subfamilyNameID="262">
+      <coord axis="wght" value="394.0"/>
+      <coord axis="cntr" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Semibold -->
+    <!-- PostScript: TestFamily-Semibold -->
+    <NamedInstance flags="0x0" postscriptNameID="265" subfamilyNameID="264">
+      <coord axis="wght" value="600.0"/>
+      <coord axis="cntr" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Bold -->
+    <!-- PostScript: TestFamily-Bold -->
+    <NamedInstance flags="0x0" postscriptNameID="267" subfamilyNameID="266">
+      <coord axis="wght" value="824.0"/>
+      <coord axis="cntr" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Black -->
+    <!-- PostScript: TestFamily-Black -->
+    <NamedInstance flags="0x0" postscriptNameID="269" subfamilyNameID="268">
+      <coord axis="wght" value="1000.0"/>
+      <coord axis="cntr" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Black Medium Contrast -->
+    <!-- PostScript: TestFamily-BlackMediumContrast -->
+    <NamedInstance flags="0x0" postscriptNameID="271" subfamilyNameID="270">
+      <coord axis="wght" value="1000.0"/>
+      <coord axis="cntr" value="50.0"/>
+    </NamedInstance>
+
+    <!-- Black High Contrast -->
+    <!-- PostScript: TestFamily-BlackHighContrast -->
+    <NamedInstance flags="0x0" postscriptNameID="273" subfamilyNameID="272">
+      <coord axis="wght" value="1000.0"/>
+      <coord axis="cntr" value="100.0"/>
+    </NamedInstance>
+  </fvar>
+
+  <gvar>
+    <version value="1"/>
+    <reserved value="0"/>
+    <glyphVariations glyph=".notdef">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="35" y="7"/>
+        <delta pt="2" x="0" y="7"/>
+        <delta pt="3" x="-35" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="35" y="0"/>
+        <delta pt="6" x="0" y="7"/>
+        <delta pt="7" x="-35" y="7"/>
+        <delta pt="8" x="-35" y="-28"/>
+        <delta pt="9" x="35" y="-28"/>
+        <delta pt="10" x="35" y="35"/>
+        <delta pt="11" x="-35" y="35"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="7"/>
+        <delta pt="14" x="0" y="7"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+        <delta pt="17" x="0" y="0"/>
+        <delta pt="18" x="0" y="7"/>
+        <delta pt="19" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="-20" y="-18"/>
+        <delta pt="2" x="0" y="-18"/>
+        <delta pt="3" x="20" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="-20" y="0"/>
+        <delta pt="6" x="0" y="-18"/>
+        <delta pt="7" x="20" y="-18"/>
+        <delta pt="8" x="10" y="10"/>
+        <delta pt="9" x="-10" y="10"/>
+        <delta pt="10" x="-10" y="-28"/>
+        <delta pt="11" x="10" y="-28"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="-18"/>
+        <delta pt="14" x="0" y="-18"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+        <delta pt="17" x="0" y="0"/>
+        <delta pt="18" x="0" y="-18"/>
+        <delta pt="19" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="4" y="0"/>
+        <delta pt="1" x="11" y="0"/>
+        <delta pt="2" x="-4" y="0"/>
+        <delta pt="3" x="-11" y="0"/>
+        <delta pt="4" x="-4" y="0"/>
+        <delta pt="5" x="11" y="0"/>
+        <delta pt="6" x="4" y="0"/>
+        <delta pt="7" x="-11" y="0"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="0" y="0"/>
+        <delta pt="10" x="0" y="0"/>
+        <delta pt="11" x="0" y="0"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="0"/>
+        <delta pt="14" x="0" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+        <delta pt="17" x="0" y="0"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="-4" y="0"/>
+        <delta pt="1" x="-11" y="0"/>
+        <delta pt="2" x="4" y="0"/>
+        <delta pt="3" x="11" y="0"/>
+        <delta pt="4" x="4" y="0"/>
+        <delta pt="5" x="-11" y="0"/>
+        <delta pt="6" x="-4" y="0"/>
+        <delta pt="7" x="11" y="0"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="0" y="0"/>
+        <delta pt="10" x="0" y="0"/>
+        <delta pt="11" x="0" y="0"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="0"/>
+        <delta pt="14" x="0" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+        <delta pt="17" x="0" y="0"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="6" y="0"/>
+        <delta pt="1" x="19" y="0"/>
+        <delta pt="2" x="-6" y="0"/>
+        <delta pt="3" x="-19" y="0"/>
+        <delta pt="4" x="-6" y="0"/>
+        <delta pt="5" x="19" y="0"/>
+        <delta pt="6" x="6" y="0"/>
+        <delta pt="7" x="-19" y="0"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="0" y="0"/>
+        <delta pt="10" x="0" y="0"/>
+        <delta pt="11" x="0" y="0"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="0"/>
+        <delta pt="14" x="0" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+        <delta pt="17" x="0" y="0"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="uni0020">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="14" y="0"/>
+        <delta pt="2" x="0" y="0"/>
+        <delta pt="3" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="-28" y="0"/>
+        <delta pt="2" x="0" y="0"/>
+        <delta pt="3" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="uni0041">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="0" x="7" y="0"/>
+        <delta pt="1" x="7" y="-20"/>
+        <delta pt="2" x="-6" y="-29"/>
+        <delta pt="3" x="-12" y="-29"/>
+        <delta pt="4" x="-25" y="-20"/>
+        <delta pt="5" x="-25" y="0"/>
+        <delta pt="6" x="14" y="0"/>
+        <delta pt="7" x="4" y="9"/>
+        <delta pt="8" x="-36" y="9"/>
+        <delta pt="9" x="-37" y="0"/>
+        <delta pt="10" x="24" y="0"/>
+        <delta pt="11" x="9" y="58"/>
+        <delta pt="12" x="3" y="68"/>
+        <delta pt="13" x="-4" y="0"/>
+        <delta pt="14" x="3" y="28"/>
+        <delta pt="15" x="-4" y="2"/>
+        <delta pt="16" x="4" y="2"/>
+        <delta pt="17" x="-4" y="28"/>
+        <delta pt="18" x="20" y="0"/>
+        <delta pt="19" x="20" y="-20"/>
+        <delta pt="20" x="14" y="-29"/>
+        <delta pt="21" x="8" y="-29"/>
+        <delta pt="22" x="-2" y="-20"/>
+        <delta pt="23" x="-2" y="0"/>
+        <delta pt="24" x="0" y="0"/>
+        <delta pt="25" x="-10" y="0"/>
+        <delta pt="26" x="0" y="9"/>
+        <delta pt="27" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="5" y="0"/>
+        <delta pt="1" x="5" y="19"/>
+        <delta pt="2" x="9" y="19"/>
+        <delta pt="3" x="6" y="19"/>
+        <delta pt="4" x="-15" y="19"/>
+        <delta pt="5" x="-15" y="0"/>
+        <delta pt="6" x="-6" y="0"/>
+        <delta pt="7" x="-14" y="-23"/>
+        <delta pt="8" x="46" y="-23"/>
+        <delta pt="9" x="39" y="0"/>
+        <delta pt="10" x="-69" y="0"/>
+        <delta pt="11" x="-27" y="-86"/>
+        <delta pt="12" x="-7" y="-16"/>
+        <delta pt="13" x="11" y="0"/>
+        <delta pt="14" x="-2" y="-39"/>
+        <delta pt="15" x="-1" y="-22"/>
+        <delta pt="16" x="-1" y="-22"/>
+        <delta pt="17" x="8" y="-39"/>
+        <delta pt="18" x="-41" y="0"/>
+        <delta pt="19" x="-41" y="16"/>
+        <delta pt="20" x="-59" y="16"/>
+        <delta pt="21" x="6" y="16"/>
+        <delta pt="22" x="12" y="16"/>
+        <delta pt="23" x="12" y="0"/>
+        <delta pt="24" x="0" y="0"/>
+        <delta pt="25" x="17" y="0"/>
+        <delta pt="26" x="0" y="-23"/>
+        <delta pt="27" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="2" y="0"/>
+        <delta pt="1" x="2" y="-9"/>
+        <delta pt="2" x="-4" y="-9"/>
+        <delta pt="3" x="-4" y="-9"/>
+        <delta pt="4" x="-2" y="-9"/>
+        <delta pt="5" x="-2" y="0"/>
+        <delta pt="6" x="2" y="0"/>
+        <delta pt="7" x="-4" y="0"/>
+        <delta pt="8" x="-4" y="0"/>
+        <delta pt="9" x="-4" y="0"/>
+        <delta pt="10" x="-4" y="0"/>
+        <delta pt="11" x="-6" y="8"/>
+        <delta pt="12" x="-10" y="0"/>
+        <delta pt="13" x="-2" y="0"/>
+        <delta pt="14" x="0" y="5"/>
+        <delta pt="15" x="-3" y="-5"/>
+        <delta pt="16" x="5" y="-5"/>
+        <delta pt="17" x="-1" y="5"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="0" y="-8"/>
+        <delta pt="20" x="4" y="-8"/>
+        <delta pt="21" x="0" y="-8"/>
+        <delta pt="22" x="0" y="-8"/>
+        <delta pt="23" x="0" y="0"/>
+        <delta pt="24" x="0" y="0"/>
+        <delta pt="25" x="0" y="0"/>
+        <delta pt="26" x="0" y="0"/>
+        <delta pt="27" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="-2" y="0"/>
+        <delta pt="1" x="-2" y="9"/>
+        <delta pt="2" x="4" y="9"/>
+        <delta pt="3" x="4" y="9"/>
+        <delta pt="4" x="2" y="9"/>
+        <delta pt="5" x="2" y="0"/>
+        <delta pt="6" x="-2" y="0"/>
+        <delta pt="7" x="4" y="0"/>
+        <delta pt="8" x="4" y="0"/>
+        <delta pt="9" x="4" y="0"/>
+        <delta pt="10" x="4" y="0"/>
+        <delta pt="11" x="6" y="-8"/>
+        <delta pt="12" x="10" y="0"/>
+        <delta pt="13" x="2" y="0"/>
+        <delta pt="14" x="0" y="-5"/>
+        <delta pt="15" x="3" y="5"/>
+        <delta pt="16" x="-5" y="5"/>
+        <delta pt="17" x="1" y="-5"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="0" y="8"/>
+        <delta pt="20" x="-4" y="8"/>
+        <delta pt="21" x="0" y="8"/>
+        <delta pt="22" x="0" y="8"/>
+        <delta pt="23" x="0" y="0"/>
+        <delta pt="24" x="0" y="0"/>
+        <delta pt="25" x="0" y="0"/>
+        <delta pt="26" x="0" y="0"/>
+        <delta pt="27" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="3" y="0"/>
+        <delta pt="1" x="3" y="-15"/>
+        <delta pt="2" x="-6" y="-15"/>
+        <delta pt="3" x="-6" y="-15"/>
+        <delta pt="4" x="-3" y="-15"/>
+        <delta pt="5" x="-3" y="0"/>
+        <delta pt="6" x="3" y="0"/>
+        <delta pt="7" x="-6" y="0"/>
+        <delta pt="8" x="-6" y="0"/>
+        <delta pt="9" x="-6" y="0"/>
+        <delta pt="10" x="-6" y="0"/>
+        <delta pt="11" x="-11" y="13"/>
+        <delta pt="12" x="-17" y="0"/>
+        <delta pt="13" x="-3" y="0"/>
+        <delta pt="14" x="-1" y="8"/>
+        <delta pt="15" x="-5" y="-9"/>
+        <delta pt="16" x="8" y="-9"/>
+        <delta pt="17" x="-1" y="8"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="0" y="-13"/>
+        <delta pt="20" x="6" y="-13"/>
+        <delta pt="21" x="0" y="-13"/>
+        <delta pt="22" x="0" y="-13"/>
+        <delta pt="23" x="0" y="0"/>
+        <delta pt="24" x="0" y="0"/>
+        <delta pt="25" x="0" y="0"/>
+        <delta pt="26" x="0" y="0"/>
+        <delta pt="27" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="uni0061">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="0" x="11" y="-8"/>
+        <delta pt="1" x="11" y="4"/>
+        <delta pt="2" x="22" y="5"/>
+        <delta pt="3" x="-4" y="-8"/>
+        <delta pt="4" x="6" y="-5"/>
+        <delta pt="5" x="3" y="-11"/>
+        <delta pt="6" x="4" y="-9"/>
+        <delta pt="7" x="4" y="9"/>
+        <delta pt="8" x="0" y="7"/>
+        <delta pt="9" x="-9" y="8"/>
+        <delta pt="10" x="-24" y="3"/>
+        <delta pt="11" x="-18" y="6"/>
+        <delta pt="12" x="-44" y="1"/>
+        <delta pt="13" x="-44" y="-16"/>
+        <delta pt="14" x="-44" y="-22"/>
+        <delta pt="15" x="-36" y="-39"/>
+        <delta pt="16" x="-24" y="-39"/>
+        <delta pt="17" x="-7" y="-39"/>
+        <delta pt="18" x="26" y="-15"/>
+        <delta pt="19" x="26" y="3"/>
+        <delta pt="20" x="17" y="0"/>
+        <delta pt="21" x="3" y="-4"/>
+        <delta pt="22" x="23" y="15"/>
+        <delta pt="23" x="22" y="8"/>
+        <delta pt="24" x="6" y="0"/>
+        <delta pt="25" x="0" y="0"/>
+        <delta pt="26" x="2" y="0"/>
+        <delta pt="27" x="11" y="-2"/>
+        <delta pt="28" x="30" y="7"/>
+        <delta pt="29" x="30" y="4"/>
+        <delta pt="30" x="30" y="13"/>
+        <delta pt="31" x="14" y="21"/>
+        <delta pt="32" x="3" y="21"/>
+        <delta pt="33" x="-15" y="21"/>
+        <delta pt="34" x="-32" y="5"/>
+        <delta pt="35" x="-34" y="-9"/>
+        <delta pt="36" x="-48" y="-14"/>
+        <delta pt="37" x="-40" y="4"/>
+        <delta pt="38" x="-36" y="14"/>
+        <delta pt="39" x="-24" y="27"/>
+        <delta pt="40" x="-13" y="27"/>
+        <delta pt="41" x="12" y="27"/>
+        <delta pt="42" x="10" y="6"/>
+        <delta pt="43" x="12" y="5"/>
+        <delta pt="44" x="-4" y="-4"/>
+        <delta pt="45" x="-16" y="-4"/>
+        <delta pt="46" x="-20" y="-4"/>
+        <delta pt="47" x="-22" y="7"/>
+        <delta pt="48" x="-22" y="25"/>
+        <delta pt="49" x="-22" y="10"/>
+        <delta pt="50" x="-22" y="-15"/>
+        <delta pt="51" x="-16" y="-30"/>
+        <delta pt="52" x="-9" y="-30"/>
+        <delta pt="53" x="-12" y="-30"/>
+        <delta pt="54" x="-11" y="-35"/>
+        <delta pt="55" x="-5" y="-35"/>
+        <delta pt="56" x="-15" y="-27"/>
+        <delta pt="57" x="-10" y="-3"/>
+        <delta pt="58" x="9" y="-3"/>
+        <delta pt="59" x="14" y="-3"/>
+        <delta pt="60" x="33" y="-1"/>
+        <delta pt="61" x="0" y="0"/>
+        <delta pt="62" x="-3" y="0"/>
+        <delta pt="63" x="0" y="-4"/>
+        <delta pt="64" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="-21" y="1"/>
+        <delta pt="1" x="-21" y="17"/>
+        <delta pt="2" x="-2" y="28"/>
+        <delta pt="3" x="20" y="23"/>
+        <delta pt="4" x="19" y="20"/>
+        <delta pt="5" x="28" y="21"/>
+        <delta pt="6" x="26" y="23"/>
+        <delta pt="7" x="26" y="15"/>
+        <delta pt="8" x="24" y="12"/>
+        <delta pt="9" x="30" y="17"/>
+        <delta pt="10" x="31" y="15"/>
+        <delta pt="11" x="77" y="31"/>
+        <delta pt="12" x="66" y="36"/>
+        <delta pt="13" x="66" y="18"/>
+        <delta pt="14" x="66" y="21"/>
+        <delta pt="15" x="49" y="19"/>
+        <delta pt="16" x="37" y="19"/>
+        <delta pt="17" x="21" y="19"/>
+        <delta pt="18" x="-2" y="5"/>
+        <delta pt="19" x="-34" y="-18"/>
+        <delta pt="20" x="-6" y="3"/>
+        <delta pt="21" x="-11" y="12"/>
+        <delta pt="22" x="-29" y="-11"/>
+        <delta pt="23" x="-17" y="-2"/>
+        <delta pt="24" x="-13" y="-3"/>
+        <delta pt="25" x="-25" y="-3"/>
+        <delta pt="26" x="-29" y="-3"/>
+        <delta pt="27" x="-21" y="2"/>
+        <delta pt="28" x="-34" y="-14"/>
+        <delta pt="29" x="-34" y="17"/>
+        <delta pt="30" x="-34" y="7"/>
+        <delta pt="31" x="-18" y="7"/>
+        <delta pt="32" x="-16" y="7"/>
+        <delta pt="33" x="-18" y="7"/>
+        <delta pt="34" x="-15" y="9"/>
+        <delta pt="35" x="-21" y="12"/>
+        <delta pt="36" x="19" y="23"/>
+        <delta pt="37" x="45" y="46"/>
+        <delta pt="38" x="52" y="7"/>
+        <delta pt="39" x="26" y="-21"/>
+        <delta pt="40" x="14" y="-21"/>
+        <delta pt="41" x="-5" y="-21"/>
+        <delta pt="42" x="-17" y="-7"/>
+        <delta pt="43" x="-31" y="1"/>
+        <delta pt="44" x="-12" y="16"/>
+        <delta pt="45" x="34" y="16"/>
+        <delta pt="46" x="61" y="16"/>
+        <delta pt="47" x="70" y="4"/>
+        <delta pt="48" x="70" y="-5"/>
+        <delta pt="49" x="70" y="-22"/>
+        <delta pt="50" x="70" y="4"/>
+        <delta pt="51" x="59" y="22"/>
+        <delta pt="52" x="50" y="22"/>
+        <delta pt="53" x="43" y="22"/>
+        <delta pt="54" x="37" y="19"/>
+        <delta pt="55" x="38" y="22"/>
+        <delta pt="56" x="47" y="28"/>
+        <delta pt="57" x="46" y="-6"/>
+        <delta pt="58" x="-2" y="-6"/>
+        <delta pt="59" x="-16" y="-6"/>
+        <delta pt="60" x="-25" y="-13"/>
+        <delta pt="61" x="0" y="0"/>
+        <delta pt="62" x="32" y="0"/>
+        <delta pt="63" x="0" y="16"/>
+        <delta pt="64" x="0" y="3"/>
+      </tuple>
+      <tuple>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="0" y="-3"/>
+        <delta pt="1" x="0" y="-1"/>
+        <delta pt="2" x="0" y="-3"/>
+        <delta pt="3" x="0" y="-3"/>
+        <delta pt="4" x="0" y="-3"/>
+        <delta pt="5" x="0" y="-3"/>
+        <delta pt="6" x="0" y="-3"/>
+        <delta pt="7" x="0" y="4"/>
+        <delta pt="8" x="0" y="4"/>
+        <delta pt="9" x="2" y="5"/>
+        <delta pt="10" x="6" y="7"/>
+        <delta pt="11" x="1" y="5"/>
+        <delta pt="12" x="0" y="-1"/>
+        <delta pt="13" x="0" y="-6"/>
+        <delta pt="14" x="0" y="-6"/>
+        <delta pt="15" x="-1" y="-6"/>
+        <delta pt="16" x="0" y="-6"/>
+        <delta pt="17" x="0" y="-6"/>
+        <delta pt="18" x="0" y="-5"/>
+        <delta pt="19" x="0" y="-4"/>
+        <delta pt="20" x="0" y="-1"/>
+        <delta pt="21" x="0" y="0"/>
+        <delta pt="22" x="0" y="0"/>
+        <delta pt="23" x="0" y="0"/>
+        <delta pt="24" x="0" y="0"/>
+        <delta pt="25" x="0" y="0"/>
+        <delta pt="26" x="0" y="0"/>
+        <delta pt="27" x="0" y="-1"/>
+        <delta pt="28" x="0" y="-2"/>
+        <delta pt="29" x="0" y="7"/>
+        <delta pt="30" x="0" y="6"/>
+        <delta pt="31" x="0" y="7"/>
+        <delta pt="32" x="0" y="7"/>
+        <delta pt="33" x="0" y="7"/>
+        <delta pt="34" x="0" y="7"/>
+        <delta pt="35" x="0" y="7"/>
+        <delta pt="36" x="0" y="0"/>
+        <delta pt="37" x="0" y="0"/>
+        <delta pt="38" x="0" y="0"/>
+        <delta pt="39" x="0" y="0"/>
+        <delta pt="40" x="0" y="0"/>
+        <delta pt="41" x="0" y="0"/>
+        <delta pt="42" x="0" y="0"/>
+        <delta pt="43" x="0" y="0"/>
+        <delta pt="44" x="0" y="0"/>
+        <delta pt="45" x="0" y="0"/>
+        <delta pt="46" x="0" y="0"/>
+        <delta pt="47" x="0" y="0"/>
+        <delta pt="48" x="0" y="0"/>
+        <delta pt="49" x="0" y="-6"/>
+        <delta pt="50" x="0" y="-7"/>
+        <delta pt="51" x="0" y="-8"/>
+        <delta pt="52" x="0" y="-8"/>
+        <delta pt="53" x="1" y="-8"/>
+        <delta pt="54" x="2" y="-5"/>
+        <delta pt="55" x="4" y="-2"/>
+        <delta pt="56" x="0" y="0"/>
+        <delta pt="57" x="0" y="0"/>
+        <delta pt="58" x="0" y="0"/>
+        <delta pt="59" x="0" y="0"/>
+        <delta pt="60" x="0" y="-1"/>
+        <delta pt="61" x="0" y="0"/>
+        <delta pt="62" x="0" y="0"/>
+        <delta pt="63" x="0" y="0"/>
+        <delta pt="64" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="0" y="3"/>
+        <delta pt="1" x="0" y="1"/>
+        <delta pt="2" x="0" y="3"/>
+        <delta pt="3" x="0" y="3"/>
+        <delta pt="4" x="0" y="3"/>
+        <delta pt="5" x="0" y="3"/>
+        <delta pt="6" x="0" y="3"/>
+        <delta pt="7" x="0" y="-4"/>
+        <delta pt="8" x="0" y="-4"/>
+        <delta pt="9" x="-2" y="-5"/>
+        <delta pt="10" x="-6" y="-7"/>
+        <delta pt="11" x="-1" y="-5"/>
+        <delta pt="12" x="0" y="1"/>
+        <delta pt="13" x="0" y="6"/>
+        <delta pt="14" x="0" y="6"/>
+        <delta pt="15" x="1" y="6"/>
+        <delta pt="16" x="0" y="6"/>
+        <delta pt="17" x="0" y="6"/>
+        <delta pt="18" x="0" y="5"/>
+        <delta pt="19" x="0" y="4"/>
+        <delta pt="20" x="0" y="1"/>
+        <delta pt="21" x="0" y="0"/>
+        <delta pt="22" x="0" y="0"/>
+        <delta pt="23" x="0" y="0"/>
+        <delta pt="24" x="0" y="0"/>
+        <delta pt="25" x="0" y="0"/>
+        <delta pt="26" x="0" y="0"/>
+        <delta pt="27" x="0" y="1"/>
+        <delta pt="28" x="0" y="2"/>
+        <delta pt="29" x="0" y="-7"/>
+        <delta pt="30" x="0" y="-6"/>
+        <delta pt="31" x="0" y="-7"/>
+        <delta pt="32" x="0" y="-7"/>
+        <delta pt="33" x="0" y="-7"/>
+        <delta pt="34" x="0" y="-7"/>
+        <delta pt="35" x="0" y="-7"/>
+        <delta pt="36" x="0" y="0"/>
+        <delta pt="37" x="0" y="0"/>
+        <delta pt="38" x="0" y="0"/>
+        <delta pt="39" x="0" y="0"/>
+        <delta pt="40" x="0" y="0"/>
+        <delta pt="41" x="0" y="0"/>
+        <delta pt="42" x="0" y="0"/>
+        <delta pt="43" x="0" y="0"/>
+        <delta pt="44" x="0" y="0"/>
+        <delta pt="45" x="0" y="0"/>
+        <delta pt="46" x="0" y="0"/>
+        <delta pt="47" x="0" y="0"/>
+        <delta pt="48" x="0" y="0"/>
+        <delta pt="49" x="0" y="6"/>
+        <delta pt="50" x="0" y="7"/>
+        <delta pt="51" x="0" y="8"/>
+        <delta pt="52" x="0" y="8"/>
+        <delta pt="53" x="-1" y="8"/>
+        <delta pt="54" x="-2" y="5"/>
+        <delta pt="55" x="-4" y="2"/>
+        <delta pt="56" x="0" y="0"/>
+        <delta pt="57" x="0" y="0"/>
+        <delta pt="58" x="0" y="0"/>
+        <delta pt="59" x="0" y="0"/>
+        <delta pt="60" x="0" y="1"/>
+        <delta pt="61" x="0" y="0"/>
+        <delta pt="62" x="0" y="0"/>
+        <delta pt="63" x="0" y="0"/>
+        <delta pt="64" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="0" y="-5"/>
+        <delta pt="1" x="0" y="0"/>
+        <delta pt="2" x="3" y="-4"/>
+        <delta pt="3" x="0" y="-4"/>
+        <delta pt="4" x="0" y="-4"/>
+        <delta pt="5" x="0" y="-4"/>
+        <delta pt="6" x="0" y="-4"/>
+        <delta pt="7" x="0" y="8"/>
+        <delta pt="8" x="0" y="8"/>
+        <delta pt="9" x="5" y="9"/>
+        <delta pt="10" x="11" y="13"/>
+        <delta pt="11" x="2" y="10"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="-9"/>
+        <delta pt="14" x="0" y="-9"/>
+        <delta pt="15" x="-1" y="-9"/>
+        <delta pt="16" x="0" y="-9"/>
+        <delta pt="17" x="0" y="-9"/>
+        <delta pt="18" x="0" y="-10"/>
+        <delta pt="19" x="0" y="-8"/>
+        <delta pt="20" x="0" y="-2"/>
+        <delta pt="21" x="0" y="1"/>
+        <delta pt="22" x="0" y="0"/>
+        <delta pt="23" x="1" y="-1"/>
+        <delta pt="24" x="0" y="0"/>
+        <delta pt="25" x="0" y="0"/>
+        <delta pt="26" x="0" y="0"/>
+        <delta pt="27" x="0" y="-1"/>
+        <delta pt="28" x="0" y="-4"/>
+        <delta pt="29" x="0" y="12"/>
+        <delta pt="30" x="0" y="13"/>
+        <delta pt="31" x="0" y="13"/>
+        <delta pt="32" x="0" y="13"/>
+        <delta pt="33" x="0" y="13"/>
+        <delta pt="34" x="0" y="13"/>
+        <delta pt="35" x="0" y="13"/>
+        <delta pt="36" x="0" y="0"/>
+        <delta pt="37" x="0" y="0"/>
+        <delta pt="38" x="0" y="0"/>
+        <delta pt="39" x="0" y="1"/>
+        <delta pt="40" x="0" y="1"/>
+        <delta pt="41" x="0" y="1"/>
+        <delta pt="42" x="0" y="1"/>
+        <delta pt="43" x="0" y="0"/>
+        <delta pt="44" x="0" y="0"/>
+        <delta pt="45" x="0" y="0"/>
+        <delta pt="46" x="0" y="0"/>
+        <delta pt="47" x="0" y="-1"/>
+        <delta pt="48" x="0" y="-1"/>
+        <delta pt="49" x="0" y="-9"/>
+        <delta pt="50" x="0" y="-13"/>
+        <delta pt="51" x="1" y="-14"/>
+        <delta pt="52" x="1" y="-14"/>
+        <delta pt="53" x="2" y="-14"/>
+        <delta pt="54" x="5" y="-11"/>
+        <delta pt="55" x="7" y="-4"/>
+        <delta pt="56" x="0" y="0"/>
+        <delta pt="57" x="0" y="0"/>
+        <delta pt="58" x="0" y="0"/>
+        <delta pt="59" x="0" y="0"/>
+        <delta pt="60" x="1" y="0"/>
+        <delta pt="61" x="0" y="0"/>
+        <delta pt="62" x="0" y="0"/>
+        <delta pt="63" x="0" y="0"/>
+        <delta pt="64" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="uni0024">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="0" x="-3" y="-28"/>
+        <delta pt="1" x="2" y="-28"/>
+        <delta pt="2" x="17" y="-12"/>
+        <delta pt="3" x="17" y="2"/>
+        <delta pt="4" x="17" y="4"/>
+        <delta pt="5" x="20" y="25"/>
+        <delta pt="6" x="-2" y="42"/>
+        <delta pt="7" x="-2" y="46"/>
+        <delta pt="8" x="8" y="49"/>
+        <delta pt="9" x="28" y="45"/>
+        <delta pt="10" x="28" y="30"/>
+        <delta pt="11" x="28" y="35"/>
+        <delta pt="12" x="21" y="7"/>
+        <delta pt="13" x="4" y="7"/>
+        <delta pt="14" x="-9" y="7"/>
+        <delta pt="15" x="-21" y="19"/>
+        <delta pt="16" x="-8" y="17"/>
+        <delta pt="17" x="-3" y="22"/>
+        <delta pt="18" x="9" y="27"/>
+        <delta pt="19" x="13" y="27"/>
+        <delta pt="20" x="22" y="27"/>
+        <delta pt="21" x="41" y="9"/>
+        <delta pt="22" x="45" y="-19"/>
+        <delta pt="23" x="34" y="5"/>
+        <delta pt="24" x="20" y="-6"/>
+        <delta pt="25" x="18" y="18"/>
+        <delta pt="26" x="12" y="36"/>
+        <delta pt="27" x="-7" y="36"/>
+        <delta pt="28" x="-18" y="36"/>
+        <delta pt="29" x="-24" y="21"/>
+        <delta pt="30" x="-24" y="9"/>
+        <delta pt="31" x="-24" y="6"/>
+        <delta pt="32" x="-17" y="-13"/>
+        <delta pt="33" x="0" y="-25"/>
+        <delta pt="34" x="-19" y="-20"/>
+        <delta pt="35" x="-20" y="-28"/>
+        <delta pt="36" x="-35" y="-28"/>
+        <delta pt="37" x="-35" y="-19"/>
+        <delta pt="38" x="-35" y="-22"/>
+        <delta pt="39" x="-32" y="0"/>
+        <delta pt="40" x="2" y="0"/>
+        <delta pt="41" x="2" y="0"/>
+        <delta pt="42" x="17" y="-9"/>
+        <delta pt="43" x="2" y="-10"/>
+        <delta pt="44" x="-4" y="-14"/>
+        <delta pt="45" x="-14" y="-20"/>
+        <delta pt="46" x="-20" y="-20"/>
+        <delta pt="47" x="-30" y="-20"/>
+        <delta pt="48" x="-45" y="-2"/>
+        <delta pt="49" x="-51" y="26"/>
+        <delta pt="50" x="-40" y="2"/>
+        <delta pt="51" x="-23" y="14"/>
+        <delta pt="52" x="-22" y="-10"/>
+        <delta pt="53" x="-23" y="-28"/>
+        <delta pt="54" x="-16" y="12"/>
+        <delta pt="55" x="2" y="12"/>
+        <delta pt="56" x="2" y="12"/>
+        <delta pt="57" x="-16" y="12"/>
+        <delta pt="58" x="3" y="0"/>
+        <delta pt="59" x="3" y="12"/>
+        <delta pt="60" x="-15" y="12"/>
+        <delta pt="61" x="-15" y="0"/>
+        <delta pt="62" x="0" y="0"/>
+        <delta pt="63" x="-7" y="0"/>
+        <delta pt="64" x="0" y="12"/>
+        <delta pt="65" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="12" y="4"/>
+        <delta pt="1" x="0" y="4"/>
+        <delta pt="2" x="-18" y="-10"/>
+        <delta pt="3" x="-18" y="-28"/>
+        <delta pt="4" x="-18" y="-28"/>
+        <delta pt="5" x="-7" y="-40"/>
+        <delta pt="6" x="3" y="-39"/>
+        <delta pt="7" x="9" y="-39"/>
+        <delta pt="8" x="0" y="-30"/>
+        <delta pt="9" x="-5" y="-25"/>
+        <delta pt="10" x="-5" y="-21"/>
+        <delta pt="11" x="-5" y="-20"/>
+        <delta pt="12" x="8" y="-20"/>
+        <delta pt="13" x="40" y="-20"/>
+        <delta pt="14" x="55" y="-20"/>
+        <delta pt="15" x="63" y="-11"/>
+        <delta pt="16" x="58" y="-10"/>
+        <delta pt="17" x="60" y="-25"/>
+        <delta pt="18" x="47" y="-35"/>
+        <delta pt="19" x="42" y="-35"/>
+        <delta pt="20" x="30" y="-35"/>
+        <delta pt="21" x="13" y="-29"/>
+        <delta pt="22" x="14" y="-12"/>
+        <delta pt="23" x="21" y="-21"/>
+        <delta pt="24" x="54" y="-32"/>
+        <delta pt="25" x="46" y="-27"/>
+        <delta pt="26" x="50" y="-26"/>
+        <delta pt="27" x="41" y="-26"/>
+        <delta pt="28" x="50" y="-26"/>
+        <delta pt="29" x="68" y="-11"/>
+        <delta pt="30" x="68" y="-2"/>
+        <delta pt="31" x="68" y="10"/>
+        <delta pt="32" x="56" y="31"/>
+        <delta pt="33" x="50" y="29"/>
+        <delta pt="34" x="44" y="29"/>
+        <delta pt="35" x="46" y="22"/>
+        <delta pt="36" x="62" y="24"/>
+        <delta pt="37" x="62" y="6"/>
+        <delta pt="38" x="62" y="5"/>
+        <delta pt="39" x="38" y="-3"/>
+        <delta pt="40" x="18" y="-3"/>
+        <delta pt="41" x="-6" y="-3"/>
+        <delta pt="42" x="-3" y="-12"/>
+        <delta pt="43" x="0" y="-9"/>
+        <delta pt="44" x="4" y="4"/>
+        <delta pt="45" x="18" y="12"/>
+        <delta pt="46" x="22" y="12"/>
+        <delta pt="47" x="36" y="12"/>
+        <delta pt="48" x="39" y="5"/>
+        <delta pt="49" x="34" y="6"/>
+        <delta pt="50" x="25" y="-3"/>
+        <delta pt="51" x="0" y="16"/>
+        <delta pt="52" x="3" y="12"/>
+        <delta pt="53" x="-8" y="4"/>
+        <delta pt="54" x="46" y="-8"/>
+        <delta pt="55" x="15" y="-8"/>
+        <delta pt="56" x="15" y="-19"/>
+        <delta pt="57" x="46" y="-19"/>
+        <delta pt="58" x="8" y="0"/>
+        <delta pt="59" x="8" y="-8"/>
+        <delta pt="60" x="39" y="-8"/>
+        <delta pt="61" x="39" y="0"/>
+        <delta pt="62" x="0" y="0"/>
+        <delta pt="63" x="63" y="0"/>
+        <delta pt="64" x="0" y="-19"/>
+        <delta pt="65" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="0" y="-7"/>
+        <delta pt="1" x="-1" y="-7"/>
+        <delta pt="2" x="0" y="-3"/>
+        <delta pt="3" x="0" y="-2"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="0" y="0"/>
+        <delta pt="10" x="0" y="0"/>
+        <delta pt="11" x="0" y="0"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="0"/>
+        <delta pt="14" x="2" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+        <delta pt="17" x="-1" y="0"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="0" y="0"/>
+        <delta pt="20" x="0" y="0"/>
+        <delta pt="21" x="0" y="0"/>
+        <delta pt="22" x="0" y="0"/>
+        <delta pt="23" x="-1" y="9"/>
+        <delta pt="24" x="0" y="7"/>
+        <delta pt="25" x="0" y="7"/>
+        <delta pt="26" x="0" y="7"/>
+        <delta pt="27" x="0" y="7"/>
+        <delta pt="28" x="-1" y="7"/>
+        <delta pt="29" x="0" y="2"/>
+        <delta pt="30" x="0" y="3"/>
+        <delta pt="31" x="0" y="1"/>
+        <delta pt="32" x="0" y="0"/>
+        <delta pt="33" x="0" y="0"/>
+        <delta pt="34" x="0" y="0"/>
+        <delta pt="35" x="0" y="0"/>
+        <delta pt="36" x="0" y="0"/>
+        <delta pt="37" x="0" y="1"/>
+        <delta pt="38" x="0" y="1"/>
+        <delta pt="39" x="0" y="0"/>
+        <delta pt="40" x="0" y="0"/>
+        <delta pt="41" x="0" y="0"/>
+        <delta pt="42" x="0" y="0"/>
+        <delta pt="43" x="0" y="0"/>
+        <delta pt="44" x="0" y="0"/>
+        <delta pt="45" x="0" y="0"/>
+        <delta pt="46" x="0" y="0"/>
+        <delta pt="47" x="0" y="0"/>
+        <delta pt="48" x="0" y="0"/>
+        <delta pt="49" x="0" y="0"/>
+        <delta pt="50" x="0" y="-6"/>
+        <delta pt="51" x="0" y="-7"/>
+        <delta pt="52" x="0" y="-6"/>
+        <delta pt="53" x="0" y="-7"/>
+        <delta pt="54" x="-4" y="0"/>
+        <delta pt="55" x="4" y="0"/>
+        <delta pt="56" x="4" y="0"/>
+        <delta pt="57" x="-4" y="0"/>
+        <delta pt="58" x="4" y="0"/>
+        <delta pt="59" x="4" y="0"/>
+        <delta pt="60" x="-4" y="0"/>
+        <delta pt="61" x="-4" y="0"/>
+        <delta pt="62" x="0" y="0"/>
+        <delta pt="63" x="0" y="0"/>
+        <delta pt="64" x="0" y="0"/>
+        <delta pt="65" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="0" y="7"/>
+        <delta pt="1" x="1" y="7"/>
+        <delta pt="2" x="0" y="3"/>
+        <delta pt="3" x="0" y="2"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="0" y="0"/>
+        <delta pt="10" x="0" y="0"/>
+        <delta pt="11" x="0" y="0"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="0"/>
+        <delta pt="14" x="-2" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+        <delta pt="17" x="1" y="0"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="0" y="0"/>
+        <delta pt="20" x="0" y="0"/>
+        <delta pt="21" x="0" y="0"/>
+        <delta pt="22" x="0" y="0"/>
+        <delta pt="23" x="1" y="-9"/>
+        <delta pt="24" x="0" y="-7"/>
+        <delta pt="25" x="0" y="-7"/>
+        <delta pt="26" x="0" y="-7"/>
+        <delta pt="27" x="0" y="-7"/>
+        <delta pt="28" x="1" y="-7"/>
+        <delta pt="29" x="0" y="-2"/>
+        <delta pt="30" x="0" y="-3"/>
+        <delta pt="31" x="0" y="-1"/>
+        <delta pt="32" x="0" y="0"/>
+        <delta pt="33" x="0" y="0"/>
+        <delta pt="34" x="0" y="0"/>
+        <delta pt="35" x="0" y="0"/>
+        <delta pt="36" x="0" y="0"/>
+        <delta pt="37" x="0" y="-1"/>
+        <delta pt="38" x="0" y="-1"/>
+        <delta pt="39" x="0" y="0"/>
+        <delta pt="40" x="0" y="0"/>
+        <delta pt="41" x="0" y="0"/>
+        <delta pt="42" x="0" y="0"/>
+        <delta pt="43" x="0" y="0"/>
+        <delta pt="44" x="0" y="0"/>
+        <delta pt="45" x="0" y="0"/>
+        <delta pt="46" x="0" y="0"/>
+        <delta pt="47" x="0" y="0"/>
+        <delta pt="48" x="0" y="0"/>
+        <delta pt="49" x="0" y="0"/>
+        <delta pt="50" x="0" y="6"/>
+        <delta pt="51" x="0" y="7"/>
+        <delta pt="52" x="0" y="6"/>
+        <delta pt="53" x="0" y="7"/>
+        <delta pt="54" x="4" y="0"/>
+        <delta pt="55" x="-4" y="0"/>
+        <delta pt="56" x="-4" y="0"/>
+        <delta pt="57" x="4" y="0"/>
+        <delta pt="58" x="-4" y="0"/>
+        <delta pt="59" x="-4" y="0"/>
+        <delta pt="60" x="4" y="0"/>
+        <delta pt="61" x="4" y="0"/>
+        <delta pt="62" x="0" y="0"/>
+        <delta pt="63" x="0" y="0"/>
+        <delta pt="64" x="0" y="0"/>
+        <delta pt="65" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="0" y="-13"/>
+        <delta pt="1" x="-1" y="-13"/>
+        <delta pt="2" x="0" y="-3"/>
+        <delta pt="3" x="0" y="-3"/>
+        <delta pt="4" x="0" y="-1"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="0" y="0"/>
+        <delta pt="10" x="0" y="0"/>
+        <delta pt="11" x="0" y="0"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="0"/>
+        <delta pt="14" x="4" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+        <delta pt="17" x="0" y="1"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="-1" y="0"/>
+        <delta pt="20" x="0" y="0"/>
+        <delta pt="21" x="0" y="1"/>
+        <delta pt="22" x="0" y="0"/>
+        <delta pt="23" x="-2" y="16"/>
+        <delta pt="24" x="0" y="13"/>
+        <delta pt="25" x="0" y="13"/>
+        <delta pt="26" x="0" y="13"/>
+        <delta pt="27" x="0" y="13"/>
+        <delta pt="28" x="0" y="13"/>
+        <delta pt="29" x="0" y="2"/>
+        <delta pt="30" x="0" y="5"/>
+        <delta pt="31" x="0" y="1"/>
+        <delta pt="32" x="0" y="0"/>
+        <delta pt="33" x="0" y="0"/>
+        <delta pt="34" x="0" y="0"/>
+        <delta pt="35" x="0" y="0"/>
+        <delta pt="36" x="0" y="2"/>
+        <delta pt="37" x="0" y="1"/>
+        <delta pt="38" x="0" y="1"/>
+        <delta pt="39" x="0" y="0"/>
+        <delta pt="40" x="0" y="0"/>
+        <delta pt="41" x="0" y="0"/>
+        <delta pt="42" x="0" y="0"/>
+        <delta pt="43" x="0" y="0"/>
+        <delta pt="44" x="0" y="0"/>
+        <delta pt="45" x="0" y="0"/>
+        <delta pt="46" x="0" y="0"/>
+        <delta pt="47" x="0" y="0"/>
+        <delta pt="48" x="1" y="0"/>
+        <delta pt="49" x="0" y="0"/>
+        <delta pt="50" x="0" y="-9"/>
+        <delta pt="51" x="0" y="-13"/>
+        <delta pt="52" x="0" y="-14"/>
+        <delta pt="53" x="0" y="-13"/>
+        <delta pt="54" x="-6" y="0"/>
+        <delta pt="55" x="6" y="0"/>
+        <delta pt="56" x="6" y="0"/>
+        <delta pt="57" x="-6" y="0"/>
+        <delta pt="58" x="6" y="0"/>
+        <delta pt="59" x="6" y="0"/>
+        <delta pt="60" x="-6" y="0"/>
+        <delta pt="61" x="-6" y="0"/>
+        <delta pt="62" x="0" y="0"/>
+        <delta pt="63" x="0" y="0"/>
+        <delta pt="64" x="0" y="0"/>
+        <delta pt="65" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="uni0024.nostroke">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="0" x="-3" y="-28"/>
+        <delta pt="1" x="2" y="-28"/>
+        <delta pt="2" x="17" y="-12"/>
+        <delta pt="3" x="17" y="2"/>
+        <delta pt="4" x="17" y="4"/>
+        <delta pt="5" x="20" y="25"/>
+        <delta pt="6" x="-2" y="42"/>
+        <delta pt="7" x="-2" y="46"/>
+        <delta pt="8" x="8" y="49"/>
+        <delta pt="9" x="28" y="45"/>
+        <delta pt="10" x="28" y="30"/>
+        <delta pt="11" x="28" y="35"/>
+        <delta pt="12" x="21" y="7"/>
+        <delta pt="13" x="4" y="7"/>
+        <delta pt="14" x="-9" y="7"/>
+        <delta pt="15" x="-21" y="19"/>
+        <delta pt="16" x="-8" y="17"/>
+        <delta pt="17" x="-3" y="22"/>
+        <delta pt="18" x="9" y="27"/>
+        <delta pt="19" x="13" y="27"/>
+        <delta pt="20" x="22" y="27"/>
+        <delta pt="21" x="41" y="9"/>
+        <delta pt="22" x="45" y="-19"/>
+        <delta pt="23" x="34" y="5"/>
+        <delta pt="24" x="20" y="-6"/>
+        <delta pt="25" x="18" y="18"/>
+        <delta pt="26" x="12" y="36"/>
+        <delta pt="27" x="-7" y="36"/>
+        <delta pt="28" x="-18" y="36"/>
+        <delta pt="29" x="-24" y="21"/>
+        <delta pt="30" x="-24" y="9"/>
+        <delta pt="31" x="-24" y="6"/>
+        <delta pt="32" x="-17" y="-13"/>
+        <delta pt="33" x="0" y="-25"/>
+        <delta pt="34" x="-19" y="-20"/>
+        <delta pt="35" x="-20" y="-28"/>
+        <delta pt="36" x="-35" y="-28"/>
+        <delta pt="37" x="-35" y="-19"/>
+        <delta pt="38" x="-35" y="-22"/>
+        <delta pt="39" x="-32" y="0"/>
+        <delta pt="40" x="2" y="0"/>
+        <delta pt="41" x="2" y="0"/>
+        <delta pt="42" x="17" y="-9"/>
+        <delta pt="43" x="2" y="-10"/>
+        <delta pt="44" x="-4" y="-14"/>
+        <delta pt="45" x="-14" y="-20"/>
+        <delta pt="46" x="-20" y="-20"/>
+        <delta pt="47" x="-30" y="-20"/>
+        <delta pt="48" x="-45" y="-2"/>
+        <delta pt="49" x="-51" y="26"/>
+        <delta pt="50" x="-40" y="2"/>
+        <delta pt="51" x="-23" y="14"/>
+        <delta pt="52" x="-22" y="-10"/>
+        <delta pt="53" x="-23" y="-28"/>
+        <delta pt="54" x="-16" y="22"/>
+        <delta pt="55" x="2" y="22"/>
+        <delta pt="56" x="2" y="12"/>
+        <delta pt="57" x="-16" y="12"/>
+        <delta pt="58" x="3" y="0"/>
+        <delta pt="59" x="3" y="-16"/>
+        <delta pt="60" x="-15" y="-16"/>
+        <delta pt="61" x="-15" y="0"/>
+        <delta pt="62" x="0" y="0"/>
+        <delta pt="63" x="-7" y="0"/>
+        <delta pt="64" x="0" y="12"/>
+        <delta pt="65" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="12" y="4"/>
+        <delta pt="1" x="-6" y="4"/>
+        <delta pt="2" x="-24" y="-7"/>
+        <delta pt="3" x="-24" y="-25"/>
+        <delta pt="4" x="-24" y="-31"/>
+        <delta pt="5" x="-12" y="-41"/>
+        <delta pt="6" x="3" y="-39"/>
+        <delta pt="7" x="9" y="-37"/>
+        <delta pt="8" x="4" y="-25"/>
+        <delta pt="9" x="-1" y="-24"/>
+        <delta pt="10" x="-1" y="-21"/>
+        <delta pt="11" x="-1" y="-20"/>
+        <delta pt="12" x="10" y="-20"/>
+        <delta pt="13" x="40" y="-20"/>
+        <delta pt="14" x="55" y="-20"/>
+        <delta pt="15" x="63" y="-11"/>
+        <delta pt="16" x="58" y="-10"/>
+        <delta pt="17" x="60" y="-25"/>
+        <delta pt="18" x="47" y="-35"/>
+        <delta pt="19" x="42" y="-35"/>
+        <delta pt="20" x="30" y="-35"/>
+        <delta pt="21" x="13" y="-29"/>
+        <delta pt="22" x="14" y="-12"/>
+        <delta pt="23" x="21" y="-21"/>
+        <delta pt="24" x="54" y="-32"/>
+        <delta pt="25" x="46" y="-27"/>
+        <delta pt="26" x="49" y="-26"/>
+        <delta pt="27" x="40" y="-26"/>
+        <delta pt="28" x="55" y="-26"/>
+        <delta pt="29" x="80" y="-10"/>
+        <delta pt="30" x="80" y="1"/>
+        <delta pt="31" x="80" y="15"/>
+        <delta pt="32" x="62" y="34"/>
+        <delta pt="33" x="50" y="31"/>
+        <delta pt="34" x="44" y="29"/>
+        <delta pt="35" x="44" y="17"/>
+        <delta pt="36" x="62" y="24"/>
+        <delta pt="37" x="62" y="6"/>
+        <delta pt="38" x="62" y="5"/>
+        <delta pt="39" x="38" y="-3"/>
+        <delta pt="40" x="18" y="-3"/>
+        <delta pt="41" x="-6" y="-3"/>
+        <delta pt="42" x="-3" y="-12"/>
+        <delta pt="43" x="0" y="-9"/>
+        <delta pt="44" x="4" y="4"/>
+        <delta pt="45" x="18" y="12"/>
+        <delta pt="46" x="22" y="12"/>
+        <delta pt="47" x="36" y="12"/>
+        <delta pt="48" x="39" y="5"/>
+        <delta pt="49" x="34" y="6"/>
+        <delta pt="50" x="25" y="-3"/>
+        <delta pt="51" x="0" y="16"/>
+        <delta pt="52" x="3" y="12"/>
+        <delta pt="53" x="-8" y="4"/>
+        <delta pt="54" x="46" y="-23"/>
+        <delta pt="55" x="15" y="-23"/>
+        <delta pt="56" x="15" y="-19"/>
+        <delta pt="57" x="46" y="-19"/>
+        <delta pt="58" x="18" y="0"/>
+        <delta pt="59" x="18" y="2"/>
+        <delta pt="60" x="49" y="2"/>
+        <delta pt="61" x="49" y="0"/>
+        <delta pt="62" x="0" y="0"/>
+        <delta pt="63" x="63" y="0"/>
+        <delta pt="64" x="0" y="-19"/>
+        <delta pt="65" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="0" y="-7"/>
+        <delta pt="1" x="-1" y="-7"/>
+        <delta pt="2" x="-1" y="-6"/>
+        <delta pt="3" x="-1" y="-5"/>
+        <delta pt="4" x="-1" y="-4"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="0" y="0"/>
+        <delta pt="10" x="0" y="0"/>
+        <delta pt="11" x="0" y="0"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="0"/>
+        <delta pt="14" x="2" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+        <delta pt="17" x="-1" y="0"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="0" y="0"/>
+        <delta pt="20" x="0" y="0"/>
+        <delta pt="21" x="0" y="0"/>
+        <delta pt="22" x="0" y="0"/>
+        <delta pt="23" x="-1" y="9"/>
+        <delta pt="24" x="0" y="7"/>
+        <delta pt="25" x="0" y="7"/>
+        <delta pt="26" x="0" y="7"/>
+        <delta pt="27" x="0" y="7"/>
+        <delta pt="28" x="0" y="7"/>
+        <delta pt="29" x="1" y="2"/>
+        <delta pt="30" x="1" y="4"/>
+        <delta pt="31" x="1" y="2"/>
+        <delta pt="32" x="0" y="0"/>
+        <delta pt="33" x="0" y="0"/>
+        <delta pt="34" x="0" y="0"/>
+        <delta pt="35" x="0" y="0"/>
+        <delta pt="36" x="0" y="0"/>
+        <delta pt="37" x="0" y="1"/>
+        <delta pt="38" x="0" y="1"/>
+        <delta pt="39" x="0" y="0"/>
+        <delta pt="40" x="0" y="0"/>
+        <delta pt="41" x="0" y="0"/>
+        <delta pt="42" x="0" y="0"/>
+        <delta pt="43" x="0" y="0"/>
+        <delta pt="44" x="0" y="0"/>
+        <delta pt="45" x="0" y="0"/>
+        <delta pt="46" x="0" y="0"/>
+        <delta pt="47" x="0" y="0"/>
+        <delta pt="48" x="0" y="0"/>
+        <delta pt="49" x="0" y="0"/>
+        <delta pt="50" x="0" y="-6"/>
+        <delta pt="51" x="0" y="-7"/>
+        <delta pt="52" x="0" y="-6"/>
+        <delta pt="53" x="0" y="-7"/>
+        <delta pt="54" x="-4" y="4"/>
+        <delta pt="55" x="4" y="4"/>
+        <delta pt="56" x="4" y="0"/>
+        <delta pt="57" x="-4" y="0"/>
+        <delta pt="58" x="0" y="0"/>
+        <delta pt="59" x="0" y="-4"/>
+        <delta pt="60" x="-7" y="-4"/>
+        <delta pt="61" x="-7" y="0"/>
+        <delta pt="62" x="0" y="0"/>
+        <delta pt="63" x="0" y="0"/>
+        <delta pt="64" x="0" y="0"/>
+        <delta pt="65" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="0" y="7"/>
+        <delta pt="1" x="1" y="7"/>
+        <delta pt="2" x="1" y="6"/>
+        <delta pt="3" x="1" y="5"/>
+        <delta pt="4" x="1" y="4"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="0" y="0"/>
+        <delta pt="10" x="0" y="0"/>
+        <delta pt="11" x="0" y="0"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="0"/>
+        <delta pt="14" x="-2" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+        <delta pt="17" x="1" y="0"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="0" y="0"/>
+        <delta pt="20" x="0" y="0"/>
+        <delta pt="21" x="0" y="0"/>
+        <delta pt="22" x="0" y="0"/>
+        <delta pt="23" x="1" y="-9"/>
+        <delta pt="24" x="0" y="-7"/>
+        <delta pt="25" x="0" y="-7"/>
+        <delta pt="26" x="0" y="-7"/>
+        <delta pt="27" x="0" y="-7"/>
+        <delta pt="28" x="0" y="-7"/>
+        <delta pt="29" x="-1" y="-2"/>
+        <delta pt="30" x="-1" y="-4"/>
+        <delta pt="31" x="-1" y="-2"/>
+        <delta pt="32" x="0" y="0"/>
+        <delta pt="33" x="0" y="0"/>
+        <delta pt="34" x="0" y="0"/>
+        <delta pt="35" x="0" y="0"/>
+        <delta pt="36" x="0" y="0"/>
+        <delta pt="37" x="0" y="-1"/>
+        <delta pt="38" x="0" y="-1"/>
+        <delta pt="39" x="0" y="0"/>
+        <delta pt="40" x="0" y="0"/>
+        <delta pt="41" x="0" y="0"/>
+        <delta pt="42" x="0" y="0"/>
+        <delta pt="43" x="0" y="0"/>
+        <delta pt="44" x="0" y="0"/>
+        <delta pt="45" x="0" y="0"/>
+        <delta pt="46" x="0" y="0"/>
+        <delta pt="47" x="0" y="0"/>
+        <delta pt="48" x="0" y="0"/>
+        <delta pt="49" x="0" y="0"/>
+        <delta pt="50" x="0" y="6"/>
+        <delta pt="51" x="0" y="7"/>
+        <delta pt="52" x="0" y="6"/>
+        <delta pt="53" x="0" y="7"/>
+        <delta pt="54" x="4" y="-4"/>
+        <delta pt="55" x="-4" y="-4"/>
+        <delta pt="56" x="-4" y="0"/>
+        <delta pt="57" x="4" y="0"/>
+        <delta pt="58" x="0" y="0"/>
+        <delta pt="59" x="0" y="4"/>
+        <delta pt="60" x="7" y="4"/>
+        <delta pt="61" x="7" y="0"/>
+        <delta pt="62" x="0" y="0"/>
+        <delta pt="63" x="0" y="0"/>
+        <delta pt="64" x="0" y="0"/>
+        <delta pt="65" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <coord axis="cntr" value="1.0"/>
+        <delta pt="0" x="0" y="-13"/>
+        <delta pt="1" x="-2" y="-13"/>
+        <delta pt="2" x="-3" y="-10"/>
+        <delta pt="3" x="-3" y="-9"/>
+        <delta pt="4" x="-3" y="-7"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="0" y="0"/>
+        <delta pt="10" x="0" y="0"/>
+        <delta pt="11" x="0" y="0"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="0"/>
+        <delta pt="14" x="4" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+        <delta pt="17" x="0" y="1"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="-1" y="0"/>
+        <delta pt="20" x="0" y="0"/>
+        <delta pt="21" x="0" y="1"/>
+        <delta pt="22" x="0" y="0"/>
+        <delta pt="23" x="-2" y="16"/>
+        <delta pt="24" x="0" y="13"/>
+        <delta pt="25" x="0" y="13"/>
+        <delta pt="26" x="1" y="13"/>
+        <delta pt="27" x="1" y="13"/>
+        <delta pt="28" x="0" y="13"/>
+        <delta pt="29" x="1" y="3"/>
+        <delta pt="30" x="1" y="6"/>
+        <delta pt="31" x="1" y="2"/>
+        <delta pt="32" x="3" y="-2"/>
+        <delta pt="33" x="0" y="0"/>
+        <delta pt="34" x="0" y="0"/>
+        <delta pt="35" x="0" y="0"/>
+        <delta pt="36" x="0" y="2"/>
+        <delta pt="37" x="0" y="1"/>
+        <delta pt="38" x="0" y="1"/>
+        <delta pt="39" x="0" y="0"/>
+        <delta pt="40" x="0" y="0"/>
+        <delta pt="41" x="0" y="0"/>
+        <delta pt="42" x="0" y="0"/>
+        <delta pt="43" x="0" y="0"/>
+        <delta pt="44" x="0" y="0"/>
+        <delta pt="45" x="0" y="0"/>
+        <delta pt="46" x="0" y="0"/>
+        <delta pt="47" x="0" y="0"/>
+        <delta pt="48" x="1" y="0"/>
+        <delta pt="49" x="0" y="0"/>
+        <delta pt="50" x="0" y="-9"/>
+        <delta pt="51" x="0" y="-13"/>
+        <delta pt="52" x="0" y="-14"/>
+        <delta pt="53" x="0" y="-13"/>
+        <delta pt="54" x="-6" y="8"/>
+        <delta pt="55" x="6" y="8"/>
+        <delta pt="56" x="6" y="0"/>
+        <delta pt="57" x="-6" y="0"/>
+        <delta pt="58" x="0" y="0"/>
+        <delta pt="59" x="0" y="-6"/>
+        <delta pt="60" x="-13" y="-6"/>
+        <delta pt="61" x="-13" y="0"/>
+        <delta pt="62" x="0" y="0"/>
+        <delta pt="63" x="0" y="0"/>
+        <delta pt="64" x="0" y="0"/>
+        <delta pt="65" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+  </gvar>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/InterpolateLayout.ttx b/Tests/varLib/data/test_results/InterpolateLayout.ttx
new file mode 100644
index 0000000..b1ea1e9
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateLayout.ttx
@@ -0,0 +1,161 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.7">
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=2 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=6 -->
+            <FeatureIndex index="0" value="0"/>
+            <FeatureIndex index="1" value="1"/>
+            <FeatureIndex index="2" value="2"/>
+            <FeatureIndex index="3" value="3"/>
+            <FeatureIndex index="4" value="4"/>
+            <FeatureIndex index="5" value="5"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="1">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=6 -->
+            <FeatureIndex index="0" value="0"/>
+            <FeatureIndex index="1" value="1"/>
+            <FeatureIndex index="2" value="2"/>
+            <FeatureIndex index="3" value="3"/>
+            <FeatureIndex index="4" value="4"/>
+            <FeatureIndex index="5" value="5"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=6 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="c2sc"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="calt"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="5"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="2">
+        <FeatureTag value="ccmp"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="2"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="3">
+        <FeatureTag value="liga"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="4">
+        <FeatureTag value="salt"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="3"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="5">
+        <FeatureTag value="ss01"/>
+        <Feature>
+          <FeatureParamsStylisticSet>
+            <Version value="0"/>
+            <UINameID value="256"/>  <!-- Alternate a -->
+          </FeatureParamsStylisticSet>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=6 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="A" out="A.sc"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="a" out="a.alt"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0" Format="1">
+          <Substitution in="ampersand" out="a,n,d"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="3"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <AlternateSubst index="0" Format="1">
+          <AlternateSet glyph="a">
+            <Alternate glyph="a.alt"/>
+            <Alternate glyph="A.sc"/>
+          </AlternateSet>
+        </AlternateSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="f">
+            <Ligature components="t" glyph="f_t"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="5">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0" Format="1">
+            <Glyph value="a"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0" Format="1">
+            <Glyph value="t"/>
+          </LookAheadCoverage>
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/InterpolateLayout2.ttx b/Tests/varLib/data/test_results/InterpolateLayout2.ttx
new file mode 100644
index 0000000..37461c4
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateLayout2.ttx
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.7">
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_1_diff.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_1_diff.ttx
new file mode 100644
index 0000000..74e9cc5
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_1_diff.ttx
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.8">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="xxxx"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="A"/>
+          </Coverage>
+          <ValueFormat value="5"/>
+          <Value XPlacement="-88" XAdvance="-177"/>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_1_diff2.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_1_diff2.ttx
new file mode 100644
index 0000000..2e21b26
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_1_diff2.ttx
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.8">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="xxxx"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="A"/>
+            <Glyph value="a"/>
+          </Coverage>
+          <ValueFormat value="5"/>
+          <!-- ValueCount=2 -->
+          <Value index="0" XPlacement="-88" XAdvance="-177"/>
+          <Value index="1" XPlacement="-27" XAdvance="-52"/>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_1_same.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_1_same.ttx
new file mode 100644
index 0000000..a61e75f
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_1_same.ttx
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.8">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="xxxx"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="A"/>
+          </Coverage>
+          <ValueFormat value="5"/>
+          <Value XPlacement="-80" XAdvance="-160"/>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_class_diff.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_class_diff.ttx
new file mode 100644
index 0000000..4f94c37
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_class_diff.ttx
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.8">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="xxxx"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="A"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1 Format="2">
+          </ClassDef1>
+          <ClassDef2 Format="1">
+            <ClassDef glyph="a" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=1 -->
+          <!-- Class2Count=2 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="-40"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_class_diff2.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_class_diff2.ttx
new file mode 100644
index 0000000..811ed58
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_class_diff2.ttx
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.8">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="xxxx"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="A"/>
+            <Glyph value="a"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1 Format="1">
+            <ClassDef glyph="a" class="1"/>
+          </ClassDef1>
+          <ClassDef2 Format="1">
+            <ClassDef glyph="a" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=2 -->
+          <!-- Class2Count=2 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="-40"/>
+            </Class2Record>
+          </Class1Record>
+          <Class1Record index="1">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="10"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_class_same.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_class_same.ttx
new file mode 100644
index 0000000..9872533
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_class_same.ttx
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.8">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="xxxx"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage Format="1">
+            <Glyph value="A"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1 Format="2">
+          </ClassDef1>
+          <ClassDef2 Format="1">
+            <ClassDef glyph="a" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=1 -->
+          <!-- Class2Count=2 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="-53"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_spec_diff.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_spec_diff.ttx
new file mode 100644
index 0000000..113bd0b
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_spec_diff.ttx
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.8">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="xxxx"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="A"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="a"/>
+              <Value1 XAdvance="-40"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_spec_diff2.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_spec_diff2.ttx
new file mode 100644
index 0000000..efc5ee5
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_spec_diff2.ttx
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.8">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="xxxx"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="A"/>
+            <Glyph value="a"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=2 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="a"/>
+              <Value1 XAdvance="-40"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="1">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="a"/>
+              <Value1 XAdvance="10"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_spec_same.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_spec_same.ttx
new file mode 100644
index 0000000..014c1ec
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_spec_same.ttx
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.8">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="xxxx"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="A"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="a"/>
+              <Value1 XAdvance="-53"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_3_diff.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_3_diff.ttx
new file mode 100644
index 0000000..65d77f9
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_3_diff.ttx
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.8">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="xxxx"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="3"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <CursivePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="a"/>
+          </Coverage>
+          <!-- EntryExitCount=1 -->
+          <EntryExitRecord index="0">
+            <EntryAnchor Format="1">
+              <XCoordinate value="49"/>
+              <YCoordinate value="29"/>
+            </EntryAnchor>
+            <ExitAnchor Format="1">
+              <XCoordinate value="444"/>
+              <YCoordinate value="295"/>
+            </ExitAnchor>
+          </EntryExitRecord>
+        </CursivePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_3_same.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_3_same.ttx
new file mode 100644
index 0000000..b7c8a25
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_3_same.ttx
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.8">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="xxxx"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="3"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <CursivePos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="a"/>
+          </Coverage>
+          <!-- EntryExitCount=1 -->
+          <EntryExitRecord index="0">
+            <EntryAnchor Format="1">
+              <XCoordinate value="60"/>
+              <YCoordinate value="15"/>
+            </EntryAnchor>
+            <ExitAnchor Format="1">
+              <XCoordinate value="405"/>
+              <YCoordinate value="310"/>
+            </ExitAnchor>
+          </EntryExitRecord>
+        </CursivePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_4_diff.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_4_diff.ttx
new file mode 100644
index 0000000..72a8ccf
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_4_diff.ttx
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.8">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="xxxx"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkBasePos index="0" Format="1">
+          <MarkCoverage Format="1">
+            <Glyph value="uni0303"/>
+          </MarkCoverage>
+          <BaseCoverage Format="1">
+            <Glyph value="a"/>
+          </BaseCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="0"/>
+                <YCoordinate value="510"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=1 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="273"/>
+                <YCoordinate value="510"/>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_4_same.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_4_same.ttx
new file mode 100644
index 0000000..9b41519
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_4_same.ttx
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.8">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="xxxx"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkBasePos index="0" Format="1">
+          <MarkCoverage Format="1">
+            <Glyph value="uni0303"/>
+          </MarkCoverage>
+          <BaseCoverage Format="1">
+            <Glyph value="a"/>
+          </BaseCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="0"/>
+                <YCoordinate value="500"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=1 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="260"/>
+                <YCoordinate value="500"/>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_5_diff.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_5_diff.ttx
new file mode 100644
index 0000000..28480e7
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_5_diff.ttx
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.8">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="xxxx"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkLigPos index="0" Format="1">
+          <MarkCoverage Format="1">
+            <Glyph value="uni0330"/>
+          </MarkCoverage>
+          <LigatureCoverage Format="1">
+            <Glyph value="f_t"/>
+          </LigatureCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="0"/>
+                <YCoordinate value="-35"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <LigatureArray>
+            <!-- LigatureCount=1 -->
+            <LigatureAttach index="0">
+              <!-- ComponentCount=2 -->
+              <ComponentRecord index="0">
+                <LigatureAnchor index="0" Format="1">
+                  <XCoordinate value="144"/>
+                  <YCoordinate value="-35"/>
+                </LigatureAnchor>
+              </ComponentRecord>
+              <ComponentRecord index="1">
+                <LigatureAnchor index="0" Format="1">
+                  <XCoordinate value="504"/>
+                  <YCoordinate value="-35"/>
+                </LigatureAnchor>
+              </ComponentRecord>
+            </LigatureAttach>
+          </LigatureArray>
+        </MarkLigPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_5_same.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_5_same.ttx
new file mode 100644
index 0000000..4830f9a
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_5_same.ttx
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.8">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="xxxx"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkLigPos index="0" Format="1">
+          <MarkCoverage Format="1">
+            <Glyph value="uni0330"/>
+          </MarkCoverage>
+          <LigatureCoverage Format="1">
+            <Glyph value="f_t"/>
+          </LigatureCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="0"/>
+                <YCoordinate value="-50"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <LigatureArray>
+            <!-- LigatureCount=1 -->
+            <LigatureAttach index="0">
+              <!-- ComponentCount=2 -->
+              <ComponentRecord index="0">
+                <LigatureAnchor index="0" Format="1">
+                  <XCoordinate value="115"/>
+                  <YCoordinate value="-50"/>
+                </LigatureAnchor>
+              </ComponentRecord>
+              <ComponentRecord index="1">
+                <LigatureAnchor index="0" Format="1">
+                  <XCoordinate value="430"/>
+                  <YCoordinate value="-50"/>
+                </LigatureAnchor>
+              </ComponentRecord>
+            </LigatureAttach>
+          </LigatureArray>
+        </MarkLigPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_6_diff.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_6_diff.ttx
new file mode 100644
index 0000000..38d6437
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_6_diff.ttx
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.8">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="xxxx"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkMarkPos index="0" Format="1">
+          <Mark1Coverage Format="1">
+            <Glyph value="uni0303"/>
+          </Mark1Coverage>
+          <Mark2Coverage Format="1">
+            <Glyph value="uni0308"/>
+          </Mark2Coverage>
+          <!-- ClassCount=1 -->
+          <Mark1Array>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="0"/>
+                <YCoordinate value="510"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </Mark1Array>
+          <Mark2Array>
+            <!-- Mark2Count=1 -->
+            <Mark2Record index="0">
+              <Mark2Anchor index="0" Format="1">
+                <XCoordinate value="0"/>
+                <YCoordinate value="703"/>
+              </Mark2Anchor>
+            </Mark2Record>
+          </Mark2Array>
+        </MarkMarkPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_6_same.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_6_same.ttx
new file mode 100644
index 0000000..05e4b51
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_6_same.ttx
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.8">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="xxxx"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkMarkPos index="0" Format="1">
+          <Mark1Coverage Format="1">
+            <Glyph value="uni0303"/>
+          </Mark1Coverage>
+          <Mark2Coverage Format="1">
+            <Glyph value="uni0308"/>
+          </Mark2Coverage>
+          <!-- ClassCount=1 -->
+          <Mark1Array>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="0"/>
+                <YCoordinate value="500"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </Mark1Array>
+          <Mark2Array>
+            <!-- Mark2Count=1 -->
+            <Mark2Record index="0">
+              <Mark2Anchor index="0" Format="1">
+                <XCoordinate value="0"/>
+                <YCoordinate value="675"/>
+              </Mark2Anchor>
+            </Mark2Record>
+          </Mark2Array>
+        </MarkMarkPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_8_diff.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_8_diff.ttx
new file mode 100644
index 0000000..12f4269
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_8_diff.ttx
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.8">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="xxxx"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="2"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=3 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="A"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="a"/>
+              <Value1 XAdvance="17"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkBasePos index="0" Format="1">
+          <MarkCoverage Format="1">
+            <Glyph value="uni0303"/>
+          </MarkCoverage>
+          <BaseCoverage Format="1">
+            <Glyph value="a"/>
+          </BaseCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="0"/>
+                <YCoordinate value="510"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=1 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="273"/>
+                <YCoordinate value="510"/>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=3 -->
+          <InputCoverage index="0" Format="1">
+            <Glyph value="A"/>
+          </InputCoverage>
+          <InputCoverage index="1" Format="1">
+            <Glyph value="a"/>
+          </InputCoverage>
+          <InputCoverage index="2" Format="1">
+            <Glyph value="uni0303"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- PosCount=2 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </PosLookupRecord>
+          <PosLookupRecord index="1">
+            <SequenceIndex value="2"/>
+            <LookupListIndex value="1"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_8_same.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_8_same.ttx
new file mode 100644
index 0000000..b7e86ba
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_8_same.ttx
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.8">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="xxxx"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="2"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=3 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage Format="1">
+            <Glyph value="A"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="a"/>
+              <Value1 XAdvance="-23"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkBasePos index="0" Format="1">
+          <MarkCoverage Format="1">
+            <Glyph value="uni0303"/>
+          </MarkCoverage>
+          <BaseCoverage Format="1">
+            <Glyph value="a"/>
+          </BaseCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="0"/>
+                <YCoordinate value="500"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=1 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="260"/>
+                <YCoordinate value="500"/>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=3 -->
+          <InputCoverage index="0" Format="1">
+            <Glyph value="A"/>
+          </InputCoverage>
+          <InputCoverage index="1" Format="1">
+            <Glyph value="a"/>
+          </InputCoverage>
+          <InputCoverage index="2" Format="1">
+            <Glyph value="uni0303"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- PosCount=2 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </PosLookupRecord>
+          <PosLookupRecord index="1">
+            <SequenceIndex value="2"/>
+            <LookupListIndex value="1"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_size_feat_same.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_size_feat_same.ttx
new file mode 100644
index 0000000..773dc59
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_size_feat_same.ttx
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.8">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="size"/>
+        <Feature>
+          <FeatureParamsSize>
+            <DesignSize value="10.0"/>
+            <SubfamilyID value="0"/>
+            <SubfamilyNameID value="0"/>
+            <RangeStart value="0.0"/>
+            <RangeEnd value="0.0"/>
+          </FeatureParamsSize>
+          <!-- LookupCount=0 -->
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutMain.ttx b/Tests/varLib/data/test_results/InterpolateLayoutMain.ttx
new file mode 100644
index 0000000..0c0af32
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateLayoutMain.ttx
@@ -0,0 +1,499 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.7">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="uni0020"/>
+    <GlyphID id="2" name="uni0041"/>
+    <GlyphID id="3" name="uni0061"/>
+    <GlyphID id="4" name="uni0024"/>
+    <GlyphID id="5" name="uni0024.nostroke"/>
+  </GlyphOrder>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="918"/>
+    <descent value="-335"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="663"/>
+    <minLeftSideBearing value="5"/>
+    <minRightSideBearing value="7"/>
+    <xMaxExtent value="653"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="5"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="62"/>
+    <maxContours value="4"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="1"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="1"/>
+    <maxSizeOfInstructions value="5"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="506"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000100"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="284"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="4"/>
+      <bWeight value="6"/>
+      <bProportion value="3"/>
+      <bContrast value="5"/>
+      <bStrokeVariation value="4"/>
+      <bArmStyle value="5"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000011"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBO"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="97"/>
+    <sTypoAscender value="730"/>
+    <sTypoDescender value="-270"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="918"/>
+    <usWinDescent value="335"/>
+    <ulCodePageRange1 value="00100000 00000000 00000000 00000011"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="474"/>
+    <sCapHeight value="677"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="640" lsb="80"/>
+    <mtx name="uni0020" width="234" lsb="0"/>
+    <mtx name="uni0024" width="497" lsb="51"/>
+    <mtx name="uni0024.nostroke" width="497" lsb="51"/>
+    <mtx name="uni0041" width="663" lsb="5"/>
+    <mtx name="uni0061" width="508" lsb="46"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="uni0020"/><!-- SPACE -->
+      <map code="0x24" name="uni0024"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="uni0041"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="uni0061"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="uni0020"/><!-- SPACE -->
+      <map code="0x24" name="uni0024"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="uni0041"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="uni0061"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+  </cmap>
+
+  <fpgm>
+    <assembly>
+      PUSHB[ ]	/* 1 value pushed */
+      0
+      FDEF[ ]	/* FunctionDefinition */
+        POP[ ]	/* PopTopStack */
+      ENDF[ ]	/* EndFunctionDefinition */
+    </assembly>
+  </fpgm>
+
+  <cvt>
+    <cv index="0" value="474"/>
+    <cv index="1" value="677"/>
+  </cvt>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="80" yMin="0" xMax="560" yMax="670">
+      <contour>
+        <pt x="80" y="0" on="1"/>
+        <pt x="500" y="670" on="1"/>
+        <pt x="560" y="670" on="1"/>
+        <pt x="140" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="560" y="0" on="1"/>
+        <pt x="500" y="0" on="1"/>
+        <pt x="80" y="670" on="1"/>
+        <pt x="140" y="670" on="1"/>
+      </contour>
+      <contour>
+        <pt x="140" y="50" on="1"/>
+        <pt x="500" y="50" on="1"/>
+        <pt x="500" y="620" on="1"/>
+        <pt x="140" y="620" on="1"/>
+      </contour>
+      <contour>
+        <pt x="80" y="0" on="1"/>
+        <pt x="80" y="670" on="1"/>
+        <pt x="560" y="670" on="1"/>
+        <pt x="560" y="0" on="1"/>
+      </contour>
+      <instructions>
+        <assembly>
+          PUSHB[ ]	/* 1 value pushed */
+          0
+          MDAP[0]	/* MoveDirectAbsPt */
+        </assembly>
+      </instructions>
+    </TTGlyph>
+
+    <TTGlyph name="uni0020"/><!-- contains no outline data -->
+
+    <TTGlyph name="uni0024" xMin="51" yMin="-115" xMax="461" yMax="750">
+      <contour>
+        <pt x="248" y="35" on="1"/>
+        <pt x="310" y="35" on="0"/>
+        <pt x="383" y="96" on="0"/>
+        <pt x="383" y="151" on="1"/>
+        <pt x="383" y="198" on="0"/>
+        <pt x="326" y="252" on="0"/>
+        <pt x="262" y="274" on="1"/>
+        <pt x="225" y="287" on="1"/>
+        <pt x="146" y="315" on="0"/>
+        <pt x="58" y="403" on="0"/>
+        <pt x="58" y="478" on="1"/>
+        <pt x="58" y="554" on="0"/>
+        <pt x="170" y="660" on="0"/>
+        <pt x="256" y="660" on="1"/>
+        <pt x="332" y="660" on="0"/>
+        <pt x="438" y="578" on="0"/>
+        <pt x="443" y="514" on="1"/>
+        <pt x="437" y="498" on="0"/>
+        <pt x="413" y="483" on="0"/>
+        <pt x="399" y="483" on="1"/>
+        <pt x="382" y="483" on="0"/>
+        <pt x="349" y="510" on="0"/>
+        <pt x="340" y="549" on="1"/>
+        <pt x="323" y="625" on="1"/>
+        <pt x="375" y="589" on="1"/>
+        <pt x="344" y="602" on="0"/>
+        <pt x="290" y="611" on="0"/>
+        <pt x="267" y="611" on="1"/>
+        <pt x="212" y="611" on="0"/>
+        <pt x="136" y="555" on="0"/>
+        <pt x="136" y="499" on="1"/>
+        <pt x="136" y="451" on="0"/>
+        <pt x="194" y="394" on="0"/>
+        <pt x="247" y="377" on="1"/>
+        <pt x="285" y="364" on="1"/>
+        <pt x="386" y="329" on="0"/>
+        <pt x="461" y="232" on="0"/>
+        <pt x="461" y="170" on="1"/>
+        <pt x="461" y="90" on="0"/>
+        <pt x="345" y="-13" on="0"/>
+        <pt x="243" y="-13" on="1"/>
+        <pt x="163" y="-13" on="0"/>
+        <pt x="54" y="68" on="0"/>
+        <pt x="51" y="133" on="1"/>
+        <pt x="58" y="148" on="0"/>
+        <pt x="80" y="164" on="0"/>
+        <pt x="96" y="164" on="1"/>
+        <pt x="114" y="164" on="0"/>
+        <pt x="145" y="138" on="0"/>
+        <pt x="154" y="98" on="1"/>
+        <pt x="171" y="22" on="1"/>
+        <pt x="118" y="58" on="1"/>
+        <pt x="152" y="44" on="0"/>
+        <pt x="219" y="35" on="0"/>
+      </contour>
+      <contour>
+        <pt x="279" y="322" on="1"/>
+        <pt x="239" y="322" on="1"/>
+        <pt x="239" y="750" on="1"/>
+        <pt x="279" y="750" on="1"/>
+      </contour>
+      <contour>
+        <pt x="238" y="-115" on="1"/>
+        <pt x="238" y="322" on="1"/>
+        <pt x="278" y="322" on="1"/>
+        <pt x="278" y="-115" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0024.nostroke" xMin="51" yMin="-115" xMax="461" yMax="750">
+      <contour>
+        <pt x="248" y="35" on="1"/>
+        <pt x="310" y="35" on="0"/>
+        <pt x="383" y="96" on="0"/>
+        <pt x="383" y="151" on="1"/>
+        <pt x="383" y="198" on="0"/>
+        <pt x="326" y="252" on="0"/>
+        <pt x="262" y="274" on="1"/>
+        <pt x="225" y="287" on="1"/>
+        <pt x="146" y="315" on="0"/>
+        <pt x="58" y="403" on="0"/>
+        <pt x="58" y="478" on="1"/>
+        <pt x="58" y="554" on="0"/>
+        <pt x="170" y="660" on="0"/>
+        <pt x="256" y="660" on="1"/>
+        <pt x="332" y="660" on="0"/>
+        <pt x="438" y="578" on="0"/>
+        <pt x="443" y="514" on="1"/>
+        <pt x="437" y="498" on="0"/>
+        <pt x="413" y="483" on="0"/>
+        <pt x="399" y="483" on="1"/>
+        <pt x="382" y="483" on="0"/>
+        <pt x="349" y="510" on="0"/>
+        <pt x="340" y="549" on="1"/>
+        <pt x="323" y="625" on="1"/>
+        <pt x="375" y="589" on="1"/>
+        <pt x="344" y="602" on="0"/>
+        <pt x="290" y="611" on="0"/>
+        <pt x="267" y="611" on="1"/>
+        <pt x="212" y="611" on="0"/>
+        <pt x="136" y="555" on="0"/>
+        <pt x="136" y="499" on="1"/>
+        <pt x="136" y="451" on="0"/>
+        <pt x="194" y="394" on="0"/>
+        <pt x="247" y="377" on="1"/>
+        <pt x="285" y="364" on="1"/>
+        <pt x="386" y="329" on="0"/>
+        <pt x="461" y="232" on="0"/>
+        <pt x="461" y="170" on="1"/>
+        <pt x="461" y="90" on="0"/>
+        <pt x="345" y="-13" on="0"/>
+        <pt x="243" y="-13" on="1"/>
+        <pt x="163" y="-13" on="0"/>
+        <pt x="54" y="68" on="0"/>
+        <pt x="51" y="133" on="1"/>
+        <pt x="58" y="148" on="0"/>
+        <pt x="80" y="164" on="0"/>
+        <pt x="96" y="164" on="1"/>
+        <pt x="114" y="164" on="0"/>
+        <pt x="145" y="138" on="0"/>
+        <pt x="154" y="98" on="1"/>
+        <pt x="171" y="22" on="1"/>
+        <pt x="118" y="58" on="1"/>
+        <pt x="152" y="44" on="0"/>
+        <pt x="219" y="35" on="0"/>
+      </contour>
+      <contour>
+        <pt x="279" y="635" on="1"/>
+        <pt x="239" y="635" on="1"/>
+        <pt x="239" y="750" on="1"/>
+        <pt x="279" y="750" on="1"/>
+      </contour>
+      <contour>
+        <pt x="238" y="-115" on="1"/>
+        <pt x="238" y="12" on="1"/>
+        <pt x="278" y="12" on="1"/>
+        <pt x="278" y="-115" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0041" xMin="5" yMin="0" xMax="653" yMax="675">
+      <contour>
+        <pt x="5" y="0" on="1"/>
+        <pt x="5" y="40" on="1"/>
+        <pt x="105" y="55" on="1"/>
+        <pt x="125" y="55" on="1"/>
+        <pt x="235" y="40" on="1"/>
+        <pt x="235" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="71" y="0" on="1"/>
+        <pt x="303" y="675" on="1"/>
+        <pt x="363" y="675" on="1"/>
+        <pt x="593" y="0" on="1"/>
+        <pt x="500" y="0" on="1"/>
+        <pt x="299" y="599" on="1"/>
+        <pt x="322" y="599" on="1"/>
+        <pt x="118" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="170" y="219" on="1"/>
+        <pt x="186" y="265" on="1"/>
+        <pt x="456" y="265" on="1"/>
+        <pt x="472" y="219" on="1"/>
+      </contour>
+      <contour>
+        <pt x="383" y="0" on="1"/>
+        <pt x="383" y="40" on="1"/>
+        <pt x="509" y="55" on="1"/>
+        <pt x="529" y="55" on="1"/>
+        <pt x="653" y="40" on="1"/>
+        <pt x="653" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0061" xMin="46" yMin="-13" xMax="501" yMax="487">
+      <contour>
+        <pt x="46" y="112" on="1"/>
+        <pt x="46" y="154" on="0"/>
+        <pt x="110" y="225" on="0"/>
+        <pt x="210" y="262" on="1"/>
+        <pt x="242" y="273" on="0"/>
+        <pt x="328" y="297" on="0"/>
+        <pt x="365" y="304" on="1"/>
+        <pt x="365" y="268" on="1"/>
+        <pt x="331" y="261" on="0"/>
+        <pt x="254" y="237" on="0"/>
+        <pt x="231" y="228" on="1"/>
+        <pt x="164" y="202" on="0"/>
+        <pt x="131" y="148" on="0"/>
+        <pt x="131" y="126" on="1"/>
+        <pt x="131" y="86" on="0"/>
+        <pt x="178" y="52" on="0"/>
+        <pt x="212" y="52" on="1"/>
+        <pt x="238" y="52" on="0"/>
+        <pt x="283" y="76" on="0"/>
+        <pt x="330" y="110" on="1"/>
+        <pt x="350" y="125" on="1"/>
+        <pt x="364" y="104" on="1"/>
+        <pt x="335" y="75" on="1"/>
+        <pt x="290" y="30" on="0"/>
+        <pt x="226" y="-13" on="0"/>
+        <pt x="180" y="-13" on="1"/>
+        <pt x="125" y="-13" on="0"/>
+        <pt x="46" y="50" on="0"/>
+      </contour>
+      <contour>
+        <pt x="325" y="92" on="1"/>
+        <pt x="325" y="320" on="1"/>
+        <pt x="325" y="394" on="0"/>
+        <pt x="280" y="442" on="0"/>
+        <pt x="231" y="442" on="1"/>
+        <pt x="214" y="442" on="0"/>
+        <pt x="169" y="435" on="0"/>
+        <pt x="141" y="424" on="1"/>
+        <pt x="181" y="455" on="1"/>
+        <pt x="155" y="369" on="1"/>
+        <pt x="148" y="347" on="0"/>
+        <pt x="124" y="324" on="0"/>
+        <pt x="104" y="324" on="1"/>
+        <pt x="62" y="324" on="0"/>
+        <pt x="59" y="364" on="1"/>
+        <pt x="73" y="421" on="0"/>
+        <pt x="177" y="487" on="0"/>
+        <pt x="252" y="487" on="1"/>
+        <pt x="329" y="487" on="0"/>
+        <pt x="405" y="408" on="0"/>
+        <pt x="405" y="314" on="1"/>
+        <pt x="405" y="102" on="1"/>
+        <pt x="405" y="68" on="0"/>
+        <pt x="425" y="41" on="0"/>
+        <pt x="442" y="41" on="1"/>
+        <pt x="455" y="41" on="0"/>
+        <pt x="473" y="53" on="0"/>
+        <pt x="481" y="63" on="1"/>
+        <pt x="501" y="41" on="1"/>
+        <pt x="469" y="-10" on="0"/>
+        <pt x="416" y="-10" on="1"/>
+        <pt x="375" y="-10" on="0"/>
+        <pt x="325" y="46" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test Family
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      Version 1.001;ADBO;Test Family Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test Family
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.001
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestFamily-Master1
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Frank Grießhammer
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Master 1
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name="dollar.nostroke"/>
+      <psName name="uni0020"/>
+      <psName name="uni0041"/>
+      <psName name="uni0061"/>
+      <psName name="uni0024"/>
+      <psName name="uni0024.nostroke"/>
+    </extraNames>
+  </post>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/Mutator.ttx b/Tests/varLib/data/test_results/Mutator.ttx
new file mode 100644
index 0000000..a106d21
--- /dev/null
+++ b/Tests/varLib/data/test_results/Mutator.ttx
@@ -0,0 +1,499 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.19">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="uni0020"/>
+    <GlyphID id="2" name="uni0041"/>
+    <GlyphID id="3" name="uni0061"/>
+    <GlyphID id="4" name="uni0024"/>
+    <GlyphID id="5" name="uni0024.nostroke"/>
+  </GlyphOrder>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="918"/>
+    <descent value="-335"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="667"/>
+    <minLeftSideBearing value="7"/>
+    <minRightSideBearing value="4"/>
+    <xMaxExtent value="656"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="5"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="62"/>
+    <maxContours value="4"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="1"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="1"/>
+    <maxSizeOfInstructions value="5"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="506"/>
+    <usWeightClass value="500"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000100"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="286"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="4"/>
+      <bWeight value="6"/>
+      <bProportion value="3"/>
+      <bContrast value="5"/>
+      <bStrokeVariation value="4"/>
+      <bArmStyle value="5"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000011"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBO"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="97"/>
+    <sTypoAscender value="730"/>
+    <sTypoDescender value="-270"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="918"/>
+    <usWinDescent value="335"/>
+    <ulCodePageRange1 value="00100000 00000000 00000000 00000011"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="477"/>
+    <sCapHeight value="677"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="640" lsb="80"/>
+    <mtx name="uni0020" width="228" lsb="0"/>
+    <mtx name="uni0024" width="510" lsb="51"/>
+    <mtx name="uni0024.nostroke" width="510" lsb="51"/>
+    <mtx name="uni0041" width="667" lsb="7"/>
+    <mtx name="uni0061" width="515" lsb="42"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="uni0020"/><!-- SPACE -->
+      <map code="0x24" name="uni0024"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="uni0041"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="uni0061"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="uni0020"/><!-- SPACE -->
+      <map code="0x24" name="uni0024"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="uni0041"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="uni0061"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+  </cmap>
+
+  <fpgm>
+    <assembly>
+      PUSHB[ ]	/* 1 value pushed */
+      0
+      FDEF[ ]	/* FunctionDefinition */
+        POP[ ]	/* PopTopStack */
+      ENDF[ ]	/* EndFunctionDefinition */
+    </assembly>
+  </fpgm>
+
+  <cvt>
+    <cv index="0" value="477"/>
+    <cv index="1" value="677"/>
+  </cvt>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="80" yMin="0" xMax="560" yMax="666">
+      <contour>
+        <pt x="83" y="0" on="1"/>
+        <pt x="503" y="666" on="1"/>
+        <pt x="557" y="666" on="1"/>
+        <pt x="137" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="557" y="0" on="1"/>
+        <pt x="503" y="0" on="1"/>
+        <pt x="83" y="666" on="1"/>
+        <pt x="137" y="666" on="1"/>
+      </contour>
+      <contour>
+        <pt x="142" y="52" on="1"/>
+        <pt x="498" y="52" on="1"/>
+        <pt x="498" y="614" on="1"/>
+        <pt x="142" y="614" on="1"/>
+      </contour>
+      <contour>
+        <pt x="80" y="0" on="1"/>
+        <pt x="80" y="666" on="1"/>
+        <pt x="560" y="666" on="1"/>
+        <pt x="560" y="0" on="1"/>
+      </contour>
+      <instructions>
+        <assembly>
+          PUSHB[ ]	/* 1 value pushed */
+          0
+          MDAP[0]	/* MoveDirectAbsPt */
+        </assembly>
+      </instructions>
+    </TTGlyph>
+
+    <TTGlyph name="uni0020"/><!-- contains no outline data -->
+
+    <TTGlyph name="uni0024" xMin="51" yMin="-115" xMax="474" yMax="746">
+      <contour>
+        <pt x="251" y="31" on="1"/>
+        <pt x="309" y="31" on="0"/>
+        <pt x="379" y="92" on="0"/>
+        <pt x="379" y="144" on="1"/>
+        <pt x="379" y="192" on="0"/>
+        <pt x="325" y="244" on="0"/>
+        <pt x="263" y="266" on="1"/>
+        <pt x="227" y="279" on="1"/>
+        <pt x="146" y="309" on="0"/>
+        <pt x="57" y="398" on="0"/>
+        <pt x="57" y="474" on="1"/>
+        <pt x="57" y="550" on="0"/>
+        <pt x="172" y="656" on="0"/>
+        <pt x="264" y="656" on="1"/>
+        <pt x="345" y="656" on="0"/>
+        <pt x="451" y="576" on="0"/>
+        <pt x="455" y="512" on="1"/>
+        <pt x="449" y="493" on="0"/>
+        <pt x="423" y="476" on="0"/>
+        <pt x="408" y="476" on="1"/>
+        <pt x="388" y="476" on="0"/>
+        <pt x="352" y="504" on="0"/>
+        <pt x="343" y="546" on="1"/>
+        <pt x="327" y="627" on="1"/>
+        <pt x="386" y="587" on="1"/>
+        <pt x="354" y="601" on="0"/>
+        <pt x="300" y="610" on="0"/>
+        <pt x="276" y="610" on="1"/>
+        <pt x="222" y="610" on="0"/>
+        <pt x="150" y="554" on="0"/>
+        <pt x="150" y="501" on="1"/>
+        <pt x="150" y="454" on="0"/>
+        <pt x="206" y="400" on="0"/>
+        <pt x="257" y="383" on="1"/>
+        <pt x="294" y="370" on="1"/>
+        <pt x="396" y="334" on="0"/>
+        <pt x="474" y="237" on="0"/>
+        <pt x="474" y="172" on="1"/>
+        <pt x="474" y="92" on="0"/>
+        <pt x="353" y="-14" on="0"/>
+        <pt x="247" y="-14" on="1"/>
+        <pt x="162" y="-14" on="0"/>
+        <pt x="53" y="65" on="0"/>
+        <pt x="51" y="131" on="1"/>
+        <pt x="59" y="149" on="0"/>
+        <pt x="84" y="167" on="0"/>
+        <pt x="101" y="167" on="1"/>
+        <pt x="122" y="167" on="0"/>
+        <pt x="153" y="139" on="0"/>
+        <pt x="161" y="99" on="1"/>
+        <pt x="176" y="17" on="1"/>
+        <pt x="118" y="56" on="1"/>
+        <pt x="153" y="42" on="0"/>
+        <pt x="217" y="31" on="0"/>
+      </contour>
+      <contour>
+        <pt x="286" y="320" on="1"/>
+        <pt x="245" y="320" on="1"/>
+        <pt x="245" y="746" on="1"/>
+        <pt x="286" y="746" on="1"/>
+      </contour>
+      <contour>
+        <pt x="242" y="-115" on="1"/>
+        <pt x="242" y="320" on="1"/>
+        <pt x="284" y="320" on="1"/>
+        <pt x="284" y="-115" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0024.nostroke" xMin="51" yMin="-115" xMax="474" yMax="746">
+      <contour>
+        <pt x="251" y="31" on="1"/>
+        <pt x="308" y="31" on="0"/>
+        <pt x="377" y="90" on="0"/>
+        <pt x="377" y="142" on="1"/>
+        <pt x="377" y="189" on="0"/>
+        <pt x="323" y="243" on="0"/>
+        <pt x="263" y="266" on="1"/>
+        <pt x="227" y="279" on="1"/>
+        <pt x="147" y="310" on="0"/>
+        <pt x="58" y="398" on="0"/>
+        <pt x="58" y="474" on="1"/>
+        <pt x="58" y="550" on="0"/>
+        <pt x="172" y="656" on="0"/>
+        <pt x="264" y="656" on="1"/>
+        <pt x="345" y="656" on="0"/>
+        <pt x="451" y="576" on="0"/>
+        <pt x="455" y="512" on="1"/>
+        <pt x="449" y="493" on="0"/>
+        <pt x="423" y="476" on="0"/>
+        <pt x="408" y="476" on="1"/>
+        <pt x="388" y="476" on="0"/>
+        <pt x="352" y="504" on="0"/>
+        <pt x="343" y="546" on="1"/>
+        <pt x="327" y="627" on="1"/>
+        <pt x="386" y="587" on="1"/>
+        <pt x="354" y="601" on="0"/>
+        <pt x="300" y="610" on="0"/>
+        <pt x="275" y="610" on="1"/>
+        <pt x="223" y="610" on="0"/>
+        <pt x="153" y="554" on="0"/>
+        <pt x="153" y="502" on="1"/>
+        <pt x="153" y="455" on="0"/>
+        <pt x="207" y="401" on="0"/>
+        <pt x="257" y="383" on="1"/>
+        <pt x="294" y="370" on="1"/>
+        <pt x="395" y="333" on="0"/>
+        <pt x="474" y="237" on="0"/>
+        <pt x="474" y="172" on="1"/>
+        <pt x="474" y="92" on="0"/>
+        <pt x="353" y="-14" on="0"/>
+        <pt x="247" y="-14" on="1"/>
+        <pt x="162" y="-14" on="0"/>
+        <pt x="53" y="65" on="0"/>
+        <pt x="51" y="131" on="1"/>
+        <pt x="59" y="149" on="0"/>
+        <pt x="84" y="167" on="0"/>
+        <pt x="101" y="167" on="1"/>
+        <pt x="122" y="167" on="0"/>
+        <pt x="153" y="139" on="0"/>
+        <pt x="161" y="99" on="1"/>
+        <pt x="176" y="17" on="1"/>
+        <pt x="118" y="56" on="1"/>
+        <pt x="153" y="42" on="0"/>
+        <pt x="217" y="31" on="0"/>
+      </contour>
+      <contour>
+        <pt x="286" y="633" on="1"/>
+        <pt x="245" y="633" on="1"/>
+        <pt x="245" y="746" on="1"/>
+        <pt x="286" y="746" on="1"/>
+      </contour>
+      <contour>
+        <pt x="242" y="-115" on="1"/>
+        <pt x="242" y="10" on="1"/>
+        <pt x="283" y="10" on="1"/>
+        <pt x="283" y="-115" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0041" xMin="7" yMin="0" xMax="656" yMax="670">
+      <contour>
+        <pt x="7" y="0" on="1"/>
+        <pt x="7" y="38" on="1"/>
+        <pt x="104" y="53" on="1"/>
+        <pt x="124" y="53" on="1"/>
+        <pt x="231" y="38" on="1"/>
+        <pt x="231" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="71" y="0" on="1"/>
+        <pt x="297" y="670" on="1"/>
+        <pt x="370" y="670" on="1"/>
+        <pt x="599" y="0" on="1"/>
+        <pt x="483" y="0" on="1"/>
+        <pt x="289" y="586" on="1"/>
+        <pt x="314" y="596" on="1"/>
+        <pt x="119" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="169" y="214" on="1"/>
+        <pt x="184" y="257" on="1"/>
+        <pt x="459" y="257" on="1"/>
+        <pt x="473" y="214" on="1"/>
+      </contour>
+      <contour>
+        <pt x="374" y="0" on="1"/>
+        <pt x="374" y="38" on="1"/>
+        <pt x="499" y="53" on="1"/>
+        <pt x="530" y="53" on="1"/>
+        <pt x="656" y="38" on="1"/>
+        <pt x="656" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0061" xMin="42" yMin="-14" xMax="511" yMax="490">
+      <contour>
+        <pt x="42" y="110" on="1"/>
+        <pt x="42" y="157" on="0"/>
+        <pt x="110" y="229" on="0"/>
+        <pt x="214" y="265" on="1"/>
+        <pt x="246" y="275" on="0"/>
+        <pt x="334" y="299" on="0"/>
+        <pt x="370" y="307" on="1"/>
+        <pt x="370" y="274" on="1"/>
+        <pt x="336" y="266" on="0"/>
+        <pt x="262" y="244" on="0"/>
+        <pt x="242" y="236" on="1"/>
+        <pt x="181" y="212" on="0"/>
+        <pt x="145" y="155" on="0"/>
+        <pt x="145" y="126" on="1"/>
+        <pt x="145" y="86" on="0"/>
+        <pt x="188" y="52" on="0"/>
+        <pt x="220" y="52" on="1"/>
+        <pt x="242" y="52" on="0"/>
+        <pt x="283" y="74" on="0"/>
+        <pt x="323" y="103" on="1"/>
+        <pt x="349" y="125" on="1"/>
+        <pt x="362" y="107" on="1"/>
+        <pt x="329" y="73" on="1"/>
+        <pt x="287" y="29" on="0"/>
+        <pt x="223" y="-14" on="0"/>
+        <pt x="175" y="-14" on="1"/>
+        <pt x="119" y="-14" on="0"/>
+        <pt x="42" y="50" on="0"/>
+      </contour>
+      <contour>
+        <pt x="318" y="88" on="1"/>
+        <pt x="318" y="328" on="1"/>
+        <pt x="318" y="400" on="0"/>
+        <pt x="276" y="448" on="0"/>
+        <pt x="228" y="448" on="1"/>
+        <pt x="210" y="448" on="0"/>
+        <pt x="166" y="442" on="0"/>
+        <pt x="137" y="431" on="1"/>
+        <pt x="185" y="460" on="1"/>
+        <pt x="164" y="379" on="1"/>
+        <pt x="159" y="348" on="0"/>
+        <pt x="129" y="320" on="0"/>
+        <pt x="107" y="320" on="1"/>
+        <pt x="61" y="320" on="0"/>
+        <pt x="55" y="363" on="1"/>
+        <pt x="67" y="421" on="0"/>
+        <pt x="174" y="490" on="0"/>
+        <pt x="259" y="490" on="1"/>
+        <pt x="342" y="490" on="0"/>
+        <pt x="420" y="409" on="0"/>
+        <pt x="420" y="313" on="1"/>
+        <pt x="420" y="93" on="1"/>
+        <pt x="420" y="64" on="0"/>
+        <pt x="437" y="40" on="0"/>
+        <pt x="453" y="40" on="1"/>
+        <pt x="465" y="40" on="0"/>
+        <pt x="482" y="53" on="0"/>
+        <pt x="492" y="66" on="1"/>
+        <pt x="511" y="47" on="1"/>
+        <pt x="479" y="-11" on="0"/>
+        <pt x="416" y="-11" on="1"/>
+        <pt x="372" y="-11" on="0"/>
+        <pt x="320" y="43" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test Family
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      Version 1.001;ADBO;Test Family Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test Family
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.001
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestFamily-Master1
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Frank Grießhammer
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Master 1
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name="dollar.nostroke"/>
+      <psName name="uni0020"/>
+      <psName name="uni0041"/>
+      <psName name="uni0061"/>
+      <psName name="uni0024"/>
+      <psName name="uni0024.nostroke"/>
+    </extraNames>
+  </post>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/Mutator_IUP-instance.ttx b/Tests/varLib/data/test_results/Mutator_IUP-instance.ttx
new file mode 100755
index 0000000..1800479
--- /dev/null
+++ b/Tests/varLib/data/test_results/Mutator_IUP-instance.ttx
@@ -0,0 +1,282 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.10">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="NULL"/>
+    <GlyphID id="2" name="nonmarkingreturn"/>
+    <GlyphID id="3" name="space"/>
+    <GlyphID id="4" name="b"/>
+    <GlyphID id="5" name="q"/>
+    <GlyphID id="6" name="a"/>
+  </GlyphOrder>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="750"/>
+    <descent value="-250"/>
+    <lineGap value="9"/>
+    <advanceWidthMax value="464"/>
+    <minLeftSideBearing value="38"/>
+    <minRightSideBearing value="38"/>
+    <xMaxExtent value="426"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="5"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="7"/>
+    <maxPoints value="20"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="20"/>
+    <maxCompositeContours value="2"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="1"/>
+    <maxComponentDepth value="1"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="347"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="3"/>
+    <fsType value="00000000 00000100"/>
+    <ySubscriptXSize value="700"/>
+    <ySubscriptYSize value="650"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="140"/>
+    <ySuperscriptXSize value="700"/>
+    <ySuperscriptYSize value="650"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="477"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="250"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="0"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="LuFo"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="0"/>
+    <usLastCharIndex value="113"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="608"/>
+    <usWinDescent value="152"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="456"/>
+    <sCapHeight value="608"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="200" lsb="0"/>
+    <mtx name="NULL" width="0" lsb="0"/>
+    <mtx name="a" width="464" lsb="38"/>
+    <mtx name="b" width="464" lsb="76"/>
+    <mtx name="nonmarkingreturn" width="200" lsb="0"/>
+    <mtx name="q" width="464" lsb="38"/>
+    <mtx name="space" width="200" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x0" name="NULL"/><!-- ???? -->
+      <map code="0xd" name="nonmarkingreturn"/><!-- ???? -->
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x62" name="b"/><!-- LATIN SMALL LETTER B -->
+      <map code="0x71" name="q"/><!-- LATIN SMALL LETTER Q -->
+    </cmap_format_4>
+    <cmap_format_6 platformID="1" platEncID="0" language="0">
+      <map code="0x0" name="NULL"/>
+      <map code="0xd" name="nonmarkingreturn"/>
+      <map code="0x20" name="space"/>
+      <map code="0x61" name="a"/>
+      <map code="0x62" name="b"/>
+      <map code="0x71" name="q"/>
+    </cmap_format_6>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x0" name="NULL"/><!-- ???? -->
+      <map code="0xd" name="nonmarkingreturn"/><!-- ???? -->
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x62" name="b"/><!-- LATIN SMALL LETTER B -->
+      <map code="0x71" name="q"/><!-- LATIN SMALL LETTER Q -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef"/><!-- contains no outline data -->
+
+    <TTGlyph name="NULL"/><!-- contains no outline data -->
+
+    <TTGlyph name="a" xMin="38" yMin="-12" xMax="388" yMax="468">
+      <contour>
+        <pt x="312" y="0" on="1"/>
+        <pt x="312" y="64" on="1"/>
+        <pt x="244" y="-12" on="1"/>
+        <pt x="180" y="-12" on="1"/>
+        <pt x="38" y="140" on="1"/>
+        <pt x="38" y="316" on="1"/>
+        <pt x="180" y="468" on="1"/>
+        <pt x="246" y="468" on="1"/>
+        <pt x="312" y="392" on="1"/>
+        <pt x="312" y="456" on="1"/>
+        <pt x="388" y="456" on="1"/>
+        <pt x="388" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="236" y="64" on="1"/>
+        <pt x="312" y="140" on="1"/>
+        <pt x="312" y="316" on="1"/>
+        <pt x="236" y="392" on="1"/>
+        <pt x="160" y="392" on="1"/>
+        <pt x="84" y="316" on="1"/>
+        <pt x="84" y="140" on="1"/>
+        <pt x="160" y="64" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="b" xMin="76" yMin="-12" xMax="426" yMax="628">
+      <contour>
+        <pt x="218" y="468" on="1"/>
+        <pt x="284" y="468" on="1"/>
+        <pt x="426" y="316" on="1"/>
+        <pt x="426" y="140" on="1"/>
+        <pt x="284" y="-12" on="1"/>
+        <pt x="220" y="-12" on="1"/>
+        <pt x="152" y="64" on="1"/>
+        <pt x="152" y="0" on="1"/>
+        <pt x="76" y="0" on="1"/>
+        <pt x="76" y="628" on="1"/>
+        <pt x="152" y="628" on="1"/>
+        <pt x="152" y="392" on="1"/>
+      </contour>
+      <contour>
+        <pt x="152" y="316" on="1"/>
+        <pt x="152" y="140" on="1"/>
+        <pt x="218" y="64" on="1"/>
+        <pt x="284" y="64" on="1"/>
+        <pt x="350" y="140" on="1"/>
+        <pt x="350" y="316" on="1"/>
+        <pt x="284" y="392" on="1"/>
+        <pt x="218" y="392" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="nonmarkingreturn"/><!-- contains no outline data -->
+
+    <TTGlyph name="q" xMin="38" yMin="-172" xMax="388" yMax="468">
+      <component glyphName="b" x="464" y="456" scale="-0.99994" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      VarFont
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      VarFont Regular: 2017
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      VarFont Regular
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      VarFont-Regular
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      VarFont
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      VarFont Regular: 2017
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      VarFont Regular
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      VarFont-Regular
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name="NULL"/>
+    </extraNames>
+  </post>
+
+</ttFont>
diff --git a/Tests/varLib/designspace_test.py b/Tests/varLib/designspace_test.py
new file mode 100644
index 0000000..fbdaab3
--- /dev/null
+++ b/Tests/varLib/designspace_test.py
@@ -0,0 +1,69 @@
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+from fontTools.varLib import designspace
+import os
+import unittest
+
+
+class DesignspaceTest(unittest.TestCase):
+    def test_load(self):
+        self.maxDiff = None
+        self.assertEqual(
+            designspace.load(_getpath("Designspace.designspace")),
+
+                {'sources':
+                  [{'location': {'weight': 0.0},
+                    'groups': {'copy': True},
+                    'filename': 'DesignspaceTest-Light.ufo',
+                    'info': {'copy': True},
+                    'name': 'master_1',
+                    'lib': {'copy': True}},
+                   {'location': {'weight': 1.0},
+                    'name': 'master_2',
+                    'filename': 'DesignspaceTest-Bold.ufo'}],
+
+                 'instances':
+                  [{'location': {'weight': 0.5},
+                    'familyname': 'DesignspaceTest',
+                    'filename': 'instance/DesignspaceTest-Medium.ufo',
+                    'kerning': {},
+                    'info': {},
+                    'stylename': 'Medium'}],
+
+                 'axes':
+                  [{'name': 'weight',
+                    'map': [{'input': 0.0, 'output': 10.0},
+                            {'input': 401.0, 'output': 66.0},
+                            {'input': 1000.0, 'output': 990.0}],
+                    'tag': 'wght',
+                    'maximum': 1000.0,
+                    'minimum': 0.0,
+                    'default': 0.0},
+                   {'maximum': 1000.0,
+                    'default': 250.0,
+                    'minimum': 0.0,
+                    'name': 'width',
+                    'tag': 'wdth'},
+                   {'name': 'contrast',
+                    'tag': 'cntr',
+                    'maximum': 100.0,
+                    'minimum': 0.0,
+                    'default': 0.0,
+                    'labelname': {'de': 'Kontrast', 'en': 'Contrast'}}]
+                }
+        )
+
+    def test_load2(self):
+        self.assertEqual(
+            designspace.load(_getpath("Designspace2.designspace")),
+                    {'sources': [], 'instances': [{}]})
+
+
+def _getpath(testfile):
+    path, _ = os.path.split(__file__)
+    return os.path.join(path, "data", testfile)
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/varLib/interpolatable_test.py b/Tests/varLib/interpolatable_test.py
new file mode 100644
index 0000000..4900d52
--- /dev/null
+++ b/Tests/varLib/interpolatable_test.py
@@ -0,0 +1,102 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.ttLib import TTFont
+from fontTools.varLib.interpolatable import main as interpolatable_main
+import os
+import shutil
+import sys
+import tempfile
+import unittest
+
+try:
+    import scipy
+except:
+    scipy = None
+
+try:
+    import munkres
+except ImportError:
+    munkres = None
+
+
+@unittest.skipUnless(scipy or munkres, "scipy or munkres not installed")
+class InterpolatableTest(unittest.TestCase):
+    def __init__(self, methodName):
+        unittest.TestCase.__init__(self, methodName)
+        # Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
+        # and fires deprecation warnings if a program uses the old name.
+        if not hasattr(self, "assertRaisesRegex"):
+            self.assertRaisesRegex = self.assertRaisesRegexp
+
+    def setUp(self):
+        self.tempdir = None
+        self.num_tempfiles = 0
+
+    def tearDown(self):
+        if self.tempdir:
+            shutil.rmtree(self.tempdir)
+
+    @staticmethod
+    def get_test_input(test_file_or_folder):
+        path, _ = os.path.split(__file__)
+        return os.path.join(path, "data", test_file_or_folder)
+
+    @staticmethod
+    def get_file_list(folder, suffix, prefix=''):
+        all_files = os.listdir(folder)
+        file_list = []
+        for p in all_files:
+            if p.startswith(prefix) and p.endswith(suffix):
+                file_list.append(os.path.abspath(os.path.join(folder, p)))
+        return file_list
+
+    def temp_path(self, suffix):
+        self.temp_dir()
+        self.num_tempfiles += 1
+        return os.path.join(self.tempdir,
+                            "tmp%d%s" % (self.num_tempfiles, suffix))
+
+    def temp_dir(self):
+        if not self.tempdir:
+            self.tempdir = tempfile.mkdtemp()
+
+    def compile_font(self, path, suffix, temp_dir):
+        ttx_filename = os.path.basename(path)
+        savepath = os.path.join(temp_dir, ttx_filename.replace('.ttx', suffix))
+        font = TTFont(recalcBBoxes=False, recalcTimestamp=False)
+        font.importXML(path)
+        font.save(savepath, reorderTables=None)
+        return font, savepath
+
+# -----
+# Tests
+# -----
+
+    def test_interpolatable_ttf(self):
+        suffix = '.ttf'
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
+        for path in ttx_paths:
+            self.compile_font(path, suffix, self.tempdir)
+
+        ttf_paths = self.get_file_list(self.tempdir, suffix)
+        self.assertIsNone(interpolatable_main(ttf_paths))
+
+
+    def test_interpolatable_otf(self):
+        suffix = '.otf'
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_otf')
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
+        for path in ttx_paths:
+            self.compile_font(path, suffix, self.tempdir)
+
+        otf_paths = self.get_file_list(self.tempdir, suffix)
+        self.assertIsNone(interpolatable_main(otf_paths))
+
+
+if __name__ == "__main__":
+    sys.exit(unittest.main())
diff --git a/Tests/varLib/interpolate_layout_test.py b/Tests/varLib/interpolate_layout_test.py
new file mode 100644
index 0000000..6f6efe0
--- /dev/null
+++ b/Tests/varLib/interpolate_layout_test.py
@@ -0,0 +1,890 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.ttLib import TTFont
+from fontTools.varLib import build
+from fontTools.varLib.interpolate_layout import interpolate_layout
+from fontTools.varLib.interpolate_layout import main as interpolate_layout_main
+from fontTools.feaLib.builder import addOpenTypeFeaturesFromString
+import difflib
+import os
+import shutil
+import sys
+import tempfile
+import unittest
+
+
+class InterpolateLayoutTest(unittest.TestCase):
+    def __init__(self, methodName):
+        unittest.TestCase.__init__(self, methodName)
+        # Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
+        # and fires deprecation warnings if a program uses the old name.
+        if not hasattr(self, "assertRaisesRegex"):
+            self.assertRaisesRegex = self.assertRaisesRegexp
+
+    def setUp(self):
+        self.tempdir = None
+        self.num_tempfiles = 0
+
+    def tearDown(self):
+        if self.tempdir:
+            shutil.rmtree(self.tempdir)
+
+    @staticmethod
+    def get_test_input(test_file_or_folder):
+        path, _ = os.path.split(__file__)
+        return os.path.join(path, "data", test_file_or_folder)
+
+    @staticmethod
+    def get_test_output(test_file_or_folder):
+        path, _ = os.path.split(__file__)
+        return os.path.join(path, "data", "test_results", test_file_or_folder)
+
+    @staticmethod
+    def get_file_list(folder, suffix, prefix=''):
+        all_files = os.listdir(folder)
+        file_list = []
+        for p in all_files:
+            if p.startswith(prefix) and p.endswith(suffix):
+                file_list.append(os.path.abspath(os.path.join(folder, p)))
+        return file_list
+
+    def temp_path(self, suffix):
+        self.temp_dir()
+        self.num_tempfiles += 1
+        return os.path.join(self.tempdir,
+                            "tmp%d%s" % (self.num_tempfiles, suffix))
+
+    def temp_dir(self):
+        if not self.tempdir:
+            self.tempdir = tempfile.mkdtemp()
+
+    def read_ttx(self, path):
+        lines = []
+        with open(path, "r", encoding="utf-8") as ttx:
+            for line in ttx.readlines():
+                # Elide ttFont attributes because ttLibVersion may change,
+                # and use os-native line separators so we can run difflib.
+                if line.startswith("<ttFont "):
+                    lines.append("<ttFont>" + os.linesep)
+                else:
+                    lines.append(line.rstrip() + os.linesep)
+        return lines
+
+    def expect_ttx(self, font, expected_ttx, tables):
+        path = self.temp_path(suffix=".ttx")
+        font.saveXML(path, tables=tables)
+        actual = self.read_ttx(path)
+        expected = self.read_ttx(expected_ttx)
+        if actual != expected:
+            for line in difflib.unified_diff(
+                    expected, actual, fromfile=expected_ttx, tofile=path):
+                sys.stdout.write(line)
+            self.fail("TTX output is different from expected")
+
+    def check_ttx_dump(self, font, expected_ttx, tables, suffix):
+        """Ensure the TTX dump is the same after saving and reloading the font."""
+        path = self.temp_path(suffix=suffix)
+        font.save(path)
+        self.expect_ttx(TTFont(path), expected_ttx, tables)
+
+    def compile_font(self, path, suffix, temp_dir, features=None):
+        ttx_filename = os.path.basename(path)
+        savepath = os.path.join(temp_dir, ttx_filename.replace('.ttx', suffix))
+        font = TTFont(recalcBBoxes=False, recalcTimestamp=False)
+        font.importXML(path)
+        if features:
+            addOpenTypeFeaturesFromString(font, features)
+        font.save(savepath, reorderTables=None)
+        return font, savepath
+
+# -----
+# Tests
+# -----
+
+    def test_varlib_interpolate_layout_GSUB_only_ttf(self):
+        """Only GSUB, and only in the base master.
+
+        The variable font will inherit the GSUB table from the
+        base master.
+        """
+        suffix = '.ttf'
+        ds_path = self.get_test_input('InterpolateLayout.designspace')
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
+        for path in ttx_paths:
+            self.compile_font(path, suffix, self.tempdir)
+
+        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
+        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
+
+        tables = ['GSUB']
+        expected_ttx_path = self.get_test_output('InterpolateLayout.ttx')
+        self.expect_ttx(instfont, expected_ttx_path, tables)
+        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
+
+
+    def test_varlib_interpolate_layout_no_GSUB_ttf(self):
+        """The base master has no GSUB table.
+
+        The variable font will end up without a GSUB table.
+        """
+        suffix = '.ttf'
+        ds_path = self.get_test_input('InterpolateLayout2.designspace')
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
+        for path in ttx_paths:
+            self.compile_font(path, suffix, self.tempdir)
+
+        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
+        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
+
+        tables = ['GSUB']
+        expected_ttx_path = self.get_test_output('InterpolateLayout2.ttx')
+        self.expect_ttx(instfont, expected_ttx_path, tables)
+        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
+
+
+    def test_varlib_interpolate_layout_GSUB_only_no_axes_ttf(self):
+        """Only GSUB, and only in the base master.
+        Designspace file has no <axes> element.
+
+        The variable font will inherit the GSUB table from the
+        base master.
+        """
+        suffix = '.ttf'
+        ds_path = self.get_test_input('InterpolateLayout3.designspace')
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
+        for path in ttx_paths:
+            self.compile_font(path, suffix, self.tempdir)
+
+        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
+        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
+
+        tables = ['GSUB']
+        expected_ttx_path = self.get_test_output('InterpolateLayout.ttx')
+        self.expect_ttx(instfont, expected_ttx_path, tables)
+        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
+
+
+    def test_varlib_interpolate_layout_GPOS_only_size_feat_same_val_ttf(self):
+        """Only GPOS; 'size' feature; same values in all masters.
+        """
+        suffix = '.ttf'
+        ds_path = self.get_test_input('InterpolateLayout.designspace')
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        fea_str = """
+        feature size {
+            parameters 10.0 0;
+        } size;
+        """
+        features = [fea_str] * 2
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
+        for i, path in enumerate(ttx_paths):
+            self.compile_font(path, suffix, self.tempdir, features[i])
+
+        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
+        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
+
+        tables = ['GPOS']
+        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_size_feat_same.ttx')
+        self.expect_ttx(instfont, expected_ttx_path, tables)
+        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
+
+
+    def test_varlib_interpolate_layout_GPOS_only_LookupType_1_same_val_ttf(self):
+        """Only GPOS; LookupType 1; same values in all masters.
+        """
+        suffix = '.ttf'
+        ds_path = self.get_test_input('InterpolateLayout.designspace')
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        fea_str = """
+        feature xxxx {
+            pos A <-80 0 -160 0>;
+        } xxxx;
+        """
+        features = [fea_str] * 2
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
+        for i, path in enumerate(ttx_paths):
+            self.compile_font(path, suffix, self.tempdir, features[i])
+
+        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
+        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
+
+        tables = ['GPOS']
+        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_1_same.ttx')
+        self.expect_ttx(instfont, expected_ttx_path, tables)
+        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
+
+
+    def test_varlib_interpolate_layout_GPOS_only_LookupType_1_diff_val_ttf(self):
+        """Only GPOS; LookupType 1; different values in each master.
+        """
+        suffix = '.ttf'
+        ds_path = self.get_test_input('InterpolateLayout.designspace')
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        fea_str_0 = """
+        feature xxxx {
+            pos A <-80 0 -160 0>;
+        } xxxx;
+        """
+        fea_str_1 = """
+        feature xxxx {
+            pos A <-97 0 -195 0>;
+        } xxxx;
+        """
+        features = [fea_str_0, fea_str_1]
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
+        for i, path in enumerate(ttx_paths):
+            self.compile_font(path, suffix, self.tempdir, features[i])
+
+        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
+        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
+
+        tables = ['GPOS']
+        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_1_diff.ttx')
+        self.expect_ttx(instfont, expected_ttx_path, tables)
+        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
+
+
+    def test_varlib_interpolate_layout_GPOS_only_LookupType_1_diff2_val_ttf(self):
+        """Only GPOS; LookupType 1; different values and items in each master.
+        """
+        suffix = '.ttf'
+        ds_path = self.get_test_input('InterpolateLayout.designspace')
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        fea_str_0 = """
+        feature xxxx {
+            pos A <-80 0 -160 0>;
+            pos a <-55 0 -105 0>;
+        } xxxx;
+        """
+        fea_str_1 = """
+        feature xxxx {
+            pos A <-97 0 -195 0>;
+        } xxxx;
+        """
+        features = [fea_str_0, fea_str_1]
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
+        for i, path in enumerate(ttx_paths):
+            self.compile_font(path, suffix, self.tempdir, features[i])
+
+        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
+        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
+
+        tables = ['GPOS']
+        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_1_diff2.ttx')
+        self.expect_ttx(instfont, expected_ttx_path, tables)
+        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
+
+
+    def test_varlib_interpolate_layout_GPOS_only_LookupType_2_spec_pairs_same_val_ttf(self):
+        """Only GPOS; LookupType 2 specific pairs; same values in all masters.
+        """
+        suffix = '.ttf'
+        ds_path = self.get_test_input('InterpolateLayout.designspace')
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        fea_str = """
+        feature xxxx {
+            pos A a -53;
+        } xxxx;
+        """
+        features = [fea_str] * 2
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
+        for i, path in enumerate(ttx_paths):
+            self.compile_font(path, suffix, self.tempdir, features[i])
+
+        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
+        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
+
+        tables = ['GPOS']
+        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_2_spec_same.ttx')
+        self.expect_ttx(instfont, expected_ttx_path, tables)
+        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
+
+
+    def test_varlib_interpolate_layout_GPOS_only_LookupType_2_spec_pairs_diff_val_ttf(self):
+        """Only GPOS; LookupType 2 specific pairs; different values in each master.
+        """
+        suffix = '.ttf'
+        ds_path = self.get_test_input('InterpolateLayout.designspace')
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        fea_str_0 = """
+        feature xxxx {
+            pos A a -53;
+        } xxxx;
+        """
+        fea_str_1 = """
+        feature xxxx {
+            pos A a -27;
+        } xxxx;
+        """
+        features = [fea_str_0, fea_str_1]
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
+        for i, path in enumerate(ttx_paths):
+            self.compile_font(path, suffix, self.tempdir, features[i])
+
+        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
+        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
+
+        tables = ['GPOS']
+        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_2_spec_diff.ttx')
+        self.expect_ttx(instfont, expected_ttx_path, tables)
+        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
+
+
+    def test_varlib_interpolate_layout_GPOS_only_LookupType_2_spec_pairs_diff2_val_ttf(self):
+        """Only GPOS; LookupType 2 specific pairs; different values and items in each master.
+        """
+        suffix = '.ttf'
+        ds_path = self.get_test_input('InterpolateLayout.designspace')
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        fea_str_0 = """
+        feature xxxx {
+            pos A a -53;
+        } xxxx;
+        """
+        fea_str_1 = """
+        feature xxxx {
+            pos A a -27;
+            pos a a 19;
+        } xxxx;
+        """
+        features = [fea_str_0, fea_str_1]
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
+        for i, path in enumerate(ttx_paths):
+            self.compile_font(path, suffix, self.tempdir, features[i])
+
+        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
+        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
+
+        tables = ['GPOS']
+        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_2_spec_diff2.ttx')
+        self.expect_ttx(instfont, expected_ttx_path, tables)
+        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
+
+
+    def test_varlib_interpolate_layout_GPOS_only_LookupType_2_class_pairs_same_val_ttf(self):
+        """Only GPOS; LookupType 2 class pairs; same values in all masters.
+        """
+        suffix = '.ttf'
+        ds_path = self.get_test_input('InterpolateLayout.designspace')
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        fea_str = """
+        feature xxxx {
+            pos [A] [a] -53;
+        } xxxx;
+        """
+        features = [fea_str] * 2
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
+        for i, path in enumerate(ttx_paths):
+            self.compile_font(path, suffix, self.tempdir, features[i])
+
+        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
+        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
+
+        tables = ['GPOS']
+        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_2_class_same.ttx')
+        self.expect_ttx(instfont, expected_ttx_path, tables)
+        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
+
+
+    def test_varlib_interpolate_layout_GPOS_only_LookupType_2_class_pairs_diff_val_ttf(self):
+        """Only GPOS; LookupType 2 class pairs; different values in each master.
+        """
+        suffix = '.ttf'
+        ds_path = self.get_test_input('InterpolateLayout.designspace')
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        fea_str_0 = """
+        feature xxxx {
+            pos [A] [a] -53;
+        } xxxx;
+        """
+        fea_str_1 = """
+        feature xxxx {
+            pos [A] [a] -27;
+        } xxxx;
+        """
+        features = [fea_str_0, fea_str_1]
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
+        for i, path in enumerate(ttx_paths):
+            self.compile_font(path, suffix, self.tempdir, features[i])
+
+        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
+        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
+
+        tables = ['GPOS']
+        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_2_class_diff.ttx')
+        self.expect_ttx(instfont, expected_ttx_path, tables)
+        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
+
+
+    def test_varlib_interpolate_layout_GPOS_only_LookupType_2_class_pairs_diff2_val_ttf(self):
+        """Only GPOS; LookupType 2 class pairs; different values and items in each master.
+        """
+        suffix = '.ttf'
+        ds_path = self.get_test_input('InterpolateLayout.designspace')
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        fea_str_0 = """
+        feature xxxx {
+            pos [A] [a] -53;
+        } xxxx;
+        """
+        fea_str_1 = """
+        feature xxxx {
+            pos [A] [a] -27;
+            pos [a] [a] 19;
+        } xxxx;
+        """
+        features = [fea_str_0, fea_str_1]
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
+        for i, path in enumerate(ttx_paths):
+            self.compile_font(path, suffix, self.tempdir, features[i])
+
+        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
+        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
+
+        tables = ['GPOS']
+        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_2_class_diff2.ttx')
+        self.expect_ttx(instfont, expected_ttx_path, tables)
+        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
+
+
+    def test_varlib_interpolate_layout_GPOS_only_LookupType_3_same_val_ttf(self):
+        """Only GPOS; LookupType 3; same values in all masters.
+        """
+        suffix = '.ttf'
+        ds_path = self.get_test_input('InterpolateLayout.designspace')
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        fea_str = """
+        feature xxxx {
+            pos cursive a <anchor 60 15> <anchor 405 310>;
+        } xxxx;
+        """
+        features = [fea_str] * 2
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
+        for i, path in enumerate(ttx_paths):
+            self.compile_font(path, suffix, self.tempdir, features[i])
+
+        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
+        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
+
+        tables = ['GPOS']
+        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_3_same.ttx')
+        self.expect_ttx(instfont, expected_ttx_path, tables)
+        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
+
+
+    def test_varlib_interpolate_layout_GPOS_only_LookupType_3_diff_val_ttf(self):
+        """Only GPOS; LookupType 3; different values in each master.
+        """
+        suffix = '.ttf'
+        ds_path = self.get_test_input('InterpolateLayout.designspace')
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        fea_str_0 = """
+        feature xxxx {
+            pos cursive a <anchor 60 15> <anchor 405 310>;
+        } xxxx;
+        """
+        fea_str_1 = """
+        feature xxxx {
+            pos cursive a <anchor 38 42> <anchor 483 279>;
+        } xxxx;
+        """
+        features = [fea_str_0, fea_str_1]
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
+        for i, path in enumerate(ttx_paths):
+            self.compile_font(path, suffix, self.tempdir, features[i])
+
+        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
+        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
+
+        tables = ['GPOS']
+        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_3_diff.ttx')
+        self.expect_ttx(instfont, expected_ttx_path, tables)
+        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
+
+
+    def test_varlib_interpolate_layout_GPOS_only_LookupType_4_same_val_ttf(self):
+        """Only GPOS; LookupType 4; same values in all masters.
+        """
+        suffix = '.ttf'
+        ds_path = self.get_test_input('InterpolateLayout.designspace')
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        fea_str = """
+        markClass uni0303 <anchor 0 500> @MARKS_ABOVE;
+        feature xxxx {
+            pos base a <anchor 260 500> mark @MARKS_ABOVE;
+        } xxxx;
+        """
+        features = [fea_str] * 2
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
+        for i, path in enumerate(ttx_paths):
+            self.compile_font(path, suffix, self.tempdir, features[i])
+
+        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
+        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
+
+        tables = ['GPOS']
+        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_4_same.ttx')
+        self.expect_ttx(instfont, expected_ttx_path, tables)
+        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
+
+
+    def test_varlib_interpolate_layout_GPOS_only_LookupType_4_diff_val_ttf(self):
+        """Only GPOS; LookupType 4; different values in each master.
+        """
+        suffix = '.ttf'
+        ds_path = self.get_test_input('InterpolateLayout.designspace')
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        fea_str_0 = """
+        markClass uni0303 <anchor 0 500> @MARKS_ABOVE;
+        feature xxxx {
+            pos base a <anchor 260 500> mark @MARKS_ABOVE;
+        } xxxx;
+        """
+        fea_str_1 = """
+        markClass uni0303 <anchor 0 520> @MARKS_ABOVE;
+        feature xxxx {
+            pos base a <anchor 285 520> mark @MARKS_ABOVE;
+        } xxxx;
+        """
+        features = [fea_str_0, fea_str_1]
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
+        for i, path in enumerate(ttx_paths):
+            self.compile_font(path, suffix, self.tempdir, features[i])
+
+        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
+        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
+
+        tables = ['GPOS']
+        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_4_diff.ttx')
+        self.expect_ttx(instfont, expected_ttx_path, tables)
+        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
+
+
+    def test_varlib_interpolate_layout_GPOS_only_LookupType_5_same_val_ttf(self):
+        """Only GPOS; LookupType 5; same values in all masters.
+        """
+        suffix = '.ttf'
+        ds_path = self.get_test_input('InterpolateLayout.designspace')
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        fea_str = """
+        markClass uni0330 <anchor 0 -50> @MARKS_BELOW;
+        feature xxxx {
+            pos ligature f_t <anchor 115 -50> mark @MARKS_BELOW
+                ligComponent <anchor 430 -50> mark @MARKS_BELOW;
+        } xxxx;
+        """
+        features = [fea_str] * 2
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
+        for i, path in enumerate(ttx_paths):
+            self.compile_font(path, suffix, self.tempdir, features[i])
+
+        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
+        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
+
+        tables = ['GPOS']
+        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_5_same.ttx')
+        self.expect_ttx(instfont, expected_ttx_path, tables)
+        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
+
+
+    def test_varlib_interpolate_layout_GPOS_only_LookupType_5_diff_val_ttf(self):
+        """Only GPOS; LookupType 5; different values in each master.
+        """
+        suffix = '.ttf'
+        ds_path = self.get_test_input('InterpolateLayout.designspace')
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        fea_str_0 = """
+        markClass uni0330 <anchor 0 -50> @MARKS_BELOW;
+        feature xxxx {
+            pos ligature f_t <anchor 115 -50> mark @MARKS_BELOW
+                ligComponent <anchor 430 -50> mark @MARKS_BELOW;
+        } xxxx;
+        """
+        fea_str_1 = """
+        markClass uni0330 <anchor 0 -20> @MARKS_BELOW;
+        feature xxxx {
+            pos ligature f_t <anchor 173 -20> mark @MARKS_BELOW
+                ligComponent <anchor 577 -20> mark @MARKS_BELOW;
+        } xxxx;
+        """
+        features = [fea_str_0, fea_str_1]
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
+        for i, path in enumerate(ttx_paths):
+            self.compile_font(path, suffix, self.tempdir, features[i])
+
+        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
+        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
+
+        tables = ['GPOS']
+        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_5_diff.ttx')
+        self.expect_ttx(instfont, expected_ttx_path, tables)
+        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
+
+
+    def test_varlib_interpolate_layout_GPOS_only_LookupType_6_same_val_ttf(self):
+        """Only GPOS; LookupType 6; same values in all masters.
+        """
+        suffix = '.ttf'
+        ds_path = self.get_test_input('InterpolateLayout.designspace')
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        fea_str = """
+        markClass uni0303 <anchor 0 500> @MARKS_ABOVE;
+        feature xxxx {
+            pos mark uni0308 <anchor 0 675> mark @MARKS_ABOVE;
+        } xxxx;
+        """
+        features = [fea_str] * 2
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
+        for i, path in enumerate(ttx_paths):
+            self.compile_font(path, suffix, self.tempdir, features[i])
+
+        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
+        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
+
+        tables = ['GPOS']
+        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_6_same.ttx')
+        self.expect_ttx(instfont, expected_ttx_path, tables)
+        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
+
+
+    def test_varlib_interpolate_layout_GPOS_only_LookupType_6_diff_val_ttf(self):
+        """Only GPOS; LookupType 6; different values in each master.
+        """
+        suffix = '.ttf'
+        ds_path = self.get_test_input('InterpolateLayout.designspace')
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        fea_str_0 = """
+        markClass uni0303 <anchor 0 500> @MARKS_ABOVE;
+        feature xxxx {
+            pos mark uni0308 <anchor 0 675> mark @MARKS_ABOVE;
+        } xxxx;
+        """
+        fea_str_1 = """
+        markClass uni0303 <anchor 0 520> @MARKS_ABOVE;
+        feature xxxx {
+            pos mark uni0308 <anchor 0 730> mark @MARKS_ABOVE;
+        } xxxx;
+        """
+        features = [fea_str_0, fea_str_1]
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
+        for i, path in enumerate(ttx_paths):
+            self.compile_font(path, suffix, self.tempdir, features[i])
+
+        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
+        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
+
+        tables = ['GPOS']
+        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_6_diff.ttx')
+        self.expect_ttx(instfont, expected_ttx_path, tables)
+        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
+
+
+    def test_varlib_interpolate_layout_GPOS_only_LookupType_8_same_val_ttf(self):
+        """Only GPOS; LookupType 8; same values in all masters.
+        """
+        suffix = '.ttf'
+        ds_path = self.get_test_input('InterpolateLayout.designspace')
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        fea_str = """
+        markClass uni0303 <anchor 0 500> @MARKS_ABOVE;
+        lookup CNTXT_PAIR_POS {
+            pos A a -23;
+        } CNTXT_PAIR_POS;
+
+        lookup CNTXT_MARK_TO_BASE {
+            pos base a <anchor 260 500> mark @MARKS_ABOVE;
+        } CNTXT_MARK_TO_BASE;
+
+        feature xxxx {
+            pos A' lookup CNTXT_PAIR_POS a' @MARKS_ABOVE' lookup CNTXT_MARK_TO_BASE;
+        } xxxx;
+        """
+        features = [fea_str] * 2
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
+        for i, path in enumerate(ttx_paths):
+            self.compile_font(path, suffix, self.tempdir, features[i])
+
+        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
+        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
+
+        tables = ['GPOS']
+        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_8_same.ttx')
+        self.expect_ttx(instfont, expected_ttx_path, tables)
+        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
+
+
+    def test_varlib_interpolate_layout_GPOS_only_LookupType_8_diff_val_ttf(self):
+        """Only GPOS; LookupType 8; different values in each master.
+        """
+        suffix = '.ttf'
+        ds_path = self.get_test_input('InterpolateLayout.designspace')
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        fea_str_0 = """
+        markClass uni0303 <anchor 0 500> @MARKS_ABOVE;
+        lookup CNTXT_PAIR_POS {
+            pos A a -23;
+        } CNTXT_PAIR_POS;
+
+        lookup CNTXT_MARK_TO_BASE {
+            pos base a <anchor 260 500> mark @MARKS_ABOVE;
+        } CNTXT_MARK_TO_BASE;
+
+        feature xxxx {
+            pos A' lookup CNTXT_PAIR_POS a' @MARKS_ABOVE' lookup CNTXT_MARK_TO_BASE;
+        } xxxx;
+        """
+        fea_str_1 = """
+        markClass uni0303 <anchor 0 520> @MARKS_ABOVE;
+        lookup CNTXT_PAIR_POS {
+            pos A a 57;
+        } CNTXT_PAIR_POS;
+
+        lookup CNTXT_MARK_TO_BASE {
+            pos base a <anchor 285 520> mark @MARKS_ABOVE;
+        } CNTXT_MARK_TO_BASE;
+
+        feature xxxx {
+            pos A' lookup CNTXT_PAIR_POS a' @MARKS_ABOVE' lookup CNTXT_MARK_TO_BASE;
+        } xxxx;
+        """
+        features = [fea_str_0, fea_str_1]
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
+        for i, path in enumerate(ttx_paths):
+            self.compile_font(path, suffix, self.tempdir, features[i])
+
+        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
+        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
+
+        tables = ['GPOS']
+        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_8_diff.ttx')
+        self.expect_ttx(instfont, expected_ttx_path, tables)
+        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
+
+
+    def test_varlib_interpolate_layout_main_ttf(self):
+        """Mostly for testing varLib.interpolate_layout.main()
+        """
+        suffix = '.ttf'
+        ds_path = self.get_test_input('Build.designspace')
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        self.temp_dir()
+        ttf_dir = os.path.join(self.tempdir, 'master_ttf_interpolatable')
+        os.makedirs(ttf_dir)
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily-')
+        for path in ttx_paths:
+            self.compile_font(path, suffix, ttf_dir)
+
+        finder = lambda s: s.replace(ufo_dir, ttf_dir).replace('.ufo', suffix)
+        varfont, _, _ = build(ds_path, finder)
+        varfont_name = 'InterpolateLayoutMain'
+        varfont_path = os.path.join(self.tempdir, varfont_name + suffix)
+        varfont.save(varfont_path)
+
+        ds_copy = os.path.splitext(varfont_path)[0] + '.designspace'
+        shutil.copy2(ds_path, ds_copy)
+        args = [ds_copy, 'weight=500', 'contrast=50']
+        interpolate_layout_main(args)
+
+        instfont_path = os.path.splitext(varfont_path)[0] + '-instance' + suffix
+        instfont = TTFont(instfont_path)
+        tables = [table_tag for table_tag in instfont.keys() if table_tag != 'head']
+        expected_ttx_path = self.get_test_output(varfont_name + '.ttx')
+        self.expect_ttx(instfont, expected_ttx_path, tables)
+
+
+if __name__ == "__main__":
+    sys.exit(unittest.main())
diff --git a/Tests/varLib/models_test.py b/Tests/varLib/models_test.py
new file mode 100644
index 0000000..c5c2a9a
--- /dev/null
+++ b/Tests/varLib/models_test.py
@@ -0,0 +1,99 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.varLib.models import (
+    normalizeLocation, supportScalar, VariationModel)
+
+
+def test_normalizeLocation():
+    axes = {"wght": (100, 400, 900)}
+    assert normalizeLocation({"wght": 400}, axes) == {'wght': 0.0}
+    assert normalizeLocation({"wght": 100}, axes) == {'wght': -1.0}
+    assert normalizeLocation({"wght": 900}, axes) == {'wght': 1.0}
+    assert normalizeLocation({"wght": 650}, axes) == {'wght': 0.5}
+    assert normalizeLocation({"wght": 1000}, axes) == {'wght': 1.0}
+    assert normalizeLocation({"wght": 0}, axes) == {'wght': -1.0}
+
+    axes = {"wght": (0, 0, 1000)}
+    assert normalizeLocation({"wght": 0}, axes) == {'wght': 0.0}
+    assert normalizeLocation({"wght": -1}, axes) == {'wght': 0.0}
+    assert normalizeLocation({"wght": 1000}, axes) == {'wght': 1.0}
+    assert normalizeLocation({"wght": 500}, axes) == {'wght': 0.5}
+    assert normalizeLocation({"wght": 1001}, axes) == {'wght': 1.0}
+
+    axes = {"wght": (0, 1000, 1000)}
+    assert normalizeLocation({"wght": 0}, axes) == {'wght': -1.0}
+    assert normalizeLocation({"wght": -1}, axes) == {'wght': -1.0}
+    assert normalizeLocation({"wght": 500}, axes) == {'wght': -0.5}
+    assert normalizeLocation({"wght": 1000}, axes) == {'wght': 0.0}
+    assert normalizeLocation({"wght": 1001}, axes) == {'wght': 0.0}
+
+
+def test_supportScalar():
+    assert supportScalar({}, {}) == 1.0
+    assert supportScalar({'wght':.2}, {}) == 1.0
+    assert supportScalar({'wght':.2}, {'wght':(0,2,3)}) == 0.1
+    assert supportScalar({'wght':2.5}, {'wght':(0,2,4)}) == 0.75
+
+
+def test_VariationModel():
+    locations = [
+        {'wght':100},
+        {'wght':-100},
+        {'wght':-180},
+        {'wdth':+.3},
+        {'wght':+120,'wdth':.3},
+        {'wght':+120,'wdth':.2},
+        {},
+        {'wght':+180,'wdth':.3},
+        {'wght':+180},
+    ]
+    model = VariationModel(locations, axisOrder=['wght'])
+
+    assert model.locations == [
+        {},
+        {'wght': -100},
+        {'wght': -180},
+        {'wght': 100},
+        {'wght': 180},
+        {'wdth': 0.3},
+        {'wdth': 0.3, 'wght': 180},
+        {'wdth': 0.3, 'wght': 120},
+        {'wdth': 0.2, 'wght': 120}]
+
+    assert model.deltaWeights == [
+        {},
+        {0: 1.0},
+        {0: 1.0},
+        {0: 1.0},
+        {0: 1.0},
+        {0: 1.0},
+        {0: 1.0, 4: 1.0, 5: 1.0},
+        {0: 1.0, 3: 0.75, 4: 0.25, 5: 1.0, 6: 0.6666666666666666},
+        {0: 1.0,
+         3: 0.75,
+         4: 0.25,
+         5: 0.6666666666666667,
+         6: 0.4444444444444445,
+         7: 0.6666666666666667}]
+
+def test_VariationModel():
+    locations = [
+        {},
+        {'bar': 0.5},
+        {'bar': 1.0},
+        {'foo': 1.0},
+        {'bar': 0.5, 'foo': 1.0},
+        {'bar': 1.0, 'foo': 1.0},
+    ]
+    model = VariationModel(locations)
+
+    assert model.locations == locations
+
+    assert model.supports == [
+        {},
+        {'bar': (0, 0.5, 1.0)},
+        {'bar': (0.5, 1.0, 1.0)},
+        {'foo': (0, 1.0, 1.0)},
+        {'bar': (0, 0.5, 1.0), 'foo': (0, 1.0, 1.0)},
+        {'bar': (0.5, 1.0, 1.0), 'foo': (0, 1.0, 1.0)},
+    ]
diff --git a/Tests/varLib/mutator_test.py b/Tests/varLib/mutator_test.py
new file mode 100644
index 0000000..de794f0
--- /dev/null
+++ b/Tests/varLib/mutator_test.py
@@ -0,0 +1,144 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.ttLib import TTFont
+from fontTools.varLib import build
+from fontTools.varLib.mutator import main as mutator
+import difflib
+import os
+import shutil
+import sys
+import tempfile
+import unittest
+
+
+class MutatorTest(unittest.TestCase):
+    def __init__(self, methodName):
+        unittest.TestCase.__init__(self, methodName)
+        # Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
+        # and fires deprecation warnings if a program uses the old name.
+        if not hasattr(self, "assertRaisesRegex"):
+            self.assertRaisesRegex = self.assertRaisesRegexp
+
+    def setUp(self):
+        self.tempdir = None
+        self.num_tempfiles = 0
+
+    def tearDown(self):
+        if self.tempdir:
+            shutil.rmtree(self.tempdir)
+
+    @staticmethod
+    def get_test_input(test_file_or_folder):
+        path, _ = os.path.split(__file__)
+        return os.path.join(path, "data", test_file_or_folder)
+
+    @staticmethod
+    def get_test_output(test_file_or_folder):
+        path, _ = os.path.split(__file__)
+        return os.path.join(path, "data", "test_results", test_file_or_folder)
+
+    @staticmethod
+    def get_file_list(folder, suffix, prefix=''):
+        all_files = os.listdir(folder)
+        file_list = []
+        for p in all_files:
+            if p.startswith(prefix) and p.endswith(suffix):
+                file_list.append(os.path.abspath(os.path.join(folder, p)))
+        return file_list
+
+    def temp_path(self, suffix):
+        self.temp_dir()
+        self.num_tempfiles += 1
+        return os.path.join(self.tempdir,
+                            "tmp%d%s" % (self.num_tempfiles, suffix))
+
+    def temp_dir(self):
+        if not self.tempdir:
+            self.tempdir = tempfile.mkdtemp()
+
+    def read_ttx(self, path):
+        lines = []
+        with open(path, "r", encoding="utf-8") as ttx:
+            for line in ttx.readlines():
+                # Elide ttFont attributes because ttLibVersion may change,
+                # and use os-native line separators so we can run difflib.
+                if line.startswith("<ttFont "):
+                    lines.append("<ttFont>" + os.linesep)
+                else:
+                    lines.append(line.rstrip() + os.linesep)
+        return lines
+
+    def expect_ttx(self, font, expected_ttx, tables):
+        path = self.temp_path(suffix=".ttx")
+        font.saveXML(path, tables=tables)
+        actual = self.read_ttx(path)
+        expected = self.read_ttx(expected_ttx)
+        if actual != expected:
+            for line in difflib.unified_diff(
+                    expected, actual, fromfile=expected_ttx, tofile=path):
+                sys.stdout.write(line)
+            self.fail("TTX output is different from expected")
+
+    def compile_font(self, path, suffix, temp_dir):
+        ttx_filename = os.path.basename(path)
+        savepath = os.path.join(temp_dir, ttx_filename.replace('.ttx', suffix))
+        font = TTFont(recalcBBoxes=False, recalcTimestamp=False)
+        font.importXML(path)
+        font.save(savepath, reorderTables=None)
+        return font, savepath
+
+# -----
+# Tests
+# -----
+
+    def test_varlib_mutator_ttf(self):
+        suffix = '.ttf'
+        ds_path = self.get_test_input('Build.designspace')
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily-')
+        for path in ttx_paths:
+            self.compile_font(path, suffix, self.tempdir)
+
+        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
+        varfont, _, _ = build(ds_path, finder)
+        varfont_name = 'Mutator'
+        varfont_path = os.path.join(self.tempdir, varfont_name + suffix)
+        varfont.save(varfont_path)
+
+        args = [varfont_path, 'wght=500', 'cntr=50']
+        mutator(args)
+
+        instfont_path = os.path.splitext(varfont_path)[0] + '-instance' + suffix
+        instfont = TTFont(instfont_path)
+        tables = [table_tag for table_tag in instfont.keys() if table_tag != 'head']
+        expected_ttx_path = self.get_test_output(varfont_name + '.ttx')
+        self.expect_ttx(instfont, expected_ttx_path, tables)
+
+    def test_varlib_mutator_iup_ttf(self):
+        suffix = '.ttf'
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_varfont_ttf')
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'Mutator_IUP')
+        for path in ttx_paths:
+            self.compile_font(path, suffix, self.tempdir)
+
+        varfont_name = 'Mutator_IUP'
+        varfont_path = os.path.join(self.tempdir, varfont_name + suffix)
+        
+        args = [varfont_path, 'wdth=80', 'ASCN=628']
+        mutator(args)
+
+        instfont_path = os.path.splitext(varfont_path)[0] + '-instance' + suffix
+        instfont = TTFont(instfont_path)
+        tables = [table_tag for table_tag in instfont.keys() if table_tag != 'head']
+        expected_ttx_path = self.get_test_output(varfont_name + '-instance.ttx')
+        self.expect_ttx(instfont, expected_ttx_path, tables)
+
+
+if __name__ == "__main__":
+    sys.exit(unittest.main())
diff --git a/Tests/varLib/varLib_test.py b/Tests/varLib/varLib_test.py
new file mode 100644
index 0000000..12c4874
--- /dev/null
+++ b/Tests/varLib/varLib_test.py
@@ -0,0 +1,236 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.ttLib import TTFont
+from fontTools.varLib import build
+from fontTools.varLib import main as varLib_main
+import difflib
+import os
+import shutil
+import sys
+import tempfile
+import unittest
+
+
+class BuildTest(unittest.TestCase):
+    def __init__(self, methodName):
+        unittest.TestCase.__init__(self, methodName)
+        # Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
+        # and fires deprecation warnings if a program uses the old name.
+        if not hasattr(self, "assertRaisesRegex"):
+            self.assertRaisesRegex = self.assertRaisesRegexp
+
+    def setUp(self):
+        self.tempdir = None
+        self.num_tempfiles = 0
+
+    def tearDown(self):
+        if self.tempdir:
+            shutil.rmtree(self.tempdir)
+
+    @staticmethod
+    def get_test_input(test_file_or_folder):
+        path, _ = os.path.split(__file__)
+        return os.path.join(path, "data", test_file_or_folder)
+
+    @staticmethod
+    def get_test_output(test_file_or_folder):
+        path, _ = os.path.split(__file__)
+        return os.path.join(path, "data", "test_results", test_file_or_folder)
+
+    @staticmethod
+    def get_file_list(folder, suffix, prefix=''):
+        all_files = os.listdir(folder)
+        file_list = []
+        for p in all_files:
+            if p.startswith(prefix) and p.endswith(suffix):
+                file_list.append(os.path.abspath(os.path.join(folder, p)))
+        return file_list
+
+    def temp_path(self, suffix):
+        self.temp_dir()
+        self.num_tempfiles += 1
+        return os.path.join(self.tempdir,
+                            "tmp%d%s" % (self.num_tempfiles, suffix))
+
+    def temp_dir(self):
+        if not self.tempdir:
+            self.tempdir = tempfile.mkdtemp()
+
+    def read_ttx(self, path):
+        lines = []
+        with open(path, "r", encoding="utf-8") as ttx:
+            for line in ttx.readlines():
+                # Elide ttFont attributes because ttLibVersion may change,
+                # and use os-native line separators so we can run difflib.
+                if line.startswith("<ttFont "):
+                    lines.append("<ttFont>" + os.linesep)
+                else:
+                    lines.append(line.rstrip() + os.linesep)
+        return lines
+
+    def expect_ttx(self, font, expected_ttx, tables):
+        path = self.temp_path(suffix=".ttx")
+        font.saveXML(path, tables=tables)
+        actual = self.read_ttx(path)
+        expected = self.read_ttx(expected_ttx)
+        if actual != expected:
+            for line in difflib.unified_diff(
+                    expected, actual, fromfile=expected_ttx, tofile=path):
+                sys.stdout.write(line)
+            self.fail("TTX output is different from expected")
+
+    def check_ttx_dump(self, font, expected_ttx, tables, suffix):
+        """Ensure the TTX dump is the same after saving and reloading the font."""
+        path = self.temp_path(suffix=suffix)
+        font.save(path)
+        self.expect_ttx(TTFont(path), expected_ttx, tables)
+
+    def compile_font(self, path, suffix, temp_dir):
+        ttx_filename = os.path.basename(path)
+        savepath = os.path.join(temp_dir, ttx_filename.replace('.ttx', suffix))
+        font = TTFont(recalcBBoxes=False, recalcTimestamp=False)
+        font.importXML(path)
+        font.save(savepath, reorderTables=None)
+        return font, savepath
+
+    def _run_varlib_build_test(self, designspace_name, font_name, tables,
+                               expected_ttx_name):
+        suffix = '.ttf'
+        ds_path = self.get_test_input(designspace_name + '.designspace')
+        ufo_dir = self.get_test_input('master_ufo')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', font_name + '-')
+        for path in ttx_paths:
+            self.compile_font(path, suffix, self.tempdir)
+
+        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
+        varfont, model, _ = build(ds_path, finder)
+
+        expected_ttx_path = self.get_test_output(expected_ttx_name + '.ttx')
+        self.expect_ttx(varfont, expected_ttx_path, tables)
+        self.check_ttx_dump(varfont, expected_ttx_path, tables, suffix)
+# -----
+# Tests
+# -----
+
+    def test_varlib_build_ttf(self):
+        """Designspace file contains <axes> element."""
+        self._run_varlib_build_test(
+            designspace_name='Build',
+            font_name='TestFamily',
+            tables=['GDEF', 'HVAR', 'MVAR', 'fvar', 'gvar'],
+            expected_ttx_name='Build'
+        )
+
+    def test_varlib_build_no_axes_ttf(self):
+        """Designspace file does not contain an <axes> element."""
+        self._run_varlib_build_test(
+            designspace_name='InterpolateLayout3',
+            font_name='TestFamily2',
+            tables=['GDEF', 'HVAR', 'MVAR', 'fvar', 'gvar'],
+            expected_ttx_name='Build3'
+        )
+
+    def test_varlib_avar_single_axis(self):
+        """Designspace file contains a 'weight' axis with <map> elements
+        modifying the normalization mapping. An 'avar' table is generated.
+        """
+        test_name = 'BuildAvarSingleAxis'
+        self._run_varlib_build_test(
+            designspace_name=test_name,
+            font_name='TestFamily3',
+            tables=['avar'],
+            expected_ttx_name=test_name
+        )
+
+    def test_varlib_avar_with_identity_maps(self):
+        """Designspace file contains two 'weight' and 'width' axes both with
+        <map> elements.
+
+        The 'width' axis only contains identity mappings, however the resulting
+        avar segment will not be empty but will contain the default axis value
+        maps: {-1.0: -1.0, 0.0: 0.0, 1.0: 1.0}.
+
+        This is to to work around an issue with some rasterizers:
+        https://github.com/googlei18n/fontmake/issues/295
+        https://github.com/fonttools/fonttools/issues/1011
+        """
+        test_name = 'BuildAvarIdentityMaps'
+        self._run_varlib_build_test(
+            designspace_name=test_name,
+            font_name='TestFamily3',
+            tables=['avar'],
+            expected_ttx_name=test_name
+        )
+
+    def test_varlib_avar_empty_axis(self):
+        """Designspace file contains two 'weight' and 'width' axes, but
+        only one axis ('weight') has some <map> elements.
+
+        Even if no <map> elements are defined for the 'width' axis, the
+        resulting avar segment still contains the default axis value maps:
+        {-1.0: -1.0, 0.0: 0.0, 1.0: 1.0}.
+
+        This is again to to work around an issue with some rasterizers:
+        https://github.com/googlei18n/fontmake/issues/295
+        https://github.com/fonttools/fonttools/issues/1011
+        """
+        test_name = 'BuildAvarEmptyAxis'
+        self._run_varlib_build_test(
+            designspace_name=test_name,
+            font_name='TestFamily3',
+            tables=['avar'],
+            expected_ttx_name=test_name
+        )
+
+    def test_varlib_main_ttf(self):
+        """Mostly for testing varLib.main()
+        """
+        suffix = '.ttf'
+        ds_path = self.get_test_input('Build.designspace')
+        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
+
+        self.temp_dir()
+        ttf_dir = os.path.join(self.tempdir, 'master_ttf_interpolatable')
+        os.makedirs(ttf_dir)
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily-')
+        for path in ttx_paths:
+            self.compile_font(path, suffix, ttf_dir)
+
+        ds_copy = os.path.join(self.tempdir, 'BuildMain.designspace')
+        shutil.copy2(ds_path, ds_copy)
+
+        # by default, varLib.main finds master TTFs inside a
+        # 'master_ttf_interpolatable' subfolder in current working dir
+        cwd = os.getcwd()
+        os.chdir(self.tempdir)
+        try:
+            varLib_main([ds_copy])
+        finally:
+            os.chdir(cwd)
+
+        varfont_path = os.path.splitext(ds_copy)[0] + '-VF' + suffix
+        self.assertTrue(os.path.exists(varfont_path))
+
+        # try again passing an explicit --master-finder
+        os.remove(varfont_path)
+        finder = "%s/master_ttf_interpolatable/{stem}.ttf" % self.tempdir
+        varLib_main([ds_copy, "--master-finder", finder])
+        self.assertTrue(os.path.exists(varfont_path))
+
+        # and also with explicit -o output option
+        os.remove(varfont_path)
+        varfont_path = os.path.splitext(varfont_path)[0] + "-o" + suffix
+        varLib_main([ds_copy, "-o", varfont_path, "--master-finder", finder])
+        self.assertTrue(os.path.exists(varfont_path))
+
+        varfont = TTFont(varfont_path)
+        tables = [table_tag for table_tag in varfont.keys() if table_tag != 'head']
+        expected_ttx_path = self.get_test_output('BuildMain.ttx')
+        self.expect_ttx(varfont, expected_ttx_path, tables)
+
+
+if __name__ == "__main__":
+    sys.exit(unittest.main())
diff --git a/Tests/voltLib/lexer_test.py b/Tests/voltLib/lexer_test.py
new file mode 100644
index 0000000..6041cb8
--- /dev/null
+++ b/Tests/voltLib/lexer_test.py
@@ -0,0 +1,35 @@
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+from fontTools.voltLib.error import VoltLibError
+from fontTools.voltLib.lexer import Lexer
+import unittest
+
+
+def lex(s):
+    return [(typ, tok) for (typ, tok, _) in Lexer(s, "test.vtp")]
+
+
+class LexerTest(unittest.TestCase):
+    def __init__(self, methodName):
+        unittest.TestCase.__init__(self, methodName)
+
+    def test_empty(self):
+        self.assertEqual(lex(""), [])
+        self.assertEqual(lex("\t"), [])
+
+    def test_string(self):
+        self.assertEqual(lex('"foo" "bar"'),
+                         [(Lexer.STRING, "foo"), (Lexer.STRING, "bar")])
+        self.assertRaises(VoltLibError, lambda: lex('"foo\n bar"'))
+
+    def test_name(self):
+        self.assertEqual(lex('DEF_FOO bar.alt1'),
+                         [(Lexer.NAME, "DEF_FOO"), (Lexer.NAME, "bar.alt1")])
+
+    def test_number(self):
+        self.assertEqual(lex("123 -456"),
+                         [(Lexer.NUMBER, 123), (Lexer.NUMBER, -456)])
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/voltLib/parser_test.py b/Tests/voltLib/parser_test.py
new file mode 100644
index 0000000..6baf900
--- /dev/null
+++ b/Tests/voltLib/parser_test.py
@@ -0,0 +1,1034 @@
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+from fontTools.voltLib.error import VoltLibError
+from fontTools.voltLib.parser import Parser
+from io import open
+import os
+import shutil
+import tempfile
+import unittest
+
+
+class ParserTest(unittest.TestCase):
+    def __init__(self, methodName):
+        unittest.TestCase.__init__(self, methodName)
+        # Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
+        # and fires deprecation warnings if a program uses the old name.
+        if not hasattr(self, "assertRaisesRegex"):
+            self.assertRaisesRegex = self.assertRaisesRegexp
+
+    def test_def_glyph_base(self):
+        [def_glyph] = self.parse(
+            'DEF_GLYPH ".notdef" ID 0 TYPE BASE END_GLYPH'
+        ).statements
+        self.assertEqual((def_glyph.name, def_glyph.id, def_glyph.unicode,
+                          def_glyph.type, def_glyph.components),
+                         (".notdef", 0, None, "BASE", None))
+
+    def test_def_glyph_base_with_unicode(self):
+        [def_glyph] = self.parse(
+            'DEF_GLYPH "space" ID 3 UNICODE 32 TYPE BASE END_GLYPH'
+        ).statements
+        self.assertEqual((def_glyph.name, def_glyph.id, def_glyph.unicode,
+                          def_glyph.type, def_glyph.components),
+                         ("space", 3, [0x0020], "BASE", None))
+
+    def test_def_glyph_base_with_unicodevalues(self):
+        [def_glyph] = self.parse(
+            'DEF_GLYPH "CR" ID 2 UNICODEVALUES "U+0009" '
+            'TYPE BASE END_GLYPH'
+        ).statements
+        self.assertEqual((def_glyph.name, def_glyph.id, def_glyph.unicode,
+                          def_glyph.type, def_glyph.components),
+                         ("CR", 2, [0x0009], "BASE", None))
+
+    def test_def_glyph_base_with_mult_unicodevalues(self):
+        [def_glyph] = self.parse(
+            'DEF_GLYPH "CR" ID 2 UNICODEVALUES "U+0009,U+000D" '
+            'TYPE BASE END_GLYPH'
+        ).statements
+        self.assertEqual((def_glyph.name, def_glyph.id, def_glyph.unicode,
+                          def_glyph.type, def_glyph.components),
+                         ("CR", 2, [0x0009, 0x000D], "BASE", None))
+
+    def test_def_glyph_base_with_empty_unicodevalues(self):
+        [def_glyph] = self.parse(
+            'DEF_GLYPH "i.locl" ID 269 UNICODEVALUES "" '
+            'TYPE BASE END_GLYPH'
+        ).statements
+        self.assertEqual((def_glyph.name, def_glyph.id, def_glyph.unicode,
+                          def_glyph.type, def_glyph.components),
+                         ("i.locl", 269, None, "BASE", None))
+
+    def test_def_glyph_base_2_components(self):
+        [def_glyph] = self.parse(
+            'DEF_GLYPH "glyphBase" ID 320 TYPE BASE COMPONENTS 2 END_GLYPH'
+        ).statements
+        self.assertEqual((def_glyph.name, def_glyph.id, def_glyph.unicode,
+                          def_glyph.type, def_glyph.components),
+                         ("glyphBase", 320, None, "BASE", 2))
+
+    def test_def_glyph_ligature_2_components(self):
+        [def_glyph] = self.parse(
+            'DEF_GLYPH "f_f" ID 320 TYPE LIGATURE COMPONENTS 2 END_GLYPH'
+        ).statements
+        self.assertEqual((def_glyph.name, def_glyph.id, def_glyph.unicode,
+                          def_glyph.type, def_glyph.components),
+                         ("f_f", 320, None, "LIGATURE", 2))
+
+    def test_def_glyph_no_type(self):
+        [def_glyph] = self.parse(
+            'DEF_GLYPH "glyph20" ID 20 END_GLYPH'
+        ).statements
+        self.assertEqual((def_glyph.name, def_glyph.id, def_glyph.unicode,
+                          def_glyph.type, def_glyph.components),
+                         ("glyph20", 20, None, None, None))
+
+    def test_def_glyph_case_sensitive(self):
+        def_glyphs = self.parse(
+            'DEF_GLYPH "A" ID 3 UNICODE 65 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "a" ID 4 UNICODE 97 TYPE BASE END_GLYPH\n'
+        ).statements
+        self.assertEqual((def_glyphs[0].name, def_glyphs[0].id,
+                          def_glyphs[0].unicode, def_glyphs[0].type,
+                          def_glyphs[0].components),
+                         ("A", 3, [0x41], "BASE", None))
+        self.assertEqual((def_glyphs[1].name, def_glyphs[1].id,
+                          def_glyphs[1].unicode, def_glyphs[1].type,
+                          def_glyphs[1].components),
+                         ("a", 4, [0x61], "BASE", None))
+
+    def test_def_group_glyphs(self):
+        [def_group] = self.parse(
+            'DEF_GROUP "aaccented"\n'
+            'ENUM GLYPH "aacute" GLYPH "abreve" GLYPH "acircumflex" '
+            'GLYPH "adieresis" GLYPH "ae" GLYPH "agrave" GLYPH "amacron" '
+            'GLYPH "aogonek" GLYPH "aring" GLYPH "atilde" END_ENUM\n'
+            'END_GROUP\n'
+        ).statements
+        self.assertEqual((def_group.name, def_group.enum),
+                         ("aaccented",
+                          ("aacute", "abreve", "acircumflex", "adieresis",
+                           "ae", "agrave", "amacron", "aogonek", "aring",
+                           "atilde")))
+
+    def test_def_group_groups(self):
+        [group1, group2, test_group] = self.parse(
+            'DEF_GROUP "Group1"\n'
+            'ENUM GLYPH "a" GLYPH "b" GLYPH "c" GLYPH "d" END_ENUM\n'
+            'END_GROUP\n'
+            'DEF_GROUP "Group2"\n'
+            'ENUM GLYPH "e" GLYPH "f" GLYPH "g" GLYPH "h" END_ENUM\n'
+            'END_GROUP\n'
+            'DEF_GROUP "TestGroup"\n'
+            'ENUM GROUP "Group1" GROUP "Group2" END_ENUM\n'
+            'END_GROUP\n'
+        ).statements
+        self.assertEqual(
+            (test_group.name, test_group.enum),
+            ("TestGroup",
+             (("Group1",), ("Group2",))))
+
+    def test_def_group_groups_not_yet_defined(self):
+        [group1, test_group1, test_group2, test_group3, group2] = self.parse(
+            'DEF_GROUP "Group1"\n'
+            'ENUM GLYPH "a" GLYPH "b" GLYPH "c" GLYPH "d" END_ENUM\n'
+            'END_GROUP\n'
+            'DEF_GROUP "TestGroup1"\n'
+            'ENUM GROUP "Group1" GROUP "Group2" END_ENUM\n'
+            'END_GROUP\n'
+            'DEF_GROUP "TestGroup2"\n'
+            'ENUM GROUP "Group2" END_ENUM\n'
+            'END_GROUP\n'
+            'DEF_GROUP "TestGroup3"\n'
+            'ENUM GROUP "Group2" GROUP "Group1" END_ENUM\n'
+            'END_GROUP\n'
+            'DEF_GROUP "Group2"\n'
+            'ENUM GLYPH "e" GLYPH "f" GLYPH "g" GLYPH "h" END_ENUM\n'
+            'END_GROUP\n'
+        ).statements
+        self.assertEqual(
+            (test_group1.name, test_group1.enum),
+            ("TestGroup1",
+             (("Group1", ), ("Group2", ))))
+        self.assertEqual(
+            (test_group2.name, test_group2.enum),
+            ("TestGroup2",
+             (("Group2", ), )))
+        self.assertEqual(
+            (test_group3.name, test_group3.enum),
+            ("TestGroup3",
+             (("Group2", ), ("Group1", ))))
+
+    # def test_def_group_groups_undefined(self):
+    #     with self.assertRaisesRegex(
+    #             VoltLibError,
+    #             r'Group "Group2" is used but undefined.'):
+    #         [group1, test_group, group2] = self.parse(
+    #             'DEF_GROUP "Group1"\n'
+    #             'ENUM GLYPH "a" GLYPH "b" GLYPH "c" GLYPH "d" END_ENUM\n'
+    #             'END_GROUP\n'
+    #             'DEF_GROUP "TestGroup"\n'
+    #             'ENUM GROUP "Group1" GROUP "Group2" END_ENUM\n'
+    #             'END_GROUP\n'
+    #         ).statements
+
+    def test_def_group_glyphs_and_group(self):
+        [def_group1, def_group2] = self.parse(
+            'DEF_GROUP "aaccented"\n'
+            'ENUM GLYPH "aacute" GLYPH "abreve" GLYPH "acircumflex" '
+            'GLYPH "adieresis" GLYPH "ae" GLYPH "agrave" GLYPH "amacron" '
+            'GLYPH "aogonek" GLYPH "aring" GLYPH "atilde" END_ENUM\n'
+            'END_GROUP\n'
+            'DEF_GROUP "KERN_lc_a_2ND"\n'
+            'ENUM GLYPH "a" GROUP "aaccented" END_ENUM\n'
+            'END_GROUP'
+        ).statements
+        self.assertEqual((def_group2.name, def_group2.enum),
+                         ("KERN_lc_a_2ND",
+                          ("a", ("aaccented", ))))
+
+    def test_def_group_range(self):
+        [def_group] = self.parse(
+            'DEF_GROUP "KERN_lc_a_2ND"\n'
+            'ENUM RANGE "a" TO "atilde" GLYPH "b" RANGE "c" TO "cdotaccent" '
+            'END_ENUM\n'
+            'END_GROUP'
+        ).statements
+        self.assertEqual((def_group.name, def_group.enum),
+                         ("KERN_lc_a_2ND",
+                          (("a", "atilde"), "b", ("c", "cdotaccent"))))
+
+    def test_group_duplicate(self):
+        self.assertRaisesRegex(
+            VoltLibError,
+            'Glyph group "dupe" already defined, '
+            'group names are case insensitive',
+            self.parse, 'DEF_GROUP "dupe"\n'
+                        'ENUM GLYPH "a" GLYPH "b" END_ENUM\n'
+                        'END_GROUP\n'
+                        'DEF_GROUP "dupe"\n'
+                        'ENUM GLYPH "x" END_ENUM\n'
+                        'END_GROUP\n'
+        )
+
+    def test_group_duplicate_case_insensitive(self):
+        self.assertRaisesRegex(
+            VoltLibError,
+            'Glyph group "Dupe" already defined, '
+            'group names are case insensitive',
+            self.parse, 'DEF_GROUP "dupe"\n'
+                        'ENUM GLYPH "a" GLYPH "b" END_ENUM\n'
+                        'END_GROUP\n'
+                        'DEF_GROUP "Dupe"\n'
+                        'ENUM GLYPH "x" END_ENUM\n'
+                        'END_GROUP\n'
+        )
+
+    def test_script_without_langsys(self):
+        [script] = self.parse(
+            'DEF_SCRIPT NAME "Latin" TAG "latn"\n'
+            'END_SCRIPT'
+        ).statements
+        self.assertEqual((script.name, script.tag, script.langs),
+                         ("Latin", "latn", []))
+
+    def test_langsys_normal(self):
+        [def_script] = self.parse(
+            'DEF_SCRIPT NAME "Latin" TAG "latn"\n'
+            'DEF_LANGSYS NAME "Romanian" TAG "ROM "\n'
+            'END_LANGSYS\n'
+            'DEF_LANGSYS NAME "Moldavian" TAG "MOL "\n'
+            'END_LANGSYS\n'
+            'END_SCRIPT'
+        ).statements
+        self.assertEqual((def_script.name, def_script.tag),
+                         ("Latin",
+                          "latn"))
+        def_lang = def_script.langs[0]
+        self.assertEqual((def_lang.name, def_lang.tag),
+                         ("Romanian",
+                          "ROM "))
+        def_lang = def_script.langs[1]
+        self.assertEqual((def_lang.name, def_lang.tag),
+                         ("Moldavian",
+                          "MOL "))
+
+    def test_langsys_no_script_name(self):
+        [langsys] = self.parse(
+            'DEF_SCRIPT TAG "latn"\n'
+            'DEF_LANGSYS NAME "Default" TAG "dflt"\n'
+            'END_LANGSYS\n'
+            'END_SCRIPT'
+        ).statements
+        self.assertEqual((langsys.name, langsys.tag),
+                         (None,
+                          "latn"))
+        lang = langsys.langs[0]
+        self.assertEqual((lang.name, lang.tag),
+                         ("Default",
+                          "dflt"))
+
+    def test_langsys_no_script_tag_fails(self):
+        with self.assertRaisesRegex(
+                VoltLibError,
+                r'.*Expected "TAG"'):
+            [langsys] = self.parse(
+                'DEF_SCRIPT NAME "Latin"\n'
+                'DEF_LANGSYS NAME "Default" TAG "dflt"\n'
+                'END_LANGSYS\n'
+                'END_SCRIPT'
+            ).statements
+
+    def test_langsys_duplicate_script(self):
+        with self.assertRaisesRegex(
+                VoltLibError,
+                'Script "DFLT" already defined, '
+                'script tags are case insensitive'):
+            [langsys1, langsys2] = self.parse(
+                'DEF_SCRIPT NAME "Default" TAG "DFLT"\n'
+                'DEF_LANGSYS NAME "Default" TAG "dflt"\n'
+                'END_LANGSYS\n'
+                'END_SCRIPT\n'
+                'DEF_SCRIPT TAG "DFLT"\n'
+                'DEF_LANGSYS NAME "Default" TAG "dflt"\n'
+                'END_LANGSYS\n'
+                'END_SCRIPT'
+            ).statements
+
+    def test_langsys_duplicate_lang(self):
+        with self.assertRaisesRegex(
+                VoltLibError,
+                'Language "dflt" already defined in script "DFLT", '
+                'language tags are case insensitive'):
+            [langsys] = self.parse(
+                'DEF_SCRIPT NAME "Default" TAG "DFLT"\n'
+                'DEF_LANGSYS NAME "Default" TAG "dflt"\n'
+                'END_LANGSYS\n'
+                'DEF_LANGSYS NAME "Default" TAG "dflt"\n'
+                'END_LANGSYS\n'
+                'END_SCRIPT\n'
+            ).statements
+
+    def test_langsys_lang_in_separate_scripts(self):
+        [langsys1, langsys2] = self.parse(
+            'DEF_SCRIPT NAME "Default" TAG "DFLT"\n'
+            'DEF_LANGSYS NAME "Default" TAG "dflt"\n'
+            'END_LANGSYS\n'
+            'DEF_LANGSYS NAME "Default" TAG "ROM "\n'
+            'END_LANGSYS\n'
+            'END_SCRIPT\n'
+            'DEF_SCRIPT NAME "Latin" TAG "latn"\n'
+            'DEF_LANGSYS NAME "Default" TAG "dflt"\n'
+            'END_LANGSYS\n'
+            'DEF_LANGSYS NAME "Default" TAG "ROM "\n'
+            'END_LANGSYS\n'
+            'END_SCRIPT'
+        ).statements
+        self.assertEqual((langsys1.langs[0].tag, langsys1.langs[1].tag),
+                         ("dflt", "ROM "))
+        self.assertEqual((langsys2.langs[0].tag, langsys2.langs[1].tag),
+                         ("dflt", "ROM "))
+
+    def test_langsys_no_lang_name(self):
+        [langsys] = self.parse(
+            'DEF_SCRIPT NAME "Latin" TAG "latn"\n'
+            'DEF_LANGSYS TAG "dflt"\n'
+            'END_LANGSYS\n'
+            'END_SCRIPT'
+        ).statements
+        self.assertEqual((langsys.name, langsys.tag),
+                         ("Latin",
+                          "latn"))
+        lang = langsys.langs[0]
+        self.assertEqual((lang.name, lang.tag),
+                         (None,
+                          "dflt"))
+
+    def test_langsys_no_langsys_tag_fails(self):
+        with self.assertRaisesRegex(
+                VoltLibError,
+                r'.*Expected "TAG"'):
+            [langsys] = self.parse(
+                'DEF_SCRIPT NAME "Latin" TAG "latn"\n'
+                'DEF_LANGSYS NAME "Default"\n'
+                'END_LANGSYS\n'
+                'END_SCRIPT'
+            ).statements
+
+    def test_feature(self):
+        [def_script] = self.parse(
+            'DEF_SCRIPT NAME "Latin" TAG "latn"\n'
+            'DEF_LANGSYS NAME "Romanian" TAG "ROM "\n'
+            'DEF_FEATURE NAME "Fractions" TAG "frac"\n'
+            'LOOKUP "fraclookup"\n'
+            'END_FEATURE\n'
+            'END_LANGSYS\n'
+            'END_SCRIPT'
+        ).statements
+        def_feature = def_script.langs[0].features[0]
+        self.assertEqual((def_feature.name, def_feature.tag,
+                          def_feature.lookups),
+                         ("Fractions",
+                          "frac",
+                          ["fraclookup"]))
+        [def_script] = self.parse(
+            'DEF_SCRIPT NAME "Latin" TAG "latn"\n'
+            'DEF_LANGSYS NAME "Romanian" TAG "ROM "\n'
+            'DEF_FEATURE NAME "Kerning" TAG "kern"\n'
+            'LOOKUP "kern1" LOOKUP "kern2"\n'
+            'END_FEATURE\n'
+            'END_LANGSYS\n'
+            'END_SCRIPT'
+        ).statements
+        def_feature = def_script.langs[0].features[0]
+        self.assertEqual((def_feature.name, def_feature.tag,
+                          def_feature.lookups),
+                         ("Kerning",
+                          "kern",
+                          ["kern1", "kern2"]))
+
+    def test_lookup_duplicate(self):
+        with self.assertRaisesRegex(
+            VoltLibError,
+            'Lookup "dupe" already defined, '
+            'lookup names are case insensitive',
+        ):
+            [lookup1, lookup2] = self.parse(
+                'DEF_LOOKUP "dupe"\n'
+                'AS_SUBSTITUTION\n'
+                'SUB GLYPH "a"\n'
+                'WITH GLYPH "a.alt"\n'
+                'END_SUB\n'
+                'END_SUBSTITUTION\n'
+                'DEF_LOOKUP "dupe"\n'
+                'AS_SUBSTITUTION\n'
+                'SUB GLYPH "b"\n'
+                'WITH GLYPH "b.alt"\n'
+                'END_SUB\n'
+                'END_SUBSTITUTION\n'
+            ).statements
+
+    def test_lookup_duplicate_insensitive_case(self):
+        with self.assertRaisesRegex(
+            VoltLibError,
+            'Lookup "Dupe" already defined, '
+            'lookup names are case insensitive',
+        ):
+            [lookup1, lookup2] = self.parse(
+                'DEF_LOOKUP "dupe"\n'
+                'AS_SUBSTITUTION\n'
+                'SUB GLYPH "a"\n'
+                'WITH GLYPH "a.alt"\n'
+                'END_SUB\n'
+                'END_SUBSTITUTION\n'
+                'DEF_LOOKUP "Dupe"\n'
+                'AS_SUBSTITUTION\n'
+                'SUB GLYPH "b"\n'
+                'WITH GLYPH "b.alt"\n'
+                'END_SUB\n'
+                'END_SUBSTITUTION\n'
+            ).statements
+
+    def test_lookup_name_starts_with_letter(self):
+        with self.assertRaisesRegex(
+            VoltLibError,
+            'Lookup name "\\\lookupname" must start with a letter'
+        ):
+            [lookup] = self.parse(
+                'DEF_LOOKUP "\lookupname"\n'
+                'AS_SUBSTITUTION\n'
+                'SUB GLYPH "a"\n'
+                'WITH GLYPH "a.alt"\n'
+                'END_SUB\n'
+                'END_SUBSTITUTION\n'
+            ).statements
+
+    def test_substitution_empty(self):
+        with self.assertRaisesRegex(
+                VoltLibError,
+                r'Expected SUB'):
+            [lookup] = self.parse(
+                'DEF_LOOKUP "empty_substitution" PROCESS_BASE PROCESS_MARKS '
+                'ALL DIRECTION LTR\n'
+                'IN_CONTEXT\n'
+                'END_CONTEXT\n'
+                'AS_SUBSTITUTION\n'
+                'END_SUBSTITUTION'
+            ).statements
+
+    def test_substitution_invalid_many_to_many(self):
+        with self.assertRaisesRegex(
+                VoltLibError,
+                r'Invalid substitution type'):
+            [lookup] = self.parse(
+                'DEF_LOOKUP "invalid_substitution" PROCESS_BASE PROCESS_MARKS '
+                'ALL DIRECTION LTR\n'
+                'IN_CONTEXT\n'
+                'END_CONTEXT\n'
+                'AS_SUBSTITUTION\n'
+                'SUB GLYPH "f" GLYPH "i"\n'
+                'WITH GLYPH "f.alt" GLYPH "i.alt"\n'
+                'END_SUB\n'
+                'END_SUBSTITUTION'
+            ).statements
+
+    def test_substitution_invalid_reverse_chaining_single(self):
+        with self.assertRaisesRegex(
+                VoltLibError,
+                r'Invalid substitution type'):
+            [lookup] = self.parse(
+                'DEF_LOOKUP "invalid_substitution" PROCESS_BASE PROCESS_MARKS '
+                'ALL DIRECTION LTR REVERSAL\n'
+                'IN_CONTEXT\n'
+                'END_CONTEXT\n'
+                'AS_SUBSTITUTION\n'
+                'SUB GLYPH "f" GLYPH "i"\n'
+                'WITH GLYPH "f_i"\n'
+                'END_SUB\n'
+                'END_SUBSTITUTION'
+            ).statements
+
+    def test_substitution_invalid_mixed(self):
+        with self.assertRaisesRegex(
+                VoltLibError,
+                r'Invalid substitution type'):
+            [lookup] = self.parse(
+                'DEF_LOOKUP "invalid_substitution" PROCESS_BASE PROCESS_MARKS '
+                'ALL DIRECTION LTR\n'
+                'IN_CONTEXT\n'
+                'END_CONTEXT\n'
+                'AS_SUBSTITUTION\n'
+                'SUB GLYPH "fi"\n'
+                'WITH GLYPH "f" GLYPH "i"\n'
+                'END_SUB\n'
+                'SUB GLYPH "f" GLYPH "l"\n'
+                'WITH GLYPH "f_l"\n'
+                'END_SUB\n'
+                'END_SUBSTITUTION'
+            ).statements
+
+    def test_substitution_single(self):
+        [lookup] = self.parse(
+            'DEF_LOOKUP "smcp" PROCESS_BASE PROCESS_MARKS ALL '
+            'DIRECTION LTR\n'
+            'IN_CONTEXT\n'
+            'END_CONTEXT\n'
+            'AS_SUBSTITUTION\n'
+            'SUB GLYPH "a"\n'
+            'WITH GLYPH "a.sc"\n'
+            'END_SUB\n'
+            'SUB GLYPH "b"\n'
+            'WITH GLYPH "b.sc"\n'
+            'END_SUB\n'
+            'END_SUBSTITUTION'
+        ).statements
+        self.assertEqual((lookup.name, list(lookup.sub.mapping.items())),
+                         ("smcp", [(("a",), ("a.sc",)), (("b",), ("b.sc",))]))
+
+    def test_substitution_single_in_context(self):
+        [group, lookup] = self.parse(
+            'DEF_GROUP "Denominators" ENUM GLYPH "one.dnom" GLYPH "two.dnom" '
+            'END_ENUM END_GROUP\n'
+            'DEF_LOOKUP "fracdnom" PROCESS_BASE PROCESS_MARKS ALL '
+            'DIRECTION LTR\n'
+            'IN_CONTEXT LEFT ENUM GROUP "Denominators" GLYPH "fraction" '
+            'END_ENUM\n'
+            'END_CONTEXT\n'
+            'AS_SUBSTITUTION\n'
+            'SUB GLYPH "one"\n'
+            'WITH GLYPH "one.dnom"\n'
+            'END_SUB\n'
+            'SUB GLYPH "two"\n'
+            'WITH GLYPH "two.dnom"\n'
+            'END_SUB\n'
+            'END_SUBSTITUTION'
+        ).statements
+        context = lookup.context[0]
+        self.assertEqual(
+            (lookup.name, list(lookup.sub.mapping.items()),
+             context.ex_or_in, context.left, context.right),
+            ("fracdnom", [(("one",), ("one.dnom",)), (("two",), ("two.dnom",))],
+             "IN_CONTEXT", [((("Denominators",), "fraction"),)], [])
+        )
+
+    def test_substitution_single_in_contexts(self):
+        [group, lookup] = self.parse(
+            'DEF_GROUP "Hebrew" ENUM GLYPH "uni05D0" GLYPH "uni05D1" '
+            'END_ENUM END_GROUP\n'
+            'DEF_LOOKUP "HebrewCurrency" PROCESS_BASE PROCESS_MARKS ALL '
+            'DIRECTION LTR\n'
+            'IN_CONTEXT\n'
+            'RIGHT GROUP "Hebrew"\n'
+            'RIGHT GLYPH "one.Hebr"\n'
+            'END_CONTEXT\n'
+            'IN_CONTEXT\n'
+            'LEFT GROUP "Hebrew"\n'
+            'LEFT GLYPH "one.Hebr"\n'
+            'END_CONTEXT\n'
+            'AS_SUBSTITUTION\n'
+            'SUB GLYPH "dollar"\n'
+            'WITH GLYPH "dollar.Hebr"\n'
+            'END_SUB\n'
+            'END_SUBSTITUTION'
+        ).statements
+        context1 = lookup.context[0]
+        context2 = lookup.context[1]
+        self.assertEqual(
+            (lookup.name, context1.ex_or_in, context1.left,
+             context1.right, context2.ex_or_in,
+             context2.left, context2.right),
+            ("HebrewCurrency", "IN_CONTEXT", [],
+             [(("Hebrew",),), ("one.Hebr",)], "IN_CONTEXT",
+             [(("Hebrew",),), ("one.Hebr",)], []))
+
+    def test_substitution_skip_base(self):
+        [group, lookup] = self.parse(
+            'DEF_GROUP "SomeMarks" ENUM GLYPH "marka" GLYPH "markb" '
+            'END_ENUM END_GROUP\n'
+            'DEF_LOOKUP "SomeSub" SKIP_BASE PROCESS_MARKS ALL '
+            'DIRECTION LTR\n'
+            'IN_CONTEXT\n'
+            'END_CONTEXT\n'
+            'AS_SUBSTITUTION\n'
+            'SUB GLYPH "A"\n'
+            'WITH GLYPH "A.c2sc"\n'
+            'END_SUB\n'
+            'END_SUBSTITUTION'
+        ).statements
+        process_base = lookup.process_base
+        self.assertEqual(
+            (lookup.name, process_base),
+            ("SomeSub", False))
+
+    def test_substitution_process_base(self):
+        [group, lookup] = self.parse(
+            'DEF_GROUP "SomeMarks" ENUM GLYPH "marka" GLYPH "markb" '
+            'END_ENUM END_GROUP\n'
+            'DEF_LOOKUP "SomeSub" PROCESS_BASE PROCESS_MARKS ALL '
+            'DIRECTION LTR\n'
+            'IN_CONTEXT\n'
+            'END_CONTEXT\n'
+            'AS_SUBSTITUTION\n'
+            'SUB GLYPH "A"\n'
+            'WITH GLYPH "A.c2sc"\n'
+            'END_SUB\n'
+            'END_SUBSTITUTION'
+        ).statements
+        process_base = lookup.process_base
+        self.assertEqual(
+            (lookup.name, process_base),
+            ("SomeSub", True))
+
+    def test_substitution_skip_marks(self):
+        [group, lookup] = self.parse(
+            'DEF_GROUP "SomeMarks" ENUM GLYPH "marka" GLYPH "markb" '
+            'END_ENUM END_GROUP\n'
+            'DEF_LOOKUP "SomeSub" PROCESS_BASE SKIP_MARKS '
+            'DIRECTION LTR\n'
+            'IN_CONTEXT\n'
+            'END_CONTEXT\n'
+            'AS_SUBSTITUTION\n'
+            'SUB GLYPH "A"\n'
+            'WITH GLYPH "A.c2sc"\n'
+            'END_SUB\n'
+            'END_SUBSTITUTION'
+        ).statements
+        process_marks = lookup.process_marks
+        self.assertEqual(
+            (lookup.name, process_marks),
+            ("SomeSub", False))
+
+    def test_substitution_process_marks(self):
+        [group, lookup] = self.parse(
+            'DEF_GROUP "SomeMarks" ENUM GLYPH "acutecmb" GLYPH "gravecmb" '
+            'END_ENUM END_GROUP\n'
+            'DEF_LOOKUP "SomeSub" PROCESS_BASE '
+            'PROCESS_MARKS "SomeMarks" \n'
+            'DIRECTION RTL\n'
+            'AS_SUBSTITUTION\n'
+            'SUB GLYPH "A"\n'
+            'WITH GLYPH "A.c2sc"\n'
+            'END_SUB\n'
+            'END_SUBSTITUTION'
+        ).statements
+        process_marks = lookup.process_marks
+        self.assertEqual(
+            (lookup.name, process_marks),
+            ("SomeSub", "SomeMarks"))
+
+    def test_substitution_process_all_marks(self):
+        [group, lookup] = self.parse(
+            'DEF_GROUP "SomeMarks" ENUM GLYPH "acutecmb" GLYPH "gravecmb" '
+            'END_ENUM END_GROUP\n'
+            'DEF_LOOKUP "SomeSub" PROCESS_BASE '
+            'PROCESS_MARKS ALL \n'
+            'DIRECTION RTL\n'
+            'AS_SUBSTITUTION\n'
+            'SUB GLYPH "A"\n'
+            'WITH GLYPH "A.c2sc"\n'
+            'END_SUB\n'
+            'END_SUBSTITUTION'
+        ).statements
+        process_marks = lookup.process_marks
+        self.assertEqual(
+            (lookup.name, process_marks),
+            ("SomeSub", True))
+
+    def test_substitution_no_reversal(self):
+        # TODO: check right context with no reversal
+        [lookup] = self.parse(
+            'DEF_LOOKUP "Lookup" PROCESS_BASE PROCESS_MARKS ALL '
+            'DIRECTION LTR\n'
+            'IN_CONTEXT\n'
+            'RIGHT ENUM GLYPH "a" GLYPH "b" END_ENUM\n'
+            'END_CONTEXT\n'
+            'AS_SUBSTITUTION\n'
+            'SUB GLYPH "a"\n'
+            'WITH GLYPH "a.alt"\n'
+            'END_SUB\n'
+            'END_SUBSTITUTION'
+        ).statements
+        self.assertEqual(
+            (lookup.name, lookup.reversal),
+            ("Lookup", None)
+        )
+
+    def test_substitution_reversal(self):
+        [lookup] = self.parse(
+            'DEF_LOOKUP "RevLookup" PROCESS_BASE PROCESS_MARKS ALL '
+            'DIRECTION LTR REVERSAL\n'
+            'IN_CONTEXT\n'
+            'RIGHT ENUM GLYPH "a" GLYPH "b" END_ENUM\n'
+            'END_CONTEXT\n'
+            'AS_SUBSTITUTION\n'
+            'SUB GROUP "DFLT_Num_standardFigures"\n'
+            'WITH GROUP "DFLT_Num_numerators"\n'
+            'END_SUB\n'
+            'END_SUBSTITUTION'
+        ).statements
+        self.assertEqual(
+            (lookup.name, lookup.reversal),
+            ("RevLookup", True)
+        )
+
+    def test_substitution_single_to_multiple(self):
+        [lookup] = self.parse(
+            'DEF_LOOKUP "ccmp" PROCESS_BASE PROCESS_MARKS ALL '
+            'DIRECTION LTR\n'
+            'IN_CONTEXT\n'
+            'END_CONTEXT\n'
+            'AS_SUBSTITUTION\n'
+            'SUB GLYPH "aacute"\n'
+            'WITH GLYPH "a" GLYPH "acutecomb"\n'
+            'END_SUB\n'
+            'SUB GLYPH "agrave"\n'
+            'WITH GLYPH "a" GLYPH "gravecomb"\n'
+            'END_SUB\n'
+            'END_SUBSTITUTION'
+        ).statements
+        self.assertEqual((lookup.name, list(lookup.sub.mapping.items())),
+                         ("ccmp",
+                          [(("aacute",), ("a", "acutecomb")),
+                           (("agrave",), ("a", "gravecomb"))]
+                          ))
+
+    def test_substitution_multiple_to_single(self):
+        [lookup] = self.parse(
+            'DEF_LOOKUP "liga" PROCESS_BASE PROCESS_MARKS ALL '
+            'DIRECTION LTR\n'
+            'IN_CONTEXT\n'
+            'END_CONTEXT\n'
+            'AS_SUBSTITUTION\n'
+            'SUB GLYPH "f" GLYPH "i"\n'
+            'WITH GLYPH "f_i"\n'
+            'END_SUB\n'
+            'SUB GLYPH "f" GLYPH "t"\n'
+            'WITH GLYPH "f_t"\n'
+            'END_SUB\n'
+            'END_SUBSTITUTION'
+        ).statements
+        self.assertEqual((lookup.name, list(lookup.sub.mapping.items())),
+                         ("liga",
+                          [(("f", "i"), ("f_i",)),
+                           (("f", "t"), ("f_t",))]))
+
+    def test_substitution_reverse_chaining_single(self):
+        [lookup] = self.parse(
+            'DEF_LOOKUP "numr" PROCESS_BASE PROCESS_MARKS ALL '
+            'DIRECTION LTR REVERSAL\n'
+            'IN_CONTEXT\n'
+            'RIGHT ENUM '
+            'GLYPH "fraction" '
+            'RANGE "zero.numr" TO "nine.numr" '
+            'END_ENUM\n'
+            'END_CONTEXT\n'
+            'AS_SUBSTITUTION\n'
+            'SUB RANGE "zero" TO "nine"\n'
+            'WITH RANGE "zero.numr" TO "nine.numr"\n'
+            'END_SUB\n'
+            'END_SUBSTITUTION'
+        ).statements
+        self.assertEqual(
+            (lookup.name, lookup.context[0].right,
+             list(lookup.sub.mapping.items())),
+            ("numr", [(("fraction", ("zero.numr", "nine.numr")),)],
+             [((("zero", "nine"),), (("zero.numr", "nine.numr"),))]))
+
+    # GPOS
+    #  ATTACH_CURSIVE
+    #  ATTACH
+    #  ADJUST_PAIR
+    #  ADJUST_SINGLE
+    def test_position_empty(self):
+        with self.assertRaisesRegex(
+                VoltLibError,
+                'Expected ATTACH, ATTACH_CURSIVE, ADJUST_PAIR, ADJUST_SINGLE'):
+            [lookup] = self.parse(
+                'DEF_LOOKUP "empty_position" PROCESS_BASE PROCESS_MARKS ALL '
+                'DIRECTION LTR\n'
+                'EXCEPT_CONTEXT\n'
+                'LEFT GLYPH "glyph"\n'
+                'END_CONTEXT\n'
+                'AS_POSITION\n'
+                'END_POSITION'
+            ).statements
+
+    def test_position_attach(self):
+        [lookup, anchor1, anchor2, anchor3, anchor4] = self.parse(
+            'DEF_LOOKUP "anchor_top" PROCESS_BASE PROCESS_MARKS ALL '
+            'DIRECTION RTL\n'
+            'IN_CONTEXT\n'
+            'END_CONTEXT\n'
+            'AS_POSITION\n'
+            'ATTACH GLYPH "a" GLYPH "e"\n'
+            'TO GLYPH "acutecomb" AT ANCHOR "top" '
+            'GLYPH "gravecomb" AT ANCHOR "top"\n'
+            'END_ATTACH\n'
+            'END_POSITION\n'
+            'DEF_ANCHOR "MARK_top" ON 120 GLYPH acutecomb COMPONENT 1 '
+            'AT POS DX 0 DY 450 END_POS END_ANCHOR\n'
+            'DEF_ANCHOR "MARK_top" ON 121 GLYPH gravecomb COMPONENT 1 '
+            'AT POS DX 0 DY 450 END_POS END_ANCHOR\n'
+            'DEF_ANCHOR "top" ON 31 GLYPH a COMPONENT 1 '
+            'AT POS DX 210 DY 450 END_POS END_ANCHOR\n'
+            'DEF_ANCHOR "top" ON 35 GLYPH e COMPONENT 1 '
+            'AT POS DX 215 DY 450 END_POS END_ANCHOR\n'
+        ).statements
+        self.assertEqual(
+            (lookup.name, lookup.pos.coverage, lookup.pos.coverage_to),
+            ("anchor_top", ("a", "e"), [(("acutecomb",), "top"),
+                                        (("gravecomb",), "top")])
+        )
+        self.assertEqual(
+            (anchor1.name, anchor1.gid, anchor1.glyph_name, anchor1.component,
+             anchor1.locked, anchor1.pos),
+            ("MARK_top", 120, "acutecomb", 1, False, (None, 0, 450, {}, {},
+             {}))
+        )
+        self.assertEqual(
+            (anchor2.name, anchor2.gid, anchor2.glyph_name, anchor2.component,
+             anchor2.locked, anchor2.pos),
+            ("MARK_top", 121, "gravecomb", 1, False, (None, 0, 450, {}, {},
+             {}))
+        )
+        self.assertEqual(
+            (anchor3.name, anchor3.gid, anchor3.glyph_name, anchor3.component,
+             anchor3.locked, anchor3.pos),
+            ("top", 31, "a", 1, False, (None, 210, 450, {}, {}, {}))
+        )
+        self.assertEqual(
+            (anchor4.name, anchor4.gid, anchor4.glyph_name, anchor4.component,
+             anchor4.locked, anchor4.pos),
+            ("top", 35, "e", 1, False, (None, 215, 450, {}, {}, {}))
+        )
+
+    def test_position_attach_cursive(self):
+        [lookup] = self.parse(
+            'DEF_LOOKUP "SomeLookup" PROCESS_BASE PROCESS_MARKS ALL '
+            'DIRECTION RTL\n'
+            'IN_CONTEXT\n'
+            'END_CONTEXT\n'
+            'AS_POSITION\n'
+            'ATTACH_CURSIVE EXIT GLYPH "a" GLYPH "b" ENTER GLYPH "c"\n'
+            'END_ATTACH\n'
+            'END_POSITION\n'
+        ).statements
+        self.assertEqual(
+            (lookup.name,
+             lookup.pos.coverages_exit, lookup.pos.coverages_enter),
+            ("SomeLookup",
+             [("a", "b")], [("c",)])
+        )
+
+    def test_position_adjust_pair(self):
+        [lookup] = self.parse(
+            'DEF_LOOKUP "kern1" PROCESS_BASE PROCESS_MARKS ALL '
+            'DIRECTION RTL\n'
+            'IN_CONTEXT\n'
+            'END_CONTEXT\n'
+            'AS_POSITION\n'
+            'ADJUST_PAIR\n'
+            ' FIRST GLYPH "A"\n'
+            ' SECOND GLYPH "V"\n'
+            ' 1 2 BY POS ADV -30 END_POS POS END_POS\n'
+            ' 2 1 BY POS ADV -30 END_POS POS END_POS\n'
+            'END_ADJUST\n'
+            'END_POSITION\n'
+        ).statements
+        self.assertEqual(
+            (lookup.name, lookup.pos.coverages_1, lookup.pos.coverages_2,
+             lookup.pos.adjust_pair),
+            ("kern1", [("A",)], [("V",)],
+             {(1, 2): ((-30, None, None, {}, {}, {}),
+                       (None, None, None, {}, {}, {})),
+              (2, 1): ((-30, None, None, {}, {}, {}),
+                       (None, None, None, {}, {}, {}))})
+        )
+
+    def test_position_adjust_single(self):
+        [lookup] = self.parse(
+            'DEF_LOOKUP "TestLookup" PROCESS_BASE PROCESS_MARKS ALL '
+            'DIRECTION LTR\n'
+            'IN_CONTEXT\n'
+            # 'LEFT GLYPH "leftGlyph"\n'
+            # 'RIGHT GLYPH "rightGlyph"\n'
+            'END_CONTEXT\n'
+            'AS_POSITION\n'
+            'ADJUST_SINGLE'
+            ' GLYPH "glyph1" BY POS ADV 0 DX 123 END_POS\n'
+            ' GLYPH "glyph2" BY POS ADV 0 DX 456 END_POS\n'
+            'END_ADJUST\n'
+            'END_POSITION\n'
+        ).statements
+        self.assertEqual(
+            (lookup.name, lookup.pos.adjust_single),
+            ("TestLookup",
+             [(("glyph1",), (0, 123, None, {}, {}, {})),
+              (("glyph2",), (0, 456, None, {}, {}, {}))])
+        )
+
+    def test_def_anchor(self):
+        [anchor1, anchor2, anchor3] = self.parse(
+            'DEF_ANCHOR "top" ON 120 GLYPH a '
+            'COMPONENT 1 AT POS DX 250 DY 450 END_POS END_ANCHOR\n'
+            'DEF_ANCHOR "MARK_top" ON 120 GLYPH acutecomb '
+            'COMPONENT 1 AT POS DX 0 DY 450 END_POS END_ANCHOR\n'
+            'DEF_ANCHOR "bottom" ON 120 GLYPH a '
+            'COMPONENT 1 AT POS DX 250 DY 0 END_POS END_ANCHOR\n'
+        ).statements
+        self.assertEqual(
+            (anchor1.name, anchor1.gid, anchor1.glyph_name, anchor1.component,
+             anchor1.locked, anchor1.pos),
+            ("top", 120, "a", 1,
+             False, (None, 250, 450, {}, {}, {}))
+        )
+        self.assertEqual(
+            (anchor2.name, anchor2.gid, anchor2.glyph_name, anchor2.component,
+             anchor2.locked, anchor2.pos),
+            ("MARK_top", 120, "acutecomb", 1,
+             False, (None, 0, 450, {}, {}, {}))
+        )
+        self.assertEqual(
+            (anchor3.name, anchor3.gid, anchor3.glyph_name, anchor3.component,
+             anchor3.locked, anchor3.pos),
+            ("bottom", 120, "a", 1,
+             False, (None, 250, 0, {}, {}, {}))
+        )
+
+    def test_def_anchor_duplicate(self):
+        self.assertRaisesRegex(
+            VoltLibError,
+            'Anchor "dupe" already defined, '
+            'anchor names are case insensitive',
+            self.parse,
+            'DEF_ANCHOR "dupe" ON 120 GLYPH a '
+            'COMPONENT 1 AT POS DX 250 DY 450 END_POS END_ANCHOR\n'
+            'DEF_ANCHOR "dupe" ON 120 GLYPH a '
+            'COMPONENT 1 AT POS DX 250 DY 450 END_POS END_ANCHOR\n'
+        )
+
+    def test_def_anchor_locked(self):
+        [anchor] = self.parse(
+            'DEF_ANCHOR "top" ON 120 GLYPH a '
+            'COMPONENT 1 LOCKED AT POS DX 250 DY 450 END_POS END_ANCHOR\n'
+        ).statements
+        self.assertEqual(
+            (anchor.name, anchor.gid, anchor.glyph_name, anchor.component,
+             anchor.locked, anchor.pos),
+            ("top", 120, "a", 1,
+             True, (None, 250, 450, {}, {}, {}))
+        )
+
+    def test_anchor_adjust_device(self):
+        [anchor] = self.parse(
+            'DEF_ANCHOR "MARK_top" ON 123 GLYPH diacglyph '
+            'COMPONENT 1 AT POS DX 0 DY 456 ADJUST_BY 12 AT 34 '
+            'ADJUST_BY 56 AT 78 END_POS END_ANCHOR'
+        ).statements
+        self.assertEqual(
+            (anchor.name, anchor.pos),
+            ("MARK_top", (None, 0, 456, {}, {}, {34: 12, 78: 56}))
+        )
+
+    def test_ppem(self):
+        [grid_ppem, pres_ppem, ppos_ppem] = self.parse(
+            'GRID_PPEM 20\n'
+            'PRESENTATION_PPEM 72\n'
+            'PPOSITIONING_PPEM 144\n'
+        ).statements
+        self.assertEqual(
+            ((grid_ppem.name, grid_ppem.value),
+             (pres_ppem.name, pres_ppem.value),
+             (ppos_ppem.name, ppos_ppem.value)),
+            (("GRID_PPEM", 20), ("PRESENTATION_PPEM", 72),
+             ("PPOSITIONING_PPEM", 144))
+        )
+
+    def test_compiler_flags(self):
+        [setting1, setting2] = self.parse(
+            'COMPILER_USEEXTENSIONLOOKUPS\n'
+            'COMPILER_USEPAIRPOSFORMAT2\n'
+        ).statements
+        self.assertEqual(
+            ((setting1.name, setting1.value),
+             (setting2.name, setting2.value)),
+            (("COMPILER_USEEXTENSIONLOOKUPS", True),
+             ("COMPILER_USEPAIRPOSFORMAT2", True))
+        )
+
+    def test_cmap(self):
+        [cmap_format1, cmap_format2, cmap_format3] = self.parse(
+            'CMAP_FORMAT 0 3 4\n'
+            'CMAP_FORMAT 1 0 6\n'
+            'CMAP_FORMAT 3 1 4\n'
+        ).statements
+        self.assertEqual(
+            ((cmap_format1.name, cmap_format1.value),
+             (cmap_format2.name, cmap_format2.value),
+             (cmap_format3.name, cmap_format3.value)),
+            (("CMAP_FORMAT", (0, 3, 4)),
+             ("CMAP_FORMAT", (1, 0, 6)),
+             ("CMAP_FORMAT", (3, 1, 4)))
+        )
+
+    def setUp(self):
+        self.tempdir = None
+        self.num_tempfiles = 0
+
+    def tearDown(self):
+        if self.tempdir:
+            shutil.rmtree(self.tempdir)
+
+    def parse(self, text):
+        if not self.tempdir:
+            self.tempdir = tempfile.mkdtemp()
+        self.num_tempfiles += 1
+        path = os.path.join(self.tempdir, "tmp%d.vtp" % self.num_tempfiles)
+        with open(path, "w") as outfile:
+            outfile.write(text)
+        return Parser(path).parse()
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tools/fontTools b/Tools/fontTools
deleted file mode 120000
index 9a21c02..0000000
--- a/Tools/fontTools
+++ /dev/null
@@ -1 +0,0 @@
-../Lib/fontTools
\ No newline at end of file
diff --git a/Tools/pyftinspect b/Tools/pyftinspect
deleted file mode 100755
index f50f81b..0000000
--- a/Tools/pyftinspect
+++ /dev/null
@@ -1,6 +0,0 @@
-#! /usr/bin/env python
-
-import sys
-from fontTools import inspect
-
-inspect.main(sys.argv[1:])
diff --git a/Tools/pyftmerge b/Tools/pyftmerge
deleted file mode 100755
index 2479258..0000000
--- a/Tools/pyftmerge
+++ /dev/null
@@ -1,6 +0,0 @@
-#! /usr/bin/env python
-
-import sys
-from fontTools import merge
-
-merge.main(sys.argv[1:])
diff --git a/Tools/pyftsubset b/Tools/pyftsubset
deleted file mode 100755
index 4bc7c7c..0000000
--- a/Tools/pyftsubset
+++ /dev/null
@@ -1,6 +0,0 @@
-#! /usr/bin/env python
-
-import sys
-from fontTools import subset
-
-subset.main(sys.argv[1:])
diff --git a/Tools/ttx b/Tools/ttx
deleted file mode 100755
index 10b9ef0..0000000
--- a/Tools/ttx
+++ /dev/null
@@ -1,6 +0,0 @@
-#! /usr/bin/env python
-
-import sys
-from fontTools import ttx
-
-ttx.main(sys.argv[1:])
diff --git a/Windows/README.TXT b/Windows/README.TXT
deleted file mode 100644
index 13f1971..0000000
--- a/Windows/README.TXT
+++ /dev/null
@@ -1,53 +0,0 @@
-
-TTX 2.0 for Windows
--------------------------
-
-Creating a Windows (9x/ME/NT/2000/XP) setup executable for TTX
-This file has been created by Adam Twardoch <list.adam@twardoch.com>
-December 14, 2004
-
-Pre-compiled versions are hosted at http://www.font.org/software/ttx/
-
-APPROACH I: Using py2exe and InnoSetup
-
-1. Install Python 2.3 for Windows: http://www.python.org/
-2. Install py2exe: http://starship.python.net/crew/theller/py2exe/
-3. Install InnoSetup 4: http://www.jrsoftware.org/
-4. Download the latest released source code of TTX/FontTools at
-   http://sourceforge.net/projects/fonttools/
-   Or alternatively grab the sources from the VCS:
-   http://fonttools.sourceforge.net/
-5. Unzip the source code of TTX/FontTools into a folder.
-6. In the folder where you unzipped TTX/FontTools, type:
-   python setup.py py2exe --icon Windows\ttx.ico --packages encodings
-7. Run Inno Setup and open Windows\fonttools-win-setup.iss
-8. In Inno Setup, select File/Compile, then Run/Run.
-
-APPROACH II: Using McMillan Installer and InnoSetup
-
-1. Install Python 2.3 for Windows: http://www.python.org/
-2. Download and unpack McMillan installer: 
-   http://py.vaults.ca/apyllo2.py/22208368
-   and put the Installer folder into your Python folder, 
-   e.g. C:\Python23\Installer
-3. Install InnoSetup 4: http://www.jrsoftware.org/
-4. Install Microsoft Visual C++ Toolkit 2003: 
-   http://msdn.microsoft.com/visualc/vctoolkit2003/
-5. Put UPX somewhere within your PATH: http://upx.sourceforge.net/
-6. Download the latest released source code of TTX/FontTools at
-   http://sourceforge.net/projects/fonttools/
-   Or alternatively grab the sources from the VCS:
-   http://fonttools.sourceforge.net/
-7. Unzip the source code of TTX/FontTools into a folder.
-8. In the folder where you unzipped TTX/FontTools, type:
-   python setup.py install -f 
-9. Edit mcmillan.bat so the paths in the file correspond to the paths in your system, 
-   and run it. 
-10.Run Inno Setup and open Windows\fonttools-win-setup.iss
-11.In Inno Setup, select File/Compile, then Run/Run.
-
-The distributable TTX Windows setup executable has been saved
-in the Output subfolder of the FontTools\Windows folder.
-
-For information on running TTX on Windows, see fonttools-win-setup.txt in this folder. 
-
diff --git a/Windows/fonttools-win-setup.iss b/Windows/fonttools-win-setup.iss
deleted file mode 100644
index 1227b9a..0000000
--- a/Windows/fonttools-win-setup.iss
+++ /dev/null
@@ -1,355 +0,0 @@
-;This file has been created by Adam Twardoch <adam@twardoch.com>
-;See README.TXT in this folder for instructions on building the setup
-
-[Setup]
-AppName=TTX
-AppVerName=TTX 2.0 r040926 for Windows
-AppPublisher=Just van Rossum
-AppPublisherURL=http://www.letterror.com/code/ttx/
-AppSupportURL=http://www.font.org/software/ttx/
-AppUpdatesURL=http://www.font.org/software/ttx/
-DefaultDirName={pf}\TTX
-DefaultGroupName=TTX
-AllowNoIcons=false
-LicenseFile=..\LICENSE.txt
-InfoBeforeFile=fonttools-win-setup.txt
-InfoAfterFile=..\Doc\changes.txt
-OutputBaseFilename=WinTTX2.0r040926
-AppCopyright=Copyright 1999-2004 by Just van Rossum, Letterror, The Netherlands.
-UninstallDisplayIcon={app}\ttx.ico
-
-[Tasks]
-Name: desktopicon; Description: Create a &desktop icon; GroupDescription: Additional icons:
-
-[Files]
-Source: ..\dist\ttx\*.*; DestDir: {app}; Flags: ignoreversion promptifolder
-Source: ..\LICENSE.txt; DestDir: {app}; Flags: ignoreversion promptifolder
-Source: ..\Doc\documentation.html; DestDir: {app}; Flags: ignoreversion promptifolder
-Source: ..\Doc\changes.txt; DestDir: {app}; Flags: ignoreversion promptifolder
-Source: ..\Doc\bugs.txt; DestDir: {app}; Flags: ignoreversion promptifolder
-Source: fonttools-win-setup.txt; DestDir: {app}; Flags: ignoreversion promptifolder
-Source: ttx.ico; DestDir: {app}; Flags: ignoreversion promptifolder; AfterInstall: AddFolderToPathVariable
-
-[Icons]
-Name: {userdesktop}\ttx.exe; Filename: {app}\ttx.exe; Tasks: desktopicon; IconFilename: {app}\ttx.ico; IconIndex: 0
-Name: {group}\TTX; Filename: {app}\ttx.exe; Tasks: desktopicon; IconFilename: {app}\ttx.ico; IconIndex: 0
-Name: {group}\TTX documentation; Filename: {app}\documentation.html; IconIndex: 0
-Name: {group}\Changes; Filename: {app}\changes.txt; IconIndex: 0
-Name: {group}\Bugs; Filename: {app}\bugs.txt; IconIndex: 0
-Name: {group}\License; Filename: {app}\LICENSE.txt; IconIndex: 0
-Name: {group}\Uninstall TTX; Filename: {uninstallexe}; IconIndex: 0
-Name: {reg:HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders,SendTo}\TTX; Filename: {app}\ttx.exe; WorkingDir: {reg:HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders,SendTo}; IconFilename: {app}\ttx.ico; IconIndex: 0; MinVersion: 0,5.00.2195
-
-[_ISTool]
-EnableISX=true
-
-[Registry]
-Root: HKCR; Subkey: .ttx; ValueType: string; ValueData: {reg:HKCR\.xml,}; Flags: createvalueifdoesntexist uninsdeletekey
-
-[Code]
-
-//
-// InnoSetup Extensions Knowledge Base
-// Article 44 - Native ISX procedures for PATH modification
-// http://www13.brinkster.com/vincenzog/isxart.asp?idart=44
-// Author: Thomas Vedel
-//
-
-// Version log:
-// 03/31/2003: Initial release (thv@lr.dk)
-
-const
-  // Modification method
-  pmAddToBeginning = $1;      // Add dir to beginning of Path
-  pmAddToEnd = $2;            // Add dir to end of Path
-  pmAddAllways = $4;          // Add also if specified dir is already included in existing path
-  pmAddOnlyIfDirExists = $8;  // Add only if specified dir actually exists
-  pmRemove = $10;             // Remove dir from path
-  pmRemoveSubdirsAlso = $20;  // Remove dir and all subdirs from path
-
-  // Scope
-  psCurrentUser = 1;          // Modify path for current user
-  psAllUsers = 2;             // Modify path for all users
-
-  // Error results
-  mpOK = 0;                   // No errors
-  mpMissingRights = -1;       // User has insufficient rights
-  mpAutoexecNoWriteacc = -2;  // Autoexec can not be written (may be readonly)
-  mpBothAddAndRemove = -3;    // User has specified that dir should both be removed from and added to path
-
-
-{ Helper procedure: Split a path environment variable into individual dirnames }
-procedure SplitPath(Path: string; var Dirs: TStringList);
-var
-  pos: integer;
-  s: string;
-begin
-  Dirs.Clear;
-  s := '';
-  pos := 1;
-  while (pos<=Length(Path)) do
-  begin
-    if (Path[pos]<>';') then
-      s := s + Path[pos];
-    if ((Path[pos]=';') or (pos=Length(Path))) then
-    begin
-      s := Trim(s);
-      s := RemoveQuotes(s);
-      s := Trim(s);
-      if (s <> '') then
-        Dirs.Add(s);
-      s := '';
-    end;
-    Pos := Pos + 1;
-  end;
-end; // procedure SplitPath
-
-
-{ Helper procedure: Concatenate individual dirnames into a path environment variable }
-procedure ConcatPath(Dirs: TStringList; Quotes: boolean; var Path: string);
-var
-  Index, MaxIndex: integer;
-  s: string;
-begin
-  MaxIndex := Dirs.Count-1;
-  Path := '';
-  for Index := 0 to MaxIndex do
-  begin
-    s := Dirs.Strings[Index];
-    if ((Quotes) and (pos(' ',s) > 0)) then
-      s := AddQuotes(s);
-    Path := Path + s;
-    if (Index < MaxIndex) then
-      Path := Path + ';'
-  end;
-end; // procedure ConcatPath
-
-
-{ Helper function: Modifies path environment string }
-procedure ModifyPathString(OldPath, DirName: string; Method: integer; Quotes: boolean; var ResultPath: string);
-var
-  Dirs: TStringList;
-  DirNotInPath: Boolean;
-  i: integer;
-begin
-  // Create Dirs variable
-  Dirs := TStringList.Create;
-
-  // Remove quotes form DirName
-  DirName := Trim(DirName);
-  DirName := RemoveQuotes(DirName);
-  DirName := Trim(DirName);
-
-  // Split old path in individual directory names
-  SplitPath(OldPath, Dirs);
-
-  // Check if dir is allready in path
-  DirNotInPath := True;
-  for i:=Dirs.Count-1 downto 0 do
-  begin
-    if (uppercase(Dirs.Strings[i]) = uppercase(DirName)) then
-      DirNotInPath := False;
-  end;
-
-  // Should dir be removed from existing Path?
-  if ((Method and (pmRemove or pmRemoveSubdirsAlso)) > 0) then
-  begin
-    for i:=Dirs.Count-1 downto 0 do
-    begin
-      if (((Method and pmRemoveSubdirsAlso) > 0) and (pos(uppercase(DirName)+'\', uppercase(Dirs.Strings[i])) = 1)) or
-         (((Method and (pmRemove) or (pmRemoveSubdirsAlso)) > 0) and (uppercase(DirName) = uppercase(Dirs.Strings[i])))
-      then
-        Dirs.Delete(i);
-    end;
-  end;
-
-  // Should dir be added to existing Path?
-  if ((Method and (pmAddToBeginning or pmAddToEnd)) > 0) then
-  begin
-    // Add dir to path
-    if (((Method and pmAddAllways) > 0) or DirNotInPath) then
-    begin
-      // Dir is not in path allready or should be added anyway
-      if (((Method and pmAddOnlyIfDirExists) = 0) or (DirExists(DirName))) then
-      begin
-        // Dir actually exsists or should be added anyway
-        if ((Method and pmAddToBeginning) > 0) then
-          Dirs.Insert(0, DirName)
-        else
-          Dirs.Append(DirName);
-      end;
-    end;
-  end;
-
-  // Concatenate directory names into one single path variable
-  ConcatPath(Dirs, Quotes, ResultPath);
-  // Finally free Dirs object
-  Dirs.Free;
-end; // ModifyPathString
-
-
-{ Helper function: Modify path on Windows 9x }
-function ModifyPath9x(DirName: string; Method: integer): integer;
-var
-  AutoexecLines: TStringList;
-  ActualLine: String;
-  PathLineNos: TStringList;
-  FirstPathLineNo: Integer;
-  OldPath, ResultPath: String;
-  LineNo, CharNo, Index: integer;
-
-  TempString: String;
-begin
-  // Expect everything to be OK
-  result := mpOK;
-
-  // Create stringslists
-  AutoexecLines := TStringList.Create;
-  PathLineNos := TStringList.Create;
-
-  // Read existing path
-  OldPath := '';
-  LoadStringFromFile('c:\Autoexec.bat', TempString);
-  AutoexecLines.Text := TempString;
-  PathLineNos.Clear;
-  // Read Autoexec line by line
-  for LineNo := 0 to AutoexecLines.Count - 1 do begin
-    ActualLine := AutoexecLines.Strings[LineNo];
-    // Check if line starts with "PATH=" after first stripping spaces and other "fill-chars"
-    if Pos('=', ActualLine) > 0 then
-    begin
-      for CharNo := Pos('=', ActualLine)-1 downto 1 do
-        if (ActualLine[CharNo]=' ') or (ActualLine[CharNo]=#9) then
-          Delete(ActualLine, CharNo, 1);
-      if Pos('@', ActualLine) = 1 then
-        Delete(ActualLine, 1, 1);
-      if (Pos('PATH=', uppercase(ActualLine))=1) or (Pos('SETPATH=', uppercase(ActualLine))=1) then
-      begin
-        // Remove 'PATH=' and add path to "OldPath" variable
-        Delete(ActualLine, 1, pos('=', ActualLine));
-        // Check if an earlier PATH variable is referenced, but there has been no previous PATH defined in Autoexec
-        if (pos('%PATH%',uppercase(ActualLine))>0) and (PathLineNos.Count=0) then
-          OldPath := ExpandConstant('{win}') + ';' + ExpandConstant('{win}')+'\COMMAND';
-        if (pos('%PATH%',uppercase(ActualLine))>0) then
-        begin
-          ActualLine := Copy(ActualLine, 1, pos('%PATH%',uppercase(ActualLine))-1) +
-                        OldPath +
-                        Copy(ActualLine, pos('%PATH%',uppercase(ActualLine))+6, Length(ActualLine));
-        end;
-        OldPath := ActualLine;
-
-        // Update list of line numbers holding path variables
-        PathLineNos.Add(IntToStr(LineNo));
-      end;
-    end;
-  end;
-
-  // Save first line number in Autoexec.bat which modifies path environment variable
-  if PathLineNos.Count > 0 then
-    FirstPathLineNo := StrToInt(PathLineNos.Strings[0])
-  else
-    FirstPathLineNo := 0;
-
-  // Modify path
-  ModifyPathString(OldPath, DirName, Method, True, ResultPath);
-
-  // Write Modified path back to Autoexec.bat
-  // First delete all existing path references from Autoexec.bat
-  Index := PathLineNos.Count-1;
-  while (Index>=0) do
-  begin
-    AutoexecLines.Delete(StrToInt(PathLineNos.Strings[Index]));
-    Index := Index-1;
-  end;
-  // Then insert new path variable into Autoexec.bat
-  AutoexecLines.Insert(FirstPathLineNo, '@PATH='+ResultPath);
-  // Delete old Autoexec.bat from disk
-  if not DeleteFile('c:\Autoexec.bat') then
-    result := mpAutoexecNoWriteAcc;
-  Sleep(500);
-  // And finally write Autoexec.bat back to disk
-  if not (result=mpAutoexecNoWriteAcc) then
-    SaveStringToFile('c:\Autoexec.bat', AutoexecLines.Text, false);
-
-  // Free stringlists
-  PathLineNos.Free;
-  AutoexecLines.Free;
-end; // ModifyPath9x
-
-
-{ Helper function: Modify path on Windows NT, 2000 and XP }
-function ModifyPathNT(DirName: string; Method, Scope: integer): integer;
-var
-  RegRootKey: integer;
-  RegSubKeyName: string;
-  RegValueName: string;
-  OldPath, ResultPath: string;
-  OK: boolean;
-begin
-  // Expect everything to be OK
-  result := mpOK;
-
-  // Initialize registry key and value names to reflect if changes should be global or local to current user only
-  case Scope of
-    psCurrentUser:
-      begin
-        RegRootKey := HKEY_CURRENT_USER;
-        RegSubKeyName := 'Environment';
-        RegValueName := 'Path';
-      end;
-    psAllUsers:
-      begin
-        RegRootKey := HKEY_LOCAL_MACHINE;
-        RegSubKeyName := 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment';
-        RegValueName := 'Path';
-      end;
-  end;
-
-  // Read current path value from registry
-  OK := RegQueryStringValue(RegRootKey, RegSubKeyName, RegValueName, OldPath);
-  if not OK then
-  begin
-    result := mpMissingRights;
-    Exit;
-  end;
-
-  // Modify path
-  ModifyPathString(OldPath, DirName, Method, False, ResultPath);
-
-  // Write new path value to registry
-  if not RegWriteStringValue(RegRootKey, RegSubKeyName, RegValueName, ResultPath) then
-  begin
-    result := mpMissingRights;
-    Exit;
-
-  end;
-end; // ModifyPathNT
-
-
-{ Main function: Modify path }
-function ModifyPath(Path: string; Method, Scope: integer): integer;
-begin
-  // Check if both add and remove has been specified (= error!)
-  if (Method and (pmAddToBeginning or pmAddToEnd) and (pmRemove or pmRemoveSubdirsAlso)) > 0 then
-  begin
-    result := mpBothAddAndRemove;
-    Exit;
-  end;
-
-  // Perform directory constant expansion
-  Path := ExpandConstantEx(Path, ' ', ' ');
-
-  // Test if Win9x
-  if InstallOnThisVersion('4,0','0,0') = irInstall then
-    ModifyPath9x(Path, Method);
-
-  // Test if WinNT, 2000 or XP
-  if InstallOnThisVersion('0,4','0,0') = irInstall then
-    ModifyPathNT(Path, Method, Scope);
-end; // ModifyPath
-
-procedure AddFolderToPathVariable();
-begin
-  ModifyPath('{app}', pmAddToBeginning, psAllUsers);
-  ModifyPath('{app}', pmAddToBeginning, psCurrentUser);
-end;
diff --git a/Windows/fonttools-win-setup.txt b/Windows/fonttools-win-setup.txt
deleted file mode 100644
index 721c858..0000000
--- a/Windows/fonttools-win-setup.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-TTX is an application to convert OpenType and TrueType files to and from an
-XML-based text format, also called TTX.
-
-The TTX setup application can create an icon for TTX on your desktop. You will
-then be able to drop .TTF or .OTF files onto the ttx.exe icon to dump the font
-to a .TTX file. Dropping a .TTX file onto it builds a TTF or OTF font.
-
-Also, the setup puts a shortcut to TTX in your Send To context menu in Windows 
-Explorer. Click on any OTF, TTF or TTX file with the right mouse button, 
-choose Send To and then TTX. 
-
-For more information, see documentation.html
diff --git a/Windows/mcmillan.bat b/Windows/mcmillan.bat
deleted file mode 100755
index c4f48c9..0000000
--- a/Windows/mcmillan.bat
+++ /dev/null
@@ -1,9 +0,0 @@
-@echo off
-mkdir Build
-mkdir ..\dist
-mkdir ..\dist\ttx
-C:\Python23\Installer\Configure.py
-C:\Python23\Installer\Makespec.py --upx --onefile --paths "C:\Python23\Lib\encodings;C:\Python23\Lib\site-packages\FontTools\fontTools\encodings;C:\Python23\Lib\site-packages\FontTools\fontTools\misc;C:\Python23\Lib\site-packages\FontTools\fontTools\pens;C:\Python23\Lib\site-packages\FontTools\fontTools\ttLib;" --icon ttx.ico --out Build C:\Python23\Lib\site-packages\FontTools\fontTools\ttx.py
-C:\Python23\Installer\Build.py Build\ttx.spec
-move Build\ttx.exe ..\dist\ttx
-
diff --git a/Windows/ttx.ico b/Windows/ttx.ico
deleted file mode 100644
index f0482b0..0000000
--- a/Windows/ttx.ico
+++ /dev/null
Binary files differ
diff --git a/dev-requirements.txt b/dev-requirements.txt
new file mode 100644
index 0000000..a34deb2
--- /dev/null
+++ b/dev-requirements.txt
@@ -0,0 +1,4 @@
+pytest>=3.0
+tox>=2.5
+bump2version>=0.5.6
+sphinx>=1.5.5
diff --git a/fonttools b/fonttools
new file mode 100755
index 0000000..92b390e
--- /dev/null
+++ b/fonttools
@@ -0,0 +1,12 @@
+#!/usr/bin/env python
+from __future__ import print_function, division, absolute_import
+import sys
+import os.path
+
+libdir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'Lib')
+sys.path.insert(0, libdir)
+
+from fontTools.__main__ import main
+
+if __name__ == '__main__':
+	sys.exit(main())
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..5592626
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,7 @@
+# we use the official Brotli module on CPython and the CFFI-based
+# extension 'brotlipy' on PyPy
+brotli==1.0.1; platform_python_implementation != "PyPy"
+brotlipy==0.7.0; platform_python_implementation == "PyPy"
+unicodedata2==10.0.0; python_version < '3.7' and platform_python_implementation != "PyPy"
+munkres==1.0.10
+zopfli==0.1.4
diff --git a/run-tests.sh b/run-tests.sh
new file mode 100755
index 0000000..0eb84de
--- /dev/null
+++ b/run-tests.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+# exit if any subcommand return non-zero status
+set -e
+
+# Choose python version
+if test "x$1" = x-3; then
+	PYTHON=python3
+	shift
+elif test "x$1" = x-2; then
+	PYTHON=python2
+	shift
+fi
+test "x$PYTHON" = x && PYTHON=python
+
+# Find tests
+FILTERS=
+for arg in "$@"; do
+	test "x$FILTERS" != x && FILTERS="$FILTERS or "
+	FILTERS="$FILTERS$arg"
+done
+
+# Run tests
+if [ -z "$FILTERS" ]; then
+	$PYTHON setup.py test
+else
+	$PYTHON setup.py test --addopts="-k \"$FILTERS\""
+fi
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..232b34a
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,57 @@
+[bumpversion]
+current_version = 3.28.0
+commit = True
+tag = False
+tag_name = {new_version}
+parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\.(?P<release>[a-z]+)(?P<dev>\d+))?
+serialize = 
+	{major}.{minor}.{patch}.{release}{dev}
+	{major}.{minor}.{patch}
+
+[bumpversion:part:release]
+optional_value = final
+values = 
+	dev
+	final
+
+[bumpversion:part:dev]
+
+[bumpversion:file:Lib/fontTools/__init__.py]
+search = __version__ = "{current_version}"
+replace = __version__ = "{new_version}"
+
+[bumpversion:file:setup.py]
+search = version="{current_version}"
+replace = version="{new_version}"
+
+[wheel]
+universal = 1
+
+[sdist]
+formats = zip
+
+[aliases]
+test = pytest
+
+[metadata]
+license_file = LICENSE
+
+[tool:pytest]
+minversion = 3.0
+testpaths = 
+	Tests
+	fontTools
+python_files = 
+	*_test.py
+python_classes = 
+	*Test
+addopts = 
+	-r a
+	--doctest-modules
+	--doctest-ignore-import-errors
+	--pyargs
+
+[egg_info]
+tag_build = 
+tag_date = 0
+
diff --git a/setup.py b/setup.py
index d5b6436..c843da0 100755
--- a/setup.py
+++ b/setup.py
@@ -1,87 +1,344 @@
 #! /usr/bin/env python
 
 from __future__ import print_function
-import os, sys
-from distutils.core import setup, Extension
-from distutils.command.build_ext import build_ext
+import io
+import sys
+import os
+from os.path import isfile, join as pjoin
+from glob import glob
+from setuptools import setup, find_packages, Command
+from distutils import log
+from distutils.util import convert_path
+import subprocess as sp
+import contextlib
+import re
 
-try:
-	# load py2exe distutils extension, if available
-	import py2exe
-except ImportError:
-	pass
+# Force distutils to use py_compile.compile() function with 'doraise' argument
+# set to True, in order to raise an exception on compilation errors
+import py_compile
+orig_py_compile = py_compile.compile
 
-try:
-	import xml.parsers.expat
-except ImportError:
-	print("*** Warning: FontTools needs PyXML, see:")
-	print("        http://sourceforge.net/projects/pyxml/")
+def doraise_py_compile(file, cfile=None, dfile=None, doraise=False):
+	orig_py_compile(file, cfile=cfile, dfile=dfile, doraise=True)
+
+py_compile.compile = doraise_py_compile
+
+needs_pytest = {'pytest', 'test'}.intersection(sys.argv)
+pytest_runner = ['pytest_runner'] if needs_pytest else []
+needs_wheel = {'bdist_wheel'}.intersection(sys.argv)
+wheel = ['wheel'] if needs_wheel else []
+needs_bumpversion = {'release'}.intersection(sys.argv)
+bumpversion = ['bump2version'] if needs_bumpversion else []
+
+# Trove classifiers for PyPI
+classifiers = {"classifiers": [
+	"Development Status :: 5 - Production/Stable",
+	"Environment :: Console",
+	"Environment :: Other Environment",
+	"Intended Audience :: Developers",
+	"Intended Audience :: End Users/Desktop",
+	"License :: OSI Approved :: MIT License",
+	"Natural Language :: English",
+	"Operating System :: OS Independent",
+	"Programming Language :: Python",
+	"Programming Language :: Python :: 2",
+	"Programming Language :: Python :: 3",
+	"Topic :: Text Processing :: Fonts",
+	"Topic :: Multimedia :: Graphics",
+	"Topic :: Multimedia :: Graphics :: Graphics Conversion",
+]}
 
 
-class build_ext_optional(build_ext):
-	"""build_ext command which doesn't abort when it fails."""
-	def build_extension(self, ext):
-		# Skip extensions which cannot be built
-		try:
-			build_ext.build_extension(self, ext)
-		except:
-			self.announce(
-				'*** WARNING: Building of extension "%s" '
-				'failed: %s' %
-				(ext.name, sys.exc_info()[1]))
+# concatenate README.rst and NEWS.rest into long_description so they are
+# displayed on the FontTols project page on PyPI
+with io.open("README.rst", "r", encoding="utf-8") as readme:
+	long_description = readme.read()
+long_description += "\nChangelog\n~~~~~~~~~\n\n"
+with io.open("NEWS.rst", "r", encoding="utf-8") as changelog:
+	long_description += changelog.read()
 
 
-if sys.version_info > (2, 3, 0, 'alpha', 1):
-	# Trove classifiers for PyPI
-	classifiers = {"classifiers": [
-		"Development Status :: 4 - Beta",
-		"Environment :: Console",
-		"Environment :: Other Environment",
-		"Intended Audience :: Developers",
-		"Intended Audience :: End Users/Desktop",
-		"License :: OSI Approved :: BSD License",
-		"Natural Language :: English",
-		"Operating System :: OS Independent",
-		"Programming Language :: Python",
-		"Topic :: Multimedia :: Graphics",
-		"Topic :: Multimedia :: Graphics :: Graphics Conversion",
-	]}
-else:
-	classifiers = {}
+@contextlib.contextmanager
+def capture_logger(name):
+	""" Context manager to capture a logger output with a StringIO stream.
+	"""
+	import logging
 
-long_description = """\
-FontTools/TTX is a library to manipulate font files from Python.
-It supports reading and writing of TrueType/OpenType fonts, reading
-and writing of AFM files, reading (and partially writing) of PS Type 1
-fonts. The package also contains a tool called "TTX" which converts
-TrueType/OpenType fonts to and from an XML-based format.
-"""
+	logger = logging.getLogger(name)
+	try:
+		import StringIO
+		stream = StringIO.StringIO()
+	except ImportError:
+		stream = io.StringIO()
+	handler = logging.StreamHandler(stream)
+	logger.addHandler(handler)
+	try:
+		yield stream
+	finally:
+		logger.removeHandler(handler)
+
+
+class release(Command):
+	"""
+	Tag a new release with a single command, using the 'bumpversion' tool
+	to update all the version strings in the source code.
+	The version scheme conforms to 'SemVer' and PEP 440 specifications.
+
+	Firstly, the pre-release '.devN' suffix is dropped to signal that this is
+	a stable release. If '--major' or '--minor' options are passed, the
+	the first or second 'semver' digit is also incremented. Major is usually
+	for backward-incompatible API changes, while minor is used when adding
+	new backward-compatible functionalities. No options imply 'patch' or bug-fix
+	release.
+
+	A new header is also added to the changelog file ("NEWS.rst"), containing
+	the new version string and the current 'YYYY-MM-DD' date.
+
+	All changes are committed, and an annotated git tag is generated. With the
+	--sign option, the tag is GPG-signed with the user's default key.
+
+	Finally, the 'patch' part of the version string is bumped again, and a
+	pre-release suffix '.dev0' is appended to mark the opening of a new
+	development cycle.
+
+	Links:
+	- http://semver.org/
+	- https://www.python.org/dev/peps/pep-0440/
+	- https://github.com/c4urself/bump2version
+	"""
+
+	description = "update version strings for release"
+
+	user_options = [
+		("major", None, "bump the first digit (incompatible API changes)"),
+		("minor", None, "bump the second digit (new backward-compatible features)"),
+		("sign", "s", "make a GPG-signed tag, using the default key"),
+		("allow-dirty", None, "don't abort if working directory is dirty"),
+	]
+
+	changelog_name = "NEWS.rst"
+	version_RE = re.compile("^[0-9]+\.[0-9]+")
+	date_fmt = u"%Y-%m-%d"
+	header_fmt = u"%s (released %s)"
+	commit_message = "Release {new_version}"
+	tag_name = "{new_version}"
+	version_files = [
+		"setup.cfg",
+		"setup.py",
+		"Lib/fontTools/__init__.py",
+	]
+
+	def initialize_options(self):
+		self.minor = False
+		self.major = False
+		self.sign = False
+		self.allow_dirty = False
+
+	def finalize_options(self):
+		if all([self.major, self.minor]):
+			from distutils.errors import DistutilsOptionError
+			raise DistutilsOptionError("--major/--minor are mutually exclusive")
+		self.part = "major" if self.major else "minor" if self.minor else None
+
+	def run(self):
+		if self.part is not None:
+			log.info("bumping '%s' version" % self.part)
+			self.bumpversion(self.part, commit=False)
+			release_version = self.bumpversion(
+				"release", commit=False, allow_dirty=True)
+		else:
+			log.info("stripping pre-release suffix")
+			release_version = self.bumpversion("release")
+		log.info("  version = %s" % release_version)
+
+		changes = self.format_changelog(release_version)
+
+		self.git_commit(release_version)
+		self.git_tag(release_version, changes, self.sign)
+
+		log.info("bumping 'patch' version and pre-release suffix")
+		next_dev_version = self.bumpversion('patch', commit=True)
+		log.info("  version = %s" % next_dev_version)
+
+	def git_commit(self, version):
+		""" Stage and commit all relevant version files, and format the commit
+		message with specified 'version' string.
+		"""
+		files = self.version_files + [self.changelog_name]
+
+		log.info("committing changes")
+		for f in files:
+			log.info("  %s" % f)
+		if self.dry_run:
+			return
+		sp.check_call(["git", "add"] + files)
+		msg = self.commit_message.format(new_version=version)
+		sp.check_call(["git", "commit", "-m", msg], stdout=sp.PIPE)
+
+	def git_tag(self, version, message, sign=False):
+		""" Create annotated git tag with given 'version' and 'message'.
+		Optionally 'sign' the tag with the user's GPG key.
+		"""
+		log.info("creating %s git tag '%s'" % (
+			"signed" if sign else "annotated", version))
+		if self.dry_run:
+			return
+		# create an annotated (or signed) tag from the new version
+		tag_opt = "-s" if sign else "-a"
+		tag_name = self.tag_name.format(new_version=version)
+		proc = sp.Popen(
+			["git", "tag", tag_opt, "-F", "-", tag_name], stdin=sp.PIPE)
+		# use the latest changes from the changelog file as the tag message
+		tag_message = u"%s\n\n%s" % (tag_name, message)
+		proc.communicate(tag_message.encode('utf-8'))
+		if proc.returncode != 0:
+			sys.exit(proc.returncode)
+
+	def bumpversion(self, part, commit=False, message=None, allow_dirty=None):
+		""" Run bumpversion.main() with the specified arguments, and return the
+		new computed version string (cf. 'bumpversion --help' for more info)
+		"""
+		import bumpversion
+
+		args = (
+			(['--verbose'] if self.verbose > 1 else []) +
+			(['--dry-run'] if self.dry_run else []) +
+			(['--allow-dirty'] if (allow_dirty or self.allow_dirty) else []) +
+			(['--commit'] if commit else ['--no-commit']) +
+			(['--message', message] if message is not None else []) +
+			['--list', part]
+		)
+		log.debug("$ bumpversion %s" % " ".join(a.replace(" ", "\\ ") for a in args))
+
+		with capture_logger("bumpversion.list") as out:
+			bumpversion.main(args)
+
+		last_line = out.getvalue().splitlines()[-1]
+		new_version = last_line.replace("new_version=", "")
+		return new_version
+
+	def format_changelog(self, version):
+		""" Write new header at beginning of changelog file with the specified
+		'version' and the current date.
+		Return the changelog content for the current release.
+		"""
+		from datetime import datetime
+
+		log.info("formatting changelog")
+
+		changes = []
+		with io.open(self.changelog_name, "r+", encoding="utf-8") as f:
+			for ln in f:
+				if self.version_RE.match(ln):
+					break
+				else:
+					changes.append(ln)
+			if not self.dry_run:
+				f.seek(0)
+				content = f.read()
+				date = datetime.today().strftime(self.date_fmt)
+				f.seek(0)
+				header = self.header_fmt % (version, date)
+				f.write(header + u"\n" + u"-"*len(header) + u"\n\n" + content)
+
+		return u"".join(changes)
+
+
+class PassCommand(Command):
+	""" This is used with Travis `dpl` tool so that it skips creating sdist
+	and wheel packages, but simply uploads to PyPI the files found in ./dist
+	folder, that were previously built inside the tox 'bdist' environment.
+	This ensures that the same files are uploaded to Github Releases and PyPI.
+	"""
+
+	description = "do nothing"
+	user_options = []
+
+	def initialize_options(self):
+		pass
+
+	def finalize_options(self):
+		pass
+
+	def run(self):
+		pass
+
+
+def find_data_files(manpath="share/man"):
+	""" Find FontTools's data_files (just man pages at this point).
+
+	By default, we install man pages to "share/man" directory relative to the
+	base installation directory for data_files. The latter can be changed with
+	the --install-data option of 'setup.py install' sub-command.
+
+	E.g., if the data files installation directory is "/usr", the default man
+	page installation directory will be "/usr/share/man".
+
+	You can override this via the $FONTTOOLS_MANPATH environment variable.
+
+	E.g., on some BSD systems man pages are installed to 'man' instead of
+	'share/man'; you can export $FONTTOOLS_MANPATH variable just before
+	installing:
+
+	$ FONTTOOLS_MANPATH="man" pip install -v .
+	    [...]
+	    running install_data
+	    copying Doc/man/ttx.1 -> /usr/man/man1
+
+	When installing from PyPI, for this variable to have effect you need to
+	force pip to install from the source distribution instead of the wheel
+	package (otherwise setup.py is not run), by using the --no-binary option:
+
+	$ FONTTOOLS_MANPATH="man" pip install --no-binary=fonttools fonttools
+
+	Note that you can only override the base man path, i.e. without the
+	section number (man1, man3, etc.). The latter is always implied to be 1,
+	for "general commands".
+	"""
+
+	# get base installation directory for man pages
+	manpagebase = os.environ.get('FONTTOOLS_MANPATH', convert_path(manpath))
+	# all our man pages go to section 1
+	manpagedir = pjoin(manpagebase, 'man1')
+
+	manpages = [f for f in glob(pjoin('Doc', 'man', 'man1', '*.1')) if isfile(f)]
+
+	data_files = [(manpagedir, manpages)]
+	return data_files
+
 
 setup(
-		name = "fonttools",
-		version = "2.4",
-		description = "Tools to manipulate font files",
-		author = "Just van Rossum",
-		author_email = "just@letterror.com",
-		maintainer = "Just van Rossum",
-		maintainer_email = "just@letterror.com",
-		url = "http://fonttools.sourceforge.net/",
-		license = "OpenSource, BSD-style",
-		platforms = ["Any"],
-		long_description = long_description,
-		
-		packages = [
-			"fontTools",
-			"fontTools.encodings",
-			"fontTools.misc",
-			"fontTools.pens",
-			"fontTools.ttLib",
-			"fontTools.ttLib.tables",
-		],
-		package_dir = {'': 'Lib'},
-		extra_path = 'FontTools',
-		scripts = ["Tools/ttx", "Tools/pyftsubset", "Tools/pyftinspect", "Tools/pyftmerge"],
-		cmdclass = {"build_ext": build_ext_optional},
-		data_files = [('share/man/man1', ["Doc/ttx.1"])],
-		**classifiers
-	)
+	name="fonttools",
+	version="3.28.0",
+	description="Tools to manipulate font files",
+	author="Just van Rossum",
+	author_email="just@letterror.com",
+	maintainer="Behdad Esfahbod",
+	maintainer_email="behdad@behdad.org",
+	url="http://github.com/fonttools/fonttools",
+	license="MIT",
+	platforms=["Any"],
+	long_description=long_description,
+	package_dir={'': 'Lib'},
+	packages=find_packages("Lib"),
+	include_package_data=True,
+	data_files=find_data_files(),
+	setup_requires=pytest_runner + wheel + bumpversion,
+	tests_require=[
+		'pytest>=3.0',
+	],
+	entry_points={
+		'console_scripts': [
+			"fonttools = fontTools.__main__:main",
+			"ttx = fontTools.ttx:main",
+			"pyftsubset = fontTools.subset:main",
+			"pyftmerge = fontTools.merge:main",
+			"pyftinspect = fontTools.inspect:main"
+		]
+	},
+	cmdclass={
+		"release": release,
+		'pass': PassCommand,
+	},
+	**classifiers
+)
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..cb1ab91
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,68 @@
+[tox]
+envlist = py{27,36}-cov, htmlcov
+
+[testenv]
+basepython =
+    py27: {env:TOXPYTHON:python2.7}
+    pypy: {env:TOXPYTHON:pypy}
+    py34: {env:TOXPYTHON:python3.4}
+    py35: {env:TOXPYTHON:python3.5}
+    py36: {env:TOXPYTHON:python3.6}
+deps =
+    cov: coverage>=4.3
+    pytest
+    -rrequirements.txt
+install_command =
+    pip install -v {opts} {packages}
+commands =
+    # run the test suite against the package installed inside tox env.
+    # We use parallel mode and then combine later so that coverage.py will take
+    # paths like .tox/py36/lib/python3.6/site-packages/fontTools and collapse
+    # them into Lib/fontTools.
+    cov: coverage run --parallel-mode -m pytest {posargs}
+    nocov: pytest {posargs}
+
+[testenv:htmlcov]
+basepython = {env:TOXPYTHON:python3.6}
+deps =
+    coverage>=4.3
+skip_install = true
+commands =
+    coverage combine
+    coverage html
+
+[testenv:codecov]
+passenv = *
+basepython = {env:TOXPYTHON:python}
+deps =
+    coverage>=4.3
+    codecov
+skip_install = true
+ignore_outcome = true
+commands =
+    coverage combine
+    codecov --env TOXENV
+
+[testenv:bdist]
+basepython = {env:TOXPYTHON:python3.6}
+deps =
+    pygments
+    docutils
+    setuptools
+    wheel
+skip_install = true
+install_command =
+    # make sure we use the latest setuptools and wheel
+    pip install --upgrade {opts} {packages}
+whitelist_externals =
+    rm
+commands =
+    # check metadata and rst long_description
+    python setup.py check --restructuredtext --strict
+    # clean up build/ and dist/ folders
+    rm -rf {toxinidir}/dist
+    python setup.py clean --all
+    # build sdist
+    python setup.py sdist --dist-dir {toxinidir}/dist
+    # build wheel from sdist
+    pip wheel -v --no-deps --no-index --wheel-dir {toxinidir}/dist --find-links {toxinidir}/dist fonttools